Imported Upstream version 1.0beta2a
[platform/upstream/syncevolution.git] / src / gtk-ui / mux-window.c
1 /*
2  * Copyright (C) 2009 Intel Corporation
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) version 3.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301  USA
18  */
19
20 #include "mux-window.h"
21 #include "mux-icon-button.h"
22
23 static GdkColor mux_window_default_title_bar_bg = { 0, 0x3300, 0x3300, 0x3300 };
24
25 #define MUX_WINDOW_DEFAULT_TITLE_BAR_HEIGHT 63
26
27 GType
28 mux_decorations_get_type (void)
29 {
30   static GType etype = 0;
31   if (etype == 0) {
32     static const GFlagsValue values[] = {
33       { MUX_DECOR_CLOSE, "MUX_CLOSE", "close" },
34       { MUX_DECOR_SETTINGS, "MUX_SETTINGS", "settings" },
35       { 0, NULL, NULL }
36     };
37     etype = g_flags_register_static (g_intern_static_string ("MuxDecorations"), values);
38   }
39   return etype;
40 }
41
42
43 enum {
44   PROP_0,
45   PROP_DECORATIONS,
46   PROP_BACK_TITLE,
47 };
48
49 enum {
50   SETTINGS_VISIBILITY_CHANGED,
51   LAST_SIGNAL
52 };
53
54 static guint mux_window_signals[LAST_SIGNAL] = { 0 };
55
56 G_DEFINE_TYPE (MuxWindow, mux_window, GTK_TYPE_WINDOW)
57
58 #define GET_PRIVATE(o) \
59     (G_TYPE_INSTANCE_GET_PRIVATE ((o), MUX_TYPE_WINDOW, MuxWindowPrivate))
60
61 typedef struct _MuxWindowPrivate MuxWindowPrivate;
62
63 struct _MuxWindowPrivate {
64     int dummy;
65 };
66
67 static void
68 mux_window_get_property (GObject *object, guint property_id,
69                          GValue *value, GParamSpec *pspec)
70 {
71     MuxWindow *win = MUX_WINDOW (object);
72
73     switch (property_id) {
74     case PROP_DECORATIONS:
75         g_value_set_uint (value, win->decorations);
76         break;
77     case PROP_BACK_TITLE:
78         g_value_set_string (value,
79                             gtk_button_get_label (GTK_BUTTON (win->back_btn)));
80         break;
81     default:
82         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
83     }
84 }
85
86 static void
87 mux_window_set_property (GObject *object, guint property_id,
88                          const GValue *value, GParamSpec *pspec)
89 {
90     MuxWindow *win = MUX_WINDOW (object);
91
92     switch (property_id) {
93     case PROP_DECORATIONS:
94         mux_window_set_decorations (win, g_value_get_uint (value));
95         break;
96     case PROP_BACK_TITLE:
97         g_free (win->back_title);
98         win->back_title = g_strdup (g_value_get_string (value));
99         if (win->back_btn) {
100             gtk_button_set_label (GTK_BUTTON (win->back_btn), win->back_title);
101         }
102         break;
103     default:
104         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
105     }
106 }
107
108 static void
109 mux_window_update_style (MuxWindow *win)
110 {
111     GdkColor *title_bar_bg = NULL;
112     guint title_bar_height;
113
114     g_return_if_fail (win->title_bar);
115
116     gtk_widget_style_get (GTK_WIDGET (win),
117                           "title-bar-height", &title_bar_height, 
118                           "title-bar-bg", &title_bar_bg,
119                           NULL);
120
121     if (title_bar_bg) {
122         gtk_widget_modify_bg (win->title_bar, GTK_STATE_NORMAL, title_bar_bg);
123         gdk_color_free (title_bar_bg);
124     } else {
125         gtk_widget_modify_bg (win->title_bar, GTK_STATE_NORMAL, 
126                               &mux_window_default_title_bar_bg);
127     }
128
129     gtk_widget_set_size_request (win->title_bar, -1, title_bar_height);
130 }
131
132 static void 
133 mux_window_style_set (GtkWidget *widget,
134                       GtkStyle *previous)
135 {
136     MuxWindow *win = MUX_WINDOW (widget);
137
138     mux_window_update_style (win);
139
140     GTK_WIDGET_CLASS (mux_window_parent_class)->style_set (widget, previous);
141 }
142
143 static void
144 mux_window_forall (GtkContainer *container,
145                    gboolean include_internals,
146                    GtkCallback callback,
147                    gpointer callback_data)
148 {
149     MuxWindow *mux_win = MUX_WINDOW (container);
150     GtkBin *bin = GTK_BIN (container);
151
152     /* FIXME: call parents forall instead ? */
153     if (bin->child)
154         (* callback) (bin->child, callback_data);
155
156     if (mux_win->title_bar)
157         (* callback) (mux_win->title_bar, callback_data);
158     if (mux_win->notebook)
159         (* callback) (mux_win->notebook, callback_data);
160 }
161
162 static void
163 mux_window_add (GtkContainer *container,
164                 GtkWidget *widget)
165 {
166     MuxWindowClass *klass;
167     GtkContainerClass *parent_container_class;
168
169     klass = MUX_WINDOW_GET_CLASS (container);
170     parent_container_class = GTK_CONTAINER_CLASS (g_type_class_peek_parent (klass));
171
172     parent_container_class->add (container, widget);
173 }
174
175 static void
176 mux_window_remove (GtkContainer *container,
177                    GtkWidget *child)
178 {
179     MuxWindow *win = MUX_WINDOW (container);
180     GtkBin *bin = GTK_BIN (container);
181
182     if (child == win->title_bar) {
183         gtk_widget_unparent (win->title_bar);
184         win->title_bar = NULL;
185     } else if (child == win->notebook) {
186         gtk_widget_unparent (win->notebook);
187         win->notebook = NULL;
188     } else if (bin->child) {
189         if (bin->child == child) {
190             /* should call parents remove... */
191             gtk_widget_unparent (child);
192             bin->child = NULL;
193         }
194     }
195 }
196
197 static void
198 mux_window_size_request (GtkWidget *widget,
199                          GtkRequisition *requisition)
200 {
201     GtkBin *bin = GTK_BIN (widget);
202     MuxWindow *mux_win = MUX_WINDOW (widget);
203     GtkRequisition req;
204
205     /* we will always be maximized so none of this should be necessary
206      * (requisition will never be used), but some widgets to assume
207      * size_request is called */
208     if (mux_win->title_bar && GTK_WIDGET_VISIBLE (mux_win->title_bar))
209         gtk_widget_size_request (mux_win->title_bar, &req);
210     if (bin->child && GTK_WIDGET_VISIBLE (bin->child))
211         gtk_widget_size_request (bin->child, &req);
212     if (mux_win->notebook && GTK_WIDGET_VISIBLE (mux_win->notebook))
213         gtk_widget_size_request (mux_win->notebook, &req);
214
215     requisition->width = 1024;
216     requisition->height = 600;
217 }
218
219 static void
220 mux_window_size_allocate (GtkWidget *widget,
221                           GtkAllocation *allocation)
222 {
223     GtkBin *bin = GTK_BIN (widget);
224     MuxWindow *mux_win = MUX_WINDOW (widget);
225     GtkAllocation child_allocation;
226     int xmargin, ymargin, title_height;
227
228     widget->allocation = *allocation;
229     xmargin = GTK_CONTAINER (widget)->border_width +
230               widget->style->xthickness;
231     ymargin = GTK_CONTAINER (widget)->border_width +
232               widget->style->ythickness;
233     title_height = 0;
234
235     if (mux_win->title_bar) {
236         GtkAllocation title_allocation;
237         GtkRequisition title_req;
238         gtk_widget_get_child_requisition (mux_win->title_bar, &title_req);
239
240         title_height = title_req.height;
241         title_allocation.x = allocation->x;
242         title_allocation.y = allocation->y;
243         title_allocation.width = allocation->width;
244         title_allocation.height = title_height;
245         gtk_widget_size_allocate (mux_win->title_bar, &title_allocation);
246
247     }
248
249     child_allocation.x = allocation->x + xmargin;
250     child_allocation.y = allocation->y + title_height + ymargin;
251     child_allocation.width = allocation->width - 2 * xmargin;
252     child_allocation.height = allocation->height - (2 * ymargin + title_height);
253
254     if (GTK_WIDGET_MAPPED (widget) &&
255         (child_allocation.x != mux_win->child_allocation.x ||
256          child_allocation.y != mux_win->child_allocation.y ||
257          child_allocation.width != mux_win->child_allocation.width ||
258          child_allocation.height != mux_win->child_allocation.height)) {
259         gdk_window_invalidate_rect (widget->window, &widget->allocation, FALSE);
260     }
261
262     if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) {
263         gtk_widget_size_allocate (bin->child, &child_allocation);
264     }
265
266     if (mux_win->notebook && GTK_WIDGET_VISIBLE (mux_win->notebook)) {
267         gtk_widget_size_allocate (mux_win->notebook, &child_allocation);
268     }
269
270     mux_win->child_allocation = child_allocation;
271 }
272
273 static void
274 mux_window_class_init (MuxWindowClass *klass)
275 {
276     GObjectClass *object_class = G_OBJECT_CLASS (klass);
277     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
278     GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
279     GParamSpec *pspec;
280
281     g_type_class_add_private (klass, sizeof (MuxWindowPrivate));
282
283     object_class->get_property = mux_window_get_property;
284     object_class->set_property = mux_window_set_property;
285
286     widget_class->size_request = mux_window_size_request;
287     widget_class->size_allocate = mux_window_size_allocate;
288     widget_class->style_set = mux_window_style_set;
289
290     container_class->forall = mux_window_forall;
291     container_class->remove = mux_window_remove;
292     container_class->add = mux_window_add;
293
294     pspec = g_param_spec_uint ("title-bar-height",
295                                "Title bar height",
296                                "Total height of the title bar",
297                                0, G_MAXUINT, MUX_WINDOW_DEFAULT_TITLE_BAR_HEIGHT,
298                                G_PARAM_READWRITE);
299     gtk_widget_class_install_style_property(widget_class, pspec);
300     pspec = g_param_spec_boxed ("title-bar-bg",
301                                 "Title bar bg color",
302                                 "Color of the title bar background",
303                                 GDK_TYPE_COLOR,
304                                 G_PARAM_READWRITE);
305     gtk_widget_class_install_style_property(widget_class, pspec);
306
307     pspec = g_param_spec_flags ("decorations", 
308                                NULL,
309                                "Bitfield of MuxDecorations defining used window decorations",
310                                MUX_TYPE_DECORATIONS, 
311                                MUX_DECOR_CLOSE,
312                                G_PARAM_READWRITE);
313     g_object_class_install_property (object_class,
314                                      PROP_DECORATIONS,
315                                      pspec);
316
317     pspec = g_param_spec_string ("back-title", 
318                                  NULL,
319                                  "title of the back button in the window decoration",
320                                  "", 
321                                  G_PARAM_READWRITE);
322     g_object_class_install_property (object_class,
323                                      PROP_BACK_TITLE,
324                                      pspec);
325
326     mux_window_signals[SETTINGS_VISIBILITY_CHANGED] = 
327             g_signal_new ("settings-visibility-changed",
328                           G_OBJECT_CLASS_TYPE (object_class),
329                           G_SIGNAL_RUN_FIRST,
330                           G_STRUCT_OFFSET (MuxWindowClass, settings_visibility_changed),
331                           NULL, NULL,
332                           g_cclosure_marshal_VOID__VOID,
333                           G_TYPE_NONE, 0, NULL);
334
335 }
336
337 static void
338 mux_window_settings_clicked (MuxIconButton *button, MuxWindow *window)
339 {
340     gboolean active;
341     active = mux_icon_button_get_active (button);
342     mux_window_set_settings_visible (window, active);
343 }
344
345 static void
346 mux_window_close_clicked (MuxWindow *window)
347 {
348     /* this is how GtkDialog does it... */
349     GdkEvent *event;
350
351     event = gdk_event_new (GDK_DELETE);
352     event->any.window = g_object_ref (GTK_WIDGET (window)->window);
353     event->any.send_event = TRUE;
354
355     gtk_main_do_event (event);
356     gdk_event_free (event);
357 }
358
359 static GdkPixbuf*
360 load_icon (MuxWindow *window, const char *icon_name)
361 {
362     static GtkIconTheme *theme = NULL;
363     GdkScreen *screen;
364     GdkPixbuf *pixbuf;
365
366     if (!theme) {
367         screen = gtk_widget_get_screen (GTK_WIDGET (window));
368         theme = gtk_icon_theme_get_for_screen (screen);
369     }
370
371     pixbuf = gtk_icon_theme_load_icon (theme, icon_name,
372                                        48, 0, NULL);
373
374
375     /* FIXME: workaround until icons are in Moblin Netbook theme */
376     if (!pixbuf) {
377         char *str = g_strdup_printf ("%s/%s.png", THEMEDIR, icon_name);
378         pixbuf = gdk_pixbuf_new_from_file_at_size (str, 48, 48, NULL);
379
380         g_free (str);
381     }
382
383     if (!pixbuf) {
384         g_warning ("Icon '%s' not found in theme", icon_name);
385         pixbuf = gtk_widget_render_icon (GTK_WIDGET (window),
386                                          GTK_STOCK_MISSING_IMAGE,
387                                          GTK_ICON_SIZE_DIALOG,
388                                          NULL);
389
390     }
391     return pixbuf;
392 }
393
394 static void
395 bread_crumb_clicked_cb (GtkButton *btn, MuxWindow *window)
396 {
397     mux_window_set_current_page (window, -1);
398 }
399
400 static void
401 mux_window_build_title_bar (MuxWindow *window)
402 {
403     GtkWidget *box, *button_box, *btn, *align;
404     GdkPixbuf *pixbuf, *pixbuf_hover;
405
406     if (window->title_bar) {
407         gtk_widget_unparent (window->title_bar);
408     }
409
410     window->title_bar = gtk_event_box_new ();
411     gtk_widget_set_name (window->title_bar, "mux_window_title_bar");
412     gtk_widget_set_parent (window->title_bar, GTK_WIDGET (window));
413     gtk_widget_show (window->title_bar);
414
415     box = gtk_hbox_new (FALSE, 0);
416     gtk_container_add (GTK_CONTAINER (window->title_bar), box);
417     gtk_widget_show (box);
418
419     align = gtk_alignment_new (0, 0.5, 0, 0);
420     gtk_box_pack_start (GTK_BOX (box), align, FALSE, FALSE, 4);
421     gtk_widget_show (align);
422
423     button_box = gtk_hbox_new (FALSE, 0);
424     gtk_widget_set_name (window->title_bar, "mux_window_bread_crumbs");
425     gtk_container_add (GTK_CONTAINER (align), button_box);
426     gtk_widget_show (button_box);
427
428     window->back_btn = gtk_button_new_with_label (window->back_title);
429     gtk_box_pack_start (GTK_BOX (button_box), window->back_btn,
430                         FALSE, FALSE, 4);
431     g_signal_connect (window->back_btn, "clicked",
432                       G_CALLBACK (bread_crumb_clicked_cb), window);
433     if (mux_window_get_current_page (window) != -1) {
434         gtk_widget_show (window->back_btn);
435     }
436     /*window->title_label = gtk_label_new (gtk_window_get_title (GTK_WINDOW (window)));
437     gtk_box_pack_start (GTK_BOX (box), window->title_label,
438                         FALSE, FALSE, 0);
439     gtk_widget_show (window->title_label);*/
440
441     button_box = gtk_hbox_new (TRUE, 0);
442     gtk_box_pack_end (GTK_BOX (box), button_box, FALSE, FALSE, 4);
443     gtk_widget_show (button_box);
444
445     if (window->decorations & MUX_DECOR_CLOSE) {
446         pixbuf = load_icon (window, "close");
447         pixbuf_hover = load_icon (window, "close_hover");
448         btn = g_object_new (MUX_TYPE_ICON_BUTTON,
449                             "normal-state-pixbuf", pixbuf,
450                             "prelight-state-pixbuf", pixbuf_hover,
451                             NULL);
452         gtk_widget_set_name (btn, "mux_icon_button_close");
453         g_signal_connect_swapped (btn, "clicked", 
454                                   G_CALLBACK (mux_window_close_clicked), window);
455         gtk_box_pack_end (GTK_BOX (button_box), btn, FALSE, FALSE, 0);
456         gtk_widget_show (btn);
457     }
458
459     if (window->decorations & MUX_DECOR_SETTINGS) {
460         pixbuf = load_icon (window, "settings");
461         pixbuf_hover = load_icon (window, "settings_hover");
462         window->settings_button = g_object_new (MUX_TYPE_ICON_BUTTON,
463                                   "normal-state-pixbuf", pixbuf,
464                                   "prelight-state-pixbuf", pixbuf_hover,
465                                   "active-state-pixbuf", pixbuf_hover,
466                                   "toggleable", TRUE,
467                                   NULL);
468         gtk_widget_set_name (window->settings_button, "mux_icon_button_settings");
469         g_signal_connect (window->settings_button, "clicked", 
470                           G_CALLBACK (mux_window_settings_clicked), window);
471         gtk_box_pack_end (GTK_BOX (button_box), window->settings_button, FALSE, FALSE, 0);
472         gtk_widget_show (window->settings_button);
473     }
474
475     mux_window_update_style (window);
476
477     gtk_widget_map (window->title_bar); /*TODO: is there a better way to do this ? */
478     if (GTK_WIDGET_VISIBLE (window))
479         gtk_widget_queue_resize (GTK_WIDGET (window));
480
481 }
482
483 static void
484 mux_window_title_changed (MuxWindow *window, 
485                           GParamSpec *pspec,
486                           gpointer user_data)
487 {
488     mux_window_build_title_bar (window);
489 }
490
491 /* For some reason metacity sometimes won't maximize but will if asked 
492  * another time. For the record, I'm not proud of writing this */
493 static gboolean
494 mux_window_try_maximize (MuxWindow *self)
495 {
496     static int count = 0;
497
498     count++;
499     gtk_window_maximize (GTK_WINDOW (self));
500
501     return (count < 10);
502 }
503
504 static void
505 mux_window_init (MuxWindow *self)
506 {
507     self->decorations = MUX_DECOR_CLOSE;
508
509     gtk_window_set_decorated (GTK_WINDOW (self), FALSE);
510
511     g_signal_connect (self, "notify::title",
512                       G_CALLBACK (mux_window_title_changed), NULL);
513
514     mux_window_build_title_bar (self);
515
516     self->notebook = gtk_notebook_new ();
517     gtk_notebook_set_show_tabs (GTK_NOTEBOOK (self->notebook), FALSE);
518     gtk_notebook_set_show_border (GTK_NOTEBOOK (self->notebook), FALSE);
519     gtk_widget_set_parent (self->notebook, GTK_WIDGET (self));
520     self->settings_index = -2;
521
522
523     gtk_window_maximize (GTK_WINDOW (self));
524     g_timeout_add (10, (GSourceFunc)mux_window_try_maximize, self);
525 }
526
527 GtkWidget*
528 mux_window_new (const char *back_title)
529 {
530     return g_object_new (MUX_TYPE_WINDOW,
531                          "back-title", back_title,
532                          NULL);
533 }
534
535 void 
536 mux_window_set_decorations (MuxWindow *window, 
537                             MuxDecorations decorations)
538 {
539     g_return_if_fail (MUX_IS_WINDOW (window));
540     
541     if (decorations != window->decorations) {
542         window->decorations = decorations;
543         mux_window_build_title_bar (window);
544     }
545 }
546
547 MuxDecorations 
548 mux_window_get_decorations (MuxWindow *window)
549 {
550     g_return_val_if_fail (MUX_IS_WINDOW (window), MUX_DECOR_NONE);
551
552     return window->decorations;
553 }
554
555 void
556 mux_window_set_settings_visible (MuxWindow *window, gboolean show)
557 {
558     gboolean visible;
559
560     visible = (mux_window_get_current_page (window) == window->settings_index);
561     if (visible != show) {
562         if (show) {
563             mux_window_set_current_page (window, window->settings_index);
564         } else {
565             mux_window_set_current_page (window, -1);
566         }
567
568         g_signal_emit (window, mux_window_signals[SETTINGS_VISIBILITY_CHANGED], 0);
569
570         if (window->settings_button)
571             mux_icon_button_set_active (MUX_ICON_BUTTON (window->settings_button),
572                                         show);
573     }
574 }
575
576 gboolean
577 mux_window_get_settings_visible (MuxWindow *window)
578 {
579     return (mux_window_get_current_page (window) == window->settings_index);
580 }
581
582 gint
583 mux_window_append_page (MuxWindow *window,
584                         GtkWidget *page,
585                         gboolean is_settings)
586 {
587     gint index;
588
589     index = gtk_notebook_append_page (GTK_NOTEBOOK (window->notebook), page, NULL);
590
591     if (is_settings) {
592         window->settings_index = index;
593     }
594     return index;
595 }
596
597 void mux_window_set_current_page (MuxWindow *window, gint index)
598 {
599     GtkBin *bin = GTK_BIN (window);
600
601     if (index == -1) {
602         gtk_widget_hide (window->notebook);
603         if (bin->child) {
604             gtk_widget_show (bin->child);
605         }
606         gtk_widget_hide (window->back_btn);
607     } else {
608         gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), index);
609         if (bin->child) {
610             gtk_widget_hide (bin->child);
611         }
612         gtk_widget_show (window->notebook);
613         gtk_widget_map (window->notebook);
614
615         gtk_widget_show (window->back_btn);
616     }
617 }
618
619 gint
620 mux_window_get_current_page (MuxWindow *window)
621 {
622     GtkBin *bin = GTK_BIN (window);
623
624     if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) {
625         return -1;
626     } else if (window->notebook) {
627         return gtk_notebook_get_current_page (GTK_NOTEBOOK (window->notebook));
628     }
629     return -1;
630 }