GnuCash c935c2f+
Loading...
Searching...
No Matches
dialog-search.c
1/*
2 * dialog-search.c -- Search Dialog
3 * Copyright (C) 2002 Derek Atkins
4 * Author: Derek Atkins <warlord@MIT.EDU>
5 *
6 * Copyright (c) 2006 David Hampton <hampton@employees.org>
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License as
10 * published by the Free Software Foundation; either version 2 of
11 * the License, or (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, contact:
20 *
21 * Free Software Foundation Voice: +1-617-542-5942
22 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
23 * Boston, MA 02110-1301, USA gnu@gnu.org
24 */
25
26#include <config.h>
27
28#include <gtk/gtk.h>
29#include <glib/gi18n.h>
30
31#include "dialog-utils.h"
32#include "gnc-component-manager.h"
33#include "gnc-ui-util.h"
34#include "gnc-ui.h"
35#include "gnc-gui-query.h"
36#include "gnc-query-view.h"
37#include "gnc-prefs.h"
38#include "gnc-session.h"
39#include "qof.h"
40#include "engine-helpers.h"
41#include "qofbookslots.h"
42
43#include "Transaction.h" /* for the SPLIT_* and TRANS_* */
44
45#include "dialog-search.h"
46#include "search-core-type.h"
47#include "search-param.h"
48
49/* This static indicates the debugging module that this .o belongs to. */
50static QofLogModule log_module = G_LOG_DOMAIN;
51
52#define DIALOG_SEARCH_CM_CLASS "dialog-search"
53#define GNC_PREFS_GROUP_SEARCH_GENERAL "dialogs.search"
54#define GNC_PREF_NEW_SEARCH_LIMIT "new-search-limit"
55#define GNC_PREF_ACTIVE_ONLY "search-for-active-only"
56
57typedef enum
58{
59 GNC_SEARCH_MATCH_ALL = 0,
60 GNC_SEARCH_MATCH_ANY = 1
61} GNCSearchType;
62
63enum search_cols
64{
65 SEARCH_COL_NAME = 0,
66 SEARCH_COL_POINTER,
67 NUM_SEARCH_COLS
68};
69
71{
72 GtkWidget *dialog;
73 GtkWidget *grouping_combo;
74 GtkWidget *match_all_label;
75 GtkWidget *criteria_table;
76 GtkWidget *criteria_scroll_window;
77 GtkWidget *result_hbox;
78
79 /* The "results" sub-window widgets */
80 GtkWidget *result_view;
81
82 /* The search_type radio-buttons */
83 GtkWidget *new_rb;
84 GtkWidget *narrow_rb;
85 GtkWidget *add_rb;
86 GtkWidget *del_rb;
87 GtkWidget *active_only_check;
88
89 /* The Select button */
90 GtkWidget *select_button;
91 GList *button_list;
92
93 /* The close/cancel buttons */
94 GtkWidget *close_button;
95 GtkWidget *cancel_button;
96
97 /* Callbacks */
98 GNCSearchResultCB result_cb;
99 GNCSearchNewItemCB new_item_cb;
101 GNCSearchFree free_cb;
102 gpointer user_data;
103
104 GNCSearchSelectedCB selected_cb;
105 gpointer select_arg;
106 gboolean allow_clear;
107
108 /* What we're searching for, and how */
109 const gchar *type_label;
110 QofIdTypeConst search_for;
111 GNCSearchType grouping; /* Match Any, Match All */
112 const QofParam *get_guid; /* Function to GetGUID from the object */
113 int search_type; /* New, Narrow, Add, Delete */
114
115 /* Our query status */
116 QofQuery *q;
117 QofQuery *start_q; /* The query to start from, if any */
118
119 /* The list of criteria */
120 GNCSearchParam *last_param;
121 GList *params_list; /* List of GNCSearchParams */
122 GList *display_list; /* List of GNCSearchParamSimples for Display */
123 gint num_cols; /* Number of Display Columns */
124 GList *crit_list; /* List of crit_data */
125
126 gint component_id;
127 const gchar *prefs_group;
128};
129
131{
132 GNCSearchParam *param;
133 GNCSearchCoreType *element;
134 GtkWidget *elemwidget;
135 GtkWidget *container;
136 GtkWidget *button;
137 GtkDialog *dialog;
138};
139
140static void search_clear_criteria (GNCSearchWindow *sw);
141static void gnc_search_dialog_display_results (GNCSearchWindow *sw);
142
143static void
144gnc_search_callback_button_execute (GNCSearchCallbackButton *cb,
145 GNCSearchWindow *sw)
146{
147 GNCQueryView *qview = GNC_QUERY_VIEW(sw->result_view);
148
149 // Sanity check
150 g_assert(qview);
151
152 /* Do we have a callback for multi-selections ? */
153 if (cb->cb_multiselect_fn && (!cb->cb_fcn ))
154 {
155 GList *entries = gnc_query_view_get_selected_entry_list (qview);
156 // Call the callback
157 (cb->cb_multiselect_fn)(GTK_WINDOW (sw->dialog), entries, sw->user_data);
158 g_list_free (entries);
159 }
160 else
161 {
162 // No, stick to the single-item callback
163 gpointer entry = gnc_query_view_get_selected_entry (qview);
164 if (cb->cb_fcn)
165 (cb->cb_fcn)(GTK_WINDOW (sw->dialog), &entry, sw->user_data);
166 }
167}
168
169static void
170gnc_search_dialog_result_clicked (GtkButton *button, GNCSearchWindow *sw)
171{
173
174 cb = g_object_get_data (G_OBJECT (button), "data");
175 gnc_search_callback_button_execute (cb, sw);
176}
177
178static void
179gnc_search_dialog_select_buttons_enable (GNCSearchWindow *sw, gint selected)
180{
181 gboolean enable, read_only;
182 GList *blist;
183
184 read_only = qof_book_is_readonly (gnc_get_current_book ());
185
186 for (blist = sw->button_list; blist; blist = blist->next)
187 {
188 GNCSearchCallbackButton *button_spec = g_object_get_data (G_OBJECT(blist->data) , "data");
189
190 if(selected == 0)
191 {
192 gtk_widget_set_sensitive (GTK_WIDGET(blist->data), FALSE);
193 continue;
194 }
195
196 if(read_only == TRUE)
197 {
198 if((selected > 1) && (!(button_spec->cb_multiselect_fn == NULL)) && (button_spec->sensitive_if_readonly == TRUE))
199 enable = TRUE;
200 else
201 enable = FALSE;
202
203 if((selected == 1) && (button_spec->sensitive_if_readonly == TRUE))
204 enable = TRUE;
205 }
206 else
207 {
208 if((selected > 1) && (!(button_spec->cb_multiselect_fn == NULL)))
209 enable = TRUE;
210 else
211 enable = FALSE;
212
213 if(selected == 1)
214 enable = TRUE;
215 }
216 gtk_widget_set_sensitive (GTK_WIDGET(blist->data), enable);
217 }
218}
219
220static void
221gnc_search_dialog_select_cb (GtkButton *button, GNCSearchWindow *sw)
222{
223 gpointer entry;
224 g_return_if_fail (sw->selected_cb);
225
226 entry = gnc_query_view_get_selected_entry (GNC_QUERY_VIEW (sw->result_view));
227 if (!entry && !sw->allow_clear)
228 {
229 char *msg = _("You must select an item from the list");
230 gnc_error_dialog (GTK_WINDOW (sw->dialog), "%s", msg);
231 return;
232 }
233
234 (sw->selected_cb)(GTK_WINDOW (sw->dialog), entry, sw->select_arg);
235 gnc_search_dialog_destroy (sw);
236}
237
238static void
239gnc_search_dialog_select_row_cb (GNCQueryView *qview,
240 gpointer item,
241 gpointer user_data)
242{
243 GNCSearchWindow *sw = user_data;
244 gint number_of_rows = GPOINTER_TO_INT(item);
245 gnc_search_dialog_select_buttons_enable(sw, number_of_rows);
246}
247
248static void
249gnc_search_dialog_double_click_cb (GNCQueryView *qview,
250 gpointer item,
251 gpointer user_data)
252{
253 GNCSearchWindow *sw = user_data;
254
255 if (sw->selected_cb)
256 /* Select the item */
257 gnc_search_dialog_select_cb (NULL, sw);
258 else if (sw->buttons)
259 /* Call the first button (usually view/edit) */
260 gnc_search_callback_button_execute (sw->buttons, sw);
261
262 /* If we get here, then nothing to do for a double-click */
263}
264
265static void
266gnc_search_dialog_init_result_view (GNCSearchWindow *sw)
267{
268 GtkTreeSelection *selection;
269
270 sw->result_view = gnc_query_view_new(sw->display_list, sw->q);
271
272 // We want the multi-selection mode of the tree view.
273 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(sw->result_view));
274 gtk_tree_selection_set_mode (selection, GTK_SELECTION_MULTIPLE);
275
276 /* Set the sort order of the tree view */
277 gnc_query_sort_order(GNC_QUERY_VIEW(sw->result_view), 1, GTK_SORT_ASCENDING);
278
279 /* Setup the list callbacks */
280 g_signal_connect (GNC_QUERY_VIEW(sw->result_view), "row_selected",
281 G_CALLBACK (gnc_search_dialog_select_row_cb), sw);
282
283 g_signal_connect (GNC_QUERY_VIEW(sw->result_view), "double_click_entry",
284 G_CALLBACK(gnc_search_dialog_double_click_cb), sw);
285}
286
287static void
288gnc_search_dialog_display_results (GNCSearchWindow *sw)
289{
290 gdouble max_count;
291
292 /* Check if this is the first time this is called for this window.
293 * If so, then build the results sub-window, the scrolled treeview,
294 * and the active buttons.
295 */
296 if (sw->result_view == NULL)
297 {
298 GtkWidget *scroller, *frame, *button_box, *button;
299
300 /* Create the view */
301 gnc_search_dialog_init_result_view (sw);
302
303 frame = gtk_frame_new(NULL);
304
305 /* Create the scroller and add the view to the scroller */
306 scroller = gtk_scrolled_window_new (NULL, NULL);
307 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scroller),
308 GTK_POLICY_AUTOMATIC,
309 GTK_POLICY_AUTOMATIC);
310 gtk_widget_set_size_request(GTK_WIDGET(scroller), 300, 100);
311 gtk_container_add (GTK_CONTAINER (scroller), sw->result_view);
312 gtk_container_add(GTK_CONTAINER(frame), scroller);
313
314 /* Create the button_box */
315 button_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 3);
316 gtk_box_set_homogeneous (GTK_BOX (button_box), FALSE);
317
318 /* ... and add all the buttons */
319 if (sw->buttons)
320 {
321 int i;
322
323 button = gtk_button_new_with_label (_("Select"));
324 g_signal_connect (G_OBJECT (button), "clicked",
325 G_CALLBACK (gnc_search_dialog_select_cb), sw);
326 gtk_box_pack_start (GTK_BOX (button_box), button, FALSE, FALSE, 3);
327 sw->select_button = button;
328
329 for (i = 0; sw->buttons[i].label; i++)
330 {
331 GNCSearchCallbackButton* button_spec = sw->buttons + i;
332 button = gtk_button_new_with_label (_(button_spec->label));
333 g_object_set_data (G_OBJECT (button), "data", button_spec);
334
335 if (qof_book_is_readonly (gnc_get_current_book ()))
336 gtk_widget_set_sensitive (GTK_WIDGET(button), button_spec->sensitive_if_readonly);
337
338 /* Save the button pointer */
339 sw->button_list = g_list_append(sw->button_list, button);
340
341 g_signal_connect (G_OBJECT (button), "clicked",
342 G_CALLBACK (gnc_search_dialog_result_clicked), sw);
343 gtk_box_pack_start (GTK_BOX (button_box), button, FALSE, FALSE, 3);
344 }
345 }
346
347 /* Add the scrolled-view and button-box to the results_box */
348 gtk_box_pack_end (GTK_BOX (sw->result_hbox), button_box, FALSE, FALSE, 3);
349 gtk_box_pack_end (GTK_BOX (sw->result_hbox), frame, TRUE, TRUE, 3);
350
351 /* And show the results */
352 gtk_widget_show_all (sw->result_hbox);
353
354 /* But may be hide the select button */
355 if (!sw->selected_cb)
356 gtk_widget_hide (sw->select_button);
357 }
358 else
359 /* Update the query in the view */
360 gnc_query_view_reset_query (GNC_QUERY_VIEW(sw->result_view), sw->q);
361
362 /* Deselect all the select buttons and any items */
363 gnc_search_dialog_select_buttons_enable (sw, 0);
364 gnc_query_view_unselect_all (GNC_QUERY_VIEW(sw->result_view));
365
366 /* set 'new search' if fewer than max_count items is returned. */
367 max_count = gnc_prefs_get_float(GNC_PREFS_GROUP_SEARCH_GENERAL, GNC_PREF_NEW_SEARCH_LIMIT);
368 if (gnc_query_view_get_num_entries(GNC_QUERY_VIEW(sw->result_view)) < max_count)
369 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (sw->new_rb), TRUE);
370
371 /* If there are results then select the first, and grab focus */
372 if (gnc_query_view_get_num_entries (GNC_QUERY_VIEW(sw->result_view)) > 0)
373 {
374 GtkTreeSelection *selection = gtk_tree_view_get_selection (GTK_TREE_VIEW(sw->result_view));
375 GtkTreePath *path = gtk_tree_path_new_first ();
376 gtk_tree_selection_select_path (selection, path);
377 gtk_tree_path_free (path);
378 gtk_widget_grab_focus (sw->result_view);
379 }
380}
381
382static void
383match_combo_changed (GtkComboBoxText *combo_box, GNCSearchWindow *sw)
384{
385 sw->grouping = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_box));
386}
387
388static void
389search_type_cb (GtkToggleButton *button, GNCSearchWindow *sw)
390{
391 GSList * buttongroup = gtk_radio_button_get_group (GTK_RADIO_BUTTON(button));
392
393 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button)))
394 {
395 sw->search_type =
396 g_slist_length (buttongroup) - g_slist_index (buttongroup, button) - 1;
397 }
398}
399
400static void
401search_active_only_cb (GtkToggleButton *button, GNCSearchWindow *sw)
402{
403
404 gnc_prefs_set_bool(sw->prefs_group, GNC_PREF_ACTIVE_ONLY,
405 gtk_toggle_button_get_active (button));
406}
407
408static QofQuery *
409create_query_fragment (QofIdTypeConst search_for, GNCSearchParam *param, QofQueryPredData *pdata)
410{
411 GNCSearchParamKind kind = gnc_search_param_get_kind (param);
412 QofQuery *q = qof_query_create_for (search_for);
413
414 if (kind == SEARCH_PARAM_ELEM)
415 {
416 /* The "op" parameter below will be ignored since q has no terms. */
417 qof_query_add_term (q, gnc_search_param_get_param_path (GNC_SEARCH_PARAM_SIMPLE (param)),
418 pdata, QOF_QUERY_OR);
419 }
420 else
421 {
422 GList *plist = gnc_search_param_get_search (GNC_SEARCH_PARAM_COMPOUND (param));
423
424 for ( ; plist; plist = plist->next)
425 {
426 QofQuery *new_q;
427 GNCSearchParam *param2 = plist->data;
428 QofQuery *q2 = create_query_fragment (search_for, param2,
430 new_q = qof_query_merge (q, q2, kind == SEARCH_PARAM_ANY ?
431 QOF_QUERY_OR : QOF_QUERY_AND);
434 q = new_q;
435 }
437 }
438 return q;
439}
440
441static void
442search_update_query (GNCSearchWindow *sw)
443{
444 static GSList *active_params = NULL;
445 QofQuery *q, *q2, *new_q;
446 GList *node;
447 QofQueryOp op;
448
449 if (sw->grouping == GNC_SEARCH_MATCH_ANY)
450 op = QOF_QUERY_OR;
451 else
452 op = QOF_QUERY_AND;
453
454 if (active_params == NULL)
455 active_params = g_slist_prepend (NULL, QOF_PARAM_ACTIVE);
456
457 /* Make sure we supply a book! */
458 if (sw->start_q == NULL)
459 {
460 sw->start_q = qof_query_create_for (sw->search_for);
461 qof_query_set_book (sw->start_q, gnc_get_current_book ());
462 }
463 else
464 {
465 /* We've got a query -- purge it of any "active" parameters */
466 qof_query_purge_terms (sw->start_q, active_params);
467 }
468
469 /* Now create a new query to work from */
470 q = qof_query_create_for (sw->search_for);
471
472 /* Walk the list of criteria */
473 for (node = sw->crit_list; node; node = node->next)
474 {
475 struct _crit_data *data = node->data;
476 QofQueryPredData* pdata;
477
478 pdata = gnc_search_core_type_get_predicate (data->element);
479 if (pdata)
480 {
481 q2 = create_query_fragment(sw->search_for, GNC_SEARCH_PARAM (data->param), pdata);
482 new_q = qof_query_merge (q, q2, op);
485 q = new_q;
486 }
487 }
488
489 /* Now combine this query with the existing query, depending on
490 * what we want to do... We can assume that cases 1, 2, and 3
491 * already have sw->q being valid!
492 */
493
494 switch (sw->search_type)
495 {
496 case 0: /* New */
497 new_q = qof_query_merge (sw->start_q, q, QOF_QUERY_AND);
499 break;
500 case 1: /* Refine */
501 new_q = qof_query_merge (sw->q, q, QOF_QUERY_AND);
503 break;
504 case 2: /* Add */
505 new_q = qof_query_merge (sw->q, q, QOF_QUERY_OR);
507 break;
508 case 3: /* Delete */
509 q2 = qof_query_invert (q);
510 new_q = qof_query_merge (sw->q, q2, QOF_QUERY_AND);
513 break;
514 default:
515 g_warning ("bad search type: %d", sw->search_type);
516 new_q = q;
517 break;
518 }
519
520 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (sw->active_only_check)))
521 {
522 qof_query_add_boolean_match (new_q, active_params, TRUE, QOF_QUERY_AND);
523 active_params = NULL;
524 }
525
526 /* Destroy the old query */
527 if (sw->q)
528 qof_query_destroy (sw->q);
529
530 /* And save the new one */
531 sw->q = new_q;
532}
533
534static void
535gnc_search_dialog_show_close_cancel (GNCSearchWindow *sw)
536{
537 if (sw->selected_cb)
538 {
539 gtk_widget_show (sw->cancel_button);
540 gtk_widget_hide (sw->close_button);
541 }
542 else
543 {
544 gtk_widget_hide (sw->cancel_button);
545 gtk_widget_show (sw->close_button);
546 }
547}
548
549static void
550gnc_search_dialog_reset_widgets (GNCSearchWindow *sw)
551{
552 gboolean sens = (sw->q != NULL);
553 gboolean crit_list_vis = FALSE;
554
555 gtk_widget_set_sensitive(GTK_WIDGET(sw->narrow_rb), sens);
556 gtk_widget_set_sensitive(GTK_WIDGET(sw->add_rb), sens);
557 gtk_widget_set_sensitive(GTK_WIDGET(sw->del_rb), sens);
558
559 if (sw->q)
560 {
561 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (sw->new_rb), FALSE);
562 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON (sw->narrow_rb), TRUE);
563 }
564
565 if (sw->crit_list)
566 crit_list_vis = TRUE;
567
568 gtk_widget_set_sensitive(sw->grouping_combo, crit_list_vis);
569 gtk_widget_set_visible (sw->criteria_scroll_window, crit_list_vis);
570 gtk_widget_set_visible (sw->match_all_label, !crit_list_vis);
571}
572
573static gboolean
574gnc_search_dialog_crit_ok (GNCSearchWindow *sw)
575{
576 struct _crit_data *data;
577 GList *l;
578 gboolean ret;
579
580 if (!sw->crit_list)
581 return TRUE;
582
583 l = g_list_last (sw->crit_list);
584 data = l->data;
585 ret = gnc_search_core_type_validate (data->element);
586
587 if (ret)
588 sw->last_param = data->param;
589
590 return ret;
591}
592
593static void
594search_find_cb (GtkButton *button, GNCSearchWindow *sw)
595{
596 if (!gnc_search_dialog_crit_ok (sw))
597 return;
598
599 search_update_query (sw);
600 search_clear_criteria (sw);
601 gnc_search_dialog_reset_widgets (sw);
602
603 if (sw->result_cb)
604 {
605 gpointer entry = NULL;
606 if (sw->result_view)
607 {
608 GNCQueryView *qview = GNC_QUERY_VIEW (sw->result_view);
609 entry = gnc_query_view_get_selected_entry (qview);
610 }
611 (sw->result_cb)(sw->q, sw->user_data, &entry);
612 }
613 else
614 gnc_search_dialog_display_results (sw);
615}
616
617static void
618search_new_item_cb (GtkButton *button, GNCSearchWindow *sw)
619{
620 gpointer res;
621
622 g_return_if_fail (sw->new_item_cb);
623
624 res = (sw->new_item_cb)(GTK_WINDOW (sw->dialog), sw->user_data);
625
626 if (res)
627 {
628 const GncGUID *guid = (const GncGUID *) ((sw->get_guid->param_getfcn)(res, sw->get_guid));
629 QofQueryOp op = QOF_QUERY_OR;
630
631 if (!sw->q)
632 {
633 if (!sw->start_q)
634 {
635 sw->start_q = qof_query_create_for (sw->search_for);
636 qof_query_set_book (sw->start_q, gnc_get_current_book ());
637 }
638 sw->q = qof_query_copy (sw->start_q);
639 op = QOF_QUERY_AND;
640 }
641
642 qof_query_add_guid_match (sw->q, g_slist_prepend (NULL, QOF_PARAM_GUID),
643 guid, op);
644
645 /* Watch this entity so we'll refresh once it's actually changed */
646 gnc_gui_component_watch_entity (sw->component_id, guid, QOF_EVENT_MODIFY);
647 }
648}
649
650static void
651search_cancel_cb (GtkButton *button, GNCSearchWindow *sw)
652{
653 /* Don't select anything */
654 gnc_search_dialog_destroy (sw);
655}
656
657static void
658search_help_cb (GtkButton *button, GNCSearchWindow *sw)
659{
660 gnc_gnome_help (GTK_WINDOW(sw->dialog), DF_MANUAL, DL_FIND_TRANSACTIONS);
661}
662
663static void
664remove_element (GtkWidget *button, GNCSearchWindow *sw)
665{
666 GtkWidget *element;
667 struct _elem_data *data;
668
669 if (!sw->crit_list)
670 return;
671
672 element = g_object_get_data (G_OBJECT (button), "element");
673 data = g_object_get_data (G_OBJECT (element), "data");
674
675 /* remove the element from the list */
676 sw->crit_list = g_list_remove (sw->crit_list, data);
677
678 /* and from the display */
679 gtk_container_remove (GTK_CONTAINER (sw->criteria_table), element);
680 gtk_container_remove (GTK_CONTAINER (sw->criteria_table), button);
681
682 /* disable match-type menu when there is no criterion */
683 if (!sw->crit_list)
684 {
685 gtk_widget_set_sensitive(sw->grouping_combo, FALSE);
686 gtk_widget_show(sw->match_all_label);
687 gtk_widget_hide(sw->criteria_scroll_window);
688 }
689}
690
691static void
692attach_element (GtkWidget *element, GNCSearchWindow *sw, int row)
693{
694 GtkWidget *remove;
695 struct _crit_data *data;
696
697 data = g_object_get_data (G_OBJECT (element), "data");
698
699 gnc_search_core_type_pass_parent (data->element, GTK_WINDOW(sw->dialog));
700
701 gtk_grid_attach (GTK_GRID (sw->criteria_table), element, 0, row, 1, 1);
702 gtk_widget_set_hexpand (element, TRUE);
703 gtk_widget_set_halign (element, GTK_ALIGN_FILL);
704 g_object_set (element, "margin", 0, NULL);
705
706 remove = gtk_button_new_with_mnemonic (_("_Remove"));
707 g_object_set_data (G_OBJECT (remove), "element", element);
708 g_signal_connect (G_OBJECT (remove), "clicked", G_CALLBACK (remove_element), sw);
709
710 gtk_grid_attach (GTK_GRID (sw->criteria_table), remove, 1, row, 1, 1);
711 gtk_widget_set_hexpand (remove, FALSE);
712 gtk_widget_set_halign (remove, GTK_ALIGN_CENTER);
713 g_object_set (remove, "margin", 0, NULL);
714
715 gtk_widget_show (remove);
716 data->button = remove; /* Save the button for later */
717}
718
719static void
720combo_box_changed (GtkComboBox *combo_box, struct _crit_data *data)
721{
722 GNCSearchParam *param;
723 GNCSearchCoreType *newelem;
724 GtkTreeModel *model;
725 GtkTreeIter iter;
726
727 if (!gtk_combo_box_get_active_iter(combo_box, &iter))
728 return;
729 model = gtk_combo_box_get_model(combo_box);
730 gtk_tree_model_get(model, &iter, SEARCH_COL_POINTER, &param, -1);
731
732 if (gnc_search_param_type_match (param, data->param))
733 {
734 /* The param type is the same, just save the new param */
735 data->param = param;
736 return;
737 }
738 data->param = param;
739
740 /* OK, let's do a widget shuffle, throw away the old widget/element,
741 * and create another one here. No need to change the crit_list --
742 * the pointer to data stays the same.
743 */
744 if (data->elemwidget)
745 gtk_container_remove (GTK_CONTAINER (data->container), data->elemwidget);
746 g_object_unref (G_OBJECT (data->element));
747
748 newelem = gnc_search_core_type_new_type_name
749 (gnc_search_param_get_param_type (param));
750 data->element = newelem;
751 data->elemwidget = gnc_search_core_type_get_widget (newelem);
752 if (data->elemwidget)
753 {
754 gtk_box_pack_start (GTK_BOX (data->container), data->elemwidget,
755 FALSE, FALSE, 0);
756 }
757
758 gnc_search_core_type_pass_parent (data->element, GTK_WINDOW(data->dialog));
759
760 /* Make sure it's visible */
761 gtk_widget_show_all (data->container);
762
763 /* Make sure we widen up if necessary */
764 gtk_widget_queue_resize (GTK_WIDGET (data->dialog));
765
766 /* And grab focus */
767 gnc_search_core_type_grab_focus (newelem);
768 gnc_search_core_type_editable_enters (newelem);
769}
770
771static void
772search_clear_criteria (GNCSearchWindow *sw)
773{
774 GList *node;
775
776 for (node = sw->crit_list; node; )
777 {
778 GList *tmp = node->next;
779 struct _crit_data *data = node->data;
780 g_object_ref (data->button);
781 remove_element (data->button, sw);
782 node = tmp;
783 }
784}
785
786static GtkWidget *
787get_comb_box_widget (GNCSearchWindow *sw, struct _crit_data *data)
788{
789 GtkWidget *combo_box;
790 GtkListStore *store;
791 GtkTreeIter iter;
792 GtkCellRenderer *cell;
793 GList *l;
794 int index = 0, current = 0;
795
796 store = gtk_list_store_new(NUM_SEARCH_COLS, G_TYPE_STRING, G_TYPE_POINTER);
797 combo_box = gtk_combo_box_new_with_model(GTK_TREE_MODEL(store));
798 g_object_unref(store);
799
800 cell = gtk_cell_renderer_text_new ();
801 gtk_cell_layout_pack_start(GTK_CELL_LAYOUT (combo_box), cell, TRUE);
802 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo_box), cell,
803 "text", SEARCH_COL_NAME,
804 NULL);
805
806 for (l = sw->params_list; l; l = l->next)
807 {
808 GNCSearchParam *param = l->data;
809
810 gtk_list_store_append(store, &iter);
811 gtk_list_store_set(store, &iter,
812 SEARCH_COL_NAME, _(gnc_search_param_get_title (param)),
813 SEARCH_COL_POINTER, param,
814 -1);
815
816 if (param == sw->last_param) /* is this the right parameter to start? */
817 current = index;
818
819 index++;
820 }
821
822 gtk_combo_box_set_active (GTK_COMBO_BOX(combo_box), current);
823 g_signal_connect (combo_box, "changed", G_CALLBACK (combo_box_changed), data);
824
825 return combo_box;
826}
827
828static GtkWidget *
829get_element_widget (GNCSearchWindow *sw, GNCSearchCoreType *element)
830{
831 GtkWidget *combo_box, *hbox, *p;
832 struct _crit_data *data;
833
834 data = g_new0 (struct _crit_data, 1);
835 data->element = element;
836 data->dialog = GTK_DIALOG (sw->dialog);
837
838 hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
839 gtk_box_set_homogeneous (GTK_BOX (hbox), FALSE);
840
841 /* only set to automatically clean up the memory */
842 g_object_set_data_full (G_OBJECT (hbox), "data", data, g_free);
843
844 p = gnc_search_core_type_get_widget (element);
845 data->elemwidget = p;
846 data->container = hbox;
847 data->param = sw->last_param;
848
849 combo_box = get_comb_box_widget (sw, data);
850 gtk_box_pack_start (GTK_BOX (hbox), combo_box, FALSE, FALSE, 0);
851 if (p)
852 gtk_box_pack_start (GTK_BOX (hbox), p, FALSE, FALSE, 0);
853 gtk_widget_show_all (hbox);
854
855 return hbox;
856}
857
858static void
859gnc_search_dialog_book_option_changed (gpointer new_val, gpointer user_data)
860{
861 GList *l;
862 GNCSearchWindow *sw = user_data;
863 gboolean *new_data = (gboolean*)new_val;
864 /* Save current dialog focus */
865 GtkWidget *focused_widget = gtk_window_get_focus(GTK_WINDOW(sw->dialog));
866
867 g_return_if_fail (sw);
868 if (strcmp (sw->search_for, GNC_ID_SPLIT) != 0)
869 return;
870
871 /* Adjust labels for future added search criteria */
872 for (l = sw->params_list; l; l = l->next)
873 {
874 GNCSearchParam *param = l->data;
875
876 if (*new_data)
877 {
878 if (strcmp (gnc_search_param_get_title (param), N_("Action")) == 0)
879 gnc_search_param_set_title (param, N_("Number/Action"));
880 if (strcmp (gnc_search_param_get_title (param), N_("Number")) == 0)
881 gnc_search_param_set_title (param, N_("Transaction Number"));
882 }
883 else
884 {
885 if (strcmp (gnc_search_param_get_title (param), N_("Number/Action")) == 0)
886 gnc_search_param_set_title (param, N_("Action"));
887 if (strcmp (gnc_search_param_get_title (param), N_("Transaction Number")) == 0)
888 gnc_search_param_set_title (param, N_("Number"));
889 }
890 }
891 /* Adjust labels for existing search criteria; walk the list of criteria */
892 for (l = sw->crit_list; l; l = l->next)
893 {
894 struct _crit_data *data = l->data;
895 GList *children = gtk_container_get_children (GTK_CONTAINER(data->container));
896
897 /* For each, walk the list of container children to get combo_box */
898 for (GList *child = children; child; child = g_list_next (child))
899 {
900 GtkWidget *combo_box = child->data;
901
902 /* Get current active item if combo_box */
903 if (GTK_IS_COMBO_BOX(combo_box))
904 {
905 GtkWidget *new_combo_box;
906 gint index;
907
908 /* Set index to current active item */
909 index = gtk_combo_box_get_active(GTK_COMBO_BOX(combo_box));
910 /* Create new combo_box to replace existing one */
911 new_combo_box = get_comb_box_widget (sw, data);
912 /* If current combo_box has focus, point to new_combo-box */
913 if (focused_widget == combo_box)
914 focused_widget = new_combo_box;
915 gtk_widget_destroy(combo_box);
916 /* Set new combo_box to current active item */
917 gtk_combo_box_set_active(GTK_COMBO_BOX(new_combo_box), index);
918 gtk_box_pack_start (GTK_BOX (data->container), new_combo_box,
919 FALSE, FALSE, 0);
920 gtk_box_reorder_child(GTK_BOX (data->container), new_combo_box, 0);
921 gtk_widget_show_all (data->container);
922 }
923 }
924 g_list_free (children);
925 }
926 gtk_widget_grab_focus(focused_widget);
927}
928
930{
932 GtkGrid *grid;
934 gint cols, rows;
935};
936
937static void
938get_grid_size (GtkWidget *child, gpointer data)
939{
940 struct grid_size *gridsize = data;
941 gint top, left, height, width;
942
943 gtk_container_child_get(GTK_CONTAINER(gridsize->grid), child,
944 "left-attach", &left,
945 "top-attach", &top,
946 "height", &height,
947 "width", &width,
948 NULL);
949
950 if (left + width >= gridsize->cols)
951 gridsize->cols = left + width;
952
953 if (top + height >= gridsize->rows)
954 gridsize->rows = top + height;
955}
956
957static void
958gnc_search_dialog_add_criterion (GNCSearchWindow *sw)
959{
960 GNCSearchCoreType *new_sct;
961 struct grid_size gridsize;
962
963 gridsize.cols = 0;
964 gridsize.rows = 0;
965
966 /* First, make sure that the last criterion is ok */
967 if (sw->crit_list)
968 {
969 if (!gnc_search_dialog_crit_ok (sw))
970 return;
971 }
972 else
973 {
974 sw->last_param = sw->params_list->data;
975
976 /* no match-all situation anymore */
977 gtk_widget_set_sensitive(sw->grouping_combo, TRUE);
978 gtk_widget_hide(sw->match_all_label);
979 gtk_widget_show(sw->criteria_scroll_window);
980 }
981 /* create a new criterion element */
982 new_sct = gnc_search_core_type_new_type_name
983 (gnc_search_param_get_param_type (sw->last_param));
984
985 if (new_sct)
986 {
987 struct _crit_data *data;
988 GtkWidget *w;
989
990 w = get_element_widget (sw, new_sct);
991 data = g_object_get_data (G_OBJECT (w), "data");
992 sw->crit_list = g_list_append (sw->crit_list, data);
993
994 gridsize.grid = GTK_GRID (sw->criteria_table);
995 gtk_container_foreach(GTK_CONTAINER(sw->criteria_table), get_grid_size, &gridsize);
996
997 attach_element (w, sw, gridsize.rows);
998
999 gnc_search_core_type_grab_focus (new_sct);
1000 gnc_search_core_type_editable_enters (new_sct);
1001 }
1002}
1003
1004static void
1005add_criterion (GtkWidget *button, GNCSearchWindow *sw)
1006{
1007 gint number_of_buttons = g_list_length (sw->crit_list) + 1;
1008 gint button_height = gtk_widget_get_allocated_height (button);
1009 gint min_height = MIN (number_of_buttons * button_height, 5 * button_height);
1010
1011 // this sets the minimum content height for the criteria scroll
1012 // window, it is set to a max of 5 buttons visible without scrolling
1013 gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW(
1014 sw->criteria_scroll_window),
1015 min_height + (button_height/2));
1016
1017 gnc_search_dialog_add_criterion (sw);
1018}
1019
1020static int
1021gnc_search_dialog_close_cb (GtkDialog *dialog, GNCSearchWindow *sw)
1022{
1023 g_return_val_if_fail (sw, TRUE);
1024
1025 /* Unregister callback on book option changes originally registered
1026 * if searching for splits */
1027 if (strcmp (sw->search_for, GNC_ID_SPLIT) == 0)
1028 gnc_book_option_remove_cb(OPTION_NAME_NUM_FIELD_SOURCE,
1029 gnc_search_dialog_book_option_changed, sw);
1030
1031 gnc_unregister_gui_component (sw->component_id);
1032
1033 /* Clear the crit list */
1034 g_list_free (sw->crit_list);
1035
1036 /* Clear the button list */
1037 g_list_free (sw->button_list);
1038
1039 /* Destroy the queries */
1040 if (sw->q) qof_query_destroy (sw->q);
1041 if (sw->start_q) qof_query_destroy (sw->start_q);
1042
1043 /* Destroy the user_data */
1044 if (sw->free_cb)
1045 (sw->free_cb)(sw->user_data);
1046
1047 /* Destroy and exit */
1048 g_free (sw);
1049 return FALSE;
1050}
1051
1052static void
1053refresh_handler (GHashTable *changes, gpointer data)
1054{
1055 GNCSearchWindow * sw = data;
1056
1057 g_return_if_fail (sw);
1058 /* This assumes that results_cb will refresh itself which is the case with
1059 * registers. Also, only refresh if you are already displaying results */
1060 if (!sw->result_cb && (sw->result_view != NULL))
1061 gnc_search_dialog_display_results (sw);
1062}
1063
1064static void
1065close_handler (gpointer data)
1066{
1067 GNCSearchWindow * sw = data;
1068
1069 g_return_if_fail (sw);
1070 gtk_widget_destroy (sw->dialog);
1071 /* DRH: should sw be freed here? */
1072}
1073
1074static const gchar *
1075type_label_to_new_button(const gchar* type_label)
1076{
1077 if (g_strcmp0(type_label, _("Bill")) == 0)
1078 {
1079 return _("New Bill");
1080 }
1081 else if (g_strcmp0(type_label, _("Customer")) == 0)
1082 {
1083 return _("New Customer");
1084 }
1085 else if (g_strcmp0(type_label, _("Employee")) == 0)
1086 {
1087 return _("New Employee");
1088 }
1089 else if (g_strcmp0(type_label, _("Expense Voucher")) == 0)
1090 {
1091 return _("New Expense Voucher");
1092 }
1093 else if (g_strcmp0(type_label, _("Invoice")) == 0)
1094 {
1095 return _("New Invoice");
1096 }
1097 else if (g_strcmp0(type_label, _("Job")) == 0)
1098 {
1099 return _("New Job");
1100 }
1101 else if (g_strcmp0(type_label, _("Order")) == 0)
1102 {
1103 return _("New Order");
1104 }
1105 else if (g_strcmp0(type_label, _("Transaction")) == 0)
1106 {
1107 return _("New Transaction");
1108 }
1109 else if (g_strcmp0(type_label, _("Split")) == 0)
1110 {
1111 return _("New Split");
1112 }
1113 else if (g_strcmp0(type_label, _("Vendor")) == 0)
1114 {
1115 return _("New Vendor");
1116 }
1117 else
1118 {
1119 PWARN("No translatable new-button label found for search type \"%s\", please add one into dialog-search.c!", type_label);
1120 return C_("Item represents an unknown object type (in the sense of bill, customer, invoice, transaction, split,…)!", "New item");
1121 }
1122}
1123
1124static void
1125gnc_search_dialog_init_widgets (GNCSearchWindow *sw, const gchar *title)
1126{
1127 GtkBuilder *builder;
1128 GtkWidget *label, *add, *box;
1129 GtkComboBoxText *combo_box;
1130 GtkWidget *widget;
1131 GtkWidget *new_item_button;
1132 const char *type_label;
1133 gboolean active;
1134
1135 builder = gtk_builder_new();
1136 gnc_builder_add_from_file (builder, "dialog-search.glade", "search_dialog");
1137
1138 /* Grab the dialog, save the dialog info */
1139 sw->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "search_dialog"));
1140 gtk_window_set_title(GTK_WINDOW(sw->dialog), title);
1141 g_object_set_data (G_OBJECT (sw->dialog), "dialog-info", sw);
1142
1143 // Set the name for this dialog so it can be easily manipulated with css
1144 gtk_widget_set_name (GTK_WIDGET(sw->dialog), "gnc-id-search");
1145 gnc_widget_style_context_add_class (GTK_WIDGET(sw->dialog), "gnc-class-search");
1146
1147 /* Grab the result hbox */
1148 sw->result_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "result_hbox"));
1149
1150 /* Grab the search-table widget */
1151 sw->criteria_table = GTK_WIDGET(gtk_builder_get_object (builder, "criteria_table"));
1152 sw->criteria_scroll_window = GTK_WIDGET(gtk_builder_get_object (builder, "criteria_scroll_window"));
1153
1154 /* Set the type label */
1155 label = GTK_WIDGET(gtk_builder_get_object (builder, "type_label"));
1156 if (sw->type_label)
1157 type_label = sw->type_label;
1158 else
1159 type_label = _(qof_object_get_type_label (sw->search_for));
1160 gtk_label_set_text (GTK_LABEL (label), type_label);
1161
1162 /* Set the 'add criterion' button */
1163 add = gtk_button_new_with_mnemonic (_("_Add"));
1164
1165 g_signal_connect (G_OBJECT (add), "clicked", G_CALLBACK (add_criterion), sw);
1166 box = GTK_WIDGET(gtk_builder_get_object (builder, "add_button_box"));
1167 gtk_box_pack_start (GTK_BOX (box), add, FALSE, FALSE, 3);
1168 gtk_widget_show (add);
1169
1170 /* Set the match-type menu */
1171 sw->grouping_combo = gtk_combo_box_text_new();
1172 combo_box = GTK_COMBO_BOX_TEXT(sw->grouping_combo);
1173 gtk_combo_box_text_append_text(combo_box, _("all criteria are met"));
1174 gtk_combo_box_text_append_text(combo_box, _("any criteria are met"));
1175 gtk_combo_box_set_active(GTK_COMBO_BOX(combo_box), sw->grouping);
1176 g_signal_connect(combo_box, "changed", G_CALLBACK (match_combo_changed), sw);
1177
1178 box = GTK_WIDGET(gtk_builder_get_object (builder, "type_menu_box"));
1179 gtk_box_pack_start (GTK_BOX (box), GTK_WIDGET(combo_box), FALSE, FALSE, 3);
1180 gtk_widget_show(GTK_WIDGET(combo_box));
1181
1182 /* Grab the 'all items match' label */
1183 sw->match_all_label = GTK_WIDGET(gtk_builder_get_object (builder, "match_all_label"));
1184
1185 /* if there's no original query, make the narrow, add, delete buttons inaccessible */
1186 sw->new_rb = GTK_WIDGET(gtk_builder_get_object (builder, "new_search_radiobutton"));
1187 g_signal_connect (sw->new_rb, "toggled",
1188 G_CALLBACK (search_type_cb), sw);
1189 sw->narrow_rb = GTK_WIDGET(gtk_builder_get_object (builder, "narrow_search_radiobutton"));
1190 g_signal_connect (sw->narrow_rb, "toggled",
1191 G_CALLBACK (search_type_cb), sw);
1192 sw->add_rb = GTK_WIDGET(gtk_builder_get_object (builder, "add_search_radiobutton"));
1193 g_signal_connect (sw->add_rb, "toggled",
1194 G_CALLBACK (search_type_cb), sw);
1195 sw->del_rb = GTK_WIDGET(gtk_builder_get_object (builder, "delete_search_radiobutton"));
1196 g_signal_connect (sw->del_rb, "toggled",
1197 G_CALLBACK (search_type_cb), sw);
1198
1199 active = gnc_prefs_get_bool(sw->prefs_group, GNC_PREF_ACTIVE_ONLY);
1200 sw->active_only_check = GTK_WIDGET(gtk_builder_get_object (builder, "active_only_check"));
1201 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sw->active_only_check), active);
1202 g_signal_connect (sw->active_only_check, "toggled",
1203 G_CALLBACK (search_active_only_cb), sw);
1204
1205 /* Figure out if we this object-type has an "active" parameter, and
1206 * if not, then set the active-check button insensitive
1207 */
1208 if (qof_class_get_parameter (sw->search_for, QOF_PARAM_ACTIVE) == NULL)
1209 gtk_widget_set_sensitive (sw->active_only_check, FALSE);
1210
1211 /* Deal with the find button */
1212 widget = GTK_WIDGET(gtk_builder_get_object (builder, "find_button"));
1213 g_signal_connect (widget, "clicked",
1214 G_CALLBACK (search_find_cb), sw);
1215
1216 /* Deal with the cancel button */
1217 sw->cancel_button = GTK_WIDGET(gtk_builder_get_object (builder, "cancel_button"));
1218 g_signal_connect (sw->cancel_button, "clicked",
1219 G_CALLBACK (search_cancel_cb), sw);
1220
1221 /* Deal with the close button */
1222 sw->close_button = GTK_WIDGET(gtk_builder_get_object (builder, "close_button"));
1223 g_signal_connect (sw->close_button, "clicked",
1224 G_CALLBACK (search_cancel_cb), sw);
1225
1226 /* Deal with the new_item button */
1227 new_item_button = GTK_WIDGET(gtk_builder_get_object (builder, "new_item_button"));
1228 gtk_button_set_label (GTK_BUTTON(new_item_button),
1229 type_label_to_new_button(type_label));
1230 g_signal_connect (new_item_button, "clicked",
1231 G_CALLBACK (search_new_item_cb), sw);
1232
1233 /* Deal with the help button */
1234 widget = GTK_WIDGET(gtk_builder_get_object (builder, "help_button"));
1235 g_signal_connect (widget, "clicked",
1236 G_CALLBACK (search_help_cb), sw);
1237
1238 /* add the first criterion */
1239 gnc_search_dialog_add_criterion (sw);
1240
1241 /* register to update criterion/criteria labels based on book option changes
1242 * if searching for splits */
1243 if (strcmp (sw->search_for, GNC_ID_SPLIT) == 0)
1244 gnc_book_option_register_cb(OPTION_NAME_NUM_FIELD_SOURCE,
1245 gnc_search_dialog_book_option_changed, sw);
1246
1247 /* Hide the 'new' button if there is no new_item_cb */
1248 if (!sw->new_item_cb)
1249 gtk_widget_hide (new_item_button);
1250
1251 /* Connect all the signals */
1252 gtk_builder_connect_signals (builder, sw);
1253
1254 /* Register ourselves */
1255 sw->component_id = gnc_register_gui_component (DIALOG_SEARCH_CM_CLASS,
1256 refresh_handler,
1257 close_handler, sw);
1258 gnc_gui_component_set_session (sw->component_id,
1259 gnc_get_current_session());
1260
1261 /* And setup the close callback */
1262 g_signal_connect (G_OBJECT (sw->dialog), "destroy",
1263 G_CALLBACK (gnc_search_dialog_close_cb), sw);
1264
1265 gnc_search_dialog_reset_widgets (sw);
1266 gnc_search_dialog_show_close_cancel (sw);
1267
1268 g_object_unref(G_OBJECT(builder));
1269}
1270
1271void
1272gnc_search_dialog_destroy (GNCSearchWindow *sw)
1273{
1274 if (!sw) return;
1275 if (sw->prefs_group)
1276 gnc_save_window_size(sw->prefs_group, GTK_WINDOW(sw->dialog));
1277 gnc_close_gui_component (sw->component_id);
1278}
1279
1280void
1281gnc_search_dialog_raise (GNCSearchWindow *sw)
1282{
1283 if (!sw) return;
1284 gtk_window_present (GTK_WINDOW(sw->dialog));
1285}
1286
1287GNCSearchWindow *
1288gnc_search_dialog_create (GtkWindow *parent,
1289 QofIdTypeConst obj_type, const gchar *title,
1290 GList *param_list,
1291 GList *display_list,
1292 QofQuery *start_query, QofQuery *show_start_query,
1293 GNCSearchCallbackButton *callbacks,
1294 GNCSearchResultCB result_callback,
1295 GNCSearchNewItemCB new_item_cb,
1296 gpointer user_data, GNCSearchFree free_cb,
1297 const gchar *prefs_group,
1298 const gchar *type_label,
1299 const gchar *style_class)
1300{
1301 GNCSearchWindow *sw = g_new0 (GNCSearchWindow, 1);
1302
1303 g_return_val_if_fail (obj_type, NULL);
1304 g_return_val_if_fail (*obj_type != '\0', NULL);
1305 g_return_val_if_fail (param_list, NULL);
1306
1307 /* Make sure the caller supplies callbacks xor result_callback */
1308 g_return_val_if_fail ((callbacks && !result_callback) ||
1309 (!callbacks && result_callback), NULL);
1310
1311 if (callbacks)
1312 g_return_val_if_fail (display_list, NULL);
1313
1314 sw->search_for = obj_type;
1315 sw->params_list = param_list;
1316 sw->display_list = display_list;
1317 sw->buttons = callbacks;
1318 sw->result_cb = result_callback;
1319 sw->new_item_cb = new_item_cb;
1320 sw->user_data = user_data;
1321 sw->free_cb = free_cb;
1322 sw->prefs_group = prefs_group;
1323 sw->type_label = type_label;
1324
1325 /* Grab the get_guid function */
1326 sw->get_guid = qof_class_get_parameter (sw->search_for, QOF_PARAM_GUID);
1327 if (start_query)
1328 sw->start_q = qof_query_copy (start_query);
1329 sw->q = show_start_query;
1330
1331 gnc_search_dialog_init_widgets (sw, title);
1332 if (sw->prefs_group)
1333 gnc_restore_window_size(sw->prefs_group, GTK_WINDOW(sw->dialog), parent);
1334 gtk_window_set_transient_for(GTK_WINDOW(sw->dialog), parent);
1335 gtk_widget_show(sw->dialog);
1336
1337 /* For some reason on Ubuntu 18.04 that uses Gtk3.22.30 and maybe others we
1338 * have to set the scroll window content min height after the dialog has been
1339 * shown to get the correct scroll window height */
1340 gtk_scrolled_window_set_min_content_height (GTK_SCROLLED_WINDOW(
1341 sw->criteria_scroll_window),
1342 gtk_widget_get_allocated_height (
1343 GTK_WIDGET(sw->grouping_combo)) * 1.5);
1344
1345 // Add a style context for this dialog so it can be easily manipulated with css
1346 if (style_class != NULL)
1347 gnc_widget_style_context_add_class (GTK_WIDGET(sw->dialog), style_class);
1348
1349 /* Maybe display the original query results? */
1350 if (callbacks && show_start_query)
1351 {
1352 gnc_search_dialog_reset_widgets (sw);
1353 gnc_search_dialog_display_results (sw);
1354 }
1355
1356 return sw;
1357}
1358
1359/* Register an on-close signal with the Search Dialog */
1360guint gnc_search_dialog_connect_on_close (GNCSearchWindow *sw,
1361 GCallback func,
1362 gpointer user_data)
1363{
1364 g_return_val_if_fail (sw, 0);
1365 g_return_val_if_fail (func, 0);
1366 g_return_val_if_fail (user_data, 0);
1367
1368 return g_signal_connect (G_OBJECT (sw->dialog), "destroy",
1369 func, user_data);
1370
1371}
1372
1373/* Un-register the signal handlers with the Search Dialog */
1374void gnc_search_dialog_disconnect (GNCSearchWindow *sw, gpointer user_data)
1375{
1376 g_return_if_fail (sw);
1377 g_return_if_fail (user_data);
1378
1379 g_signal_handlers_disconnect_matched (sw->dialog, G_SIGNAL_MATCH_DATA,
1380 0, 0, NULL, NULL, user_data);
1381}
1382
1383/* Clear all callbacks with this Search Window */
1384void gnc_search_dialog_set_select_cb (GNCSearchWindow *sw,
1385 GNCSearchSelectedCB selected_cb,
1386 gpointer user_data,
1387 gboolean allow_clear)
1388{
1389 g_return_if_fail (sw);
1390
1391 sw->selected_cb = selected_cb;
1392 sw->select_arg = user_data;
1393 sw->allow_clear = allow_clear;
1394
1395 /* Show or hide the select button */
1396 if (sw->select_button)
1397 {
1398 if (selected_cb)
1399 gtk_widget_show (sw->select_button);
1400 else
1401 gtk_widget_hide (sw->select_button);
1402 }
1403
1404 /* Show the proper close/cancel button */
1405 gnc_search_dialog_show_close_cancel (sw);
1406}
1407
1408
1409/* TEST CODE BELOW HERE */
1410
1411static GList *
1412get_params_list (QofIdTypeConst type)
1413{
1414 GList *list = NULL;
1415
1416 list = gnc_search_param_prepend (list, "Txn: All Accounts",
1418 type, SPLIT_TRANS, TRANS_SPLITLIST,
1419 SPLIT_ACCOUNT_GUID, NULL);
1420 list = gnc_search_param_prepend (list, "Split Account", GNC_ID_ACCOUNT,
1421 type, SPLIT_ACCOUNT, QOF_PARAM_GUID,
1422 NULL);
1423 list = gnc_search_param_prepend (list, "Split->Txn->Void?", NULL, type,
1424 SPLIT_TRANS, TRANS_VOID_STATUS, NULL);
1425 list = gnc_search_param_prepend (list, "Split Int64", NULL, type,
1426 "d-share-int64", NULL);
1427 list = gnc_search_param_prepend (list, "Split Amount (double)", NULL, type,
1428 "d-share-amount", NULL);
1429 list = gnc_search_param_prepend (list, "Split Value (debcred)", NULL, type,
1430 SPLIT_VALUE, NULL);
1431 list = gnc_search_param_prepend (list, "Split Amount (numeric)", NULL, type,
1432 SPLIT_AMOUNT, NULL);
1433 list = gnc_search_param_prepend (list, "Date Reconciled (date)", NULL, type,
1434 SPLIT_DATE_RECONCILED, NULL);
1435 list = gnc_search_param_prepend (list, "Split Memo (string)", NULL, type,
1436 SPLIT_MEMO, NULL);
1437
1438 return list;
1439}
1440
1441static GList *
1442get_display_list (QofIdTypeConst type)
1443{
1444 GList *list = NULL;
1445
1446 list = gnc_search_param_prepend (list, "Amount", NULL, type, SPLIT_AMOUNT,
1447 NULL);
1448 list = gnc_search_param_prepend (list, "Memo", NULL, type, SPLIT_MEMO, NULL);
1449 list = gnc_search_param_prepend (list, "Date", NULL, type, SPLIT_TRANS,
1450 TRANS_DATE_POSTED, NULL);
1451
1452 return list;
1453}
1454
1455
1456static void
1457do_nothing (GtkWindow *dialog, gpointer *a, gpointer b)
1458{
1459 return;
1460}
1461
1462void
1463gnc_search_dialog_test (void)
1464{
1465 static GList *params = NULL;
1466 static GList *display = NULL;
1467 static GNCSearchCallbackButton buttons[] =
1468 {
1469 /* Don't mark these as translatable since these are only test strings! */
1470 { ("View Split"), do_nothing, NULL, TRUE },
1471 { ("New Split"), do_nothing, NULL, TRUE },
1472 { ("Do Something"), do_nothing, NULL, TRUE },
1473 { ("Do Nothing"), do_nothing, NULL, TRUE },
1474 { ("Who Cares?"), do_nothing, NULL, FALSE },
1475 { NULL }
1476 };
1477
1478 if (params == NULL)
1479 params = get_params_list (GNC_ID_SPLIT);
1480
1481 if (display == NULL)
1482 display = get_display_list (GNC_ID_SPLIT);
1483
1484/* FIXME: All this does is leak. */
1485 gnc_search_dialog_create (NULL, GNC_ID_SPLIT,
1486 _("Find Transaction"),
1487 params, display,
1488 NULL, NULL, buttons, NULL, NULL, NULL, NULL,
1489 NULL, NULL, NULL);
1490}
API for Transactions and Splits (journal entries)
Generic api to store and retrieve preferences.
utility functions for the GnuCash UI
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition qofbook.cpp:497
const QofParam * qof_class_get_parameter(QofIdTypeConst obj_name, const char *parameter)
Return the registered Parameter Definition for the requested parameter.
Definition qofclass.cpp:136
#define ACCOUNT_MATCH_ALL_TYPE
This is the type-override when you want to match all accounts.
Definition Account.h:1754
const gchar * QofIdTypeConst
QofIdTypeConst declaration.
Definition qofid.h:82
void gnc_gnome_help(GtkWindow *parent, const char *file_name, const char *anchor)
Launch the systems default help browser, gnome's yelp for linux, and open to a given link within a gi...
#define PWARN(format, args...)
Log a warning.
Definition qoflog.h:250
const char * qof_object_get_type_label(QofIdTypeConst type_name)
Get the printable label for a type.
gdouble gnc_prefs_get_float(const gchar *group, const gchar *pref_name)
Get an float value from the preferences backend.
gboolean gnc_prefs_set_bool(const gchar *group, const gchar *pref_name, gboolean value)
Store a boolean value into the preferences backend.
gboolean gnc_prefs_get_bool(const gchar *group, const gchar *pref_name)
Get a boolean value from the preferences backend.
void qof_query_add_term(QofQuery *q, QofQueryParamList *param_list, QofQueryPredData *pred_data, QofQueryOp op)
This is the general function that adds a new Query Term to a query.
Definition qofquery.cpp:681
void qof_query_add_guid_match(QofQuery *q, QofQueryParamList *param_list, const GncGUID *guid, QofQueryOp op)
DOCUMENT ME !!
QofQuery * qof_query_merge(QofQuery *q1, QofQuery *q2, QofQueryOp op)
Combine two queries together using the Boolean set (logical) operator 'op'.
QofQueryPredData * qof_query_core_predicate_copy(const QofQueryPredData *pdata)
Copy a predicate.
QofQuery * qof_query_copy(QofQuery *q)
Make a copy of the indicated query.
void qof_query_core_predicate_free(QofQueryPredData *pdata)
Destroy a predicate.
void qof_query_set_book(QofQuery *query, QofBook *book)
Set the book to be searched.
QofQuery * qof_query_invert(QofQuery *q)
Make a copy of the indicated query, inverting the sense of the search.
void qof_query_add_boolean_match(QofQuery *q, QofQueryParamList *param_list, gboolean value, QofQueryOp op)
Handy-dandy convenience routines, avoids having to create a separate predicate for boolean matches.
void qof_query_purge_terms(QofQuery *q, QofQueryParamList *param_list)
Remove query terms of a particular type from q.
Definition qofquery.cpp:705
QofQueryOp
Query Term Operators, for combining Query Terms.
Definition qofquery.h:93
void qof_query_destroy(QofQuery *query)
Frees the resources associate with a Query object.
#define SPLIT_ACCOUNT_GUID
for guid_match_all
Definition Split.h:571
gboolean sensitive_if_readonly
TRUE if this action should be sensitive even in a read-only book.
The type used to store guids in C.
Definition guid.h:75
A Query.
Definition qofquery.cpp:75
gint cols
The number of columns and rows in the grid.
GtkGrid * grid
The grid being sized.