GnuCash 2.4.99
combocell-gnome.c
00001 /********************************************************************\
00002  * combocell-gnome.c -- implement combobox pull down cell for gnome *
00003  *                                                                  *
00004  * This program is free software; you can redistribute it and/or    *
00005  * modify it under the terms of the GNU General Public License as   *
00006  * published by the Free Software Foundation; either version 2 of   *
00007  * the License, or (at your option) any later version.              *
00008  *                                                                  *
00009  * This program is distributed in the hope that it will be useful,  *
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
00012  * GNU General Public License for more details.                     *
00013  *                                                                  *
00014  * You should have received a copy of the GNU General Public License*
00015  * along with this program; if not, contact:                        *
00016  *                                                                  *
00017  * Free Software Foundation           Voice:  +1-617-542-5942       *
00018  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
00019  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
00020  *                                                                  *
00021 \********************************************************************/
00022 
00023 /*
00024  * FILE: combocell-gnome.c
00025  *
00026  * FUNCTION: Implement gnome portion of a pull-down combo widget
00027  *           embedded in a table cell.
00028  *
00029  * HISTORY:
00030  * Copyright (c) 1998 Linas Vepstas <linas@linas.org>
00031  * Copyright (c) 1998-1999 Rob Browning <rlb@cs.utexas.edu>
00032  * Copyright (c) 2000 Linas Vepstas <linas@linas.org>
00033  * Copyright (c) 2006 David Hampton <hampton@employees.org>
00034  */
00035 
00036 #include "config.h"
00037 
00038 #include <gnome.h>
00039 #include <string.h>
00040 
00041 #include "QuickFill.h"
00042 #include "combocell.h"
00043 #include "gnc-gconf-utils.h"
00044 #include "gnucash-item-edit.h"
00045 #include "gnucash-item-list.h"
00046 #include "gnucash-sheet.h"
00047 #include "table-allgui.h"
00048 
00049 #define KEY_AUTO_RAISE_LISTS    "auto_raise_lists"
00050 
00051 typedef struct _PopBox
00052 {
00053     GnucashSheet *sheet;
00054     GncItemEdit  *item_edit;
00055     GncItemList  *item_list;
00056     GtkListStore *tmp_store;
00057 
00058     gboolean signals_connected; /* list signals connected? */
00059 
00060     gboolean list_popped;  /* list is popped up? */
00061 
00062     gboolean autosize;
00063 
00064     QuickFill *qf;
00065     gboolean use_quickfill_cache;  /* If TRUE, we don't own the qf */
00066 
00067     gboolean in_list_select;
00068 
00069     gboolean strict;
00070 
00071     gunichar complete_char; /* char to be used for auto-completion */
00072 
00073     GList *ignore_strings;
00074 } PopBox;
00075 
00076 
00077 static void gnc_combo_cell_gui_realize (BasicCell *bcell, gpointer w);
00078 static void gnc_combo_cell_gui_move (BasicCell *bcell);
00079 static void gnc_combo_cell_gui_destroy (BasicCell *bcell);
00080 static gboolean gnc_combo_cell_enter (BasicCell *bcell,
00081                                       int *cursor_position,
00082                                       int *start_selection,
00083                                       int *end_selection);
00084 static void gnc_combo_cell_leave (BasicCell *bcell);
00085 static void gnc_combo_cell_destroy (BasicCell *bcell);
00086 
00087 static GOnce auto_pop_init_once = G_ONCE_INIT;
00088 static gboolean auto_pop_combos = FALSE;
00089 
00090 
00091 static void
00092 gnc_combo_cell_set_autopop (GConfEntry *entry, gpointer user_data)
00093 {
00094     GConfValue *value;
00095 
00096     value = gconf_entry_get_value(entry);
00097     auto_pop_combos = gconf_value_get_bool(value);
00098 }
00099 
00100 static gpointer
00101 gnc_combo_cell_autopop_init (gpointer unused)
00102 {
00103     auto_pop_combos = gnc_gconf_get_bool (GCONF_GENERAL_REGISTER,
00104                                           KEY_AUTO_RAISE_LISTS,
00105                                           NULL);
00106 
00107     gnc_gconf_general_register_cb(KEY_AUTO_RAISE_LISTS,
00108                                   gnc_combo_cell_set_autopop,
00109                                   NULL);
00110     return NULL;
00111 }
00112 
00113 BasicCell *
00114 gnc_combo_cell_new (void)
00115 {
00116     ComboCell * cell;
00117 
00118     g_once(&auto_pop_init_once, gnc_combo_cell_autopop_init, NULL);
00119 
00120     cell = g_new0 (ComboCell, 1);
00121 
00122     gnc_combo_cell_init (cell);
00123 
00124     return &cell->cell;
00125 }
00126 
00127 void
00128 gnc_combo_cell_init (ComboCell *cell)
00129 {
00130     PopBox *box;
00131 
00132     gnc_basic_cell_init (&(cell->cell));
00133 
00134     cell->cell.is_popup = TRUE;
00135 
00136     cell->cell.destroy = gnc_combo_cell_destroy;
00137 
00138     cell->cell.gui_realize = gnc_combo_cell_gui_realize;
00139     cell->cell.gui_destroy = gnc_combo_cell_gui_destroy;
00140 
00141     box = g_new0 (PopBox, 1);
00142 
00143     box->sheet = NULL;
00144     box->item_edit = NULL;
00145     box->item_list = NULL;
00146     box->tmp_store = gtk_list_store_new (1, G_TYPE_STRING);
00147     box->signals_connected = FALSE;
00148     box->list_popped = FALSE;
00149     box->autosize = FALSE;
00150 
00151     cell->cell.gui_private = box;
00152 
00153     box->qf = gnc_quickfill_new ();
00154     box->use_quickfill_cache = FALSE;
00155 
00156     box->in_list_select = FALSE;
00157 
00158     box->strict = TRUE;
00159 
00160     box->complete_char = '\0';
00161 
00162     box->ignore_strings = NULL;
00163 }
00164 
00165 static void
00166 select_item_cb (GncItemList *item_list, char *item_string, gpointer data)
00167 {
00168     ComboCell *cell = data;
00169     PopBox *box = cell->cell.gui_private;
00170 
00171     box->in_list_select = TRUE;
00172     gnucash_sheet_modify_current_cell (box->sheet, item_string);
00173     box->in_list_select = FALSE;
00174 
00175     gnc_item_edit_hide_popup (box->item_edit);
00176     box->list_popped = FALSE;
00177 }
00178 
00179 static void
00180 change_item_cb (GncItemList *item_list, char *item_string, gpointer data)
00181 {
00182     ComboCell *cell = data;
00183     PopBox *box = cell->cell.gui_private;
00184 
00185     box->in_list_select = TRUE;
00186     gnucash_sheet_modify_current_cell (box->sheet, item_string);
00187     box->in_list_select = FALSE;
00188 }
00189 
00190 static void
00191 activate_item_cb (GncItemList *item_list, char *item_string, gpointer data)
00192 {
00193     ComboCell *cell = data;
00194     PopBox *box = cell->cell.gui_private;
00195 
00196     gnc_item_edit_hide_popup (box->item_edit);
00197     box->list_popped = FALSE;
00198 }
00199 
00200 static void
00201 key_press_item_cb (GncItemList *item_list, GdkEventKey *event, gpointer data)
00202 {
00203     ComboCell *cell = data;
00204     PopBox *box = cell->cell.gui_private;
00205 
00206     switch (event->keyval)
00207     {
00208     case GDK_Escape:
00209         gnc_item_edit_hide_popup (box->item_edit);
00210         box->list_popped = FALSE;
00211         break;
00212 
00213     default:
00214         gtk_widget_event (GTK_WIDGET(box->sheet),
00215                           (GdkEvent *) event);
00216         break;
00217     }
00218 }
00219 
00220 static void
00221 combo_disconnect_signals (ComboCell *cell)
00222 {
00223     PopBox *box = cell->cell.gui_private;
00224 
00225     if (!box->signals_connected)
00226         return;
00227 
00228     g_signal_handlers_disconnect_matched (G_OBJECT (box->item_list), G_SIGNAL_MATCH_DATA,
00229                                           0, 0, NULL, NULL, cell);
00230 
00231     box->signals_connected = FALSE;
00232 }
00233 
00234 static void
00235 combo_connect_signals (ComboCell *cell)
00236 {
00237     PopBox *box = cell->cell.gui_private;
00238 
00239     if (box->signals_connected)
00240         return;
00241 
00242     g_signal_connect (G_OBJECT (box->item_list), "select_item",
00243                       G_CALLBACK (select_item_cb), cell);
00244 
00245     g_signal_connect (G_OBJECT (box->item_list), "change_item",
00246                       G_CALLBACK (change_item_cb), cell);
00247 
00248     g_signal_connect (G_OBJECT (box->item_list), "activate_item",
00249                       G_CALLBACK (activate_item_cb), cell);
00250 
00251     g_signal_connect (G_OBJECT (box->item_list), "key_press_event",
00252                       G_CALLBACK (key_press_item_cb), cell);
00253 
00254     box->signals_connected = TRUE;
00255 }
00256 
00257 static void
00258 block_list_signals (ComboCell *cell)
00259 {
00260     PopBox *box = cell->cell.gui_private;
00261 
00262     if (!box->signals_connected)
00263         return;
00264 
00265     g_signal_handlers_block_matched (G_OBJECT (box->item_list), G_SIGNAL_MATCH_DATA,
00266                                      0, 0, NULL, NULL, cell);
00267 }
00268 
00269 static void
00270 unblock_list_signals (ComboCell *cell)
00271 {
00272     PopBox *box = cell->cell.gui_private;
00273 
00274     if (!box->signals_connected)
00275         return;
00276 
00277     g_signal_handlers_unblock_matched (G_OBJECT (box->item_list), G_SIGNAL_MATCH_DATA,
00278                                        0, 0, NULL, NULL, cell);
00279 }
00280 
00281 static void
00282 gnc_combo_cell_gui_destroy (BasicCell *bcell)
00283 {
00284     PopBox *box = bcell->gui_private;
00285     ComboCell *cell = (ComboCell *) bcell;
00286 
00287     if (cell->cell.gui_realize == NULL)
00288     {
00289         if (box != NULL && box->item_list != NULL)
00290         {
00291             combo_disconnect_signals(cell);
00292             g_object_unref (box->item_list);
00293             box->item_list = NULL;
00294         }
00295 
00296         /* allow the widget to be shown again */
00297         cell->cell.gui_realize = gnc_combo_cell_gui_realize;
00298         cell->cell.gui_move = NULL;
00299         cell->cell.enter_cell = NULL;
00300         cell->cell.leave_cell = NULL;
00301         cell->cell.gui_destroy = NULL;
00302     }
00303 }
00304 
00305 static void
00306 gnc_combo_cell_destroy (BasicCell *bcell)
00307 {
00308     ComboCell *cell = (ComboCell *) bcell;
00309     PopBox *box = cell->cell.gui_private;
00310 
00311     gnc_combo_cell_gui_destroy (&(cell->cell));
00312 
00313     if (box != NULL)
00314     {
00315         GList *node;
00316 
00317         /* Don't destroy the qf if its not ours to destroy */
00318         if (FALSE == box->use_quickfill_cache)
00319         {
00320             gnc_quickfill_destroy (box->qf);
00321             box->qf = NULL;
00322         }
00323 
00324         for (node = box->ignore_strings; node; node = node->next)
00325         {
00326             g_free (node->data);
00327             node->data = NULL;
00328         }
00329 
00330         g_list_free (box->ignore_strings);
00331         box->ignore_strings = NULL;
00332 
00333         g_free (box);
00334         cell->cell.gui_private = NULL;
00335     }
00336 
00337     cell->cell.gui_private = NULL;
00338     cell->cell.gui_realize = NULL;
00339 }
00340 
00341 void
00342 gnc_combo_cell_set_sort_enabled (ComboCell *cell, gboolean enabled)
00343 {
00344     PopBox *box;
00345 
00346     if (cell == NULL)
00347         return;
00348 
00349     box = cell->cell.gui_private;
00350     if (box->item_list == NULL)
00351         return;
00352 
00353     block_list_signals (cell);
00354     gnc_item_list_set_sort_enabled(box->item_list, enabled);
00355     unblock_list_signals (cell);
00356 }
00357 
00358 void
00359 gnc_combo_cell_clear_menu (ComboCell * cell)
00360 {
00361     PopBox *box;
00362 
00363     if (cell == NULL)
00364         return;
00365 
00366     box = cell->cell.gui_private;
00367     if (box == NULL)
00368         return;
00369 
00370     /* Don't destroy the qf if its not ours to destroy */
00371     if (FALSE == box->use_quickfill_cache)
00372     {
00373         gnc_quickfill_destroy (box->qf);
00374         box->qf = gnc_quickfill_new ();
00375     }
00376 
00377     if (box->item_list != NULL)
00378     {
00379         block_list_signals (cell);
00380 
00381         gnc_item_list_clear (box->item_list);
00382 
00383         unblock_list_signals (cell);
00384     }
00385 }
00386 
00387 void
00388 gnc_combo_cell_use_quickfill_cache (ComboCell * cell, QuickFill *shared_qf)
00389 {
00390     PopBox *box;
00391 
00392     if (cell == NULL) return;
00393 
00394     box = cell->cell.gui_private;
00395     if (NULL == box) return;
00396 
00397     if (FALSE == box->use_quickfill_cache)
00398     {
00399         box->use_quickfill_cache = TRUE;
00400         gnc_quickfill_destroy (box->qf);
00401     }
00402     box->qf = shared_qf;
00403 }
00404 
00405 void
00406 gnc_combo_cell_use_list_store_cache (ComboCell * cell, gpointer data)
00407 {
00408     if (cell == NULL) return;
00409 
00410     cell->shared_store = data;
00411 }
00412 
00413 void
00414 gnc_combo_cell_add_menu_item (ComboCell *cell, const char * menustr)
00415 {
00416     PopBox *box;
00417 
00418     if (cell == NULL)
00419         return;
00420     if (menustr == NULL)
00421         return;
00422 
00423     box = cell->cell.gui_private;
00424 
00425     if (box->item_list != NULL)
00426     {
00427         block_list_signals (cell);
00428 
00429         gnc_item_list_append (box->item_list, menustr);
00430         if (cell->cell.value &&
00431                 (strcmp (menustr, cell->cell.value) == 0))
00432             gnc_item_list_select (box->item_list, menustr);
00433 
00434         unblock_list_signals (cell);
00435     }
00436     else
00437     {
00438         GtkTreeIter iter;
00439 
00440         gtk_list_store_append(box->tmp_store, &iter);
00441         gtk_list_store_set(box->tmp_store, &iter, 0, menustr, -1);
00442     }
00443 
00444     /* If we're going to be using a pre-fab quickfill,
00445      * then don't fill it in here */
00446     if (FALSE == box->use_quickfill_cache)
00447     {
00448         gnc_quickfill_insert (box->qf, menustr, QUICKFILL_ALPHA);
00449     }
00450 }
00451 
00452 void
00453 gnc_combo_cell_add_account_menu_item (ComboCell *cell, char * menustr)
00454 {
00455     PopBox *box;
00456     gchar *menu_copy, *value_copy;
00457 
00458     if (cell == NULL)
00459         return;
00460     if (menustr == NULL)
00461         return;
00462 
00463     box = cell->cell.gui_private;
00464 
00465     if (box->item_list != NULL)
00466     {
00467         block_list_signals (cell);
00468 
00469         gnc_item_list_append (box->item_list, menustr);
00470         if (cell->cell.value)
00471         {
00472             menu_copy = g_strdelimit(g_strdup(menustr), "-:/\\.", ' ');
00473             value_copy =
00474                 g_strdelimit(g_strdup(cell->cell.value), "-:/\\.", ' ');
00475             if (strcmp (menu_copy, value_copy) == 0)
00476             {
00477                 gnc_combo_cell_set_value (cell, menustr);
00478                 gnc_item_list_select (box->item_list, menustr);
00479             }
00480             g_free(value_copy);
00481             g_free(menu_copy);
00482         }
00483         unblock_list_signals (cell);
00484     }
00485 
00486     /* If we're going to be using a pre-fab quickfill,
00487      * then don't fill it in here */
00488     if (FALSE == box->use_quickfill_cache)
00489     {
00490         gnc_quickfill_insert (box->qf, menustr, QUICKFILL_ALPHA);
00491     }
00492 }
00493 
00494 void
00495 gnc_combo_cell_set_value (ComboCell *cell, const char *str)
00496 {
00497     gnc_basic_cell_set_value (&cell->cell, str);
00498 }
00499 
00500 static void
00501 gnc_combo_cell_modify_verify (BasicCell *_cell,
00502                               const char *change,
00503                               int change_len,
00504                               const char *newval,
00505                               int newval_len,
00506                               int *cursor_position,
00507                               int *start_selection,
00508                               int *end_selection)
00509 {
00510     ComboCell *cell = (ComboCell *) _cell;
00511     PopBox *box = cell->cell.gui_private;
00512     const char *match_str;
00513     QuickFill *match;
00514     gboolean pop_list;
00515     glong newval_chars;
00516     glong change_chars;
00517 
00518     newval_chars = g_utf8_strlen (newval, newval_len);
00519     change_chars = g_utf8_strlen (change, change_len);
00520 
00521     if (box->in_list_select)
00522     {
00523         gnc_basic_cell_set_value_internal (_cell, newval);
00524 
00525         *cursor_position = -1;
00526         *start_selection = 0;
00527         *end_selection = -1;
00528 
00529         return;
00530     }
00531 
00532     /* If deleting, just accept */
00533     if (change == NULL)
00534     {
00535         gnc_basic_cell_set_value_internal (_cell, newval);
00536         return;
00537     }
00538 
00539     /* If we are inserting in the middle, just accept */
00540     if (*cursor_position < _cell->value_chars)
00541     {
00542         gnc_basic_cell_set_value_internal (_cell, newval);
00543         return;
00544     }
00545 
00546     match = gnc_quickfill_get_string_match (box->qf, newval);
00547 
00548     match_str = gnc_quickfill_string (match);
00549 
00550     if ((match == NULL) || (match_str == NULL))
00551     {
00552         gnc_basic_cell_set_value_internal (_cell, newval);
00553 
00554         block_list_signals (cell);
00555         gnc_item_list_select (box->item_list, NULL);
00556         unblock_list_signals (cell);
00557 
00558         return;
00559     }
00560 
00561     *start_selection = newval_chars;
00562     *end_selection = -1;
00563     *cursor_position += change_chars;
00564 
00565     if (!box->list_popped)
00566         pop_list = auto_pop_combos;
00567     else
00568         pop_list = FALSE;
00569 
00570     if (pop_list)
00571     {
00572         gnc_item_edit_show_popup (box->item_edit);
00573         box->list_popped = TRUE;
00574     }
00575 
00576     block_list_signals (cell);
00577     gnc_item_list_select (box->item_list, match_str);
00578     unblock_list_signals (cell);
00579 
00580     gnc_basic_cell_set_value_internal (_cell, match_str);
00581 }
00582 
00583 static gboolean
00584 gnc_combo_cell_direct_update (BasicCell *bcell,
00585                               int *cursor_position,
00586                               int *start_selection,
00587                               int *end_selection,
00588                               void *gui_data)
00589 {
00590     ComboCell *cell = (ComboCell *) bcell;
00591     PopBox *box = cell->cell.gui_private;
00592     GdkEventKey *event = gui_data;
00593     gboolean keep_on_going = FALSE;
00594     gboolean extra_colon;
00595     gunichar unicode_value;
00596     QuickFill *match;
00597     const char *match_str;
00598     int prefix_len;
00599     int find_pos;
00600     int new_pos;
00601 
00602     if (event->type != GDK_KEY_PRESS)
00603         return FALSE;
00604 
00605     unicode_value = gdk_keyval_to_unicode(event->keyval);
00606     switch (event->keyval)
00607     {
00608     case GDK_slash:
00609         if (!(event->state & GDK_MOD1_MASK))
00610         {
00611             if (unicode_value == box->complete_char)
00612                 break;
00613 
00614             return FALSE;
00615         }
00616         keep_on_going = TRUE;
00617         /* fall through */
00618     case GDK_Tab:
00619     case GDK_ISO_Left_Tab:
00620         if (!(event->state & GDK_CONTROL_MASK) &&
00621                 !keep_on_going)
00622             return FALSE;
00623 
00624         match = gnc_quickfill_get_string_len_match
00625                 (box->qf, bcell->value, *cursor_position);
00626         if (match == NULL)
00627             return TRUE;
00628 
00629         match = gnc_quickfill_get_unique_len_match
00630                 (match, &prefix_len);
00631         if (match == NULL)
00632             return TRUE;
00633 
00634         match_str = gnc_quickfill_string (match);
00635 
00636         if ((match_str != NULL) &&
00637                 (strncmp (match_str, bcell->value,
00638                           strlen (bcell->value)) == 0) &&
00639                 (strcmp (match_str, bcell->value) != 0))
00640         {
00641             gnc_basic_cell_set_value_internal (bcell,
00642                                                match_str);
00643 
00644             block_list_signals (cell);
00645             gnc_item_list_select (box->item_list,
00646                                   match_str);
00647             unblock_list_signals (cell);
00648         }
00649 
00650         *cursor_position += prefix_len;
00651         *start_selection = *cursor_position;
00652         *end_selection = -1;
00653 
00654         return TRUE;
00655     }
00656 
00657     if (box->complete_char == 0)
00658         return FALSE;
00659 
00660     if (unicode_value != box->complete_char)
00661         return FALSE;
00662 
00663     if (event->state & (GDK_CONTROL_MASK | GDK_MOD1_MASK))
00664         return FALSE;
00665 
00666     if ((*cursor_position < bcell->value_chars) &&
00667             ((*end_selection < bcell->value_chars) ||
00668              (*cursor_position < *start_selection)))
00669         return FALSE;
00670 
00671     if ((*cursor_position == bcell->value_chars) &&
00672             (*start_selection != *end_selection) &&
00673             (*end_selection < bcell->value_chars))
00674         return FALSE;
00675 
00676     find_pos = -1;
00677     if (*start_selection < bcell->value_chars)
00678     {
00679         int i = *start_selection;
00680         const char *c;
00681         gunichar uc;
00682 
00683         c = g_utf8_offset_to_pointer (bcell->value, i);
00684         while (*c)
00685         {
00686             uc = g_utf8_get_char (c);
00687             if (uc == box->complete_char)
00688             {
00689                 find_pos = (i + 1);
00690                 break;
00691             }
00692             c = g_utf8_next_char (c);
00693             i++;
00694         }
00695     }
00696 
00697     new_pos = *cursor_position;
00698 
00699     if (find_pos >= 0)
00700     {
00701         new_pos = find_pos;
00702         extra_colon = FALSE;
00703     }
00704     else
00705     {
00706         new_pos = bcell->value_chars;
00707         extra_colon = TRUE;
00708     }
00709 
00710     match = gnc_quickfill_get_string_len_match (box->qf,
00711             bcell->value, new_pos);
00712     if (match == NULL)
00713         return FALSE;
00714 
00715     if (extra_colon)
00716     {
00717         match = gnc_quickfill_get_char_match (match,
00718                                               box->complete_char);
00719         if (match == NULL)
00720             return FALSE;
00721 
00722         new_pos++;
00723     }
00724 
00725     match_str = gnc_quickfill_string (match);
00726 
00727     if ((match_str != NULL) &&
00728             (strncmp (match_str, bcell->value, strlen (bcell->value)) == 0) &&
00729             (strcmp (match_str, bcell->value) != 0))
00730     {
00731         gnc_basic_cell_set_value_internal (bcell, match_str);
00732 
00733         block_list_signals (cell);
00734         gnc_item_list_select (box->item_list, match_str);
00735         unblock_list_signals (cell);
00736     }
00737 
00738     *cursor_position = new_pos;
00739     *start_selection = new_pos;
00740     *end_selection = -1;
00741 
00742     return TRUE;
00743 }
00744 
00745 static void
00746 gnc_combo_cell_gui_realize (BasicCell *bcell, gpointer data)
00747 {
00748     GnucashSheet *sheet = data;
00749     GnomeCanvasItem *item = sheet->item_editor;
00750     GncItemEdit *item_edit = GNC_ITEM_EDIT (item);
00751     ComboCell *cell = (ComboCell *) bcell;
00752     PopBox *box = cell->cell.gui_private;
00753 
00754     /* initialize gui-specific, private data */
00755     box->sheet = sheet;
00756     box->item_edit = item_edit;
00757     if (cell->shared_store)
00758         box->item_list = gnc_item_edit_new_list(box->item_edit, cell->shared_store);
00759     else
00760         box->item_list = gnc_item_edit_new_list(box->item_edit, box->tmp_store);
00761     g_object_ref_sink(box->item_list);
00762 
00763     /* to mark cell as realized, remove the realize method */
00764     cell->cell.gui_realize = NULL;
00765     cell->cell.gui_move = gnc_combo_cell_gui_move;
00766     cell->cell.enter_cell = gnc_combo_cell_enter;
00767     cell->cell.leave_cell = gnc_combo_cell_leave;
00768     cell->cell.gui_destroy = gnc_combo_cell_gui_destroy;
00769     cell->cell.modify_verify = gnc_combo_cell_modify_verify;
00770     cell->cell.direct_update = gnc_combo_cell_direct_update;
00771 }
00772 
00773 static void
00774 gnc_combo_cell_gui_move (BasicCell *bcell)
00775 {
00776     PopBox *box = bcell->gui_private;
00777 
00778     combo_disconnect_signals ((ComboCell *) bcell);
00779 
00780     gnc_item_edit_set_popup (box->item_edit, NULL, NULL,
00781                              NULL, NULL, NULL, NULL, NULL);
00782 
00783     box->list_popped = FALSE;
00784 }
00785 
00786 static int
00787 get_popup_height (GnomeCanvasItem *item,
00788                   int space_available,
00789                   int row_height,
00790                   gpointer user_data)
00791 {
00792     PopBox *box = user_data;
00793     int count, pad = 4;
00794 
00795     count = gnc_item_list_num_entries(box->item_list);
00796     return MIN(space_available, (count * (row_height + pad)) + pad);
00797 }
00798 
00799 static int
00800 popup_autosize (GnomeCanvasItem *item,
00801                 int max_width,
00802                 gpointer user_data)
00803 {
00804     PopBox *box = user_data;
00805 
00806     if (!box || !box->autosize)
00807         return max_width;
00808 
00809     return gnc_item_list_autosize (GNC_ITEM_LIST (item)) + 20;
00810 }
00811 
00812 static void
00813 popup_set_focus (GnomeCanvasItem *item,
00814                  gpointer user_data)
00815 {
00816     gtk_widget_grab_focus (GTK_WIDGET (GNC_ITEM_LIST (item)->tree_view));
00817 }
00818 
00819 static void
00820 popup_post_show (GnomeCanvasItem *item,
00821                  gpointer user_data)
00822 {
00823     /* What the hell is this doing here? Well, under gtk+ 1.2.9,
00824      * the scrollbars never appear without it. Why does it work?
00825      * Why are you asking so many questions? There's nothing to
00826      * see here. These aren't the droids you're looking for.
00827      * Move along. */
00828     gtk_widget_size_request (GNC_ITEM_LIST (item)->frame, NULL);
00829 
00830     gnc_item_list_autosize (GNC_ITEM_LIST (item));
00831     gnc_item_list_show_selected (GNC_ITEM_LIST (item));
00832 }
00833 
00834 static int
00835 popup_get_width (GnomeCanvasItem *item,
00836                  gpointer user_data)
00837 {
00838     return GTK_WIDGET (GNC_ITEM_LIST (item)->tree_view)->allocation.width;
00839 }
00840 
00841 static gboolean
00842 gnc_combo_cell_enter (BasicCell *bcell,
00843                       int *cursor_position,
00844                       int *start_selection,
00845                       int *end_selection)
00846 {
00847     ComboCell *cell = (ComboCell *) bcell;
00848     PopBox *box = bcell->gui_private;
00849     GList *find = NULL;
00850 
00851     if (bcell->value)
00852         find = g_list_find_custom (box->ignore_strings,
00853                                    bcell->value,
00854                                    (GCompareFunc) strcmp);
00855     if (find)
00856         return FALSE;
00857 
00858     gnc_item_edit_set_popup (box->item_edit,
00859                              GNOME_CANVAS_ITEM (box->item_list),
00860                              get_popup_height, popup_autosize,
00861                              popup_set_focus, popup_post_show,
00862                              popup_get_width, box);
00863 
00864     block_list_signals (cell);
00865     gnc_item_list_select (box->item_list, bcell->value);
00866     unblock_list_signals (cell);
00867 
00868     combo_connect_signals (cell);
00869 
00870     *cursor_position = -1;
00871     *start_selection = 0;
00872     *end_selection = -1;
00873 
00874     return TRUE;
00875 }
00876 
00877 static void
00878 gnc_combo_cell_leave (BasicCell *bcell)
00879 {
00880     PopBox *box = bcell->gui_private;
00881 
00882     combo_disconnect_signals ((ComboCell *) bcell);
00883 
00884     gnc_item_edit_set_popup (box->item_edit, NULL, NULL,
00885                              NULL, NULL, NULL, NULL, NULL);
00886 
00887     box->list_popped = FALSE;
00888 
00889     if (box->strict)
00890     {
00891         if (bcell->value)
00892         {
00893             if (gnc_item_in_list (box->item_list, bcell->value))
00894                 return;
00895 
00896             if (g_list_find_custom (box->ignore_strings,
00897                                     bcell->value,
00898                                     (GCompareFunc) strcmp))
00899                 return;
00900         }
00901         gnc_basic_cell_set_value_internal (bcell, "");
00902     }
00903 }
00904 
00905 void
00906 gnc_combo_cell_set_strict (ComboCell *cell, gboolean strict)
00907 {
00908     PopBox *box;
00909 
00910     if (cell == NULL)
00911         return;
00912 
00913     box = cell->cell.gui_private;
00914 
00915     box->strict = strict;
00916 }
00917 
00918 void
00919 gnc_combo_cell_set_complete_char (ComboCell *cell, gunichar complete_char)
00920 {
00921     PopBox *box;
00922 
00923     if (cell == NULL)
00924         return;
00925 
00926     box = cell->cell.gui_private;
00927 
00928     box->complete_char = complete_char;
00929 }
00930 
00931 void
00932 gnc_combo_cell_add_ignore_string (ComboCell *cell,
00933                                   const char *ignore_string)
00934 {
00935     PopBox *box;
00936 
00937     if (cell == NULL)
00938         return;
00939 
00940     if (!ignore_string)
00941         return;
00942 
00943     box = cell->cell.gui_private;
00944 
00945     box->ignore_strings = g_list_prepend (box->ignore_strings,
00946                                           g_strdup (ignore_string));
00947 }
00948 
00949 void
00950 gnc_combo_cell_set_autosize (ComboCell *cell, gboolean autosize)
00951 {
00952     PopBox *box;
00953 
00954     if (!cell)
00955         return;
00956 
00957     box = cell->cell.gui_private;
00958     if (!box)
00959         return;
00960 
00961     box->autosize = autosize;
00962 }
00963 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines