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