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