]> git.sur5r.net Git - glabels/blob - glabels2/src/bonobo-mdi.c
Initial revision
[glabels] / glabels2 / src / bonobo-mdi.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * bonobo-mdi.c - implementation of a BonoboMDI object
4  *
5  * Copyright (C) 2001-2002 Free Software Foundation
6  *
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.
11  *
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.
16  *
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., 59 Temple Place, Suite 330, 
20  * Boston, MA 02111-1307, USA.
21  *
22  * Author: Paolo Maggi 
23  */
24
25 /*
26  * Modified by the gedit Team, 2001-2002. See the AUTHORS file for a 
27  * list of people on the gedit Team.  
28  * See the ChangeLog files for a list of changes. 
29  */
30
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
34
35 #include "bonobo-mdi.h"
36
37 #include <bonobo/bonobo-i18n.h>
38 #include <marshal.h>
39 #include <bonobo/bonobo-dock-layout.h>
40 #include <bonobo/bonobo-ui-util.h>
41
42 #include "debug.h"
43
44 #include <string.h>
45 #include <time.h>
46 #include <unistd.h>
47
48 #define BONOBO_MDI_KEY          "BonoboMDI"
49 #define BONOBO_MDI_CHILD_KEY    "BonoboMDIChild"
50 #define UI_COMPONENT_KEY        "UIComponent"
51 #define BOOK_KEY                "Book"
52 #define WINDOW_INFO_KEY         "BonoboMDIWindowInfo"
53
54 static void            bonobo_mdi_class_init     (BonoboMDIClass  *);
55 static void            bonobo_mdi_instance_init  (BonoboMDI *);
56 static void            bonobo_mdi_finalize       (GObject *);
57 static void            child_list_menu_create    (BonoboMDI *, BonoboWindow *);
58
59 static void            child_list_activated_cb   (BonoboUIComponent *uic, gpointer user_data, 
60                                                   const gchar* verbname);
61
62 void                   child_list_menu_remove_item(BonoboMDI *, BonoboMDIChild *);
63 void                   child_list_menu_add_item   (BonoboMDI *, BonoboMDIChild *);
64
65 static void            app_create               (BonoboMDI *, gchar *, const char *);
66 static void            app_clone                (BonoboMDI *, BonoboWindow *, const char *);
67 static void            app_destroy              (BonoboWindow *, BonoboMDI *);
68 static void            app_set_view             (BonoboMDI *, BonoboWindow *, GtkWidget *);
69
70 static gboolean        app_close_book           (BonoboWindow *, GdkEventAny *, BonoboMDI *);
71
72 static GtkWidget      *book_create              (BonoboMDI *);
73 static void            book_switch_page         (GtkNotebook *, GtkNotebookPage *,
74                                                  gint, BonoboMDI *);
75 static gboolean        book_motion              (GtkWidget *widget, GdkEventMotion *e,
76                                                  gpointer data);
77 static gboolean        book_button_press        (GtkWidget *widget, GdkEventButton *e,
78                                                  gpointer data);
79 static gboolean        book_button_release      (GtkWidget *widget, GdkEventButton *e,
80                                                  gpointer data);
81 static void            book_add_view            (GtkNotebook *, GtkWidget *);
82 static void            set_page_by_widget       (GtkNotebook *, GtkWidget *);
83
84 static gboolean        toplevel_focus           (BonoboWindow *, GdkEventFocus *, BonoboMDI *);
85
86 static void            set_active_view          (BonoboMDI *, GtkWidget *);
87
88 /* convenience functions that call child's "virtual" functions */
89 static GtkWidget      *child_set_label         (BonoboMDIChild *, GtkWidget *);
90
91 static void            child_name_changed       (BonoboMDIChild *mdi_child, 
92                                                  gchar* old_name, 
93                                                  BonoboMDI *mdi);
94 static void            bonobo_mdi_update_child  (BonoboMDI *mdi, BonoboMDIChild *child);
95
96 static gchar*          escape_underscores       (const gchar* text);
97
98 static GtkWidget*      get_book_from_window     (BonoboWindow *window);
99
100 static GdkCursor *drag_cursor = NULL;
101
102 enum {
103         ADD_CHILD,
104         REMOVE_CHILD,
105         ADD_VIEW,
106         REMOVE_VIEW,
107         CHILD_CHANGED,
108         VIEW_CHANGED,
109         TOP_WINDOW_CREATED,
110         TOP_WINDOW_DESTROY,
111         ALL_WINDOWS_DESTROYED,
112         LAST_SIGNAL
113 };
114
115
116 struct _BonoboMDIPrivate
117 {
118         GtkPositionType tab_pos;
119
120         guint            signal_id;
121         gint             in_drag : 1;
122
123         gchar           *mdi_name; 
124         gchar           *title;
125
126         gchar           *ui_xml;
127         gchar           *ui_file_name;
128         BonoboUIVerb    *verbs;
129
130         /* Probably only one of these would do, but... redundancy rules ;) */
131         BonoboMDIChild  *active_child;
132         GtkWidget       *active_view;  
133         BonoboWindow    *active_window;
134
135         GList           *windows;       /* toplevel windows -  BonoboWindow widgets */
136         GList           *children;      /* children - BonoboMDIChild objects*/
137
138         GSList          *registered;    /* see comment for bonobo_mdi_(un)register() functions 
139                                          * below for an explanation. */
140         
141         /* Paths for insertion of mdi-child list menu via */
142         gchar           *child_list_path;
143
144         gint             default_window_height;
145         gint             default_window_width;
146 };
147
148 typedef gboolean   (*BonoboMDISignal1) (GObject *, gpointer, gpointer);
149 typedef void       (*BonoboMDISignal2) (GObject *, gpointer, gpointer);
150
151 static GObjectClass *parent_class = NULL;
152 static guint mdi_signals [LAST_SIGNAL] = { 0 };
153
154 GType
155 bonobo_mdi_get_type (void)
156 {
157         static GType bonobo_mdi_type = 0;
158
159         if (bonobo_mdi_type == 0)
160         {
161                 static const GTypeInfo our_info =
162                 {
163                         sizeof (BonoboMDIClass),
164                         NULL,           /* base_init */
165                         NULL,           /* base_finalize */
166                         (GClassInitFunc) bonobo_mdi_class_init,
167                         NULL,           /* class_finalize */
168                         NULL,           /* class_data */
169                         sizeof (BonoboMDI),
170                         0,              /* n_preallocs */
171                         (GInstanceInitFunc) bonobo_mdi_instance_init
172                 };
173
174                 bonobo_mdi_type = g_type_register_static (G_TYPE_OBJECT,
175                                                     "BonoboMDI",
176                                                     &our_info,
177                                                     0);
178         }
179
180         return bonobo_mdi_type;
181 }
182
183 static void 
184 bonobo_mdi_class_init (BonoboMDIClass *class)
185 {
186         GObjectClass *object_class;
187         
188         object_class = (GObjectClass*) class;
189         
190         object_class->finalize = bonobo_mdi_finalize;
191
192         mdi_signals[ADD_CHILD] = 
193                 g_signal_new ("add_child",
194                               G_OBJECT_CLASS_TYPE (object_class),
195                               G_SIGNAL_RUN_LAST,
196                               G_STRUCT_OFFSET (BonoboMDIClass, add_child),
197                               NULL, NULL,
198                               gl_marshal_BOOLEAN__OBJECT,
199                               G_TYPE_BOOLEAN, 
200                               1, 
201                               BONOBO_TYPE_MDI_CHILD);
202         
203         mdi_signals[REMOVE_CHILD] = 
204                 g_signal_new ("remove_child",
205                               G_OBJECT_CLASS_TYPE (object_class),
206                               G_SIGNAL_RUN_LAST,
207                               G_STRUCT_OFFSET (BonoboMDIClass, remove_child),
208                               NULL, NULL,
209                               gl_marshal_BOOLEAN__OBJECT,
210                               G_TYPE_BOOLEAN, 
211                               1, 
212                               BONOBO_TYPE_MDI_CHILD);
213         
214         mdi_signals[ADD_VIEW] = 
215                 g_signal_new ("add_view",
216                               G_OBJECT_CLASS_TYPE (object_class),
217                               G_SIGNAL_RUN_LAST,
218                               G_STRUCT_OFFSET (BonoboMDIClass, add_view),
219                               NULL, NULL,                            
220                               gl_marshal_BOOLEAN__OBJECT,
221                               G_TYPE_BOOLEAN, 
222                               1, 
223                               GTK_TYPE_WIDGET);
224         
225         mdi_signals[REMOVE_VIEW] = 
226                 g_signal_new ("remove_view",
227                               G_OBJECT_CLASS_TYPE (object_class),
228                               G_SIGNAL_RUN_LAST,
229                               G_STRUCT_OFFSET (BonoboMDIClass, remove_view),
230                               NULL, NULL,
231                               gl_marshal_BOOLEAN__OBJECT,
232                               G_TYPE_BOOLEAN, 
233                               1, 
234                               GTK_TYPE_WIDGET);
235
236         mdi_signals[CHILD_CHANGED] = 
237                 g_signal_new ("child_changed",
238                               G_OBJECT_CLASS_TYPE (object_class),
239                               G_SIGNAL_RUN_LAST,
240                               G_STRUCT_OFFSET (BonoboMDIClass, child_changed),
241                               NULL, NULL,
242                               g_cclosure_marshal_VOID__OBJECT,
243                               G_TYPE_NONE, 
244                               1, 
245                               BONOBO_TYPE_MDI_CHILD);
246
247         mdi_signals[VIEW_CHANGED] = 
248                 g_signal_new ("view_changed",
249                               G_OBJECT_CLASS_TYPE (object_class),
250                               G_SIGNAL_RUN_LAST,
251                               G_STRUCT_OFFSET(BonoboMDIClass, view_changed),
252                               NULL, NULL,
253                               g_cclosure_marshal_VOID__OBJECT,
254                               G_TYPE_NONE, 
255                               1, 
256                               GTK_TYPE_WIDGET);
257
258         mdi_signals[TOP_WINDOW_CREATED] = 
259                 g_signal_new ("top_window_created",
260                               G_OBJECT_CLASS_TYPE (object_class),
261                               G_SIGNAL_RUN_LAST,
262                               G_STRUCT_OFFSET (BonoboMDIClass, top_window_created),
263                               NULL, NULL,
264                               g_cclosure_marshal_VOID__OBJECT,
265                               G_TYPE_NONE, 
266                               1, 
267                               BONOBO_TYPE_WINDOW);
268
269         mdi_signals[TOP_WINDOW_DESTROY] = 
270                 g_signal_new ("top_window_destroy",
271                               G_OBJECT_CLASS_TYPE (object_class),
272                               G_SIGNAL_RUN_LAST,
273                               G_STRUCT_OFFSET (BonoboMDIClass, top_window_destroy),
274                               NULL, NULL,
275                               g_cclosure_marshal_VOID__OBJECT,
276                               G_TYPE_NONE, 
277                               1, 
278                               BONOBO_TYPE_WINDOW);
279
280         mdi_signals[ALL_WINDOWS_DESTROYED] = 
281                 g_signal_new ("all_windows_destroyed",
282                               G_OBJECT_CLASS_TYPE (object_class),
283                               G_SIGNAL_RUN_LAST,
284                               G_STRUCT_OFFSET (BonoboMDIClass, all_windows_destroyed),
285                               NULL, NULL,
286                               g_cclosure_marshal_VOID__VOID,
287                               G_TYPE_NONE, 
288                               0);
289
290         
291         class->add_child                = NULL;
292         class->remove_child             = NULL;
293         class->add_view                 = NULL;
294         class->remove_view              = NULL;
295         class->child_changed            = NULL;
296         class->view_changed             = NULL;
297         class->top_window_created       = NULL;
298
299         parent_class = gtk_type_class (G_TYPE_OBJECT);
300 }
301
302 static void 
303 bonobo_mdi_finalize (GObject *object)
304 {
305         BonoboMDI *mdi;
306
307         gl_debug (DEBUG_MDI, "");
308
309         g_return_if_fail (BONOBO_IS_MDI (object));
310         
311         mdi = BONOBO_MDI (object);
312         g_return_if_fail (mdi->priv != NULL);
313         
314         bonobo_mdi_remove_all (mdi, TRUE);
315
316         if (mdi->priv->child_list_path != NULL)
317                 g_free (mdi->priv->child_list_path);
318         
319         if (mdi->priv->mdi_name != NULL)
320                 g_free (mdi->priv->mdi_name);
321         
322         if (mdi->priv->title != NULL)
323                 g_free (mdi->priv->title);
324
325         if (mdi->priv->ui_xml != NULL)
326                 g_free (mdi->priv->ui_xml);
327         
328         if (mdi->priv->ui_file_name != NULL)
329                 g_free (mdi->priv->ui_file_name);
330         
331         g_free (mdi->priv);
332         mdi->priv = NULL;
333
334         if (G_OBJECT_CLASS (parent_class)->finalize)
335                 (* G_OBJECT_CLASS (parent_class)->finalize)(object);
336
337         gl_debug (DEBUG_MDI, "END");
338 }
339
340
341 static void 
342 bonobo_mdi_instance_init (BonoboMDI *mdi)
343 {
344         gl_debug (DEBUG_MDI, "");
345
346         g_return_if_fail (BONOBO_IS_MDI (mdi));
347
348         mdi->priv = g_new0 (BonoboMDIPrivate, 1);
349         g_return_if_fail (mdi->priv != NULL);
350                 
351         mdi->priv->tab_pos              = GTK_POS_TOP;
352                 
353         mdi->priv->signal_id            = 0;
354         mdi->priv->in_drag              = FALSE;
355
356         mdi->priv->children             = NULL;
357         mdi->priv->windows              = NULL;
358         mdi->priv->registered           = NULL;
359         
360         mdi->priv->active_child         = NULL;
361         mdi->priv->active_window        = NULL;
362         mdi->priv->active_view          = NULL;
363
364         mdi->priv->ui_xml               = NULL;
365         mdi->priv->ui_file_name         = NULL;
366         mdi->priv->verbs                = NULL; 
367         
368         mdi->priv->child_list_path      = NULL;
369
370         gl_debug (DEBUG_MDI, "END");
371 }
372
373
374 /**
375  * bonobo_mdi_new:
376  * @mdi_name: Application name as used in filenames and paths.
377  * @title: Title of the application windows.
378  * 
379  * Description:
380  * Creates a new MDI object. @mdi_name and @title are used for
381  * MDI's calling bonobo_window_new (). 
382  * 
383  * Return value:
384  * A pointer to a new BonoboMDI object.
385  **/
386 GObject*
387 bonobo_mdi_new (const gchar *mdi_name, const gchar *title,
388                 gint default_window_width, gint default_window_height) 
389 {
390         BonoboMDI *mdi;
391
392         gl_debug (DEBUG_MDI, "");
393         
394         mdi = g_object_new (BONOBO_TYPE_MDI, NULL);
395   
396         mdi->priv->mdi_name = g_strdup (mdi_name);
397         mdi->priv->title    = g_strdup (title);
398
399         mdi->priv->default_window_width = default_window_width;
400         mdi->priv->default_window_height = default_window_height;
401
402         gl_debug (DEBUG_MDI, "END");
403
404         return G_OBJECT (mdi);
405 }
406
407 static GtkWidget *
408 child_set_label (BonoboMDIChild *child, GtkWidget *label)
409 {
410         GtkWidget *w;
411         w = BONOBO_MDI_CHILD_GET_CLASS (child)->set_label (child, label, NULL);
412         
413         return w;
414 }
415
416 static void 
417 set_page_by_widget (GtkNotebook *book, GtkWidget *view)
418 {
419         gint i;
420         
421         i = gtk_notebook_page_num (book, view);
422         
423         if (gtk_notebook_get_current_page (book) != i)
424                 gtk_notebook_set_current_page (book, i);
425 }
426
427 static void 
428 child_list_activated_cb (BonoboUIComponent *uic, gpointer user_data, const gchar* verbname)
429 {
430         BonoboMDI* mdi;
431         BonoboMDIChild *child = BONOBO_MDI_CHILD (user_data);
432         g_return_if_fail (child != NULL);
433
434         gl_debug (DEBUG_MDI, "");
435
436         mdi = BONOBO_MDI (g_object_get_data (G_OBJECT (child), BONOBO_MDI_KEY));
437         g_return_if_fail (mdi != NULL);
438                 
439         if (child && (child != mdi->priv->active_child)) 
440         {
441                 GList *views = bonobo_mdi_child_get_views (child);
442                 
443                 if (views)
444                         bonobo_mdi_set_active_view (mdi, views->data);
445                 else
446                         bonobo_mdi_add_view (mdi, child);
447         }
448
449         gl_debug (DEBUG_MDI, "END");
450 }
451
452 static gchar* 
453 escape_underscores (const gchar* text)
454 {
455         GString *str;
456         gint length;
457         const gchar *p;
458         const gchar *end;
459
460         g_return_val_if_fail (text != NULL, NULL);
461
462         length = strlen (text);
463
464         str = g_string_new ("");
465
466         p = text;
467         end = text + length;
468
469         while (p != end)
470         {
471                 const gchar *next;
472                 next = g_utf8_next_char (p);
473
474                 switch (*p)
475                 {
476                         case '_':
477                                 g_string_append (str, "__");
478                                 break;
479                         default:
480                                 g_string_append_len (str, p, next - p);
481                                 break;
482                 }
483
484                 p = next;
485         }
486
487         return g_string_free (str, FALSE);
488 }
489
490 static void 
491 child_list_menu_create (BonoboMDI *mdi, BonoboWindow *win)
492 {
493         GList *child;
494         BonoboUIComponent *ui_component;
495
496         if (mdi->priv->child_list_path == NULL)
497                 return;
498         
499         ui_component = BONOBO_UI_COMPONENT (
500                         g_object_get_data (G_OBJECT (win), UI_COMPONENT_KEY));
501                         
502         child = mdi->priv->children;
503         
504         bonobo_ui_component_freeze (ui_component, NULL);
505         
506         while (child) 
507         {
508                 gchar *xml = NULL;
509                 gchar *cmd = NULL;
510                 gchar *verb_name = NULL;
511                 gchar *tip;
512                 gchar *escaped_name;
513                 gchar *safe_name;
514                 gchar *child_name = bonobo_mdi_child_get_name (BONOBO_MDI_CHILD (child->data));
515
516                 safe_name = g_markup_escape_text (child_name, strlen (child_name));
517                 g_return_if_fail (safe_name != NULL);
518
519                 escaped_name = escape_underscores (safe_name);
520                 g_return_if_fail (escaped_name != NULL);
521
522                 tip =  g_strdup_printf (_("Activate %s"), safe_name);
523                 verb_name = g_strdup_printf ("Child_%p", child->data);
524                 xml = g_strdup_printf ("<menuitem name=\"%s\" verb=\"%s\""
525                                 " _label=\"%s\"/>", verb_name, verb_name, escaped_name);
526                 cmd =  g_strdup_printf ("<cmd name = \"%s\" _label=\"%s\""
527                                 " _tip=\"%s\"/>", verb_name, escaped_name, tip);
528
529                 g_free (tip);
530                 g_free (child_name);
531                 g_free (safe_name);
532                 g_free (escaped_name);
533
534                 bonobo_ui_component_set_translate (ui_component, mdi->priv->child_list_path, xml, NULL);
535                 bonobo_ui_component_set_translate (ui_component, "/commands/", cmd, NULL);
536                 bonobo_ui_component_add_verb (ui_component, verb_name, child_list_activated_cb, child->data); 
537                 
538                 g_free (xml); 
539                 g_free (cmd);
540                 g_free (verb_name);
541
542                 child = g_list_next (child);
543         }
544
545         bonobo_ui_component_thaw (ui_component, NULL);
546 }
547
548
549 void 
550 child_list_menu_remove_item (BonoboMDI *mdi, BonoboMDIChild *child)
551 {
552         GList *win_node;
553         gchar *path, *cmd, *verb_name;
554         
555         gl_debug (DEBUG_MDI, "");
556
557         if(mdi->priv->child_list_path == NULL)
558                 return;
559         
560         win_node = mdi->priv->windows;
561
562         verb_name = g_strdup_printf ("Child_%p", child);
563         path = g_strdup_printf ("%s%s", mdi->priv->child_list_path, verb_name);
564         cmd = g_strdup_printf ("/commands/%s", verb_name);
565
566         while (win_node) 
567         {
568                 BonoboUIComponent *ui_component;
569                 
570                 ui_component = BONOBO_UI_COMPONENT (
571                         g_object_get_data (G_OBJECT (win_node->data), UI_COMPONENT_KEY));
572                 
573                 bonobo_ui_component_remove_verb (ui_component, verb_name);
574                 bonobo_ui_component_rm (ui_component, path, NULL);
575                 bonobo_ui_component_rm (ui_component, cmd, NULL);
576                 win_node = g_list_next (win_node);
577         }
578
579         g_free (path);
580         g_free (cmd);
581         g_free (verb_name);
582
583         gl_debug (DEBUG_MDI, "END");
584 }
585
586
587 void 
588 child_list_menu_add_item (BonoboMDI *mdi, BonoboMDIChild *child)
589 {
590         GList *win_node;
591         gchar *child_name, *escaped_name, *safe_name;
592         gchar* xml, *cmd, *verb_name, *tip;
593         int accel_num;
594         
595         if(mdi->priv->child_list_path == NULL)
596                 return;
597
598         accel_num = g_list_length (mdi->priv->children);
599         
600         win_node = mdi->priv->windows;
601
602         child_name = bonobo_mdi_child_get_name (child);
603
604         safe_name = g_markup_escape_text (child_name, strlen (child_name));
605         g_return_if_fail (safe_name != NULL);
606
607         escaped_name = escape_underscores (safe_name);
608         g_return_if_fail (escaped_name != NULL);
609
610         verb_name = g_strdup_printf ("Child_%p", child);
611         
612         tip = g_strdup_printf (_("Activate %s"), safe_name);
613         xml = g_strdup_printf ("<menuitem name=\"%s\" verb=\"%s\""
614                                 " _label=\"%s\"/>", verb_name, verb_name, escaped_name);
615
616         if (accel_num > 9)
617                 cmd =  g_strdup_printf ("<cmd name = \"%s\" _label=\"%s\""
618                                 " _tip=\"%s\"/>", verb_name, escaped_name, tip);
619         else
620                 cmd =  g_strdup_printf ("<cmd name = \"%s\" _label=\"%s\""
621                         " _tip=\"%s\" accel=\"*Alt*%d\"/>", verb_name,
622                         escaped_name, tip, accel_num);
623
624         
625         while (win_node) 
626         {
627                 BonoboUIComponent *ui_component;
628                 
629                 ui_component = BONOBO_UI_COMPONENT (
630                         g_object_get_data (G_OBJECT (win_node->data), UI_COMPONENT_KEY));
631                 
632
633                 bonobo_ui_component_set_translate (ui_component, mdi->priv->child_list_path, xml, NULL);
634                 bonobo_ui_component_set_translate (ui_component, "/commands/", cmd, NULL);
635                 bonobo_ui_component_add_verb (ui_component, verb_name, child_list_activated_cb, child); 
636
637                 win_node = g_list_next (win_node);
638         }
639         
640         g_free (tip);
641         g_free (escaped_name);
642         g_free (child_name);
643         g_free (xml);
644         g_free (cmd);
645         g_free (verb_name);
646         g_free (safe_name);
647 }
648
649 static gboolean 
650 book_motion (GtkWidget *widget, GdkEventMotion *e, gpointer data)
651 {
652         BonoboMDI *mdi;
653
654         mdi = BONOBO_MDI (data);
655
656         if (!drag_cursor)
657                 drag_cursor = gdk_cursor_new (GDK_HAND2);
658
659         if (e->window == GTK_NOTEBOOK (widget)->event_window) 
660         {
661                 mdi->priv->in_drag = TRUE;
662                 gtk_grab_add (widget);
663                 gdk_pointer_grab (widget->window, FALSE,
664                                                  GDK_POINTER_MOTION_MASK |
665                                                  GDK_BUTTON_RELEASE_MASK, NULL,
666                                                  drag_cursor, GDK_CURRENT_TIME);
667                 if (mdi->priv->signal_id) 
668                 {
669                         g_signal_handler_disconnect (G_OBJECT (widget), 
670                                                      mdi->priv->signal_id);
671                         mdi->priv->signal_id = 0;
672                 }
673         }
674
675         return FALSE;
676 }
677
678 static gboolean 
679 book_button_press (GtkWidget *widget, GdkEventButton *e, gpointer data)
680 {
681         BonoboMDI *mdi;
682
683         mdi = BONOBO_MDI (data);
684
685         if ((e->button == 1) && (e->window == GTK_NOTEBOOK (widget)->event_window))
686                 mdi->priv->signal_id = g_signal_connect (
687                                 G_OBJECT (widget), 
688                                 "motion_notify_event",
689                                 G_CALLBACK (book_motion), 
690                                 mdi);
691
692         return FALSE;
693 }
694
695 static gboolean
696 book_button_release (GtkWidget *widget, GdkEventButton *e, gpointer data)
697 {
698         gint x = e->x_root, y = e->y_root;
699         BonoboMDI *mdi;
700
701         mdi = BONOBO_MDI(data);
702         
703         if (mdi->priv->signal_id) 
704         {
705                 g_signal_handler_disconnect (G_OBJECT (widget), mdi->priv->signal_id);
706                 mdi->priv->signal_id = 0;
707         }
708
709         if ((e->button == 1) && mdi->priv->in_drag) 
710         {       
711                 GdkWindow *window;
712                 GList *child;
713                 BonoboWindow *win;
714                 GtkWidget *view, *new_book;
715                 GtkNotebook *old_book = GTK_NOTEBOOK (widget);
716
717                 mdi->priv->in_drag = FALSE;
718                 gdk_pointer_ungrab (GDK_CURRENT_TIME);
719                 gtk_grab_remove (widget);
720
721                 window = gdk_window_at_pointer (&x, &y);
722                 if (window)
723                         window = gdk_window_get_toplevel (window);
724
725                 child = mdi->priv->windows;
726                 
727                 while (child) 
728                 {
729                         if (window == GTK_WIDGET (child->data)->window) 
730                         {
731                                 int cur_page;
732
733                                 /* page was dragged to another notebook */
734
735                                 old_book = GTK_NOTEBOOK(widget);
736                                 new_book = get_book_from_window (BONOBO_WINDOW (child->data));
737
738                                 if (old_book == (GtkNotebook *) new_book) 
739                                         /* page has been dropped on the source notebook */
740                                         return FALSE;
741
742                                 cur_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (old_book));
743         
744                                 if (cur_page >= 0) 
745                                 {
746                                         view = gtk_notebook_get_nth_page (GTK_NOTEBOOK (old_book), cur_page);
747                                         gtk_container_remove (GTK_CONTAINER(old_book), view);
748
749                                         book_add_view (GTK_NOTEBOOK (new_book), view);
750
751                                         win = bonobo_mdi_get_window_from_view (view);
752                                         gdk_window_raise (GTK_WIDGET(win)->window);
753
754                                         mdi->priv->active_window = win;
755
756                                         cur_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (old_book));
757
758                                         if (cur_page < 0) 
759                                         {
760                                                 mdi->priv->active_window = win;
761                                                 win = BONOBO_WINDOW (gtk_widget_get_toplevel (
762                                                                         GTK_WIDGET (old_book)));
763                                                 mdi->priv->windows = g_list_remove(mdi->priv->windows, win);
764                                                 gtk_widget_destroy (GTK_WIDGET (win));
765                                         }                                       
766                                                                                 
767                                         g_signal_emit (G_OBJECT (mdi), 
768                                                        mdi_signals [CHILD_CHANGED], 
769                                                        0,
770                                                        NULL);
771
772                                         g_signal_emit (G_OBJECT (mdi), 
773                                                        mdi_signals [VIEW_CHANGED], 
774                                                        0,
775                                                        view);
776                                 }
777
778                                 return FALSE;
779                         }
780                                 
781                         child = child->next;
782                 }
783
784                 if (g_list_length (old_book->children) == 1)
785                         return FALSE;
786
787                 /* create a new toplevel */
788                 if (old_book->cur_page) 
789                 {
790                         gint width, height;
791                         int cur_page = gtk_notebook_get_current_page (old_book);
792
793                         view = gtk_notebook_get_nth_page (old_book, cur_page);
794                                 
795                         win = bonobo_mdi_get_window_from_view (view);
796
797                         gtk_window_get_size (GTK_WINDOW (win), &width, &height);
798                 
799                         gtk_container_remove (GTK_CONTAINER (old_book), view);
800                         
801                         app_clone (mdi, win, NULL);
802                                 
803                         new_book = book_create (mdi);
804         
805                         book_add_view (GTK_NOTEBOOK (new_book), view);
806
807                         gtk_window_set_position (GTK_WINDOW (mdi->priv->active_window), GTK_WIN_POS_MOUSE);
808         
809                         gtk_window_set_default_size (GTK_WINDOW (mdi->priv->active_window), width, height);
810
811                         if (!GTK_WIDGET_VISIBLE (mdi->priv->active_window))
812                                 gtk_widget_show (GTK_WIDGET (mdi->priv->active_window));
813                 }
814         }
815
816         return FALSE;
817 }
818
819 static GtkWidget *
820 book_create (BonoboMDI *mdi)
821 {
822         GtkWidget *us;
823         GtkWidget *vbox;
824
825         gl_debug (DEBUG_MDI, "");
826
827         g_return_val_if_fail (mdi->priv->active_window != NULL, NULL);
828         
829         vbox = gtk_vbox_new (FALSE, 0);
830         us = gtk_notebook_new ();
831
832         gtk_notebook_set_tab_pos (GTK_NOTEBOOK (us), mdi->priv->tab_pos);
833
834         gtk_box_pack_start (GTK_BOX (vbox), us, TRUE, TRUE, 0); 
835
836         gtk_widget_show_all (vbox);
837
838         bonobo_window_set_contents (mdi->priv->active_window, vbox);
839         g_object_set_data (G_OBJECT (mdi->priv->active_window), BOOK_KEY, us);
840         
841         gtk_widget_add_events (us, GDK_BUTTON1_MOTION_MASK);
842
843         g_signal_connect (G_OBJECT (us), "switch_page",
844                           G_CALLBACK (book_switch_page), mdi);
845
846         g_signal_connect (G_OBJECT (us), "button_press_event",
847                           G_CALLBACK (book_button_press), mdi);
848         g_signal_connect (G_OBJECT (us), "button_release_event",
849                           G_CALLBACK (book_button_release), mdi);
850
851         gtk_notebook_set_scrollable (GTK_NOTEBOOK (us), TRUE);
852         
853         gl_debug (DEBUG_MDI, "END");
854
855         return us;
856 }
857
858 static void 
859 book_add_view (GtkNotebook *book, GtkWidget *view)
860 {
861         BonoboMDIChild *child;
862         GtkWidget *title;
863
864         gl_debug (DEBUG_MDI, "");
865
866         child = bonobo_mdi_get_child_from_view (view);
867
868         title = child_set_label (child, NULL);
869
870         gtk_notebook_append_page (book, view, title);
871
872         if (g_list_length (book->children) > 1)
873                 set_page_by_widget (book, view);
874
875         gl_debug (DEBUG_MDI, "END");
876 }
877
878 static void 
879 book_switch_page (GtkNotebook *book, GtkNotebookPage *pg, gint page_num, BonoboMDI *mdi)
880 {
881         BonoboWindow *win;
882         GtkWidget *page;
883         
884         gl_debug (DEBUG_MDI, "");
885
886         win = BONOBO_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (book)));
887
888         page = gtk_notebook_get_nth_page (book, page_num);
889
890         if (page != NULL) 
891         {
892                 if (page != mdi->priv->active_view)
893                         app_set_view (mdi, win, page);
894         }
895         else 
896                 app_set_view (mdi, win, NULL);  
897
898         gl_debug (DEBUG_MDI, "END");
899
900 }
901
902 static GtkWidget*
903 get_book_from_window (BonoboWindow *window)
904 {
905         gpointer *book;
906
907         g_return_val_if_fail (window != NULL, NULL);
908
909         book = g_object_get_data (G_OBJECT (window), BOOK_KEY);
910
911         return (book != NULL) ? GTK_WIDGET (book) : NULL;
912 }
913
914 static gboolean
915 toplevel_focus (BonoboWindow *win, GdkEventFocus *event, BonoboMDI *mdi)
916 {
917         GtkWidget *contents;
918         
919         gl_debug (DEBUG_MDI, "");
920
921         /* updates active_view and active_child when a new toplevel receives focus */
922         g_return_val_if_fail (BONOBO_IS_WINDOW (win), FALSE);
923         
924         mdi->priv->active_window = win;
925         
926         contents = get_book_from_window (win);
927         
928         if (GTK_NOTEBOOK (contents)->cur_page) 
929         {
930                 int cur_page;
931                 GtkWidget *child;
932                         
933                 cur_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (contents));
934                 child = gtk_notebook_get_nth_page (GTK_NOTEBOOK (contents), cur_page);
935                 set_active_view (mdi, child);
936         } 
937         else 
938                 set_active_view (mdi, NULL);
939         
940         gl_debug (DEBUG_MDI, "END");
941
942         return FALSE;
943 }
944
945 static gboolean 
946 app_configure_event_handler (GtkWidget *widget, GdkEventConfigure *event)
947 {
948         BonoboMDIWindowInfo *window_info;
949
950         g_return_val_if_fail (BONOBO_IS_WINDOW (widget), FALSE);
951         g_return_val_if_fail (event != NULL, FALSE);
952         
953         window_info = (BonoboMDIWindowInfo*) 
954                         g_object_get_data (G_OBJECT (widget), WINDOW_INFO_KEY);
955         g_return_val_if_fail (window_info != NULL, FALSE);
956
957         window_info->width = event->width;
958         window_info->height = event->height;
959
960         return FALSE;
961 }
962
963 static gboolean 
964 app_window_state_event_handler (GtkWidget *widget, GdkEventWindowState *event)
965 {
966         BonoboMDIWindowInfo *window_info;
967
968         g_return_val_if_fail (BONOBO_IS_WINDOW (widget), FALSE);
969         g_return_val_if_fail (event != NULL, FALSE);
970         
971         window_info = (BonoboMDIWindowInfo*) 
972                         g_object_get_data (G_OBJECT (widget), WINDOW_INFO_KEY);
973         g_return_val_if_fail (window_info != NULL, FALSE);
974         
975         window_info->state = event->new_window_state;
976         
977         return FALSE;
978 }
979
980
981 static void 
982 app_clone (BonoboMDI *mdi, BonoboWindow *win, const char *window_role)
983 {
984         gl_debug (DEBUG_MDI, "");
985         
986         app_create (mdi, NULL, window_role);
987
988         if (win != NULL)
989         {
990                 const BonoboMDIWindowInfo *window_info = bonobo_mdi_get_window_info (win);
991                 g_return_if_fail (window_info != NULL);
992
993                 if ((window_info->state & GDK_WINDOW_STATE_MAXIMIZED) != 0)
994                         gtk_window_maximize (GTK_WINDOW (mdi->priv->active_window));
995                 else
996                 {
997                         gtk_window_set_default_size (GTK_WINDOW (mdi->priv->active_window), 
998                                              window_info->width,
999                                              window_info->height);
1000                         
1001                         gtk_window_unmaximize (GTK_WINDOW (mdi->priv->active_window));
1002                 }
1003
1004                 if ((window_info->state & GDK_WINDOW_STATE_STICKY ) != 0)
1005                         gtk_window_stick (GTK_WINDOW (mdi->priv->active_window));
1006                 else
1007                         gtk_window_unstick (GTK_WINDOW (mdi->priv->active_window));
1008         }
1009
1010         gl_debug (DEBUG_MDI, "END");
1011 }
1012
1013
1014 static gboolean
1015 app_close_book (BonoboWindow *win, GdkEventAny *event, BonoboMDI *mdi)
1016 {
1017         BonoboMDIChild *child;
1018         GtkWidget *view;
1019         gint handler_ret = TRUE;
1020         
1021         gl_debug (DEBUG_MDI, "");
1022
1023         if (g_list_length (mdi->priv->windows) == 1) 
1024         {               
1025                 if (!bonobo_mdi_remove_all (mdi, FALSE))
1026                 {
1027                         gl_debug (DEBUG_MDI, "END1");
1028
1029                         return TRUE;
1030                 }
1031
1032                 mdi->priv->windows = g_list_remove (mdi->priv->windows, win);
1033                 gtk_widget_destroy (GTK_WIDGET (win));
1034                 
1035                 /* only emit al_windows_destroyed signal if there are no non-MDI windows registered
1036                    with it. */
1037                 if (mdi->priv->registered == NULL)
1038                         g_signal_emit (G_OBJECT (mdi), mdi_signals [ALL_WINDOWS_DESTROYED], 0);
1039         }
1040         else 
1041         {
1042                 GList *children = gtk_container_get_children (
1043                                         GTK_CONTAINER (get_book_from_window (win)));
1044                 GList *li;
1045
1046                 if (children == NULL) 
1047                 {
1048                         mdi->priv->windows = g_list_remove (mdi->priv->windows, win);
1049                         gtk_widget_destroy (GTK_WIDGET (win));
1050
1051                         gl_debug (DEBUG_MDI, "END2");
1052
1053                         return FALSE;
1054                 }
1055
1056                 /* first check if all the children in this notebook can be removed */
1057                 for (li = children; li != NULL; li = li->next) 
1058                 {
1059                         GList *node;
1060                         view = li->data;
1061
1062                         child = bonobo_mdi_get_child_from_view (view);
1063                         
1064                         node = bonobo_mdi_child_get_views (child);
1065                         
1066                         while (node) 
1067                         {
1068                                 if (bonobo_mdi_get_window_from_view (node->data) != win)
1069                                         break;
1070                                 
1071                                 node = node->next;
1072                         }
1073                         
1074                         if (node == NULL) 
1075                         {   
1076                                 /* all the views reside in this BonoboWindow */
1077                                 g_signal_emit (G_OBJECT(mdi), 
1078                                               mdi_signals [REMOVE_CHILD],
1079                                               0,
1080                                               child, 
1081                                               &handler_ret);
1082
1083                                 if (handler_ret == FALSE) 
1084                                 {
1085                                         g_list_free (children);
1086
1087                                         gl_debug (DEBUG_MDI, "END3");
1088
1089                                         return TRUE;
1090                                 }
1091                         }
1092                 }
1093                 
1094                 /* now actually remove all children/views! */
1095                 for (li = children; li != NULL; li = li->next) 
1096                 {
1097                         view = li->data;
1098
1099                         child = bonobo_mdi_get_child_from_view (view);
1100                         
1101                         /* if this is the last view, remove the child */
1102                         if (g_list_length (bonobo_mdi_child_get_views (child)) == 1)
1103                                 bonobo_mdi_remove_child (mdi, child, TRUE);
1104                         else
1105                                 bonobo_mdi_remove_view (mdi, view, TRUE);
1106                 }
1107
1108                 g_list_free (children);
1109         }
1110
1111         gl_debug (DEBUG_MDI, "END");
1112         
1113         return FALSE;
1114 }
1115
1116 static void 
1117 app_set_view (BonoboMDI *mdi, BonoboWindow *win, GtkWidget *view)
1118 {
1119         gl_debug (DEBUG_MDI, "");
1120
1121         gtk_window_set_title (GTK_WINDOW (win), mdi->priv->title);
1122         
1123         set_active_view (mdi, view);
1124
1125         gl_debug (DEBUG_MDI, "END");
1126 }
1127
1128 static void 
1129 app_destroy (BonoboWindow *win, BonoboMDI *mdi)
1130 {
1131         gl_debug (DEBUG_MDI, "");
1132         
1133         if (mdi->priv->active_window == win)
1134                 mdi->priv->active_window = 
1135                         (mdi->priv->windows != NULL) ? BONOBO_WINDOW (mdi->priv->windows->data) : NULL;
1136
1137         g_signal_emit (G_OBJECT (mdi), mdi_signals [TOP_WINDOW_DESTROY], 0, win);
1138
1139         gl_debug (DEBUG_MDI, "END");
1140 }
1141
1142 /* Generates a unique string for a window role.
1143  *
1144  * Taken from EOG.
1145  */
1146 static char *
1147 gen_role (void)
1148 {
1149         char *ret;
1150         static char *hostname;
1151         time_t t;
1152         static int serial;
1153
1154         t = time (NULL);
1155
1156         if (!hostname) {
1157                 static char buffer [512];
1158
1159                 if ((gethostname (buffer, sizeof (buffer) - 1) == 0) &&
1160                     (buffer [0] != 0))
1161                         hostname = buffer;
1162                 else
1163                         hostname = "localhost";
1164         }
1165
1166         ret = g_strdup_printf ("bonobo-mdi-window-%d-%d-%d-%ld-%d@%s",
1167                                getpid (),
1168                                getgid (),
1169                                getppid (),
1170                                (long) t,
1171                                serial++,
1172                                hostname);
1173
1174         return ret;
1175 }
1176
1177 static void 
1178 app_create (BonoboMDI *mdi, gchar *layout_string, const char *window_role)
1179 {
1180         GtkWidget *window;
1181         BonoboWindow *bw;
1182         gchar* config_path;
1183         BonoboUIContainer *ui_container = NULL;
1184         BonoboUIComponent *ui_component = NULL;
1185         BonoboMDIWindowInfo *window_info = NULL;
1186
1187         gl_debug (DEBUG_MDI, "");
1188
1189         window = bonobo_window_new (mdi->priv->mdi_name, mdi->priv->title);
1190         g_return_if_fail (window != NULL);
1191
1192         gtk_window_set_default_size (GTK_WINDOW (window),
1193                                      mdi->priv->default_window_width,
1194                                      mdi->priv->default_window_height);
1195
1196         if (window_role)
1197                 gtk_window_set_role (GTK_WINDOW (window), window_role);
1198         else {
1199                 char *role;
1200
1201                 role = gen_role ();
1202                 gtk_window_set_role (GTK_WINDOW (window), role);
1203                 g_free (role);
1204         }
1205         
1206         bw = BONOBO_WINDOW (window);
1207
1208         mdi->priv->windows = g_list_append (mdi->priv->windows, window);
1209
1210         g_signal_connect (G_OBJECT (window), "delete_event", 
1211                           G_CALLBACK (app_close_book), mdi);
1212         g_signal_connect (G_OBJECT (window), "focus_in_event",
1213                           G_CALLBACK (toplevel_focus), mdi);
1214         g_signal_connect (G_OBJECT (window), "destroy",
1215                           G_CALLBACK (app_destroy), mdi);
1216         g_signal_connect (G_OBJECT (window), "configure_event",
1217                           G_CALLBACK (app_configure_event_handler), NULL);
1218         g_signal_connect (G_OBJECT (window), "window_state_event",
1219                           G_CALLBACK (app_window_state_event_handler), NULL);
1220
1221         /* Create Container: */
1222         ui_container = bonobo_window_get_ui_container (bw);
1223
1224         config_path = g_strdup_printf ("/%s/UIConfig/kvps", mdi->priv->mdi_name);
1225
1226         bonobo_ui_engine_config_set_path (bonobo_window_get_ui_engine (bw),
1227                                      config_path);
1228         g_free (config_path);
1229
1230         /* Create a UI component with which to communicate with the window */
1231         ui_component = bonobo_ui_component_new_default ();
1232
1233         /* Associate the BonoboUIComponent with the container */
1234         bonobo_ui_component_set_container (
1235                 ui_component, BONOBO_OBJREF (ui_container), NULL);
1236
1237         /* set up UI */
1238         if (mdi->priv->ui_xml != NULL)
1239         {
1240                 bonobo_ui_component_set_translate (ui_component, 
1241                                 "/", mdi->priv->ui_xml, NULL);
1242         }
1243         else
1244                 if (mdi->priv->ui_file_name)
1245                 {
1246                         bonobo_ui_util_set_ui (ui_component, "", mdi->priv->ui_file_name,
1247                                        mdi->priv->mdi_name, NULL);
1248                 }
1249
1250         if (mdi->priv->verbs)
1251                 bonobo_ui_component_add_verb_list_with_data (ui_component, 
1252                                 mdi->priv->verbs, mdi);
1253         
1254         mdi->priv->active_window = bw;
1255         mdi->priv->active_child = NULL;
1256         mdi->priv->active_view = NULL;
1257
1258         window_info = g_new0 (BonoboMDIWindowInfo, 1);
1259                         
1260         g_object_set_data (G_OBJECT (bw), UI_COMPONENT_KEY, ui_component);
1261         g_object_set_data_full (G_OBJECT (bw), WINDOW_INFO_KEY, window_info, g_free);
1262
1263         g_signal_emit (G_OBJECT (mdi), mdi_signals [TOP_WINDOW_CREATED], 0, window);
1264
1265         child_list_menu_create (mdi, bw);
1266
1267         gl_debug (DEBUG_MDI, "END");
1268 }
1269
1270 static void 
1271 set_active_view (BonoboMDI *mdi, GtkWidget *view)
1272 {
1273         BonoboMDIChild *old_child;
1274         GtkWidget *old_view;
1275                 
1276         gl_debug (DEBUG_MDI, "");
1277
1278         old_child = mdi->priv->active_child;
1279         old_view = mdi->priv->active_view;
1280
1281         mdi->priv->active_view = view;
1282
1283         if (!view) 
1284                 mdi->priv->active_child = NULL;
1285         else
1286         {
1287                 
1288                 mdi->priv->active_child = bonobo_mdi_get_child_from_view (view);
1289                 mdi->priv->active_window = bonobo_mdi_get_window_from_view (view);
1290         
1291                 gtk_widget_grab_focus (GTK_WIDGET (view));
1292         }
1293         
1294         if (view == old_view)
1295         {
1296                 gl_debug (DEBUG_MDI, "END1");
1297
1298                 return;
1299         }
1300         
1301         if (mdi->priv->active_child != old_child)
1302         {
1303                 gl_debug (DEBUG_MDI, "Emit child_changed");
1304
1305                 g_signal_emit (G_OBJECT (mdi), mdi_signals [CHILD_CHANGED], 0, old_child);
1306         }
1307
1308         gl_debug (DEBUG_MDI, "Emit view_changed");
1309         
1310         g_signal_emit (G_OBJECT (mdi), mdi_signals [VIEW_CHANGED], 0, old_view);
1311
1312         gl_debug (DEBUG_MDI, "END2");
1313 }
1314
1315 /**
1316  * bonobo_mdi_set_active_view:
1317  * @mdi: A pointer to an MDI object.
1318  * @view: A pointer to the view that is to become the active one.
1319  * 
1320  * Description:
1321  * Sets the active view to @view. It also raises the window containing it
1322  * and gives it focus.
1323  **/
1324 void 
1325 bonobo_mdi_set_active_view (BonoboMDI *mdi, GtkWidget *view)
1326 {
1327         GtkWindow *window;
1328         
1329         gl_debug (DEBUG_MDI, "");
1330
1331         g_return_if_fail (mdi != NULL);
1332         g_return_if_fail (BONOBO_IS_MDI (mdi));
1333         g_return_if_fail (view != NULL);
1334         g_return_if_fail (GTK_IS_WIDGET (view));
1335         
1336         set_page_by_widget (GTK_NOTEBOOK (view->parent), view);
1337         
1338         window = GTK_WINDOW (bonobo_mdi_get_window_from_view (view));
1339         
1340         gtk_window_present (window);
1341
1342         set_active_view (mdi, view);
1343
1344         gl_debug (DEBUG_MDI, "END");
1345 }
1346
1347 /**
1348  * bonobo_mdi_add_view:
1349  * @mdi: A pointer to a BonoboMDI object.
1350  * @child: A pointer to a child.
1351  * 
1352  * Description:
1353  * Creates a new view of the child and adds it to the MDI. BonoboMDIChild
1354  * @child has to be added to the MDI with a call to bonobo_mdi_add_child
1355  * before its views are added to the MDI. 
1356  * An "add_view" signal is emitted to the MDI after the view has been
1357  * created, but before it is shown and added to the MDI, with a pointer to
1358  * the created view as its parameter. The view is added to the MDI only if
1359  * the signal handler (if it exists) returns %TRUE. If the handler returns
1360  * %FALSE, the created view is destroyed and not added to the MDI. 
1361  * 
1362  * Return value:
1363  * %TRUE if adding the view succeeded and %FALSE otherwise.
1364  **/
1365 gboolean 
1366 bonobo_mdi_add_view (BonoboMDI *mdi, BonoboMDIChild *child)
1367 {
1368         GtkWidget *view;
1369         GtkWidget *book;
1370         gint ret = TRUE;
1371         
1372         gl_debug (DEBUG_MDI, "");
1373
1374         g_return_val_if_fail (mdi != NULL, FALSE);
1375         g_return_val_if_fail (BONOBO_IS_MDI (mdi), FALSE);
1376         g_return_val_if_fail (child != NULL, FALSE);
1377         g_return_val_if_fail (BONOBO_IS_MDI_CHILD (child), FALSE);
1378         
1379         view = bonobo_mdi_child_add_view (child);
1380
1381         g_return_val_if_fail (view != NULL, FALSE);
1382
1383         g_signal_emit (G_OBJECT (mdi), mdi_signals [ADD_VIEW], 0, view, &ret);
1384
1385         if (!ret) 
1386         {
1387                 bonobo_mdi_child_remove_view (child, view);
1388                 
1389                 gl_debug (DEBUG_MDI, "END1");
1390
1391                 return FALSE;
1392         }
1393
1394         if (mdi->priv->active_window == NULL) 
1395         {
1396                 app_create (mdi, NULL, NULL);
1397                 gtk_widget_show (GTK_WIDGET (mdi->priv->active_window));
1398         }
1399
1400         
1401         if (!GTK_WIDGET_VISIBLE (view))
1402                 gtk_widget_show (view);
1403
1404         book = get_book_from_window (mdi->priv->active_window);
1405         
1406         if (book == NULL)
1407                 book = book_create (mdi);
1408         
1409         book_add_view (GTK_NOTEBOOK (book), view);
1410         
1411         /* this reference will compensate the view's unrefing
1412            when removed from its parent later, as we want it to
1413            stay valid until removed from the child with a call
1414            to bonobo_mdi_child_remove_view() */
1415         g_object_ref (G_OBJECT (view));
1416         gtk_object_sink (GTK_OBJECT (view));
1417         
1418         g_object_set_data (G_OBJECT (view), BONOBO_MDI_CHILD_KEY, child);
1419         
1420         gl_debug (DEBUG_MDI, "END2");
1421
1422         return TRUE;
1423 }
1424
1425 /**
1426  * bonobo_mdi_add_toplevel_view:
1427  * @mdi: A pointer to a BonoboMDI object.
1428  * @child: A pointer to a BonoboMDIChild object to be added to the MDI.
1429  * @window_role: X window role to use for the window, for session-management
1430  * purposes.  If this is %NULL, a unique role string will be automatically
1431  * generated.
1432  * 
1433  * Description:
1434  * Creates a new view of the child and adds it to the MDI; it behaves the
1435  * same way as bonobo_mdi_add_view in %BONOBO_MDI_MODAL and %BONOBO_MDI_TOPLEVEL
1436  * modes, but in %BONOBO_MDI_NOTEBOOK mode, the view is added in a new
1437  * toplevel window unless the active one has no views in it. 
1438  * 
1439  * Return value: 
1440  * %TRUE if adding the view succeeded and %FALSE otherwise.
1441  **/
1442 gboolean
1443 bonobo_mdi_add_toplevel_view (BonoboMDI *mdi, BonoboMDIChild *child, const char *window_role)
1444 {
1445         GtkWidget *view;
1446         gint ret = TRUE;
1447         
1448         gl_debug (DEBUG_MDI, "");
1449
1450         g_return_val_if_fail (mdi != NULL, FALSE);
1451         g_return_val_if_fail (BONOBO_IS_MDI (mdi), FALSE);
1452         g_return_val_if_fail (child != NULL, FALSE);
1453         g_return_val_if_fail (BONOBO_IS_MDI_CHILD (child), FALSE);
1454         
1455         view = bonobo_mdi_child_add_view (child);
1456
1457         g_return_val_if_fail (view != NULL, FALSE);
1458
1459         g_signal_emit (G_OBJECT (mdi), mdi_signals [ADD_VIEW], 0, view, &ret);
1460
1461         if (ret == FALSE) 
1462         {
1463                 bonobo_mdi_child_remove_view (child, view);
1464
1465                 gl_debug (DEBUG_MDI, "END1");
1466
1467                 return FALSE;
1468         }
1469
1470         bonobo_mdi_open_toplevel (mdi, window_role);
1471         
1472         if (!GTK_WIDGET_VISIBLE (view))
1473                 gtk_widget_show (view);
1474
1475         book_add_view (GTK_NOTEBOOK (get_book_from_window (mdi->priv->active_window)), view);
1476         
1477         /* this reference will compensate the view's unrefing
1478            when removed from its parent later, as we want it to
1479            stay valid until removed from the child with a call
1480            to bonobo_mdi_child_remove_view() */
1481         g_object_ref (G_OBJECT (view)); 
1482         gtk_object_sink (GTK_OBJECT (view));
1483         
1484         g_object_set_data (G_OBJECT (view), BONOBO_MDI_CHILD_KEY, child);
1485         
1486         gl_debug (DEBUG_MDI, "END2");
1487
1488         return TRUE;
1489 }
1490
1491 /**
1492  * bonobo_mdi_remove_view:
1493  * @mdi: A pointer to a BonoboMDI object.
1494  * @view: View to remove.
1495  * @force: If TRUE, the "remove_view" signal is not emitted.
1496  * 
1497  * Description:
1498  * Removes a view from an MDI. 
1499  * A "remove_view" signal is emitted to the MDI before actually removing
1500  * view. The view is removed only if the signal handler (if it exists and
1501  * the @force is set to %FALSE) returns %TRUE. 
1502  * 
1503  * Return value: 
1504  * %TRUE if the view was removed and %FALSE otherwise.
1505  **/
1506 gboolean
1507 bonobo_mdi_remove_view (BonoboMDI *mdi, GtkWidget *view, gint force)
1508 {
1509         GtkWidget *book;
1510         BonoboWindow *window;
1511         BonoboMDIChild *child;
1512         gint ret = TRUE;
1513         gint pn;
1514         gboolean was_active_window = FALSE;
1515                 
1516         gl_debug (DEBUG_MDI, "");
1517         
1518         g_return_val_if_fail (mdi != NULL, FALSE);
1519         g_return_val_if_fail (BONOBO_IS_MDI (mdi), FALSE);
1520         g_return_val_if_fail (view != NULL, FALSE);
1521         g_return_val_if_fail (GTK_IS_WIDGET (view), FALSE);
1522
1523         if (!force)
1524                 g_signal_emit (G_OBJECT (mdi), mdi_signals [REMOVE_VIEW], 0, view, &ret);
1525
1526         if (ret == FALSE)
1527         {
1528                 gl_debug (DEBUG_MDI, "END1");
1529
1530                 return FALSE;
1531         }
1532
1533         was_active_window = (view == mdi->priv->active_view);
1534         
1535         child = bonobo_mdi_get_child_from_view (view);
1536         window = bonobo_mdi_get_window_from_view (view);
1537         
1538         book = get_book_from_window (window);
1539         g_return_val_if_fail (book != NULL, TRUE);
1540
1541         if (g_list_length (GTK_NOTEBOOK (book)->children) == 1)
1542                 app_set_view (mdi, window, NULL);
1543                 
1544         bonobo_mdi_child_remove_view (child, view);
1545
1546         pn = gtk_notebook_page_num (GTK_NOTEBOOK (book), view);
1547         gtk_notebook_remove_page (GTK_NOTEBOOK (book), pn);
1548
1549         if (GTK_NOTEBOOK (book)->cur_page == NULL) 
1550         {
1551                 if ((g_list_length (mdi->priv->windows) > 1) || 
1552                     (mdi->priv->registered != NULL)) 
1553                 {
1554                         gl_debug (DEBUG_VIEW, "Destroy window");
1555
1556                         /* if this is NOT the last toplevel or a registered object
1557                         exists, destroy the toplevel */
1558                         mdi->priv->windows = g_list_remove (mdi->priv->windows, window);
1559                         gtk_widget_destroy (GTK_WIDGET (window));
1560         
1561                         if (mdi->priv->active_window && was_active_window)
1562                                 mdi->priv->active_view = bonobo_mdi_get_view_from_window (
1563                                                                         mdi, 
1564                                                                         mdi->priv->active_window);
1565                 }
1566         }
1567         else
1568         {
1569                 pn = gtk_notebook_get_current_page (GTK_NOTEBOOK (book));
1570                 app_set_view (mdi, window, gtk_notebook_get_nth_page (GTK_NOTEBOOK (book), pn));
1571         }
1572
1573                 
1574         gl_debug (DEBUG_MDI, "END2");
1575
1576         return TRUE;
1577 }
1578
1579 static void 
1580 child_name_changed (BonoboMDIChild *mdi_child, gchar* old_name, BonoboMDI *mdi)
1581 {
1582         bonobo_mdi_update_child (mdi, mdi_child);
1583 }
1584
1585 /**
1586  * bonobo_mdi_add_child:
1587  * @mdi: A pointer to a BonoboMDI object.
1588  * @child: A pointer to a BonoboMDIChild to add to the MDI.
1589  * 
1590  * Description:
1591  * Adds a new child to the MDI. No views are added: this has to be done with
1592  * a call to bonobo_mdi_add_view. 
1593  * First an "add_child" signal is emitted to the MDI with a pointer to the
1594  * child as its parameter. The child is added to the MDI only if the signal
1595  * handler (if it exists) returns %TRUE. If the handler returns %FALSE, the
1596  * child is not added to the MDI. 
1597  * 
1598  * Return value: 
1599  * %TRUE if the child was added successfully and %FALSE otherwise.
1600  **/
1601 gint 
1602 bonobo_mdi_add_child (BonoboMDI *mdi, BonoboMDIChild *child)
1603 {
1604         gint ret = TRUE;
1605
1606         gl_debug (DEBUG_MDI, "");
1607
1608         g_return_val_if_fail (mdi != NULL, FALSE);
1609         g_return_val_if_fail (BONOBO_IS_MDI (mdi), FALSE);
1610         g_return_val_if_fail (child != NULL, FALSE);
1611         g_return_val_if_fail (BONOBO_IS_MDI_CHILD (child), FALSE);
1612
1613         g_signal_emit (G_OBJECT (mdi), mdi_signals [ADD_CHILD], 0, child, &ret);
1614
1615         if (ret == FALSE)
1616         {
1617                 gl_debug (DEBUG_MDI, "END1");
1618
1619                 return FALSE;
1620         }
1621
1622         bonobo_mdi_child_set_parent (child, G_OBJECT (mdi));
1623
1624         mdi->priv->children = g_list_append (mdi->priv->children, child);
1625
1626         g_signal_connect (G_OBJECT (child), "name_changed",
1627                           G_CALLBACK (child_name_changed), mdi);
1628
1629         child_list_menu_add_item (mdi, child);
1630
1631         g_object_set_data (G_OBJECT (child), BONOBO_MDI_KEY, mdi);
1632
1633         gl_debug (DEBUG_MDI, "END2");
1634
1635         return TRUE;
1636 }
1637
1638 /**
1639  * bonobo_mdi_remove_child:
1640  * @mdi: A pointer to a BonoboMDI object.
1641  * @child: Child to remove.
1642  * @force: If TRUE, the "remove_child" signal is not emitted
1643  * 
1644  * Description:
1645  * Removes a child and all of its views from the MDI. 
1646  * A "remove_child" signal is emitted to the MDI with @child as its parameter
1647  * before actually removing the child. The child is removed only if the signal
1648  * handler (if it exists and the @force is set to %FALSE) returns %TRUE. 
1649  * 
1650  * Return value: 
1651  * %TRUE if the removal was successful and %FALSE otherwise.
1652  **/
1653 gint 
1654 bonobo_mdi_remove_child (BonoboMDI *mdi, BonoboMDIChild *child, gint force)
1655 {
1656         gint ret = TRUE;
1657         GList *view_node;
1658         GtkWidget *view;
1659
1660         gl_debug (DEBUG_MDI, "");
1661         
1662         g_return_val_if_fail (mdi != NULL, FALSE);
1663         g_return_val_if_fail (BONOBO_IS_MDI (mdi), FALSE);
1664         g_return_val_if_fail (child != NULL, FALSE);
1665         g_return_val_if_fail (BONOBO_IS_MDI_CHILD (child), FALSE);
1666
1667         /* if force is set to TRUE, don't call the remove_child handler (ie there is no way for the
1668            user to stop removal of the child) */
1669
1670         if (!force)
1671                 g_signal_emit (G_OBJECT (mdi), mdi_signals [REMOVE_CHILD], 0, child, &ret);
1672
1673         if (ret == FALSE)
1674         {
1675                 gl_debug (DEBUG_MDI, "END1");
1676
1677                 return FALSE;
1678         }
1679
1680         view_node = bonobo_mdi_child_get_views (child);
1681         
1682         while (view_node) 
1683         {
1684                 view = GTK_WIDGET (view_node->data);
1685                 view_node = view_node->next;
1686                 bonobo_mdi_remove_view (mdi, GTK_WIDGET (view), TRUE);
1687         }
1688
1689         mdi->priv->children = g_list_remove (mdi->priv->children, child);
1690
1691         child_list_menu_remove_item (mdi, child);
1692
1693         if (child == mdi->priv->active_child)
1694                 mdi->priv->active_child = NULL;
1695
1696         bonobo_mdi_child_set_parent (child, NULL);
1697
1698         g_signal_handlers_disconnect_by_func (G_OBJECT (child), G_CALLBACK (child_name_changed), mdi);
1699
1700         g_object_unref (G_OBJECT (child));
1701
1702         gl_debug (DEBUG_MDI, "END2");
1703
1704         return TRUE;
1705 }
1706
1707 /**
1708  * bonobo_mdi_remove_all:
1709  * @mdi: A pointer to a BonoboMDI object.
1710  * @force: If TRUE, the "remove_child" signal is not emitted
1711  * 
1712  * Description:
1713  * Removes all children and all views from the MDI. 
1714  * A "remove_child" signal is emitted to the MDI for each child before
1715  * actually trying to remove any. If signal handlers for all children (if
1716  * they exist and the @force is set to %FALSE) return %TRUE, all children
1717  * and their views are removed and none otherwise. 
1718  * 
1719  * Return value:
1720  * %TRUE if the removal was successful and %FALSE otherwise.
1721  **/
1722 gint 
1723 bonobo_mdi_remove_all (BonoboMDI *mdi, gint force)
1724 {
1725         GList *child_node;
1726         gint handler_ret = TRUE;
1727
1728         gl_debug (DEBUG_MDI, "");
1729
1730         g_return_val_if_fail (mdi != NULL, FALSE);
1731         g_return_val_if_fail (BONOBO_IS_MDI (mdi), FALSE);
1732
1733         /* first check if removal of any child will be prevented by the
1734            remove_child signal handler */
1735         if (!force) 
1736         {
1737                 child_node = mdi->priv->children;
1738                 while (child_node) 
1739                 {
1740                         g_signal_emit (G_OBJECT (mdi), 
1741                                        mdi_signals [REMOVE_CHILD],
1742                                        0,
1743                                        child_node->data, 
1744                                        &handler_ret);
1745
1746                         /* if any of the children may not be removed, none will be */
1747                         if (handler_ret == FALSE)
1748                         {
1749                                 gl_debug (DEBUG_MDI, "END1");
1750
1751                                 return FALSE;
1752                         }
1753
1754                         child_node = child_node->next;
1755                 }
1756         }
1757
1758         /* remove all the children with force arg set to true so that remove_child
1759            handlers are not called again */
1760         while (mdi->priv->children)
1761                 bonobo_mdi_remove_child (mdi, BONOBO_MDI_CHILD (mdi->priv->children->data), TRUE);
1762
1763         gl_debug (DEBUG_MDI, "END2");
1764
1765         return TRUE;
1766 }
1767
1768 /**
1769  * bonobo_mdi_open_toplevel:
1770  * @mdi: A pointer to a BonoboMDI object.
1771  * @window_role: X window role to use for the window, for session-management
1772  * purposes.  If this is %NULL, a unique role string will be automatically
1773  * generated.
1774  * 
1775  * Description:
1776  * Opens a new toplevel window. This is usually used only for opening
1777  * the initial window on startup (just before calling gtkmain()) if no
1778  * windows were open because a session was restored or children were added
1779  * because of command line args).
1780  **/
1781 void 
1782 bonobo_mdi_open_toplevel (BonoboMDI *mdi, const char *window_role)
1783 {
1784         gl_debug (DEBUG_MDI, "");
1785
1786         g_return_if_fail (mdi != NULL);
1787         g_return_if_fail (BONOBO_IS_MDI (mdi));
1788
1789         app_clone (mdi, mdi->priv->active_window, window_role);
1790
1791         book_create (mdi);
1792                 
1793         gtk_widget_show (GTK_WIDGET (mdi->priv->active_window));
1794
1795         gl_debug (DEBUG_MDI, "END1");
1796 }
1797
1798 /**
1799  * bonobo_mdi_update_child:
1800  * @mdi: A pointer to a BonoboMDI object.
1801  * @child: Child to update.
1802  * 
1803  * Description:
1804  * Updates all notebook labels of @child's views and their window titles
1805  * after its name changes. It is not required if bonobo_mdi_child_set_name()
1806  * is used for setting the child's name.
1807  **/
1808 static void 
1809 bonobo_mdi_update_child (BonoboMDI *mdi, BonoboMDIChild *child)
1810 {
1811         GtkWidget *view, *title;
1812         GList *view_node;
1813         GList *win_node;
1814         gchar* child_name, *path, *path_cmd, *tip, *escaped_name;
1815
1816         gl_debug (DEBUG_MDI, "");
1817
1818         g_return_if_fail (mdi != NULL);
1819         g_return_if_fail (BONOBO_IS_MDI (mdi));
1820         g_return_if_fail (child != NULL);
1821         g_return_if_fail (BONOBO_IS_MDI_CHILD (child));
1822
1823         view_node = bonobo_mdi_child_get_views (child);
1824         
1825         while (view_node) 
1826         {
1827                 view = GTK_WIDGET (view_node->data);
1828
1829                 title = child_set_label (child, NULL);
1830                 
1831                 gtk_notebook_set_tab_label (GTK_NOTEBOOK (view->parent), view, title);
1832                 
1833                 view_node = g_list_next (view_node);
1834         }
1835
1836         /* Update child list menus */   
1837         if(mdi->priv->child_list_path == NULL)
1838         {
1839                 gl_debug (DEBUG_MDI, "END1");
1840
1841                 return;
1842         }
1843         
1844         win_node = mdi->priv->windows;
1845
1846         child_name = bonobo_mdi_child_get_name (child);
1847         escaped_name = escape_underscores (child_name);
1848         path = g_strdup_printf ("%sChild_%p", mdi->priv->child_list_path, child);
1849         path_cmd =  g_strdup_printf ("/commands/Child_%p", child);
1850         tip = g_strdup_printf (_("Activate %s"), child_name);
1851                         
1852         while (win_node) 
1853         {
1854                 BonoboUIComponent *ui_component;
1855                 
1856                 ui_component = BONOBO_UI_COMPONENT (
1857                         g_object_get_data (G_OBJECT (win_node->data), UI_COMPONENT_KEY));
1858
1859                 bonobo_ui_component_set_prop (ui_component, path, "label", escaped_name, NULL);
1860                 bonobo_ui_component_set_prop (ui_component, path, "tip", tip, NULL);
1861
1862                 win_node = g_list_next (win_node);
1863         }
1864
1865         g_free (escaped_name);
1866         g_free (path);
1867         g_free (path_cmd);
1868         g_free (tip);
1869         g_free (child_name);
1870
1871         gl_debug (DEBUG_MDI, "END2");
1872 }
1873
1874 /**
1875  * bonobo_mdi_find_child:
1876  * @mdi: A pointer to a BonoboMDI object.
1877  * @name: A string with a name of the child to find.
1878  * 
1879  * Description:
1880  * Finds a child named @name.
1881  * 
1882  * Return value: 
1883  * A pointer to the BonoboMDIChild object if the child was found and NULL
1884  * otherwise.
1885  **/
1886 BonoboMDIChild *
1887 bonobo_mdi_find_child (BonoboMDI *mdi, const gchar *name)
1888 {
1889         GList *child_node;
1890
1891         gl_debug (DEBUG_MDI, "");
1892
1893         g_return_val_if_fail (mdi != NULL, NULL);
1894         g_return_val_if_fail (BONOBO_IS_MDI (mdi), NULL);
1895
1896         child_node = mdi->priv->children;
1897         while (child_node) 
1898         {
1899                 gchar* child_name = bonobo_mdi_child_get_name (BONOBO_MDI_CHILD (child_node->data));
1900                  
1901                 if (strcmp (child_name, name) == 0)
1902                 {
1903                         g_free (child_name);    
1904
1905                         gl_debug (DEBUG_MDI, "END1");
1906
1907                         return BONOBO_MDI_CHILD (child_node->data);
1908                 }
1909                 
1910                 g_free (child_name);
1911                 
1912                 child_node = g_list_next (child_node);
1913         }
1914
1915         gl_debug (DEBUG_MDI, "END2");
1916
1917         return NULL;
1918 }
1919
1920 /**
1921  * bonobo_mdi_get_active_child:
1922  * @mdi: A pointer to a BonoboMDI object.
1923  * 
1924  * Description:
1925  * Returns a pointer to the active BonoboMDIChild object.
1926  * 
1927  * Return value: 
1928  * A pointer to the active BonoboMDIChild object. %NULL, if there is none.
1929  **/
1930 BonoboMDIChild *
1931 bonobo_mdi_get_active_child (BonoboMDI *mdi)
1932 {
1933         g_return_val_if_fail (mdi != NULL, NULL);
1934         g_return_val_if_fail (BONOBO_IS_MDI (mdi), NULL);
1935
1936         if (mdi->priv->active_view)
1937                 return (bonobo_mdi_get_child_from_view (mdi->priv->active_view));
1938
1939         return NULL;
1940 }
1941
1942 /**
1943  * bonobo_mdi_get_active_view:
1944  * @mdi: A pointer to a BonoboMDI object.
1945  * 
1946  * Description:
1947  * Returns a pointer to the active view (the one with the focus).
1948  * 
1949  * Return value: 
1950  * A pointer to a GtkWidget *.
1951  **/
1952 GtkWidget *
1953 bonobo_mdi_get_active_view (BonoboMDI *mdi)
1954 {
1955         g_return_val_if_fail (mdi != NULL, NULL);
1956         g_return_val_if_fail (BONOBO_IS_MDI (mdi), NULL);
1957
1958         return mdi->priv->active_view;
1959 }
1960
1961 /**
1962  * bonobo_mdi_get_active_window:
1963  * @mdi: A pointer to a BonoboMDI object.
1964  * 
1965  * Description:
1966  * Returns a pointer to the toplevel window containing the active view.
1967  * 
1968  * Return value:
1969  * A pointer to a BonoboWindow that has the focus.
1970  **/
1971 BonoboWindow *
1972 bonobo_mdi_get_active_window (BonoboMDI *mdi)
1973 {
1974         g_return_val_if_fail (mdi != NULL, NULL);
1975         g_return_val_if_fail (BONOBO_IS_MDI (mdi), NULL);
1976
1977         return mdi->priv->active_window;
1978 }
1979
1980 void 
1981 bonobo_mdi_set_ui_template (BonoboMDI *mdi, const gchar *xml, BonoboUIVerb verbs[])
1982 {
1983         g_return_if_fail (mdi != NULL);
1984         g_return_if_fail (BONOBO_IS_MDI (mdi));
1985         g_return_if_fail (xml != NULL);
1986
1987         if (mdi->priv->ui_xml != NULL)
1988                g_free (mdi->priv->ui_xml);
1989         
1990         mdi->priv->ui_xml = g_strdup (xml);
1991
1992         /* FIXME */
1993         mdi->priv->verbs = verbs;
1994 }
1995
1996 void          
1997 bonobo_mdi_set_ui_template_file (BonoboMDI *mdi, const gchar *file_name, BonoboUIVerb verbs[])
1998 {
1999         g_return_if_fail (mdi != NULL);
2000         g_return_if_fail (BONOBO_IS_MDI (mdi));
2001         g_return_if_fail (file_name != NULL);
2002
2003         if (mdi->priv->ui_file_name != NULL)
2004                 g_free (mdi->priv->ui_file_name);
2005         
2006         mdi->priv->ui_file_name = g_strdup (file_name);
2007
2008         /* FIXME */
2009         mdi->priv->verbs = verbs;
2010 }
2011
2012 /**
2013  * bonobo_mdi_set_child_list_path:
2014  * @mdi: A pointer to a BonoboMDI object.
2015  * @path: A menu path where the child list menu should be inserted
2016  * 
2017  * Description:
2018  * Sets the position for insertion of menu items used to activate the MDI
2019  * children that were added to the MDI. See gnome_app_find_menu_pos for
2020  * details on menu paths. If the path is not set or set to %NULL, these menu
2021  * items aren't going to be inserted in the MDI menu structure. Note that if
2022  * you want all menu items to be inserted in their own submenu, you have to
2023  * create that submenu (and leave it empty, of course).
2024  **/
2025 void 
2026 bonobo_mdi_set_child_list_path (BonoboMDI *mdi, const gchar *path)
2027 {
2028         g_return_if_fail (mdi != NULL);
2029         g_return_if_fail (BONOBO_IS_MDI (mdi));
2030
2031         if (mdi->priv->child_list_path)
2032                 g_free (mdi->priv->child_list_path);
2033
2034         mdi->priv->child_list_path = g_strdup (path);
2035 }
2036
2037 /**
2038  * bonobo_mdi_register:
2039  * @mdi: A pointer to a BonoboMDI object.
2040  * @object: Object to register.
2041  * 
2042  * Description:
2043  * Registers GObject @object with MDI. 
2044  * This is mostly intended for applications that open other windows besides
2045  * those opened by the MDI and want to continue to run even when no MDI
2046  * windows exist (an example of this would be GIMP's window with tools, if
2047  * the pictures were MDI children). As long as there is an object registered
2048  * with the MDI, the MDI will not destroy itself when the last of its windows
2049  * is closed. If no objects are registered, closing the last MDI window
2050  * results in MDI being destroyed. 
2051  **/
2052 void 
2053 bonobo_mdi_register (BonoboMDI *mdi, GObject *object)
2054 {
2055         if (!g_slist_find (mdi->priv->registered, object))
2056                 mdi->priv->registered = g_slist_append (mdi->priv->registered, object);
2057 }
2058
2059 /**
2060  * bonobo_mdi_unregister:
2061  * @mdi: A pointer to a BonoboMDI object.
2062  * @object: Object to unregister.
2063  * 
2064  * Description:
2065  * Removes GObject @object from the list of registered objects. 
2066  **/
2067 void 
2068 bonobo_mdi_unregister (BonoboMDI *mdi, GObject *object)
2069 {
2070         mdi->priv->registered = g_slist_remove (mdi->priv->registered, object);
2071 }
2072
2073 /**
2074  * bonobo_mdi_get_child_from_view:
2075  * @view: A pointer to a GtkWidget.
2076  * 
2077  * Description:
2078  * Returns a child that @view is a view of.
2079  * 
2080  * Return value:
2081  * A pointer to the BonoboMDIChild the view belongs to.
2082  **/
2083 BonoboMDIChild *
2084 bonobo_mdi_get_child_from_view (GtkWidget *view)
2085 {
2086         return BONOBO_MDI_CHILD (g_object_get_data (G_OBJECT(view), BONOBO_MDI_CHILD_KEY));
2087 }
2088
2089 /**
2090  * bonobo_mdi_get_window_from_view:
2091  * @view: A pointer to a GtkWidget.
2092  * 
2093  * Description:
2094  * Returns the toplevel window for this view.
2095  * 
2096  * Return value:
2097  * A pointer to the BonoboWindow containg the specified view.
2098  **/
2099 BonoboWindow *
2100 bonobo_mdi_get_window_from_view (GtkWidget *view)
2101 {
2102         return BONOBO_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (view)));
2103 }
2104
2105 /**
2106  * bonobo_mdi_get_view_from_window:
2107  * @mdi: A pointer to a BonoboMDI object.
2108  * @win: A pointer to a BonoboWindow widget.
2109  * 
2110  * Description:
2111  * Returns the pointer to the view in the MDI toplevel window @win.
2112  * If the mode is set to %GNOME_MDI_NOTEBOOK, the view in the current
2113  * page is returned.
2114  * 
2115  * Return value: 
2116  * A pointer to a view.
2117  **/
2118 GtkWidget *
2119 bonobo_mdi_get_view_from_window (BonoboMDI *mdi, BonoboWindow *win)
2120 {
2121         GtkWidget *book;
2122         
2123         gl_debug (DEBUG_MDI, "");
2124
2125         g_return_val_if_fail (mdi != NULL, NULL);
2126         g_return_val_if_fail (BONOBO_IS_MDI (mdi), NULL);
2127         g_return_val_if_fail (win != NULL, NULL);
2128         g_return_val_if_fail (BONOBO_IS_WINDOW (win), NULL);
2129
2130         book = get_book_from_window (win);
2131         g_return_val_if_fail (book != NULL, NULL);
2132         
2133         if (GTK_NOTEBOOK (book)->cur_page) 
2134         {
2135                 int cur_page = gtk_notebook_get_current_page (GTK_NOTEBOOK (book));
2136
2137                 gl_debug (DEBUG_MDI, "END1");
2138
2139                 return gtk_notebook_get_nth_page (GTK_NOTEBOOK (book), cur_page);
2140         } 
2141         else 
2142         {
2143                 gl_debug (DEBUG_MDI, "END2");
2144
2145                 return NULL;
2146         }
2147 }
2148
2149 void 
2150 bonobo_mdi_construct (BonoboMDI *mdi, const gchar* name, const gchar* title,
2151                 gint default_window_width, gint default_window_height)
2152 {
2153         g_return_if_fail (mdi->priv->mdi_name == NULL);
2154         g_return_if_fail (mdi->priv->title == NULL);
2155
2156         g_return_if_fail (name != NULL);
2157         g_return_if_fail (title != NULL);
2158
2159         mdi->priv->mdi_name = g_strdup (name);
2160         mdi->priv->title = g_strdup (title);
2161
2162         mdi->priv->default_window_width = default_window_width;
2163         mdi->priv->default_window_height = default_window_height;
2164
2165 }
2166
2167 GList *
2168 bonobo_mdi_get_children (BonoboMDI *mdi)
2169 {
2170         g_return_val_if_fail (BONOBO_IS_MDI (mdi), FALSE);
2171
2172         return mdi->priv->children;
2173 }
2174
2175 GList *
2176 bonobo_mdi_get_windows (BonoboMDI *mdi)
2177 {
2178         g_return_val_if_fail (BONOBO_IS_MDI (mdi), FALSE);
2179
2180         return mdi->priv->windows;
2181 }
2182
2183         
2184
2185 BonoboUIComponent*
2186 bonobo_mdi_get_ui_component_from_window (BonoboWindow* win)
2187 {
2188         return BONOBO_UI_COMPONENT (
2189                         g_object_get_data (G_OBJECT (win), UI_COMPONENT_KEY));
2190 }
2191
2192 const BonoboMDIWindowInfo *
2193 bonobo_mdi_get_window_info (BonoboWindow *win)
2194 {
2195         return (const BonoboMDIWindowInfo *) 
2196                         g_object_get_data (G_OBJECT (win), WINDOW_INFO_KEY);
2197 }