select_handlers.c

Go to the documentation of this file.
00001 /*****************************************************************************
00002  *
00003  * cdebconf - An implementation of the Debian Configuration Management
00004  *            System
00005  *
00006  * $Id$
00007  *
00008  * cdebconf is (c) 2000-2007 Randolph Chung and others under the following
00009  * license.
00010  *
00011  * Redistribution and use in source and binary forms, with or without
00012  * modification, are permitted provided that the following conditions
00013  * are met:
00014  *
00015  * 1. Redistributions of source code must retain the above copyright
00016  * notice, this list of conditions and the following disclaimer.
00017  *
00018  * 2. Redistributions in binary form must reproduce the above copyright
00019  * notice, this list of conditions and the following disclaimer in the
00020  * documentation and/or other materials provided with the distribution.
00021  *
00022  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
00023  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00024  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00025  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
00026  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
00027  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
00028  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
00029  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00030  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
00031  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
00032  * SUCH DAMAGE.
00033  *
00034  *****************************************************************************/
00035 
00042 #include "select_handlers.h"
00043 
00044 #include <string.h>
00045 #include <gtk/gtk.h>
00046 
00047 #include "frontend.h"
00048 #include "question.h"
00049 
00050 #include "fe_gtk.h"
00051 #include "fe_data.h"
00052 #include "descriptions.h"
00053 #include "choice_model.h"
00054 #include "ui.h"
00055 
00061 static void set_value_from_combo(struct question * question,
00062                                  GtkComboBox * combo_box)
00063 {
00064     GtkTreeModel * model;
00065     GtkTreeIter iter;
00066     char * value;
00067 
00068     if (gtk_combo_box_get_active_iter(combo_box, &iter)) {
00069         model = gtk_combo_box_get_model(GTK_COMBO_BOX(combo_box));
00070         gtk_tree_model_get(model, &iter,
00071                            /* column: */ CHOICE_MODEL_VALUE, &value,
00072                            -1 /* end of list */);
00073         question_setvalue(question, value);
00074         g_free(value);
00075     } else {
00076         question_setvalue(question, "");
00077     }
00078 }
00079 
00085 static void set_value_from_select(struct question * question,
00086                                   GtkTreeView * view)
00087 {
00088     GtkTreeSelection * selection;
00089     GtkTreeModel * model;
00090     GtkTreeIter iter;
00091     char * value;
00092     
00093     selection = gtk_tree_view_get_selection(view);
00094     if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
00095         gtk_tree_model_get(model, &iter,
00096                            /* column: */ CHOICE_MODEL_VALUE, &value,
00097                            -1 /* end of list */);
00098         question_setvalue(question, value);
00099         g_free(value);
00100     } else {
00101         question_setvalue(question, "");
00102     }
00103 }
00104 
00112 static gboolean is_country(int index, const char * choice,
00113                            const char * choice_translated)
00114 {
00115     return '-' == choice_translated[0] && '-' == choice_translated[1];
00116 }
00117 
00125 static gboolean is_disk(int index, const char * choice,
00126                         const char * choice_translated)
00127 {
00128     return NULL == strstr(choice_translated, "    ");
00129 }
00130 
00132 static const struct {
00134     const char * tag;
00136     parent_predicate parent_predicate;
00137 } special_questions[] = {
00138     { "countrychooser/country-name", is_country },
00139     { "partman/choose_partition", is_disk },
00140     { NULL, NULL }
00141 };
00142 
00149 static parent_predicate get_special_predicate(const char * tag) {
00150     int i;
00151 
00152     for (i = 0; NULL != special_questions[i].tag; i++) {
00153         if (0 == strcmp(tag, special_questions[i].tag)) {
00154             return special_questions[i].parent_predicate;
00155         }
00156     }
00157     return NULL;
00158 }
00159 
00170 static int create_select_list(struct frontend * fe, struct question * question,
00171                               GtkWidget * question_box, GtkTreeModel * model)
00172 {
00173     GtkTreePath * path = NULL;
00174     GtkWidget * view;
00175     GtkWidget * scroll;
00176     GtkWidget * frame;
00177     GtkCellRenderer * text_renderer;
00178     char * description;
00179 
00180     model = fe_gtk_choice_model_create_full(
00181         fe, question, get_special_predicate(question->tag));
00182     if (NULL == model) {
00183         g_warning("create_model_from_choices failed.");
00184         return DC_NOTOK;
00185     }
00186 
00187     /* INFO(INFO_DEBUG, "GTK_DI - gtkhandler_select_single_list() called"); */
00188 
00189     /* check NULL! */
00190     view = gtk_tree_view_new_with_model(model);
00191 
00192     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view),
00193                                       FALSE /* no headers */);
00194     gtk_tree_view_set_enable_search(GTK_TREE_VIEW(view),
00195                                     TRUE /* enable typeahead */);
00196     gtk_tree_selection_set_mode(
00197         gtk_tree_view_get_selection(GTK_TREE_VIEW(view)),
00198         GTK_SELECTION_BROWSE);
00199 
00200     description = q_get_description(question);
00201     text_renderer = gtk_cell_renderer_text_new();
00202     gtk_tree_view_insert_column_with_attributes(
00203         GTK_TREE_VIEW(view), -1 /* insert at the end */,
00204         description /* title */, text_renderer,
00205         "text", CHOICE_MODEL_TRANSLATED_VALUE,
00206         NULL /* end of attribute list */);
00207     g_free(description);
00208 
00209     g_signal_connect_swapped(G_OBJECT(view), "row-activated",
00210                              G_CALLBACK(fe_gtk_set_answer_ok), fe);
00211 
00212     path = fe_gtk_choice_model_get_first_selected(model);
00213     /* Select first row if there is no other default */
00214     if (NULL == path) {
00215         path = gtk_tree_path_new_first();
00216     } else {
00217         /* Only expand path when there was a previous selection. */
00218         gtk_tree_view_expand_to_path(GTK_TREE_VIEW(view), path);
00219     } 
00220     /* XXX: test if we really need to scroll if we set cursor */
00221     gtk_tree_view_scroll_to_cell(
00222         GTK_TREE_VIEW(view), path,
00223         NULL /* don't scroll to a particular column */,
00224         TRUE /* please align */,
00225         0.5 /* center row */,
00226         0 /* left column */);
00227     gtk_tree_view_set_cursor(GTK_TREE_VIEW(view), path,
00228                              NULL /* don't focus a particular column */,
00229                              FALSE /* don't start editing */);
00230     gtk_tree_path_free(path);
00231 
00232     scroll = gtk_scrolled_window_new(NULL /* create horizontal adjustement */,
00233                                      NULL /* create vertical adjustement */);
00234     gtk_container_add(GTK_CONTAINER(scroll), view);
00235     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
00236                                    GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
00237     frame = gtk_frame_new(NULL);
00238     gtk_container_add(GTK_CONTAINER(frame), scroll);
00239 
00240     fe_gtk_add_common_layout(fe, question, question_box, frame);
00241 
00242     gtk_widget_grab_focus(view);
00243 
00244     fe_gtk_register_setter(fe, SETTER_FUNCTION(set_value_from_select),
00245                            question, view);
00246 
00247     return DC_OK;
00248 }
00249 
00256 static void update_selection_for_toggle(GtkCellRendererToggle * cell,
00257                                         const gchar * path_string,
00258                                         GtkTreeModel * model)
00259 {
00260     GtkTreeIter iter;
00261     gboolean value;
00262 
00263     if (gtk_tree_model_get_iter_from_string(model, &iter, path_string)) {
00264         gtk_tree_model_get(model, &iter,
00265                            /* column: */ CHOICE_MODEL_SELECTED, &value,
00266                            -1 /* end of list */);
00267         value ^= TRUE;
00268         fe_gtk_choice_model_set(model, &iter,
00269                                 /* column: */ CHOICE_MODEL_SELECTED, value,
00270                                 -1 /* end of list */);
00271     }
00272 }
00273 
00282 static void set_value_from_multiselect(struct question * question,
00283                                        GtkTreeModel * model)
00284 {
00285     GtkTreeIter iter;
00286     gboolean valid;
00287     gboolean selected;
00288     gchar ** selected_values;
00289     guint selected_index;
00290     gchar * result;
00291 
00292     /* check NULL! */
00293     selected_values = g_malloc0(
00294         sizeof (char *) * (1 /* terminating NULL */ +
00295                            fe_gtk_choice_model_get_length(model)));
00296     selected_index = 0;
00297     valid = gtk_tree_model_get_iter_first(model, &iter);
00298     while (valid) {
00299         gtk_tree_model_get(model, &iter,
00300                            /* column: */ CHOICE_MODEL_SELECTED, &selected,
00301                            -1 /* end of list */);
00302         if (selected) {
00303             gtk_tree_model_get(model, &iter,
00304                                /* column: */ CHOICE_MODEL_VALUE,
00305                                              &selected_values[selected_index],
00306                                -1 /* end of list */);
00307             selected_index++;
00308         }
00309         valid = gtk_tree_model_iter_next(model, &iter);
00310     }
00311     if (0 < selected_index) {
00312         result = g_strjoinv(", ", selected_values);
00313     } else {
00314         /* no selections */
00315         result = g_strdup("");
00316     }
00317     question_setvalue(question, result);
00318     g_free(result);
00319 
00320     for (selected_index = 0; NULL != selected_values[selected_index];
00321          selected_index++) {
00322         g_free(selected_values[selected_index]);
00323     }
00324     g_free(selected_values);
00325 }
00326 
00332 static void update_model_from_toggle_button(
00333     GtkToggleButton * toggle_button, GtkTreeRowReference * row_reference)
00334 {
00335     GtkTreeModel * model = gtk_tree_row_reference_get_model(row_reference);
00336     GtkTreePath * path = gtk_tree_row_reference_get_path(row_reference);
00337     GtkTreeIter iter;
00338 
00339     g_assert(NULL != model);
00340     g_assert(NULL != path);
00341 
00342     if (gtk_tree_model_get_iter(model, &iter, path)) {
00343         gtk_list_store_set(
00344             GTK_LIST_STORE(model), &iter,
00345             /* column: */ CHOICE_MODEL_SELECTED,
00346                           gtk_toggle_button_get_active(toggle_button),
00347             -1 /* end of list */);
00348     }
00349 }
00350 
00360 static gulong connect_signal_to_row(GObject * object, const gchar * signal,
00361                                     GtkTreeModel * model, GtkTreeIter * iter,
00362                                     GCallback handler)
00363 {
00364     GtkTreePath * path;
00365     GtkTreeRowReference * row_reference;
00366 
00367     path = gtk_tree_model_get_path(model, iter);
00368     row_reference = gtk_tree_row_reference_new(model, path);
00369     gtk_tree_path_free(path);
00370     return g_signal_connect_data(object, "toggled", handler, row_reference,
00371                                  (GClosureNotify) gtk_tree_row_reference_free,
00372                                  0 /* no connect options */);
00373 }
00374 
00385 static int create_multiselect_list(struct frontend * fe,
00386                                    struct question * question,
00387                                    GtkWidget * question_box,
00388                                    GtkTreeModel * model) 
00389 {
00390     GtkTreeIter iter;
00391     GtkWidget * view;
00392     GtkWidget * scroll;
00393     GtkWidget * frame;
00394     GtkCellRenderer * toggle_renderer;
00395     GtkCellRenderer * text_renderer;
00396     GtkTreePath * path;
00397 
00398     view = gtk_tree_view_new_with_model(model);
00399 
00400     gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), FALSE);
00401 
00402     toggle_renderer = gtk_cell_renderer_toggle_new();
00403     g_signal_connect(G_OBJECT(toggle_renderer), "toggled",
00404                      G_CALLBACK(update_selection_for_toggle), model);
00405     gtk_tree_view_insert_column_with_attributes(
00406         GTK_TREE_VIEW(view), -1 /* insert at the end */,
00407         NULL /* no title */, toggle_renderer,
00408         "active", CHOICE_MODEL_SELECTED,
00409         NULL /* end of attribute list */);
00410 
00411     text_renderer = gtk_cell_renderer_text_new();
00412     gtk_tree_view_insert_column_with_attributes(
00413         GTK_TREE_VIEW(view), -1 /* insert at the end */,
00414         NULL /* no title */, text_renderer,
00415         /* column: */ "text", CHOICE_MODEL_TRANSLATED_VALUE,
00416         NULL /* end of list */);
00417 
00418     /* select the first row */
00419     gtk_tree_model_get_iter_first(model, &iter);
00420     path = gtk_tree_model_get_path(model, &iter);
00421     gtk_tree_view_set_cursor(GTK_TREE_VIEW(view), path,
00422                              NULL /* don't focus on a particular column */,
00423                              FALSE /* do not start editing */);
00424     gtk_tree_path_free(path);
00425 
00426     scroll = gtk_scrolled_window_new(NULL /* create horizontal adjustement */,
00427                                      NULL /* create vertical adjustement */);
00428     gtk_container_add(GTK_CONTAINER(scroll), view);
00429     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
00430                                    GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
00431     frame = gtk_frame_new(NULL /* no label */);
00432     gtk_container_add(GTK_CONTAINER(frame), scroll);
00433 
00434     fe_gtk_add_common_layout(fe, question, question_box, frame);
00435     gtk_widget_grab_focus(view);
00436 
00437     fe_gtk_register_setter(fe, SETTER_FUNCTION(set_value_from_multiselect),
00438                            question, model);
00439     return DC_OK;
00440 }
00441 
00446 static void focus_first_child(GtkContainer * container)
00447 {
00448     GList * children;
00449 
00450     children = gtk_container_get_children(container);
00451     gtk_widget_grab_focus(GTK_WIDGET(children->data));
00452     g_list_free(children);
00453 }
00454 
00465 static int create_multiselect_checkboxes(struct frontend * fe,
00466                                          struct question * question,
00467                                          GtkWidget * question_box,
00468                                          GtkTreeModel * model)
00469 {
00470     GtkWidget * check_container;
00471     GtkWidget * check;
00472     GtkTreeIter iter;
00473     gboolean valid;
00474     gchar * label;
00475     gboolean selected;
00476     
00477     /* XXX: should we return DC_OK? */
00478     g_assert(0 < fe_gtk_choice_model_get_length(model));
00479 
00480     check_container = gtk_vbox_new(FALSE /* don't make children equal */,
00481                                    0 /* padding */);
00482 
00483     valid = gtk_tree_model_get_iter_first(model, &iter);
00484     while (valid) {
00485         gtk_tree_model_get(model, &iter,
00486                            /* column: */ CHOICE_MODEL_TRANSLATED_VALUE, &label,
00487                            /* column: */ CHOICE_MODEL_SELECTED, &selected,
00488                            -1 /* end of list */);
00489 
00490         check = gtk_check_button_new_with_label(label);
00491         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check), selected);
00492 
00493         connect_signal_to_row(G_OBJECT(check), "toggled", model, &iter,
00494                               G_CALLBACK(update_model_from_toggle_button));
00495 
00496         gtk_box_pack_start(GTK_BOX(check_container), check,
00497                            FALSE /* don't expand */, FALSE /* don't fill */,
00498                            0 /* padding */);
00499 
00500         g_free(label);
00501         valid = gtk_tree_model_iter_next(model, &iter);
00502     }
00503     
00504     fe_gtk_add_common_layout(fe, question, question_box, check_container);
00505 
00506     if (fe_gtk_is_first_question(question)) {
00507         focus_first_child(GTK_CONTAINER(check_container));
00508     }
00509 
00510     fe_gtk_register_setter(fe, SETTER_FUNCTION(set_value_from_multiselect),
00511                            question, model);
00512 
00513     return DC_OK;
00514 }
00515 
00521 static void set_selected_active(GtkWidget * combo_box, GtkTreeModel * model)
00522 {
00523     GtkTreePath * path;
00524     GtkTreeIter iter;
00525     gboolean valid;
00526 
00527     path = fe_gtk_choice_model_get_first_selected(model);
00528     if (NULL != path) {
00529         valid = gtk_tree_model_get_iter(model, &iter, path);
00530         if (valid) {
00531             gtk_combo_box_set_active_iter(GTK_COMBO_BOX(combo_box), &iter);
00532         }
00533         gtk_tree_path_free(path);
00534     }
00535 }
00536 
00547 static int create_select_combo(struct frontend * fe,
00548                                struct question * question,
00549                                GtkWidget * question_box,
00550                                GtkTreeModel * model)
00551 {
00552     GtkWidget * combo_box;
00553     GtkCellRenderer * text_renderer;
00554 
00555     /* XXX: check NULL! */
00556     combo_box = gtk_combo_box_new_with_model(model);
00557     /* XXX: check NULL! */
00558     text_renderer = gtk_cell_renderer_text_new();
00559     gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(combo_box), text_renderer,
00560                                TRUE /* expand */);
00561     gtk_cell_layout_set_attributes(GTK_CELL_LAYOUT(combo_box), text_renderer,
00562                                    "text", CHOICE_MODEL_TRANSLATED_VALUE,
00563                                    NULL /* end of list */);
00564     set_selected_active(combo_box, model);
00565 
00566     fe_gtk_add_common_layout(fe, question, question_box, combo_box);
00567 
00568     if (fe_gtk_is_first_question(question)) {
00569         gtk_widget_grab_focus(combo_box);
00570     }
00571 
00572     fe_gtk_register_setter(fe, SETTER_FUNCTION(set_value_from_combo),
00573                            question, combo_box);
00574 
00575     return DC_OK;
00576 }
00577 
00588 static int handle_all(struct frontend * fe, struct question * question,
00589                       GtkWidget * question_box, gboolean multiselect)
00590 {
00591     GtkTreeModel * model;
00592 
00593     if (NULL == (model = fe_gtk_choice_model_create(fe, question))) {
00594         g_warning("create_model_from_choices failed.");
00595         return DC_NOTOK;
00596     }
00597 
00598     if (multiselect && IS_QUESTION_SINGLE(question)) {
00599         return create_multiselect_list(fe, question, question_box, model);
00600     }
00601     if (multiselect) {
00602         return create_multiselect_checkboxes(fe, question, question_box,
00603                                              model);
00604     }
00605     if (IS_QUESTION_SINGLE(question)) {
00606         return create_select_list(fe, question, question_box, model);
00607     }
00608     return create_select_combo(fe, question, question_box, model);
00609 }
00610 
00618 int fe_gtk_handle_select(struct frontend * fe, struct question * question,
00619                          GtkWidget * question_box)
00620 {
00621     return handle_all(fe, question, question_box, FALSE /* simple select */);
00622 }
00623 
00631 int fe_gtk_handle_multiselect(struct frontend * fe, struct question * question,
00632                               GtkWidget * question_box)
00633 {
00634     return handle_all(fe, question, question_box, TRUE /* multiselect */);
00635 }
00636 
00637 /* vim: et sw=4 si
00638  */ 

Generated on Sat Jul 7 23:41:41 2007 for fe_gtk by  doxygen 1.5.1