00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
00017 
00029 
00030 #include <gtk/gtk.h>
00031 
00032 
00033 
00034 
00035 #include "ergtklog.h"
00036 #include "erGtkSelectionGroup.h"
00037 
00038 
00039 
00040 static GtkEventBoxClass* g_parent_class = NULL;
00041 
00042 
00043 enum
00044 {
00045     SELECTION_UPDATE,  
00046     LAST_SIGNAL,
00047 };
00048 
00049 static gint g_signals[LAST_SIGNAL];  
00050 
00051 
00052 
00053 
00054 
00055 
00056 
00057 static void add_buttons(erGtkSelectionGroup *item, GtkToggleButton** button_tbl);
00058 
00059 
00060 static gboolean real_set_details(erGtkSelectionGroup *item, const guint min_selected, const guint max_selected);
00061 static void real_freeze_buttons(erGtkSelectionGroup *item, const gboolean freeze);
00062 static GtkToggleButton* real_get_button(erGtkSelectionGroup *item, const guint button_id);
00063 static void real_get_selected_buttons(erGtkSelectionGroup *item, gint* button_ids, const guint len);
00064 
00065 
00066 static void     ergtk_selection_group_class_init(erGtkSelectionGroupClass* klass);
00067 static void     ergtk_selection_group_init(erGtkSelectionGroup* selection);
00068 static void     ergtk_selection_group_dispose(GObject* object);
00069 static void     ergtk_selection_group_finalize(GObject* object);
00070 static void     on_button_toggled(GtkToggleButton* button, gpointer user_data);
00071 static gboolean on_delayed_select_button(gpointer user_data);
00072 static gboolean on_delayed_deselect_button(gpointer user_data);
00073 
00074 
00075 
00076 GtkWidget *ergtk_selection_group_new(GtkToggleButton** button_tbl)
00077 {
00078     GtkToggleButton** p_button;
00079 
00080     
00081     for (p_button = button_tbl ; *p_button ; p_button++)
00082     {
00083         g_return_val_if_fail(GTK_IS_TOGGLE_BUTTON(*p_button), FALSE);
00084     }
00085 
00086     
00087     erGtkSelectionGroup* item = (erGtkSelectionGroup*) g_object_new(ERGTK_SELECTION_GROUP_TYPE, NULL);
00088 
00089     
00090     add_buttons(item, button_tbl);
00091     
00092     return GTK_WIDGET(item);
00093 }
00094 
00095 static void add_buttons(erGtkSelectionGroup *item, GtkToggleButton** button_tbl)
00096 {
00097     g_return_if_fail(ERGTK_IS_SELECTION_GROUP(item));
00098     g_return_if_fail(item->buttons == NULL);
00099     g_return_if_fail(item->history == NULL);
00100 
00101     GtkToggleButton*  button;
00102     GtkToggleButton** p_button;
00103 
00104     
00105     for (p_button = button_tbl ; *p_button ; p_button++)
00106     {
00107         button = *p_button;
00108         g_object_ref(G_OBJECT(button));
00109         g_signal_connect_after(G_OBJECT(button), "toggled", G_CALLBACK(on_button_toggled), item);
00110         item->buttons = g_slist_append(item->buttons, button);
00111 
00112         if (gtk_toggle_button_get_active(button))
00113         {
00114             item->history = g_slist_append(item->history, button);
00115         }
00116     }
00117 }
00118 
00119 
00120 GType ergtk_selection_group_get_type(void)
00121 {
00122     static GType class_type = 0;
00123 
00124     if (class_type == 0)
00125     {
00126         static const GTypeInfo class_info =
00127         {
00128             sizeof(erGtkSelectionGroupClass),
00129             NULL,               
00130             NULL,               
00131             (GClassInitFunc) ergtk_selection_group_class_init,
00132             NULL,               
00133             NULL,               
00134             sizeof(erGtkSelectionGroup),
00135             0,                  
00136             (GInstanceInitFunc) ergtk_selection_group_init,
00137         };
00138 
00139         class_type = g_type_register_static(GTK_TYPE_EVENT_BOX, "erGtkSelectionGroup", &class_info, 0);
00140     }
00141     return class_type;
00142 }
00143 
00144 
00145 static void ergtk_selection_group_class_init(erGtkSelectionGroupClass* klass)
00146 {
00147     GObjectClass* object_class = (GObjectClass *)klass;
00148 
00149     
00150     g_parent_class = g_type_class_peek_parent(klass);
00151 
00152     
00153     object_class->dispose  = ergtk_selection_group_dispose;
00154     object_class->finalize = ergtk_selection_group_finalize;
00155     
00156     klass->set_details          = real_set_details;
00157     klass->freeze_buttons       = real_freeze_buttons;
00158     klass->get_button           = real_get_button;
00159     klass->get_selected_buttons = real_get_selected_buttons;
00160     klass->add_buttons          = add_buttons;
00161 
00162     
00163     g_signals[SELECTION_UPDATE] = g_signal_new( "selection_update",
00164                                                 G_OBJECT_CLASS_TYPE(object_class),
00165                                                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,
00166                                                 G_STRUCT_OFFSET(erGtkSelectionGroupClass, selection_update),  
00167                                                 NULL,             
00168                                                 NULL,             
00169                                                 gtk_marshal_VOID__POINTER,
00170                                                 G_TYPE_NONE,      
00171                                                 1,                
00172                                                 G_TYPE_POINTER ); 
00173 }
00174 
00175 
00176 static void ergtk_selection_group_init(erGtkSelectionGroup* item)
00177 {
00178     g_return_if_fail(ERGTK_IS_SELECTION_GROUP(item));
00179 
00180     
00181     item->dispose_has_run      = FALSE;
00182     item->buttons              = NULL;
00183     item->min_selected         = 0;
00184     item->max_selected         = 9999;
00185     item->freeze_buttons       = FALSE;
00186     item->history              = NULL;
00187 }
00188 
00189 
00190 static void ergtk_selection_group_dispose(GObject* object)
00191 {
00192     g_return_if_fail(ERGTK_IS_SELECTION_GROUP(object));
00193 
00194     erGtkSelectionGroup* item = (erGtkSelectionGroup*)object;
00195 
00196     
00197     if (!item->dispose_has_run)
00198     {
00199         item->dispose_has_run = TRUE;
00200 
00201         g_slist_foreach(item->buttons, (GFunc)g_object_unref, NULL);
00202     }
00203 
00204     
00205     GObjectClass* parent_object_class = G_OBJECT_CLASS(g_parent_class);
00206     if (parent_object_class->dispose)
00207     {
00208         parent_object_class->dispose(object);
00209     }
00210 }
00211 
00212 
00213 static void ergtk_selection_group_finalize(GObject* object)
00214 {
00215     g_return_if_fail(ERGTK_IS_SELECTION_GROUP(object));
00216 
00217     erGtkSelectionGroup* item = (erGtkSelectionGroup*)object;
00218 
00219     
00220     if (item->buttons)
00221     {
00222         g_slist_free(item->buttons);
00223     }
00224     if (item->history)
00225     {
00226         g_slist_free(item->history);
00227     }
00228 
00229     
00230     GObjectClass* parent_object_class = G_OBJECT_CLASS(g_parent_class);
00231     if (parent_object_class->finalize)
00232     {
00233         parent_object_class->finalize(object);
00234     }
00235 }
00236 
00237 
00238 gboolean ergtk_selection_group_set_details(erGtkSelectionGroup *item, const guint min_selected, const guint max_selected)
00239 {
00240     LOGPRINTF("entry: item [%p] min [%u] max [%u]", item, min_selected, max_selected);
00241     g_return_val_if_fail(ERGTK_IS_SELECTION_GROUP(item),   FALSE);
00242     g_return_val_if_fail((item->dispose_has_run == FALSE), FALSE);
00243 
00244     erGtkSelectionGroupClass* klass = ERGTK_SELECTION_GROUP_GET_CLASS(item);
00245 
00246     
00247     if (klass->set_details)
00248     {
00249         return klass->set_details(item, min_selected, max_selected);
00250     }
00251     else
00252     {
00253         return FALSE;  
00254     }
00255 }
00256 
00257 static gboolean real_set_details(erGtkSelectionGroup *item, const guint min_selected, const guint max_selected)
00258 {
00259     LOGPRINTF("entry: item [%p] min [%u] max [%u]", item, min_selected, max_selected);
00260     g_return_val_if_fail(ERGTK_IS_SELECTION_GROUP(item),   FALSE);
00261     g_return_val_if_fail((item->dispose_has_run == FALSE), FALSE);
00262 
00263     gint num_buttons = g_slist_length(item->buttons);
00264     g_return_val_if_fail(min_selected <= max_selected, FALSE);
00265     g_return_val_if_fail(min_selected <= num_buttons,  FALSE);
00266     g_return_val_if_fail(max_selected >= 0,            FALSE);
00267 
00268     GSList* history_last          = g_slist_last(item->history);
00269     GSList* buttons_last_selected = history_last ? g_slist_find(item->buttons, history_last->data) : NULL;
00270     
00271     GtkToggleButton* button_tmp = NULL;
00272 
00273     
00274     item->min_selected = min_selected;
00275     item->max_selected = max_selected;
00276     
00277     
00278     gint num_selected = g_slist_length(item->history);
00279     LOGPRINTF("num_buttons [%d] num_selected [%d]", num_buttons, num_selected);
00280     while (num_selected < min_selected)
00281     {
00282         
00283         if (buttons_last_selected)
00284         {
00285             buttons_last_selected = g_slist_next(buttons_last_selected);
00286         }
00287         else
00288         {
00289             buttons_last_selected = item->buttons;
00290         }
00291         item->history = g_slist_append(item->history, buttons_last_selected->data);
00292         history_last  = g_slist_last(item->history);
00293         num_selected++;
00294 
00295         
00296         
00297         
00298         gtk_toggle_button_set_active(buttons_last_selected->data, TRUE);
00299     }
00300     while (num_selected > max_selected)
00301     {
00302         
00303         button_tmp = history_last->data;
00304         item->history = g_slist_delete_link(item->history, history_last);
00305         history_last  = g_slist_last(item->history);
00306         num_selected--;
00307         
00308         
00309         
00310         
00311         gtk_toggle_button_set_active(button_tmp, FALSE);
00312     }
00313 
00314     LOGPRINTF("leave: num_selected [%d] history_length [%d]", num_selected, g_slist_length(item->history));
00315     return TRUE;  
00316 }
00317 
00318 
00319 void ergtk_selection_group_freeze_buttons(erGtkSelectionGroup *item,
00320                                               const gboolean freeze)
00321 {
00322     LOGPRINTF("entry: item [%p] freeze [%d]", item, freeze);
00323     g_return_if_fail(ERGTK_IS_SELECTION_GROUP(item));
00324     g_return_if_fail((item->dispose_has_run == FALSE));
00325 
00326     erGtkSelectionGroupClass* klass = ERGTK_SELECTION_GROUP_GET_CLASS(item);
00327 
00328     
00329     if (klass->freeze_buttons)
00330     {
00331         klass->freeze_buttons(item, freeze);
00332     }
00333 }
00334 
00335 static void real_freeze_buttons(erGtkSelectionGroup *item,
00336                                 const gboolean freeze)
00337 {
00338     LOGPRINTF("entry: item [%p] freeze [%d]", item, freeze);
00339     g_return_if_fail(ERGTK_IS_SELECTION_GROUP(item));
00340     g_return_if_fail((item->dispose_has_run == FALSE));
00341 
00342     item->freeze_buttons = freeze;
00343 }
00344 
00345 
00346 GtkToggleButton* ergtk_selection_group_get_button(erGtkSelectionGroup *item, const guint button_id)
00347 {
00348     LOGPRINTF("entry: item [%p] button_id [%d]", item, button_id);
00349     g_return_val_if_fail(ERGTK_IS_SELECTION_GROUP(item),   NULL);
00350     g_return_val_if_fail((item->dispose_has_run == FALSE), NULL);
00351 
00352     erGtkSelectionGroupClass* klass = ERGTK_SELECTION_GROUP_GET_CLASS(item);
00353 
00354     
00355     if (klass->get_button)
00356     {
00357         return klass->get_button(item, button_id);
00358     }
00359     else
00360     {
00361         return NULL;
00362     }
00363 }
00364 
00365 static GtkToggleButton* real_get_button(erGtkSelectionGroup *item, const guint button_id)
00366 {
00367     LOGPRINTF("entry: item [%p] button_id [%d]", item, button_id);
00368     g_return_val_if_fail(ERGTK_IS_SELECTION_GROUP(item),   NULL);
00369     g_return_val_if_fail((item->dispose_has_run == FALSE), NULL);
00370 
00371     GtkToggleButton* button = g_slist_nth_data(item->buttons, button_id);
00372 
00373     return button;
00374 }
00375 
00376 
00377 void ergtk_selection_group_get_selected_buttons(erGtkSelectionGroup *item, gint* button_ids, const guint len)
00378 {
00379     LOGPRINTF("entry: item [%p]", item);
00380     g_return_if_fail(ERGTK_IS_SELECTION_GROUP(item));
00381     g_return_if_fail((item->dispose_has_run == FALSE));
00382 
00383     erGtkSelectionGroupClass* klass = ERGTK_SELECTION_GROUP_GET_CLASS(item);
00384 
00385     
00386     if (klass->get_selected_buttons)
00387     {
00388         klass->get_selected_buttons(item, button_ids, len);
00389     }
00390 }
00391 
00392 static void real_get_selected_buttons(erGtkSelectionGroup *item, gint* button_ids, const guint len)
00393 {
00394     LOGPRINTF("entry: item [%p]", item);
00395     g_return_if_fail(ERGTK_IS_SELECTION_GROUP(item)  );
00396     g_return_if_fail((item->dispose_has_run == FALSE));
00397 
00398     int     i;
00399     GSList* history = item->history;
00400 
00401     for (i = 0 ; i < len ; i++)
00402     {
00403         if (history)
00404         {
00405             button_ids[i] = g_slist_index(item->buttons, history->data);
00406             history = g_slist_next(history);
00407         }
00408         else
00409         {
00410             button_ids[i] = -1;
00411         }
00412         LOGPRINTF("button_id[%d] = [%d]", i, button_ids[i]);
00413     }
00414 }
00415 
00416 
00417 
00418 static void on_button_toggled(GtkToggleButton* button, gpointer user_data)
00419 {
00420     LOGPRINTF("entry: button [%p] item [%p]", button, user_data);
00421     erGtkSelectionGroup* item = (erGtkSelectionGroup*)user_data;
00422     
00423     g_return_if_fail(ERGTK_IS_SELECTION_GROUP(item));
00424     g_return_if_fail((item)->dispose_has_run == FALSE);
00425 
00426     gboolean active         = gtk_toggle_button_get_active(button);
00427     GSList*  history_button = g_slist_find(item->history, button);
00428     guint    history_length = g_slist_length(item->history);
00429     LOGPRINTF("active [%d] history_button [%p] history_len [%u]", 
00430                active, history_button, history_length);
00431 
00432     GtkToggleButton* button_tmp;
00433 
00434     if (item->freeze_buttons)
00435     {
00436         
00437         if (history_button)
00438         {
00439             
00440             if (!active)
00441             {
00442                 g_timeout_add(500, on_delayed_select_button, (gpointer)button);
00443             }
00444         }
00445         else
00446         {
00447             
00448             if (active)
00449             {
00450                 g_timeout_add(500, on_delayed_deselect_button, (gpointer)button);
00451             }
00452         }
00453     }
00454     else
00455     {
00456         
00457         if (active)
00458         {
00459             if (history_button == NULL)
00460             {
00461                 
00462                 LOGPRINTF("add button [%p] to history", button);
00463                 item->history = g_slist_append(item->history, button);
00464                 history_length++;
00465 
00466                 
00467                 LOGPRINTF("enforce [%d] history_len [%d] max_selected [%d]", 
00468                         item->enforce_max_selected, history_length, item->max_selected);
00469                 if (history_length > item->max_selected)
00470                 {
00471                     
00472                     
00473                     button_tmp = GTK_TOGGLE_BUTTON(item->history->data);
00474                     LOGPRINTF("remove button [%p] to history", button_tmp);
00475                     item->history = g_slist_delete_link(item->history, item->history);
00476 
00477                     
00478                     
00479                     gtk_toggle_button_set_active(button_tmp, FALSE);
00480                     LOGPRINTF("button [%p] set to not-active", button_tmp);
00481                 }
00482             }
00483             else
00484             {
00485                 
00486             }
00487         }
00488         else
00489         {
00490             if (history_button)
00491             {
00492                 LOGPRINTF("enforce [%d] history_len [%d] min_selected [%d]", 
00493                         item->enforce_min_selected, history_length, item->min_selected);
00494                 if (history_length > item->min_selected)
00495                 {
00496 
00497                     
00498                     item->history = g_slist_delete_link(item->history, history_button);
00499                 }
00500                 else
00501                 {
00502                     
00503                     g_timeout_add(500, on_delayed_select_button, (gpointer)button);
00504                 }
00505             }
00506             else
00507             {
00508                 
00509             }
00510         }
00511     }
00512 
00513     
00514     g_signal_emit(item, g_signals[SELECTION_UPDATE], 0, (gpointer)button);
00515 }
00516 
00517 static gboolean on_delayed_select_button(gpointer user_data)
00518 {
00519     LOGPRINTF("entry: button [%p]", user_data);
00520     
00521 
00522     GtkToggleButton* button = (GtkToggleButton*)user_data;
00523     
00524     g_return_val_if_fail(GTK_IS_TOGGLE_BUTTON(button), FALSE);
00525 
00526     gtk_toggle_button_set_active(button, TRUE);
00527 
00528     return FALSE;  
00529 }
00530 
00531 static gboolean on_delayed_deselect_button(gpointer user_data)
00532 {
00533     LOGPRINTF("entry: button [%p]", user_data);
00534     
00535 
00536     GtkToggleButton* button = (GtkToggleButton*)user_data;
00537     
00538     g_return_val_if_fail(GTK_IS_TOGGLE_BUTTON(button), FALSE);
00539 
00540     gtk_toggle_button_set_active(button, FALSE);
00541 
00542     return FALSE;  
00543 }
00544 
00545 gint ergtk_selection_group_get_length(erGtkSelectionGroup *item)
00546 {
00547     LOGPRINTF("entry: item [%p]", item);
00548 
00549     gint num_buttons = 0;
00550     
00551     g_return_val_if_fail(ERGTK_IS_SELECTION_GROUP(item), 0);
00552     g_return_val_if_fail(item->buttons, 0);
00553  
00554     num_buttons = g_slist_length(item->buttons);
00555 
00556     LOGPRINTF("%d buttons cotrolled by erGtkSelectionGroup [%p]",num_buttons, item);
00557 
00558     return num_buttons;
00559 }
00560