4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 1998 Damon Chaplin
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 # This gets information about object heirarchies and signals
24 # by compiling a small C program. CFLAGS and LDFLAGS must be
25 # set appropriately before running this script.
27 # NOTE: the lookup_signal_arg_names() function contains the argument names of
28 # standard GTK signal handlers. This may need to be updated for new
29 # GTK signals or Gnome widget signals.
30 # FIXME: this is only needed for undocumented signals, we should remove
31 # the list as its totally undermaintained anyway
35 push @INC, '@PACKAGE_DATA_DIR@';
36 require "gtkdoc-common.pl";
40 # name of documentation module
45 %optctl = (module => \$MODULE,
46 types => \$TYPES_FILE,
47 nogtkinit => \$NO_GTK_INIT,
48 'output-dir' => \$OUTPUT_DIR,
49 'version' => \$PRINT_VERSION,
50 'help' => \$PRINT_HELP);
52 GetOptions(\%optctl, "module=s", "types:s", "output-dir:s", "nogtkinit", "version", "help");
65 gtkdoc-scanobj version @VERSION@ - introspect gtk-objects
67 --module=MODULE_NAME Name of the doc module being parsed
68 --types=FILE The name of the file to store the types in
69 --output-dir=DIRNAME The directory where the results are stored
70 --version Print the version of this program
71 --help Print this help
76 $OUTPUT_DIR = $OUTPUT_DIR ? $OUTPUT_DIR : ".";
78 $TYPES_FILE = $TYPES_FILE ? $TYPES_FILE : "$OUTPUT_DIR/$MODULE.types";
80 open (TYPES, $TYPES_FILE) || die "Cannot open $TYPES_FILE: $!\n";
81 open (OUTPUT, ">$MODULE-scan.c") || die "Cannot open $MODULE-scan.c: $!\n";
83 my $old_signals_filename = "$OUTPUT_DIR/$MODULE.signals";
84 my $new_signals_filename = "$OUTPUT_DIR/$MODULE.signals.new";
85 my $old_hierarchy_filename = "$OUTPUT_DIR/$MODULE.hierarchy";
86 my $new_hierarchy_filename = "$OUTPUT_DIR/$MODULE.hierarchy.new";
87 my $old_args_filename = "$OUTPUT_DIR/$MODULE.args";
88 my $new_args_filename = "$OUTPUT_DIR/$MODULE.args.new";
90 # write a C program to scan the types
108 $ntypes = @types + 1;
117 GtkType object_types[$ntypes];
120 get_object_types (void)
126 print OUTPUT " object_types[i++] = $_ ();\n";
136 * This uses GTK type functions to output signal prototypes and the widget
140 /* The output files */
141 gchar *signals_filename = "$new_signals_filename";
142 gchar *hierarchy_filename = "$new_hierarchy_filename";
143 gchar *args_filename = "$new_args_filename";
146 static void output_signals (void);
147 static void output_widget_signals (FILE *fp,
148 GtkType object_type);
149 static void output_widget_signal (FILE *fp,
151 gchar *object_class_name,
153 static gchar * get_type_name (GtkType type,
154 gboolean * is_pointer);
155 static gchar * get_gdk_event (const gchar * signal_name);
156 static gchar ** lookup_signal_arg_names (gchar * type,
157 const gchar * signal_name);
159 static void output_widget_hierarchy (void);
160 static void output_hierarchy (FILE *fp,
164 static void output_args (void);
165 static void output_widget_args (FILE *fp, GtkType object_type);
168 main (int argc, char *argv[])
178 gtk_init (&argc, &argv);
186 output_widget_hierarchy ();
194 output_signals (void)
199 fp = fopen (signals_filename, "w");
202 g_warning ("Couldn't open output file: %s : %s", signals_filename, g_strerror(errno));
206 for (i = 0; object_types[i]; i++)
207 output_widget_signals (fp, object_types[i]);
213 /* This outputs all the signals of one widget. */
215 output_widget_signals (FILE *fp, GtkType object_type)
217 GtkObjectClass *class;
218 gchar *object_class_name;
221 class = gtk_type_class (object_type);
222 if (!class || class->nsignals == 0)
225 object_class_name = gtk_type_name (object_type);
227 for (sig = 0; sig < class->nsignals; sig++)
229 if (!class->signals[sig])
231 /*g_print ("Signal slot [%u] is empty\\n", sig);*/
235 output_widget_signal (fp, object_type, object_class_name,
236 class->signals[sig]);
241 /* This outputs one signal. */
243 output_widget_signal (FILE *fp,
248 GtkSignalQuery *query_info;
249 gchar *ret_type, *pos, *type_name, *arg_name, *object_arg, *object_arg_start;
254 gint param_num, widget_num, event_num, callback_num;
256 gchar signal_name[128];
258 /* g_print ("Object: %s Type: %i Signal: %u\\n", object_name, object_type,
262 widget_num = event_num = callback_num = 0;
264 query_info = gtk_signal_query (signal_id);
265 if (query_info == NULL)
267 g_warning ("Couldn't query signal");
271 /* Output the signal object type and the argument name. We assume the
272 type is a pointer - I think that is OK. We remove "Gtk" or "Gnome" and
273 convert to lower case for the argument name. */
275 sprintf (pos, "%s ", object_name);
278 /* Try to come up with a sensible variable name for the first arg
279 * I chops off 2 know prefixes :/ and makes the name lowercase
280 * It should replace lowercase -> uppercase with '_'
281 * see gtkdoc-scangobject.in for better algorithm
283 if (!strncmp (object_name, "Gtk", 3))
284 object_arg = object_name + 3;
285 else if (!strncmp (object_name, "Gnome", 5))
286 object_arg = object_name + 5;
288 object_arg = object_name;
290 object_arg_start = pos;
291 sprintf (pos, "*%s\\n", object_arg);
293 g_strdown (object_arg_start);
294 if (!strcmp (object_arg_start, "widget"))
297 /* Convert signal name to use underscores rather than dashes '-'. */
298 strncpy (signal_name, query_info->signal_name, 127);
299 signal_name[127] = '\\0';
300 for (i = 0; signal_name[i]; i++)
302 if (signal_name[i] == '-')
303 signal_name[i] = '_';
306 /* Output the signal parameters. */
307 arg_names = lookup_signal_arg_names (object_name, signal_name);
309 for (param = 0; param < query_info->nparams; param++)
313 sprintf (pos, "%s\\n", arg_names[param]);
318 type_name = get_type_name (query_info->params[param], &is_pointer);
320 /* Most arguments to the callback are called "arg1", "arg2", etc.
321 GdkWidgets are called "widget", "widget2", ...
322 GdkEvents are called "event", "event2", ...
323 GtkCallbacks are called "callback", "callback2", ... */
324 if (!strcmp (type_name, "GtkWidget"))
327 arg_num = &widget_num;
329 else if (!strcmp (type_name, "GdkEvent"))
331 type_name = get_gdk_event (signal_name);
333 arg_num = &event_num;
336 else if (!strcmp (type_name, "GtkCallback")
337 || !strcmp (type_name, "GtkCCallback"))
339 arg_name = "callback";
340 arg_num = &callback_num;
345 arg_num = ¶m_num;
347 sprintf (pos, "%s ", type_name);
350 if (!arg_num || *arg_num == 0)
351 sprintf (pos, "%s%s\\n", is_pointer ? "*" : " ", arg_name);
353 sprintf (pos, "%s%s%i\\n", is_pointer ? "*" : " ", arg_name,
362 /* Output the return type and function name. */
363 ret_type = get_type_name (query_info->return_val, &is_pointer);
366 "<SIGNAL>\\n<NAME>%s::%s</NAME>\\n<RETURNS>%s%s</RETURNS>\\n%s</SIGNAL>\\n\\n",
367 object_name, query_info->signal_name, ret_type, is_pointer ? "*" : "", buffer);
372 /* Returns the type name to use for a signal argument or return value, given
373 the GtkType from the signal info. It also sets is_pointer to TRUE if the
374 argument needs a '*' since it is a pointer. */
376 get_type_name (GtkType type, gboolean * is_pointer)
381 type_name = gtk_type_name (type);
393 case GTK_TYPE_DOUBLE:
394 case GTK_TYPE_POINTER:
395 /* These all have normal C type names so they are OK. */
398 case GTK_TYPE_STRING:
399 /* A GtkString is really a gchar*. */
405 /* We use a gint for both of these. Hopefully a subtype with a decent
406 name will be registered and used instead, as GTK+ does itself. */
410 /* A boxed value is just an opaque pointer, I think. */
413 case GTK_TYPE_SIGNAL:
415 case GTK_TYPE_FOREIGN:
416 case GTK_TYPE_CALLBACK:
417 case GTK_TYPE_C_CALLBACK:
418 /* FIXME: These are wrong. I think they expand into more than 1 argument.
419 See the GtkArg struct in gtktypeutils.h and gtkargcollector.c.
420 Fortunately I doubt anything uses these as signal args. */
427 /* For all GtkObject subclasses we can use the class name with a "*",
428 e.g. 'GtkWidget *'. */
429 if (gtk_type_is_a (type, GTK_TYPE_OBJECT))
437 get_gdk_event (const gchar * signal_name)
439 static gchar *GbGDKEvents[] =
441 "button_press_event", "GdkEventButton",
442 "button_release_event", "GdkEventButton",
443 "motion_notify_event", "GdkEventMotion",
444 "delete_event", "GdkEvent",
445 "destroy_event", "GdkEvent",
446 "expose_event", "GdkEventExpose",
447 "key_press_event", "GdkEventKey",
448 "key_release_event", "GdkEventKey",
449 "enter_notify_event", "GdkEventCrossing",
450 "leave_notify_event", "GdkEventCrossing",
451 "configure_event", "GdkEventConfigure",
452 "focus_in_event", "GdkEventFocus",
453 "focus_out_event", "GdkEventFocus",
454 "map_event", "GdkEvent",
455 "unmap_event", "GdkEvent",
456 "property_notify_event", "GdkEventProperty",
457 "selection_clear_event", "GdkEventSelection",
458 "selection_request_event", "GdkEventSelection",
459 "selection_notify_event", "GdkEventSelection",
460 "proximity_in_event", "GdkEventProximity",
461 "proximity_out_event", "GdkEventProximity",
462 "drag_begin_event", "GdkEventDragBegin",
463 "drag_request_event", "GdkEventDragRequest",
464 "drag_end_event", "GdkEventDragRequest",
465 "drop_enter_event", "GdkEventDropEnter",
466 "drop_leave_event", "GdkEventDropLeave",
467 "drop_data_available_event", "GdkEventDropDataAvailable",
468 "other_event", "GdkEventOther",
469 "client_event", "GdkEventClient",
470 "no_expose_event", "GdkEventNoExpose",
471 "visibility_notify_event", "GdkEventVisibility",
472 "window_state_event", "GdkEventWindowState",
473 "scroll_event", "GdkEventScroll",
479 for (i = 0; GbGDKEvents[i]; i += 2)
481 if (!strcmp (signal_name, GbGDKEvents[i]))
482 return GbGDKEvents[i + 1];
488 /* This returns argument names to use for some known GTK signals.
489 It is passed a widget name, e.g. 'GtkCList' and a signal name, e.g.
490 'select_row' and it returns a pointer to an array of argument types and
493 lookup_signal_arg_names (gchar * type, const gchar * signal_name)
495 /* Each arg array starts with the object type name and the signal name,
496 and then signal arguments follow. */
497 static gchar *GbArgTable[][16] =
499 {"GtkCList", "select_row",
502 "GdkEventButton *event"},
503 {"GtkCList", "unselect_row",
506 "GdkEventButton *event"},
507 {"GtkCList", "click_column",
510 {"GtkCList", "resize_column",
514 {"GtkCList", "extend_selection",
515 "GtkScrollType scroll_type",
517 "gboolean auto_start_selection"},
518 {"GtkCList", "scroll_vertical",
519 "GtkScrollType scroll_type",
521 {"GtkCList", "scroll_horizontal",
522 "GtkScrollType scroll_type",
524 {"GtkContainer", "focus",
525 "GtkDirectionType direction"},
526 {"GtkCTree", "tree_select_row",
529 {"GtkCTree", "tree_unselect_row",
533 {"GtkCTree", "tree_expand",
535 {"GtkCTree", "tree_collapse",
537 {"GtkCTree", "tree_move",
540 "GList *new_sibling"},
541 {"GtkCTree", "change_focus_row_expansion",
542 "GtkCTreeExpansionType expansion"},
544 {"GtkEditable", "insert_text",
546 "gint new_text_length",
548 {"GtkEditable", "delete_text",
551 {"GtkEditable", "set_editable",
552 "gboolean is_editable"},
553 {"GtkEditable", "move_cursor",
556 {"GtkEditable", "move_word",
558 {"GtkEditable", "move_page",
561 {"GtkEditable", "move_to_row",
563 {"GtkEditable", "move_to_column",
566 {"GtkEditable", "kill_char",
568 {"GtkEditable", "kill_word",
570 {"GtkEditable", "kill_line",
574 {"GtkInputDialog", "enable_device",
576 {"GtkInputDialog", "disable_device",
579 {"GtkListItem", "extend_selection",
580 "GtkScrollType scroll_type",
582 "gboolean auto_start_selection"},
583 {"GtkListItem", "scroll_vertical",
584 "GtkScrollType scroll_type",
586 {"GtkListItem", "scroll_horizontal",
587 "GtkScrollType scroll_type",
590 {"GtkMenuShell", "move_current",
591 "GtkMenuDirectionType direction"},
592 {"GtkMenuShell", "activate_current",
593 "gboolean force_hide"},
596 {"GtkNotebook", "switch_page",
597 "GtkNotebookPage *page",
599 {"GtkStatusbar", "text_pushed",
602 {"GtkStatusbar", "text_popped",
605 {"GtkTipsQuery", "widget_entered",
608 "gchar *tip_private"},
609 {"GtkTipsQuery", "widget_selected",
612 "gchar *tip_private",
613 "GdkEventButton *event"},
614 {"GtkToolbar", "orientation_changed",
615 "GtkOrientation orientation"},
616 {"GtkToolbar", "style_changed",
617 "GtkToolbarStyle style"},
618 {"GtkWidget", "draw",
619 "GdkRectangle *area"},
620 {"GtkWidget", "size_request",
621 "GtkRequisition *requisition"},
622 {"GtkWidget", "size_allocate",
623 "GtkAllocation *allocation"},
624 {"GtkWidget", "state_changed",
625 "GtkStateType state"},
626 {"GtkWidget", "style_set",
627 "GtkStyle *previous_style"},
629 {"GtkWidget", "install_accelerator",
630 "gchar *signal_name",
634 {"GtkWidget", "add_accelerator",
635 "guint accel_signal_id",
636 "GtkAccelGroup *accel_group",
638 "GdkModifierType accel_mods",
639 "GtkAccelFlags accel_flags"},
641 {"GtkWidget", "parent_set",
642 "GtkObject *old_parent"},
644 {"GtkWidget", "remove_accelerator",
645 "GtkAccelGroup *accel_group",
647 "GdkModifierType accel_mods"},
648 {"GtkWidget", "debug_msg",
650 {"GtkWindow", "move_resize",
655 {"GtkWindow", "set_focus",
656 "GtkWidget *widget"},
658 {"GtkWidget", "selection_get",
659 "GtkSelectionData *data",
662 {"GtkWidget", "selection_received",
663 "GtkSelectionData *data",
666 {"GtkWidget", "drag_begin",
667 "GdkDragContext *drag_context"},
668 {"GtkWidget", "drag_end",
669 "GdkDragContext *drag_context"},
670 {"GtkWidget", "drag_data_delete",
671 "GdkDragContext *drag_context"},
672 {"GtkWidget", "drag_leave",
673 "GdkDragContext *drag_context",
675 {"GtkWidget", "drag_motion",
676 "GdkDragContext *drag_context",
680 {"GtkWidget", "drag_drop",
681 "GdkDragContext *drag_context",
685 {"GtkWidget", "drag_data_get",
686 "GdkDragContext *drag_context",
687 "GtkSelectionData *data",
690 {"GtkWidget", "drag_data_received",
691 "GdkDragContext *drag_context",
694 "GtkSelectionData *data",
703 for (i = 0; GbArgTable[i][0]; i++)
705 if (!strcmp (type, GbArgTable[i][0])
706 && !strcmp (signal_name, GbArgTable[i][1]))
707 return &GbArgTable[i][2];
713 /* This outputs the hierarchy of all widgets which have been initialized,
714 i.e. by calling their XXX_get_type() initialization function. */
716 output_widget_hierarchy (void)
720 fp = fopen (hierarchy_filename, "w");
723 g_warning ("Couldn't open output file: %s : %s", hierarchy_filename, g_strerror(errno));
726 output_hierarchy (fp, GTK_TYPE_OBJECT, 0);
731 /* This is called recursively to output the hierarchy of a widget. */
733 output_hierarchy (FILE *fp,
743 for (i = 0; i < level; i++)
745 fprintf (fp, "%s\\n", gtk_type_name (type));
747 list = gtk_type_children_types (type);
751 GtkType child = (GtkType) list->data;
752 output_hierarchy (fp, child, level + 1);
764 fp = fopen (args_filename, "w");
767 g_warning ("Couldn't open output file: %s : %s", args_filename, g_strerror(errno));
771 for (i = 0; object_types[i]; i++)
772 output_widget_args (fp, object_types[i]);
779 output_widget_args (FILE *fp, GtkType object_type)
781 GtkObjectClass *class;
782 gchar *object_class_name;
787 gchar flags[16], *pos;
789 class = gtk_type_class (object_type);
793 object_class_name = gtk_type_name (object_type);
795 args = gtk_object_query_args (class->type, &arg_flags, &n_args);
797 for (arg = 0; arg < n_args; arg++)
800 /* We use one-character flags for simplicity. */
801 if (arg_flags[arg] & GTK_ARG_READABLE)
803 if (arg_flags[arg] & GTK_ARG_WRITABLE)
805 if (arg_flags[arg] & GTK_ARG_CONSTRUCT)
807 if (arg_flags[arg] & GTK_ARG_CONSTRUCT_ONLY)
809 if (arg_flags[arg] & GTK_ARG_CHILD_ARG)
813 fprintf (fp, "<ARG>\\n<NAME>%s</NAME>\\n<TYPE>%s</TYPE>\\n<FLAGS>%s</FLAGS>\\n</ARG>\\n\\n",
814 args[arg].name, gtk_type_name (args[arg].type), flags);
824 # Compile and run our file
826 $CC = $ENV{CC} ? $ENV{CC} : "gcc";
827 $LD = $ENV{LD} ? $ENV{LD} : $CC;
828 $CFLAGS = $ENV{CFLAGS} ? $ENV{CFLAGS} : "";
829 $LDFLAGS = $ENV{LDFLAGS} ? $ENV{LDFLAGS} : "";
830 $RUN = $ENV{RUN} ? $ENV{RUN} : "";
833 if ($CC =~ /libtool/) {
834 $o_file = "$MODULE-scan.lo"
836 $o_file = "$MODULE-scan.o"
839 print "gtk-doc: Compiling scanner\n";
840 $command = "$CC $CFLAGS -c -o $o_file $MODULE-scan.c";
841 system($command) == 0 or die "Compilation of scanner failed: $!\n";
843 print "gtk-doc: Linking scanner\n";
844 $command = "$LD -o $MODULE-scan $o_file $LDFLAGS";
845 system($command) == 0 or die "Linking of scanner failed: $!\n";
847 print "gtk-doc: Running scanner $MODULE-scan\n";
848 # use sh -c to avoid terminating the whole run without seeing the error
849 system("sh -c \"$RUN ./$MODULE-scan\"") == 0 or die "Scan failed: $!\n";
851 unlink "./$MODULE-scan.c", "./$MODULE-scan.o", "./$MODULE-scan.lo", "./$MODULE-scan";
853 &UpdateFileIfChanged ($old_signals_filename, $new_signals_filename, 0);
854 &UpdateFileIfChanged ($old_hierarchy_filename, $new_hierarchy_filename, 0);
855 &UpdateFileIfChanged ($old_args_filename, $new_args_filename, 0);