GnuCash  5.6-150-g038405b370+
dialog-dup-trans.c
1 /********************************************************************\
2  * dialog-dup-trans.c -- duplicate transaction dialog *
3  * Copyright (C) 2001 Gnumatic, Inc. *
4  * Author: Dave Peticolas <dave@krondo.com> *
5  * *
6  * This program is free software; you can redistribute it and/or *
7  * modify it under the terms of the GNU General Public License as *
8  * published by the Free Software Foundation; either version 2 of *
9  * the License, or (at your option) any later version. *
10  * *
11  * This program is distributed in the hope that it will be useful, *
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14  * GNU General Public License for more details. *
15  * *
16  * You should have received a copy of the GNU General Public License*
17  * along with this program; if not, contact: *
18  * *
19  * Free Software Foundation Voice: +1-617-542-5942 *
20  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
21  * Boston, MA 02110-1301, USA gnu@gnu.org *
22 \********************************************************************/
23 
24 #include <config.h>
25 
26 #include <gtk/gtk.h>
27 #include <glib/gi18n.h>
28 #include <time.h>
29 #include <stdlib.h>
30 
31 #include "dialog-dup-trans.h"
32 #include "dialog-utils.h"
33 #include "gnc-date-edit.h"
34 #include "qof.h"
35 
36 /* This static indicates the debugging module that this .o belongs to. */
37 G_GNUC_UNUSED static QofLogModule log_module = G_LOG_DOMAIN;
38 
39 typedef struct
40 {
41  GtkWidget * dialog;
42 
43  gboolean focus_out;
44 
45  GtkWidget * date_edit;
46  GtkWidget * num_edit;
47  GtkWidget * tnum_edit;
48  GtkWidget * link_edit;
49 
50  GtkWidget *duplicate_title_label; // GtkLabel
51  GtkWidget *duplicate_table; // GtkTable
52  GtkWidget *date_label; // GtkLabel
53  GtkWidget *num_label; // GtkLabel
54  GtkWidget *tnum_label; // GtkLabel
55  GtkWidget *link_label; // GtkLabel
57 
58 /* Parses the string value and returns true if it is a
59  * number. In that case, *num is set to the value parsed.
60  * Copied from numcell.c */
61 static gboolean
62 parse_num (const char *string, long int *num)
63 {
64  long int number;
65 
66  if (string == NULL)
67  return FALSE;
68 
69  if (!gnc_strisnum (string))
70  return FALSE;
71 
72  number = strtol (string, NULL, 10);
73 
74  if ((number == LONG_MIN) || (number == LONG_MAX))
75  return FALSE;
76 
77  if (num != NULL)
78  *num = number;
79 
80  return TRUE;
81 }
82 
83 static gboolean
84 gnc_dup_inc_dec (GtkWidget *widget, const gchar *text, gint inc_dec)
85 {
86  long int num;
87 
88  if (parse_num (text, &num))
89  {
90  gchar *format;
91  gchar *out;
92  num = num + inc_dec;
93 
94  if (num == -1)
95  num = 0;
96 
97  if (g_str_has_prefix (text, "0"))
98  format = g_strdup_printf ("%s%ld%s", "%0", g_utf8_strlen (text, -1), "d");
99  else
100  format = g_strdup_printf ("%s", "%ld");
101 
102  out = g_strdup_printf (format, num);
103 
104  gtk_entry_set_text (GTK_ENTRY(widget), out);
105  g_free (format);
106  g_free (out);
107  return TRUE;
108  }
109  return FALSE;
110 }
111 
112 static gboolean
113 gnc_dup_key_press_event_cb (GtkWidget *widget, GdkEventKey *event, gpointer user_data)
114 {
115  const gchar *text = gtk_entry_get_text (GTK_ENTRY(widget));
116 
117  if (gnc_strisnum (text))
118  {
119  GdkModifierType modifiers = gtk_accelerator_get_default_mod_mask ();
120  gint increment;
121 
122  if ((event->state & modifiers) == GDK_CONTROL_MASK ||
123  (event->state & modifiers) == GDK_MOD1_MASK)
124  return FALSE;
125 
126  /* See https://bugs.gnucash.org/show_bug.cgi?id=798386 for semicolon */
127  if (event->keyval == GDK_KEY_plus || event->keyval == GDK_KEY_KP_Add ||
128  event->keyval == GDK_KEY_semicolon)
129  increment = 1;
130  else if (event->keyval == GDK_KEY_minus || event->keyval == GDK_KEY_KP_Subtract)
131  increment = -1;
132  else
133  return FALSE;
134 
135  return gnc_dup_inc_dec (widget, text, increment);
136 
137  }
138  else
139  return FALSE;
140 }
141 
142 static void
143 gnc_dup_trans_dialog_create (GtkWidget * parent, DupTransDialog *dt_dialog,
144  gboolean show_date, time64 date,
145  const char *num_str, const char *tnum_str)
146 {
147  GtkWidget *dialog;
148  GtkBuilder *builder;
149  const gchar *tt = _("You can type '+' or '-' to increment or decrement the number.");
150 
151  builder = gtk_builder_new ();
152  gnc_builder_add_from_file (builder, "gnc-plugin-page-register.glade", "num_adjustment");
153  gnc_builder_add_from_file (builder, "gnc-plugin-page-register.glade", "tnum_adjustment");
154  gnc_builder_add_from_file (builder, "gnc-plugin-page-register.glade", "duplicate_transaction_dialog");
155 
156  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "duplicate_transaction_dialog"));
157  dt_dialog->dialog = dialog;
158 
159  // Set the name for this dialog so it can be easily manipulated with css
160  gtk_widget_set_name (GTK_WIDGET(dialog), "gnc-id-duplicate-transaction");
161  gnc_widget_style_context_add_class (GTK_WIDGET(dialog), "gnc-class-transactions");
162 
163  /* parent */
164  if (parent != NULL)
165  gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(parent));
166 
167  /* date widget */
168  dt_dialog->date_label = GTK_WIDGET(gtk_builder_get_object (builder, "date_label"));
169  if (show_date)
170  {
171  GtkWidget *date_edit;
172  GtkWidget *hbox;
173 
174  date_edit = gnc_date_edit_new (date, FALSE, FALSE);
175  gnc_date_activates_default (GNC_DATE_EDIT(date_edit), TRUE);
176  hbox = GTK_WIDGET(gtk_builder_get_object (builder, "date_hbox"));
177  gtk_widget_show (date_edit);
178 
179  gnc_date_make_mnemonic_target (GNC_DATE_EDIT(date_edit), dt_dialog->date_label);
180 
181  gtk_box_pack_end (GTK_BOX(hbox), date_edit, TRUE, TRUE, 0);
182  dt_dialog->date_edit = date_edit;
183  }
184  else
185  {
186  GtkWidget *date_edit;
187  date_edit = gnc_date_edit_new (date, FALSE, FALSE);
188  dt_dialog->date_edit = date_edit;
189  }
190 
191  dt_dialog->duplicate_title_label = GTK_WIDGET(gtk_builder_get_object (builder, "duplicate_title_label"));
192  dt_dialog->duplicate_table = GTK_WIDGET(gtk_builder_get_object (builder, "duplicate_table"));
193  dt_dialog->num_label = GTK_WIDGET(gtk_builder_get_object (builder, "num_label"));
194  dt_dialog->tnum_label = GTK_WIDGET(gtk_builder_get_object (builder, "tnum_label"));
195 
196  dt_dialog->num_edit = GTK_WIDGET(gtk_builder_get_object (builder, "num_entry"));
197  dt_dialog->tnum_edit = GTK_WIDGET(gtk_builder_get_object (builder, "tnum_entry"));
198 
199  if (num_str)
200  gtk_entry_set_text (GTK_ENTRY(dt_dialog->num_edit), num_str);
201  if (tnum_str)
202  gtk_entry_set_text (GTK_ENTRY(dt_dialog->tnum_edit), tnum_str);
203 
204  g_signal_connect (dt_dialog->num_edit, "key-press-event",
205  G_CALLBACK(gnc_dup_key_press_event_cb),
206  dt_dialog);
207 
208  g_signal_connect (dt_dialog->tnum_edit, "key-press-event",
209  G_CALLBACK(gnc_dup_key_press_event_cb),
210  dt_dialog);
211 
212  if (gnc_strisnum (num_str))
213  {
214  gtk_widget_set_tooltip_text (GTK_WIDGET(dt_dialog->num_edit), tt);
215  gnc_dup_inc_dec (GTK_WIDGET(dt_dialog->num_edit), num_str, 1);
216  }
217  if (gnc_strisnum (tnum_str))
218  {
219  gtk_widget_set_tooltip_text (GTK_WIDGET(dt_dialog->tnum_edit), tt);
220  gnc_dup_inc_dec (GTK_WIDGET(dt_dialog->tnum_edit), tnum_str, 1);
221  }
222 
223  /* Transaction Linked Document */
224  {
225  dt_dialog->link_label = GTK_WIDGET(gtk_builder_get_object (builder, "link_label"));
226  dt_dialog->link_edit = GTK_WIDGET(gtk_builder_get_object (builder, "link_check_button"));
227  }
228 
229  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, dt_dialog);
230 
231  g_object_unref (G_OBJECT(builder));
232 }
233 
234 static gboolean
235 gnc_dup_trans_dialog_internal (GtkWidget * parent,
236  const char* window_title, const char* title,
237  gboolean show_date, time64 *date_p, GDate *gdate_p,
238  const char *num, char **out_num,
239  const char *tnum, char **out_tnum,
240  const char *tlink, char **out_tlink)
241 {
242  DupTransDialog *dt_dialog;
243  GtkWidget *entry;
244  gboolean ok;
245  gint result;
246 
247  dt_dialog = g_new0 (DupTransDialog, 1);
248 
249  gnc_dup_trans_dialog_create (parent, dt_dialog, show_date,
250  *date_p, num, tnum);
251 
252  if (!show_date)
253  {
254  // The "date" field isn't being asked for, so we make the widgets invisible
255  gtk_widget_set_visible (dt_dialog->date_label, FALSE);
256  if (dt_dialog->date_edit)
257  gtk_widget_set_visible (dt_dialog->date_edit, FALSE);
258  // If no "date" field, there must be a "num" field, so give it focus
259  if (out_num)
260  gtk_widget_grab_focus (dt_dialog->num_edit);
261  }
262  else
263  {
264  GNCDateEdit *gde;
265 
266  gde = GNC_DATE_EDIT(dt_dialog->date_edit);
267  entry = gde->date_entry;
268  gtk_widget_grab_focus (entry);
269  }
270 
271  if (window_title)
272  gtk_window_set_title (GTK_WINDOW(dt_dialog->dialog), window_title);
273 
274  if (title)
275  {
276  gchar *full_text = g_strdup_printf ("<b>%s</b>", title);
277  gtk_label_set_markup (GTK_LABEL(dt_dialog->duplicate_title_label), full_text);
278  g_free (full_text);
279  }
280 
281  if (!out_num)
282  {
283  // The "num" field isn't being asked for, so we make the widgets invisible
284  gtk_widget_set_visible (dt_dialog->num_label, FALSE);
285  gtk_widget_set_visible (dt_dialog->num_edit, FALSE);
286  }
287 
288  if (!tnum)
289  {
290  // The "tnum" field isn't being asked for, so we make the widgets invisible
291  gtk_widget_set_visible (dt_dialog->tnum_label, FALSE);
292  gtk_widget_set_visible (dt_dialog->tnum_edit, FALSE);
293  }
294 
295  if (!show_date && !tnum)
296  {
297  // The "date" and the "tnum" fields aren't being asked for, this is a split copy
298  gtk_label_set_markup (GTK_LABEL(dt_dialog->num_label), _("Action/Number"));
299  }
300 
301  if (tnum)
302  {
303  gtk_entry_set_activates_default (GTK_ENTRY(dt_dialog->num_edit), FALSE);
304  gtk_entry_set_activates_default (GTK_ENTRY(dt_dialog->tnum_edit), TRUE);
305  }
306 
307  if (tlink)
308  {
309  gtk_widget_set_visible (dt_dialog->link_label, TRUE);
310  gtk_widget_set_visible (dt_dialog->link_edit, TRUE);
311  }
312  else
313  {
314  gtk_widget_set_visible (dt_dialog->link_label, FALSE);
315  gtk_widget_set_visible (dt_dialog->link_edit, FALSE);
316  }
317 
318  result = gtk_dialog_run (GTK_DIALOG(dt_dialog->dialog));
319 
320  if (result == GTK_RESPONSE_OK)
321  {
322  if (date_p)
323  *date_p = gnc_date_edit_get_date (GNC_DATE_EDIT(dt_dialog->date_edit));
324  if (gdate_p)
325  gnc_date_edit_get_gdate (GNC_DATE_EDIT(dt_dialog->date_edit), gdate_p);
326  if (out_num)
327  *out_num = g_strdup (gtk_entry_get_text (GTK_ENTRY(dt_dialog->num_edit)));
328  if (tnum)
329  *out_tnum = g_strdup (gtk_entry_get_text (GTK_ENTRY(dt_dialog->tnum_edit)));
330  if (tlink)
331  {
332  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(dt_dialog->link_edit)))
333  *out_tlink = g_strdup (tlink);
334  }
335  ok = TRUE;
336  }
337  else
338  ok = FALSE;
339 
340  gtk_widget_destroy (GTK_WIDGET(dt_dialog->dialog));
341  g_free (dt_dialog);
342 
343  return ok;
344 }
345 
346 gboolean
347 gnc_dup_trans_dialog (GtkWidget * parent, const char* title,
348  gboolean show_date, time64 *date_p,
349  const char *num, char **out_num,
350  const char *tnum, char **out_tnum,
351  const char *tlink, char **out_tlink)
352 {
353  return gnc_dup_trans_dialog_internal (parent, NULL, title,
354  show_date, date_p, NULL,
355  num, out_num, tnum, out_tnum,
356  tlink, out_tlink);
357 }
358 
359 gboolean
360 gnc_dup_trans_dialog_gdate (GtkWidget * parent, GDate *gdate_p,
361  const char *num, char **out_num)
362 {
363  time64 tmp_time;
364  g_assert (gdate_p);
365 
366  tmp_time = gdate_to_time64 (*gdate_p);
367  return gnc_dup_trans_dialog_internal (parent, NULL, NULL, TRUE,
368  &tmp_time, gdate_p,
369  num, out_num, NULL, NULL,
370  NULL, NULL);
371 }
372 
373 gboolean
374 gnc_dup_time64_dialog (GtkWidget * parent, const char *window_title,
375  const char* title, time64 *date)
376 {
377  return gnc_dup_trans_dialog_internal (parent, window_title, title, TRUE,
378  date, NULL,
379  NULL, NULL, NULL, NULL,
380  NULL, NULL);
381 }
382 
383 gboolean
384 gnc_dup_date_dialog (GtkWidget * parent, const char* title, GDate *gdate_p)
385 {
386  time64 tmp_time;
387  g_assert (gdate_p);
388 
389  tmp_time = gdate_to_time64 (*gdate_p);
390  return gnc_dup_trans_dialog_internal (parent, NULL, title, TRUE,
391  &tmp_time, gdate_p,
392  NULL, NULL, NULL, NULL,
393  NULL, NULL);
394 }
395 
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
gboolean gnc_strisnum(const gchar *s)
Returns true if string s is a number, possibly surrounded by whitespace.
Definition: qofutil.cpp:187
time64 gdate_to_time64(GDate d)
Turns a GDate into a time64, returning the first second of the day.
Definition: gnc-date.cpp:1253
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87