go.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 
00040 #include "go.h"
00041 
00042 #include <string.h>
00043 #include <gtk/gtk.h>
00044 #include <gdk/gdkkeysyms.h>
00045 
00046 #include "frontend.h"
00047 #include "question.h"
00048 #include "template.h"
00049 #include "plugin.h"
00050 #include "database.h"
00051 
00052 #include "fe_gtk.h"
00053 #include "fe_data.h"
00054 #include "ui.h"
00055 #include "handlers.h"
00056 #include "select_handlers.h"
00057 #include "progress.h"
00058 #ifdef DI_UDEB
00059 # include "di.h"
00060 #endif
00061 
00066 struct setter {
00071     setter_function func;
00072 
00074     struct question * question;
00075 
00077     void * user_data;
00078 
00080     struct setter * next;
00081 };
00082 
00092 void fe_gtk_register_setter(struct frontend * fe, setter_function func,
00093                             struct question * question, void * user_data)
00094 {
00095     struct frontend_data * fe_data = fe->data;
00096     struct setter * setter;
00097 
00098     /* check NULL! */
00099     setter = g_malloc0(sizeof (struct setter));
00100     setter->func = func;
00101     setter->question = question;
00102     setter->user_data = user_data;
00103     setter->next = fe_data->setters;
00104     fe_data->setters = setter;
00105 }
00106 
00111 static void call_setters(struct frontend * fe)
00112 {
00113     struct frontend_data * fe_data = fe->data;
00114     struct setter * setter;
00115 
00116     setter = fe_data->setters;
00117     while (NULL != setter) {
00118         (*setter->func)(setter->question, setter->user_data);
00119         setter = setter->next;
00120     }
00121 }
00122 
00127 static void free_setters(struct frontend_data * fe_data)
00128 {
00129     struct setter * setter;
00130     struct setter * previous;
00131 
00132     setter = fe_data->setters;
00133     while (NULL != setter) {
00134         previous = setter;
00135         setter = setter->next;
00136         g_free(previous);
00137     }
00138     fe_data->setters = NULL;
00139 }
00140 
00148 static gboolean handle_goback_key(GtkWidget * widget, GdkEventKey * key,
00149                                   struct frontend * fe)
00150 {
00151     if (GDK_Escape == key->keyval) {
00152         fe_gtk_set_answer_goback(fe);
00153         return TRUE;
00154     }
00155     return FALSE;
00156 }
00157 
00164 static void create_goback_button(struct frontend * fe)
00165 {
00166     GtkWidget * button;
00167     char * label;
00168 
00169     label = fe_gtk_get_text(fe, "debconf/button-goback", "Go Back");
00170     /* XXX: check NULL! */
00171     button = gtk_button_new_with_label(label);
00172     g_free(label);
00173 
00174     g_signal_connect_swapped(G_OBJECT(button), "clicked",
00175                              G_CALLBACK(fe_gtk_set_answer_goback), fe);
00176 
00177     fe_gtk_add_button(fe, button);
00178     fe_gtk_set_button_secondary(fe, button, TRUE);
00179     fe_gtk_add_global_key_handler(fe, button, G_CALLBACK(handle_goback_key));
00180 }
00181 
00182 /* documented in fe_gtk.h */
00183 GtkWidget * fe_gtk_create_continue_button(struct frontend * fe)
00184 {
00185     GtkWidget * button;
00186     char * label;
00187 
00188     /* XXX: check NULL! */
00189     label = fe_gtk_get_text(fe, "debconf/button-continue", "Continue");
00190     button = gtk_button_new_with_label(label);
00191     g_free(label);
00192 
00193     g_signal_connect_swapped(G_OBJECT(button), "clicked",
00194                              G_CALLBACK(fe_gtk_set_answer_ok), fe);
00195     fe_gtk_add_button(fe, button);
00196     GTK_WIDGET_SET_FLAGS(button, GTK_CAN_DEFAULT);
00197     gtk_widget_grab_default(GTK_WIDGET(button));
00198 
00199     return button;
00200 }
00201 
00209 static void create_default_buttons_if_needed(struct frontend * fe)
00210 {
00211     struct frontend_data * fe_data = fe->data;
00212     GtkContainer * action_box = GTK_CONTAINER(fe_data->action_box);
00213 
00214     if (NULL == gtk_container_get_children(action_box)) {
00215         (void) fe_gtk_create_continue_button(fe);
00216     }
00217 }
00218 
00223 static void destroy_buttons(struct frontend * fe)
00224 {
00225     struct frontend_data * fe_data = fe->data;
00226     GtkContainer * action_box = GTK_CONTAINER(fe_data->action_box);
00227 
00228     gtk_container_foreach(action_box, (GtkCallback) gtk_widget_destroy,
00229                           NULL /* no user_data */);
00230 }
00231 
00234 static const struct {
00236     const char * type;
00238     fe_gtk_handler handler;
00239 } question_handlers[] = {
00240     { "boolean",        fe_gtk_handle_boolean },
00241     { "multiselect",    fe_gtk_handle_multiselect },
00242     { "note",           fe_gtk_handle_note },
00243     { "password",       fe_gtk_handle_password },
00244     { "select",         fe_gtk_handle_select },
00245     { "string",         fe_gtk_handle_string },
00246     { "error",          fe_gtk_handle_note },
00247     { "text",           fe_gtk_handle_text },
00248     { "",               NULL },
00249 };
00250 
00256 static fe_gtk_handler find_internal_handler(const char * type)
00257 {
00258     int i;
00259 
00260     for (i = 0; NULL != question_handlers[i].handler; i++) {
00261         if (0 == strcmp(type, question_handlers[i].type)) {
00262             return question_handlers[i].handler;
00263         }
00264     }
00265     return NULL;
00266 }
00267 
00277 static fe_gtk_handler find_external_handler(struct frontend * fe,
00278                                             const char * type)
00279 {
00280     struct frontend_data * fe_data = fe->data;
00281     struct plugin * plugin;
00282 
00283     /* Look in already loaded plugins first. */
00284     plugin = g_hash_table_lookup(fe_data->plugins, type);
00285     if (NULL == plugin) {
00286         /* Load if available */
00287         if (NULL == (plugin = plugin_find(fe, type))) {
00288             g_warning("No plugin for %s", type);
00289             return NULL;
00290         }
00291         /* Save for later use */
00292         g_hash_table_insert(fe_data->plugins, g_strdup(type), plugin);
00293     }
00294     return (fe_gtk_handler) plugin->handler;
00295 }
00296 
00302 static void update_question_database(struct frontend * fe)
00303 {
00304     struct question * question;
00305 
00306     question = fe->questions;
00307     while (NULL != question) {
00308         fe->qdb->methods.set(fe->qdb, question);
00309         question = question->next;
00310     }
00311 }
00312 
00322 static gboolean call_question_handlers(struct frontend * fe,
00323                                        GtkWidget * question_box)
00324 {
00325     struct question * question;
00326     fe_gtk_handler handler;
00327 
00328     question = fe->questions;
00329     while (NULL != question) {
00330         handler = find_internal_handler(question->template->type);
00331         if (NULL == handler) {
00332             handler = find_external_handler(fe, question->template->type);
00333         }
00334         if (NULL == handler) {
00335             return FALSE;
00336         }
00337         if (DC_OK != handler(fe, question, question_box)) {
00338             g_warning("tag \"%s\" failed to display!", question->tag);
00339             /* XXX: return FALSE? */
00340         }
00341         question = question->next;
00342     }
00343     return TRUE;
00344 }
00345 
00367 static GtkWidget * create_question_box(struct frontend * fe,
00368                                        GtkWidget * container)
00369 {
00370     GtkWidget * question_box;
00371     GtkWidget * question_box_scroll = NULL;
00372 
00373     /* check NULL! */
00374     question_box = gtk_vbox_new(FALSE /* don't make children equal */,
00375                                 0 /* padding */);
00376 
00377     if (IS_QUESTION_SINGLE(fe->questions)) {
00378         gtk_box_pack_start(GTK_BOX(container), question_box,
00379                            TRUE /* expand */, TRUE /* fill */,
00380                            0 /* padding */);
00381     } else {
00382         question_box_scroll = gtk_scrolled_window_new(
00383             NULL /* create horizontal adjustement */,
00384             NULL /* create vertical adjustement */);
00385         gtk_scrolled_window_add_with_viewport(
00386             GTK_SCROLLED_WINDOW(question_box_scroll), question_box);
00387         gtk_scrolled_window_set_policy(
00388             GTK_SCROLLED_WINDOW(question_box_scroll),
00389             GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
00390         gtk_scrolled_window_set_shadow_type(
00391             GTK_SCROLLED_WINDOW(question_box_scroll), GTK_SHADOW_NONE);
00392         gtk_box_pack_start(GTK_BOX(container), question_box_scroll,
00393                            TRUE /* expand */, TRUE /* fill */,
00394                            DEFAULT_PADDING);
00395     }
00396     return question_box;
00397 }
00398 
00405 static void wait_answer(struct frontend * fe)
00406 {
00407     struct frontend_data * fe_data = fe->data;
00408 
00409     g_mutex_lock(fe_data->answer_mutex);
00410     while (DC_NO_ANSWER == fe_data->answer) {
00411         g_cond_wait(fe_data->answer_cond, fe_data->answer_mutex);
00412     }
00413     g_mutex_unlock(fe_data->answer_mutex);
00414 }
00415 
00438 int fe_gtk_go(struct frontend * fe)
00439 {
00440     struct frontend_data * fe_data = fe->data;
00441     GtkWidget * question_box;
00442 
00443     if (NULL == fe->questions) {
00444         return DC_OK;
00445     }
00446 
00447     fe_gtk_set_answer(fe, DC_NO_ANSWER);
00448 
00449     gdk_threads_enter();
00450 #ifdef DI_UDEB
00451     /* XXX: rename */
00452     fe_gtk_di_run_dialog(fe);
00453 #endif /* DI_UDEB */
00454     if (NULL != fe_data->progress_data) {
00455         fe_gtk_hide_progress(fe);
00456     }
00457     question_box = create_question_box(fe, fe_data->target_box);
00458     if (!call_question_handlers(fe, question_box)) {
00459         /* an handler was missing */
00460         fe_gtk_set_answer(fe, DC_NOTIMPL);
00461         goto end;
00462     }
00463     create_default_buttons_if_needed(fe);
00464     if (CAN_GO_BACK(fe)) {
00465         create_goback_button(fe);
00466     }
00467     fe_gtk_show_target_box(fe);
00468     fe_gtk_show_buttons(fe);
00469     gdk_threads_leave();
00470 
00471     /* frontend blocked here until a button has been pressed */
00472     wait_answer(fe);
00473 
00474     if (DC_NOTOK == fe_data->answer) {
00475         goto end;
00476     }
00477 
00478     gdk_threads_enter();
00479     fe_gtk_set_buttons_sensitive(fe, FALSE);
00480     if (DC_OK == fe_data->answer) {
00481         call_setters(fe);
00482         update_question_database(fe);
00483     }
00484     fe_gtk_empty_target_box(fe);
00485     destroy_buttons(fe);
00486     gdk_threads_leave();
00487 
00488 end:
00489     free_setters(fe_data);
00490     return fe_data->answer;
00491 }
00492 
00493 
00494 /* vim: et sw=4 si
00495  */

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