1 /*
2 * AiksaurusGTK - A GTK interface to the AikSaurus library
3 * Copyright (C) 2001 by Jared Davis
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18 * 02111-1307, USA.
19 */
20
21 #include "AiksaurusGTK_picbutton.h"
22 #include <iostream>
23 using namespace std;
24
25
26 //
27 // AiksaurusGTK_menudata class
28 // ---------------------------
29 // This is a hack job that allows us to pass more info through
30 // the gtk callback functions.
31 //
32 // See the comments in the menu functions below for an explanation of why
33 // this is here and what it is used for.
34 //
35 class AiksaurusGTK_menudata
36 {
37 public:
38
39 AiksaurusGTK_picbutton* d_picbutton_ptr;
40 GList* d_glist_ptr;
41
AiksaurusGTK_menudata()42 AiksaurusGTK_menudata() : d_picbutton_ptr(NULL), d_glist_ptr(NULL)
43 {
44
45 }
46 };
47
48
49
50
51 //////////////////////////////////////////////////////////////////////////
52 // //
53 // Creation and Menu Addition //
54 // //
55 //////////////////////////////////////////////////////////////////////////
56
AiksaurusGTK_picbutton(GtkWidget * window,const char * stock)57 AiksaurusGTK_picbutton::AiksaurusGTK_picbutton(GtkWidget *window, const char* stock)
58 {
59 d_window_ptr = window;
60
61 // Initialize with no menu stuff at all.
62 d_hasmenu = false;
63 d_menushowing = false;
64 d_menu_data_ptr = NULL;
65 d_menu_options_ptr = NULL;
66 d_menu_ptr = NULL;
67 d_numVisible = 0;
68
69 d_enabled = true;
70 d_hashover = false;
71 d_mouseover = false;
72
73 d_button_ptr = gtk_button_new();
74 gtk_widget_show(d_button_ptr);
75 gtk_widget_set_can_focus (d_button_ptr, false);
76
77 d_pixmap_ptr = gtk_image_new_from_icon_name(stock,GTK_ICON_SIZE_SMALL_TOOLBAR);
78
79 gtk_widget_show(d_pixmap_ptr);
80
81 gtk_container_add(
82 GTK_CONTAINER(d_button_ptr),
83 d_pixmap_ptr
84 );
85
86 d_hashover = true;
87
88 g_signal_connect(
89 G_OBJECT(d_button_ptr),
90 "enter",
91 G_CALLBACK(cbHover),
92 this
93 );
94
95 g_signal_connect(
96 G_OBJECT(d_button_ptr),
97 "leave",
98 G_CALLBACK(cbUnhover),
99 this
100 );
101
102 handleRelief();
103
104 }
105
106
~AiksaurusGTK_picbutton()107 AiksaurusGTK_picbutton::~AiksaurusGTK_picbutton()
108 {
109 // TO DO: what if this is null?
110 gtk_widget_destroy(d_menu_ptr);
111
112 if (d_menu_data_ptr != NULL)
113 delete[] d_menu_data_ptr;
114 }
115
116 GtkWidget*
getButton()117 AiksaurusGTK_picbutton::getButton()
118 {
119 return d_button_ptr;
120 }
121
122
123 GtkWidget*
getMenuButton()124 AiksaurusGTK_picbutton::getMenuButton()
125 {
126 return d_menu_button_ptr;
127 }
128
129
130
131 //////////////////////////////////////////////////////////////////////////
132 // //
133 // Enabling and Disabling Support //
134 // //
135 //////////////////////////////////////////////////////////////////////////
136
137 void
disable()138 AiksaurusGTK_picbutton::disable()
139 {
140 bool mousestate = d_mouseover;
141 d_mouseover = false;
142 handleRelief();
143 d_mouseover = mousestate;
144
145 d_enabled = false;
146
147 gtk_widget_set_sensitive(
148 d_button_ptr,
149 false
150 );
151
152 if (d_hasmenu)
153 {
154 gtk_widget_set_sensitive(
155 d_menu_button_ptr,
156 false
157 );
158 }
159 }
160
161
162 void
enable()163 AiksaurusGTK_picbutton::enable()
164 {
165 d_enabled = true;
166
167 gtk_widget_set_sensitive(
168 d_button_ptr,
169 true
170 );
171
172 if (d_hasmenu)
173 {
174 gtk_widget_set_sensitive(
175 d_menu_button_ptr,
176 true
177 );
178 }
179
180 handleRelief();
181 }
182
183
184
185
186
187 //////////////////////////////////////////////////////////////////////////
188 // //
189 // Hover Effect //
190 // //
191 //////////////////////////////////////////////////////////////////////////
192
193 void
handleRelief()194 AiksaurusGTK_picbutton::handleRelief()
195 {
196 const GtkReliefStyle off = GTK_RELIEF_NONE;
197 const GtkReliefStyle on = GTK_RELIEF_HALF;
198
199 GtkReliefStyle d_border_state = off;
200
201 if (!d_hashover)
202 {
203 d_border_state = on;
204 }
205
206 else
207 {
208 if (d_menushowing || d_mouseover)
209 {
210 d_border_state = on;
211 }
212 }
213
214 gtk_button_set_relief(
215 GTK_BUTTON(d_button_ptr),
216 d_border_state
217 );
218
219 if (d_hasmenu)
220 {
221 gtk_button_set_relief(
222 GTK_BUTTON(d_menu_button_ptr),
223 d_border_state
224 );
225 }
226 }
227
228
229 void
hover()230 AiksaurusGTK_picbutton::hover()
231 {
232 d_mouseover = true;
233 handleRelief();
234 }
235
236
237 void
unhover()238 AiksaurusGTK_picbutton::unhover()
239 {
240 d_mouseover = false;
241 handleRelief();
242 }
243
244
245
246 //////////////////////////////////////////////////////////////////////////
247 // //
248 // Drop-Down Menu //
249 // //
250 //////////////////////////////////////////////////////////////////////////
251
252 //
253 // GTK provides little or no support for what we're about to try to do.
254 //
255 // Our goal:
256 // Provide a convenient interface to manage a dynamic dropdown menu.
257
258
259
260 //
261 // popMenu: invoked when the menu-button is pressed.
262 // displays the menu to the user.
263 void
popMenu()264 AiksaurusGTK_picbutton::popMenu()
265 {
266 d_menushowing = true;
267
268 gtk_menu_popup(
269 GTK_MENU(d_menu_ptr),
270 NULL,
271 NULL,
272 cbPopupFunction,
273 this,
274 0,
275 0
276 );
277 }
278
279
280 //
281 // popupFunction: invoked by gtk_menu_popup in popMenu().
282 // Calculates the coordinates for the popup menu to appear at.
283 //
284 void
popupFunction(int * x,int * y)285 AiksaurusGTK_picbutton::popupFunction(int* x, int* y)
286 {
287 gdk_window_get_origin(gtk_widget_get_window (d_button_ptr), x, y);
288 (*y) += gtk_widget_get_allocated_height (d_button_ptr);
289 }
290
291
292 //
293 // selectionDone: invoked when the user has clicked off of the menu
294 // and the menu is no longer active. sets the relief of the main
295 // buttons back to non-hovering state.
296 //
297 void
selectionDone()298 AiksaurusGTK_picbutton::selectionDone()
299 {
300 d_menushowing = false;
301 handleRelief();
302 }
303
304
305 //
306 // cbMenuActivate: invoked when menu option is clicked.
307 // gpointer is a pointer-to-AiksaurusGTK_menudata (implemented at top of file).
308 // just call menuActivate on the correct picbutton.
309 //
310 void
cbMenuActivate(GtkMenuItem *,gpointer data)311 AiksaurusGTK_picbutton::cbMenuActivate(GtkMenuItem*, gpointer data)
312 {
313 static_cast<AiksaurusGTK_menudata*>(data)->d_picbutton_ptr->menuActivate(data);
314 }
315
316 //
317 // menuActivate: invoked by cbMenuActivate
318 // gpointer is pointer-to-AiksaurusGTK_menudata (implemented at top of file).
319 // call a callback function of the form
320 // void callback(GList* entry, gpointer data)
321 // where 'data' is user-data send to addMenu function.
322 //
323 typedef void (*AikCallbackFn)(gpointer, gpointer);
324
325 void
menuActivate(gpointer item)326 AiksaurusGTK_picbutton::menuActivate(gpointer item)
327 {
328 AiksaurusGTK_menudata* data = static_cast<AiksaurusGTK_menudata*>(item);
329 selectionDone();
330
331 AikCallbackFn f = (AikCallbackFn)d_onclick_function;
332 f(data->d_glist_ptr, d_onclick_data);
333 }
334
335
336 //
337 // addMenu function: invoked by user.
338 // add a menu button to the picbutton and prepare for running a menu.
339 //
340 void
addMenu(const AiksaurusGTK_strlist & options,GCallback onClick,gpointer onClickData)341 AiksaurusGTK_picbutton::addMenu
342 (const AiksaurusGTK_strlist& options, GCallback onClick, gpointer onClickData)
343 {
344 d_onclick_function = onClick;
345 d_onclick_data = onClickData;
346
347 d_hasmenu = true;
348
349 d_menu_button_ptr = gtk_button_new();
350 gtk_widget_show(d_menu_button_ptr);
351
352 gtk_widget_set_can_focus(d_menu_button_ptr, false);
353
354 d_menu_pixmap_widget_ptr = gtk_arrow_new(GTK_ARROW_DOWN,GTK_SHADOW_NONE);
355
356 gtk_widget_show(d_menu_pixmap_widget_ptr);
357
358 gtk_container_add(
359 GTK_CONTAINER(d_menu_button_ptr),
360 d_menu_pixmap_widget_ptr
361 );
362
363 g_signal_connect(
364 G_OBJECT(d_menu_button_ptr),
365 "enter",
366 G_CALLBACK(cbHover),
367 this
368 );
369
370 g_signal_connect(
371 G_OBJECT(d_menu_button_ptr),
372 "leave",
373 G_CALLBACK(cbUnhover),
374 this
375 );
376
377 handleRelief();
378
379 g_signal_connect(
380 G_OBJECT(d_menu_button_ptr),
381 "clicked",
382 G_CALLBACK(cbPopMenu),
383 this
384 );
385
386 menuCreate();
387
388 d_menu_options_ptr = const_cast<AiksaurusGTK_strlist*>(&options);
389 }
390
391
392 void
menuCreate()393 AiksaurusGTK_picbutton::menuCreate()
394 {
395 if (d_menu_ptr != NULL)
396 gtk_widget_destroy(d_menu_ptr);
397
398 d_menu_ptr = gtk_menu_new();
399
400 gtk_widget_show(d_menu_ptr);
401
402 g_signal_connect(
403 G_OBJECT(d_menu_ptr),
404 "selection-done",
405 G_CALLBACK(cbSelectionDone),
406 this
407 );
408
409 if (d_menu_data_ptr != NULL)
410 delete[] d_menu_data_ptr;
411
412 d_menu_data_ptr = NULL;
413 }
414
415
416 void
limitVisibleOptions(int numVisible)417 AiksaurusGTK_picbutton::limitVisibleOptions(int numVisible)
418 {
419 d_numVisible = numVisible;
420 }
421
422 void
updateMenuOptions()423 AiksaurusGTK_picbutton::updateMenuOptions()
424 {
425 menuCreate();
426
427 GList* itor = const_cast<GList*>(d_menu_options_ptr->list());
428
429 int i = 0;
430 d_menu_data_ptr = new AiksaurusGTK_menudata[ d_menu_options_ptr->size() ];
431
432 while(itor != NULL)
433 {
434 if (d_numVisible > 0)
435 {
436 if (i >= d_numVisible)
437 break;
438 }
439
440 d_menu_data_ptr[i].d_picbutton_ptr = this;
441 d_menu_data_ptr[i].d_glist_ptr = itor;
442
443 GtkWidget* option = gtk_menu_item_new_with_label(
444 static_cast<char*>(itor->data)
445 );
446
447 gtk_widget_show(option);
448
449 gtk_menu_shell_append (GTK_MENU_SHELL(d_menu_ptr), option);
450
451 g_signal_connect(
452 G_OBJECT(option),
453 "activate",
454 G_CALLBACK(cbMenuActivate),
455 &d_menu_data_ptr[i]
456 );
457
458 ++i;
459 itor = itor->next;
460 }
461 }
462
463
464 //////////////////////////////////////////////////////////////////////////
465 // //
466 // Callback Functions //
467 // //
468 //////////////////////////////////////////////////////////////////////////
469
cbHover(GtkWidget *,gpointer data)470 void AiksaurusGTK_picbutton::cbHover(GtkWidget*, gpointer data)
471 {
472 static_cast<AiksaurusGTK_picbutton*>(data)->hover();
473 }
474
cbUnhover(GtkWidget *,gpointer data)475 void AiksaurusGTK_picbutton::cbUnhover(GtkWidget*, gpointer data)
476 {
477 static_cast<AiksaurusGTK_picbutton*>(data)->unhover();
478 }
479
cbPopMenu(GtkWidget *,gpointer data)480 void AiksaurusGTK_picbutton::cbPopMenu(GtkWidget*, gpointer data)
481 {
482 static_cast<AiksaurusGTK_picbutton*>(data)->popMenu();
483 }
484
cbPopupFunction(GtkMenu *,int * x,int * y,gboolean *,gpointer data)485 void AiksaurusGTK_picbutton::cbPopupFunction(GtkMenu*, int* x, int* y, gboolean*, gpointer data)
486 {
487 static_cast<AiksaurusGTK_picbutton*>(data)->popupFunction(x, y);
488 }
489
cbSelectionDone(GtkMenuShell *,gpointer data)490 void AiksaurusGTK_picbutton::cbSelectionDone(GtkMenuShell*, gpointer data)
491 {
492 static_cast<AiksaurusGTK_picbutton*>(data)->selectionDone();
493 }
494
495