ergtklistview.c

Go to the documentation of this file.
00001 /**
00002  * \file ergtklistview.c
00003  * \brief ereader gtk library - GtkTreeView object adapted for ereader system
00004  */
00005  
00006 /*
00007  * This file is part of libergtk.
00008  *
00009  * libergtk is free software: you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation, either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * libergtk is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program. If not, see <http://www.gnu.org/licenses/>.
00021  */
00022 
00023 /**
00024  * Copyright (C) 2008 iRex Technologies B.V.
00025  * All rights reserved.
00026  */
00027 
00028 // system include files
00029 #include <string.h>
00030 #include <ctype.h>
00031 #include <gtk/gtk.h>
00032 #include <math.h>
00033 #include <gdk/gdkkeysyms.h>
00034 
00035 #include "gtk/gtkprivate.h"
00036 
00037 // ereader library includes
00038 
00039 // local includes
00040 #include "ergtk_log.h"
00041 #include "ergtklistview.h"
00042 
00043 
00044 
00045 // local data
00046 static GtkTreeViewClass* g_parent_class = NULL;
00047 static guint pre_move_signal = 0;
00048 static guint navigate_cursor_signal = 0;
00049 
00050 
00051 // local functions
00052 
00053 
00054 // implementation of virtual methods
00055 static void             add_move_binding        (GtkBindingSet      *binding_set,
00056                                                  guint               keyval,
00057                                                  GdkModifierType     modmask,
00058                                                  erGtkListViewKeyPress navigate);
00059 
00060 
00061 // signal handlers
00062 static void             on_realize              (GtkWidget          *widget);
00063 
00064 static gboolean         on_pre_move_cursor      (erGtkListView      *er_listview,
00065                                                  erGtkListViewKeyPress navigate);
00066 
00067 static void             on_style_set            (GtkWidget          *widget, 
00068                                                  GtkStyle           *previous_style);
00069 
00070 static void             on_size_allocate        (GtkWidget          *widget, 
00071                                                  GtkAllocation      *allocation);
00072 
00073 static gboolean         on_button_press_event   (GtkWidget          *widget,
00074                                                  GdkEventButton     *event);
00075 
00076 static gboolean         on_button_release_event (GtkWidget          *widget,
00077                                                  GdkEventButton     *event);
00078 
00079 static gboolean         on_motion_notify_event  (GtkWidget          *widget, 
00080                                                  GdkEventMotion     *event);
00081 
00082 static void             on_column_notify_title  (GObject            *object,
00083                                                  GParamSpec         *arg1,
00084                                                  gpointer           user_data);
00085 
00086 static gboolean         on_focus_in             (GtkWidget          *widget, 
00087                                                  gpointer           data);
00088 
00089 static gboolean         on_focus_out            (GtkWidget          *widget, 
00090                                                  gpointer           data);
00091 
00092 //
00093 // Mandatory widget functions
00094 //
00095 
00096 static void ergtk_list_view_class_init (erGtkListViewClass* klass)
00097 {
00098     GObjectClass    *object_class = (GObjectClass*  )klass;
00099     GtkWidgetClass  *widget_class = (GtkWidgetClass*)klass;
00100     
00101     GtkBindingSet   *binding_set = gtk_binding_set_by_class (klass);
00102 
00103     // remember parent class struct, needed for chaining up to parent class
00104     g_parent_class = g_type_class_peek_parent(klass);
00105 
00106     // overload virtual methods
00107     widget_class->realize              = on_realize;
00108     widget_class->style_set            = on_style_set;
00109     widget_class->size_allocate        = on_size_allocate;
00110     widget_class->button_press_event   = on_button_press_event;
00111     widget_class->button_release_event = on_button_release_event;
00112     widget_class->motion_notify_event  = on_motion_notify_event;
00113 
00114     // signal handlers
00115     klass->pre_move_cursor = on_pre_move_cursor;
00116 
00117     gtk_widget_class_install_style_property (widget_class,
00118                                              g_param_spec_int ("column-spacing",
00119                                                                P_("Column spacing"),
00120                                                                P_("Empty pixels between columns"),
00121                                                                0, G_MAXINT, 0,
00122                                                                GTK_PARAM_READABLE));
00123 
00124     pre_move_signal = g_signal_new ("pre-move-cursor",
00125                                     G_TYPE_FROM_CLASS (object_class),
00126                                     G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
00127                                     G_STRUCT_OFFSET (erGtkListViewClass, pre_move_cursor),
00128                                     NULL, NULL,
00129                                     gtk_marshal_VOID__ENUM,
00130                                     G_TYPE_NONE,  
00131                                     1,            
00132                                     G_TYPE_INT);
00133 
00134     navigate_cursor_signal = g_signal_new("navigate-cursor",
00135                                           G_TYPE_FROM_CLASS (object_class),
00136                                           G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
00137                                           0,
00138                                           NULL, NULL,
00139                                           gtk_marshal_VOID__ENUM,
00140                                           G_TYPE_NONE,  
00141                                           1,            
00142                                           G_TYPE_INT);
00143     
00144     add_move_binding(binding_set, GDK_Up,           0, ERGTK_LIST_VIEW_PRESS_SHORT_UP);
00145     add_move_binding(binding_set, GDK_KP_Up,        0, ERGTK_LIST_VIEW_PRESS_SHORT_UP);
00146 
00147     add_move_binding(binding_set, GDK_Down,         0, ERGTK_LIST_VIEW_PRESS_SHORT_DOWN);
00148     add_move_binding(binding_set, GDK_KP_Down,      0, ERGTK_LIST_VIEW_PRESS_SHORT_DOWN);
00149 
00150     add_move_binding(binding_set, GDK_Left,         0, ERGTK_LIST_VIEW_PRESS_SHORT_UP);
00151     add_move_binding(binding_set, GDK_KP_Left,      0, ERGTK_LIST_VIEW_PRESS_SHORT_UP);
00152 
00153     add_move_binding(binding_set, GDK_Right,        0, ERGTK_LIST_VIEW_PRESS_SHORT_DOWN);
00154     add_move_binding(binding_set, GDK_KP_Right,     0, ERGTK_LIST_VIEW_PRESS_SHORT_DOWN);
00155 
00156     add_move_binding(binding_set, GDK_Page_Up,      0, ERGTK_LIST_VIEW_PRESS_LONG_UP);
00157     add_move_binding(binding_set, GDK_KP_Page_Up,   0, ERGTK_LIST_VIEW_PRESS_LONG_UP);
00158     
00159     add_move_binding(binding_set, GDK_Page_Down,    0, ERGTK_LIST_VIEW_PRESS_LONG_DOWN);
00160     add_move_binding(binding_set, GDK_KP_Page_Down, 0, ERGTK_LIST_VIEW_PRESS_LONG_DOWN);
00161 
00162     add_move_binding(binding_set, GDK_Home,         0, ERGTK_LIST_VIEW_PRESS_SHORT_UP);
00163     add_move_binding(binding_set, GDK_KP_Home,      0, ERGTK_LIST_VIEW_PRESS_SHORT_UP);
00164 
00165     add_move_binding(binding_set, GDK_End,          0, ERGTK_LIST_VIEW_PRESS_SHORT_DOWN);
00166     add_move_binding(binding_set, GDK_KP_End,       0, ERGTK_LIST_VIEW_PRESS_SHORT_DOWN);
00167 
00168     add_move_binding(binding_set, GDK_space,        0, ERGTK_LIST_VIEW_PRESS_ACTIVATE);
00169     add_move_binding(binding_set, GDK_Return,       0, ERGTK_LIST_VIEW_PRESS_ACTIVATE);
00170     add_move_binding(binding_set, GDK_ISO_Enter,    0, ERGTK_LIST_VIEW_PRESS_ACTIVATE);
00171     add_move_binding(binding_set, GDK_KP_Enter,     0, ERGTK_LIST_VIEW_PRESS_ACTIVATE);
00172 }
00173 
00174 
00175 // init instance
00176 static void ergtk_list_view_init (erGtkListView *er_listview)
00177 {
00178     g_return_if_fail(ERGTK_IS_LIST_VIEW(er_listview));
00179 
00180     GtkTreeView*  treeview = (GtkTreeView*) er_listview;
00181     //GtkWidget*    widget = (GtkWidget*) er_listview;
00182    
00183     // override defaults of GtkTreeView
00184     gtk_tree_view_set_headers_clickable(treeview, FALSE);
00185     gtk_tree_view_set_enable_search(treeview, FALSE);
00186 
00187     // initialise private data
00188     er_listview->row_height       = -1;
00189     er_listview->is_forced_height = FALSE;
00190     er_listview->is_button_down   = FALSE;
00191 }
00192 
00193 
00194 GType ergtk_list_view_get_type (void)
00195 {
00196     static GType class_type = 0;
00197 
00198     if (class_type == 0)
00199     {
00200         static const GTypeInfo class_info =
00201         {
00202             sizeof(erGtkListViewClass),
00203             NULL,               /* base_init */
00204             NULL,               /* base_finalize */
00205             (GClassInitFunc) ergtk_list_view_class_init,
00206             NULL,               /* class_finalize */
00207             NULL,               /* class_data */
00208             sizeof(erGtkListView),
00209             0,                  /* n_preallocs */
00210             (GInstanceInitFunc) ergtk_list_view_init,
00211             NULL                /* *value_table */            
00212             
00213         };
00214         class_type = g_type_register_static(GTK_TYPE_TREE_VIEW, "erGtkListView", &class_info, 0);
00215     }
00216 
00217     return class_type;
00218 }
00219 
00220 
00221 GtkWidget *ergtk_list_view_new (void)
00222 {
00223     return g_object_new (ERGTK_TYPE_LIST_VIEW, NULL);
00224 }
00225 
00226 
00227 GtkWidget *ergtk_list_view_new_with_model (GtkTreeModel *model)
00228 {
00229     return g_object_new (ERGTK_TYPE_LIST_VIEW, "model", model, NULL);
00230 }
00231 
00232 
00233 gint ergtk_list_view_append_column ( erGtkListView     *er_listview,
00234                                      GtkTreeViewColumn *column      )
00235 {
00236     g_return_val_if_fail( ERGTK_IS_LIST_VIEW(er_listview), 0 );
00237     g_return_val_if_fail( GTK_IS_TREE_VIEW_COLUMN(column), 0 );
00238 
00239     const gchar *title    = gtk_tree_view_column_get_title(column);
00240     GtkTreeView *treeview = (GtkTreeView *) er_listview;
00241 
00242     gint              num_columns    = 0;     // return value
00243     GtkWidget         *widget        = NULL;
00244     GtkWidget         *label         = NULL;
00245     GtkWidget         *event_box     = NULL;
00246     GtkBox            *vbox          = NULL;
00247     const gchar       *listview_name = gtk_widget_get_name( (GtkWidget*)er_listview );
00248     gchar             *header_name   = g_strdup_printf("%s-header", listview_name);
00249 
00250     LOGPRINTF("entry: title [%s]", title);
00251 
00252     // insert filler column for spacing
00253     if ( gtk_tree_view_get_column(treeview, 0) != NULL )
00254     {
00255        GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
00256        g_object_set( G_OBJECT(renderer),
00257                      "xpad", 0,
00258                      "ypad", 0,
00259                      "text", "",
00260                      NULL );
00261        GtkTreeViewColumn *filler_column = gtk_tree_view_column_new_with_attributes( "", renderer, NULL );
00262        gtk_tree_view_column_set_sizing(filler_column, GTK_TREE_VIEW_COLUMN_FIXED);
00263        gtk_tree_view_append_column(treeview, filler_column);
00264     }
00265 
00266     // set custom header for column
00267     if (title)
00268     {
00269         widget = gtk_tree_view_column_get_widget(column);
00270         if (widget == NULL)
00271         {
00272             //   column
00273             //     |-- widget vbox
00274             //           |-- label
00275             //           |-- event_box
00276             //
00277             widget = gtk_vbox_new(FALSE, 0);
00278             vbox = GTK_BOX(widget);
00279             //
00280             label = gtk_label_new( title );
00281             gtk_widget_set_name(label, header_name);
00282             gtk_misc_set_alignment( GTK_MISC(label), 0.0, 0.0);
00283             gtk_box_pack_start(vbox, label, FALSE, FALSE, 0);
00284             //
00285             event_box = gtk_event_box_new();
00286             gtk_widget_set_name(event_box, header_name);
00287             // TODO: replace hardcoded event-box height with a new style property
00288             gtk_widget_set_size_request(event_box, -1, 15);
00289             GTK_WIDGET_UNSET_FLAGS(event_box, GTK_CAN_FOCUS);
00290             gtk_box_pack_start(vbox, event_box, FALSE, FALSE, 0);
00291             //
00292             gtk_widget_show_all(widget);
00293             gtk_tree_view_column_set_widget(column, widget);
00294 
00295             // tell me when column title changes
00296             g_signal_connect(column, "notify::title", G_CALLBACK(on_column_notify_title), label);
00297         }
00298     }
00299 
00300     // add column to tree view
00301     num_columns = gtk_tree_view_append_column(treeview, column);
00302 
00303     // clean up
00304     g_free(header_name);
00305 
00306     return num_columns;
00307 }
00308 
00309 
00310 // on column title changed
00311 static void on_column_notify_title ( GObject            *object,
00312                                      GParamSpec         *arg1,
00313                                      gpointer           user_data )
00314 {
00315     LOGPRINTF("entry");
00316     g_return_if_fail(GTK_IS_TREE_VIEW_COLUMN(object));
00317     g_return_if_fail(GTK_IS_LABEL(user_data));
00318 
00319     GtkTreeViewColumn   *column = GTK_TREE_VIEW_COLUMN(object);
00320     GtkLabel            *label  = GTK_LABEL(user_data);
00321 
00322     if (column && label)
00323     {
00324         gtk_label_set_text(label, gtk_tree_view_column_get_title(column));
00325     }
00326 }
00327 
00328 
00329 //
00330 // Public widget methods
00331 //
00332 
00333 static void on_style_set (GtkWidget *widget, GtkStyle *previous_style)
00334 {
00335     g_return_if_fail(ERGTK_IS_LIST_VIEW(widget));
00336 
00337     GtkWidgetClass* parent_widget_class = GTK_WIDGET_CLASS(g_parent_class);
00338         
00339     // chain to parent class
00340     if (parent_widget_class->style_set)
00341     {
00342         parent_widget_class->style_set(widget, previous_style);
00343     }
00344 }
00345 
00346 
00347 static void on_size_allocate (GtkWidget *widget, GtkAllocation *allocation)
00348 {
00349     g_return_if_fail(ERGTK_IS_LIST_VIEW(widget));
00350 
00351     GtkWidgetClass      *parent_widget_class = GTK_WIDGET_CLASS(g_parent_class);
00352     GtkTreeView         *treeview    = (GtkTreeView  *) widget;
00353     erGtkListView       *er_listview = (erGtkListView*) widget;
00354 
00355     GList               *columns = NULL;
00356     GList               *p_col;
00357     GtkTreeViewColumn   *col;
00358     gint                col_idx;
00359     GList               *renderers = NULL;
00360     GList               *p_rend;
00361     GtkCellRenderer     *rend;
00362     GtkWidget           *child = NULL;
00363     GtkAllocation       child_allocation;
00364     gint                focus_line_width = 0;
00365     gint                column_spacing   = 0;
00366     gint                filler_width     = 0;
00367     gint                row_height       = 1;
00368     gboolean            is_forced_height = er_listview->is_forced_height;
00369     gint                height;
00370 
00371     LOGPRINTF("entry");
00372     
00373     // chain to parent class
00374     if (parent_widget_class->size_allocate)
00375     {
00376         parent_widget_class->size_allocate(widget, allocation);
00377     }
00378 
00379     // get style properties
00380     gtk_widget_style_get( widget,
00381                           "column-spacing",   &column_spacing,
00382                           "focus-line-width", &focus_line_width,
00383                           NULL );
00384     filler_width = column_spacing - (2 * focus_line_width);
00385     if (filler_width < 0)
00386     {
00387         filler_width = 0;
00388     }
00389 
00390     // adjust width of column headers
00391     // get max. current column height
00392     columns = gtk_tree_view_get_columns(treeview);
00393     for ( p_col = columns, col_idx = 0 ;
00394           p_col ;
00395           p_col = p_col->next, col_idx++ )
00396     {
00397         col = GTK_TREE_VIEW_COLUMN(p_col->data);
00398 
00399         if (col_idx % 2 == 0)
00400         {
00401             // data column
00402             //   set header width
00403             child = gtk_tree_view_column_get_widget(col);
00404             if (child)
00405             {
00406                 child_allocation       = child->allocation;
00407                 child_allocation.width = gtk_tree_view_column_get_width(col);
00408                 child_allocation.width -= 2 * focus_line_width;
00409                 gtk_widget_size_allocate(child, &child_allocation);
00410             }
00411 
00412             // adjust max. column height, if needed
00413             // note: this (probably) reflects the height of the first row only
00414             if ( !is_forced_height )
00415             {
00416                 gtk_tree_view_column_cell_get_size(col, NULL, NULL, NULL, NULL, &height );
00417                 LOGPRINTF("height [%d]", height);
00418                 if (height > row_height)
00419                 {
00420                     row_height = height;
00421                 }
00422             }
00423         }
00424         else
00425         {
00426             // filler column
00427             //   set column width
00428             gtk_tree_view_column_set_min_width(col, filler_width);
00429         }
00430     }
00431 
00432     // force height of first row to all rows
00433     if (is_forced_height)
00434     {
00435         row_height = er_listview->row_height;
00436     }
00437     else
00438     {
00439         er_listview->row_height = row_height;
00440     }
00441     row_height -= 2 * focus_line_width;
00442     for ( p_col = columns, col_idx = 0 ;
00443           p_col ;
00444           p_col = p_col->next, col_idx++ )
00445     {
00446         col = GTK_TREE_VIEW_COLUMN(p_col->data);
00447 
00448         if (col_idx % 2 == 0)
00449         {
00450             // data column
00451             //   get cell renderers
00452             //   set fixed height on each renderer
00453             renderers = gtk_tree_view_column_get_cell_renderers(col);
00454             for ( p_rend = renderers ; p_rend ; p_rend = p_rend->next )
00455             {
00456                 rend = GTK_CELL_RENDERER(p_rend->data);
00457                 gtk_cell_renderer_set_fixed_size(rend, -1, row_height);
00458             }
00459             g_list_free(renderers);
00460             renderers = NULL;
00461         }
00462     }
00463 
00464     // clean up
00465     g_list_free(columns);
00466     columns = NULL;
00467 }
00468 
00469 
00470 static gboolean on_button_press_event (GtkWidget      *widget,
00471                                        GdkEventButton *event)
00472 {
00473     g_return_val_if_fail(ERGTK_IS_LIST_VIEW(widget), FALSE);
00474 
00475     GtkTreeView     *treeview    = (GtkTreeView  *) widget;
00476     erGtkListView   *er_listview = (erGtkListView*) widget;
00477     GtkTreePath     *path = NULL;
00478     gint            *pi;
00479     gint            item_idx = 0;
00480     gboolean        ok;
00481 
00482     LOGPRINTF("entry: x y [%lf %lf] button [%d]", event->x, event->y, event->button);
00483     
00484     // find treeview item with this event
00485     ok = gtk_tree_view_get_path_at_pos(treeview, event->x, event->y, &path, NULL, NULL, NULL);
00486     if (ok  &&  path)
00487     {
00488         pi = gtk_tree_path_get_indices(path);
00489         if (pi)
00490         {
00491             item_idx = *pi;
00492         }
00493     }
00494     LOGPRINTF("item_idx [%d]", item_idx);
00495 
00496     // act only on left-button single-click 
00497     if (   ok
00498         && path
00499         && event->button == 1
00500         && event->type   == GDK_BUTTON_PRESS )
00501     {
00502         // store button-press info
00503         er_listview->is_button_down     = TRUE;
00504         er_listview->press_event_button = event->button;
00505         er_listview->press_event_state  = event->state;
00506         er_listview->press_event_x      = event->x;
00507         er_listview->press_event_y      = event->y;
00508 
00509         // remember treeview item with this event
00510         er_listview->press_item_idx = item_idx;
00511 
00512         // move cursor to selected item
00513         ergtk_list_view_set_cursor(er_listview, item_idx);
00514     }
00515     else
00516     {
00517         er_listview->is_button_down = FALSE;
00518     }
00519     LOGPRINTF("is_down [%d] item_idx [%d]", er_listview->is_button_down, item_idx);
00520     
00521     // clean up
00522     if (path) { gtk_tree_path_free(path); }
00523 
00524     return TRUE;  // stop event handling right now, no calls to other handlers
00525 }
00526 
00527 
00528 static gboolean on_button_release_event (GtkWidget      *widget,
00529                                          GdkEventButton *event)
00530 {
00531     g_return_val_if_fail(ERGTK_IS_LIST_VIEW(widget), FALSE);
00532 
00533     GtkTreeView     *treeview    = (GtkTreeView  *) widget;
00534     erGtkListView   *er_listview = (erGtkListView*) widget;
00535     GtkTreePath     *path = NULL;
00536     gint            *pi;
00537     gint            item_idx = 0;
00538     gboolean        ok;
00539     
00540     // find treeview item with this event
00541     ok = gtk_tree_view_get_path_at_pos(treeview, event->x, event->y, &path, NULL, NULL, NULL);
00542     if (ok && path)
00543     {
00544         // find item index
00545         pi = gtk_tree_path_get_indices(path);
00546         if (pi)
00547         {
00548             item_idx = *pi;
00549         }
00550 
00551         // act only on item that has received the left-button-down event
00552         if (   event->button == 1
00553             && er_listview->is_button_down
00554             && er_listview->press_event_button == event->button
00555             && er_listview->press_item_idx     == item_idx     )
00556         {
00557             LOGPRINTF("activate row [%d]", item_idx);
00558             gtk_tree_view_row_activated(treeview, path, NULL);
00559         }
00560     }
00561 
00562     // remember button not down
00563     er_listview->is_button_down = FALSE;
00564     
00565     // clean up
00566     if (path) { gtk_tree_path_free(path); }
00567 
00568     return TRUE;  // stop event handling right now, no calls to other handlers
00569 }
00570 
00571 
00572 static gboolean on_motion_notify_event(GtkWidget *widget, 
00573                                        GdkEventMotion *event)
00574 {
00575     // ignore drag-and-drop and rubberbanding
00576     return TRUE;
00577 }
00578 
00579 
00580 static void on_realize(GtkWidget *widget)
00581 {
00582     GtkWidgetClass  *parent_widget_class = GTK_WIDGET_CLASS(g_parent_class);
00583 
00584     // call parent first
00585     if (parent_widget_class->realize)
00586     {
00587         parent_widget_class->realize(widget);
00588     }
00589     
00590     // disable pointer motion events
00591     GdkEventMask mask = gdk_window_get_events(widget->window);
00592     gdk_window_set_events(widget->window, mask & ~(GDK_POINTER_MOTION_MASK));
00593 }
00594 
00595 
00596 void ergtk_list_view_get_cursor (erGtkListView *er_listview, gint *row)
00597 {
00598     GtkTreeView     *treeview = (GtkTreeView*) er_listview;
00599     GtkTreePath     *path = NULL;
00600     gint            *pi;
00601     gint            currow = -1;        // cursor row, return value
00602 
00603     g_return_if_fail(ERGTK_LIST_VIEW(er_listview));
00604 
00605     if (row != NULL)
00606     {
00607         path = gtk_tree_path_new();
00608         gtk_tree_view_get_cursor(treeview, &path, NULL);
00609         if (path == NULL)
00610         {
00611             LOGPRINTF("no cursor set");
00612         }
00613         else
00614         {
00615             pi = gtk_tree_path_get_indices(path);
00616             if (pi)
00617             {
00618                 currow = *pi;
00619             }
00620         }
00621         gtk_tree_path_free(path);
00622 
00623         *row = currow;
00624     }
00625 }
00626 
00627 
00628 void ergtk_list_view_get_view_size (erGtkListView *er_listview, gint *num_rows, gint *num_items)
00629 {
00630     g_return_if_fail(ERGTK_IS_LIST_VIEW(er_listview));
00631 
00632     GtkTreeView             *treeview = (GtkTreeView*) er_listview;
00633     GdkRectangle            visible_rect;
00634     GtkTreePath             *path = NULL;
00635     gboolean                ok;
00636     gint                    *pi;
00637     gint                    vertical_separator = 0;
00638 
00639     LOGPRINTF("entry");
00640     
00641     // get style properties
00642     gtk_widget_style_get( GTK_WIDGET(er_listview),
00643                           "vertical-separator", &vertical_separator,
00644                           NULL );
00645 
00646     // get maximum number of rows visible
00647     if (num_rows != NULL)
00648     { 
00649         // calculate max. number of items in treeview
00650         gtk_tree_view_get_visible_rect(treeview, &visible_rect);
00651         if (   visible_rect.height     > 0
00652             && er_listview->row_height > 0 )
00653         {
00654             *num_rows = visible_rect.height / (er_listview->row_height + vertical_separator);
00655         }
00656         else
00657         {
00658             *num_rows = 1;
00659         }
00660         LOGPRINTF("num_rows [%d]", *num_rows);
00661     }
00662     
00663     // get current number of items
00664     if (num_items != NULL)
00665     { 
00666         pi   = NULL;
00667         ok   = gtk_tree_view_get_visible_range(treeview, NULL, &path);
00668         if (ok  &&  path)
00669         {
00670             pi = gtk_tree_path_get_indices(path);
00671         }
00672 
00673         if (pi)
00674         {
00675             *num_items = *pi + 1;
00676         }
00677         else
00678         {
00679             *num_items = 0;
00680         }
00681         LOGPRINTF("num_items [%d]", *num_items);
00682 
00683         // clean up
00684         if (path) { gtk_tree_path_free(path); }
00685         path = NULL;
00686     }
00687     LOGPRINTF("leave");
00688 }
00689 
00690 
00691 void ergtk_list_view_set_cursor (erGtkListView  *er_listview, gint row)
00692 {
00693     g_return_if_fail(ERGTK_IS_LIST_VIEW(er_listview));
00694 
00695     GtkTreeView      *treeview  = (GtkTreeView*) er_listview;
00696     GtkWidget        *widget    = (GtkWidget  *) er_listview;
00697     GtkTreeSelection *selection = gtk_tree_view_get_selection(treeview);
00698     
00699     GtkTreePath *path = NULL;  
00700     gint num_rows = 0;
00701     gint num_items = 0;
00702     
00703     LOGPRINTF("entry: row %d", row);
00704 
00705     ergtk_list_view_get_view_size(er_listview, &num_rows, &num_items); 
00706     LOGPRINTF("num_rows %d, num_items %d", num_rows, num_items);
00707 
00708     // force row within valid range
00709     if (row >= num_items)
00710     {
00711         row = num_items - 1;
00712     }
00713     if (row < 0)
00714     {
00715         row = -1;
00716     }
00717 
00718     // create path from index and set cursor
00719     if (num_items > 0)
00720     {
00721         path = gtk_tree_path_new_from_indices(row, -1);
00722         gtk_widget_grab_focus (widget);
00723         gtk_tree_selection_select_path(selection, path);  
00724         gtk_tree_view_set_cursor(treeview, path, NULL, FALSE);
00725     }
00726 
00727     // clean up
00728     if (path) { gtk_tree_path_free(path); }
00729 
00730     return;
00731 }
00732 
00733 
00734 void ergtk_list_view_set_focus_mode (erGtkListView *er_listview, 
00735                                      gboolean      focus_in,
00736                                      gboolean      focus_out)
00737 {
00738     LOGPRINTF("entry");
00739 
00740     if (!focus_in)
00741     {
00742         // block focus-in events 
00743         g_signal_connect(G_OBJECT(er_listview), "focus-in-event", G_CALLBACK (on_focus_in), NULL);
00744     }
00745     if (!focus_out)
00746     {
00747         // block focus-out events 
00748         g_signal_connect(G_OBJECT(er_listview), "focus-out-event", G_CALLBACK (on_focus_out), NULL);
00749     }
00750 }
00751 
00752 
00753 void ergtk_list_view_set_row_height (erGtkListView *er_listview, gint row_height)
00754 {
00755     g_return_if_fail(ERGTK_IS_LIST_VIEW(er_listview));
00756 
00757     gint        focus_line_width = 0;
00758 
00759     LOGPRINTF("entry");
00760 
00761     if (row_height > 0)
00762     {
00763         gtk_widget_style_get( GTK_WIDGET(er_listview),
00764                               "focus-line-width", &focus_line_width,
00765                               NULL );
00766         er_listview->row_height       = row_height + (2 * focus_line_width);
00767         er_listview->is_forced_height = TRUE;
00768     }
00769     else
00770     {
00771         er_listview->row_height       = -1;
00772         er_listview->is_forced_height = FALSE;
00773     }
00774 }
00775 
00776 
00777 //
00778 // Internal functions
00779 //
00780 
00781 static gboolean on_pre_move_cursor (erGtkListView         *er_listview,
00782                                     erGtkListViewKeyPress navigate     )
00783 {
00784     GtkTreeView     *treeview = (GtkTreeView *) er_listview;
00785 
00786     gboolean        rc;  
00787     GtkTreePath     *path = NULL;
00788     gint            currow   = 0;       // cursor row
00789     gint            num_rows = 0;       // number of rows
00790     gint            num_items = 0;      // number of items currently displayed
00791 
00792     g_return_val_if_fail (ERGTK_LIST_VIEW (er_listview), FALSE);
00793 
00794     if (!GTK_WIDGET_HAS_FOCUS (GTK_WIDGET (er_listview)))
00795     {
00796         WARNPRINTF("widget has no focus");
00797         return FALSE;
00798     }
00799 
00800     ergtk_list_view_get_cursor(er_listview, &currow);
00801     ergtk_list_view_get_view_size(er_listview, &num_rows, &num_items); 
00802 
00803     LOGPRINTF("navigate [%d] currow [%d] num_rows [%d] num_items [%d]",
00804                navigate,     currow,     num_rows,     num_items       );
00805 
00806     switch (navigate)
00807     {
00808     case ERGTK_LIST_VIEW_PRESS_SHORT_UP:
00809         if (currow > 0)
00810         {
00811             // move up line in view
00812             g_signal_emit_by_name(er_listview, "move-cursor", GTK_MOVEMENT_DISPLAY_LINES, -1, &rc);
00813         }
00814         else 
00815         {      
00816             // emit signal
00817             g_signal_emit_by_name(er_listview, "navigate-cursor", ERGTK_LIST_VIEW_PRESS_SHORT_UP);
00818         }
00819         break;
00820         
00821     case ERGTK_LIST_VIEW_PRESS_SHORT_DOWN:
00822         if (currow + 1 < num_items)
00823         {
00824             g_signal_emit_by_name (er_listview, "move-cursor", GTK_MOVEMENT_DISPLAY_LINES, 1, &rc);
00825         }
00826         else
00827         {
00828             g_signal_emit_by_name(er_listview, "navigate-cursor", ERGTK_LIST_VIEW_PRESS_SHORT_DOWN);
00829         }
00830         break;
00831         
00832     case ERGTK_LIST_VIEW_PRESS_LONG_UP:
00833         g_signal_emit_by_name(er_listview, "navigate-cursor", ERGTK_LIST_VIEW_PRESS_LONG_UP);
00834         break;
00835         
00836     case ERGTK_LIST_VIEW_PRESS_LONG_DOWN:
00837         g_signal_emit_by_name(er_listview, "navigate-cursor", ERGTK_LIST_VIEW_PRESS_LONG_DOWN);
00838         break;
00839         
00840     case ERGTK_LIST_VIEW_PRESS_ACTIVATE:
00841         gtk_tree_view_row_activated(treeview, path, NULL);
00842         break;
00843 
00844     default:
00845         g_assert_not_reached();
00846         break;
00847     }
00848 
00849     // clean up
00850     if (path) { gtk_tree_path_free(path); }
00851 
00852     return TRUE;
00853 }
00854 
00855 
00856 static void
00857 add_move_binding (GtkBindingSet         *binding_set,
00858                   guint                 keyval,
00859                   GdkModifierType       modmask,
00860                   erGtkListViewKeyPress navigate)
00861 {
00862     gtk_binding_entry_add_signal(binding_set, keyval, modmask,
00863                                  "pre-move-cursor", 1,
00864                                  G_TYPE_ENUM, navigate);
00865 
00866     gtk_binding_entry_add_signal(binding_set, keyval, GDK_SHIFT_MASK,
00867                                  "pre-move-cursor", 1,
00868                                  G_TYPE_ENUM, navigate);
00869 
00870     if ((modmask & GDK_CONTROL_MASK) == GDK_CONTROL_MASK)
00871     {
00872         return;
00873     }
00874 
00875     gtk_binding_entry_add_signal(binding_set, keyval, GDK_CONTROL_MASK | GDK_SHIFT_MASK,
00876                                  "pre-move-cursor", 1,
00877                                  G_TYPE_ENUM, navigate);
00878 
00879     gtk_binding_entry_add_signal(binding_set, keyval, GDK_CONTROL_MASK,
00880                                  "pre-move-cursor", 1,
00881                                  G_TYPE_ENUM, navigate);
00882 }
00883 
00884 
00885 static gboolean on_focus_in(GtkWidget *widget, gpointer data)
00886 {
00887     LOGPRINTF("entry");
00888 
00889     // Return TRUE to stop other handlers from being invoked for the event 
00890     return  TRUE;
00891 }
00892 
00893 
00894 static gboolean on_focus_out(GtkWidget *widget, gpointer data)
00895 {
00896     LOGPRINTF("entry");
00897 
00898     // Return TRUE to stop other handlers from being invoked for the event 
00899     return  TRUE;
00900 }
00901 
Generated by  doxygen 1.6.2-20100208