/* moko-navigation-list.c * * Authored by Ken Zhao * * Copyright (C) 2006-2007 OpenMoko Inc. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser Public License as published by * the Free Software Foundation; version 2 of the license. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser Public License for more details. * * Current Version: $Rev$ ($Date: 2006/11/28 17:38:14 $) [$Author: Ken $] */ #include "moko-navigation-list.h" G_DEFINE_TYPE (MokoNavigationList, moko_navigation_list, GTK_TYPE_VIEWPORT) #define NAVIGATION_LIST_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MOKO_TYPE_NAVIGATION_LIST, MokoNavigationListPrivate)) typedef struct _MokoNavigationListPrivate { GtkWidget* navigationcontainer; /* GtkFixed */ GtkWidget* navigationsw; /* GtkScrolledWindow */ GtkWidget* treeview; /* MokoTreeView */ } MokoNavigationListPrivate; /* forward declarations */ /* ... */ /* declare virtual methods */ static void moko_navigation_list_size_request (GtkWidget *widget, GtkRequisition *requisition); static void moko_navigation_list_size_allocate (GtkWidget *widget, GtkAllocation *allocation); static void moko_navigation_list_get_view_allocation (GtkViewport *viewport, GtkAllocation *view_allocation); static void moko_navigation_list_reclamp_adjustment (GtkAdjustment *adjustment, gboolean *value_changed) { gdouble value = adjustment->value; value = CLAMP (value, 0, adjustment->upper - adjustment->page_size); if (value != adjustment->value) { adjustment->value = value; if (value_changed) *value_changed = TRUE; } else if (value_changed) *value_changed = FALSE; } static void moko_navigation_list_set_hadjustment_values (GtkViewport *viewport, gboolean *value_changed) { GtkBin *bin = GTK_BIN (viewport); GtkAllocation view_allocation; GtkAdjustment *hadjustment = gtk_viewport_get_hadjustment (viewport); gdouble old_page_size; gdouble old_upper; gdouble old_value; moko_navigation_list_get_view_allocation (viewport, &view_allocation); old_page_size = hadjustment->page_size; old_upper = hadjustment->upper; old_value = hadjustment->value; hadjustment->page_size = view_allocation.width; hadjustment->step_increment = view_allocation.width * 0.1; hadjustment->page_increment = view_allocation.width * 0.9; hadjustment->lower = 0; if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) { GtkRequisition child_requisition; gtk_widget_get_child_requisition (bin->child, &child_requisition); hadjustment->upper = MAX (child_requisition.width, view_allocation.width); } else hadjustment->upper = view_allocation.width; if (gtk_widget_get_direction (GTK_WIDGET (viewport)) == GTK_TEXT_DIR_RTL) { gdouble dist = old_upper - (old_value + old_page_size); hadjustment->value = hadjustment->upper - dist - hadjustment->page_size; moko_navigation_list_reclamp_adjustment (hadjustment, value_changed); *value_changed = (old_value != hadjustment->value); } else moko_navigation_list_reclamp_adjustment (hadjustment, value_changed); } static void moko_navigation_list_get_view_allocation (GtkViewport *viewport, GtkAllocation *view_allocation) { GtkWidget *widget = GTK_WIDGET (viewport); GtkAllocation *allocation = &widget->allocation; gint border_width = GTK_CONTAINER (viewport)->border_width; view_allocation->x = 0; view_allocation->y = 0; if (viewport->shadow_type != GTK_SHADOW_NONE) { view_allocation->x = widget->style->xthickness; view_allocation->y = widget->style->ythickness; } view_allocation->width = MAX (1, allocation->width - view_allocation->x * 2 - border_width * 2); view_allocation->height = MAX (1, allocation->height - view_allocation->y * 2 - border_width * 2); } static void moko_navigation_list_set_vadjustment_values (GtkViewport *viewport, gboolean *value_changed) { GtkBin *bin = GTK_BIN (viewport); GtkAllocation view_allocation; GtkAdjustment *vadjustment = gtk_viewport_get_vadjustment (viewport); moko_navigation_list_get_view_allocation (viewport, &view_allocation); vadjustment->page_size = view_allocation.height; vadjustment->step_increment = view_allocation.height * 0.1; vadjustment->page_increment = view_allocation.height * 0.9; vadjustment->lower = 0; if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) { GtkRequisition child_requisition; gtk_widget_get_child_requisition (bin->child, &child_requisition); vadjustment->upper = MAX (child_requisition.height, view_allocation.height); } else vadjustment->upper = view_allocation.height; moko_navigation_list_reclamp_adjustment (vadjustment, value_changed); } static void moko_navigation_list_dispose (GObject *object) { if (G_OBJECT_CLASS (moko_navigation_list_parent_class)->dispose) G_OBJECT_CLASS (moko_navigation_list_parent_class)->dispose (object); } static void moko_navigation_list_finalize (GObject *object) { G_OBJECT_CLASS (moko_navigation_list_parent_class)->finalize (object); } static void moko_navigation_list_size_request(GtkWidget *widget, GtkRequisition *requisition) { GtkViewport *viewport; GtkBin *bin; GtkRequisition child_requisition; gint width,height; gint navigationsw_width,navigationsw_height; viewport = GTK_VIEWPORT (widget); bin = GTK_BIN (widget); gtk_widget_get_size_request (widget, &width, &height); navigationsw_width = width - 6; navigationsw_height = height - 3; MokoNavigationListPrivate* priv = NAVIGATION_LIST_PRIVATE (widget); gtk_widget_set_size_request (priv->navigationsw, navigationsw_width, navigationsw_height); requisition->width = (GTK_CONTAINER (widget)->border_width + GTK_WIDGET (widget)->style->xthickness) * 2; requisition->height = (GTK_CONTAINER (widget)->border_width * 2 + GTK_WIDGET (widget)->style->ythickness) * 2; if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) { gtk_widget_size_request (bin->child, &child_requisition); requisition->width += child_requisition.width; requisition->height += child_requisition.height; } } static void moko_navigation_list_size_allocate (GtkWidget *widget, GtkAllocation *allocation) { GtkViewport *viewport = GTK_VIEWPORT (widget); GtkBin *bin = GTK_BIN (widget); gint border_width = GTK_CONTAINER (widget)->border_width; gboolean hadjustment_value_changed, vadjustment_value_changed; GtkAdjustment *hadjustment = gtk_viewport_get_hadjustment (viewport); GtkAdjustment *vadjustment = gtk_viewport_get_vadjustment (viewport); /* If our size changed, and we have a shadow, queue a redraw on widget->window to * redraw the shadow correctly. */ if (GTK_WIDGET_MAPPED (widget) && viewport->shadow_type != GTK_SHADOW_NONE && (widget->allocation.width != allocation->width || widget->allocation.height != allocation->height)) gdk_window_invalidate_rect (widget->window, NULL, FALSE); widget->allocation = *allocation; moko_navigation_list_set_hadjustment_values (viewport, &hadjustment_value_changed); moko_navigation_list_set_vadjustment_values (viewport, &vadjustment_value_changed); if (GTK_WIDGET_REALIZED (widget)) { GtkAllocation view_allocation; moko_navigation_list_get_view_allocation (viewport, &view_allocation); gdk_window_move_resize (widget->window, allocation->x + border_width, allocation->y + border_width, allocation->width - border_width * 2, allocation->height - border_width * 2); gdk_window_move_resize (viewport->view_window, view_allocation.x, view_allocation.y, view_allocation.width, view_allocation.height); } if (bin->child && GTK_WIDGET_VISIBLE (bin->child)) { GtkAllocation child_allocation; child_allocation.x = 0; child_allocation.y = 0; child_allocation.width = hadjustment->upper; child_allocation.height = vadjustment->upper; if (GTK_WIDGET_REALIZED (widget)) gdk_window_move_resize (viewport->bin_window, - hadjustment->value, - vadjustment->value, child_allocation.width, child_allocation.height); gtk_widget_size_allocate (bin->child, &child_allocation); } gtk_adjustment_changed (hadjustment); gtk_adjustment_changed (vadjustment); if (hadjustment_value_changed) gtk_adjustment_value_changed (hadjustment); if (vadjustment_value_changed) gtk_adjustment_value_changed (vadjustment); gtk_widget_set_size_request (widget, allocation->width, allocation->height); } static void moko_navigation_list_class_init (MokoNavigationListClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass); /* register private data */ g_type_class_add_private (klass, sizeof (MokoNavigationListPrivate)); /* hook virtual methods */ widget_class->size_request = moko_navigation_list_size_request; widget_class->size_allocate = moko_navigation_list_size_allocate; /* install properties */ /* ... */ object_class->dispose = moko_navigation_list_dispose; object_class->finalize = moko_navigation_list_finalize; } static void moko_navigation_list_init (MokoNavigationList *self) { MokoNavigationListPrivate* priv = NAVIGATION_LIST_PRIVATE (self); gtk_widget_set_name ( GTK_WIDGET (self), "mokonavigationlist-background" ); gtk_viewport_set_shadow_type ( GTK_VIEWPORT (self), GTK_SHADOW_NONE ); priv->navigationcontainer = gtk_fixed_new(); gtk_container_add ( GTK_CONTAINER (self), GTK_WIDGET (priv->navigationcontainer) ); priv->treeview = moko_tree_view_new (); gtk_tree_view_set_rules_hint ( GTK_TREE_VIEW (priv->treeview), TRUE ); gtk_tree_view_set_headers_visible ( GTK_TREE_VIEW (priv->treeview), TRUE ); priv->navigationsw = moko_tree_view_put_into_scrolled_window (MOKO_TREE_VIEW (priv->treeview)); gtk_widget_set_size_request ( GTK_WIDGET (self), 458, 160 ); gtk_fixed_put ( GTK_FIXED (priv->navigationcontainer), GTK_WIDGET (priv->navigationsw), 4, 2 ); } GtkWidget* moko_navigation_list_new (void) { MokoNavigationList* self = MOKO_NAVIGATION_LIST ( g_object_new (MOKO_TYPE_NAVIGATION_LIST, NULL)); return GTK_WIDGET (self); } GtkWidget* moko_navigation_list_new_with_model (GtkTreeModel *model) { MokoNavigationList* self = MOKO_NAVIGATION_LIST ( g_object_new (MOKO_TYPE_NAVIGATION_LIST, NULL)); MokoNavigationListPrivate* priv = NAVIGATION_LIST_PRIVATE (self); gtk_viewport_set_shadow_type ( GTK_VIEWPORT (self), GTK_SHADOW_NONE ); priv->navigationcontainer = gtk_fixed_new(); gtk_container_add ( GTK_CONTAINER (self), GTK_WIDGET (priv->navigationcontainer) ); priv->treeview = moko_tree_view_new_with_model (model); priv->navigationsw = moko_tree_view_put_into_scrolled_window ( MOKO_TREE_VIEW (priv->treeview)); gtk_widget_set_size_request ( GTK_WIDGET (self), 458, 160 ); gtk_fixed_put ( GTK_FIXED (priv->navigationcontainer), GTK_WIDGET (priv->navigationsw), 4, 2 ); return GTK_WIDGET (self); } void moko_navigation_list_append_column (MokoNavigationList* self, GtkTreeViewColumn* column) { MokoNavigationListPrivate* priv = NAVIGATION_LIST_PRIVATE (self); moko_tree_view_append_column( MOKO_TREE_VIEW (priv->treeview), column ); } GtkWidget* moko_navigation_list_get_tree_view (MokoNavigationList* self) { MokoNavigationListPrivate* priv = NAVIGATION_LIST_PRIVATE (self); return GTK_WIDGET (priv->treeview); }