libergtk/src/erGtkSelectionGroup.c File Reference

ereader gtk library - control a group of GtkToggleButton objects, similar to a radiobutton. More...

#include <gtk/gtk.h>
#include "ergtklog.h"
#include "erGtkSelectionGroup.h"

Go to the source code of this file.

Enumerations

enum  { SELECTION_UPDATE, LAST_SIGNAL }

Functions

static void add_buttons (erGtkSelectionGroup *item, GtkToggleButton **button_tbl)
static gboolean real_set_details (erGtkSelectionGroup *item, const guint min_selected, const guint max_selected)
static void real_freeze_buttons (erGtkSelectionGroup *item, const gboolean freeze)
static GtkToggleButton * real_get_button (erGtkSelectionGroup *item, const guint button_id)
static void real_get_selected_buttons (erGtkSelectionGroup *item, gint *button_ids, const guint len)
static void ergtk_selection_group_class_init (erGtkSelectionGroupClass *klass)
static void ergtk_selection_group_init (erGtkSelectionGroup *selection)
static void ergtk_selection_group_dispose (GObject *object)
static void ergtk_selection_group_finalize (GObject *object)
static void on_button_toggled (GtkToggleButton *button, gpointer user_data)
static gboolean on_delayed_select_button (gpointer user_data)
static gboolean on_delayed_deselect_button (gpointer user_data)
GtkWidget * ergtk_selection_group_new (GtkToggleButton **button_tbl)
GType ergtk_selection_group_get_type (void)
gboolean ergtk_selection_group_set_details (erGtkSelectionGroup *item, const guint min_selected, const guint max_selected)
void ergtk_selection_group_freeze_buttons (erGtkSelectionGroup *item, const gboolean freeze)
GtkToggleButton * ergtk_selection_group_get_button (erGtkSelectionGroup *item, const guint button_id)
void ergtk_selection_group_get_selected_buttons (erGtkSelectionGroup *item, gint *button_ids, const guint len)
gint ergtk_selection_group_get_length (erGtkSelectionGroup *item)

Variables

static GtkEventBoxClass * g_parent_class = NULL
static gint g_signals [LAST_SIGNAL]


Detailed Description

ereader gtk library - control a group of GtkToggleButton objects, similar to a radiobutton.

Ensure only the allowed number of buttons is selected at any time. Note: no relation with the standard GTK class erGtkSelection.

Copyright (C) 2007 iRex Technologies B.V. All rights reserved.

Definition in file erGtkSelectionGroup.c.


Enumeration Type Documentation

anonymous enum

Enumerator:
SELECTION_UPDATE 
LAST_SIGNAL 

Definition at line 43 of file erGtkSelectionGroup.c.

00044 {
00045     SELECTION_UPDATE,  // selection has changed, i.e. one or more buttons have changed state
00046     LAST_SIGNAL,
00047 };


Function Documentation

static void add_buttons ( erGtkSelectionGroup item,
GtkToggleButton **  button_tbl 
) [static]

Definition at line 95 of file erGtkSelectionGroup.c.

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     // add buttons to object
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 }

Here is the call graph for this function:

static void ergtk_selection_group_class_init ( erGtkSelectionGroupClass klass  )  [static]

Definition at line 145 of file erGtkSelectionGroup.c.

00146 {
00147     GObjectClass* object_class = (GObjectClass *)klass;
00148 
00149     // remember parent class struct, needed for chaining up to parent class
00150     g_parent_class = g_type_class_peek_parent(klass);
00151 
00152     // overload some virtual methods
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     // event to notify selection update 
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),  // class closure
00167                                                 NULL,             // accumulator
00168                                                 NULL,             // accu data
00169                                                 gtk_marshal_VOID__POINTER,
00170                                                 G_TYPE_NONE,      // return type
00171                                                 1,                // #params
00172                                                 G_TYPE_POINTER ); // param types
00173 }

Here is the call graph for this function:

static void ergtk_selection_group_dispose ( GObject *  object  )  [static]

Definition at line 190 of file erGtkSelectionGroup.c.

00191 {
00192     g_return_if_fail(ERGTK_IS_SELECTION_GROUP(object));
00193 
00194     erGtkSelectionGroup* item = (erGtkSelectionGroup*)object;
00195 
00196     // release reference on buttons
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     // chain to parent class
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 }

static void ergtk_selection_group_finalize ( GObject *  object  )  [static]

Definition at line 213 of file erGtkSelectionGroup.c.

00214 {
00215     g_return_if_fail(ERGTK_IS_SELECTION_GROUP(object));
00216 
00217     erGtkSelectionGroup* item = (erGtkSelectionGroup*)object;
00218 
00219     // free linked lists in item
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     // chain to parent class
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 }

void ergtk_selection_group_freeze_buttons ( erGtkSelectionGroup item,
const gboolean  freeze 
)

force buttons to stay in their current state, even when user changes them

Parameters:
item - the erGtkSelectionGroup object
freeze - TRUE force buttons in current state, FALSE allow user to change buttons
Returns:
--

Definition at line 319 of file erGtkSelectionGroup.c.

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     // chain to real method, which may be overloaded
00329     if (klass->freeze_buttons)
00330     {
00331         klass->freeze_buttons(item, freeze);
00332     }
00333 }

GtkToggleButton* ergtk_selection_group_get_button ( erGtkSelectionGroup item,
const guint  button_id 
)

get button by index

Parameters:
item - the erGtkSelectionGroup object
button_id - id of the requested button as an index (0 ..) in button_tbl on .._new()
Returns:
ptr to the requested button, or NULL

Definition at line 346 of file erGtkSelectionGroup.c.

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     // chain to real method, which may be overloaded
00355     if (klass->get_button)
00356     {
00357         return klass->get_button(item, button_id);
00358     }
00359     else
00360     {
00361         return NULL;
00362     }
00363 }

gint ergtk_selection_group_get_length ( erGtkSelectionGroup item  ) 

get the number of how many buttons in erGtkSelectionGroup

Parameters:
item - the erGtkSelectionGroup object
Returns:
the number of buttons

Definition at line 545 of file erGtkSelectionGroup.c.

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 }

void ergtk_selection_group_get_selected_buttons ( erGtkSelectionGroup item,
gint *  button_ids,
const guint  len 
)

get index of currently selected buttons

Parameters:
item - the erGtkSelectionGroup object
button_ids (out) - array selected buttons id (0 ..), latest selected first, unused items set to -1
len - number of elements in array button_ids
Returns:
--

Definition at line 377 of file erGtkSelectionGroup.c.

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     // chain to real method, which may be overloaded
00386     if (klass->get_selected_buttons)
00387     {
00388         klass->get_selected_buttons(item, button_ids, len);
00389     }
00390 }

GType ergtk_selection_group_get_type ( void   ) 

Definition at line 120 of file erGtkSelectionGroup.c.

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,               /* base_init */
00130             NULL,               /* base_finalize */
00131             (GClassInitFunc) ergtk_selection_group_class_init,
00132             NULL,               /* class_finalize */
00133             NULL,               /* class_data */
00134             sizeof(erGtkSelectionGroup),
00135             0,                  /* n_preallocs */
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 }

Here is the call graph for this function:

static void ergtk_selection_group_init ( erGtkSelectionGroup selection  )  [static]

Definition at line 176 of file erGtkSelectionGroup.c.

00177 {
00178     g_return_if_fail(ERGTK_IS_SELECTION_GROUP(item));
00179 
00180     // set object defaults
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 }

GtkWidget* ergtk_selection_group_new ( GtkToggleButton **  button_tbl  ) 

create a new erGtkSelectionGroup object

Parameters:
button_tbl - NULL-terminated array of ptrs to the buttons that must be controlled
Returns:
reference to created widget

Definition at line 76 of file erGtkSelectionGroup.c.

00077 {
00078     GtkToggleButton** p_button;
00079 
00080     // check input
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     // create new object
00087     erGtkSelectionGroup* item = (erGtkSelectionGroup*) g_object_new(ERGTK_SELECTION_GROUP_TYPE, NULL);
00088 
00089     // add buttons to new object
00090     add_buttons(item, button_tbl);
00091     
00092     return GTK_WIDGET(item);
00093 }

Here is the call graph for this function:

gboolean ergtk_selection_group_set_details ( erGtkSelectionGroup item,
const guint  min_selected,
const guint  max_selected 
)

set selection details for the buttons controlled by the erGtkSelectionGroup object

Parameters:
item - the erGtkSelectionGroup object
min_selected - the minimum number of buttons that must be selected
max_selected - the maximum number of buttons that must be selected
enforce_min - enforce that at least min_selected buttons are selected
enforce_max - enforce that at most max_selected buttons are selected
Returns:
TRUE ok, FALSE error

Definition at line 238 of file erGtkSelectionGroup.c.

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     // chain to real method, which may be overloaded
00247     if (klass->set_details)
00248     {
00249         return klass->set_details(item, min_selected, max_selected);
00250     }
00251     else
00252     {
00253         return FALSE;  // not ok
00254     }
00255 }

static void on_button_toggled ( GtkToggleButton *  button,
gpointer  user_data 
) [static]

Definition at line 418 of file erGtkSelectionGroup.c.

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         // force button to the state conform our administration
00437         if (history_button)
00438         {
00439             // force button to state selected
00440             if (!active)
00441             {
00442                 g_timeout_add(500, on_delayed_select_button, (gpointer)button);
00443             }
00444         }
00445         else
00446         {
00447             // force button to state de-selected
00448             if (active)
00449             {
00450                 g_timeout_add(500, on_delayed_deselect_button, (gpointer)button);
00451             }
00452         }
00453     }
00454     else
00455     {
00456         // enforce min/max selected buttons
00457         if (active)
00458         {
00459             if (history_button == NULL)
00460             {
00461                 // remember we've seen this button selected
00462                 LOGPRINTF("add button [%p] to history", button);
00463                 item->history = g_slist_append(item->history, button);
00464                 history_length++;
00465 
00466                 // enforce max selected buttons
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                     // too many buttons selected, deselect the oldest selected button
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                     // Note: xx_set_active may call us again,
00478                     //       so make sure all item data has been updated before
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                 // we've seen this button active before, ignore
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                     // remember we've seen this button de-selected
00498                     item->history = g_slist_delete_link(item->history, history_button);
00499                 }
00500                 else
00501                 {
00502                     // too few buttons selected, refuse de-selecting this button
00503                     g_timeout_add(500, on_delayed_select_button, (gpointer)button);
00504                 }
00505             }
00506             else
00507             {
00508                 // we did not see this button active, ignore
00509             }
00510         }
00511     }
00512 
00513     // report selectionGroup has changed
00514     g_signal_emit(item, g_signals[SELECTION_UPDATE], 0, (gpointer)button);
00515 }

Here is the call graph for this function:

static gboolean on_delayed_deselect_button ( gpointer  user_data  )  [static]

Definition at line 531 of file erGtkSelectionGroup.c.

00532 {
00533     LOGPRINTF("entry: button [%p]", user_data);
00534     // TODO: check on dispose_has_run, but how to do this?
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;  // FALSE = don't call again
00543 }

static gboolean on_delayed_select_button ( gpointer  user_data  )  [static]

Definition at line 517 of file erGtkSelectionGroup.c.

00518 {
00519     LOGPRINTF("entry: button [%p]", user_data);
00520     // TODO: check on dispose_has_run, but how to do this?
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;  // FALSE = don't call again
00529 }

static void real_freeze_buttons ( erGtkSelectionGroup item,
const gboolean  freeze 
) [static]

Definition at line 335 of file erGtkSelectionGroup.c.

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 }

static GtkToggleButton * real_get_button ( erGtkSelectionGroup item,
const guint  button_id 
) [static]

Definition at line 365 of file erGtkSelectionGroup.c.

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 }

static void real_get_selected_buttons ( erGtkSelectionGroup item,
gint *  button_ids,
const guint  len 
) [static]

Definition at line 392 of file erGtkSelectionGroup.c.

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 }

static gboolean real_set_details ( erGtkSelectionGroup item,
const guint  min_selected,
const guint  max_selected 
) [static]

Definition at line 257 of file erGtkSelectionGroup.c.

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     // store settings in item
00274     item->min_selected = min_selected;
00275     item->max_selected = max_selected;
00276     
00277     // enforce button states to be within specified range
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         // find the next button after the newest button from history list
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         // and select this button
00296         // Note: xx_set_active calls signal handler who modifies item data,
00297         //       so make sure all item data has been updated before
00298         gtk_toggle_button_set_active(buttons_last_selected->data, TRUE);
00299     }
00300     while (num_selected > max_selected)
00301     {
00302         // remove the newest button from history list
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         // and deselect this button
00309         // Note: xx_set_active calls signal handler who modifies item data,
00310         //       so make sure all item data has been updated before
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;  // ok
00316 }


Variable Documentation

GtkEventBoxClass* g_parent_class = NULL [static]

Definition at line 40 of file erGtkSelectionGroup.c.

gint g_signals[LAST_SIGNAL] [static]

Definition at line 49 of file erGtkSelectionGroup.c.


Generated on Sun Dec 14 17:12:35 2008 by  doxygen 1.5.6