2 * This file is part of hildon-libs
4 * Copyright (C) 2005, 2006 Nokia Corporation.
6 * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
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
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.
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
26 * SECTION:hildon-appview
27 * @short_description: A widget which present one view of an application
28 * @see_also: #HildonApp
30 * #HildonAppView is a widget which presents one view of an application.
31 * Application can have many different views and the appview helps to organize.
32 * It has automatic fullscreen and menu handling. It also helps to handle
33 * components like a toolbar.
40 #include "hildon-app.h"
41 #include <hildon-appview.h>
42 #include <hildon-find-toolbar.h>
44 #include <gtk/gtkadjustment.h>
45 #include <gtk/gtkmenu.h>
46 #include <gtk/gtkimcontext.h>
47 #include <gtk/gtkmenuitem.h>
48 #include <gtk/gtkcheckmenuitem.h>
49 #include <gtk/gtkmenushell.h>
50 #include <gtk/gtkwindow.h>
51 #include <gtk/gtkwidget.h>
52 #include <gtk/gtkhbox.h>
53 #include <gtk/gtkvbox.h>
54 #include <gtk/gtklabel.h>
55 #include <gtk/gtkprogressbar.h>
56 #include <gtk/gtkimage.h>
57 #include <gtk/gtkiconfactory.h>
59 #include <gdk/gdkkeysyms.h>
64 #include <X11/Xatom.h>
68 #define _(String) gettext(String)
72 PROP_CONNECTED_ADJUSTMENT,
73 PROP_FULLSCREEN_KEY_ALLOWED,
79 /*The size of screen*/
80 #define WINDOW_HEIGHT 480
81 #define WINDOW_WIDTH 800
83 #define NAVIGATOR_HEIGHT WINDOW_HEIGHT
85 #define APPVIEW_HEIGHT 396
86 #define APPVIEW_WIDTH 672
88 #define TOOLBAR_HEIGHT 40
90 #define TOOLBAR_DOWN 9
91 #define TOOLBAR_MIDDLE 10
92 #define TOOLBAR_RIGHT 24
93 #define TOOLBAR_LEFT 24
94 #define TOOLBAR_WIDTH APPVIEW_WIDTH
96 #define WORKAREA_ATOM "_NET_WORKAREA"
98 /* Non atom defines */
99 #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */
100 #define _NET_WM_STATE_ADD 1 /* add/set property */
103 * These margins are set to be 5pixels smaller than in the specs
104 * Inner things are allocation that extra space
107 #define MARGIN_TOOLBAR_TOP 2
108 #define MARGIN_TOOLBAR_BOTTOM 6
109 #define MARGIN_TOOLBAR_LEFT 22
110 #define MARGIN_TOOLBAR_RIGHT 23
112 #define MARGIN_APPVIEW_TOP 0
113 #define MARGIN_APPVIEW_BOTTOM 24
114 #define MARGIN_APPVIEW_LEFT 24
115 #define MARGIN_APPVIEW_RIGHT 24
118 #define HILDON_APPVIEW_GET_PRIVATE(obj) \
119 (G_TYPE_INSTANCE_GET_PRIVATE ((obj),\
120 HILDON_TYPE_APPVIEW, HildonAppViewPrivate))
123 #define DEFAULT_WIDTH 20
124 #define DEFAULT_HEIGHT 28
125 #define BANNER_WIDTH DEFAULT_WIDTH
126 #define BANNER_HEIGHT DEFAULT_HEIGHT
128 static GtkBinClass *parent_class;
130 static void hildon_appview_init(HildonAppView * self);
131 static void hildon_appview_class_init(HildonAppViewClass * appview_class);
133 static void hildon_appview_menupopupfunc(GtkMenu *menu, gint *x, gint *y,
136 static void hildon_appview_menupopupfuncfull(GtkMenu *menu, gint *x, gint *y,
139 static gboolean hildon_appview_expose(GtkWidget * widget,
140 GdkEventExpose * event);
141 static void hildon_appview_forall(GtkContainer * container,
142 gboolean include_internals,
143 GtkCallback callback,
144 gpointer callback_data);
145 static void hildon_appview_show_all(GtkWidget *widget);
147 static void hildon_appview_size_allocate(GtkWidget * widget,
148 GtkAllocation * allocation);
149 static void hildon_appview_size_request(GtkWidget * widget,
150 GtkRequisition * requisition);
151 static void hildon_appview_finalize(GObject * obj_self);
152 static void hildon_appview_set_property(GObject * object, guint property_id,
153 const GValue * value, GParamSpec * pspec);
154 static void hildon_appview_get_property(GObject * object, guint property_id,
155 GValue * value, GParamSpec * pspec);
156 static void hildon_appview_destroy(GtkObject *obj);
157 static void hildon_appview_real_fullscreen_state_change(HildonAppView *
161 static void hildon_appview_switched_to(HildonAppView * self);
162 static void get_client_area(GtkWidget * widget,
163 GtkAllocation * allocation);
165 typedef void (*HildonAppViewSignal) (HildonAppView *, gint, gpointer);
170 TOOLBAR_TOGGLE_REQUEST,
171 FULLSCREEN_STATE_CHANGE,
175 INCREASE_BUTTON_EVENT,
176 DECREASE_BUTTON_EVENT,
177 HILDON_APPVIEW_LAST_SIGNAL
180 static guint appview_signals[HILDON_APPVIEW_LAST_SIGNAL] = { 0 };
188 struct _HildonAppViewPrivate {
192 GtkAllocation allocation;
194 guint fullscreen : 1;
195 guint fullscreenshortcutallowed : 1;
197 /* For future expansion.
198 * We might use the below variables for disabling keyrepeat if we need it someday. */
199 guint increase_button_pressed_down : 1;
200 guint decrease_button_pressed_down : 1;
201 gint visible_toolbars;
202 GtkAdjustment * connected_adjustment;
207 /* FIXME: Extremely old Legacy code. I wonder why we need
208 a custom marshaller in the first place. */
209 static void hildon_appview_signal_marshal(GClosure * closure,
210 GValue * return_value,
211 guint n_param_values,
212 const GValue * param_values,
213 gpointer invocation_hint,
214 gpointer marshal_data)
216 register HildonAppViewSignal callback;
217 register GCClosure *cc = (GCClosure *) closure;
218 register gpointer data1, data2;
220 g_assert(n_param_values == 2);
222 if (G_CCLOSURE_SWAP_DATA(closure)) {
223 data1 = closure->data;
224 data2 = g_value_peek_pointer(param_values + 0);
226 data1 = g_value_peek_pointer(param_values + 0);
227 data2 = closure->data;
231 /* FIXME: This is a compilation workaround for gcc > 3.3 since glib is buggy */
232 /* see http://bugzilla.gnome.org/show_bug.cgi?id=310175 */
237 (HildonAppViewSignal) (marshal_data !=
238 NULL ? marshal_data : cc->callback);
240 callback((HildonAppView *) data1,
241 (gint) g_value_get_int(param_values + 1), data2);
244 GType hildon_appview_get_type(void)
246 static GType appview_type = 0;
249 static const GTypeInfo appview_info = {
250 sizeof(HildonAppViewClass),
251 NULL, /* base_init */
252 NULL, /* base_finalize */
253 (GClassInitFunc) hildon_appview_class_init,
254 NULL, /* class_finalize */
255 NULL, /* class_data */
256 sizeof(HildonAppView),
258 (GInstanceInitFunc) hildon_appview_init,
260 appview_type = g_type_register_static(GTK_TYPE_BIN,
268 * Class initialisation.
270 static void hildon_appview_class_init(HildonAppViewClass * appview_class)
272 /* Get convenience variables */
273 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(appview_class);
274 GObjectClass *object_class = G_OBJECT_CLASS(appview_class);
275 GtkContainerClass *container_class =
276 GTK_CONTAINER_CLASS(appview_class);
278 /* Set the global parent_class here */
279 parent_class = g_type_class_peek_parent(appview_class);
281 object_class->set_property = hildon_appview_set_property;
282 object_class->get_property = hildon_appview_get_property;
284 /* Set the widgets virtual functions */
285 widget_class->size_allocate = hildon_appview_size_allocate;
286 widget_class->size_request = hildon_appview_size_request;
287 widget_class->expose_event = hildon_appview_expose;
288 widget_class->show_all = hildon_appview_show_all;
289 /* widget_class->realize = hildon_appview_realize; */
291 /* now the object stuff */
292 object_class->finalize = hildon_appview_finalize;
294 /* To the container */
295 container_class->forall = hildon_appview_forall;
298 GTK_OBJECT_CLASS(appview_class)->destroy = hildon_appview_destroy;
300 /* And own virtual functions */
301 appview_class->fullscreen_state_change =
302 hildon_appview_real_fullscreen_state_change;
303 appview_class->switched_to = hildon_appview_switched_to;
305 g_type_class_add_private(appview_class,
306 sizeof(struct _HildonAppViewPrivate));
309 appview_signals[TOOLBAR_CHANGED] =
310 g_signal_new("toolbar-changed",
311 G_OBJECT_CLASS_TYPE(object_class),
313 G_STRUCT_OFFSET(HildonAppViewClass, toolbar_changed),
315 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
317 appview_signals[TOOLBAR_TOGGLE_REQUEST] =
318 g_signal_new("toolbar-toggle-request",
319 G_OBJECT_CLASS_TYPE(object_class),
321 G_STRUCT_OFFSET(HildonAppViewClass,
322 toolbar_toggle_request), NULL, NULL,
323 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
325 appview_signals[FULLSCREEN_STATE_CHANGE] =
326 g_signal_new("fullscreen_state_change",
327 G_OBJECT_CLASS_TYPE(object_class),
329 G_STRUCT_OFFSET(HildonAppViewClass,
330 fullscreen_state_change), NULL, NULL,
331 hildon_appview_signal_marshal, G_TYPE_NONE, 1,
334 appview_signals[TITLE_CHANGE] =
335 g_signal_new("title_change",
336 G_OBJECT_CLASS_TYPE(object_class),
338 G_STRUCT_OFFSET(HildonAppViewClass, title_change),
340 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
342 appview_signals[SWITCHED_TO] =
343 g_signal_new("switched_to",
344 G_OBJECT_CLASS_TYPE(object_class),
346 G_STRUCT_OFFSET(HildonAppViewClass, switched_to),
348 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
350 appview_signals[SWITCHED_FROM] =
351 g_signal_new("switched_from",
352 G_OBJECT_CLASS_TYPE(object_class),
354 G_STRUCT_OFFSET(HildonAppViewClass, switched_from),
356 g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
358 appview_signals[INCREASE_BUTTON_EVENT] =
359 g_signal_new("increase_button_event",
360 G_OBJECT_CLASS_TYPE(object_class),
362 G_STRUCT_OFFSET(HildonAppViewClass, increase_button_event),
364 g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1,
367 appview_signals[DECREASE_BUTTON_EVENT] =
368 g_signal_new("decrease_button_event",
369 G_OBJECT_CLASS_TYPE(object_class),
371 G_STRUCT_OFFSET(HildonAppViewClass, decrease_button_event),
373 g_cclosure_marshal_VOID__UINT, G_TYPE_NONE, 1,
377 g_object_class_install_property(object_class, PROP_CONNECTED_ADJUSTMENT,
378 g_param_spec_object("connected-adjustment",
379 "Connected GtkAdjustment",
380 "The GtkAdjustment. The increase and decrease hardware buttons are mapped to this.",
384 g_object_class_install_property(object_class, PROP_FULLSCREEN_KEY_ALLOWED,
385 g_param_spec_boolean("fullscreen-key-allowed",
386 "Fullscreen key allowed",
387 "Whether the fullscreen key is allowed or not",
391 g_object_class_install_property(object_class, PROP_FULLSCREEN,
392 g_param_spec_boolean("fullscreen",
394 "Whether the appview should be fullscreen or not",
397 g_object_class_install_property(object_class, PROP_TITLE,
398 g_param_spec_string("title",
403 g_object_class_install_property(object_class, PROP_MENU_UI,
404 g_param_spec_string("menu-ui",
406 "UI string for application view menu",
409 widget_class = (GtkWidgetClass*) appview_class;
413 * Performs the initialisation of the widget.
415 static void hildon_appview_init(HildonAppView * self)
417 HildonAppViewPrivate *priv = self->priv =
418 HILDON_APPVIEW_GET_PRIVATE(self);
420 /* the vbox is used to handle both the view's main body and how many
421 * toolbars as the user wants */
423 self->vbox = gtk_vbox_new(TRUE, TOOLBAR_MIDDLE);
424 /* TOOLBAR_MIDDLE is here properly used, as originally meant. In order to
425 * be free to use whatever distance between toolbars, it's crucial to mind
426 * that the relevant gtkrc file must contain the following border property
427 * for the "toolbar-frame-middle" property: border = {24, 24, 5, 4}
430 gtk_widget_set_parent(self->vbox, GTK_WIDGET(self));
432 priv->visible_toolbars = 0;
434 priv->title = g_strdup("");
436 priv->fullscreen = FALSE;
437 priv->fullscreenshortcutallowed = FALSE;
438 priv->increase_button_pressed_down = FALSE;
439 priv->decrease_button_pressed_down = FALSE;
441 priv->connected_adjustment = NULL;
445 * Performs the standard gtk finalize function, freeing allocated
446 * memory and propagating the finalization to the parent.
448 static void hildon_appview_finalize(GObject * obj_self)
451 g_assert(HILDON_APPVIEW(obj_self));
452 self = HILDON_APPVIEW(obj_self);
454 if (self->priv->menu_ui)
455 g_free (self->priv->menu_ui);
457 if (self->priv->connected_adjustment != NULL)
458 g_object_remove_weak_pointer (G_OBJECT (self->priv->connected_adjustment),
459 (gpointer) &self->priv->connected_adjustment);
461 if (G_OBJECT_CLASS(parent_class)->finalize)
462 G_OBJECT_CLASS(parent_class)->finalize(obj_self);
464 g_free(self->priv->title);
468 * An accessor to set private properties of HildonAppView.
470 static void hildon_appview_set_property(GObject * object, guint property_id,
471 const GValue * value, GParamSpec * pspec)
473 HildonAppView *appview = HILDON_APPVIEW (object);
475 switch (property_id) {
476 case PROP_CONNECTED_ADJUSTMENT:
477 hildon_appview_set_connected_adjustment (appview, g_value_get_object (value));
480 case PROP_FULLSCREEN_KEY_ALLOWED:
481 hildon_appview_set_fullscreen_key_allowed (appview, g_value_get_boolean (value));
484 case PROP_FULLSCREEN:
485 hildon_appview_set_fullscreen (appview, g_value_get_boolean (value));
489 hildon_appview_set_title (appview, g_value_get_string (value));
493 hildon_appview_set_menu_ui (appview, g_value_get_string (value));
497 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
503 * An accessor to get private properties of HildonAppView.
505 static void hildon_appview_get_property(GObject * object, guint property_id,
506 GValue * value, GParamSpec * pspec)
508 HildonAppViewPrivate *priv = HILDON_APPVIEW_GET_PRIVATE(object);
510 switch (property_id) {
511 case PROP_CONNECTED_ADJUSTMENT:
512 g_value_set_object (value, priv->connected_adjustment);
515 case PROP_FULLSCREEN_KEY_ALLOWED:
516 g_value_set_boolean (value, priv->fullscreenshortcutallowed);
519 case PROP_FULLSCREEN:
520 g_value_set_boolean (value, priv->fullscreen);
524 g_value_set_string (value, priv->title);
528 g_value_set_string (value, priv->menu_ui);
532 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
538 * Used when the HildonAppView is exposed, this function gets a GtkBoxChild
539 * as first argument, and a pointer to a gint as second argument. If such
540 * GtkBoxChild is visible, the function increments the gint. It is used
541 * in a loop, to compute the number of visible toolbars.
543 static void visible_toolbar(gpointer child, gpointer number_of_visible_toolbars)
545 if(GTK_WIDGET_VISIBLE(((GtkBoxChild *)child)->widget))
546 (*((gint *)number_of_visible_toolbars))++;
550 * Used in the paint_toolbar function to discover how many toolbars are
551 * above the find toolbar. It's called in a loop that iterates through
552 * all the children of the GtkVBox of the HildonAppView.
554 static void find_findtoolbar_index(gpointer child, gpointer number_of_visible_toolbars)
556 gint *pass_bundle = (gint *)number_of_visible_toolbars;
558 if(((GtkBoxChild *)child)->widget->allocation.y < pass_bundle[0]
559 && GTK_WIDGET_VISIBLE(((GtkBoxChild *)child)->widget))
564 * Used in the paint_toolbar function, it's get a GtkBoxChild as first argument
565 * and a pointer to a GtkWidget as the second one, which will be addressed to
566 * the find toolbar widget, if it is contained in the given GtkBoxChild.
568 static void find_findtoolbar(gpointer child, gpointer widget)
570 if(HILDON_IS_FIND_TOOLBAR(((GtkBoxChild *)child)->widget)
571 && GTK_WIDGET_VISIBLE(((GtkBoxChild *)child)->widget))
572 (*((GtkWidget **)widget)) = ((GtkBoxChild *)child)->widget;
576 * Paints all the toolbar children of the GtkVBox of the HildonAppView.
578 static void paint_toolbar(GtkWidget *widget, GtkBox *box,
579 GdkEventExpose * event,
582 gint toolbar_num = 0;
585 GtkWidget *findtoolbar = NULL;
586 gchar toolbar_mode[40];
588 /* Iterate through all the children of the vbox of the HildonAppView.
589 * The visible_toolbar function increments toolbar_num if the toolbar
590 * is visible. After this loop, toobar_num will contain the number
591 * of the visible toolbars. */
592 g_list_foreach(box->children, visible_toolbar,
593 (gpointer) &toolbar_num);
597 /* Loop through all the children of the GtkVBox of the HildonAppView.
598 * The find_findtoolbar function will assign a pointer to the find toolbar
599 * to "findtoolbar" argument. If the findtoolbar is not found, i.e. it
600 * isn't in the GtkVBox, then the "findtoolbar" argument will stay NULL */
601 g_list_foreach(box->children, find_findtoolbar,
602 (gpointer) &findtoolbar);
603 if(findtoolbar != NULL){
606 /* an array for convient data passing
607 * the first member contains the y allocation
608 * of the find toolbar, and the second allocation
609 * contains the index(how many toolbars are above
611 pass_bundle[0] = findtoolbar->allocation.y;
612 pass_bundle[1] = ftb_index;
614 /* computes how many toolbars are above the find toolbar, and the
615 * value is stored in pass_bundle[1] */
616 g_list_foreach(box->children, find_findtoolbar_index,
617 (gpointer) pass_bundle);
618 ftb_index = pass_bundle[1];
621 sprintf(toolbar_mode, "toolbar%sframe-top",
622 fullscreen ? "-fullscreen-" : "-");
623 gtk_paint_box(widget->style, widget->window,
624 GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
625 &event->area, widget, toolbar_mode,
626 widget->allocation.x,
627 GTK_WIDGET(box)->allocation.y -TOOLBAR_UP,
628 widget->allocation.width, TOOLBAR_UP);
630 /*top most toolbar painting*/
631 if(findtoolbar != NULL && ftb_index == 0 )
633 sprintf(toolbar_mode, "findtoolbar%s",
634 fullscreen ? "-fullscreen" : "");
636 gtk_paint_box(widget->style, widget->window,
637 GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
638 &event->area, widget, toolbar_mode,
639 widget->allocation.x,
640 GTK_WIDGET(box)->allocation.y,
641 widget->allocation.width,
644 sprintf(toolbar_mode, "toolbar%s",
645 fullscreen ? "-fullscreen" : "");
646 gtk_paint_box(widget->style, widget->window,
647 GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
648 &event->area, widget, toolbar_mode,
649 widget->allocation.x,
650 GTK_WIDGET(box)->allocation.y,
651 widget->allocation.width,
654 /*multi toolbar painting*/
655 for(count = 0; count < toolbar_num - 1; count++)
657 sprintf(toolbar_mode, "toolbar%sframe-middle",
658 fullscreen ? "-fullscreen-" : "-");
660 gtk_paint_box(widget->style, widget->window,
661 GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
662 &event->area, widget, toolbar_mode,
663 widget->allocation.x,
664 GTK_WIDGET(box)->allocation.y +
665 (1 + count) * TOOLBAR_HEIGHT +
666 count * TOOLBAR_MIDDLE,
667 widget->allocation.width,
670 if(findtoolbar != NULL && count + 1 == ftb_index){
671 sprintf(toolbar_mode, "findtoolbar%s",
672 fullscreen ? "-fullscreen" : "");
674 gtk_paint_box(widget->style, widget->window,
675 GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
676 &event->area, widget, toolbar_mode,
677 widget->allocation.x,
678 GTK_WIDGET(box)->allocation.y +
679 (1 + count) * (TOOLBAR_HEIGHT + TOOLBAR_MIDDLE),
680 widget->allocation.width,
683 sprintf(toolbar_mode, "toolbar%s",
684 fullscreen ? "-fullscreen" : "");
686 gtk_paint_box(widget->style, widget->window,
687 GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
688 &event->area, widget, toolbar_mode,
689 widget->allocation.x,
690 GTK_WIDGET(box)->allocation.y +
691 (1 + count) * (TOOLBAR_HEIGHT + TOOLBAR_MIDDLE),
692 widget->allocation.width,
696 sprintf(toolbar_mode, "toolbar%sframe-bottom",
697 fullscreen ? "-fullscreen-" : "-");
699 gtk_paint_box(widget->style, widget->window,
700 GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
701 &event->area, widget, toolbar_mode,
702 widget->allocation.x,
703 GTK_WIDGET(box)->allocation.y +
704 GTK_WIDGET(box)->allocation.height,
705 widget->allocation.width, TOOLBAR_DOWN);
709 * Callback function to an expose event.
711 static gboolean hildon_appview_expose(GtkWidget * widget,
712 GdkEventExpose * event)
714 gint toolbar_num = 0;
715 GtkBox *box = GTK_BOX(HILDON_APPVIEW(widget)->vbox);
717 if(GTK_WIDGET_VISIBLE(box) && box->children != NULL)
719 HildonAppViewPrivate *priv = HILDON_APPVIEW_GET_PRIVATE(widget);
721 /* Iterate through all the children of the vbox of the HildonAppView.
722 * The visible_toolbar function increments toolbar_num if the toolbar
723 * is visible. After this loop, toobar_num will contain the number
724 * of the visible toolbars. */
725 g_list_foreach(box->children, visible_toolbar,
726 (gpointer) &toolbar_num);
728 if( priv->visible_toolbars != toolbar_num)
730 /* If the code reaches this block, it means that a toolbar as
731 * been added or removed since last time the view was drawn.
732 * Let's then compute the new height of the toolbars areas */
734 /* the height difference */
735 gint change = (priv->visible_toolbars - toolbar_num) *
736 (TOOLBAR_HEIGHT+TOOLBAR_MIDDLE+TOOLBAR_UP);
738 change = TOOLBAR_MIDDLE + TOOLBAR_UP;
739 /* the new y-coordinate for the toolbars area */
740 y_pos = HILDON_APPVIEW(widget)->vbox->allocation.y - change;
742 gtk_widget_queue_draw_area(widget, 0, y_pos, widget->allocation.width,
743 change + HILDON_APPVIEW(widget)->vbox->allocation.height +
745 priv->visible_toolbars = toolbar_num;
750 if (HILDON_APPVIEW(widget)->priv->fullscreen)
753 paint_toolbar(widget, box, event, TRUE);
757 gint appview_height_decrement = 0;
760 appview_height_decrement = toolbar_num * TOOLBAR_HEIGHT +
761 (toolbar_num - 1) * TOOLBAR_MIDDLE
762 + TOOLBAR_UP + TOOLBAR_DOWN;
764 paint_toolbar(widget, box, event, FALSE);
768 appview_height_decrement = MARGIN_APPVIEW_BOTTOM;
770 gtk_paint_box(widget->style, widget->window,
771 GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
772 &event->area, widget, "bottom-border",
773 widget->allocation.x,
774 widget->allocation.y +
775 (widget->allocation.height - MARGIN_APPVIEW_BOTTOM),
776 widget->allocation.width, MARGIN_APPVIEW_BOTTOM);
778 gtk_paint_box( widget->style, widget->window,
779 GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
781 widget, "left-border", widget->allocation.x,
782 widget->allocation.y, MARGIN_APPVIEW_LEFT,
783 widget->allocation.height - appview_height_decrement );
784 gtk_paint_box( widget->style, widget->window,
785 GTK_WIDGET_STATE(widget), GTK_SHADOW_OUT,
787 widget, "right-border",
788 (widget->allocation.x +
789 widget->allocation.width) -
790 MARGIN_APPVIEW_RIGHT, widget->allocation.y,
791 MARGIN_APPVIEW_RIGHT,
792 widget->allocation.height - appview_height_decrement );
795 GTK_WIDGET_CLASS(parent_class)->expose_event(widget, event);
801 * Responds to the usual size_request signal.
803 static void hildon_appview_size_request(GtkWidget * widget,
804 GtkRequisition * requisition)
806 HildonAppViewPrivate *priv = HILDON_APPVIEW(widget)->priv;
807 GtkWidget *child = GTK_BIN(widget)->child;
809 /* forward the size_request to the eventual child of the main container */
811 gtk_widget_size_request(child, requisition);
813 /* forward the size_request to the eventual vbox (which may contain
815 if (HILDON_APPVIEW(widget)->vbox != NULL)
816 gtk_widget_size_request(HILDON_APPVIEW(widget)->vbox, requisition);
818 /* express the size_request for the view */
819 if (priv->fullscreen) {
820 requisition->height = WINDOW_HEIGHT;
821 requisition->width = WINDOW_WIDTH;
823 requisition->height = APPVIEW_HEIGHT;
824 requisition->width = APPVIEW_WIDTH;
829 * Computes size and position for the children of the view.
831 static void hildon_appview_size_allocate(GtkWidget * widget,
832 GtkAllocation * allocation)
834 GtkAllocation box_allocation;
835 GtkAllocation alloc = *allocation;
836 gint border_width = GTK_CONTAINER(widget)->border_width;
837 GtkBin *bin = GTK_BIN(widget);
838 GtkBox *box = GTK_BOX(HILDON_APPVIEW(widget)->vbox);
839 gboolean at_least_one_visible_toolbar = FALSE;
841 if(!GTK_IS_WIDGET(bin->child)) return;
843 widget->allocation = *allocation;
845 if (bin->child != NULL && GTK_IS_WIDGET(bin->child)) {
846 if (HILDON_APPVIEW(widget)->priv->fullscreen) {
847 alloc.x += border_width;
848 alloc.y += border_width;
849 alloc.width -= (border_width * 2);
850 alloc.height -= (border_width * 2);
852 alloc.x += border_width + MARGIN_APPVIEW_LEFT;
853 alloc.y += border_width + MARGIN_APPVIEW_TOP;
854 alloc.width -= (border_width * 2) + (MARGIN_APPVIEW_LEFT +
855 MARGIN_APPVIEW_RIGHT);
856 alloc.height -= (border_width * 2) + MARGIN_APPVIEW_TOP;
860 if (box->children != NULL) {
863 /* Iterate through all the children of the vbox of the HildonAppView.
864 * The visible_toolbar function increments toolbar_num if the toolbar
865 * is visible. After this loop, toobar_num will contain the number
866 * of the visible toolbars. */
867 g_list_foreach(box->children, visible_toolbar,
870 box_height = length * TOOLBAR_HEIGHT +
871 (length - 1) * TOOLBAR_MIDDLE;
873 if(bin->child != NULL) {
874 alloc.height = alloc.height - box_height - TOOLBAR_UP
876 at_least_one_visible_toolbar = TRUE;
879 box_allocation.y = allocation->height - box_height - TOOLBAR_DOWN;
880 box_allocation.height = box_height;
881 box_allocation.x = allocation->x + TOOLBAR_LEFT;
882 box_allocation.width = allocation->width - TOOLBAR_LEFT -
884 gtk_widget_size_allocate(GTK_WIDGET(box), &box_allocation);
888 /* The bottom skin graphics is visible only when there are no toolbars */
889 if ((HILDON_APPVIEW(widget)->priv->fullscreen == FALSE) &&
890 (at_least_one_visible_toolbar == FALSE))
891 alloc.height -= MARGIN_APPVIEW_BOTTOM;
893 gtk_widget_size_allocate(GTK_WIDGET(bin->child), &alloc);
897 * Overrides gtk_container_forall, calling the callback function for each of
898 * the children of HildonAppPrivate.
900 static void hildon_appview_forall(GtkContainer * container,
901 gboolean include_internals,
902 GtkCallback callback,
903 gpointer callback_data)
905 HildonAppView *self = HILDON_APPVIEW(container);
907 g_assert(callback != NULL);
909 GTK_CONTAINER_CLASS(parent_class)->forall(container, include_internals,
910 callback, callback_data);
911 if(include_internals && self->vbox != NULL)
912 (* callback)(GTK_WIDGET(self->vbox), callback_data);
916 * Shows all the widgets in the container.
918 static void hildon_appview_show_all(GtkWidget *widget)
920 HildonAppView *self = HILDON_APPVIEW(widget);
923 gtk_widget_show_all(self->vbox);
925 /* Parent handless stuff inside appview */
926 GTK_WIDGET_CLASS(parent_class)->show_all(widget);
930 * Frees all the resources and propagates the destroy call to the parent.
932 static void hildon_appview_destroy(GtkObject *obj)
934 HildonAppView *self = HILDON_APPVIEW(obj);
936 if(self->vbox != NULL){
937 gtk_widget_unparent(self->vbox);
941 GTK_OBJECT_CLASS(parent_class)->destroy(obj);
944 /*******************/
946 /*******************/
948 /*Signal - When is changed to this appview, this is called*/
949 static void hildon_appview_switched_to(HildonAppView * self)
953 g_assert(self && HILDON_IS_APPVIEW(self));
955 parent = gtk_widget_get_parent(GTK_WIDGET(self));
956 hildon_appview_set_fullscreen( self, self->priv->fullscreen );
959 /*Signal - When the fullscreen state is changed, this is called*/
960 static void hildon_appview_real_fullscreen_state_change(HildonAppView *
965 HildonAppViewPrivate *priv;
966 g_assert(self && HILDON_IS_APPVIEW(self));
969 /* Ensure that state is really changed */
970 if( priv->fullscreen == fullscreen )
974 gtk_window_fullscreen( GTK_WINDOW(
975 gtk_widget_get_parent(GTK_WIDGET(self))) );
977 gtk_window_unfullscreen( GTK_WINDOW(
978 gtk_widget_get_parent(GTK_WIDGET(self))) );
980 priv->fullscreen = fullscreen;
983 /*******************/
985 /*******************/
989 * queries a window for the root window coordinates and size of its
990 * client area (i.e. minus the title borders etc.)
992 static void get_client_area(GtkWidget * widget, GtkAllocation * allocation)
994 GdkWindow *window = widget->window;
997 gdk_window_get_origin(window, &allocation->x, &allocation->y);
999 memset( allocation, 0, sizeof(GtkAllocation) );
1002 /*The menu popuping needs a menu popup-function*/
1003 static void hildon_appview_menupopupfunc( GtkMenu *menu, gint *x, gint *y,
1004 gboolean *push_in, GtkWidget *widget )
1006 GtkAllocation client_area = { 0, 0, 0, 0 };
1008 get_client_area( GTK_WIDGET(widget), &client_area );
1010 gtk_widget_style_get (GTK_WIDGET (menu), "horizontal-offset", x,
1011 "vertical-offset", y, NULL);
1013 *x += client_area.x;
1014 *y += client_area.y;
1018 /* Similar to above, but used in fullscreen mode */
1019 static void hildon_appview_menupopupfuncfull( GtkMenu *menu, gint *x, gint *y,
1023 gtk_widget_style_get (GTK_WIDGET (menu), "horizontal-offset", x,
1024 "vertical-offset", y, NULL);
1030 /*******************/
1031 /*public functions*/
1032 /*******************/
1036 * hildon_appview_new:
1037 * @title:the application view title of the new @HildonAppView
1039 * Use this function to create a new application view. The title will
1040 * be set only if two-part-title is enabled on the @HildonApp.
1042 * Returns: A #HildonAppView.
1044 GtkWidget *hildon_appview_new(const gchar * title)
1046 HildonAppView *newappview = g_object_new(HILDON_TYPE_APPVIEW, NULL);
1048 hildon_appview_set_title(newappview, title);
1049 return GTK_WIDGET(newappview);
1053 * hildon_appview_add_with_scrollbar
1054 * @self : a @HildonAppView
1055 * @child : a @GtkWidget
1057 * Adds the @child to the @self(HildonAppView) and creates a vertical
1058 * scrollbar to it. Similar as adding first a #GtkScrolledWindow
1059 * and then the @child to it.
1061 void hildon_appview_add_with_scrollbar(HildonAppView * self,
1064 GtkScrolledWindow *scrolledw;
1066 g_return_if_fail(HILDON_IS_APPVIEW(self));
1067 g_return_if_fail(GTK_IS_WIDGET(child));
1068 g_return_if_fail(child->parent == NULL);
1070 scrolledw = GTK_SCROLLED_WINDOW(gtk_scrolled_window_new(NULL, NULL));
1071 gtk_scrolled_window_set_policy(scrolledw, GTK_POLICY_NEVER,
1072 GTK_POLICY_AUTOMATIC);
1073 gtk_scrolled_window_set_shadow_type(scrolledw, GTK_SHADOW_NONE);
1075 /* Check whether child widget supports adjustments */
1076 if (GTK_WIDGET_GET_CLASS (child)->set_scroll_adjustments_signal)
1077 gtk_container_add(GTK_CONTAINER(scrolledw), child);
1080 if( GTK_IS_CONTAINER(child) )
1081 gtk_container_set_focus_vadjustment( GTK_CONTAINER(child),
1082 gtk_scrolled_window_get_vadjustment(scrolledw) );
1083 gtk_scrolled_window_add_with_viewport(scrolledw, child);
1086 gtk_container_add(GTK_CONTAINER(self), GTK_WIDGET(scrolledw));
1090 * hildon_appview_get_title:
1091 * @self : a #HildonAppView
1093 * Gets the title of given #HildonAppView.
1095 * Returns: the title of the application view
1097 const gchar *hildon_appview_get_title(HildonAppView * self)
1099 g_return_val_if_fail(self && HILDON_IS_APPVIEW(self), "");
1100 return self->priv->title;
1104 * hildon_appview_set_title:
1105 * @self : a #HildonAppView
1106 * @newname : the new title of the application view.
1108 * Sets an title of an application view. The title is visible only if
1109 * two-part-title is enabled on the #HildonApp.
1112 void hildon_appview_set_title(HildonAppView * self, const gchar * newname)
1116 g_return_if_fail(self && HILDON_IS_APPVIEW(self));
1117 oldtitle = self->priv->title;
1119 if (newname != NULL)
1120 self->priv->title = g_strdup(newname);
1122 self->priv->title = g_strdup("");
1125 g_signal_emit_by_name(G_OBJECT(self), "title_change");
1129 * hildon_appview_set_toolbar:
1130 * @self: a #HildonAppView
1131 * @toolbar: a #GtkToolbar
1133 * Sets the #GtkToolbar to given #HildonAppView. This is, however, not a
1134 * recommended way to set your toolbars. When you have multi toolbars, calling
1135 * this function more than once will just replace the bottom most toolbar.
1136 * There is a #GtkVBox in #HildonAppView's public structure, the programmer
1137 * is responsible to pack his toolbars in the #GtkVBox, and #HildonAppView will
1138 * take care of putting them at the right place.
1141 #ifndef HILDON_DISABLE_DEPRECATED
1142 void hildon_appview_set_toolbar(HildonAppView * self, GtkToolbar * toolbar)
1144 GtkBox *box = GTK_BOX(HILDON_APPVIEW(self)->vbox);
1145 g_return_if_fail(self && HILDON_IS_APPVIEW(self));
1146 if(toolbar != NULL)/*for failure checking*/
1147 g_return_if_fail(GTK_IS_TOOLBAR(toolbar));
1149 /*if it is NULL, it unsets the last one,
1150 * if it is not null, it unsets the last one anyway*/
1151 if(box->children != NULL){
1152 GtkWidget *last_widget;
1154 last_widget = ((GtkBoxChild *)g_list_last
1155 (box->children)->data)->widget;
1156 gtk_container_remove(GTK_CONTAINER(box),
1160 gtk_box_pack_end(box, GTK_WIDGET(toolbar), TRUE, TRUE, 0);
1161 gtk_widget_queue_resize(GTK_WIDGET(self));
1162 /*deprecated signal*/
1163 g_signal_emit_by_name(G_OBJECT(self), "toolbar-changed");
1167 * hildon_appview_get_toolbar:
1168 * @self: a #HildonAppView
1170 * This function will only
1171 * return the last widget that has been packed into the #GtkVBox in the public
1172 * structure. Note this does not, however, mean that it is the bottom most
1175 * Return value: the #GtkToolbar assigned to this application view.
1177 #ifndef HILDON_DISABLE_DEPRECATED
1178 GtkToolbar *hildon_appview_get_toolbar(HildonAppView * self)
1180 GtkBox *box = GTK_BOX(HILDON_APPVIEW(self)->vbox);
1181 g_return_val_if_fail(self != NULL && HILDON_IS_APPVIEW(self), FALSE);
1182 if(box != NULL && box->children != NULL)
1183 return GTK_TOOLBAR(((GtkBoxChild*)
1184 g_list_last(box->children)->data)->widget);
1190 * hildon_appview_set_fullscreen:
1191 * @self: a #HildonAppView
1192 * @fullscreen: the new state of fullscreen mode. TRUE means fullscreen
1193 * will be set. FALSE the opposite.
1195 * Set the fullscreen state of given #HildonAppView class.
1197 void hildon_appview_set_fullscreen(HildonAppView * self,
1198 gboolean fullscreen)
1200 g_return_if_fail(self && HILDON_IS_APPVIEW(self));
1201 g_signal_emit_by_name(G_OBJECT(self), "fullscreen_state_change",
1206 * hildon_appview_get_fullscreen:
1207 * @self: a #HildonAppView
1209 * Gets the current state of fullscreen mode.
1211 * Returns: the current state of fullscreen mode
1213 gboolean hildon_appview_get_fullscreen(HildonAppView * self)
1215 g_return_val_if_fail(self && HILDON_IS_APPVIEW(self), FALSE);
1216 return self->priv->fullscreen;
1220 * hildon_appview_get_fullscreen_key_allowed:
1221 * @self: a #HildonAppView
1223 * Check if fullscreening with a shortcut is allowed for given
1226 * Returns: wheter it's possible to swith fullscreen on/off with
1229 gboolean hildon_appview_get_fullscreen_key_allowed(HildonAppView * self)
1231 g_return_val_if_fail(self && HILDON_IS_APPVIEW(self), FALSE);
1232 return self->priv->fullscreenshortcutallowed;
1236 * hildon_appview_set_fullscreen_key_allowed:
1237 * @self: a #HildonAppView
1238 * @allow: wheter it's possible to swith fullscreen on/off with
1241 * Sets given #HildonAppView whether to allow toggling fullscreen mode
1242 * with a shortcut key.
1244 void hildon_appview_set_fullscreen_key_allowed(HildonAppView * self,
1247 g_return_if_fail(self && HILDON_IS_APPVIEW(self));
1248 self->priv->fullscreenshortcutallowed = allow;
1252 * hildon_appview_get_menu:
1253 * @self : #HildonAppView
1255 * Gets the #GtMenu assigned to the #HildonAppview.
1257 * Returns: the #GtkMenu assigned to this application view
1259 GtkMenu *hildon_appview_get_menu(HildonAppView * self)
1261 g_return_val_if_fail(self && HILDON_IS_APPVIEW(self), NULL);
1263 if (self->priv->menu == NULL) {
1264 /* Create hildonlike menu */
1267 GtkWidget *parent = gtk_widget_get_parent (GTK_WIDGET (self));
1269 /* Try to get appview menu from ui manager */
1270 if (parent && HILDON_IS_APP (parent))
1272 uim = hildon_app_get_ui_manager (HILDON_APP (parent));
1276 gtk_ui_manager_get_widget (uim, "/HildonApp");
1281 if (self->priv->menu == NULL)
1283 /* Fall back to oldskool menus */
1284 self->priv->menu = GTK_WIDGET (g_object_new (GTK_TYPE_MENU, NULL));
1287 gtk_widget_set_name(GTK_WIDGET(self->priv->menu),
1288 "menu_force_with_corners");
1289 gtk_widget_show_all (self->priv->menu);
1292 return GTK_MENU(self->priv->menu);
1296 * _hildon_appview_toggle_menu:
1297 * @self : a #HildonAppView
1298 * @button_event_time :
1300 * This function should be only called from @HildonApp.
1301 * Should be renamed to popup menu. Just the first parameter is used.
1303 * Returns: Whether or not something was done (whether or not we had
1306 gboolean _hildon_appview_toggle_menu(HildonAppView * self,
1307 Time button_event_time)
1311 g_return_val_if_fail(self && HILDON_IS_APPVIEW(self), FALSE);
1313 if (!self->priv->menu)
1316 if (GTK_WIDGET_VISIBLE(self->priv->menu)) {
1317 gtk_menu_popdown(GTK_MENU(self->priv->menu));
1318 gtk_menu_shell_deactivate(GTK_MENU_SHELL(self->priv->menu));
1322 /* Avoid opening an empty menu */
1323 children = gtk_container_get_children(
1324 GTK_CONTAINER(hildon_appview_get_menu(self)));
1325 if (children != NULL) {
1328 g_list_free(children);
1329 menu = GTK_WIDGET(hildon_appview_get_menu(self));
1330 if (self->priv->fullscreen) {
1331 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1332 (GtkMenuPositionFunc)
1333 hildon_appview_menupopupfuncfull,
1334 self, 0, button_event_time);
1336 gtk_menu_popup(GTK_MENU(menu), NULL, NULL,
1337 (GtkMenuPositionFunc)
1338 hildon_appview_menupopupfunc,
1339 self, 0, button_event_time);
1341 gtk_menu_shell_select_first(GTK_MENU_SHELL(menu), TRUE);
1349 * _hildon_appview_menu_visible
1350 * @self: a #HildonAppView
1352 * Checks whether the titlebar menu is currently visible
1353 * Returns: TRUE if the menu is visible, FALSE if not
1356 gboolean _hildon_appview_menu_visible(HildonAppView * self)
1358 g_return_val_if_fail (HILDON_IS_APPVIEW (self), FALSE);
1360 return GTK_WIDGET_VISIBLE(GTK_WIDGET(hildon_appview_get_menu(self)));
1364 * hildon_appview_set_connected_adjustment
1365 * @self : #HildonAppView
1366 * @adjustment : a new #GtkAdjustment set to reach to increase
1367 * / decrease hardware keys or NULL to unset
1369 * Sets a #GtkAdjustment which will change when increase/decrease buttons
1372 void hildon_appview_set_connected_adjustment (HildonAppView * self,
1373 GtkAdjustment * adjustment)
1375 g_return_if_fail (HILDON_IS_APPVIEW (self));
1377 /* Disconnect old adjustment */
1378 if (self->priv->connected_adjustment != NULL)
1379 g_object_remove_weak_pointer (G_OBJECT (self->priv->connected_adjustment),
1380 (gpointer) &self->priv->connected_adjustment);
1382 /* Start using the new one */
1383 self->priv->connected_adjustment = adjustment;
1384 if (self->priv->connected_adjustment != NULL)
1385 g_object_add_weak_pointer (G_OBJECT (self->priv->connected_adjustment),
1386 (gpointer) &self->priv->connected_adjustment);
1390 * hildon_appview_get_connected_adjustment
1391 * @self : a #HildonAppView
1393 * Retrieves the #GtkAdjustment which is connected to this application view
1394 * and is changed with increase / decrease hardware buttons.
1396 * Returns: currently connectd #GtkAdjustment assigned to this
1397 * application view or NULL if it's not set
1399 GtkAdjustment * hildon_appview_get_connected_adjustment (HildonAppView * self)
1401 g_return_val_if_fail (HILDON_IS_APPVIEW (self), NULL);
1403 return self->priv->connected_adjustment;
1408 * hildon_appview_set_menu_ui
1409 * @self : a #HildonAppView
1410 * @ui_string : a #GtkUIManager ui description string
1412 * Sets the ui description (xml) from which the UIManager creates menus
1413 * (see @GtkUIManager for details on how to use it)
1415 void hildon_appview_set_menu_ui(HildonAppView *self, const gchar *ui_string)
1417 g_return_if_fail (HILDON_IS_APPVIEW (self));
1421 if (self->priv->menu_ui)
1422 g_free (self->priv->menu_ui);
1424 self->priv->menu_ui = g_strdup (ui_string);
1426 /* FIXME: We should update the menu here, preferrably by a
1427 * hildon_app_ensure_menu_update() which re-installs the menu ui
1428 * and calls gtk_ui_manager_ensure_update()
1434 if (self->priv->menu_ui)
1436 g_free (self->priv->menu_ui);
1437 self->priv->menu_ui = NULL;
1441 g_object_notify (G_OBJECT(self), "menu-ui");
1445 * hildon_appview_get_menu_ui
1446 * @self : a #HildonAppView
1448 * Sets the ui description (xml) from which the UIManager creates menus
1449 * (see #GtkUIManager for details on how to use it)
1451 * Returns: currently set ui description
1453 const gchar *hildon_appview_get_menu_ui(HildonAppView *self)
1455 g_return_val_if_fail (HILDON_IS_APPVIEW (self), NULL);
1457 return (self->priv->menu_ui);
1461 /* Called when '+' hardkey is pressed/released */
1462 void _hildon_appview_increase_button_state_changed (HildonAppView * self,
1465 self->priv->increase_button_pressed_down = newkeytype;
1467 /* Transform '+' press into adjustment update (usually scrollbar move) */
1468 if ((self->priv->connected_adjustment != NULL) && (newkeytype == GDK_KEY_PRESS))
1470 gfloat clampedvalue = CLAMP (gtk_adjustment_get_value (self->priv->connected_adjustment) + self->priv->connected_adjustment->step_increment,
1471 self->priv->connected_adjustment->lower,
1472 self->priv->connected_adjustment->upper - self->priv->connected_adjustment->page_size);
1473 gtk_adjustment_set_value (self->priv->connected_adjustment, clampedvalue);
1476 g_signal_emit (G_OBJECT (self), appview_signals[INCREASE_BUTTON_EVENT], 0, newkeytype);
1479 /* Called when '-' hardkey is pressed/released */
1480 void _hildon_appview_decrease_button_state_changed (HildonAppView * self,
1483 self->priv->decrease_button_pressed_down = newkeytype;
1485 /* Transform '-' press into adjustment update (usually scrollbar move) */
1486 if ((self->priv->connected_adjustment != NULL) && (newkeytype == GDK_KEY_PRESS))
1488 gfloat clampedvalue = CLAMP (gtk_adjustment_get_value (self->priv->connected_adjustment) - self->priv->connected_adjustment->step_increment,
1489 self->priv->connected_adjustment->lower,
1490 self->priv->connected_adjustment->upper - self->priv->connected_adjustment->page_size);
1491 gtk_adjustment_set_value (self->priv->connected_adjustment, clampedvalue);
1494 g_signal_emit (G_OBJECT (self), appview_signals[DECREASE_BUTTON_EVENT], 0, newkeytype);