Re: Question about TreeView particularly GtkCellRendererCombo



1. Given that this is a dialog which may be popped up and dismissed several
times whilst the application runs, do I need to do any g_object_unrefs on
any of the list stores or other parts to avoid memory leaks?

If you'll be only hiding your popup window and reuse it several times,
you don't need to do anything, since the data structures will be
needed throughout the whole application life cycle.

On the other hand, if you intend to replace models or if you'll be
destroying your popup after every usage and re-create it on demand,
you need to unref models after they have been added to their place.
This way, the container will hold the last reference to that model and
when the models will be destroyed together with the parent.

Note that you don't need to unref GtkAdjustment, since it's created
with only floating reference which is sunken when adjustment gets
added in cell renderer.

(TIP: How to determine refcount of the created object?
If one of the object's ancestors is GInitiallyUnowned, the object has
only floating reference when created, otherwise it has refcount of 1.)

The required code modifications are marked inside my code snippet below.

2. Given that the type field in my structure is an integer, do I take it
that the appropriate starting type cannot be selected nor the final value
extracted when the dialog completes out of the combo as an integer, I have
to make the list column a string and insert the appropriate string and
decipher the appropriate string at the end as the Combo renderer is based on
a text entry so there is no equivalent of xxxx_get_active() like I have with
a "standalone" combo box?

I don't quite understand what are you trying to say here. But in
general, "look-a-like" treeview widgets (like GtkCellRendererCombo)
are whole different beasts than their "standalone" counterparts.

Their main task is to display things that they are ordered to. Orders
can come directly from their properties (these kind of orders are
created with g_object_set function call) or from underlying treeview's
model (put in place with gtk_tree_view_column_set_attributes function
call).

The "bonus" functionality is limited to the time when the cell
renderer is in "editing mode" (this is why you need to set their
"editable" property to TRUE - if you don't, combo and spin cell
renderers offer the same functionality as the non-editable text cell
renderer). Plus their bonus is strictly superficial - the underlying
model is NOT modified at all. It's programmer's responsibility to get
the selected value at the end of the editing and update the model.

I'm not sure if I answered your question, so feel free to send another mail.

3. Is there a sensible way without speaking pixels that I can say "allow for
at least n rows initially" as if my initial list is empty or has only one
row the thing is stupidly small (I am using a scroll on the treeview as it
could get arbitrarily large).

I only know gtk_widget_set_size_request function (and trying to avoid
it since it usually brings more problems than it solves). My usual
approach is to pack GtkScrolledWindow in such a way that it gets
resided whenever the main window is resized and the set the default
window size. It's not the perfect solution, but most of the time works
OK.


--------------
#include <gtk/gtk.h>

enum
{
   TYPE_COL,
   TEXT_COL,
   TEXT_SENS_COL,
   INT_COL,
   INT_SENS_COL,
   NO_COLS
};


static void
cb_type_changed( GtkCellRendererText *renderer,
                gchar               *path,
                gchar               *new_text,
                GtkListStore        *master )
{
   GtkTreeIter   iter;
   gchar        *old_text;
   gboolean      text_sens = FALSE;
   gboolean      int_sens = FALSE;

   /* Get previous value from master model */
   gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( master ),
                                        &iter, path );
   gtk_tree_model_get( GTK_TREE_MODEL( master ), &iter,
                       TYPE_COL, &old_text, -1 );

   /* Do we need to change anything? */
   if( ! g_strcmp0( old_text, new_text ) )
       return;

   /* What changes are needed? */
   if( ! g_strcmp0( new_text, "String" ) )
       text_sens = TRUE;
   else if( ! g_strcmp0( new_text, "Integer" ) )
       int_sens = TRUE;

   /* Update master model */
   gtk_list_store_set( master, &iter, TYPE_COL, new_text,
                                      TEXT_SENS_COL, text_sens,
                                      INT_SENS_COL, int_sens,
                                      -1 );
}


static void
cb_string_param_changed( GtkCellRendererText *renderer,
                        gchar               *path,
                        gchar               *new_text,
                        GtkListStore        *master )
{
   GtkTreeIter   iter;

   /* Update master model */
   gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( master ),
                                        &iter, path );
   gtk_list_store_set( master, &iter, TEXT_COL, new_text, -1 );
}

static void
cb_integer_param_changed( GtkCellRendererText *renderer,
                         gchar               *path,
                         gchar               *new_text,
                         GtkListStore        *master )
{
   GtkTreeIter   iter;
   gint          value;

   /* Update master model */
   gtk_tree_model_get_iter_from_string( GTK_TREE_MODEL( master ),
                                        &iter, path );
   value = (gint)strtol( new_text, NULL, 10 );
   gtk_list_store_set( master, &iter, INT_COL, value, -1 );
}

int
main( int    argc,
     char **argv )
{
   GtkWidget         *window;
   GtkWidget         *treeview;
   GtkListStore      *model;
   GtkListStore      *master;
   GtkTreeIter        iter;
   GtkCellRenderer   *renderer;
   GtkAdjustment     *adj;
   GtkTreeViewColumn *col;

   gtk_init( &argc, &argv );

   window = gtk_window_new( GTK_WINDOW_TOPLEVEL );
   g_signal_connect( G_OBJECT( window ), "destroy",
                     G_CALLBACK( gtk_main_quit ), NULL );


   /* create treeview */
   treeview = gtk_tree_view_new();
   gtk_tree_view_set_reorderable( GTK_TREE_VIEW( treeview ), TRUE );
   gtk_container_add( GTK_CONTAINER( window ), treeview );

   /* Create "master" model for treeview and add 2 entries */
   master = gtk_list_store_new( NO_COLS, G_TYPE_STRING,
                                         G_TYPE_STRING,
                                         G_TYPE_BOOLEAN,
                                         G_TYPE_INT,
                                         G_TYPE_BOOLEAN );
   gtk_list_store_append( master, &iter );
   gtk_list_store_set( master, &iter, TYPE_COL, "None",
                                      TEXT_COL, "Parameter 1",
                                      TEXT_SENS_COL, FALSE,
                                      INT_COL, 0,
                                      INT_SENS_COL, FALSE,
                                      -1 );
   gtk_list_store_append( master, &iter );
   gtk_list_store_set( master, &iter, TYPE_COL, "Integer",
                                      TEXT_COL, "Parameter 1",
                                      TEXT_SENS_COL, FALSE,
                                      INT_COL, 5,
                                      INT_SENS_COL, TRUE,
                                      -1 );
   gtk_tree_view_set_model( GTK_TREE_VIEW( treeview ),
                            GTK_TREE_MODEL( master ) );
   /* master has now refcount of 2 (1 from our initial creation +
    * 1 from addition to treeview). We'll remove our reference so
    * the model will be destroyed if:
    *   - it's removed from treeview
    *   - treeview gets destroyed
    */
   g_object_unref( G_OBJECT( master ) );

   /* column 1 */
   /* Create model for combo renderer and populate it */
   model = gtk_list_store_new( 2, G_TYPE_STRING, G_TYPE_INT );
   gtk_list_store_append( model, &iter );
   gtk_list_store_set( model, &iter, 0, "None", 1, 0, -1 );
   gtk_list_store_append( model, &iter );
   gtk_list_store_set( model, &iter, 0, "String", 1, 1, -1 );
   gtk_list_store_append( model, &iter );
   gtk_list_store_set( model, &iter, 0, "Integer", 1, 2, -1 );

   /* Create column and add renderer to it */
   col = gtk_tree_view_column_new();
   gtk_tree_view_column_set_resizable( col, TRUE );

   renderer = gtk_cell_renderer_combo_new();
   g_signal_connect( G_OBJECT( renderer ), "edited",
                     G_CALLBACK( cb_type_changed ), master );
   g_object_set( G_OBJECT( renderer ), "model", GTK_TREE_MODEL( model ),
                                       "text-column", 0,
                                       "editable", TRUE,
                                       "has_entry", FALSE,
                                       NULL );
   /* Unref model (the same logic applies here as with the first unref) */
   g_object_unref( G_OBJECT( model ) );

   gtk_tree_view_column_pack_start( col, renderer, TRUE );
   gtk_tree_view_column_set_attributes( col, renderer, "text", TYPE_COL,
                                        NULL );
   gtk_tree_view_append_column( GTK_TREE_VIEW( treeview ), col );

   /* column 2 */
   /* Again create model for combo renderer and populate it */
   model = gtk_list_store_new( 1, G_TYPE_STRING );
   gtk_list_store_append( model, &iter );
   gtk_list_store_set( model, &iter, 0, "Parameter 1", -1 );
   gtk_list_store_append( model, &iter );
   gtk_list_store_set( model, &iter, 0, "Parameter 2", -1 );
   gtk_list_store_append( model, &iter );
   gtk_list_store_set( model, &iter, 0, "Parameter 3", -1 );

   /* Create column and add renderer to it */
   col = gtk_tree_view_column_new();
   gtk_tree_view_column_set_resizable( col, TRUE );

   renderer = gtk_cell_renderer_combo_new();
   g_signal_connect( G_OBJECT( renderer ), "edited",
                     G_CALLBACK( cb_string_param_changed ), master );
   g_object_set( G_OBJECT( renderer ), "model", GTK_TREE_MODEL( model ),
                                       "text-column", 0,
                                       "has_entry", FALSE,
                                       NULL );
   /* Unref model (the same logic applies here as with the first unref) */
   g_object_unref( G_OBJECT( model ) );

   gtk_tree_view_column_pack_start( col, renderer, TRUE );
   gtk_tree_view_column_set_attributes( col, renderer,
                                        "text", TEXT_COL,
                                        "sensitive", TEXT_SENS_COL,
                                        "editable", TEXT_SENS_COL,
                                        NULL );
   gtk_tree_view_append_column( GTK_TREE_VIEW( treeview ), col );

   /* column 3 */
   /* Create adjustment for spin renderer */
   adj = GTK_ADJUSTMENT( gtk_adjustment_new( 0, 0, 100, 1, 10, 0 ) );

   /* Create column and add renderer to it */
   col = gtk_tree_view_column_new();
   gtk_tree_view_column_set_resizable( col, TRUE );

   renderer = gtk_cell_renderer_spin_new();
   g_signal_connect( G_OBJECT( renderer ), "edited",
                     G_CALLBACK( cb_integer_param_changed ), master );
   g_object_set( G_OBJECT( renderer ), "adjustment", adj,
                                       "editable", TRUE,
                                       NULL );
   gtk_tree_view_column_pack_start( col, renderer, TRUE );
   gtk_tree_view_column_set_attributes( col, renderer,
                                        "text", INT_COL,
                                        "sensitive", INT_SENS_COL,
                                        "editable", INT_SENS_COL,
                                        NULL );
   gtk_tree_view_append_column( GTK_TREE_VIEW( treeview ), col );


   gtk_widget_show_all( window );

   gtk_main();

   return( 0);
}
--------------

-- 
Tadej BorovÅak
00386 (0)40 613 131
tadeboro gmail com
tadej borovsak gmail com


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]