Re: Question about TreeView particularly GtkCellRendererCombo



I think that this cannot be achieved, main reason being that combo
cell renderer is actually drawing it's content with 2 different
renderers. First renderer is drawing text in non-editing state and
gets it's data from treeview's model. The second renders the whole
popup list in editing state and it's data comes from internal model.

This is why you need to copy the new string to the master model in the
"edited" signal handler - you cannot use data in combo renderer's
internal model in non-editing state.

2008/12/16 John M Collins <jmc xisl com>:
Thanks again for your help:

I do actually need to create and delete this dialog as needed, indeed in
principle I could have several of them active at the same time (they are
sub-structures hanging off a list of "master" structures) so I do need to be
careful that everything is destroyed.

The question you didn't quite understand was:

In your example code you specified the relevant column of the "master" list
store as a string, so when the rows were initialised you set a string value
"None", "String" "Integer" in the code below:

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 );

The type field in the structure which is being represented here by the combo
box renderer is an int so with the column given as a string the int has to
be translated to the appropriate string in the combo box list when it is
initialised and when the dialog finishes (or when the selection changes and
the relevant columns made sensitive or not sensitive) the string being
displayed has to be translated back to the appropriate int value.

I just wondered if it was possible to make the TYPE_COL column in the
"master" list store a G_TYPE_INT and have it select the relevant row in the
"model" combo box list store row and display the string for that so the code
could be:


gtk_list_store_append( master, &iter );
gtk_list_store_set( master, &iter, TYPE_COL, MY_STRUCT_TYPE_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, MY_STRUCT_TYPE_INT,
                                      TEXT_COL, "Parameter 1",
                                      TEXT_SENS_COL, FALSE,
                                      INT_COL, 5,
                                      INT_SENS_COL, TRUE,
                                      -1 );

My conclusion was that as the GtkCellRendererCombo was a derivative of the
GtkCellRendererText, (and with "has-entry" TRUE the string in there can be
different from any in the combo box list), this is not possible but I just
wanted to confirm it.


On Tue, 2008-12-16 at 12:25 +0100, Tadej BorovÅak wrote:

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);
}
--------------

John Collins Xi Software Ltd www.xisl.com






-- 
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]