1 /*
2 * $Id: menu.c,v 1.4 2005/01/01 15:27:54 baum Exp $
3 *
4 * This file implements the menu widget
5 *
6 * Copyright (c) 2001 - 2005 Peter G. Baum http://www.dr-baum.net
7 *
8 * See the file "license.terms" for information on usage and redistribution
9 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
10 *
11 */
12
13 /*
14 History:
15 2013-07: added commands, options, commands
16 2012-09: added -data option
17 2008-10: added command, class
18 10: switched from GnoclWidgetOptions to GnoclOption
19 2002-04: updates for gtk 2.0
20 09: accelerator for menuItems
21 2001-03: Begin of developement
22 */
23
24 /*
25 TODO?: menu factory? But as tcl function!
26 */
27
28 /**
29 \page page_menu gnocl::menu
30 \htmlinclude menu.html
31 **/
32
33 #include "gnocl.h"
34
35 static GnoclOption menuOptions[] =
36 {
37 { "-children", GNOCL_LIST, NULL },
38 { "-tearoff", GNOCL_BOOL, NULL },
39 { "-title", GNOCL_STRING, "tearoff-title" },
40 { "-name", GNOCL_STRING, "name" },
41 { "-visible", GNOCL_BOOL, "visible" },
42 { "-sensitive", GNOCL_BOOL, "sensitive" },
43
44 /* GtkObject Properties */
45 { "-data", GNOCL_OBJ, "", gnoclOptData },
46 { NULL }
47 };
48
49
50 static const int childrenIdx = 0;
51 static const int tearoffIdx = 1;
52
53
54 /**
55 /brief Structure used to pass use script supplied values pointer positition to popup menu command.
56 **/
57
58 typedef struct
59 {
60 int x;
61 int y;
62 } MenuPositionData;
63
position_function(GtkMenu * menu,gint * x,gint * y,gboolean * push_in,gpointer user_data)64 static void position_function ( GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data )
65 {
66 /* Do not do this in your own code */
67
68 MenuPositionData *position_data = user_data;
69
70 if ( x )
71 {
72 *x = position_data->x;
73 }
74
75 if ( y )
76 {
77 *y = position_data->y;
78 }
79
80 if ( push_in )
81 {
82 *push_in = FALSE;
83 }
84 }
85
86
87 /**
88 \brief
89 **/
gnoclMenuShellAddChildren(Tcl_Interp * interp,GtkMenuShell * shell,Tcl_Obj * children,int atEnd)90 int gnoclMenuShellAddChildren ( Tcl_Interp *interp, GtkMenuShell *shell, Tcl_Obj *children, int atEnd )
91 {
92 int n, noChilds;
93
94 if ( Tcl_ListObjLength ( interp, children, &noChilds ) != TCL_OK || noChilds < 1 )
95 {
96 Tcl_SetResult ( interp, "GNOCL ERROR: Widget-list must be a list with at least one element", TCL_STATIC );
97 return TCL_ERROR;
98 }
99
100 for ( n = 0; n < noChilds; ++n )
101 {
102 GtkWidget *childWidget;
103 Tcl_Obj *tp;
104 const char *childName;
105
106 if ( Tcl_ListObjIndex ( interp, children, n, &tp ) != TCL_OK )
107 {
108 return TCL_ERROR;
109 }
110
111 childName = Tcl_GetString ( tp );
112
113 childWidget = gnoclChildNotPacked ( childName, interp );
114
115 if ( childWidget == NULL )
116 {
117 return TCL_ERROR;
118 }
119
120 if ( !GTK_CHECK_TYPE ( childWidget, GTK_TYPE_MENU_ITEM ) )
121 {
122 Tcl_AppendResult ( interp, "child window \"",
123 childName, "\" is not a menu item.", ( char * ) NULL );
124 return TCL_ERROR;
125 }
126
127 if ( atEnd )
128 {
129 gtk_menu_shell_append ( shell, childWidget );
130 }
131
132 else
133 {
134 gtk_menu_shell_prepend ( shell, childWidget );
135 }
136 }
137
138 return TCL_OK;
139 }
140
141
142 /**
143 \brief
144 **/
configure(Tcl_Interp * interp,GtkMenu * menu,GnoclOption options[])145 static int configure ( Tcl_Interp *interp, GtkMenu *menu, GnoclOption options[] )
146 {
147 if ( options[tearoffIdx].status == GNOCL_STATUS_CHANGED )
148 {
149 /* the tearoff widget is created on creation of the menu.
150 Here it is only hidden or shown */
151 GtkWidget *widget = GTK_WIDGET ( GTK_MENU_SHELL ( menu )->children->data );
152
153 if ( options[tearoffIdx].val.b )
154 {
155 gtk_widget_show ( widget );
156 }
157
158 else
159 {
160 gtk_widget_hide ( widget );
161 }
162 }
163
164 if ( options[childrenIdx].status == GNOCL_STATUS_CHANGED )
165 {
166 return gnoclMenuShellAddChildren ( interp, GTK_MENU_SHELL ( menu ), options[childrenIdx].val.obj, 1 );
167 }
168
169 return TCL_OK;
170 }
171
172
173 static const char *cmds[] =
174 {
175 "delete", "configure", "add",
176 "addBegin", "addEnd", "popup",
177 "popdown", "class",
178 NULL
179 };
180
181 /**
182 \brief
183 **/
menuFunc(ClientData data,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])184 int menuFunc ( ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj * const objv[] )
185 {
186
187
188 enum cmdIdx
189 {
190 DeleteIdx, ConfigureIdx, AddIdx,
191 BeginIdx, EndIdx, PopupIdx,
192 PopdownIdx, ClassIdx
193 };
194
195 GtkMenu *menu = GTK_MENU ( data );
196 int idx;
197
198 if ( objc < 2 )
199 {
200 Tcl_WrongNumArgs ( interp, 1, objv, "command" );
201 return TCL_ERROR;
202 }
203
204 if ( Tcl_GetIndexFromObj ( interp, objv[1], cmds, "command", TCL_EXACT, &idx ) != TCL_OK )
205 {
206 return TCL_ERROR;
207 }
208
209 switch ( idx )
210 {
211
212 case ClassIdx:
213 Tcl_SetObjResult ( interp, Tcl_NewStringObj ( "menu", -1 ) );
214 break;
215 case DeleteIdx:
216 return gnoclDelete ( interp, GTK_WIDGET ( menu ), objc, objv );
217
218 case ConfigureIdx:
219 {
220 int ret = TCL_ERROR;
221
222 if ( gnoclParseAndSetOptions ( interp, objc - 1, objv + 1, menuOptions, G_OBJECT ( menu ) ) == TCL_OK )
223 {
224 ret = configure ( interp, menu, menuOptions );
225 }
226
227 gnoclClearOptions ( menuOptions );
228
229 return ret;
230 }
231
232 break;
233
234 case AddIdx: /* AddIdx and EndIdx is the same */
235 case BeginIdx:
236 case EndIdx:
237 {
238 if ( objc != 3 )
239 {
240 Tcl_WrongNumArgs ( interp, 2, objv, "widget-list" );
241 return TCL_ERROR;
242 }
243
244 return gnoclMenuShellAddChildren (
245
246 interp,
247 GTK_MENU_SHELL ( menu ),
248 objv[2],
249 idx != BeginIdx );
250 }
251
252 case PopupIdx:
253 {
254 /* supply coordinates to accurately place the menu */
255
256 MenuPositionData position_data;
257 position_data.x = atoi ( Tcl_GetString ( objv[2] ) );
258 position_data.y = atoi ( Tcl_GetString ( objv[3] ) );
259
260 #ifdef DEBUG_MENU
261 g_print ( "POPUP x: %d y: %d\n", position_data.x, position_data.y );
262 #endif
263
264 if ( position_data.x != 0 && position_data.y != 0 )
265 {
266 gtk_menu_popup ( menu, NULL, NULL, position_function, &position_data, 0, 0 );
267 }
268
269 else
270 {
271 gtk_menu_popup ( menu, NULL, NULL, NULL, NULL, 0, 0 );
272 }
273 }
274 break;
275
276 case PopdownIdx:
277 #ifdef DEBUG_ME
278 g_print ( "POPDOWN x: %d y: %d\n", objv[3], objv[4] );
279 #endif
280 gtk_menu_popdown ( menu );
281 break;
282 }
283
284 return TCL_OK;
285 }
286
287
288 /**
289 \brief
290 **/
gnoclMenuCmd(ClientData data,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])291 int gnoclMenuCmd ( ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj * const objv[] )
292 {
293
294 if ( gnoclGetCmdsAndOpts ( interp, cmds, menuOptions, objv, objc ) == TCL_OK )
295 {
296 return TCL_OK;
297 }
298
299 int ret;
300 GtkMenu *menu;
301 GtkWidget *tearoff;
302
303 if ( gnoclParseOptions ( interp, objc, objv, menuOptions ) != TCL_OK )
304 {
305 gnoclClearOptions ( menuOptions );
306 return TCL_ERROR;
307 }
308
309 /* set tearoff to "on" as default */
310 menu = GTK_MENU ( gtk_menu_new() );
311
312 tearoff = gtk_tearoff_menu_item_new();
313
314 gtk_menu_shell_append ( GTK_MENU_SHELL ( menu ), tearoff );
315
316 gtk_widget_show ( tearoff );
317
318 gtk_widget_show ( GTK_WIDGET ( menu ) );
319
320 ret = gnoclSetOptions ( interp, menuOptions, G_OBJECT ( menu ), -1 );
321
322 if ( ret == TCL_OK )
323 ret = configure ( interp, menu, menuOptions );
324
325 gnoclClearOptions ( menuOptions );
326
327 if ( ret != TCL_OK )
328 {
329 gtk_widget_destroy ( GTK_WIDGET ( menu ) );
330 return TCL_ERROR;
331 }
332
333 return gnoclRegisterWidget ( interp, GTK_WIDGET ( menu ), menuFunc );
334 }
335
336