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