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