* examples/hildon-stackable-window-example.c (new_window): * src/Makefile.am (noinst_...
[hildon] / src / hildon-program.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public License
10  * as published by the Free Software Foundation; version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 /**
26  * SECTION:hildon-program
27  * @short_description: An object that represents an application running in the Hildon framework.
28  *
29  * The HildonProgram is an object used to represent an application running
30  * in the Hildon framework.
31  * 
32  * Such an application is thought to have one or more HildonWindow. These
33  * shall be registered to the HildonProgram with hildon_program_add_window,
34  * and can be unregistered similarly with hildon_program_remove_window.
35  * 
36  * The HildonProgram provides the programmer with commodities such
37  * as applying a common toolbar and menu to all the HildonWindow
38  * registered to it. This is done with hildon_program_set_common_menu()
39  * and hildon_program_set_common_toolbar().
40  * 
41  * The HildonProgram is also used to apply program-wide properties that
42  * are specific to the Hildon framework. For instance
43  * hildon_program_set_can_hibernate() sets whether or not an application
44  * can be set to hibernate by the Hildon task navigator, in situations of
45  * low memory.
46  * 
47  * <example>
48  * <programlisting>
49  * HildonProgram *program;
50  * HildonWindow *first_window;
51  * HildonWindow *second_window;
52  * GtkToolbar *common_toolbar, *window_specific_toolbar;
53  * GtkMenu *menu;
54  * <!-- -->
55  * program = HILDON_PROGRAM (hildon_program_get_instance ());
56  * <!-- -->
57  * window1 = HILDON_WINDOW (hildon_window_new ());
58  * window2 = HILDON_WINDOW (hildon_window_new ());
59  * <!-- -->
60  * common_toolbar = create_common_toolbar ();
61  * window_specific_toolbar = create_window_specific_toolbar ();
62  * <!-- -->
63  * menu = create_menu ();
64  * <!-- -->
65  * hildon_program_add_window (program, window1);
66  * hildon_program_add_window (program, window2);
67  * <!-- -->
68  * hildon_program_set_common_menu (program, menu);
69  * <!-- -->
70  * hildon_program_set_common_toolbar (program, common_toolbar);
71  * hildon_window_add_toolbar (first_window, window_specific_toolbar);
72  * <!-- -->
73  * hildon_program_set_can_hibernate (program, TRUE);
74  * </programlisting>
75  * </example>
76  */
77
78 #ifdef                                          HAVE_CONFIG_H
79 #include                                        <config.h>
80 #endif
81
82 #include                                        "hildon-program.h"
83 #include                                        "hildon-program-private.h"
84 #include                                        "hildon-window-private.h"
85 #include                                        "hildon-stackable-window.h"
86 #include                                        "hildon-stackable-window-private.h"
87 #include                                        <X11/Xatom.h>
88
89 static void
90 hildon_program_init                             (HildonProgram *self);
91
92 static void
93 hildon_program_finalize                         (GObject *self);
94
95 static void
96 hildon_program_class_init                       (HildonProgramClass *self);
97
98 static void
99 hildon_program_get_property                     (GObject *object, 
100                                                  guint property_id,
101                                                  GValue *value, 
102                                                  GParamSpec *pspec);
103 static void
104 hildon_program_set_property                     (GObject *object, 
105                                                  guint property_id,
106                                                  const GValue *value, 
107                                                  GParamSpec *pspec);
108
109 enum
110 {
111     PROP_0,
112     PROP_IS_TOPMOST,
113     PROP_KILLABLE
114 };
115
116 GType G_GNUC_CONST
117 hildon_program_get_type                         (void)
118 {
119     static GType program_type = 0;
120
121     if (! program_type)
122     {
123         static const GTypeInfo program_info =
124         {
125             sizeof (HildonProgramClass),
126             NULL,       /* base_init */
127             NULL,       /* base_finalize */
128             (GClassInitFunc) hildon_program_class_init,
129             NULL,       /* class_finalize */
130             NULL,       /* class_data */
131             sizeof (HildonProgram),
132             0,  /* n_preallocs */
133             (GInstanceInitFunc) hildon_program_init,
134         };
135         program_type = g_type_register_static(G_TYPE_OBJECT,
136                 "HildonProgram", &program_info, 0);
137     }
138     return program_type;
139 }
140
141 static void
142 hildon_program_init                             (HildonProgram *self)
143 {
144     HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (self);
145     g_assert (priv);
146     
147     priv->killable = FALSE;
148     priv->window_count = 0;
149     priv->is_topmost = FALSE;
150     priv->window_group = GDK_WINDOW_XID (gdk_display_get_default_group (gdk_display_get_default()));
151     priv->common_toolbar = NULL;
152     priv->name = NULL;
153 }
154
155 static void
156 hildon_program_finalize                         (GObject *self)
157 {
158     HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (HILDON_PROGRAM (self));
159     g_assert (priv);
160     
161     if (priv->common_toolbar)
162     {
163         g_object_unref (priv->common_toolbar);
164         priv->common_toolbar = NULL;
165     }
166
167     if (priv->common_menu)
168     {
169         g_object_unref (priv->common_menu);
170         priv->common_menu = NULL;
171     }
172
173     g_free (priv->name);
174 }
175
176 static void
177 hildon_program_class_init                       (HildonProgramClass *self)
178 {
179     GObjectClass *object_class = G_OBJECT_CLASS (self);
180
181     g_type_class_add_private (self, sizeof (HildonProgramPrivate));
182
183     /* Set up object virtual functions */
184     object_class->finalize      = hildon_program_finalize;
185     object_class->set_property  = hildon_program_set_property;
186     object_class->get_property  = hildon_program_get_property;
187
188     /* Install properties */
189
190     /**
191      * HildonProgram:is-topmost:
192      *
193      * Whether one of the program's window or dialog currently
194      * is activated by window manager. 
195      */
196     g_object_class_install_property (object_class, PROP_IS_TOPMOST,
197                 g_param_spec_boolean ("is-topmost",
198                 "Is top-most",
199                 "Whether one of the program's window or dialog currently "
200                 "is activated by window manager",
201                 FALSE,
202                 G_PARAM_READABLE)); 
203
204     /**
205      * HildonProgram:can-hibernate:
206      *
207      * Whether the program should be set to hibernate by the Task
208      * Navigator in low memory situation.
209      */
210     g_object_class_install_property (object_class, PROP_KILLABLE,
211                 g_param_spec_boolean ("can-hibernate",
212                 "Can hibernate",
213                 "Whether the program should be set to hibernate by the Task "
214                 "Navigator in low memory situation",
215                 FALSE,
216                 G_PARAM_READWRITE)); 
217     return;
218 }
219
220 static void
221 hildon_program_set_property                     (GObject *object, 
222                                                  guint property_id,
223                                                  const GValue *value, 
224                                                  GParamSpec *pspec)
225 {
226     switch (property_id) {
227
228         case PROP_KILLABLE:
229             hildon_program_set_can_hibernate (HILDON_PROGRAM (object), g_value_get_boolean (value));
230             break;
231             
232         default:
233             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
234             break;
235     }
236
237 }
238
239 static void
240 hildon_program_get_property                     (GObject *object, 
241                                                  guint property_id,
242                                                  GValue *value, 
243                                                  GParamSpec *pspec)
244 {
245     HildonProgramPrivate *priv = HILDON_PROGRAM_GET_PRIVATE (object);
246     g_assert (priv);
247
248     switch (property_id)
249     {
250         case PROP_KILLABLE:
251             g_value_set_boolean (value, priv->killable);
252             break;
253
254         case PROP_IS_TOPMOST:
255             g_value_set_boolean (value, priv->is_topmost);
256             break;
257
258         default:
259             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
260             break;
261     }
262 }
263
264 /* Utilities */
265 static gint 
266 hildon_program_window_list_compare              (gconstpointer window_a, 
267                                                  gconstpointer window_b)
268 {
269     g_return_val_if_fail (HILDON_IS_WINDOW(window_a) && 
270                           HILDON_IS_WINDOW(window_b), 1);
271
272     return window_a != window_b;
273 }
274
275 /*
276  * foreach function, checks if a window is topmost and acts consequently
277  */
278 static void
279 hildon_program_window_list_is_is_topmost        (gpointer data, 
280                                                  gpointer window_id_)
281 {
282     if (data && HILDON_IS_WINDOW (data))
283     {
284         HildonWindow *window = HILDON_WINDOW (data);
285         Window window_id = * (Window*)window_id_;
286
287         hildon_window_update_topmost (window, window_id);
288     }
289 }
290
291 /*
292  * Check the _MB_CURRENT_APP_WINDOW on the root window, and update
293  * the top_most status accordingly
294  */
295 static void
296 hildon_program_update_top_most                  (HildonProgram *program)
297 {
298     XWMHints *wm_hints;
299     Window active_window;
300     HildonProgramPrivate *priv;
301
302     priv = HILDON_PROGRAM_GET_PRIVATE (program);
303     g_assert (priv);
304     
305     active_window = hildon_window_get_active_window();
306
307     if (active_window)
308     {
309       gint xerror;
310       
311       gdk_error_trap_push ();
312       wm_hints = XGetWMHints (GDK_DISPLAY (), active_window);
313       xerror = gdk_error_trap_pop ();
314       if (xerror)
315         return;
316
317       if (wm_hints)
318       {
319
320           if (wm_hints->window_group == priv->window_group)
321           {
322               if (!priv->is_topmost)
323               {
324                   priv->is_topmost = TRUE;
325                   g_object_notify (G_OBJECT (program), "is-topmost");
326               }
327           }
328           else if (priv->is_topmost)
329           {
330             priv->is_topmost = FALSE;
331             g_object_notify (G_OBJECT (program), "is-topmost");
332           }
333       }
334       XFree (wm_hints);
335     }
336
337     /* Check each window if it was is_topmost */
338     g_slist_foreach (priv->windows, 
339             (GFunc)hildon_program_window_list_is_is_topmost, &active_window);
340 }
341
342 /*
343  * We keep track of the _MB_CURRENT_APP_WINDOW property on the root window,
344  * to detect when a window belonging to this program was is_topmost. This
345  * is based on the window group WM hint.
346  */
347 static GdkFilterReturn
348 hildon_program_root_window_event_filter         (GdkXEvent *xevent,
349                                                  GdkEvent *event,
350                                                  gpointer data)
351 {
352     XAnyEvent *eventti = xevent;
353     HildonProgram *program = HILDON_PROGRAM (data);
354     Atom active_app_atom =
355             XInternAtom (GDK_DISPLAY (), "_MB_CURRENT_APP_WINDOW", False);
356
357     if (eventti->type == PropertyNotify)
358     {
359         XPropertyEvent *pevent = xevent;
360
361         if (pevent->atom == active_app_atom)
362         {
363             hildon_program_update_top_most (program);
364         }
365     }
366
367     return GDK_FILTER_CONTINUE;
368 }
369
370 /* 
371  * Checks if the window is the topmost window of the program and in
372  * that case forces the window to take the common toolbar.
373  */
374 static void
375 hildon_program_common_toolbar_topmost_window    (gpointer window, 
376                                                  gpointer data)
377 {
378     if (HILDON_IS_WINDOW (window) && hildon_window_get_is_topmost (HILDON_WINDOW (window)))
379         hildon_window_take_common_toolbar (HILDON_WINDOW (window));
380 }
381
382 /**
383  * hildon_program_get_instance:
384  *
385  * Return value: Returns the #HildonProgram for the current process.
386  * The object is created on the first call. Note that you're not supposed 
387  * to unref the returned object since it's not reffed in the first place.
388  **/
389 HildonProgram*
390 hildon_program_get_instance                     (void)
391 {
392     static HildonProgram *program = NULL;
393
394     if (! program)
395     {
396         program = g_object_new (HILDON_TYPE_PROGRAM, NULL);
397     }
398
399     return program;
400 }
401
402 /**
403  * hildon_program_add_window:
404  * @self: The @HildonProgram to which the window should be registered
405  * @window: A @HildonWindow to be added
406  *
407  * Registers a @HildonWindow as belonging to a given @HildonProgram. This
408  * allows to apply program-wide settings as all the registered windows,
409  * such as hildon_program_set_common_menu() and
410  * hildon_pogram_set_common_toolbar()
411  **/
412 void
413 hildon_program_add_window                       (HildonProgram *self, 
414                                                  HildonWindow *window)
415 {
416     HildonProgramPrivate *priv;
417     
418     g_return_if_fail (HILDON_IS_PROGRAM (self));
419     
420     priv = HILDON_PROGRAM_GET_PRIVATE (self);
421     g_assert (priv);
422
423     if (g_slist_find_custom (priv->windows, window,
424            hildon_program_window_list_compare) )
425     {
426         /* We already have that window */
427         return;
428     }
429
430     if (!priv->window_count)
431     {
432         hildon_program_update_top_most (self);
433         
434         /* Now that we have a window we should start keeping track of
435          * the root window */
436         gdk_window_set_events (gdk_get_default_root_window (),
437                 gdk_window_get_events (gdk_get_default_root_window ()) | GDK_PROPERTY_CHANGE_MASK);
438
439         gdk_window_add_filter (gdk_get_default_root_window (),
440                 hildon_program_root_window_event_filter, self );
441     }
442     
443     hildon_window_set_can_hibernate_property (window, &priv->killable);
444
445     hildon_window_set_program (window, G_OBJECT (self));
446
447     priv->windows = g_slist_append (priv->windows, window);
448     priv->window_count ++;
449 }
450
451 /**
452  * hildon_program_remove_window:
453  * @self: The #HildonProgram to which the window should be unregistered
454  * @window: The @HildonWindow to unregister
455  *
456  * Used to unregister a window from the program. Subsequent calls to
457  * hildon_program_set_common_menu() and hildon_pogram_set_common_toolbar()
458  * will not affect the window
459  **/
460 void
461 hildon_program_remove_window                    (HildonProgram *self, 
462                                                  HildonWindow *window)
463 {
464     HildonProgramPrivate *priv;
465     
466     g_return_if_fail (HILDON_IS_PROGRAM (self));
467     
468     priv = HILDON_PROGRAM_GET_PRIVATE (self);
469     g_assert (priv);
470     
471     hildon_window_unset_program (window);
472
473     priv->windows = g_slist_remove (priv->windows, window);
474
475     priv->window_count --;
476
477     if (! priv->window_count)
478         gdk_window_remove_filter (gdk_get_default_root_window(),
479                 hildon_program_root_window_event_filter,
480                 self);
481 }
482
483 /**
484  * hildon_program_set_can_hibernate:
485  * @self: The #HildonProgram which can hibernate or not
486  * @can_hibernate: whether or not the #HildonProgram can hibernate
487  *
488  * Used to set whether or not the Hildon task navigator should
489  * be able to set the program to hibernation in case of low memory
490  **/
491 void
492 hildon_program_set_can_hibernate                (HildonProgram *self, 
493                                                  gboolean can_hibernate)
494 {
495     HildonProgramPrivate *priv;
496     
497     g_return_if_fail (HILDON_IS_PROGRAM (self));
498     
499     priv = HILDON_PROGRAM_GET_PRIVATE (self);
500     g_assert (priv);
501
502     if (priv->killable != can_hibernate)
503     {
504         g_slist_foreach (priv->windows, 
505                 (GFunc) hildon_window_set_can_hibernate_property, &can_hibernate);
506     }
507
508     priv->killable = can_hibernate;
509 }
510
511 /**
512  * hildon_program_get_can_hibernate:
513  * @self: The #HildonProgram which can hibernate or not
514  * 
515  * Return value: Whether or not this #HildonProgram is set to be
516  * support hibernation from the Hildon task navigator
517  **/
518 gboolean
519 hildon_program_get_can_hibernate                (HildonProgram *self)
520 {
521     HildonProgramPrivate *priv;
522     
523     g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
524    
525     priv = HILDON_PROGRAM_GET_PRIVATE (self);
526     g_assert (priv);
527
528     return priv->killable;
529 }
530
531 /**
532  * hildon_program_set_common_menu:
533  * @self: The #HildonProgram in which the common menu should be used
534  * @menu: A GtkMenu to use as common menu for the program
535  *
536  * Sets a GtkMenu that will appear in all the @HildonWindow registered
537  * to the #HildonProgram. Only one common GtkMenu can be set, further
538  * call will detach the previous common GtkMenu. A @HildonWindow
539  * can use it's own GtkMenu with @hildon_window_set_menu
540  **/
541 void
542 hildon_program_set_common_menu                  (HildonProgram *self, 
543                                                  GtkMenu *menu)
544 {
545     HildonProgramPrivate *priv;
546
547     g_return_if_fail (HILDON_IS_PROGRAM (self));
548
549     priv = HILDON_PROGRAM_GET_PRIVATE (self);
550     g_assert (priv);
551
552     if (priv->common_menu)
553     {
554         if (GTK_WIDGET_VISIBLE (priv->common_menu))
555         {
556             gtk_menu_popdown (GTK_MENU (priv->common_menu));
557             gtk_menu_shell_deactivate (GTK_MENU_SHELL (priv->common_menu));
558         }
559
560         if (gtk_menu_get_attach_widget (GTK_MENU (priv->common_menu)))
561         {
562             gtk_menu_detach (GTK_MENU (priv->common_menu));
563         }
564         else
565         {
566             g_object_unref (priv->common_menu);
567         }
568     }
569
570     priv->common_menu = GTK_WIDGET (menu);
571
572     if (priv->common_menu)
573     {
574         g_object_ref (menu);
575         gtk_object_sink (GTK_OBJECT (menu));
576         gtk_widget_show_all (GTK_WIDGET (menu));
577     }
578 }
579
580 /**
581  * hildon_program_get_common_menu:
582  * @self: The #HildonProgram from which to retrieve the common menu
583  *
584  * Return value: the GtkMenu that was set as common menu for this
585  * #HildonProgram, or NULL of no common menu was set.
586  **/
587 GtkMenu*
588 hildon_program_get_common_menu                  (HildonProgram *self)
589 {
590     HildonProgramPrivate *priv;
591
592     g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
593
594     priv = HILDON_PROGRAM_GET_PRIVATE (self);
595     g_assert (priv);
596
597     return GTK_MENU (priv->common_menu);
598 }
599
600 /**
601  * hildon_program_set_common_toolbar:
602  * @self: The #HildonProgram in which the common toolbar should be used
603  * @toolbar: A GtkToolbar to use as common toolbar for the program
604  *
605  * Sets a GtkToolbar that will appear in all the @HildonWindow registered
606  * to the #HildonProgram. Only one common GtkToolbar can be set, further
607  * call will detach the previous common GtkToolbar. A @HildonWindow
608  * can use its own GtkToolbar with @hildon_window_set_toolbar. Both
609  * #HildonProgram and @HildonWindow specific toolbars will be shown
610  **/
611 void
612 hildon_program_set_common_toolbar               (HildonProgram *self, 
613                                                  GtkToolbar *toolbar)
614 {
615     HildonProgramPrivate *priv;
616
617     g_return_if_fail (HILDON_IS_PROGRAM (self));
618
619     priv = HILDON_PROGRAM_GET_PRIVATE (self);
620     g_assert (priv);
621
622     if (priv->common_toolbar)
623     {
624         if (priv->common_toolbar->parent)
625         {
626             gtk_container_remove (GTK_CONTAINER (priv->common_toolbar->parent), 
627                                   priv->common_toolbar);
628         }
629         
630         g_object_unref (priv->common_toolbar);
631     }
632
633     priv->common_toolbar = GTK_WIDGET (toolbar);
634
635     if (priv->common_toolbar)
636     {
637         g_object_ref (priv->common_toolbar);
638         gtk_object_sink (GTK_OBJECT (priv->common_toolbar) );
639     }
640
641     /* if the program is the topmost we have to update the common
642        toolbar right now for the topmost window */
643     if (priv->is_topmost)
644       {
645         g_slist_foreach (priv->windows, 
646                          (GFunc) hildon_program_common_toolbar_topmost_window, NULL);
647       }
648 }
649
650 /**
651  * hildon_program_get_common_toolbar:
652  * @self: The #HildonProgram from which to retrieve the common toolbar
653  *
654  * Return value: the GtkToolbar that was set as common toolbar for this
655  * #HildonProgram, or NULL of no common menu was set.
656  **/
657 GtkToolbar*
658 hildon_program_get_common_toolbar               (HildonProgram *self)
659 {
660     HildonProgramPrivate *priv;
661
662     g_return_val_if_fail (HILDON_IS_PROGRAM (self), NULL);
663
664     priv = HILDON_PROGRAM_GET_PRIVATE (self);
665     g_assert (priv);
666
667     return priv->common_toolbar ? GTK_TOOLBAR (priv->common_toolbar) : NULL;
668 }
669
670 /**
671  * hildon_program_get_is_topmost:
672  * @self: A #HildonWindow
673  *
674  * Return value: Whether or not one of the program's window or dialog is 
675  * currenltly activated by the window manager.
676  **/
677 gboolean
678 hildon_program_get_is_topmost                   (HildonProgram *self)
679 {
680     HildonProgramPrivate *priv;
681
682     g_return_val_if_fail (HILDON_IS_PROGRAM (self), FALSE);
683     
684     priv = HILDON_PROGRAM_GET_PRIVATE (self);
685     g_assert (priv);
686
687     return priv->is_topmost;
688 }
689
690 /**
691  * hildon_program_go_to_root_window:
692  * @self: A #HildonProgram
693  *
694  * Will close all windows in the #HildonProgram but the first one (the
695  * root window) by sending them a delete event. If any of the windows
696  * refuses to close (by handling it) no further events will be
697  * sent. All windows in the program must be #HildonStackableWindow for
698  * this to work.
699  */
700 void
701 hildon_program_go_to_root_window                (HildonProgram *self)
702 {
703     HildonProgramPrivate *priv;
704     GSList *windows, *iter;
705     gboolean windows_left;
706
707     g_return_if_fail (HILDON_IS_PROGRAM (self));
708     priv = HILDON_PROGRAM_GET_PRIVATE (self);
709     g_assert (priv);
710
711     /* List of windows in reverse order (starting from the topmost one) */
712     windows = g_slist_reverse (g_slist_copy (priv->windows));
713     iter = windows;
714
715     /* Destroy all the windows but the last one (which is the root
716      * window, as the list is reversed) */
717     windows_left = (iter != NULL && iter->next != NULL);
718     while (windows_left)
719     {
720         if (HILDON_IS_STACKABLE_WINDOW (iter->data))
721         {
722             GdkEvent *event;
723             HildonStackableWindow *win;
724
725             /* Mark the window as "going home" */
726             win = HILDON_STACKABLE_WINDOW (iter->data);
727             hildon_stackable_window_set_going_home (win, TRUE);
728
729             /* Set win pointer to NULL if the window is destroyed */
730             g_object_add_weak_pointer (G_OBJECT (win), (gpointer) &win);
731
732             /* Send a delete event */
733             event = gdk_event_new (GDK_DELETE);
734             event->any.window = g_object_ref (GTK_WIDGET (win)->window);
735             gtk_main_do_event (event);
736             gdk_event_free (event);
737
738             /* Continue sending delete events if the window has been destroyed */
739             if (win == NULL)
740             {
741                 iter = iter->next;
742                 windows_left = (iter != NULL && iter->next != NULL);
743             }
744             else
745             {
746                 g_object_remove_weak_pointer (G_OBJECT (win), (gpointer) &win);
747                 hildon_stackable_window_set_going_home (win, FALSE);
748                 windows_left = FALSE;
749             }
750         }
751         else
752         {
753             g_warning ("Window list contains a non-stackable window");
754             windows_left = FALSE;
755         }
756     }
757
758     /* Show the last window that hasn't been destroyed */
759     if (iter != NULL && GTK_IS_WIDGET (iter->data))
760     {
761         gtk_widget_show (GTK_WIDGET (iter->data));
762     }
763
764     g_slist_free (windows);
765 }