1 /******************************************************************************/
2 /* */
3 /* Copyright (C) 2001-2008 Cédric Auger (cedric@grisbi.org) */
4 /* 2003-2008 Benjamin Drieu (bdrieu@april.org) */
5 /* 2009-2019 Pierre Biava (grisbi@pierre.biava.name) */
6 /* https://www.grisbi.org/ */
7 /* */
8 /* This program is free software; you can redistribute it and/or modify */
9 /* it under the terms of the GNU General Public License as published by */
10 /* the Free Software Foundation; either version 2 of the License, or */
11 /* (at your option) any later version. */
12 /* */
13 /* This program is distributed in the hope that it will be useful, */
14 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
15 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
16 /* GNU General Public License for more details. */
17 /* */
18 /* You should have received a copy of the GNU General Public License */
19 /* along with this program; if not, write to the Free Software */
20 /* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
21 /* */
22 /******************************************************************************/
23
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27
28 #include "include.h"
29
30 #include <glib/gi18n.h>
31 #include <gdk/gdkkeysyms.h>
32 #include <ctype.h>
33
34 /*START_INCLUDE*/
35 #include "gtk_combofix.h"
36 #include "grisbi_app.h"
37 #include "gsb_data_account.h"
38 #include "gsb_data_budget.h"
39 #include "gsb_data_category.h"
40 #include "gsb_data_form.h"
41 #include "gsb_form.h"
42 #include "gsb_form_widget.h"
43 #include "structures.h"
44 #include "utils_buttons.h"
45 #include "utils_str.h"
46 #include "erreur.h"
47 /*END_INCLUDE*/
48
49 /*START_STATIC*/
50 static gint block_expose_event;
51 static GtkTreeIter report_parent_iter;
52 /*END_STATIC*/
53
54 /*START_EXTERN*/
55 /*END_EXTERN*/
56
57 #define COMBOFIX_MIN_WIDTH 250
58
59 typedef struct _GtkComboFixPrivate GtkComboFixPrivate;
60
61 struct _GtkComboFixPrivate
62 {
63 GtkWidget * entry;
64 GtkWidget * button;
65 GtkWidget * popup;
66 GtkWidget * tree_view;
67
68 /* tree_view */
69 GtkTreeStore * store;
70 GtkTreeModel * model_filter;
71 GtkTreeModel * model_sort;
72 GtkTreeSelection * selection;
73
74 /* set properties */
75 gboolean case_sensitive; /* TRUE for case sensitive (in that case, the first entry give the case) */
76 gboolean force; /* TRUE if the entry content must belong to the list */
77 gboolean mixed_sort; /* TRUE mix the different list, FALSE separate them */
78 gint visible_items; /* number of items */
79 gint minimum_key_length; /* minimum_key_length of completion */
80 gboolean ignore_accents; /* if case_sensitive is TRUE ignore accents in the completion */
81
82 gint type; /* type : 0 : payee, 1 : category, 2 : budget */
83
84 /* old entry */
85 gchar * old_entry;
86 };
87
88 G_DEFINE_TYPE_WITH_PRIVATE (GtkComboFix, gtk_combofix, GTK_TYPE_BOX)
89
90 enum CombofixColumns
91 {
92 COMBOFIX_COL_VISIBLE_STRING = 0, /* string : what we see in the combofix */
93 COMBOFIX_COL_REAL_STRING, /* string : what we set in the entry when selecting something */
94 COMBOFIX_COL_VISIBLE, /* boolean : if that line has to be showed */
95 COMBOFIX_COL_LIST_NUMBER, /* int : the number of the list 0, 1 ou 2 (CREDIT DEBIT SPECIAL) */
96 COMBOFIX_COL_SEPARATOR, /* TRUE : if this is a separator */
97 COMBOFIX_N_COLUMNS
98 };
99
100 enum CombofixKeyDirection
101 {
102 COMBOFIX_UP = 0,
103 COMBOFIX_PAGE_UP,
104 COMBOFIX_DOWN,
105 COMBOFIX_PAGE_DOWN
106 };
107
108 /******************************************************************************/
109 /* Private functions */
110 /******************************************************************************/
111 /**
112 * positionne le bouton "Change" du formulaire si le compte destinataire
113 * du transfert a une devise différente du compte de départ.
114 *
115 * \param
116 * \param
117 * \param
118 * \param
119 *
120 * \return FALSE
121 **/
gtk_combofix_completion_match_selected(GtkEntryCompletion * entry,GtkTreeModel * model,GtkTreeIter * iter,GtkComboFix * combofix)122 static gboolean gtk_combofix_completion_match_selected (GtkEntryCompletion *entry,
123 GtkTreeModel *model,
124 GtkTreeIter *iter,
125 GtkComboFix *combofix)
126 {
127 GtkComboFixPrivate *priv;
128
129 priv = gtk_combofix_get_instance_private (combofix);
130 devel_debug (NULL);
131
132 if (priv->type == METATREE_CATEGORY)
133 {
134 gchar *tmp_str;
135
136 gtk_tree_model_get (GTK_TREE_MODEL (model), iter, 0, &tmp_str, -1);
137 if (g_str_has_prefix (tmp_str, _("Transfer : ")))
138 {
139 GtkWidget *widget;
140
141 widget = gsb_form_widget_get_widget (TRANSACTION_FORM_DEVISE);
142 if (widget != NULL && gtk_widget_get_visible (widget))
143 {
144 const gchar *entry_account_name;
145 gint entry_account_currency;
146 gint entry_account_number;
147 gint form_account_nb;
148 gint form_account_currency;
149
150 form_account_nb = gsb_form_get_account_number ();
151 form_account_currency = gsb_data_account_get_currency (form_account_nb);
152
153 /* data of selected account */
154 entry_account_name = memchr (tmp_str, ':', strlen (tmp_str));
155 entry_account_name = entry_account_name + 2;
156 entry_account_number = gsb_data_account_get_no_account_by_name (entry_account_name);
157 entry_account_currency = gsb_data_account_get_currency (entry_account_number);
158
159 if (entry_account_currency == form_account_currency)
160 gtk_widget_hide (gsb_form_widget_get_widget (TRANSACTION_FORM_CHANGE));
161 else
162 gtk_widget_show (gsb_form_widget_get_widget (TRANSACTION_FORM_CHANGE));
163 }
164 }
165 g_free (tmp_str);
166 }
167 return FALSE;
168 }
169
170 /**
171 * get the first item of completion and fill the entry with it
172 * Works if the length of the text >= the length of the completion key
173 *
174 * \param entry
175 *
176 * \return
177 **/
gtk_combofix_completion_choose_first_item(GtkWidget * entry)178 static void gtk_combofix_completion_choose_first_item (GtkWidget *entry)
179 {
180 GtkEntryCompletion *completion;
181 GtkTreeModel *model;
182 GtkTreeIter iter;
183 const gchar *key;
184 gchar *string;
185
186 completion = gtk_entry_get_completion (GTK_ENTRY (entry));
187 model = gtk_entry_completion_get_model (completion);
188 key = gtk_entry_get_text (GTK_ENTRY (entry));
189
190 if (gtk_tree_model_get_iter_first (model, &iter))
191 {
192 do
193 {
194 gtk_tree_model_get (GTK_TREE_MODEL (model), &iter, 0, &string, -1);
195 if (string && g_str_has_prefix (string, key))
196 {
197 gtk_entry_set_text (GTK_ENTRY (entry), string);
198 g_free (string);
199 break;
200 }
201 }
202 while (gtk_tree_model_iter_next (model, &iter));
203 }
204 }
205
206 /**
207 * insert un item dans la completion
208 *
209 * \param
210 * \param
211 *
212 * \return
213 **/
gtk_combofix_completion_insert_new_item(GtkComboFix * combofix,const gchar * text)214 static void gtk_combofix_completion_insert_new_item (GtkComboFix *combofix,
215 const gchar *text)
216 {
217 GtkEntryCompletion *completion;
218 GtkTreeModel *store;
219 GtkTreeIter iter;
220 GtkTreeIter new_iter;
221 GtkComboFixPrivate *priv;
222
223 priv = gtk_combofix_get_instance_private (combofix);
224
225 completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
226 store = gtk_entry_completion_get_model (completion);
227
228 if (gtk_tree_model_get_iter_first (store, &iter))
229 {
230 do
231 {
232 gchar *tmp_str;
233 gchar *str_to_free1;
234 gchar *str_to_free2;
235
236 gtk_tree_model_get (store, &iter, 0, &tmp_str, -1);
237 if (!tmp_str)
238 continue;
239
240 str_to_free1 = g_utf8_casefold (text, -1);
241 str_to_free2 = g_utf8_casefold (tmp_str, -1);
242 if (g_utf8_collate (str_to_free1, str_to_free2) < 0)
243 {
244 gtk_list_store_insert_before (GTK_LIST_STORE (store), &new_iter, &iter);
245 gtk_list_store_set (GTK_LIST_STORE (store), &new_iter, 0, text, -1);
246 g_free (str_to_free1);
247 g_free (str_to_free2);
248 g_free (tmp_str);
249
250 return;
251 }
252 g_free (str_to_free1);
253 g_free (str_to_free2);
254 g_free (tmp_str);
255 }
256 while (gtk_tree_model_iter_next (store, &iter));
257 }
258 else
259 {
260 gtk_list_store_append (GTK_LIST_STORE (store), &new_iter);
261 gtk_list_store_set (GTK_LIST_STORE (store), &new_iter, 0, text, -1);
262 }
263 }
264
265 /**
266 *
267 *
268 * \param
269 *
270 * \return
271 **/
gtk_combofix_completion_match_func(GtkEntryCompletion * completion,const gchar * key,GtkTreeIter * iter,gpointer user_data)272 static gboolean gtk_combofix_completion_match_func (GtkEntryCompletion *completion,
273 const gchar *key,
274 GtkTreeIter *iter,
275 gpointer user_data)
276 {
277 GtkTreeModel *model;
278 gchar *new_key;
279 gchar *new_text;
280 const gchar *search;
281 gchar *text;
282 gchar *tmp_text;
283 gssize nbre_bytes;
284
285 model = gtk_entry_completion_get_model (completion);
286 gtk_tree_model_get (model, iter, 0, &text, -1);
287
288 if (!text)
289 return FALSE;
290
291 search = gtk_entry_get_text (GTK_ENTRY (gtk_entry_completion_get_entry (completion)));
292 if (!search)
293 return FALSE;
294
295 new_key = utils_str_remove_accents (search);
296 tmp_text = utils_str_remove_accents (text);
297 nbre_bytes = strlen (new_key);
298 new_text = g_strndup (tmp_text, nbre_bytes);
299 g_free (tmp_text);
300
301 if (g_strcmp0 (new_text, new_key) == 0)
302 {
303 g_free (new_key);
304 g_free (new_text);
305 return TRUE;
306 }
307 else
308 {
309 g_free (new_key);
310 g_free (new_text);
311 return FALSE;
312 }
313
314 return FALSE;
315 }
316
317 /**
318 * supprime le séparateur pour les états comme tiers
319 *
320 * \param model
321 * \param iter_parent
322 *
323 * \return the position of parent_iter
324 **/
gtk_combofix_remove_for_report(GtkTreeModel * model,GtkTreeIter * iter_parent)325 static void gtk_combofix_remove_for_report (GtkTreeModel *model,
326 GtkTreeIter *iter_parent)
327 {
328 GtkTreeIter iter;
329 gboolean separator;
330 gboolean valid;
331
332 valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(model), &iter);
333 while (valid)
334 {
335 gtk_tree_model_get (GTK_TREE_MODEL(model),
336 &iter,
337 COMBOFIX_COL_SEPARATOR, &separator,
338 -1);
339 if (separator)
340 {
341 break;
342 }
343 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(model), &iter);
344 }
345
346 gtk_tree_store_remove (GTK_TREE_STORE (model), iter_parent);
347 gtk_tree_store_remove (GTK_TREE_STORE (model), &iter);
348 }
349
350 /**
351 * vérifie si il existe un séparateur, l'ajoute si nécessaire
352 *
353 * \param model
354 *
355 * \return TRUE si un séparateur a été ajouté, FALSE sinon
356 **/
gtk_combofix_search_for_report(GtkTreeModel * model)357 static gboolean gtk_combofix_search_for_report (GtkTreeModel *model)
358 {
359 GtkTreeIter iter;
360 gchar *tmp_str;
361 gboolean separator;
362 gboolean valid;
363
364 valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(model), &iter);
365 while (valid)
366 {
367 gtk_tree_model_get (GTK_TREE_MODEL(model),
368 &iter,
369 COMBOFIX_COL_SEPARATOR, &separator,
370 -1);
371
372 if (separator)
373 {
374 if (gtk_tree_model_iter_next (GTK_TREE_MODEL(model), &iter))
375 report_parent_iter = iter;
376
377 return FALSE;
378 }
379 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(model), &iter);
380 }
381
382 gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
383 gtk_tree_store_set (GTK_TREE_STORE (model),
384 &iter,
385 COMBOFIX_COL_LIST_NUMBER, 0,
386 COMBOFIX_COL_SEPARATOR, TRUE,
387 -1);
388
389 tmp_str = g_strdup (_("Report"));
390 gtk_tree_store_append (GTK_TREE_STORE (model), &iter, NULL);
391 gtk_tree_store_set (GTK_TREE_STORE (model),
392 &iter,
393 COMBOFIX_COL_VISIBLE_STRING, tmp_str,
394 COMBOFIX_COL_REAL_STRING, tmp_str,
395 COMBOFIX_COL_VISIBLE, TRUE,
396 COMBOFIX_COL_LIST_NUMBER, 1,
397 -1);
398 g_free (tmp_str);
399 report_parent_iter = iter;
400
401 return TRUE;
402 }
403
404 /**
405 * vérifie si l'état dont le nom est passé en paramètre existe
406 *
407 * \param model
408 * \param report_name
409 *
410 * \return TRUE if exist or FALSE
411 **/
gtk_combofix_search_report(GtkTreeModel * model,const gchar * report_name)412 static gboolean gtk_combofix_search_report (GtkTreeModel *model,
413 const gchar *report_name)
414 {
415 GtkTreeIter iter;
416
417 if (!gtk_tree_model_iter_has_child (GTK_TREE_MODEL (model), &report_parent_iter))
418 return FALSE;
419
420 if (!gtk_tree_model_iter_children (GTK_TREE_MODEL (model), &iter, &report_parent_iter))
421 return FALSE;
422 do
423 {
424 gchar *tmp_str = NULL;
425
426 gtk_tree_model_get (GTK_TREE_MODEL(model),
427 &iter,
428 COMBOFIX_COL_VISIBLE_STRING, &tmp_str,
429 -1);
430
431 if (tmp_str)
432 {
433 if (g_utf8_collate (tmp_str, report_name) == 0)
434 {
435 g_free (tmp_str);
436 return TRUE;
437 }
438 g_free (tmp_str);
439 }
440 }
441 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (model), &iter));
442
443 return FALSE;
444 }
445
446 /**
447 * vérifie si la chaine text existe déjà
448 *
449 * \param
450 * \param
451 * \param
452 * \param
453 *
454 * \return TRUE si trouvé FALSE autrement
455 **/
gtk_combofix_search_for_text(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * iter,gpointer * data)456 static gboolean gtk_combofix_search_for_text (GtkTreeModel *model,
457 GtkTreePath *path,
458 GtkTreeIter *iter,
459 gpointer *data)
460 {
461 gchar *tmp_str;
462 gboolean case_sensitive;
463 gboolean separator;
464 gint return_value;
465
466 gtk_tree_model_get (GTK_TREE_MODEL(model),
467 iter,
468 COMBOFIX_COL_REAL_STRING, &tmp_str,
469 COMBOFIX_COL_SEPARATOR, &separator,
470 -1);
471
472 if (separator)
473 {
474 g_free (tmp_str);
475 return FALSE;
476 }
477
478 case_sensitive = GPOINTER_TO_INT (data[2]);
479 if (case_sensitive)
480 return_value = !strcmp ((gchar *) data[0], tmp_str);
481 else
482 return_value = !g_utf8_collate (g_utf8_casefold ((gchar *) data[0], -1),
483 g_utf8_casefold (tmp_str, -1));
484 if (return_value)
485 data[1] = GINT_TO_POINTER (1);
486
487 return return_value;
488 }
489
490 /**
491 * fill a parent_iter of the model given in param
492 * with the string given in param
493 *
494 * \param store
495 * \param parent_iter
496 * \param string
497 * \param list_number the number of the list
498 *
499 * \return TRUE
500 **/
gtk_combofix_fill_iter_parent(GtkTreeStore * store,GtkTreeIter * iter_parent,const gchar * string,gint list_number)501 static gboolean gtk_combofix_fill_iter_parent (GtkTreeStore *store,
502 GtkTreeIter *iter_parent,
503 const gchar *string,
504 gint list_number)
505 {
506 gtk_tree_store_append (store, iter_parent, NULL);
507 gtk_tree_store_set (store,
508 iter_parent,
509 COMBOFIX_COL_VISIBLE_STRING, string,
510 COMBOFIX_COL_REAL_STRING, string,
511 COMBOFIX_COL_VISIBLE, TRUE,
512 COMBOFIX_COL_LIST_NUMBER, list_number,
513 -1);
514
515 return TRUE;
516 }
517
518 /**
519 * fill a child_iter of the model given in param
520 * with the string given in param
521 *
522 * \param store
523 * \param parent_iter
524 * \param string
525 * \param list_number the number of the list
526 *
527 * \return TRUE
528 **/
gtk_combofix_fill_iter_child(GtkTreeStore * store,GtkTreeIter * iter_parent,const gchar * string,const gchar * real_string,gint list_number)529 static gboolean gtk_combofix_fill_iter_child (GtkTreeStore *store,
530 GtkTreeIter *iter_parent,
531 const gchar *string,
532 const gchar *real_string,
533 gint list_number)
534 {
535 GtkTreeIter iter_child;
536
537 gtk_tree_store_append (store, &iter_child, iter_parent);
538 gtk_tree_store_set (store,
539 &iter_child,
540 COMBOFIX_COL_VISIBLE_STRING, string,
541 COMBOFIX_COL_REAL_STRING, real_string,
542 COMBOFIX_COL_VISIBLE, TRUE,
543 COMBOFIX_COL_LIST_NUMBER, list_number,
544 -1);
545
546 return TRUE;
547 }
548
549 /**
550 * fill the model of the combofix given in param
551 * with the list given in param
552 * carreful : the list is not cleared, so if needed, must do it before
553 *
554 * \param combofix
555 * \param list a g_slist of strings
556 * \param list_number the number of the list for a complex, 0 else
557 *
558 * \return TRUE ok, FALSE pb
559 **/
gtk_combofix_fill_store(GtkComboFix * combofix,GSList * list,gint list_number)560 static gboolean gtk_combofix_fill_store (GtkComboFix *combofix,
561 GSList *list,
562 gint list_number)
563 {
564 GSList *tmp_list;
565 GtkEntryCompletion *completion;
566 GtkTreeModel *completion_store;
567 GtkTreeIter iter_parent;
568 gchar *free_str1;
569 gchar *last_parent = NULL;
570 GtkComboFixPrivate *priv;
571
572 priv = gtk_combofix_get_instance_private (combofix);
573 if (!list)
574 return FALSE;
575
576 /* normally the list cannot begin by a child, but we check here to
577 * avoid a big crash */
578
579 if (list->data && ((gchar *) (list->data))[0] == '\t')
580 {
581 gboolean FAILED = TRUE;
582
583 g_print ("GtkComboFix error : the first entry in the list is a child, cannot fill the combofix\n");
584 g_return_val_if_fail (FAILED, FALSE);
585 }
586
587 completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
588 completion_store = gtk_entry_completion_get_model (completion);
589
590 free_str1 = g_utf8_casefold (_("Report"), -1);
591 tmp_list = list;
592
593 while (tmp_list)
594 {
595 gchar *string;
596 gchar* tmp_str;
597
598 string = tmp_list->data;
599
600 /* create the new iter where it's necessary and iter will focus on it */
601 if (string)
602 {
603 GtkTreeIter new_iter;
604
605 if (string[0] == '\t')
606 {
607 /* it's a child */
608 tmp_str = g_strconcat (last_parent, " : ", string + 1, NULL);
609 gtk_combofix_fill_iter_child (priv->store, &iter_parent, string + 1, tmp_str, list_number);
610
611 /* append a row in the completion */
612 gtk_list_store_append (GTK_LIST_STORE (completion_store), &new_iter);
613 gtk_list_store_set (GTK_LIST_STORE (completion_store), &new_iter, 0, tmp_str, -1);
614 g_free (tmp_str);
615 }
616 else
617 {
618 /* it's a parent */
619 gtk_combofix_fill_iter_parent (priv->store, &iter_parent, string, list_number);
620 /* append a row in the completion ignore reports for payees */
621 if (priv->type == METATREE_PAYEE)
622 {
623 gchar *free_str2;
624
625 free_str2 = g_utf8_casefold (string, -1);
626 if (g_utf8_collate (free_str1, free_str2))
627 {
628 gtk_list_store_append (GTK_LIST_STORE (completion_store), &new_iter);
629 gtk_list_store_set (GTK_LIST_STORE (completion_store), &new_iter, 0, string, -1);
630 }
631 g_free (free_str2);
632 }
633 else
634 {
635 /* on n'affiche pas la tête de categ/IB dans la completion si force Categ/IB est TRUE */
636 /* et il existe des sous catégories. On traite les listes credit et débit */
637 if (priv->type && priv->force && list_number < 2)
638 {
639 gint div_number;
640 gint nbre_sub_division = 0;
641
642 if (priv->type == METATREE_CATEGORY)
643 {
644 div_number = gsb_data_category_get_number_by_name (string, priv->force, priv->type);
645 nbre_sub_division = gsb_data_category_get_sub_category_list_length (div_number);
646 }
647 else
648 {
649 div_number = gsb_data_budget_get_number_by_name (string, priv->force, priv->type);
650 nbre_sub_division = gsb_data_budget_get_sub_budget_list_length (div_number);
651 }
652
653 if (nbre_sub_division == 0)
654 {
655 gtk_list_store_append (GTK_LIST_STORE (completion_store), &new_iter);
656 gtk_list_store_set (GTK_LIST_STORE (completion_store), &new_iter, 0, string, -1);
657 }
658 }
659 else
660 {
661 gtk_list_store_append (GTK_LIST_STORE (completion_store), &new_iter);
662 gtk_list_store_set (GTK_LIST_STORE (completion_store), &new_iter, 0, string, -1);
663 }
664 }
665
666 last_parent = string;
667 }
668 }
669
670 tmp_list = tmp_list->next;
671 }
672 g_free (free_str1);
673
674 priv->visible_items += g_slist_length (list);
675
676 return TRUE;
677 }
678
679 /**
680 * set all the rows of the list to be showed
681 *
682 * \param combofix
683 *
684 * \return FALSE
685 **/
gtk_combofix_set_all_visible_rows(GtkComboFix * combofix)686 static gboolean gtk_combofix_set_all_visible_rows (GtkComboFix *combofix)
687 {
688 GtkTreeModel *model;
689 GtkTreePath *path;
690 GtkTreeIter iter;
691 gint path_ok;
692 GtkComboFixPrivate *priv;
693
694 if (!combofix)
695 return FALSE;
696
697 priv = gtk_combofix_get_instance_private (combofix);
698 priv->visible_items = 0;
699 model = GTK_TREE_MODEL (priv->store);
700 path = gtk_tree_path_new_first ();
701 path_ok = gtk_tree_model_get_iter (model, &iter, path);
702
703 while (path_ok)
704 {
705 gint value;
706
707 /* if mixed_sort is set, we don't show any separator line */
708 if (priv->mixed_sort)
709 {
710 gint separator;
711
712 gtk_tree_model_get (GTK_TREE_MODEL (model),
713 &iter,
714 COMBOFIX_COL_SEPARATOR, &separator,
715 -1);
716
717 if (separator)
718 value = FALSE;
719 else
720 value = TRUE;
721 }
722 else
723 value = TRUE;
724
725 gtk_tree_store_set (GTK_TREE_STORE (model),
726 &iter,
727 COMBOFIX_COL_VISIBLE, value,
728 -1);
729
730 priv->visible_items++;
731
732 /* increment the path */
733 if (gtk_tree_model_iter_has_child (model, &iter))
734 gtk_tree_path_down (path);
735 else
736 gtk_tree_path_next (path);
737
738 path_ok = gtk_tree_model_get_iter (model, &iter, path);
739
740 /* if path_ok is FALSE, perhaps we are on the end of the children list... */
741 if (!path_ok && gtk_tree_path_get_depth (path) > 1)
742 {
743 gtk_tree_path_up (path);
744 gtk_tree_path_next (path);
745 path_ok = gtk_tree_model_get_iter (model, &iter, path);
746 }
747 }
748 gtk_tree_view_expand_all (GTK_TREE_VIEW (priv->tree_view));
749
750 return FALSE;
751 }
752
753 /**
754 *
755 *
756 * \param
757 *
758 * \return
759 **/
gtk_combofix_get_screen_height(GtkComboFix * combofix,gint y)760 static gint gtk_combofix_get_screen_height (GtkComboFix *combofix,
761 gint y)
762 {
763 #if GTK_CHECK_VERSION (3,22,0)
764 GdkWindow *window;
765 GdkDisplay *display;
766 GdkMonitor *monitor;
767 GdkRectangle rectangle;
768
769 window = gtk_widget_get_window (GTK_WIDGET (combofix));
770 display = gdk_window_get_display (window);
771 monitor = gdk_display_get_monitor_at_point (display, 0, y);
772 gdk_monitor_get_geometry (monitor, &rectangle);
773
774 return rectangle.height;
775 #else
776 return gdk_screen_height ();
777 #endif
778 }
779
780 /**
781 * set the position and the size of the popup
782 *
783 * \param combofix
784 *
785 * \return FALSE
786 **/
gtk_combofix_set_popup_position(GtkComboFix * combofix)787 static gboolean gtk_combofix_set_popup_position (GtkComboFix *combofix)
788 {
789 GtkWidget *form_transaction_part;
790 gint x = 0;
791 gint y = 0;
792 gint height;
793 gint row_spacing;
794 gint num_row;
795 GdkRectangle rectangle;
796 GtkAllocation allocation;
797 gint horizontal_separator;
798 gint screen_height;
799 GtkComboFixPrivate *priv;
800
801 if (!combofix)
802 return FALSE;
803
804 priv = gtk_combofix_get_instance_private (combofix);
805
806 /* get the position of the combofix */
807 /* en fait il semble qu'on récupère toujours la position de "form_transaction_part" */
808 gdk_window_get_origin (gtk_widget_get_window (priv->entry), &x, &y);
809
810 /* get the allocation of combofix */
811 gtk_widget_get_allocation (GTK_WIDGET (combofix), &allocation);
812
813 /* on corrige le bug (?) de gdk_window_get_origin () */
814 form_transaction_part = gsb_form_get_form_transaction_part ();
815 row_spacing = gtk_grid_get_row_spacing (GTK_GRID (form_transaction_part));
816 num_row = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (combofix), "num_row"));
817 if (num_row)
818 y += (num_row * (allocation.height + row_spacing));
819
820 gtk_widget_style_get (GTK_WIDGET (priv->tree_view),
821 "horizontal-separator", &horizontal_separator,
822 NULL);
823
824 if (gtk_widget_get_realized (priv->tree_view))
825 {
826 gtk_tree_view_get_cell_area (GTK_TREE_VIEW (priv->tree_view),
827 gtk_tree_path_new_first (),
828 NULL,
829 &rectangle);
830 /* the 4 is found at home, a good number to avoid the scrollbar with 1 item */
831 height = (priv->visible_items) * (rectangle.height + horizontal_separator) + 4;
832 }
833 else
834 {
835 height = (priv->visible_items) * (allocation.height + horizontal_separator) + 4;
836 }
837
838 /* if the popup is too small to contain all, we check to set it on the bottom or on the top
839 * if the place on the top is more than 2 times bigger than the bottom, we set it on the top */
840
841 screen_height = gtk_combofix_get_screen_height (combofix, y);
842
843 if (((screen_height - y - allocation.height) < height)
844 &&
845 (((screen_height - y) * 2) <= y))
846 {
847 /* popup on the top */
848 if (y > height)
849 y = y - height;
850 else
851 {
852 height = y;
853 y = 0;
854 }
855 }
856 else
857 {
858 /* popup on the bottom */
859 y += allocation.height;
860
861 if ((screen_height - y) < height)
862 height = screen_height - y;
863 }
864
865 gtk_window_move (GTK_WINDOW (priv->popup), x, y);
866 gtk_window_resize (GTK_WINDOW (priv->popup), allocation.width, height);
867
868 return FALSE;
869 }
870
871 /**
872 * called when the popup is exposed, used to set the selection
873 * because don't work if set directly after the entry_set
874 *
875 * \param combofix
876 *
877 * \return FALSE
878 **/
gtk_combofix_expose_entry(GtkComboFix * combofix)879 static gboolean gtk_combofix_expose_entry (GtkComboFix *combofix)
880 {
881 GtkComboFixPrivate *priv;
882
883 priv = gtk_combofix_get_instance_private (combofix);
884
885 if (block_expose_event)
886 return FALSE;
887
888 block_expose_event = 1;
889
890 gtk_editable_select_region (GTK_EDITABLE (priv->entry),
891 gtk_editable_get_position (GTK_EDITABLE (priv->entry)),
892 -1);
893 return FALSE;
894 }
895
896 /**
897 * called for a button press while the popup is showed
898 * if the mouse is outside the popup, hide it
899 *
900 * \param popup
901 * \param ev
902 * \param combofix
903 *
904 * \return TRUE if we are on the popup, FALSE else
905 **/
gtk_combofix_button_press(GtkWidget * popup,GdkEventButton * ev,GtkComboFix * combofix)906 static gboolean gtk_combofix_button_press (GtkWidget *popup,
907 GdkEventButton *ev,
908 GtkComboFix *combofix)
909 {
910 GtkAllocation allocation;
911
912 gtk_widget_get_allocation (popup, &allocation);
913
914 if ((ev->x_root > allocation.x)
915 && (ev->x_root < (allocation.x + allocation. width))
916 && (ev->y_root > allocation.y)
917 && (ev->x_root < (allocation.y +allocation. height)))
918 return TRUE;
919
920 gtk_widget_hide (popup);
921
922 return FALSE;
923 }
924
925 /**
926 * called when the entry receive a focus out event
927 * hide the popup and check the content of the entry if force is set
928 *
929 * \param entry
930 * \param ev
931 * \param combofix
932 *
933 * \return FALSE
934 **/
gtk_combofix_focus_out(GtkWidget * entry,GdkEvent * ev,GtkComboFix * combofix)935 static gboolean gtk_combofix_focus_out (GtkWidget *entry,
936 GdkEvent *ev,
937 GtkComboFix *combofix)
938 {
939 gtk_combofix_hide_popup (combofix);
940
941 /* hide the selection */
942 gtk_editable_select_region (GTK_EDITABLE (entry), 0, 0);
943
944 return (FALSE);
945 }
946
947 /**
948 * called when the entry receive a focus in event
949 *
950 *
951 * \param entry
952 * \param ev
953 * \param combofix
954 *
955 * \return FALSE
956 **/
gtk_combofix_focus_in(GtkWidget * entry,GdkEvent * ev,GtkComboFix * combofix)957 static gboolean gtk_combofix_focus_in (GtkWidget *entry,
958 GdkEvent *ev,
959 GtkComboFix *combofix)
960 {
961 const gchar *text;
962 GtkComboFixPrivate *priv;
963
964 priv = gtk_combofix_get_instance_private (combofix);
965
966 text = gtk_entry_get_text (GTK_ENTRY (priv->entry));
967
968 if (priv->old_entry && strlen (priv->old_entry))
969 g_free (priv->old_entry);
970
971 if (text && strlen (text))
972 priv->old_entry = g_strdup (text);
973 else
974 priv->old_entry = NULL;
975
976 return (FALSE);
977 }
978
979 /**
980 * the popup need to be modal to work fine, but the entry won't receive
981 * some signal anymore... that function continue the signal to the entry
982 *
983 * \param popup
984 * \param ev
985 * \param combofix
986 *
987 * \return FALSE or TRUE according to the entry key press event return
988 **/
gtk_combofix_popup_key_press_event(GtkWidget * popup,GdkEventKey * ev,GtkComboFix * combofix)989 static gboolean gtk_combofix_popup_key_press_event (GtkWidget *popup,
990 GdkEventKey *ev,
991 GtkComboFix *combofix)
992 {
993 gboolean return_val;
994 GtkComboFixPrivate *priv;
995
996 priv = gtk_combofix_get_instance_private (combofix);
997
998 g_signal_emit_by_name (priv->entry,
999 "key-press-event",
1000 ev,
1001 &return_val);
1002 return return_val;
1003 }
1004
1005 /**
1006 * get the selected item and fill the entry with it
1007 *
1008 * \param combofix
1009 *
1010 * \return TRUE if ok, FALSE if no selection
1011 **/
gtk_combofix_choose_selection(GtkComboFix * combofix)1012 static gboolean gtk_combofix_choose_selection (GtkComboFix *combofix)
1013 {
1014 GtkTreeIter iter;
1015 gchar *string;
1016 GtkComboFixPrivate *priv;
1017
1018 priv = gtk_combofix_get_instance_private (combofix);
1019
1020 /* if there is no selection, go away */
1021 if (!gtk_tree_selection_get_selected (priv->selection, NULL, &iter))
1022 return FALSE;
1023
1024 gtk_tree_model_get (GTK_TREE_MODEL (priv->model_sort),
1025 &iter,
1026 COMBOFIX_COL_REAL_STRING, &string,
1027 -1);
1028
1029 if (string && strlen (string))
1030 gtk_combofix_set_text (combofix, string);
1031
1032 return TRUE;
1033 }
1034
1035 /**
1036 * move the iter given in param of 1 step up or down and
1037 * go into the children if necessary
1038 *
1039 * \param model the tree model
1040 * \param iter a pointer to the iter to move
1041 * \param direction COMBOFIX_DOWN or COMBOFIX_UP
1042 *
1043 * \return TRUE ok, FALSE no change
1044 **/
gtk_combofix_move_selection_one_step(GtkComboFix * combofix,GtkTreeIter * iter,gint direction)1045 static gboolean gtk_combofix_move_selection_one_step (GtkComboFix *combofix,
1046 GtkTreeIter *iter,
1047 gint direction)
1048 {
1049 gint result = 0;
1050 GtkTreePath *path;
1051 GtkTreePath *saved_path;
1052 GtkTreeModel *model;
1053 gint separator;
1054 GtkComboFixPrivate *priv;
1055
1056 priv = gtk_combofix_get_instance_private (combofix);
1057 model = priv->model_sort;
1058 path = gtk_tree_model_get_path (model, iter);
1059 saved_path = gtk_tree_path_copy (path);
1060
1061 switch (direction)
1062 {
1063 case COMBOFIX_DOWN:
1064 do
1065 {
1066 if (gtk_tree_model_iter_has_child (model, iter)
1067 && gtk_tree_view_row_expanded (GTK_TREE_VIEW (priv->tree_view), path))
1068 gtk_tree_path_down (path);
1069 else
1070 gtk_tree_path_next (path);
1071
1072 result = gtk_tree_model_get_iter (model, iter, path);
1073
1074 /* if result is FALSE, perhaps we are on the end of the children list... */
1075 if (!result && gtk_tree_path_get_depth (path) > 1)
1076 {
1077 gtk_tree_path_up (path);
1078 gtk_tree_path_next (path);
1079 result = gtk_tree_model_get_iter (model, iter, path);
1080 }
1081
1082 /* check if we are not on a separator */
1083 if (result)
1084 gtk_tree_model_get (model,
1085 iter,
1086 COMBOFIX_COL_SEPARATOR, &separator,
1087 -1);
1088 else
1089 separator = 0;
1090 }
1091 while (separator);
1092 break;
1093
1094 case COMBOFIX_UP:
1095 do
1096 {
1097 result = gtk_tree_path_prev (path);
1098
1099 if (result)
1100 {
1101 /* there is a prev path, but now, if we are on a parent, go to the last child,
1102 * else, stay there */
1103 result = gtk_tree_model_get_iter (model, iter, path);
1104
1105 if (result
1106 && gtk_tree_model_iter_has_child (model, iter)
1107 && gtk_tree_view_row_expanded (GTK_TREE_VIEW (priv->tree_view), path))
1108 {
1109 /* there is some children, go to the last one */
1110 gint i;
1111
1112 gtk_tree_path_down (path);
1113
1114 for (i = 0 ; i < gtk_tree_model_iter_n_children (model, iter) - 1 ; i++)
1115 gtk_tree_path_next (path);
1116
1117 result = gtk_tree_model_get_iter (model, iter, path);
1118 }
1119 }
1120 else
1121 {
1122 /* there is no prev path, if we are not on the toplevel, go to the
1123 * parent */
1124
1125 if (gtk_tree_path_get_depth (path) > 1)
1126 {
1127 gtk_tree_path_up (path);
1128 result = gtk_tree_model_get_iter (model, iter, path);
1129 }
1130 }
1131 /* check if we are not on a separator */
1132 if (result)
1133 gtk_tree_model_get (model,
1134 iter,
1135 COMBOFIX_COL_SEPARATOR, &separator,
1136 -1);
1137 else
1138 separator = 0;
1139 }
1140 while (separator);
1141 break;
1142 }
1143
1144 gtk_tree_path_free (path);
1145
1146 /* if result is FALSE, iter was changed so set it to its initial value */
1147 if (!result)
1148 gtk_tree_model_get_iter (model, iter, saved_path);
1149
1150 gtk_tree_path_free (saved_path);
1151
1152 return result;
1153 }
1154
1155 /**
1156 * return the number of visible rows showed on a page in the popup
1157 *
1158 * \param combofix
1159 *
1160 * \return the number of visible rows, 0 if problem
1161 **/
gtk_combofix_get_rows_number_by_page(GtkComboFix * combofix)1162 static gint gtk_combofix_get_rows_number_by_page (GtkComboFix *combofix)
1163 {
1164 gint return_value;
1165 GtkAdjustment *adjustment;
1166 GtkComboFixPrivate *priv;
1167
1168 if (!combofix)
1169 return 0;
1170
1171 priv = gtk_combofix_get_instance_private (combofix);
1172 adjustment = gtk_scrollable_get_vadjustment (GTK_SCROLLABLE (priv->tree_view));
1173 return_value = priv->visible_items
1174 * gtk_adjustment_get_page_size (adjustment)
1175 / gtk_adjustment_get_upper (adjustment);
1176
1177 return return_value;
1178 }
1179
1180 /**
1181 * called to move the selection in the tree_view
1182 * didn't succeed to give the focus to the tree_view so must do
1183 * this manual
1184 *
1185 * \param combofix
1186 * \param direction a combofix_key_direction
1187 *
1188 * \return FALSE
1189 **/
gtk_combofix_move_selection(GtkComboFix * combofix,gint direction)1190 static gboolean gtk_combofix_move_selection (GtkComboFix *combofix,
1191 gint direction)
1192 {
1193 GtkTreeIter sorted_iter;
1194 gint result = 0;
1195 GtkComboFixPrivate *priv;
1196
1197 if (!combofix)
1198 return FALSE;
1199
1200 priv = gtk_combofix_get_instance_private (combofix);
1201
1202 if (gtk_tree_selection_get_selected (priv->selection, NULL, &sorted_iter))
1203 {
1204 /* there is already a selection */
1205 gint i;
1206
1207 switch (direction)
1208 {
1209 case COMBOFIX_DOWN:
1210 result = gtk_combofix_move_selection_one_step (combofix,
1211 &sorted_iter,
1212 COMBOFIX_DOWN);
1213 break;
1214
1215 case COMBOFIX_UP:
1216 result = gtk_combofix_move_selection_one_step (combofix,
1217 &sorted_iter,
1218 COMBOFIX_UP);
1219 break;
1220
1221 case COMBOFIX_PAGE_DOWN:
1222 for (i=0 ; i<gtk_combofix_get_rows_number_by_page (combofix) ; i++)
1223 result = result | gtk_combofix_move_selection_one_step (combofix,
1224 &sorted_iter,
1225 COMBOFIX_DOWN);
1226 break;
1227
1228 case COMBOFIX_PAGE_UP:
1229 for (i=0 ; i<gtk_combofix_get_rows_number_by_page (combofix) ; i++)
1230 result = result | gtk_combofix_move_selection_one_step (combofix,
1231 &sorted_iter,
1232 COMBOFIX_UP);
1233 break;
1234 }
1235 }
1236 else
1237 {
1238 /* there is no current selection,
1239 * get the first selectable line */
1240 gint separator = 0;
1241 do
1242 {
1243 if (separator)
1244 result = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->model_sort), &sorted_iter);
1245 else
1246 result = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->model_sort), &sorted_iter);
1247 if (result)
1248 gtk_tree_model_get (GTK_TREE_MODEL (priv->model_sort),
1249 &sorted_iter,
1250 COMBOFIX_COL_SEPARATOR, &separator,
1251 -1);
1252 else
1253 separator = 0;
1254 }
1255 while (separator);
1256 }
1257
1258 if (result)
1259 {
1260 GtkTreePath *path;
1261
1262 gtk_tree_selection_select_iter (priv->selection, &sorted_iter);
1263 path = gtk_tree_model_get_path (GTK_TREE_MODEL (priv->model_sort), &sorted_iter);
1264 if (path)
1265 {
1266 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->tree_view), path, NULL, FALSE, 0, 0);
1267 gtk_tree_path_free (path);
1268 }
1269 }
1270
1271 return FALSE;
1272 }
1273
1274 /**
1275 * called for a key-press-event on the entry of the combofix
1276 *
1277 * \param entry
1278 * \param ev
1279 * \param combofix
1280 *
1281 * \return FALSE or TRUE, depends if need to block the signal
1282 **/
gtk_combofix_key_press_event(GtkWidget * entry,GdkEventKey * ev,GtkComboFix * combofix)1283 static gboolean gtk_combofix_key_press_event (GtkWidget *entry,
1284 GdkEventKey *ev,
1285 GtkComboFix *combofix)
1286 {
1287 GrisbiAppConf *a_conf;
1288 GtkComboFixPrivate *priv;
1289
1290 priv = gtk_combofix_get_instance_private (combofix);
1291 a_conf = (GrisbiAppConf *) grisbi_app_get_a_conf ();
1292
1293 switch (ev->keyval)
1294 {
1295 case GDK_KEY_ISO_Left_Tab:
1296 case GDK_KEY_Tab :
1297 case GDK_KEY_KP_Enter :
1298 case GDK_KEY_Return :
1299 /* we get the current selection */
1300 if (gtk_widget_get_visible (priv->popup)
1301 && strlen (gtk_entry_get_text (GTK_ENTRY (priv->entry))) == 0)
1302 {
1303 if (!gtk_combofix_choose_selection (combofix))
1304 {
1305 /* here we did entry key, but no selection... so
1306 * keep the current completion */
1307 gtk_combofix_hide_popup (combofix);
1308 gtk_editable_select_region (GTK_EDITABLE (priv->entry), 0, 0);
1309 }
1310 }
1311 else if ((gint) strlen (gtk_entry_get_text (GTK_ENTRY (priv->entry))) >=
1312 a_conf->completion_minimum_key_length)
1313 {
1314 gtk_combofix_completion_choose_first_item (entry);
1315 }
1316 /* le traitement de ENTER est fait dans le formulaire */
1317 return FALSE;
1318 break;
1319
1320 case GDK_KEY_Escape:
1321 if (gtk_widget_get_visible (priv->popup))
1322 {
1323 gtk_combofix_hide_popup (combofix);
1324 gtk_combofix_set_text (combofix, priv->old_entry);
1325 gtk_editable_select_region (GTK_EDITABLE (priv->entry), 0, 0);
1326 return TRUE;
1327 }
1328 break;
1329
1330 case GDK_KEY_Down :
1331 case GDK_KEY_KP_Down :
1332 /* show the popup if necessary */
1333 if (!gtk_widget_get_visible (priv->popup))
1334 gtk_combofix_show_popup (combofix);
1335
1336 gtk_combofix_move_selection (combofix, COMBOFIX_DOWN);
1337 gtk_combofix_choose_selection (combofix);
1338 return TRUE;
1339 break;
1340
1341 case GDK_KEY_Up :
1342 case GDK_KEY_KP_Up :
1343 /* move the selection up in the combofix only if the popup is showed,
1344 * else let the program works with the upper key */
1345 if (gtk_widget_get_visible (priv->popup))
1346 {
1347 gtk_combofix_move_selection (combofix, COMBOFIX_UP);
1348 gtk_combofix_choose_selection (combofix);
1349 return TRUE;
1350 }
1351 break;
1352
1353 case GDK_KEY_Page_Up :
1354 case GDK_KEY_KP_Page_Up :
1355 /* show the popup if necessary */
1356 if (!gtk_widget_get_visible (priv->popup))
1357 gtk_combofix_show_popup (combofix);
1358
1359 gtk_combofix_move_selection (combofix, COMBOFIX_PAGE_UP);
1360 gtk_combofix_choose_selection (combofix);
1361 return TRUE;
1362 break;
1363
1364 case GDK_KEY_Page_Down :
1365 case GDK_KEY_KP_Page_Down :
1366 /* show the popup if necessary */
1367 if (!gtk_widget_get_visible (priv->popup))
1368 gtk_combofix_show_popup (combofix);
1369
1370 gtk_combofix_move_selection (combofix, COMBOFIX_PAGE_DOWN);
1371 gtk_combofix_choose_selection (combofix);
1372 return TRUE;
1373 break;
1374 }
1375 return FALSE;
1376 }
1377
1378 /**
1379 * Called when a button press event is triggered on the tree view.
1380 * Select an entry if clicked.
1381 *
1382 * \param tree_view GtkTreeView that triggered event. It should be the tree view
1383 attached to a gtk combofix.
1384 * \param ev Triggered event.
1385 * \param combofix The GtkComboFix that contains tree view.
1386 *
1387 * \return TRUE to block the signal, FALSE else
1388 **/
gtk_combofix_button_press_event(GtkWidget * tree_view,GdkEventButton * ev,GtkComboFix * combofix)1389 static gboolean gtk_combofix_button_press_event (GtkWidget *tree_view,
1390 GdkEventButton *ev,
1391 GtkComboFix *combofix)
1392 {
1393 if (ev->type == GDK_BUTTON_PRESS)
1394 {
1395 gtk_combofix_choose_selection (combofix);
1396 gtk_combofix_hide_popup (combofix);
1397 return TRUE;
1398 }
1399
1400 return FALSE;
1401 }
1402
1403 /**
1404 * this function is very important, called when the popup
1405 * gets the release event. without that, when click outside,
1406 * move the mouse on the combofix will select some string with clicking anything
1407 *
1408 * to avoid that we need to propagate the signal release-event to the entry of the combofix
1409 *
1410 * \param popup
1411 * \param ev
1412 * \param combofix
1413 *
1414 * \return the returned value of the release event signal propagated
1415 **/
gtk_combofix_button_release_event(GtkWidget * popup,GdkEventKey * ev,GtkComboFix * combofix)1416 static gboolean gtk_combofix_button_release_event (GtkWidget *popup,
1417 GdkEventKey *ev,
1418 GtkComboFix *combofix)
1419 {
1420 gboolean return_val;
1421 GtkComboFixPrivate *priv;
1422
1423 priv = gtk_combofix_get_instance_private (combofix);
1424
1425 g_signal_emit_by_name (priv->entry,
1426 "button-release-event",
1427 ev,
1428 &return_val);
1429 return return_val;
1430 }
1431
1432 /**
1433 * called to select the text in the tree_view
1434 *
1435 * \param combofix
1436 * \param item name of the item
1437 *
1438 * \return FALSE
1439 **/
gtk_combofix_select_item(GtkComboFix * combofix,const gchar * item)1440 static gboolean gtk_combofix_select_item (GtkComboFix *combofix,
1441 const gchar *item)
1442 {
1443 GtkTreeModel *model;
1444 GtkTreeIter iter;
1445 GtkTreePath *path;
1446 gchar *ptr;
1447 gchar *tmp_item = NULL;
1448 gint result = 0;
1449 GtkComboFixPrivate *priv;
1450
1451 if (!combofix)
1452 return FALSE;
1453 if (!item || strlen (item) == 0)
1454 return FALSE;
1455
1456 priv = gtk_combofix_get_instance_private (combofix);
1457 if ((ptr = g_utf8_strchr (item, -1, ':')))
1458 tmp_item = g_strndup (item, (ptr - item) -1);
1459 else
1460 tmp_item = g_strdup (item);
1461
1462 model = GTK_TREE_MODEL (priv->model_sort);
1463 result = gtk_tree_model_get_iter_first (model, &iter);
1464
1465 while (result)
1466 {
1467 gchar *tmp_str;
1468
1469 gtk_tree_model_get (model, &iter, COMBOFIX_COL_REAL_STRING, &tmp_str, -1);
1470
1471 if (tmp_str && tmp_item)
1472 {
1473 gchar *tmp_str_casefold, *tmp_item_casefold;
1474 int collate;
1475
1476 tmp_str_casefold = g_utf8_casefold (tmp_str, -1);
1477 tmp_item_casefold = g_utf8_casefold (tmp_item, -1);
1478 collate = g_utf8_collate (tmp_str_casefold , tmp_item_casefold);
1479 g_free (tmp_item_casefold);
1480 g_free (tmp_str_casefold);
1481
1482 if (collate == 0)
1483 break;
1484 }
1485
1486 result = gtk_tree_model_iter_next (model, &iter);
1487 }
1488
1489 g_free (tmp_item);
1490
1491 if (result == 0)
1492 gtk_tree_model_get_iter_first (model, &iter);
1493
1494 gtk_tree_selection_select_iter (priv->selection, &iter);
1495 path = gtk_tree_model_get_path (model, &iter);
1496 if (path)
1497 {
1498 gtk_tree_view_scroll_to_cell (GTK_TREE_VIEW (priv->tree_view), path, NULL, FALSE, 0, 0);
1499 gtk_tree_path_free (path);
1500 }
1501
1502 return FALSE;
1503 }
1504
1505 /**
1506 * the default function to sort the combofix,
1507 * if mixed is set, all the list will be sorted by alphabetic order,
1508 * else, for a complex combofix, each list will be sorted by itself
1509 *
1510 * \param model_sort
1511 * \param iter_1
1512 * \param iter_2
1513 * \param combofix
1514 *
1515 * \return -1 if iter_1 before iter_2 ...
1516 **/
gtk_combofix_default_sort_func(GtkTreeModel * model_sort,GtkTreeIter * iter_1,GtkTreeIter * iter_2,GtkComboFix * combofix)1517 static gint gtk_combofix_default_sort_func (GtkTreeModel *model_sort,
1518 GtkTreeIter *iter_1,
1519 GtkTreeIter *iter_2,
1520 GtkComboFix *combofix)
1521 {
1522 gint list_number_1;
1523 gint list_number_2;
1524 gchar *string_1;
1525 gchar *string_2;
1526 gchar *cmp_string_1;
1527 gchar *cmp_string_2;
1528 gint return_value = 0;
1529 gboolean separator_1;
1530 gboolean separator_2;
1531 GtkComboFixPrivate *priv;
1532
1533 priv = gtk_combofix_get_instance_private (combofix);
1534 if (iter_1)
1535 gtk_tree_model_get (GTK_TREE_MODEL (model_sort),
1536 iter_1,
1537 COMBOFIX_COL_LIST_NUMBER, &list_number_1,
1538 COMBOFIX_COL_VISIBLE_STRING, &string_1,
1539 COMBOFIX_COL_SEPARATOR, &separator_1,
1540 -1);
1541 else
1542 return -1;
1543
1544 if (iter_2)
1545 gtk_tree_model_get (GTK_TREE_MODEL (model_sort),
1546 iter_2,
1547 COMBOFIX_COL_LIST_NUMBER, &list_number_2,
1548 COMBOFIX_COL_VISIBLE_STRING, &string_2,
1549 COMBOFIX_COL_SEPARATOR, &separator_2,
1550 -1);
1551 else
1552 return 1;
1553
1554 if (priv->mixed_sort == FALSE)
1555 return_value = list_number_1 - list_number_2;
1556
1557 if (return_value == 0)
1558 {
1559 if (separator_1)
1560 return 1;
1561 if (separator_2)
1562 return -1;
1563
1564 if (string_1 == NULL)
1565 return -1;
1566 if (string_2 == NULL)
1567 return 1;
1568
1569 cmp_string_1 = g_utf8_collate_key (string_1, -1);
1570 cmp_string_2 = g_utf8_collate_key (string_2, -1);
1571 return_value = strcmp (cmp_string_1, cmp_string_2);
1572
1573 g_free (cmp_string_1);
1574 g_free (cmp_string_2);
1575 }
1576 g_free (string_1);
1577 g_free (string_2);
1578
1579 return return_value;
1580 }
1581
1582 /**
1583 * check if the given row is or not a separator,
1584 * used in interne in gtk
1585 *
1586 * \param model
1587 * \param iter
1588 * \param combofix
1589 *
1590 * \return TRUE if it's a separator, FALSE else
1591 **/
gtk_combofix_separator_func(GtkTreeModel * model,GtkTreeIter * iter,GtkComboFix * combofix)1592 static gboolean gtk_combofix_separator_func (GtkTreeModel *model,
1593 GtkTreeIter *iter,
1594 GtkComboFix *combofix)
1595 {
1596 gboolean value;
1597
1598 gtk_tree_model_get (GTK_TREE_MODEL (model), iter, COMBOFIX_COL_SEPARATOR, &value, -1);
1599
1600 if (value)
1601 return TRUE;
1602 return FALSE;
1603 }
1604
1605 /**
1606 *
1607 *
1608 * \param
1609 *
1610 * \return
1611 **/
gtk_combofix_create_button(GtkComboFix * combofix)1612 static void gtk_combofix_create_button (GtkComboFix *combofix)
1613 {
1614 GtkComboFixPrivate *priv;
1615
1616 priv = gtk_combofix_get_instance_private (combofix);
1617
1618 priv->button = utils_buttons_button_new_from_image ("gsb-arrow-down-16.png");
1619 gtk_button_set_relief (GTK_BUTTON (priv->button), GTK_RELIEF_NONE);
1620 g_signal_connect_swapped (G_OBJECT (priv->button),
1621 "clicked",
1622 G_CALLBACK (gtk_combofix_show_popup),
1623 combofix);
1624
1625 gtk_widget_show_all (priv->button);
1626 }
1627
1628 /**
1629 *
1630 *
1631 * \param
1632 *
1633 * \return
1634 **/
gtk_combofix_create_entry(GtkComboFix * combofix)1635 static void gtk_combofix_create_entry (GtkComboFix *combofix)
1636 {
1637 GtkEntryCompletion *completion;
1638 GtkListStore *completion_store;
1639 GrisbiAppConf *a_conf;
1640 GtkComboFixPrivate *priv;
1641
1642 priv = gtk_combofix_get_instance_private (combofix);
1643 a_conf = (GrisbiAppConf *) grisbi_app_get_a_conf ();
1644
1645 /* create entry */
1646 priv->entry = gtk_entry_new ();
1647
1648 /* set completion */
1649 completion = gtk_entry_completion_new ();
1650 gtk_entry_completion_set_inline_selection (completion, TRUE);
1651 if (etat.combofix_case_sensitive)
1652 gtk_entry_completion_set_match_func (completion,
1653 (GtkEntryCompletionMatchFunc) gtk_combofix_completion_match_func,
1654 NULL,
1655 NULL);
1656 gtk_entry_completion_set_minimum_key_length (completion, a_conf->completion_minimum_key_length);
1657 gtk_entry_completion_set_popup_single_match (completion, TRUE);
1658 gtk_entry_completion_set_text_column (completion, 0);
1659
1660 /* set store */
1661 completion_store = gtk_list_store_new (1, G_TYPE_STRING);
1662 gtk_entry_completion_set_model (completion, GTK_TREE_MODEL (completion_store));
1663 g_object_unref (completion_store);
1664
1665 /* set entry completion */
1666 gtk_entry_set_completion (GTK_ENTRY (priv->entry), completion);
1667 g_object_unref (completion);
1668
1669 /* set entry signals */
1670 g_signal_connect (G_OBJECT (priv->entry),
1671 "key-press-event",
1672 G_CALLBACK (gtk_combofix_key_press_event),
1673 combofix);
1674 g_signal_connect_swapped (G_OBJECT (priv->entry),
1675 "draw",
1676 G_CALLBACK (gtk_combofix_expose_entry),
1677 combofix);
1678 g_signal_connect_after (G_OBJECT (priv->entry),
1679 "focus-in-event",
1680 G_CALLBACK (gtk_combofix_focus_in),
1681 combofix);
1682 g_signal_connect_after (G_OBJECT (priv->entry),
1683 "focus-out-event",
1684 G_CALLBACK (gtk_combofix_focus_out),
1685 combofix);
1686
1687 /* set completion signal */
1688 g_signal_connect (G_OBJECT (completion),
1689 "match-selected",
1690 G_CALLBACK (gtk_combofix_completion_match_selected),
1691 combofix);
1692
1693 gtk_widget_set_hexpand (priv->entry, TRUE);
1694 gtk_widget_show (priv->entry);
1695 }
1696
1697 /**
1698 *
1699 *
1700 * \param
1701 *
1702 * \return
1703 **/
gtk_combofix_create_popup(GtkComboFix * combofix)1704 static void gtk_combofix_create_popup (GtkComboFix *combofix)
1705 {
1706 GtkWidget *frame;
1707 GtkCellRenderer *cell_renderer;
1708 GtkTreeViewColumn *tree_view_column;
1709 GtkWidget *scrolled_window;
1710 GtkComboFixPrivate *priv;
1711
1712 priv = gtk_combofix_get_instance_private (combofix);
1713
1714 priv->popup = gtk_window_new (GTK_WINDOW_POPUP);
1715 g_object_ref (G_OBJECT (priv->popup));
1716 g_signal_connect (G_OBJECT (priv->popup),
1717 "key-press-event",
1718 G_CALLBACK (gtk_combofix_popup_key_press_event),
1719 combofix);
1720
1721 g_signal_connect (G_OBJECT (priv->popup),
1722 "button-press-event",
1723 G_CALLBACK (gtk_combofix_button_press),
1724 combofix);
1725 g_signal_connect (G_OBJECT (priv->popup),
1726 "button-release-event",
1727 G_CALLBACK (gtk_combofix_button_release_event),
1728 combofix);
1729
1730 frame = gtk_frame_new (NULL);
1731 gtk_container_add (GTK_CONTAINER (priv->popup), frame);
1732 gtk_widget_show (frame);
1733
1734 scrolled_window = gtk_scrolled_window_new (FALSE, FALSE);
1735 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW(scrolled_window),
1736 GTK_POLICY_AUTOMATIC,
1737 GTK_POLICY_AUTOMATIC);
1738 gtk_container_add (GTK_CONTAINER (frame), scrolled_window);
1739 gtk_widget_show (scrolled_window);
1740
1741 /* Create the tree_store */
1742 priv->store = gtk_tree_store_new (COMBOFIX_N_COLUMNS,
1743 G_TYPE_STRING,
1744 G_TYPE_STRING,
1745 G_TYPE_BOOLEAN,
1746 G_TYPE_INT,
1747 G_TYPE_BOOLEAN);
1748
1749 /* we set the store in a filter to show only what is selected */
1750 priv->model_filter = gtk_tree_model_filter_new (GTK_TREE_MODEL (priv->store), NULL);
1751 gtk_tree_model_filter_set_visible_column (GTK_TREE_MODEL_FILTER (priv->model_filter),
1752 COMBOFIX_COL_VISIBLE);
1753
1754 /* we set the filter in a sorting model */
1755 priv->model_sort = gtk_tree_model_sort_new_with_model (GTK_TREE_MODEL (priv->model_filter));
1756 gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE (priv->model_sort),
1757 COMBOFIX_COL_VISIBLE_STRING,
1758 GTK_SORT_ASCENDING);
1759 gtk_tree_sortable_set_sort_func (GTK_TREE_SORTABLE (priv->model_sort),
1760 COMBOFIX_COL_VISIBLE_STRING,
1761 (GtkTreeIterCompareFunc) gtk_combofix_default_sort_func,
1762 combofix,
1763 NULL);
1764
1765 /* make the column */
1766 cell_renderer = gtk_cell_renderer_text_new ();
1767 tree_view_column = gtk_tree_view_column_new_with_attributes ("",
1768 cell_renderer,
1769 "text", COMBOFIX_COL_VISIBLE_STRING,
1770 NULL);
1771 gtk_tree_view_column_set_sizing (tree_view_column, GTK_TREE_VIEW_COLUMN_FIXED);
1772
1773 /* set the sorting model in the tree view */
1774 priv->tree_view = gtk_tree_view_new_with_model (GTK_TREE_MODEL (priv->model_sort));
1775
1776 priv->selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (priv->tree_view));
1777 gtk_tree_selection_set_mode (GTK_TREE_SELECTION (priv->selection), GTK_SELECTION_SINGLE);
1778 gtk_tree_view_set_hover_selection (GTK_TREE_VIEW (priv->tree_view), TRUE);
1779 gtk_tree_view_set_headers_visible (GTK_TREE_VIEW (priv->tree_view), FALSE);
1780 gtk_tree_view_append_column (GTK_TREE_VIEW (priv->tree_view), tree_view_column);
1781 gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (priv->tree_view), TRUE);
1782 gtk_tree_view_set_row_separator_func (GTK_TREE_VIEW (priv->tree_view),
1783 (GtkTreeViewRowSeparatorFunc) gtk_combofix_separator_func,
1784 combofix,
1785 NULL);
1786
1787 g_signal_connect (G_OBJECT (priv->tree_view),
1788 "button-press-event",
1789 G_CALLBACK (gtk_combofix_button_press_event),
1790 combofix);
1791 gtk_container_add (GTK_CONTAINER (scrolled_window), priv->tree_view);
1792
1793 gtk_widget_show (priv->tree_view);
1794 }
1795
1796 /******************************************************************************/
1797 /* Fonctions propres à l'initialisation de l'objet */
1798 /******************************************************************************/
1799 /**
1800 * called when create a new combofix
1801 *
1802 * \param combofix
1803 *
1804 * \return
1805 **/
gtk_combofix_init(GtkComboFix * combofix)1806 static void gtk_combofix_init (GtkComboFix *combofix)
1807 {
1808 GtkWidget *hbox;
1809 GtkWidget *vbox;
1810 GtkComboFixPrivate *priv;
1811
1812 priv = gtk_combofix_get_instance_private (combofix);
1813
1814 /* set the fields of the combofix */
1815 priv->force = FALSE;
1816 priv->case_sensitive = FALSE;
1817 priv->visible_items = 0;
1818 priv->ignore_accents = TRUE; /* reproduit le fonctionnement de la completion de gtk */
1819 priv->minimum_key_length = 1; /* la recherche commence au premier caractère */
1820
1821 /* the combofix is a vbox */
1822 vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
1823 gtk_container_add (GTK_CONTAINER (combofix), vbox);
1824 gtk_widget_show (vbox);
1825
1826 /* a hbox which contains the entry and the button */
1827 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
1828 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, FALSE, 0);
1829 gtk_widget_show (hbox);
1830
1831 /* set the entry with completion */
1832 gtk_combofix_create_entry (combofix);
1833 gtk_box_pack_start (GTK_BOX (hbox), priv->entry, TRUE, TRUE, 0);
1834
1835 /* set the button */
1836 gtk_combofix_create_button (combofix);
1837 gtk_box_pack_start (GTK_BOX (hbox), priv->button, FALSE, FALSE, 0);
1838
1839 /* set the popup but don't show it */
1840 gtk_combofix_create_popup (combofix);
1841 }
1842
1843 /**
1844 * called when destroy a combofix
1845 *
1846 * \param combofix
1847 *
1848 * \return
1849 **/
gtk_combofix_dispose(GObject * combofix)1850 static void gtk_combofix_dispose (GObject *combofix)
1851 {
1852 G_OBJECT_CLASS (gtk_combofix_parent_class)->dispose (combofix);
1853 }
1854
1855 /**
1856 * called when destroy combofix
1857 *
1858 * \param combofix
1859 *
1860 * \return
1861 **/
gtk_combofix_finalize(GObject * combofix)1862 static void gtk_combofix_finalize (GObject *combofix)
1863 {
1864 GtkComboFixPrivate *priv;
1865
1866 priv = gtk_combofix_get_instance_private (GTK_COMBOFIX (combofix));
1867
1868 if (priv->old_entry && strlen (priv->old_entry))
1869 g_free (priv->old_entry);
1870
1871 /* Unref/free the model first, to workaround gtk/gail bug #694711 */
1872 gtk_tree_view_set_model (GTK_TREE_VIEW (priv->tree_view), NULL);
1873 g_object_unref (priv->model_sort);
1874 g_object_unref (priv->model_filter);
1875 g_object_unref (priv->store);
1876
1877 gtk_widget_destroy (priv->popup);
1878 g_object_unref (priv->popup);
1879
1880 G_OBJECT_CLASS (gtk_combofix_parent_class)->finalize (combofix);
1881 }
1882
1883 /**
1884 * called when create a new combofix
1885 *
1886 * \param combofix
1887 *
1888 * \return
1889 **/
gtk_combofix_class_init(GtkComboFixClass * klass)1890 static void gtk_combofix_class_init (GtkComboFixClass *klass)
1891 {
1892 GObjectClass *object_class;
1893
1894 object_class = G_OBJECT_CLASS (klass);
1895 object_class->dispose = gtk_combofix_dispose;
1896 object_class->finalize = gtk_combofix_finalize;
1897
1898 #if GTK_CHECK_VERSION (3,20,0)
1899 gtk_widget_class_set_css_name (GTK_WIDGET_CLASS (klass), "combofix");
1900 #endif
1901 }
1902
1903 /******************************************************************************/
1904 /* Public functions */
1905 /******************************************************************************/
1906 /**
1907 * create a combofix, with several list set one after the others
1908 * by default, force is not set, no max items
1909 * and case unsensitive
1910 *
1911 * \param list a g_slist of name (\t at the beginning makes it as a child)
1912 * \param type 0 : payee, 1 : category, 2 : budget
1913 *
1914 * \return the new widget
1915 **/
gtk_combofix_new(GSList * list,gint type)1916 GtkWidget *gtk_combofix_new (GSList *list,
1917 gint type)
1918 {
1919 GtkComboFix *combofix;
1920 GtkComboFixPrivate *priv;
1921
1922 combofix = g_object_new (GTK_TYPE_COMBOFIX, NULL);
1923 priv = gtk_combofix_get_instance_private (combofix);
1924 priv->type = type;
1925
1926 gtk_combofix_set_list (combofix, list);
1927
1928 gtk_widget_set_size_request (GTK_WIDGET (combofix), COMBOFIX_MIN_WIDTH, -1);
1929
1930 return (GTK_WIDGET (combofix));
1931 }
1932
1933 /**
1934 * create a new gtk_conbofix with properties
1935 *
1936 * \param list a g_slist of name (\t at the beginning makes it as a child)
1937 * \param force TRUE and the text must be in the list
1938 * \param sort TRUE and the list will be sorted automatickly
1939 * \param max_items the minimum of characters to show the popup
1940 * \param type 0 : payee, 1 : category, 2 : budget
1941 *
1942 * \return a gtkcombofix
1943 **/
gtk_combofix_new_with_properties(GSList * list,gboolean force_text,gboolean case_sensitive,gboolean mixed_sort,gint type)1944 GtkWidget *gtk_combofix_new_with_properties (GSList *list,
1945 gboolean force_text,
1946 gboolean case_sensitive,
1947 gboolean mixed_sort,
1948 gint type)
1949 {
1950 GtkComboFix *combofix;
1951 GtkComboFixPrivate *priv;
1952
1953 combofix = g_object_new (GTK_TYPE_COMBOFIX, NULL);
1954 priv = gtk_combofix_get_instance_private (combofix);
1955
1956 priv->force = force_text;
1957 priv->case_sensitive = case_sensitive;
1958 priv->mixed_sort = mixed_sort;
1959 priv->type = type;
1960
1961 gtk_combofix_set_list (combofix, list);
1962 gtk_widget_set_size_request (GTK_WIDGET (combofix), COMBOFIX_MIN_WIDTH, -1);
1963
1964 return (GTK_WIDGET (combofix));
1965 }
1966
1967 /**
1968 * get the entry in the combofix
1969 *
1970 * \param combofix
1971 *
1972 * \return a widget
1973 **/
gtk_combofix_get_entry(GtkComboFix * combofix)1974 GtkWidget *gtk_combofix_get_entry (GtkComboFix *combofix)
1975 {
1976 GtkComboFixPrivate *priv;
1977
1978 priv = gtk_combofix_get_instance_private (combofix);
1979
1980 g_return_val_if_fail (combofix, NULL);
1981 g_return_val_if_fail (GTK_IS_COMBOFIX (combofix), NULL);
1982
1983 return priv->entry;
1984 }
1985
1986 /**
1987 * get the text in the combofix
1988 *
1989 * \param combofix
1990 *
1991 * \return a const gchar
1992 **/
gtk_combofix_get_text(GtkComboFix * combofix)1993 const gchar *gtk_combofix_get_text (GtkComboFix *combofix)
1994 {
1995 GtkComboFixPrivate *priv;
1996
1997 priv = gtk_combofix_get_instance_private (combofix);
1998
1999 g_return_val_if_fail (combofix , NULL);
2000 g_return_val_if_fail (GTK_IS_COMBOFIX (combofix), NULL);
2001
2002 return (gtk_entry_get_text (GTK_ENTRY (priv->entry)));
2003 }
2004
2005 /**
2006 * set the text in the combofix without showing the popup or
2007 * doing any check
2008 *
2009 * \param combofix
2010 * \param text
2011 *
2012 * \return
2013 **/
gtk_combofix_set_text(GtkComboFix * combofix,const gchar * text)2014 void gtk_combofix_set_text (GtkComboFix *combofix,
2015 const gchar *text)
2016 {
2017 GtkComboFixPrivate *priv;
2018
2019 priv = gtk_combofix_get_instance_private (combofix);
2020
2021 /* bloque l'appel à gtk_combofix_expose_entry () pendant cette fonction */
2022 block_expose_event = 1;
2023
2024 g_return_if_fail (combofix);
2025 g_return_if_fail (GTK_IS_COMBOFIX (combofix));
2026
2027 if (text && strlen (text) > 0)
2028 gtk_entry_set_text (GTK_ENTRY (priv->entry), text);
2029 else
2030 gtk_entry_set_text (GTK_ENTRY (priv->entry), "");
2031
2032 }
2033
2034 /**
2035 * set the properties of combofix
2036 *
2037 * \param combofix
2038 * \param type of the combofix : 0 : payee, 1 : category, 2 : budget
2039 *
2040 * \return
2041 **/
gtk_combofix_set_properties(GtkWidget * combofix)2042 void gtk_combofix_set_properties (GtkWidget *combofix)
2043 {
2044 gint old_case_sensitive = 0;
2045 GtkComboFixPrivate *priv;
2046
2047 g_return_if_fail (combofix);
2048 g_return_if_fail (GTK_IS_COMBOFIX (combofix));
2049
2050 priv = gtk_combofix_get_instance_private (GTK_COMBOFIX (combofix));
2051 old_case_sensitive = priv->case_sensitive;
2052
2053 if (priv->type)
2054 {
2055 priv->force = etat.combofix_force_category;
2056 priv->mixed_sort = etat.combofix_mixed_sort;
2057 }
2058 else
2059 {
2060 priv->force = etat.combofix_force_payee;
2061 priv->mixed_sort = FALSE;
2062 }
2063 priv->case_sensitive = etat.combofix_case_sensitive;
2064 if (old_case_sensitive - etat.combofix_case_sensitive)
2065 {
2066 GtkEntryCompletion *completion;
2067
2068 completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
2069 if (old_case_sensitive)
2070 {
2071 gtk_entry_completion_set_match_func (completion, NULL, NULL, NULL);
2072 }
2073 else
2074 {
2075 gtk_entry_completion_set_match_func (completion,
2076 (GtkEntryCompletionMatchFunc) gtk_combofix_completion_match_func,
2077 NULL,
2078 NULL);
2079 }
2080 }
2081 }
2082
2083 /**
2084 * set the flag to force/unforce the text in the entry
2085 * if force is set, the value in the entry must belong to the list
2086 *
2087 * \param combofix
2088 * \param value
2089 *
2090 * \return
2091 **/
gtk_combofix_set_force_text(GtkComboFix * combofix,gboolean value)2092 void gtk_combofix_set_force_text (GtkComboFix *combofix,
2093 gboolean value)
2094 {
2095 GtkComboFixPrivate *priv;
2096
2097 g_return_if_fail (combofix);
2098 g_return_if_fail (GTK_IS_COMBOFIX (combofix));
2099
2100 priv = gtk_combofix_get_instance_private (combofix);
2101 priv->force = value;
2102 }
2103
2104 /**
2105 * set if the completion is case sensitive or not
2106 *
2107 * \param combofix
2108 * \param case_sensitive TRUE or FALSE
2109 *
2110 * \return
2111 **/
gtk_combofix_set_case_sensitive(GtkComboFix * combofix,gboolean case_sensitive)2112 void gtk_combofix_set_case_sensitive (GtkComboFix *combofix,
2113 gboolean case_sensitive)
2114 {
2115 gint old_case_sensitive = 0;
2116 GtkComboFixPrivate *priv;
2117
2118 g_return_if_fail (combofix);
2119 g_return_if_fail (GTK_IS_COMBOFIX (combofix));
2120
2121 priv = gtk_combofix_get_instance_private (combofix);
2122 old_case_sensitive = priv->case_sensitive;
2123 priv->case_sensitive = case_sensitive;
2124 if (old_case_sensitive - case_sensitive)
2125 {
2126 GtkEntryCompletion *completion;
2127
2128 completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
2129 if (old_case_sensitive)
2130 {
2131 gtk_entry_completion_set_match_func (completion, NULL, NULL, NULL);
2132 }
2133 else
2134 {
2135 gtk_entry_completion_set_match_func (completion,
2136 (GtkEntryCompletionMatchFunc) gtk_combofix_completion_match_func,
2137 NULL,
2138 NULL);
2139 }
2140 }
2141 }
2142
2143 /**
2144 * hide the popup
2145 *
2146 * \param combofix
2147 *
2148 * \return FALSE
2149 **/
gtk_combofix_hide_popup(GtkComboFix * combofix)2150 gboolean gtk_combofix_hide_popup (GtkComboFix *combofix)
2151 {
2152 GtkComboFixPrivate *priv;
2153
2154 g_return_val_if_fail (combofix != NULL, FALSE);
2155 g_return_val_if_fail (GTK_IS_COMBOFIX (combofix), FALSE);
2156
2157 priv = gtk_combofix_get_instance_private (combofix);
2158 if (gtk_widget_get_visible (priv->popup))
2159 {
2160 gtk_grab_remove (priv->popup);
2161 gtk_widget_hide (priv->popup);
2162 }
2163 return FALSE;
2164 }
2165
2166 /**
2167 * show the popup with all the content, not according to the entry
2168 *
2169 * \param combofix
2170 *
2171 * return FALSE
2172 **/
gtk_combofix_show_popup(GtkComboFix * combofix)2173 gboolean gtk_combofix_show_popup (GtkComboFix *combofix)
2174 {
2175 GtkComboFixPrivate *priv;
2176
2177 priv = gtk_combofix_get_instance_private (combofix);
2178 if (gtk_widget_get_visible (priv->popup))
2179 return FALSE;
2180
2181 gtk_combofix_set_all_visible_rows (combofix);
2182 gtk_combofix_set_popup_position (combofix);
2183 gtk_widget_show (priv->popup);
2184 gtk_combofix_select_item (combofix, gtk_combofix_get_text (combofix));
2185 gtk_widget_grab_focus (GTK_WIDGET (priv->entry));
2186 gtk_window_set_modal (GTK_WINDOW (priv->popup), TRUE);
2187 return FALSE;
2188 }
2189
2190 /**
2191 * set for the complex combofix if the different list have to
2192 * be mixed or separate
2193 *
2194 * \param combofix
2195 * \param mixed_sort TRUE or FALSE
2196 *
2197 * \return
2198 **/
gtk_combofix_set_mixed_sort(GtkComboFix * combofix,gboolean mixed_sort)2199 void gtk_combofix_set_mixed_sort (GtkComboFix *combofix,
2200 gboolean mixed_sort)
2201 {
2202 GtkComboFixPrivate *priv;
2203
2204 g_return_if_fail (combofix);
2205 g_return_if_fail (GTK_IS_COMBOFIX (combofix));
2206
2207 priv = gtk_combofix_get_instance_private (combofix);
2208 priv->mixed_sort = mixed_sort;
2209 }
2210
2211 /**
2212 * change the list of an existing combofix
2213 *
2214 * \param combofix
2215 * \param list the new list
2216 *
2217 * \return TRUE if ok, FALSE if problem
2218 **/
gtk_combofix_set_list(GtkComboFix * combofix,GSList * list)2219 gboolean gtk_combofix_set_list (GtkComboFix *combofix,
2220 GSList *list)
2221 {
2222 GtkComboFixPrivate *priv;
2223
2224 GSList *tmp_list;
2225 gint list_number = 0;
2226 gint length;
2227 GtkEntryCompletion *completion;
2228 GtkTreeModel *completion_store;
2229 GtkTreeIter iter;
2230
2231 g_return_val_if_fail (combofix, FALSE);
2232 g_return_val_if_fail (GTK_IS_COMBOFIX (combofix), FALSE);
2233 g_return_val_if_fail (list, FALSE);
2234
2235 priv = gtk_combofix_get_instance_private (combofix);
2236 if (!priv->store || !GTK_IS_TREE_STORE (priv->store))
2237 return FALSE;
2238 else
2239 gtk_tree_store_clear (priv->store);
2240
2241 completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
2242 completion_store = gtk_entry_completion_get_model (completion);
2243 if (GTK_LIST_STORE (completion_store))
2244 gtk_list_store_clear (GTK_LIST_STORE (completion_store));
2245
2246 tmp_list = list;
2247 length = g_slist_length (list);
2248
2249 while (tmp_list)
2250 {
2251 gtk_combofix_fill_store (combofix, tmp_list->data, list_number);
2252
2253 /* set the separator */
2254 if (list_number < (length-1))
2255 {
2256 gtk_tree_store_append (priv->store, &iter, NULL);
2257 gtk_tree_store_set (priv->store,
2258 &iter,
2259 COMBOFIX_COL_LIST_NUMBER, list_number,
2260 COMBOFIX_COL_SEPARATOR, TRUE,
2261 -1);
2262 }
2263 list_number++;
2264
2265 tmp_list = tmp_list->next;
2266 }
2267
2268 return TRUE;
2269 }
2270
2271 /**
2272 * append a new line in a combofix
2273 *
2274 * \param combofix text
2275 *
2276 * \return
2277 **/
gtk_combofix_append_text(GtkComboFix * combofix,const gchar * text)2278 void gtk_combofix_append_text (GtkComboFix *combofix,
2279 const gchar *text)
2280 {
2281 GtkTreeIter iter_parent;
2282 gint empty;
2283 gpointer pointeurs[3] = { (gpointer) text, NULL, NULL };
2284 GtkComboFixPrivate *priv;
2285
2286 if (!combofix || !GTK_IS_COMBOFIX (combofix))
2287 return;
2288
2289 priv = gtk_combofix_get_instance_private (combofix);
2290
2291 /* On sort pour les catégories/IB car la mise à jour est globale */
2292 if (priv-> type)
2293 return;
2294
2295 pointeurs[2] = GINT_TO_POINTER (priv->case_sensitive);
2296
2297 empty = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (priv->entry), "empty"));
2298 if (empty || priv->force)
2299 return;
2300
2301 if (priv->old_entry && strcmp (text, priv->old_entry) == 0)
2302 return;
2303
2304 gtk_tree_model_foreach (GTK_TREE_MODEL (priv->store),
2305 (GtkTreeModelForeachFunc) gtk_combofix_search_for_text,
2306 pointeurs);
2307
2308 if (pointeurs[1] && GINT_TO_POINTER (pointeurs[1]))
2309 return;
2310
2311 gtk_combofix_fill_iter_parent (priv->store, &iter_parent, text, 0);
2312
2313 if (priv->old_entry && strlen (priv->old_entry))
2314 g_free (priv->old_entry);
2315 priv->old_entry = g_strdup (text);
2316
2317 /* update completion */
2318 gtk_combofix_completion_insert_new_item (combofix, text);
2319 }
2320
2321 /**
2322 * append a report as payee in a combofix
2323 *
2324 * \param combofix
2325 * \param report_name the name of report
2326 *
2327 * \return
2328 **/
gtk_combofix_append_report(GtkComboFix * combofix,const gchar * report_name)2329 void gtk_combofix_append_report (GtkComboFix *combofix,
2330 const gchar *report_name)
2331 {
2332 GtkEntryCompletion *completion;
2333 GtkTreeModel *completion_model;
2334 GtkTreeIter new_iter;
2335 gchar *tmp_str;
2336 gchar *tmp_str2;
2337 GtkComboFixPrivate *priv;
2338
2339 g_return_if_fail (combofix);
2340 g_return_if_fail (GTK_IS_COMBOFIX (combofix));
2341
2342 if (!report_name || strlen (report_name) == 0)
2343 return;
2344
2345 priv = gtk_combofix_get_instance_private (combofix);
2346
2347 /* initialisation iter à invalid probablement inutile */
2348 report_parent_iter.stamp = 0;
2349 /* on cherche la partie etats on l'ajoute si nécessaire */
2350 if (gtk_combofix_search_for_report (GTK_TREE_MODEL (priv->store)))
2351 priv->visible_items++;
2352
2353 if (!report_parent_iter.stamp)
2354 return;
2355
2356 /* on sort si l'état demandé existe déjà */
2357 if (gtk_combofix_search_report (GTK_TREE_MODEL (priv->store), report_name))
2358 return;
2359
2360 /* sinon on l'ajoute dans la liste des tiers */
2361 tmp_str = g_strdup (_("Report"));
2362 tmp_str2 = g_strconcat (tmp_str, " : ", report_name, NULL);
2363 gtk_combofix_fill_iter_child (priv->store, &report_parent_iter, report_name, tmp_str2, 1);
2364 priv->visible_items++;
2365 g_free (tmp_str);
2366
2367 /* update completion */
2368 completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
2369 completion_model = gtk_entry_completion_get_model (completion);
2370 gtk_list_store_append (GTK_LIST_STORE (completion_model), &new_iter);
2371 gtk_list_store_set (GTK_LIST_STORE (completion_model), &new_iter, 0, tmp_str2, -1);
2372 g_free (tmp_str2);
2373 }
2374
2375 /**
2376 * remove a line in a combofix
2377 *
2378 * \param combofix text
2379 *
2380 * \return
2381 **/
gtk_combofix_remove_text(GtkComboFix * combofix,const gchar * text)2382 void gtk_combofix_remove_text (GtkComboFix *combofix,
2383 const gchar *text)
2384 {
2385 GtkEntryCompletion *completion;
2386 GtkTreeModel *completion_model;
2387 GtkTreeIter iter;
2388 gboolean case_sensitive;
2389 gboolean valid;
2390 GtkComboFixPrivate *priv;
2391
2392 priv = gtk_combofix_get_instance_private (combofix);
2393
2394 case_sensitive = priv->case_sensitive;
2395 valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store), &iter);
2396
2397 while (valid)
2398 {
2399 gchar *tmp_str;
2400 gboolean separator;
2401
2402 gtk_tree_model_get (GTK_TREE_MODEL (priv->store),
2403 &iter,
2404 COMBOFIX_COL_REAL_STRING, &tmp_str,
2405 COMBOFIX_COL_SEPARATOR, &separator,
2406 -1);
2407
2408 if (separator)
2409 {
2410 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), &iter);
2411 continue;
2412 }
2413 if (case_sensitive && !strcmp (text, tmp_str))
2414 {
2415 g_free (tmp_str);
2416 break;
2417 }
2418 else if (!g_utf8_collate (g_utf8_casefold (text, -1), g_utf8_casefold (tmp_str, -1)))
2419 {
2420 g_free (tmp_str);
2421 break;
2422 }
2423
2424 g_free (tmp_str);
2425
2426 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), &iter);
2427 }
2428
2429 if (valid)
2430 gtk_tree_store_remove (priv->store, &iter);
2431
2432 /* update completion */
2433 completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
2434 completion_model = gtk_entry_completion_get_model (completion);
2435 valid = gtk_tree_model_get_iter_first (completion_model, &iter);
2436
2437 while (valid)
2438 {
2439 gchar *tmp_str;
2440
2441 gtk_tree_model_get (completion_model, &iter, 0, &tmp_str, -1);
2442
2443 if (case_sensitive && !strcmp (text, tmp_str))
2444 {
2445 g_free (tmp_str);
2446 break;
2447 }
2448 else if (!g_utf8_collate (g_utf8_casefold (text, -1), g_utf8_casefold (tmp_str, -1)))
2449 {
2450 g_free (tmp_str);
2451 break;
2452 }
2453
2454 g_free (tmp_str);
2455
2456 valid = gtk_tree_model_iter_next (completion_model, &iter);
2457 }
2458
2459 if (valid)
2460 gtk_list_store_remove (GTK_LIST_STORE (completion_model), &iter);
2461 }
2462
2463
2464 /**
2465 * remove a report in a payee combofix
2466 *
2467 * \param combofix
2468 * \param report_number
2469 *
2470 * \return
2471 **/
gtk_combofix_remove_report(GtkComboFix * combofix,const gchar * report_name)2472 void gtk_combofix_remove_report (GtkComboFix *combofix,
2473 const gchar *report_name)
2474 {
2475 GtkEntryCompletion *completion;
2476 GtkTreeModel *completion_model;
2477 GtkTreeIter iter;
2478 gchar *tmp_str;
2479 gchar *tmp_str2;
2480 gboolean valid;
2481 GtkComboFixPrivate *priv;
2482
2483 /* on récupère le nom de l'état */
2484 tmp_str = g_strdup (_("Report"));
2485 tmp_str2 = g_strconcat (tmp_str, " : ", report_name, NULL);
2486 g_free (tmp_str);
2487
2488 priv = gtk_combofix_get_instance_private (combofix);
2489 valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (priv->store), &iter);
2490
2491 while (valid)
2492 {
2493 gboolean separator;
2494
2495 gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &iter, COMBOFIX_COL_SEPARATOR, &separator, -1);
2496 valid = gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), &iter);
2497
2498 if (separator)
2499 {
2500 break;
2501 }
2502 }
2503
2504 if (valid)
2505 {
2506 if (gtk_tree_model_iter_has_child (GTK_TREE_MODEL (priv->store), &iter))
2507 {
2508 gint children;
2509 GtkTreeIter child;
2510
2511 children = gtk_tree_model_iter_n_children (GTK_TREE_MODEL (priv->store), &iter);
2512 valid = FALSE;
2513
2514 if (gtk_tree_model_iter_children (GTK_TREE_MODEL (priv->store), &child, &iter))
2515 {
2516 do
2517 {
2518 gtk_tree_model_get (GTK_TREE_MODEL (priv->store), &child,
2519 COMBOFIX_COL_REAL_STRING, &tmp_str,
2520 -1);
2521
2522 if (strcmp (tmp_str, tmp_str2) == 0)
2523 {
2524 g_free (tmp_str);
2525 valid = TRUE;
2526 children --;
2527 break;
2528 }
2529
2530 g_free (tmp_str);
2531 }
2532 while (gtk_tree_model_iter_next (GTK_TREE_MODEL (priv->store), &child));
2533
2534 if (valid)
2535 gtk_tree_store_remove (priv->store, &child);
2536 }
2537 if (children == 0)
2538 {
2539 gtk_combofix_remove_for_report (GTK_TREE_MODEL (priv->store), &iter);
2540 }
2541 }
2542 else
2543 gtk_combofix_remove_for_report (GTK_TREE_MODEL (priv->store), &iter);
2544 }
2545
2546 /* update completion */
2547 completion = gtk_entry_get_completion (GTK_ENTRY (priv->entry));
2548 completion_model = gtk_entry_completion_get_model (completion);
2549 valid = gtk_tree_model_get_iter_first (completion_model, &iter);
2550
2551 while (valid)
2552 {
2553 gtk_tree_model_get (completion_model, &iter, 0, &tmp_str, -1);
2554
2555 if (etat.combofix_case_sensitive && !strcmp (tmp_str2, tmp_str))
2556 {
2557 g_free (tmp_str);
2558 break;
2559 }
2560 else if (!g_utf8_collate (g_utf8_casefold (tmp_str2, -1), g_utf8_casefold (tmp_str, -1)))
2561 {
2562 g_free (tmp_str);
2563 break;
2564 }
2565
2566 g_free (tmp_str);
2567
2568 valid = gtk_tree_model_iter_next (completion_model, &iter);
2569 }
2570
2571 if (valid)
2572 gtk_list_store_remove (GTK_LIST_STORE (completion_model), &iter);
2573 g_free (tmp_str2);
2574 }
2575
2576 /**
2577 *
2578 *
2579 * \param combofix text
2580 *
2581 * \return
2582 **/
gtk_combofix_set_selection_callback(GtkComboFix * combofix,GCallback func,gpointer data)2583 void gtk_combofix_set_selection_callback (GtkComboFix *combofix,
2584 GCallback func,
2585 gpointer data)
2586 {
2587 GtkComboFixPrivate *priv;
2588
2589 priv = gtk_combofix_get_instance_private (combofix);
2590
2591 if (func)
2592 g_signal_connect (G_OBJECT (priv->selection),
2593 "changed",
2594 G_CALLBACK (func),
2595 data);
2596 }
2597
2598 /**
2599 *
2600 *
2601 * \param
2602 *
2603 * \return
2604 **/
2605 /* Local Variables: */
2606 /* c-basic-offset: 4 */
2607 /* End: */
2608