#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] |
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.
anonymous enum |
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 };
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 }
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 }
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
item | - the erGtkSelectionGroup object | |
freeze | - TRUE force buttons in current state, FALSE allow user to change buttons |
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
item | - the erGtkSelectionGroup object | |
button_id | - id of the requested button as an index (0 ..) in button_tbl on .._new() |
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
item | - the erGtkSelectionGroup object |
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
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 |
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 }
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
button_tbl | - NULL-terminated array of ptrs to the buttons that must be controlled |
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 }
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
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 |
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 }
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 }
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.