/* moko-finger-tool-box.c * * Authored by Michael 'Mickey' Lauer * * 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) [$Author: mickey $] */ #include "moko-application.h" #include "moko-finger-tool-box.h" #include "moko-finger-window.h" #include "moko-pixmap-button.h" #include "moko-window.h" #include #include //#define DEBUG_THIS_FILE #ifdef DEBUG_THIS_FILE #define moko_debug(fmt,...) g_debug(fmt,##__VA_ARGS__) #define moko_debug_minder(predicate) moko_debug( __FUNCTION__ ); g_return_if_fail(predicate) #else #define moko_debug(...) #endif G_DEFINE_TYPE (MokoFingerToolBox, moko_finger_tool_box, MOKO_TYPE_ALIGNMENT) #define MOKO_FINGER_TOOL_BOX_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), MOKO_TYPE_FINGER_TOOL_BOX, MokoFingerToolBoxPrivate)) #define INNER_PADDING 10 static void moko_finger_tool_box_show(GtkWidget* widget); static void moko_finger_tool_box_hide(GtkWidget* widget); typedef struct _MokoFingerToolBoxPrivate { GdkBitmap* mask; GtkWidget* hbox; /* GtkHBox */ GtkWidget* rightarrow; /* MokoPixmapButton */ gboolean rightArrowVisible; guint maxButtonsPerPage; guint numberOfButtons; guint leftButton; guint buttonWidth; GtkWidget* popup; /* GtkWindow */ GtkWidget* parent; GdkPixbuf* background_pixbuf; GdkPixbuf* button_pixbuf; GdkPixbuf* rightarrow_pixbuf; guint numChild; } MokoFingerToolBoxPrivate; static gchar* get_bg_pixmap_name (GtkWidget *widget) { return widget->style->rc_style->bg_pixmap_name[GTK_STATE_NORMAL]; } static void moko_finger_tool_box_dispose (GObject *object) { if (G_OBJECT_CLASS (moko_finger_tool_box_parent_class)->dispose) G_OBJECT_CLASS (moko_finger_tool_box_parent_class)->dispose (object); } static void moko_finger_tool_box_finalize (GObject *object) { G_OBJECT_CLASS (moko_finger_tool_box_parent_class)->finalize (object); } static void moko_finger_tool_box_class_init(MokoFingerToolBoxClass *klass) { /* register private data */ g_type_class_add_private (klass, sizeof (MokoFingerToolBoxPrivate)); /* hook virtual methods */ GtkWidgetClass *widget_class = (GtkWidgetClass*) klass; widget_class->show = moko_finger_tool_box_show; widget_class->hide = moko_finger_tool_box_hide; /* install properties */ /* ... */ GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = moko_finger_tool_box_dispose; object_class->finalize = moko_finger_tool_box_finalize; } static void checkstatus( GtkWidget* child, MokoFingerToolBox* self ) { MokoFingerToolBoxPrivate* priv = MOKO_FINGER_TOOL_BOX_GET_PRIVATE(self); guint maxButtonsPerPage = priv->maxButtonsPerPage; if ( priv->rightArrowVisible ) maxButtonsPerPage--; moko_debug( "maxButtonsPerPage = %d", maxButtonsPerPage ); moko_debug( "child: '%s'", gtk_widget_get_name( child ) ); if ( strcmp( "mokofingertoolbox-toolbutton", gtk_widget_get_name( child ) ) == 0 ) { if ( priv->numChild < priv->leftButton || priv->numChild > priv->leftButton + maxButtonsPerPage-1 ) { moko_debug( "hiding child %d", priv->numChild ); gtk_widget_hide( child ); } else { moko_debug( "showing child %d", priv->numChild ); gtk_widget_show( child ); } } priv->numChild++; } static void cb_size_allocate(GtkWidget* widget, GtkAllocation* allocation, MokoFingerToolBox* self) { moko_debug( "size allocate %d, %d, %d, %d", allocation->x, allocation->y, allocation->width, allocation->height ); MokoFingerToolBoxPrivate* priv = MOKO_FINGER_TOOL_BOX_GET_PRIVATE(self); GtkAllocation* a = &(priv->hbox->allocation); //FIXME get from style priv->maxButtonsPerPage = a->width / ( priv->buttonWidth + (INNER_PADDING/2) ); moko_debug( "-- width % buttonWidth = %d", a->width % priv->buttonWidth ); priv->rightArrowVisible = priv->numberOfButtons > priv->maxButtonsPerPage; priv->numChild = 0; gtk_container_foreach( GTK_CONTAINER(priv->hbox), (GtkCallback) checkstatus, self ); if ( priv->rightArrowVisible ) gtk_widget_show( priv->rightarrow ); else gtk_widget_hide( priv->rightarrow ); moko_debug( "right button = %d", priv->rightArrowVisible ); } static void cb_configure(GtkWidget* widget, GtkAllocation* a, MokoFingerToolBox* self) { guint padding_top; guint padding_bottom; guint padding_left; guint padding_right; gtk_alignment_get_padding( GTK_ALIGNMENT(widget), &padding_top, &padding_bottom, &padding_left, &padding_right ); //If padding defined in gtkrc, reset top/bottom/left/right. GtkBorder* padding = NULL; gtk_widget_style_get (widget, "padding", &padding, NULL); if ( padding ) { padding_top = padding->top; padding_bottom = padding->bottom; padding_left = padding->left; padding_right = padding->right; } moko_debug( "my padding is %d, %d, %d, %d", padding_left, padding_top, padding_right, padding_bottom ); //FIXME unref all existing pixmaps, check whether we really need to draw new ones moko_debug( "generating pixmaps for size = %d, %d", a->width, a->height ); MokoFingerToolBoxPrivate* priv = MOKO_FINGER_TOOL_BOX_GET_PRIVATE(self); guint maxButtonsPerPage = priv->maxButtonsPerPage; if ( priv->rightArrowVisible ) maxButtonsPerPage--; moko_debug( "max buttons per page is %d", maxButtonsPerPage ); if ( !priv->background_pixbuf ) { gchar *filename = NULL; if ((filename = get_bg_pixmap_name (GTK_WIDGET (self)))) { priv->background_pixbuf = gdk_pixbuf_new_from_file( filename, NULL); if (!priv->background_pixbuf) { g_warning ("moko_finger_tool_box: could not load pixbuf for the finger tool box: %s", filename); return; } } else { g_warning ("moko_finger_tool_box: theme does not specify a background image for the finger tool box"); return; } /* TODO: ideally this should inspect bg_pixmap of the child items */ gchar *dirname = g_path_get_dirname (filename); gchar *buttonfile = g_build_filename (dirname, "btn_type03.png", NULL); priv->button_pixbuf = gdk_pixbuf_new_from_file( buttonfile, NULL); g_free (dirname); g_free (buttonfile); } GdkPixbuf* pixbuf = NULL; guint w = gdk_pixbuf_get_width( priv->button_pixbuf ); guint h = gdk_pixbuf_get_height( priv->button_pixbuf ); guint x = padding_left - 1; guint y = 0; if (GDK_IS_PIXBUF (priv->background_pixbuf)) { pixbuf = gdk_pixbuf_scale_simple( priv->background_pixbuf, a->width, a->height, GDK_INTERP_BILINEAR ); gdk_pixbuf_copy_area( priv->background_pixbuf, 0, 0, gdk_pixbuf_get_width( priv->background_pixbuf ), gdk_pixbuf_get_height( priv->background_pixbuf ), pixbuf, 0, 0 ); } guint composite_num; if ( maxButtonsPerPage == 0 ) composite_num = priv->numberOfButtons - priv->leftButton; else if ( priv->numberOfButtons - priv->leftButton >= maxButtonsPerPage ) composite_num = maxButtonsPerPage; else composite_num = priv->numberOfButtons % maxButtonsPerPage; for ( int i = 0; i < composite_num; ++i ) { //gdk_pixbuf_copy_area( priv->button_pixbuf, 0, 0, w, h, pixbuf, x, y ); gdk_pixbuf_composite( priv->button_pixbuf, pixbuf, x, y, w, h, x, y, 1, 1, GDK_INTERP_NEAREST, 255 ); x += w + INNER_PADDING - 2; } if ( priv->rightArrowVisible ) { /* FIXME: this really should be retrieved from the style */ if ( !priv->rightarrow_pixbuf ) priv->rightarrow_pixbuf = gdk_pixbuf_new_from_file( get_bg_pixmap_name (priv->rightarrow) , NULL); guint rightarrow_w = gdk_pixbuf_get_width( priv->rightarrow_pixbuf ); guint rightarrow_h = gdk_pixbuf_get_height( priv->rightarrow_pixbuf ); guint rightarrow_x = a->width - rightarrow_w; gdk_pixbuf_composite( priv->rightarrow_pixbuf, pixbuf, rightarrow_x, y, rightarrow_w, rightarrow_h, rightarrow_x, y, 1, 1, GDK_INTERP_NEAREST, 255 ); } #ifdef CRAZY_DEBUG_CODE GtkWindow* window = gtk_window_new( GTK_WINDOW_TOPLEVEL ); GtkImage* image = gtk_image_new_from_pixbuf( pixbuf ); gtk_container_add( GTK_CONTAINER(window), GTK_WIDGET(image) ); gtk_widget_show( image ); gtk_widget_show( window ); #endif gdk_pixbuf_render_pixmap_and_mask( pixbuf, NULL, &priv->mask, 1); gtk_widget_shape_combine_mask(priv->popup, priv->mask, 0, 0); priv->buttonWidth = w; } static void cb_right_button_pressed(GtkWidget* widget, MokoFingerToolBox* self) { moko_debug( "right button pressed" ); MokoFingerToolBoxPrivate* priv = MOKO_FINGER_TOOL_BOX_GET_PRIVATE(self); priv->leftButton += priv->maxButtonsPerPage -1 ; // % priv->numberOfButtons; if ( priv->leftButton >= priv->numberOfButtons ) priv->leftButton = 0; // force redraw //FIXME force redraw } static void moko_finger_tool_box_show(GtkWidget* widget) { //gtk_widget_ensure_style( widget ); //FIXME needed here? moko_debug( "moko_finger_wheel_show" ); GTK_WIDGET_CLASS(moko_finger_tool_box_parent_class)->show(widget); MokoFingerToolBoxPrivate* priv = MOKO_FINGER_TOOL_BOX_GET_PRIVATE(widget); if ( !priv->popup ) { priv->popup = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_window_set_type_hint( GTK_WINDOW (priv->popup), GDK_WINDOW_TYPE_HINT_DIALOG ); gtk_window_set_decorated( GTK_WINDOW (priv->popup), FALSE ); gtk_container_add( GTK_CONTAINER(priv->popup), widget ); } MokoWindow* window =(MokoWindow* )(priv->parent); g_return_if_fail( MOKO_IS_FINGER_WINDOW(window) ); //FIXME set also transient for all other windows belonging to this app? gtk_window_set_transient_for( GTK_WINDOW (priv->popup), GTK_WINDOW (window) ); GtkAllocation geometry; moko_finger_window_get_geometry_hint( MOKO_FINGER_WINDOW(window), widget, &geometry ); g_signal_connect_after( G_OBJECT(widget), "size_allocate", G_CALLBACK(cb_size_allocate), widget ); gtk_window_move( GTK_WINDOW (priv->popup), geometry.x, geometry.y ); gtk_widget_set_size_request( GTK_WIDGET(widget), geometry.width, geometry.height ); gtk_window_resize( GTK_WINDOW (priv->popup), geometry.width, geometry.height ); gtk_widget_show( priv->popup ); GtkWidget* wheel = moko_finger_window_get_wheel( MOKO_FINGER_WINDOW(window) ); if ( wheel && GTK_WIDGET_VISIBLE(wheel) ) moko_finger_wheel_set_transient_for( MOKO_FINGER_WHEEL (wheel), GTK_WINDOW (priv->popup) ); //moko_finger_wheel_raise( wheel ); } static void moko_finger_tool_box_hide(GtkWidget* widget) { moko_debug( "moko_finger_tool_box_hide" ); GTK_WIDGET_CLASS(moko_finger_tool_box_parent_class)->hide(widget); MokoFingerToolBoxPrivate* priv = MOKO_FINGER_TOOL_BOX_GET_PRIVATE(widget); gtk_widget_hide( priv->popup ); } static void moko_finger_tool_box_init(MokoFingerToolBox *self) { MokoFingerToolBoxPrivate* priv = MOKO_FINGER_TOOL_BOX_GET_PRIVATE(self); gtk_widget_set_name( GTK_WIDGET(self), "mokofingertoolbox" ); priv->rightarrow = moko_pixmap_button_new(); gtk_widget_set_name( priv->rightarrow, "mokofingertoolbox-rightarrow" ); priv->hbox = gtk_hbox_new( FALSE, INNER_PADDING ); gtk_container_add( GTK_CONTAINER(self), priv->hbox ); gtk_box_pack_end( GTK_BOX(priv->hbox), priv->rightarrow, FALSE, FALSE, 0 ); gtk_widget_show( priv->hbox ); g_signal_connect( G_OBJECT(priv->rightarrow), "clicked", G_CALLBACK(cb_right_button_pressed), self ); g_signal_connect_after( G_OBJECT(self), "size-allocate", G_CALLBACK(cb_configure), self ); } /* public API */ GtkWidget* moko_finger_tool_box_new(GtkWidget* parent) { MokoFingerToolBox* self=g_object_new(moko_finger_tool_box_get_type(), NULL); MokoFingerToolBoxPrivate* priv = MOKO_FINGER_TOOL_BOX_GET_PRIVATE(self); priv->parent=parent; return GTK_WIDGET(self); } GtkWidget* moko_finger_tool_box_add_button(MokoFingerToolBox* self) { MokoFingerToolBoxPrivate* priv = MOKO_FINGER_TOOL_BOX_GET_PRIVATE(self); GtkWidget* b = moko_pixmap_button_new(); gtk_widget_set_name( b, "mokofingertoolbox-toolbutton" ); priv->numberOfButtons++; gtk_box_pack_start( GTK_BOX(priv->hbox), b, FALSE, FALSE, 0 ); gtk_widget_show( b ); // save button for inside the expose event we want to get its shape //if ( !priv->button ) priv->button = b; // force redraw gtk_widget_queue_draw( priv->hbox ); return b; } GtkWidget* moko_finger_tool_box_add_button_without_label(MokoFingerToolBox* self) { MokoFingerToolBoxPrivate* priv = MOKO_FINGER_TOOL_BOX_GET_PRIVATE(self); GtkWidget* b = moko_pixmap_button_new(); gtk_widget_set_name( b, "mokofingertoolbox-toolbutton" ); priv->numberOfButtons++; gtk_box_pack_start( GTK_BOX(priv->hbox), b, FALSE, FALSE, 0 ); gtk_widget_show( b ); // save button for inside the expose event we want to get its shape //if ( !priv->button ) priv->button = b; // force redraw gtk_widget_queue_draw( priv->hbox ); return b; }