1 /**
2 \brief
3 \include ../doc_includes/gnocl.dox
4 **/
5 
6 /*
7  * $Id: gnocl.c,v 1.45 2006-02-27 20:56:39 baum Exp $
8  *
9  * This file implements a Tcl interface to GTK+ and Gnome
10  *
11  * Copyright (c) 2001 - 2005 Peter G. Baum  http://www.dr-baum.net
12  *
13  * See the file "license.terms" for information on usage and redistribution
14  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
15  *
16  */
17 
18 
19 /* doc pages commands created within this module */
20 
21 /**
22 \page page_inventory gnocl::inventory
23 \htmlinclude inventory.html
24 **/
25 
26 /**
27 \page page_mainLoop gnocl::mainLoop
28 \htmlinclude mainLoop.html
29 **/
30 
31 #include "gnocl.h"
32 #include <ctype.h>
33 
34 static GHashTable *name2widgetList;
35 static const char idPrefix[] = "::gnocl::_WID";
36 
37 
38 /**
39 \brief      Convert contents of hash list to a glist
40 **/
hash_to_list(gpointer key,gpointer value,gpointer user_data)41 void hash_to_list ( gpointer key, gpointer value, gpointer user_data )
42 {
43 	GList **list = user_data;
44 
45 	*list = g_list_prepend ( *list, value );
46 }
47 
48 /**
49 \brief
50 **/
sorter(gconstpointer a,gconstpointer b)51 gint sorter ( gconstpointer a, gconstpointer b )
52 {
53 	return ( strcmp ( ( const gchar * ) a, ( const gchar * ) b ) );
54 }
55 
56 /**
57 \brief
58 */
simpleDestroyFunc(GtkWidget * widget,gpointer data)59 static void simpleDestroyFunc (
60 	GtkWidget *widget,
61 	gpointer data )
62 {
63 	const char *name = gnoclGetNameFromWidget ( widget );
64 	gnoclForgetWidgetFromName ( name );
65 	Tcl_DeleteCommand ( ( Tcl_Interp * ) data, ( char * ) name );
66 	g_free ( ( char * ) name );
67 }
68 
69 
70 /**
71 \brief
72 */
gnoclRegisterWidget(Tcl_Interp * interp,GtkWidget * widget,Tcl_ObjCmdProc * proc)73 int gnoclRegisterWidget ( Tcl_Interp *interp, GtkWidget *widget, Tcl_ObjCmdProc *proc )
74 {
75 	const char *name = gnoclGetAutoWidgetId();
76 	gnoclMemNameAndWidget ( name, widget );
77 
78 	/* remove object from hash table and clear the associated function */
79 	g_signal_connect_after ( G_OBJECT ( widget ), "destroy", G_CALLBACK ( simpleDestroyFunc ), interp );
80 
81 	if ( proc != NULL )
82 	{
83 		Tcl_CreateObjCommand ( interp, ( char * ) name, proc, widget, NULL );
84 	}
85 
86 	Tcl_SetObjResult ( interp, Tcl_NewStringObj ( name, -1 ) );
87 
88 	return TCL_OK;
89 }
90 
91 /**
92 \brief
93 */
gnoclChildNotPacked(const char * name,Tcl_Interp * interp)94 GtkWidget *gnoclChildNotPacked ( const char *name, Tcl_Interp *interp )
95 {
96 	GtkWidget *child = gnoclGetWidgetFromName ( name, interp );
97 
98 	if ( child == NULL )
99 		return NULL;
100 
101 	if ( gnoclAssertNotPacked ( child, interp, name ) )
102 		return NULL;
103 
104 	return child;
105 }
106 
107 /**
108 \brief
109 */
gnoclAssertNotPacked(GtkWidget * child,Tcl_Interp * interp,const char * name)110 int gnoclAssertNotPacked (	GtkWidget *child,	Tcl_Interp *interp,	const char *name )
111 {
112 	if ( gtk_widget_get_parent ( child ) != NULL )
113 	{
114 		if ( name && interp )
115 			Tcl_AppendResult ( interp, "window \"", name, "\" has already a parent.", ( char * ) NULL );
116 
117 		return 1;
118 	}
119 
120 	return 0;
121 }
122 
123 
124 /**
125 \brief
126 \notes      "char *" and not "const char *" because of a not very strict
127             handling of "const char *" in Tcl e.g. Tcl_CreateObjCommand
128 */
gnoclGetAutoWidgetId(void)129 char *gnoclGetAutoWidgetId ( void )
130 {
131 	static int no = 0;
132 	/*
133 	static char buffer[30];
134 	*/
135 
136 	char *buffer = g_new ( char, sizeof ( idPrefix ) + 15 );
137 	strcpy ( buffer, idPrefix );
138 
139 	/* with namespace, since the Id is also the widget command */
140 	sprintf ( buffer + sizeof ( idPrefix ) - 1, "%d", ++no );
141 
142 	return buffer;
143 }
144 
145 /**
146 \brief
147 */
148 /* -----------------
149    handle widget <-> name mapping
150 -------------------- */
gnoclGetWidgetFromName(const char * id,Tcl_Interp * interp)151 GtkWidget *gnoclGetWidgetFromName ( const char *id, Tcl_Interp *interp )
152 {
153 	GtkWidget *widget = NULL;
154 	int       n;
155 
156 	if ( strncmp ( id, idPrefix, sizeof ( idPrefix ) - 1 ) == 0
157 			&& ( n = atoi ( id + sizeof ( idPrefix ) - 1 ) ) > 0 )
158 	{
159 		widget = g_hash_table_lookup ( name2widgetList, GINT_TO_POINTER ( n ) );
160 	}
161 
162 	if ( widget == NULL && interp != NULL )
163 	{
164 		Tcl_AppendResult ( interp, "Unknown widget \"", id, "\".", ( char * ) NULL );
165 	}
166 
167 	return widget;
168 }
169 
170 /**
171 \brief      Returns the widget name associated with pointer
172 \note       This will not apply to objects created from xml UI decscriptions.
173 */
gnoclGetNameFromWidget(GtkWidget * widget)174 const char *gnoclGetNameFromWidget ( GtkWidget *widget )
175 {
176 	const char *name = g_object_get_data ( G_OBJECT ( widget ), "gnocl::name" );
177 
178 	if ( name == NULL && ( GTK_IS_TREE_VIEW ( widget ) || GTK_IS_TEXT_VIEW ( widget ) ) )
179 	{
180 		name = gnoclGetNameFromWidget ( gtk_widget_get_parent ( widget ) );
181 	}
182 
183 	return name;
184 }
185 
186 /**
187 \brief
188 */
gnoclMemNameAndWidget(const char * path,GtkWidget * widget)189 int gnoclMemNameAndWidget ( const char *path,  GtkWidget *widget )
190 {
191 	int n;
192 
193 	n = atoi ( path + sizeof ( idPrefix ) - 1 );
194 
195 	assert ( n > 0 );
196 	assert ( g_hash_table_lookup ( name2widgetList, GINT_TO_POINTER ( n ) ) == NULL );
197 	assert ( strncmp ( path, idPrefix, sizeof ( idPrefix ) - 1 ) == 0 );
198 
199 	/* memorize the name of the widget in the widget */
200 	g_object_set_data ( G_OBJECT ( widget ), "gnocl::name", ( char * ) path );
201 	g_hash_table_insert ( name2widgetList, GINT_TO_POINTER ( n ), widget );
202 
203 	return 0;
204 }
205 
206 /**
207 \brief
208 */
gnoclForgetWidgetFromName(const char * path)209 int gnoclForgetWidgetFromName ( const char *path )
210 {
211 	int n = atoi ( path + sizeof ( idPrefix ) - 1 );
212 	assert ( gnoclGetWidgetFromName ( path, NULL ) );
213 	assert ( strncmp ( path, idPrefix, sizeof ( idPrefix ) - 1 ) == 0 );
214 	assert ( n > 0 );
215 
216 	g_hash_table_remove ( name2widgetList, GINT_TO_POINTER ( n ) );
217 
218 	return 0;
219 }
220 
221 /**
222 \brief      Return a list of all names in the specific hash lists.
223 \since      0.9.94g
224 **/
gnoclGetInventory(ClientData data,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])225 const char *gnoclGetInventory ( ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj * const objv[] )
226 {
227 
228 	/*--------------------------------------*/
229 	static GnoclOption options[] =
230 	{
231 		{ NULL, GNOCL_STRING, NULL },
232 		{ NULL },
233 	};
234 
235 	static const char *cmds[] = { "widget", "pixBuf", NULL };
236 
237 	if ( gnoclGetCmdsAndOpts ( interp, cmds, options, objv, objc ) == TCL_OK )
238 	{
239 		return TCL_OK;
240 	}
241 
242 	/*--------------------------------------*/
243 
244 
245 	gint _i;
246 
247 
248 	enum optIdx { WidgetIdx, PixBufIdx };
249 	int idx;
250 
251 
252 	if ( Tcl_GetIndexFromObj ( interp, objv[1], cmds, "command", TCL_EXACT, &idx ) != TCL_OK )
253 	{
254 		Tcl_WrongNumArgs ( interp, 1, objv, "[widget | pixBuf]\n" );
255 		return TCL_ERROR;
256 	}
257 
258 	GList *list;
259 	GList *l;
260 	GString *str;
261 
262 
263 	str = g_string_new ( "" );
264 
265 	list = NULL;
266 
267 
268 	switch ( idx )
269 	{
270 		case WidgetIdx:
271 			{
272 				g_hash_table_foreach ( name2widgetList, hash_to_list, &list );
273 				list = g_list_sort ( list, sorter );
274 
275 				for ( l = list; l != NULL; l = l->next )
276 				{
277 					str = g_string_append ( str, gnoclGetNameFromWidget ( l->data ) );
278 					str = g_string_append ( str, " " );
279 				}
280 
281 			} break;
282 		case PixBufIdx:
283 			{
284 				gnoclGetPixBufList ( &list );
285 				list = g_list_sort ( list, sorter );
286 
287 				for ( l = list; l != NULL; l = l->next )
288 				{
289 					str = g_string_append ( str, gnoclGetNameFromPixBuf ( l->data ) );
290 					str = g_string_append ( str, " " );
291 				}
292 			} break;
293 		default: {} break;
294 	}
295 
296 	Tcl_SetResult ( interp, str->str, TCL_STATIC );
297 	return TCL_OK;
298 }
299 
300 /**
301 \brief
302 */
gnoclDelete(Tcl_Interp * interp,GtkWidget * widget,int objc,Tcl_Obj * const objv[])303 int gnoclDelete ( Tcl_Interp *interp, GtkWidget *widget, int objc, Tcl_Obj * const objv[] )
304 {
305 	if ( objc != 2 )
306 	{
307 		/* widget delete */
308 		Tcl_WrongNumArgs ( interp, 1, objv, "" );
309 		return TCL_ERROR;
310 	}
311 
312 	/* TODO: all children*/
313 	gtk_widget_destroy ( widget );
314 
315 	return TCL_OK;
316 }
317 
318 /**
319 \brief
320 \note
321    Prefix Explanation
322    %%     % plus rest of string as is
323    %!     rest of string as is
324    %#     stock text/icon/accelerator/... first '_' is underline character
325    %_     text with underline
326 
327    else:  use defaultStringPrefix
328 
329    FIXME: what about translation?
330 */
331 
gnoclGetStringType(Tcl_Obj * obj)332 GnoclStringType gnoclGetStringType ( Tcl_Obj *obj )
333 {
334 
335 	char *name = Tcl_GetString ( obj );
336 
337 	if ( *name == 0 )
338 	{
339 		return GNOCL_STR_EMPTY;
340 	}
341 
342 	assert ( GNOCL_STOCK_PREFIX[0] == '%' && GNOCL_STOCK_PREFIX[1] == '#' );
343 
344 	if ( *name == '%' )
345 	{
346 		/* if something is changed here, also change gnoclGetStringFromObj! */
347 		switch ( name[1] )
348 		{
349 			case '!':
350 			case '%': return GNOCL_STR_STR;
351 			case '#': return GNOCL_STR_STOCK | GNOCL_STR_UNDERLINE;
352 			case '/': return GNOCL_STR_FILE;
353 			case '_': return GNOCL_STR_UNDERLINE;
354 			case '<': return GNOCL_STR_MARKUP | GNOCL_STR_UNDERLINE;
355 			case '?': return GNOCL_STR_BUFFER;
356 		}
357 	}
358 
359 	return GNOCL_STR_STR;
360 }
361 
362 
363 /**
364 \brief
365 */
getEscStringFromObj(Tcl_Obj * op,int * len,int useEscape)366 static char *getEscStringFromObj ( Tcl_Obj *op, int *len, int useEscape )
367 {
368 
369 	char *ret;
370 
371 	if ( op == NULL )
372 	{
373 		return NULL;
374 	}
375 
376 
377 	ret = Tcl_GetStringFromObj ( op, len );
378 
379 
380 	if ( useEscape && *ret == '%' )
381 	{
382 
383 		/* if something is changed here, also change gnoclGetStringType! */
384 		switch ( ret[1] )
385 		{
386 			case '/':	/* GNOCL_STR_FILE */
387 			case '#':	/* GNOCL_STR_STOCK */
388 			case '?':	/* GNOCL_STR_BUFFER */
389 			case '_':	/* GNOCL_STR_UNDERLINE */
390 			case '<':	/* GNOCL_STR_MARKUP */
391 			case '!':	/* GNOCL_STR_STR */
392 				{
393 					ret += 2;
394 
395 					if ( len )
396 					{
397 						*len -= 2;
398 					}
399 				}
400 				break;
401 
402 			case '%': /* escaped % */
403 				{
404 					ret += 1;
405 
406 					if ( len )
407 					{
408 						*len -= 1;
409 					}
410 				}
411 				break;
412 
413 				/* translate
414 				case ???:   ret = _( ret + 2 );
415 				            if( len )   *len = strlen( ret );
416 				            break;
417 				*/
418 		}
419 
420 		// printf ( " -> %s\n", ret );
421 	}
422 
423 	return ret;
424 }
425 
426 /**
427 \brief Strip away percentage markup from string names.
428 */
gnoclGetStringFromObj(Tcl_Obj * op,int * len)429 char *gnoclGetStringFromObj ( Tcl_Obj *op,	int *len )
430 {
431 	/* TODO: global flag "gnoclNoStringEscape"? */
432 	return getEscStringFromObj ( op, len, 1 );
433 }
434 
435 /**
436 \brief
437 */
gnoclGetString(Tcl_Obj * op)438 char *gnoclGetString ( Tcl_Obj *op )
439 {
440 	return gnoclGetStringFromObj ( op, NULL );
441 }
442 
443 /**
444 \brief
445 */
gnoclStringDup(Tcl_Obj * op)446 char *gnoclStringDup ( Tcl_Obj *op )
447 {
448 	int txtLen;
449 	const char *txt;
450 
451 	if ( op == NULL )
452 		return NULL;
453 
454 	txt = gnoclGetStringFromObj ( op, &txtLen );
455 
456 	return g_memdup ( txt, txtLen + 1 );
457 }
458 
459 /**
460 \brief
461 */
gnoclGtkToStockName(const char * gtk)462 Tcl_Obj *gnoclGtkToStockName ( const char *gtk )
463 {
464 	Tcl_Obj *ret;
465 	GString *name;
466 
467 	assert ( strncmp ( gtk, "gtk-", 4 ) == 0 );
468 
469 	name = g_string_new ( NULL );
470 
471 	for ( gtk += 3; *gtk; ++gtk )
472 	{
473 		if ( *gtk == '-' )
474 		{
475 			++gtk;
476 			g_string_append_c ( name, toupper ( *gtk ) );
477 		}
478 
479 		else
480 			g_string_append_c ( name, *gtk );
481 	}
482 
483 	ret = Tcl_NewStringObj ( name->str, -1 );
484 
485 	g_string_free ( name, 1 );
486 	return ret;
487 }
488 
489 /**
490 \brief	Parse the markup string and create a notional stockitem name.
491 */
createStockName(const char * init,Tcl_Obj * obj)492 GString *createStockName ( const char *init, Tcl_Obj *obj )
493 {
494 	int len;
495 
496 	const char *name = getEscStringFromObj ( obj, &len, 1 );
497 
498 	GString *gtkName = g_string_new ( init );
499 	int   isFirst = 1;
500 
501 	/* we can use isupper and tolower since the IDs use only ASCII */
502 	/* "AbcDef" is changed to "init-abc-def" */
503 
504 	for ( ; *name; ++name )
505 	{
506 		if ( isupper ( *name ) 	|| ( isdigit ( *name ) && ( isFirst || !isdigit ( name[-1] ) ) ) )
507 		{
508 			g_string_append_c ( gtkName, '-' );
509 			g_string_append_c ( gtkName, tolower ( *name ) );
510 		}
511 
512 		else
513 			g_string_append_c ( gtkName, *name );
514 
515 		isFirst = 0;
516 	}
517 
518 	g_string_append_c ( gtkName, 0 );
519 
520 	return gtkName;
521 }
522 
523 /**
524 \brief
525 */
gnoclGetStockItem(Tcl_Obj * obj,Tcl_Interp * interp,GtkStockItem * sp)526 int gnoclGetStockItem ( Tcl_Obj *obj, Tcl_Interp *interp, GtkStockItem *sp )
527 {
528 
529 	GString *gtkName;
530 
531 	gtkName = createStockName ( "gtk", obj );
532 
533 
534 	if ( gtk_stock_lookup ( gtkName->str, sp ) == 0 )
535 	{
536 
537 		g_string_free ( gtkName, 1 );
538 		gtkName = createStockName ( "gnome-stock", obj );
539 
540 		if ( gtk_stock_lookup ( gtkName->str, sp ) == 0 )
541 		{
542 
543 			/* as last chance use the original string */
544 			int len;
545 			const char *name;
546 			g_string_free ( gtkName, 1 );
547 
548 			name = getEscStringFromObj ( obj, &len, 1 );
549 
550 			if ( gtk_stock_lookup ( name, sp ) == 0 )
551 			{
552 
553 				/* TODO? test the original string? */
554 				if ( interp != NULL )
555 				{
556 					Tcl_AppendResult ( interp, "unknown stock item \"", Tcl_GetString ( obj ), "\"", ( char * ) NULL );
557 				}
558 
559 				return TCL_ERROR;
560 			}
561 		}
562 	}
563 
564 	g_string_free ( gtkName, 1 );
565 
566 	return TCL_OK;
567 }
568 
569 /**
570 \brief
571 */
gnoclGetImage(Tcl_Interp * interp,Tcl_Obj * obj,GtkIconSize size,GtkWidget ** widget)572 int gnoclGetImage ( Tcl_Interp *interp, Tcl_Obj *obj, GtkIconSize size, GtkWidget **widget )
573 {
574 	GnoclStringType type = gnoclGetStringType ( obj );
575 
576 	if ( type == GNOCL_STR_EMPTY )
577 	{
578 		*widget = NULL;
579 	}
580 
581 	else if ( type & GNOCL_STR_STOCK )
582 	{
583 #if 0
584 		int     len;
585 		char    *txt = gnoclGetStringFromObj ( obj, &len );
586 		Tcl_Obj *obj2 = Tcl_NewStringObj ( txt, len );
587 		int     idx;
588 		int     ret = Tcl_GetIndexFromObj ( interp, obj2, opts, "option", TCL_EXACT, &idx );
589 		Tcl_DecrRefCount ( obj2 );
590 
591 		if ( ret != TCL_OK )
592 		{
593 			return ret;
594 		}
595 
596 		*widget = gnome_stock_pixmap_widget ( window, opts[idx] );
597 
598 #endif
599 		GtkStockItem sp;
600 
601 		if ( gnoclGetStockItem ( obj, interp, &sp ) != TCL_OK )
602 		{
603 			return TCL_ERROR;
604 		}
605 
606 		*widget = gtk_image_new_from_stock ( sp.stock_id, size );
607 
608 		if ( *widget == NULL )
609 		{
610 			Tcl_AppendResult ( interp, "Unknown stock pixmap \"", sp.stock_id, "\".", ( char * ) NULL );
611 			return TCL_ERROR;
612 		}
613 	}
614 
615 	else if ( type == GNOCL_STR_FILE )
616 	{
617 		char   *txt = gnoclGetStringFromObj ( obj, NULL );
618 #ifdef GNOCL_USE_GNOME
619 		fprintf ( stderr,
620 				  "gtk_image_new_from_file seems to hang with gnome support.\n"
621 				  "Please don't use menu icons if you need gnome support or\n"
622 				  "comment the line USE_GNOME in the makefile to \n"
623 				  "disable gnome support.\n"
624 				  "If this works for you *with* USE_GNOME please mail your\n"
625 				  "GTK+ and Gnome versions to peter@dr-baum.net.\n"
626 				  "Thank you!\n" );
627 #endif
628 		*widget = gtk_image_new_from_file ( txt );
629 	}
630 
631 	else if ( type == GNOCL_STR_BUFFER )
632 	{
633 		GdkPixbuf *pixbuf = NULL;
634 
635 		char   *txt = gnoclGetStringFromObj ( obj, NULL );
636 #ifdef GNOCL_USE_GNOME
637 		g_print ( "got a buffer = %s\n", txt );
638 #endif
639 		pixbuf = gnoclGetPixBufFromName ( txt, interp );
640 		*widget = gtk_image_new_from_file ( pixbuf );
641 	}
642 
643 	else
644 	{
645 		Tcl_AppendResult ( interp, "invalid pixmap type for \"", Tcl_GetString ( obj ), "\"", ( char * ) NULL );
646 		return TCL_ERROR;
647 	}
648 
649 	return TCL_OK;
650 }
651 
652 /**
653 \note   This block needs to be placed into a speparate include file so
654         that it can appended to by a module creation wizard.
655 **/
656 
657 typedef struct
658 {
659 	char *name;
660 	Tcl_ObjCmdProc *proc;
661 } GnoclCmd;
662 
663 
664 static GnoclCmd commands[] =
665 {
666 
667 	{ "commands", gnoclCommandsCmd },
668 
669 	/* non-gtk widgets */
670 	{ "spinner", gnoclSpinnerCmd },
671 	{ "dial", gnoclDialCmd },
672 //	{ "level", gnoclLevelCmd },
673 	{ "tickerTape", gnoclTickerTapeCmd },
674 	{ "richTextToolBar", gnoclRichTextToolBarCmd },
675 
676 	/* recent manager */
677 	{ "recentManager", gnoclRecentManagerCmd},
678 
679 	/* gnocl only widget */
680 	{ "splashScreen", gnoclSplashScreenCmd },
681 	{ "debug", gnoclDebugCmd },
682 	{ "callback", gnoclCallbackCmd },
683 	{ "clipboard", gnoclClipboardCmd },
684 	{ "configure", gnoclConfigureCmd },
685 	{ "info", gnoclInfoCmd },
686 	{ "mainLoop", gnoclMainLoop },
687 	{ "update", gnoclUpdateCmd },
688 	{ "resourceFile", gnoclResourceFileCmd },
689 	{ "getStyle", gnoclGetStyleCmd },
690 	{ "setStyle", gnoclSetStyleCmd },
691 	{ "winfo", gnoclWinfoCmd },
692 	{ "print", gnoclPrintCmd },
693 	{ "pixBuf", gnoclPixBufCmd },
694 	{ "pixMap", gnoclPixMapCmd },
695 
696 	//{ "cairo", gnoclCairoCmd }, /* moved into own package */
697 
698 	/* colour convertion commands */
699 	{ "clr2rgb", gnoclClr2RGBCmd },
700 	{ "rgb2hex", gnoclRGB2HexCmd },
701 	{ "parseColor", gnoclParseColorCmd },
702 	{ "screen", gnoclScreenCmd},
703 
704 	/* Gtk Widgets */
705 	{ "button", gnoclButtonCmd },
706 	{ "box", gnoclBoxCmd },
707 	{ "hBox", gnoclHBoxCmd },
708 	{ "vBox", gnoclVBoxCmd },
709 	{ "fixed", gnoclFixedCmd },
710 	{ "checkButton", gnoclCheckButtonCmd },
711 //	{ "clock", gnoclClockCmd },
712 	{ "colorSelection", gnoclColorSelectionCmd },
713 	{ "colorSelectionDialog", gnoclColorSelectionDialogCmd },
714 	{ "colorWheel", gnoclColorWheelCmd },
715 	{ "combo", gnoclComboCmd },
716 	{ "dialog", gnoclDialogCmd },
717 	{ "entry", gnoclEntryCmd },
718 	{ "eventBox", gnoclEventBoxCmd },
719 	{ "fileSelection", gnoclFileSelectionCmd },
720 	{ "fontSelection", gnoclFontSelectionCmd },
721 	{ "image", gnoclImageCmd },
722 	{ "label", gnoclLabelCmd },
723 	{ "list", gnoclListCmd },
724 	{ "listStore", gnoclListCmd },
725 	{ "menu", gnoclMenuCmd },
726 	{ "menuBar", gnoclMenuBarCmd },
727 	{ "menuItem", gnoclMenuItemCmd },
728 	{ "menuCheckItem", gnoclMenuCheckItemCmd },
729 	{ "menuRadioItem", gnoclMenuRadioItemCmd },
730 	{ "menuRecentChooser", gnoclMenuRecentChooserCmd },
731 	{ "menuSeparator", gnoclMenuSeparatorCmd },
732 	{ "notebook", gnoclNotebookCmd },
733 	{ "optionMenu", gnoclOptionMenuCmd },
734 	{ "pageSetupDialog", gnoclPageSetupDialogCmd },
735 	{ "paned", gnoclPanedCmd },
736 	{ "plug", gnoclPlugCmd },
737 	{ "progressBar", gnoclProgressBarCmd },
738 
739 	/* move megawidgets into seprate package? */
740 	{ "labelEntry", gnoclLabelEntryCmd },
741 
742 
743 	{ "pBar2", gnoclPBarCmd },
744 
745 	{ "radioButton", gnoclRadioButtonCmd },
746 	{ "scale", gnoclScaleCmd },
747 	{ "scrolledWindow", gnoclScrolledWindowCmd },
748 	{ "separator", gnoclSeparatorCmd },
749 	{ "socket", gnoclSocketCmd },
750 	{ "spinButton", gnoclSpinButtonCmd },
751 	{ "statusBar", gnoclStatusBarCmd },
752 	{ "table", gnoclTableCmd },
753 	{ "text", gnoclTextCmd },
754 
755 	{ "textView", gnoclTextViewCmd },
756 
757 	{ "toolBar", gnoclToolBarCmd },
758 	{ "tree", gnoclTreeCmd },
759 	{ "treeStore", gnoclTreeCmd },
760 	{ "window", gnoclWindowCmd },
761 	{ "calendar", gnoclCalendarCmd },
762 	{ "curve", gnoclCurveCmd },
763 	{ "gammaCurve", gnoclGammaCurveCmd },
764 	{ "ruler", gnoclRulerCmd },
765 	{ "layout", gnoclLayoutCmd },
766 	{ "aspectFrame", gnoclAspectFrameCmd },
767 	{ "iconView", gnoclIconViewCmd },
768 	{ "infoBar", gnoclInfoBarCmd },
769 	{ "accelarator", gnoclAcceleratorCmd },
770 	{ "linkButton", gnoclLinkButtonCmd },
771 	{ "toggleButton", gnoclToggleButtonCmd },
772 	{ "scaleButton", gnoclScaleButtonCmd },
773 	{ "volumeButton", gnoclVolumeButtonCmd },
774 	{ "arrowButton", gnoclArrowButtonCmd },
775 	{ "recentChooser", gnoclRecentChooserCmd },
776 	{ "recentChooserDialog", gnoclRecentChooserDialogCmd },
777 	{ "fileChooserButton", gnoclFileChooserButtonCmd },
778 	{ "folderChooserButton", gnoclFolderChooserButtonCmd },
779 	{ "drawingArea", gnoclDrawingAreaCmd },
780 	{ "printerDialog", gnoclPrinterDialogCmd },
781 	{ "printDialog", gnoclPrintDialogCmd },
782 	{ "pageSetup", gnoclPageSetupCmd },
783 	{ "handleBox", gnoclHandleBoxCmd },
784 
785 	{ "toolPalette", gnoclToolPaletteCmd },
786 	{ "toolItemGroup", gnoclToolItemGroupCmd },
787 
788 	/* implement extra textbuffers */
789 	{ "textBuffer", gnoclTextBufferCmd },
790 	{ "assistant", gnoclAssistantCmd },
791 
792 	/* miscellaneous funcs */
793 	{ "signalStop", gnoclSignalStopCmd },
794 	{ "pointer", gnoclPointerCmd },
795 	{ "exec", gnoclExecCmd },
796 	{ "toggle", gnoclToggleCmd },
797 	{ "setProperty", gnoclSetPropertyCmd },
798 	{ "setOpts", gnoclSetOpts},
799 	{ "showURI", gnoclShowUriCmd },
800 	{ "signalEmit", gnoclSignalEmitCmd },
801 	{ "beep", gnoclBeepCmd },
802 	{ "Hsv2Rgb", gnoclHsv2RgbCmd },
803 //	{ "string", gnoclStringCmd },
804 	{ "inventory", gnoclGetInventory },
805 	{ "stockItem", gnoclStockItemCmd },
806 	{ "bind", gnoclBindCmd },
807 	{ "colorButton", gnoclColorButtonCmd },
808 	{ "comboBox", gnoclComboBoxCmd },
809 	{ "comboEntry", gnoclComboEntryCmd },
810 	{ "expander", gnoclExpanderCmd },
811 	{ "fileChooser", gnoclFileChooserCmd },
812 	{ "fileChooserDialog", gnoclFileChooserDialogCmd },
813 	{ "folderChooserDialog", gnoclFileChooserDialogCmd },
814 	{ "fontSelectionDialog", gnoclFontSelectionDialogCmd },
815 	{ "fontButton", gnoclFontButtonCmd },
816 	{ "aboutDialog", gnoclAboutDialogCmd },
817 	{ "keyFile", gnoclKeyFileCmd},
818 	{ "tooltip", gnoclToolTip},
819 
820 	{ "grab", gnoclGrabCmd},
821 
822 #ifdef GNOCL_USE_GNOME
823 	{ "app", gnoclAppCmd },
824 	{ "appBar", gnoclAppBarCmd },
825 #endif
826 	{ NULL, NULL },
827 };
828 
829 /**
830 \brief
831 \author
832 \date
833 \note
834 **/
getCommandsNames(Tcl_Interp * interp)835 int getCommandsNames ( Tcl_Interp *interp )
836 {
837 
838 	GnoclCmd *cmds = commands;
839 
840 	for ( ; cmds->name; ++cmds )
841 	{
842 		Tcl_AppendResult ( interp, cmds->name, " ", NULL );
843 	}
844 
845 	return TCL_OK;
846 }
847 
848 /*
849  *----------------------------------------------------------------------
850  *
851  * eventSetupProc --
852  *
853  *      This procedure implements the setup part of the gtk
854  *      event source.  It is invoked by Tcl_DoOneEvent before entering
855  *      the notifier to check for events on all displays.
856  *
857  * Results:
858  *      None.
859  *
860  * Side effects:
861  *      ??? TODO
862  *      the maximum
863  *      block time will be set to 0 to ensure that the notifier returns
864  *      control to Tcl even if there is no more data on the X connection.
865  *
866  *----------------------------------------------------------------------
867  */
eventSetupProc(ClientData clientData,int flags)868 static void eventSetupProc ( ClientData clientData, int flags )
869 {
870 	/*
871 	   TODO: make blockTime configurable
872 	         via Tcl_GetVar( "::gnocl::blockTime" )
873 	*/
874 	Tcl_Time blockTime = { 0, 10000 };
875 
876 	if ( ! ( flags & TCL_WINDOW_EVENTS ) )
877 	{
878 		/* under which circumstances do we get here? */
879 		return;
880 	}
881 
882 	/*
883 	   is this the correct way?
884 	   polling is bad, but how to do better?
885 	*/
886 	Tcl_SetMaxBlockTime ( &blockTime );
887 
888 	return;
889 }
890 
891 
892 /*
893  *----------------------------------------------------------------------
894  *
895  * eventCheckProc --
896  *
897  *      This procedure checks for events sitting in the gtk event
898  *      queue.
899  *
900  * Results:
901  *      None.
902  *
903  * Side effects:
904  *      Moves queued events onto the Tcl event queue.
905  *
906  *----------------------------------------------------------------------
907  */
908 
909 typedef struct
910 {
911 	Tcl_Event header;      /* Standard information for all events. */
912 	GdkEvent event;
913 } GnoclEvent;
914 
915 
916 /**
917 \brief
918 \note
919  *----------------------------------------------------------------------
920  *
921  * eventProc --
922  *
923  *      This procedure is called by Tcl_DoOneEvent when a window event
924  *      reaches the front of the event queue.  This procedure is responsible
925  *      for actually handling the event.
926  *
927  * Results:
928  *      Returns 1 if the event was handled, meaning it should be removed
929  *      from the queue.  Returns 0 if the event was not handled, meaning
930  *      it should stay on the queue.  The event isn't handled if the
931  *      TCL_WINDOW_EVENTS bit isn't set in flags, if a restrict proc
932  *      prevents the event from being handled.
933  *
934  * Side effects:
935  *      Whatever the event handlers for the event do.
936  *
937  *----------------------------------------------------------------------
938  */
eventProc(Tcl_Event * evPtr,int flags)939 static int eventProc ( Tcl_Event *evPtr, int flags )
940 {
941 	/*
942 	   with while loop more responsive than
943 	   with Tcl_SetMaxBlockTime( 0 );
944 	   is this the right way?
945 	*/
946 	/* assert( gtk_events_pending() );
947 	   This is not true, since between eventCheckProc and eventProc
948 	   the event can have been handled by the gtk event loop if we
949 	   are in mainLoop
950 	*/
951 	gtk_main_iteration_do ( 0 );
952 	return 1;
953 }
954 
955 /**
956 \brief
957 */
eventCheckProc(ClientData clientData,int flags)958 static void eventCheckProc ( ClientData clientData, int flags )
959 {
960 	/* FIXME: what to do with flags? */
961 	if ( gtk_events_pending() )
962 	{
963 		/* Tcl_Time blockTime = { 0, 0 }; */
964 		GnoclEvent *gp = ( GnoclEvent * ) ckalloc ( sizeof ( GnoclEvent ) );
965 		gp->header.proc = eventProc;
966 		Tcl_QueueEvent ( ( Tcl_Event * ) gp, TCL_QUEUE_TAIL );
967 
968 		/* Tcl_SetMaxBlockTime( &blockTime ); */
969 
970 		/* gp->event = *gtk_get_current_event( ); */
971 	}
972 
973 	return;
974 }
975 
976 /**
977 \brief
978 **/
979 #ifdef GNOCL_USE_GNOME
980 static GnomeAppBar *gnoclAppBar = NULL;
gnoclRegisterHintAppBar(GtkWidget * widget,GnomeAppBar * appBar)981 int gnoclRegisterHintAppBar (
982 	GtkWidget *widget,
983 	GnomeAppBar *appBar )
984 {
985 	gnoclAppBar = appBar;
986 	return 0;
987 }
988 
gnoclGetHintAppBar(GtkWidget * widget)989 GnomeAppBar *gnoclGetHintAppBar ( GtkWidget *widget )
990 {
991 	if ( gnoclAppBar != NULL )
992 	{
993 		GtkWidget *top = gtk_widget_get_toplevel ( GTK_WIDGET ( gnoclAppBar ) );
994 		GtkWidget *wTop = widget->parent;
995 
996 		/*
997 		if( top == wTop )
998 		   return gnoclAppBar;
999 		else
1000 		*/
1001 
1002 		while ( wTop != NULL && GTK_CHECK_TYPE ( wTop, GTK_TYPE_MENU ) )
1003 		{
1004 			wTop = gtk_menu_get_attach_widget ( GTK_MENU ( wTop ) );
1005 			/* this happens for popup menus */
1006 
1007 			if ( wTop == NULL )
1008 				break;
1009 
1010 			if ( gtk_widget_get_toplevel ( wTop ) == top )
1011 				return gnoclAppBar;
1012 
1013 			wTop = wTop->parent;
1014 		}
1015 
1016 		printf ( "DEBUG: gnoclAppBar not found\n" );
1017 	}
1018 
1019 	return NULL;
1020 }
1021 
1022 /**
1023 \brief
1024 */
putHintInAppBar(GtkWidget * menuitem,gpointer data)1025 static void putHintInAppBar (
1026 	GtkWidget* menuitem,
1027 	gpointer data
1028 )
1029 {
1030 	/*
1031 	MenuLabelParams *para = (MenuLabelParams *)data;
1032 	const char *txt = gtk_object_get_data( GTK_OBJECT(menuitem),
1033 	      "gnocl_tooltip" );
1034 	*/
1035 	const char *txt = ( const char * ) data;
1036 	GnomeAppBar *appBar = gnoclGetHintAppBar ( GTK_WIDGET ( menuitem ) );
1037 
1038 	if ( appBar )
1039 		gnome_appbar_set_status ( appBar, txt );
1040 }
1041 
1042 /**
1043 \brief
1044 */
removeHintFromAppBar(GtkWidget * menuitem,gpointer data)1045 static void removeHintFromAppBar (
1046 	GtkWidget *menuitem,
1047 	gpointer data )
1048 {
1049 	GnomeAppBar *appBar = gnoclGetHintAppBar ( GTK_WIDGET ( data ) );
1050 
1051 	if ( appBar )
1052 		gnome_appbar_refresh ( appBar );
1053 }
1054 
1055 #endif
1056 
1057 /**
1058 \brief
1059 \todo   Update using new modules, current method deprecated since Gtk+ 2.12
1060 **/
gnoclGetTooltips()1061 GtkTooltips *gnoclGetTooltips( )
1062 {
1063 	static GtkTooltips *tooltips = NULL;
1064 
1065 	if ( tooltips == NULL )
1066 	{
1067 		tooltips = gtk_tooltips_new( );
1068 	}
1069 
1070 	return tooltips;
1071 }
1072 
1073 /**
1074 \brief
1075 **/
gnoclGetAccelGroup()1076 GtkAccelGroup *gnoclGetAccelGroup( )
1077 {
1078 	static GtkAccelGroup *accelGroup = NULL;
1079 
1080 	if ( accelGroup == NULL )
1081 	{
1082 		accelGroup = gtk_accel_group_new();
1083 	}
1084 
1085 	/* return gtk_accel_group_get_default( ); */
1086 	return accelGroup;
1087 }
1088 
1089 /**
1090 \brief
1091 **/
tclTimerFunc(gpointer data)1092 static gint tclTimerFunc ( gpointer data )
1093 {
1094 	/* Tcl_Interp *interp = (Tcl_Interp *)data; */
1095 	while ( Tcl_DoOneEvent ( TCL_DONT_WAIT ) )
1096 	{
1097 		/* printf( "t" ); fflush( stdout ); */
1098 		if ( gtk_events_pending() )
1099 		{
1100 			break;
1101 		}
1102 	}
1103 
1104 	/* TODO: or Tcl_ServiceAll(); ? */
1105 	return 1;
1106 }
1107 
1108 /**
1109 \brief
1110 **/
gnoclMainLoop(ClientData data,Tcl_Interp * interp,int objc,Tcl_Obj * const objv[])1111 int gnoclMainLoop (	ClientData data, Tcl_Interp *interp, int objc, Tcl_Obj * const objv[] )
1112 {
1113 
1114 	/*--------------------------------------*/
1115 	static GnoclOption options[] =
1116 	{
1117 		{ "-timeout", GNOCL_STRING, NULL },
1118 		{ NULL },
1119 	};
1120 
1121 	static const char *cmds[] =  { NULL, NULL};
1122 
1123 	if ( gnoclGetCmdsAndOpts ( interp, cmds, options, objv, objc ) == TCL_OK )
1124 	{
1125 		return TCL_OK;
1126 	}
1127 
1128 	/*--------------------------------------*/
1129 
1130 
1131 	guint32 timeout = 100;
1132 
1133 	/* TODO? flag for not looping in tclTimerFunc? */
1134 
1135 	if ( ( objc != 1 && objc != 3 ) || ( objc == 3 && strcmp ( Tcl_GetString ( objv[1] ), "-timeout" ) ) )
1136 	{
1137 		Tcl_WrongNumArgs ( interp, 1, objv, "?-timeout val?" );
1138 		return TCL_ERROR;
1139 	}
1140 
1141 	if ( objc == 3 )
1142 	{
1143 		long    val;
1144 
1145 		if ( Tcl_GetLongFromObj ( interp, objv[2], &val ) != TCL_OK )
1146 			return TCL_ERROR;
1147 
1148 		/* beware of problems with casting signed -> unsigned! */
1149 		if ( val < 0 )
1150 		{
1151 			Tcl_SetResult ( interp, "Timeout value must be greater or equal zero.", TCL_STATIC );
1152 			return TCL_ERROR;
1153 		}
1154 
1155 		timeout = ( guint32 ) val;
1156 	}
1157 
1158 	/* TODO
1159 	   this prevents the rekursive calls of tcl and gtk event loops
1160 	   but then we can't use "vwait var" if var is set in a gnocl::callback
1161 	Tcl_DeleteEventSource( eventSetupProc, eventCheckProc, interp );
1162 	*/
1163 
1164 	/* if timeout == 0 don't call tcl's event loop */
1165 
1166 	if ( timeout > 0 )
1167 	{
1168 		g_timeout_add ( timeout, tclTimerFunc, NULL );
1169 	}
1170 
1171 	/* TODO? use gtk_idle_add( tclTimerFunc, NULL ); ? */
1172 
1173 	gtk_main();
1174 
1175 	return 0;
1176 }
1177 
1178 /**
1179 \brief
1180 **/
gnoclGetArgv(Tcl_Interp * interp,int * pargc)1181 char **gnoclGetArgv (
1182 	Tcl_Interp *interp,
1183 	int *pargc )
1184 {
1185 	int  narg;
1186 	char **argv;
1187 	int k;
1188 
1189 	typedef char *Pchar;
1190 
1191 	Tcl_Obj *obj = Tcl_ObjGetVar2 ( interp, Tcl_NewStringObj ( "::argv", -1 ), NULL, 0 );
1192 
1193 	if ( obj == NULL || Tcl_ListObjLength ( NULL, obj, &narg ) != TCL_OK )
1194 	{
1195 		narg = 0;
1196 	}
1197 
1198 	argv = g_new ( Pchar, narg + 2 );
1199 
1200 	argv[0] = ( char * ) gnoclGetAppName ( interp );
1201 
1202 	for ( k = 0; k < narg; ++k )
1203 	{
1204 		Tcl_Obj *child;
1205 
1206 		if ( Tcl_ListObjIndex ( NULL, obj, k, &child ) == TCL_OK )
1207 		{
1208 			argv[k+1] = Tcl_GetString ( child );
1209 		}
1210 
1211 		else
1212 		{
1213 			argv[k+1] = "";
1214 		}
1215 	}
1216 
1217 	argv[narg+1] = NULL;
1218 
1219 	*pargc = narg + 1;
1220 
1221 	return argv;
1222 }
1223 
1224 /**
1225 \brief
1226 **/
gnoclGetAppName(Tcl_Interp * interp)1227 const char *gnoclGetAppName ( Tcl_Interp *interp )
1228 {
1229 	const char *ret = Tcl_GetVar ( interp, "gnocl::appName", TCL_GLOBAL_ONLY );
1230 
1231 	if ( ret == NULL )
1232 		ret = Tcl_GetVar ( interp, "argv0", TCL_GLOBAL_ONLY );
1233 
1234 	if ( ret == NULL )
1235 		ret = "gnocl";
1236 
1237 	return ret;
1238 }
1239 
1240 /**
1241 \brief
1242 **/
gnoclGetAppVersion(Tcl_Interp * interp)1243 const char *gnoclGetAppVersion ( Tcl_Interp *interp )
1244 {
1245 	const char *ret = Tcl_GetVar ( interp, "gnocl::appVersion",
1246 								   TCL_GLOBAL_ONLY );
1247 
1248 	if ( ret == NULL )
1249 		ret = VERSION;
1250 
1251 	return ret;
1252 }
1253 
1254 /**
1255 \brief
1256 **/
Gnocl_Init(Tcl_Interp * interp)1257 int Gnocl_Init ( Tcl_Interp *interp )
1258 {
1259 	char cmdBuf[128] = "gnocl::";
1260 	int k;
1261 	int argc;
1262 	char **argv;
1263 	char **argvp;
1264 
1265 	/* printf( "Initializing gnocl version %s\n", VERSION ); */
1266 
1267 	if ( Tcl_InitStubs ( interp, "8.3", 0 ) == NULL )
1268 	{
1269 		return TCL_ERROR;
1270 	}
1271 
1272 	/* TODO? set locale before every jump to Tcl? */
1273 	Tcl_PutEnv ( "LC_NUMERIC=C" );
1274 
1275 	argvp = argv = gnoclGetArgv ( interp, &argc );
1276 
1277 	if ( !gtk_init_check ( &argc, &argvp ) )
1278 	{
1279 		Tcl_SetResult ( interp, "could not initialize gtk", TCL_STATIC );
1280 		return TCL_ERROR;
1281 	}
1282 
1283 	/* TODO: change argv in Tcl */
1284 	g_free ( argv );
1285 
1286 	Tcl_CreateEventSource ( eventSetupProc, eventCheckProc, interp );
1287 
1288 	/* Tcl_CreateExitHandler( exitHandler, NULL ); */
1289 	if ( Tcl_PkgProvide ( interp, "Gnocl", VERSION ) != TCL_OK )
1290 		return TCL_ERROR;
1291 
1292 	/* Create command within the interpret to make new widgets available */
1293 	for ( k = 0; commands[k].name; ++k )
1294 	{
1295 
1296 		strcpy ( cmdBuf + 7, commands[k].name );
1297 		Tcl_CreateObjCommand ( interp, cmdBuf, commands[k]. proc, NULL, NULL );
1298 	}
1299 
1300 	/* initialize the hash table to contain list of pointers to named widgets */
1301 	name2widgetList = g_hash_table_new ( g_direct_hash, g_direct_equal );
1302 
1303 
1304 	/* FIXME: is there a more elegant way? */
1305 	/*        use gtk_idle_add( tclTimerFunc, NULL ); ? */
1306 	g_timeout_add ( 100, tclTimerFunc, NULL );
1307 	Tcl_SetMainLoop ( gtk_main );
1308 
1309 	return TCL_OK;
1310 }
1311 
1312 
1313