2 * Copyright (C) 2009 Intel Corporation
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.
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.
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
20 #include "mux-window.h"
21 #include "mux-icon-button.h"
23 static GdkColor mux_window_default_title_bar_bg = { 0, 0x3300, 0x3300, 0x3300 };
25 #define MUX_WINDOW_DEFAULT_TITLE_BAR_HEIGHT 63
28 mux_decorations_get_type (void)
30 static GType etype = 0;
32 static const GFlagsValue values[] = {
33 { MUX_DECOR_CLOSE, "MUX_CLOSE", "close" },
34 { MUX_DECOR_SETTINGS, "MUX_SETTINGS", "settings" },
37 etype = g_flags_register_static (g_intern_static_string ("MuxDecorations"), values);
50 SETTINGS_VISIBILITY_CHANGED,
54 static guint mux_window_signals[LAST_SIGNAL] = { 0 };
56 G_DEFINE_TYPE (MuxWindow, mux_window, GTK_TYPE_WINDOW)
58 #define GET_PRIVATE(o) \
59 (G_TYPE_INSTANCE_GET_PRIVATE ((o), MUX_TYPE_WINDOW, MuxWindowPrivate))
61 typedef struct _MuxWindowPrivate MuxWindowPrivate;
63 struct _MuxWindowPrivate {
68 mux_window_get_property (GObject *object, guint property_id,
69 GValue *value, GParamSpec *pspec)
71 MuxWindow *win = MUX_WINDOW (object);
73 switch (property_id) {
74 case PROP_DECORATIONS:
75 g_value_set_uint (value, win->decorations);
78 g_value_set_string (value,
79 gtk_button_get_label (GTK_BUTTON (win->back_btn)));
82 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
87 mux_window_set_property (GObject *object, guint property_id,
88 const GValue *value, GParamSpec *pspec)
90 MuxWindow *win = MUX_WINDOW (object);
92 switch (property_id) {
93 case PROP_DECORATIONS:
94 mux_window_set_decorations (win, g_value_get_uint (value));
97 g_free (win->back_title);
98 win->back_title = g_strdup (g_value_get_string (value));
100 gtk_button_set_label (GTK_BUTTON (win->back_btn), win->back_title);
104 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
109 mux_window_update_style (MuxWindow *win)
111 GdkColor *title_bar_bg = NULL;
112 guint title_bar_height;
114 g_return_if_fail (win->title_bar);
116 gtk_widget_style_get (GTK_WIDGET (win),
117 "title-bar-height", &title_bar_height,
118 "title-bar-bg", &title_bar_bg,
122 gtk_widget_modify_bg (win->title_bar, GTK_STATE_NORMAL, title_bar_bg);
123 gdk_color_free (title_bar_bg);
125 gtk_widget_modify_bg (win->title_bar, GTK_STATE_NORMAL,
126 &mux_window_default_title_bar_bg);
129 gtk_widget_set_size_request (win->title_bar, -1, title_bar_height);
133 mux_window_style_set (GtkWidget *widget,
136 MuxWindow *win = MUX_WINDOW (widget);
138 mux_window_update_style (win);
140 GTK_WIDGET_CLASS (mux_window_parent_class)->style_set (widget, previous);
144 mux_window_forall (GtkContainer *container,
145 gboolean include_internals,
146 GtkCallback callback,
147 gpointer callback_data)
149 MuxWindow *mux_win = MUX_WINDOW (container);
150 GtkBin *bin = GTK_BIN (container);
152 /* FIXME: call parents forall instead ? */
154 (* callback) (bin->child, callback_data);
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);
163 mux_window_add (GtkContainer *container,
166 MuxWindowClass *klass;
167 GtkContainerClass *parent_container_class;
169 klass = MUX_WINDOW_GET_CLASS (container);
170 parent_container_class = GTK_CONTAINER_CLASS (g_type_class_peek_parent (klass));
172 parent_container_class->add (container, widget);
176 mux_window_remove (GtkContainer *container,
179 MuxWindow *win = MUX_WINDOW (container);
180 GtkBin *bin = GTK_BIN (container);
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);
198 mux_window_size_request (GtkWidget *widget,
199 GtkRequisition *requisition)
201 GtkBin *bin = GTK_BIN (widget);
202 MuxWindow *mux_win = MUX_WINDOW (widget);
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);
215 requisition->width = 1024;
216 requisition->height = 600;
220 mux_window_size_allocate (GtkWidget *widget,
221 GtkAllocation *allocation)
223 GtkBin *bin = GTK_BIN (widget);
224 MuxWindow *mux_win = MUX_WINDOW (widget);
225 GtkAllocation child_allocation;
226 int xmargin, ymargin, title_height;
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;
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);
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);
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);
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);
262 if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) {
263 gtk_widget_size_allocate (bin->child, &child_allocation);
266 if (mux_win->notebook && GTK_WIDGET_VISIBLE (mux_win->notebook)) {
267 gtk_widget_size_allocate (mux_win->notebook, &child_allocation);
270 mux_win->child_allocation = child_allocation;
274 mux_window_class_init (MuxWindowClass *klass)
276 GObjectClass *object_class = G_OBJECT_CLASS (klass);
277 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
278 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (klass);
281 g_type_class_add_private (klass, sizeof (MuxWindowPrivate));
283 object_class->get_property = mux_window_get_property;
284 object_class->set_property = mux_window_set_property;
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;
290 container_class->forall = mux_window_forall;
291 container_class->remove = mux_window_remove;
292 container_class->add = mux_window_add;
294 pspec = g_param_spec_uint ("title-bar-height",
296 "Total height of the title bar",
297 0, G_MAXUINT, MUX_WINDOW_DEFAULT_TITLE_BAR_HEIGHT,
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",
305 gtk_widget_class_install_style_property(widget_class, pspec);
307 pspec = g_param_spec_flags ("decorations",
309 "Bitfield of MuxDecorations defining used window decorations",
310 MUX_TYPE_DECORATIONS,
313 g_object_class_install_property (object_class,
317 pspec = g_param_spec_string ("back-title",
319 "title of the back button in the window decoration",
322 g_object_class_install_property (object_class,
326 mux_window_signals[SETTINGS_VISIBILITY_CHANGED] =
327 g_signal_new ("settings-visibility-changed",
328 G_OBJECT_CLASS_TYPE (object_class),
330 G_STRUCT_OFFSET (MuxWindowClass, settings_visibility_changed),
332 g_cclosure_marshal_VOID__VOID,
333 G_TYPE_NONE, 0, NULL);
338 mux_window_settings_clicked (MuxIconButton *button, MuxWindow *window)
341 active = mux_icon_button_get_active (button);
342 mux_window_set_settings_visible (window, active);
346 mux_window_close_clicked (MuxWindow *window)
348 /* this is how GtkDialog does it... */
351 event = gdk_event_new (GDK_DELETE);
352 event->any.window = g_object_ref (GTK_WIDGET (window)->window);
353 event->any.send_event = TRUE;
355 gtk_main_do_event (event);
356 gdk_event_free (event);
360 load_icon (MuxWindow *window, const char *icon_name)
362 static GtkIconTheme *theme = NULL;
367 screen = gtk_widget_get_screen (GTK_WIDGET (window));
368 theme = gtk_icon_theme_get_for_screen (screen);
371 pixbuf = gtk_icon_theme_load_icon (theme, icon_name,
375 /* FIXME: workaround until icons are in Moblin Netbook theme */
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);
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,
395 bread_crumb_clicked_cb (GtkButton *btn, MuxWindow *window)
397 mux_window_set_current_page (window, -1);
401 mux_window_build_title_bar (MuxWindow *window)
403 GtkWidget *box, *button_box, *btn, *align;
404 GdkPixbuf *pixbuf, *pixbuf_hover;
406 if (window->title_bar) {
407 gtk_widget_unparent (window->title_bar);
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);
415 box = gtk_hbox_new (FALSE, 0);
416 gtk_container_add (GTK_CONTAINER (window->title_bar), box);
417 gtk_widget_show (box);
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);
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);
428 window->back_btn = gtk_button_new_with_label (window->back_title);
429 gtk_box_pack_start (GTK_BOX (button_box), window->back_btn,
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);
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,
439 gtk_widget_show (window->title_label);*/
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);
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,
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);
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,
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);
475 mux_window_update_style (window);
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));
484 mux_window_title_changed (MuxWindow *window,
488 mux_window_build_title_bar (window);
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 */
494 mux_window_try_maximize (MuxWindow *self)
496 static int count = 0;
499 gtk_window_maximize (GTK_WINDOW (self));
505 mux_window_init (MuxWindow *self)
507 self->decorations = MUX_DECOR_CLOSE;
509 gtk_window_set_decorated (GTK_WINDOW (self), FALSE);
511 g_signal_connect (self, "notify::title",
512 G_CALLBACK (mux_window_title_changed), NULL);
514 mux_window_build_title_bar (self);
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;
523 gtk_window_maximize (GTK_WINDOW (self));
524 g_timeout_add (10, (GSourceFunc)mux_window_try_maximize, self);
528 mux_window_new (const char *back_title)
530 return g_object_new (MUX_TYPE_WINDOW,
531 "back-title", back_title,
536 mux_window_set_decorations (MuxWindow *window,
537 MuxDecorations decorations)
539 g_return_if_fail (MUX_IS_WINDOW (window));
541 if (decorations != window->decorations) {
542 window->decorations = decorations;
543 mux_window_build_title_bar (window);
548 mux_window_get_decorations (MuxWindow *window)
550 g_return_val_if_fail (MUX_IS_WINDOW (window), MUX_DECOR_NONE);
552 return window->decorations;
556 mux_window_set_settings_visible (MuxWindow *window, gboolean show)
560 visible = (mux_window_get_current_page (window) == window->settings_index);
561 if (visible != show) {
563 mux_window_set_current_page (window, window->settings_index);
565 mux_window_set_current_page (window, -1);
568 g_signal_emit (window, mux_window_signals[SETTINGS_VISIBILITY_CHANGED], 0);
570 if (window->settings_button)
571 mux_icon_button_set_active (MUX_ICON_BUTTON (window->settings_button),
577 mux_window_get_settings_visible (MuxWindow *window)
579 return (mux_window_get_current_page (window) == window->settings_index);
583 mux_window_append_page (MuxWindow *window,
585 gboolean is_settings)
589 index = gtk_notebook_append_page (GTK_NOTEBOOK (window->notebook), page, NULL);
592 window->settings_index = index;
597 void mux_window_set_current_page (MuxWindow *window, gint index)
599 GtkBin *bin = GTK_BIN (window);
602 gtk_widget_hide (window->notebook);
604 gtk_widget_show (bin->child);
606 gtk_widget_hide (window->back_btn);
608 gtk_notebook_set_current_page (GTK_NOTEBOOK (window->notebook), index);
610 gtk_widget_hide (bin->child);
612 gtk_widget_show (window->notebook);
613 gtk_widget_map (window->notebook);
615 gtk_widget_show (window->back_btn);
620 mux_window_get_current_page (MuxWindow *window)
622 GtkBin *bin = GTK_BIN (window);
624 if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) {
626 } else if (window->notebook) {
627 return gtk_notebook_get_current_page (GTK_NOTEBOOK (window->notebook));