GnuCash  5.6-150-g038405b370+
dialog-doclink.c
1 /********************************************************************\
2  * dialog-doclink.c -- Document link dialog *
3  * Copyright (C) 2020 Robert Fewell *
4  * *
5  * This program is free software; you can redistribute it and/or *
6  * modify it under the terms of the GNU General Public License as *
7  * published by the Free Software Foundation; either version 2 of *
8  * the License, or (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License*
16  * along with this program; if not, contact: *
17  * *
18  * Free Software Foundation Voice: +1-617-542-5942 *
19  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
20  * Boston, MA 02110-1301, USA gnu@gnu.org *
21 \********************************************************************/
22 
23 #include <config.h>
24 
25 #include <gtk/gtk.h>
26 #include <glib/gi18n.h>
27 
28 #include "dialog-doclink.h"
29 #include "dialog-doclink-utils.h"
30 
31 #include "dialog-utils.h"
32 #include "gnc-component-manager.h"
33 #include "gnc-session.h"
34 #include "Transaction.h"
35 
38 #include "gnc-main-window.h"
39 #include "gnc-prefs.h"
40 #include "gnc-ui.h"
41 #include "gnc-ui-util.h"
42 #include "gnc-gnome-utils.h"
43 #include "gnc-uri-utils.h"
44 #include "gnc-filepath-utils.h"
45 #include "Account.h"
46 #include "dialog-invoice.h"
47 
48 #define DIALOG_DOCLINK_CM_CLASS "dialog-doclink"
49 #define GNC_PREFS_GROUP_BUS "dialogs.business-doclink"
50 #define GNC_PREFS_GROUP_TRANS "dialogs.trans-doclink"
51 
53 enum GncDoclinkColumn
54 {
55  DATE_ITEM,
56  DATE_INT64, // used just for sorting date_trans
57  DESC_ID,
58  DESC_ITEM,
59  DISPLAY_URI,
60  AVAILABLE,
61  ITEM_POINTER,
62  URI,
63  URI_RELATIVE, // used just for sorting relative_pix
64  URI_RELATIVE_PIX
65 };
66 
67 typedef struct
68 {
69  GtkWidget *window;
70  GtkWidget *view;
71  GtkWidget *path_head_label;
72  GtkWidget *total_entries_label;
73  gchar *path_head;
74  gboolean is_list_trans;
75  gboolean book_ro;
76  GtkTreeModel *model;
77  gint component_id;
78  QofSession *session;
80 
81 /* This static indicates the debugging module that this .o belongs to. */
82 static QofLogModule log_module = GNC_MOD_GUI;
83 
84 /* =================================================================== */
85 
86 void
87 gnc_doclink_open_uri (GtkWindow *parent, const gchar *uri)
88 {
89  if (uri && *uri)
90  {
91  gchar *scheme = gnc_uri_get_scheme (uri);
92  gchar *path_head = gnc_doclink_get_path_head ();
93  gchar *run_uri = gnc_doclink_get_use_uri (path_head, uri, scheme);
94  gchar *run_scheme = gnc_uri_get_scheme (run_uri);
95 
96  PINFO("Open uri scheme is '%s', uri is '%s'", run_scheme, run_uri);
97 
98  if (run_scheme) // make sure we have a scheme entry
99  {
100  gnc_launch_doclink (GTK_WINDOW (parent), run_uri);
101  g_free (run_scheme);
102  }
103  g_free (run_uri);
104  g_free (path_head);
105  g_free (scheme);
106  }
107 }
108 
109 /* =================================================================== */
110 
111 static void
112 location_ok_cb (GtkEntry *entry, gpointer user_data)
113 {
114  GtkWidget *ok_button = user_data;
115  gboolean have_scheme = FALSE;
116  const gchar *text = gtk_entry_get_text (entry);
117  GtkWidget *warning_hbox = g_object_get_data (G_OBJECT(entry), "whbox");
118 
119  if (text && *text)
120  {
121  gchar *scheme = gnc_uri_get_scheme (text);
122 
123  if (scheme)
124  have_scheme = TRUE;
125  g_free (scheme);
126  }
127  gtk_widget_set_visible (warning_hbox, !have_scheme);
128  gtk_widget_set_sensitive (ok_button, have_scheme);
129 }
130 
131 static void
132 file_ok_cb (GtkButton *button, GtkWidget *ok_button)
133 {
134  const gchar *uri = g_object_get_data (G_OBJECT(button), "uri");
135  gboolean file_true = FALSE;
136 
137  if (uri)
138  {
139  gchar *full_filename = gnc_uri_get_path (uri);
140 
141  /* Test for a valid filename and not a directory */
142  if (full_filename && !g_file_test (full_filename, G_FILE_TEST_IS_DIR))
143  file_true = TRUE;
144 
145  g_free (full_filename);
146  }
147  gtk_widget_set_sensitive (ok_button, file_true);
148 }
149 
150 static void
151 fcb_clicked_cb (GtkButton *button, GtkWidget *ok_button)
152 {
153  GtkWidget *dialog = gtk_widget_get_toplevel (GTK_WIDGET(button));
154  GtkWidget *label = g_object_get_data (G_OBJECT(button), "fcb_label");
155  const gchar *path_head = g_object_get_data (G_OBJECT(button), "path_head");
156  const gchar *uri = g_object_get_data (G_OBJECT(button), "uri");
157  GtkFileChooserNative *native;
158  gint res;
159 
160  native = gtk_file_chooser_native_new (_("Select document"),
161  GTK_WINDOW(dialog),
162  GTK_FILE_CHOOSER_ACTION_OPEN,
163  _("_OK"),
164  _("_Cancel"));
165 
166  if (uri && *uri)
167  {
168  gchar *scheme = gnc_uri_get_scheme (uri);
169  gchar *full_filename = gnc_doclink_get_unescape_uri (path_head, uri, scheme);
170  gchar *path = g_path_get_dirname (full_filename);
171  gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(native), path);
172  g_free (full_filename);
173  g_free (scheme);
174  g_free (path);
175  }
176  else if (path_head)
177  gtk_file_chooser_set_current_folder_uri (GTK_FILE_CHOOSER(native), path_head);
178 
179  res = gtk_native_dialog_run (GTK_NATIVE_DIALOG(native));
180  if (res == GTK_RESPONSE_ACCEPT)
181  {
182  gchar *uri = gtk_file_chooser_get_uri (GTK_FILE_CHOOSER(native));
183 
184  if (uri && *uri)
185  {
186  gchar *filename = g_path_get_basename (uri);
187  gchar *unescape_filename = g_uri_unescape_string (filename, NULL);
188  gtk_label_set_text (GTK_LABEL(label), unescape_filename);
189 
190  DEBUG("Native file uri is '%s'", uri);
191 
192  g_object_set_data_full (G_OBJECT(button), "uri", g_strdup (uri), g_free);
193  g_free (filename);
194  g_free (unescape_filename);
195  }
196  g_free (uri);
197  file_ok_cb (button, ok_button);
198  }
199  g_object_unref (native);
200 }
201 
202 static void
203 uri_type_selected_cb (GtkToggleButton *button, GtkWidget *widget)
204 {
205  GtkWidget *top = gtk_widget_get_toplevel (widget);
206  GtkWidget *parent_hbox = gtk_widget_get_parent (widget);
207  GtkWidget *ok_button = g_object_get_data (G_OBJECT(widget), "okbut");
208  gboolean active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button));
209 
210  // set the visibility of the parent hbox for widget
211  gtk_widget_set_visible (parent_hbox, active);
212 
213  // make the window resize after hiding widgets
214  if (active)
215  {
216  if (g_strcmp0 (gtk_buildable_get_name (
217  GTK_BUILDABLE(parent_hbox)), "location_hbox") == 0)
218  location_ok_cb (GTK_ENTRY (widget), ok_button);
219  else
220  file_ok_cb (GTK_BUTTON(widget), ok_button);
221 
222  gtk_window_resize (GTK_WINDOW(top), 600, 10); // width, height
223  }
224 }
225 
226 static void
227 setup_location_dialog (GtkBuilder *builder, GtkWidget *button_loc, const gchar *uri)
228 {
229  GtkLabel *location_label = GTK_LABEL(gtk_builder_get_object (builder, "location_label"));
230  GtkEntry *entry = GTK_ENTRY(gtk_builder_get_object (builder, "location_entry"));
231  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button_loc), TRUE);
232 
233  // set entry settings
234  gtk_entry_set_width_chars (entry, 80);
235  gtk_entry_set_activates_default (entry, TRUE);
236  gtk_widget_grab_focus (GTK_WIDGET(entry));
237 
238  // update label and set entry text if required
239  if (uri)
240  {
241  gtk_label_set_text (location_label, _("Amend the URL"));
242  gtk_entry_set_text (entry, uri);
243  }
244  else
245  {
246  gchar *enter_uri = g_strdup_printf (_("Enter an URL like \"%s\""),
247  PACKAGE_URL);
248  gtk_label_set_text (location_label, enter_uri);
249  g_free (enter_uri);
250  }
251 }
252 
253 static void
254 setup_file_dialog (GtkBuilder *builder, const gchar *path_head, const gchar *uri, gchar *scheme)
255 {
256  GtkWidget *fcb = GTK_WIDGET(gtk_builder_get_object (builder, "file_chooser_button"));
257  gchar *display_uri = gnc_doclink_get_unescape_uri (path_head, uri, scheme);
258 
259  if (display_uri)
260  {
261  GtkWidget *existing_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "existing_hbox"));
262  GtkWidget *image = gtk_image_new_from_icon_name ("dialog-warning", GTK_ICON_SIZE_SMALL_TOOLBAR);
263  gchar *use_uri = gnc_doclink_get_use_uri (path_head, uri, scheme);
264  gchar *uri_label = g_strdup_printf ("%s \"%s\"", _("Existing Document Link is"), display_uri);
265  GtkWidget *label = gtk_label_new (uri_label);
266 
267  if (g_file_test (display_uri, G_FILE_TEST_EXISTS))
268  gtk_box_pack_start (GTK_BOX(existing_hbox), label, FALSE, TRUE, 0);
269  else
270  {
271  gtk_box_pack_start (GTK_BOX(existing_hbox), image, FALSE, FALSE, 0);
272  gtk_box_pack_start (GTK_BOX(existing_hbox), label, FALSE, TRUE, 0);
273  }
274 
275  PINFO("Path head: '%s', URI: '%s', Filename: '%s'", path_head, uri, display_uri);
276 
277  gtk_label_set_ellipsize (GTK_LABEL(label), PANGO_ELLIPSIZE_START);
278 
279  // Set the style context for this label so it can be easily manipulated with css
280  gnc_widget_style_context_add_class (GTK_WIDGET(label), "gnc-class-highlight");
281  gtk_widget_show_all (existing_hbox);
282 
283  g_free (uri_label);
284  g_free (use_uri);
285  }
286  g_object_set_data_full (G_OBJECT(fcb), "path_head", g_strdup (path_head), g_free);
287  gtk_widget_grab_focus (GTK_WIDGET(fcb));
288  g_free (display_uri);
289 }
290 
291 static gboolean
292 gnc_doclink_get_uri_event_cb (GtkWidget *widget, GdkEventKey *event,
293  gpointer user_data)
294 {
295  if (event->keyval == GDK_KEY_Escape)
296  {
297  gtk_dialog_response (GTK_DIALOG(widget),
298  GTK_RESPONSE_CANCEL);
299  return TRUE;
300  }
301  return FALSE;
302 }
303 
304 gchar *
305 gnc_doclink_get_uri_dialog (GtkWindow *parent, const gchar *title,
306  const gchar *uri)
307 {
308  GtkWidget *dialog, *button_loc, *button_file, *ok_button, *warning_hbox;
309  GtkBuilder *builder;
310  gboolean uri_is_file, have_uri = FALSE;
311  GtkEntry *entry;
312  GtkWidget *fcb;
313  GtkWidget *fcb_label;
314  GtkWidget *head_label;
315  int result;
316  gchar *ret_uri = NULL;
317  gchar *path_head = gnc_doclink_get_path_head ();
318  gchar *scheme = NULL;
319 
320  /* Create the dialog box */
321  builder = gtk_builder_new();
322  gnc_builder_add_from_file (builder, "dialog-doclink.glade",
323  "linked_doc_dialog");
324  dialog = GTK_WIDGET(gtk_builder_get_object (builder, "linked_doc_dialog"));
325  gtk_window_set_title (GTK_WINDOW(dialog), title);
326 
327  if (parent != NULL)
328  gtk_window_set_transient_for (GTK_WINDOW(dialog), GTK_WINDOW(parent));
329 
330  // Set the name and style context for this widget so it can be easily manipulated with css
331  gtk_widget_set_name (GTK_WIDGET(dialog), "gnc-id-doclink");
332  gnc_widget_style_context_add_class (GTK_WIDGET(dialog), "gnc-class-doclink");
333 
334  // Use this event to capture the escape key being pressed
335  g_signal_connect (dialog, "key_press_event",
336  G_CALLBACK(gnc_doclink_get_uri_event_cb), dialog);
337 
338  head_label = GTK_WIDGET(gtk_builder_get_object (builder, "path_head_label"));
339  ok_button = GTK_WIDGET(gtk_builder_get_object (builder, "ok_button"));
340 
341  fcb = GTK_WIDGET(gtk_builder_get_object (builder, "file_chooser_button"));
342  fcb_label = GTK_WIDGET(gtk_builder_get_object (builder, "file_chooser_button_label"));
343  g_object_set_data (G_OBJECT(fcb), "fcb_label", fcb_label);
344  g_object_set_data (G_OBJECT(fcb), "okbut", ok_button);
345  g_signal_connect (fcb, "clicked", G_CALLBACK(fcb_clicked_cb), ok_button);
346 
347  button_file = GTK_WIDGET(gtk_builder_get_object (builder, "linked_file"));
348  g_signal_connect (button_file, "toggled", G_CALLBACK(uri_type_selected_cb), fcb);
349 
350  gtk_widget_show_all (GTK_WIDGET(gtk_builder_get_object (builder, "file_hbox")));
351 
352  warning_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "warning_hbox"));
353  entry = GTK_ENTRY(gtk_builder_get_object (builder, "location_entry"));
354  g_object_set_data (G_OBJECT(entry), "whbox", warning_hbox);
355  g_object_set_data (G_OBJECT(entry), "okbut", ok_button);
356 
357  g_signal_connect (entry, "changed", G_CALLBACK(location_ok_cb), ok_button);
358 
359  button_loc = GTK_WIDGET(gtk_builder_get_object (builder, "linked_loc"));
360  g_signal_connect (button_loc, "toggled", G_CALLBACK(uri_type_selected_cb), entry);
361 
362  // display path head text and test if present
363  gnc_doclink_set_path_head_label (head_label, NULL, NULL);
364 
365  // Check for uri is empty or NULL
366  if (uri && *uri)
367  {
368  scheme = gnc_uri_get_scheme (uri);
369  have_uri = TRUE;
370 
371  if (!scheme || g_strcmp0 (scheme, "file") == 0) // use the correct dialog
372  uri_is_file = TRUE;
373  else
374  uri_is_file = FALSE;
375  }
376  else
377  {
378  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button_loc), TRUE);
379  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(button_file), TRUE);
380  }
381 
382  // make sure we start with the right dialog
383  if (have_uri)
384  {
385  if (uri_is_file) // file
386  {
387  gchar *filename = g_path_get_basename (uri);
388 
389  g_object_set_data_full (G_OBJECT(fcb), "uri", g_strdup (uri), g_free);
390 
391  if (filename)
392  {
393  gchar *unescape_filename = g_uri_unescape_string (filename, NULL);
394  gtk_label_set_text (GTK_LABEL(fcb_label), unescape_filename);
395  g_free (unescape_filename);
396  g_free (filename);
397  }
398  setup_file_dialog (builder, path_head, uri, scheme);
399  }
400  else // location
401  setup_location_dialog (builder, button_loc, uri);
402  }
403  else
404  g_object_set_data_full (G_OBJECT(fcb), "path_head", g_strdup (path_head), g_free);
405 
406  g_free (scheme);
407  g_object_unref (G_OBJECT(builder));
408 
409  // run the dialog
410  result = gtk_dialog_run (GTK_DIALOG(dialog));
411  if (result == GTK_RESPONSE_OK) //ok button
412  {
413  if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(button_loc))) // location
414  {
415  const gchar *dialog_uri = gtk_entry_get_text (GTK_ENTRY(entry));
416 
417  ret_uri = g_strdup (dialog_uri);
418 
419  DEBUG("Dialog Location URI: '%s'", dialog_uri);
420  }
421  else // file
422  {
423  const gchar *dialog_uri = g_object_get_data (G_OBJECT(fcb), "uri");
424 
425  PINFO("Dialog File URI: '%s', Path head: '%s'", dialog_uri, path_head);
426 
427  // relative paths do not start with a '/'
428  if (g_str_has_prefix (dialog_uri, path_head))
429  {
430  const gchar *part = dialog_uri + strlen (path_head);
431  ret_uri = g_strdup (part);
432  }
433  else
434  ret_uri = g_strdup (dialog_uri);
435 
436  DEBUG("Dialog File URI: '%s'", ret_uri);
437  }
438  }
439  else if (result == GTK_RESPONSE_REJECT) // remove button
440  ret_uri = g_strdup ("");
441  else
442  ret_uri = g_strdup (uri); // any other button
443 
444  g_free (path_head);
445  gtk_widget_destroy (dialog);
446  return ret_uri;
447 }
448 
449 
450 /* =================================================================== */
451 
452 
453 static void close_handler (gpointer user_data);
454 
455 static gboolean
456 gnc_doclink_dialog_window_delete_event_cb (GtkWidget *widget,
457  GdkEvent *event,
458  gpointer user_data)
459 {
460  DoclinkDialog *doclink_dialog = user_data;
461  // this cb allows the window size to be saved on closing with the X
462  if (doclink_dialog->is_list_trans)
463  gnc_save_window_size (GNC_PREFS_GROUP_TRANS,
464  GTK_WINDOW(doclink_dialog->window));
465  else
466  gnc_save_window_size (GNC_PREFS_GROUP_BUS,
467  GTK_WINDOW(doclink_dialog->window));
468  return FALSE;
469 }
470 
471 static void
472 gnc_doclink_dialog_window_destroy_cb (GtkWidget *object, gpointer user_data)
473 {
474  DoclinkDialog *doclink_dialog = user_data;
475 
476  ENTER(" ");
477  gnc_unregister_gui_component (doclink_dialog->component_id);
478 
479  if (doclink_dialog->window)
480  {
481  g_free (doclink_dialog->path_head);
482  gtk_widget_destroy (doclink_dialog->window);
483  doclink_dialog->window = NULL;
484  }
485  g_free (doclink_dialog);
486  LEAVE(" ");
487 }
488 
489 static gboolean
490 gnc_doclink_dialog_window_key_press_cb (GtkWidget *widget, GdkEventKey *event,
491  gpointer user_data)
492 {
493  DoclinkDialog *doclink_dialog = user_data;
494 
495  if (event->keyval == GDK_KEY_Escape)
496  {
497  close_handler (doclink_dialog);
498  return TRUE;
499  }
500  else
501  return FALSE;
502 }
503 
504 static void
505 doclink_dialog_update (DoclinkDialog *doclink_dialog)
506 {
507  GtkTreeModel *model;
508  GtkTreeIter iter;
509  gboolean valid;
510 
511  /* disconnect the model from the treeview */
512  model = gtk_tree_view_get_model (GTK_TREE_VIEW (doclink_dialog->view));
513  g_object_ref (G_OBJECT(model));
514  gtk_tree_view_set_model (GTK_TREE_VIEW (doclink_dialog->view), NULL);
515 
516  /* Get first row in list store */
517  valid = gtk_tree_model_get_iter_first (model, &iter);
518 
519  while (valid)
520  {
521  gchar *uri;
522  gchar *scheme;
523 
524  gtk_tree_model_get (model, &iter, URI, &uri, -1);
525 
526  scheme = gnc_uri_get_scheme (uri);
527 
528  if (!scheme || gnc_uri_is_file_scheme (scheme))
529  {
530  gchar *filename =
531  gnc_doclink_get_unescape_uri (doclink_dialog->path_head,
532  uri, scheme);
533 
534  if (g_file_test (filename, G_FILE_TEST_EXISTS))
535  gtk_list_store_set (GTK_LIST_STORE(model), &iter, AVAILABLE, _("File Found"), -1);
536  else
537  gtk_list_store_set (GTK_LIST_STORE(model), &iter, AVAILABLE, _("File Not Found"), -1);
538 
539  g_free (filename);
540  }
541  else
542  {
543  gchar *escaped = g_uri_escape_string (uri, ":/.", TRUE);
544  GNetworkMonitor *nm = g_network_monitor_get_default ();
545  GSocketConnectable *conn = g_network_address_parse_uri (escaped, 80, NULL);
546 
547  if (conn)
548  {
549  if (g_network_monitor_can_reach (nm, conn, NULL, NULL))
550  gtk_list_store_set (GTK_LIST_STORE(model), &iter, AVAILABLE, _("Address Found"), -1);
551  else
552  gtk_list_store_set (GTK_LIST_STORE(model), &iter, AVAILABLE, _("Address Not Found"), -1);
553  }
554  g_free (escaped);
555  }
556  g_free (uri);
557  g_free (scheme);
558 
559  valid = gtk_tree_model_iter_next (model, &iter);
560  }
561  /* reconnect the model to the treeview */
562  gtk_tree_view_set_model (GTK_TREE_VIEW (doclink_dialog->view), model);
563  g_object_unref (G_OBJECT(model));
564 }
565 
566 static void
567 update_model_with_changes (DoclinkDialog *doclink_dialog, GtkTreeIter *iter,
568  const gchar *uri)
569 {
570  gchar *display_uri;
571  gboolean rel = FALSE;
572  gchar *scheme = gnc_uri_get_scheme (uri);
573 
574  if (!scheme) // path is relative
575  rel = TRUE;
576 
577  display_uri = gnc_doclink_get_unescape_uri (doclink_dialog->path_head,
578  uri, scheme);
579  gtk_list_store_set (GTK_LIST_STORE (doclink_dialog->model), iter,
580  DISPLAY_URI, display_uri, AVAILABLE, _("File Found"),
581  URI, uri,
582  URI_RELATIVE, rel, // used just for sorting relative column
583  URI_RELATIVE_PIX, (rel == TRUE ? "emblem-default" : NULL), -1);
584 
585  if (!rel && !gnc_uri_is_file_scheme (scheme))
586  gtk_list_store_set (GTK_LIST_STORE (doclink_dialog->model), iter,
587  AVAILABLE, _("Unknown"), -1);
588 
589  g_free (display_uri);
590  g_free (scheme);
591 }
592 
593 static void
594 update_total_entries (DoclinkDialog *doclink_dialog)
595 {
596  gint entries =
597  gtk_tree_model_iter_n_children (GTK_TREE_MODEL (doclink_dialog->model),
598  NULL);
599 
600  if (entries > 0)
601  {
602  gchar *total = g_strdup_printf ("%s %d", _("Total Entries"), entries);
603  gtk_label_set_text (GTK_LABEL (doclink_dialog->total_entries_label),
604  total);
605  gtk_widget_show (doclink_dialog->total_entries_label);
606  g_free (total);
607  }
608  else
609  gtk_widget_hide (doclink_dialog->total_entries_label);
610 }
611 
612 static void
613 row_selected_bus_cb (GtkTreeView *view, GtkTreePath *path,
614  GtkTreeViewColumn *col, gpointer user_data)
615 {
616  DoclinkDialog *doclink_dialog = user_data;
617  GtkTreeIter iter;
618  GncInvoice *invoice;
619  gchar *uri = NULL;
620 
621  // path describes a non-existing row - should not happen
622  g_return_if_fail (gtk_tree_model_get_iter (doclink_dialog->model,
623  &iter, path));
624 
625  gtk_tree_model_get (doclink_dialog->model, &iter, URI,
626  &uri, ITEM_POINTER, &invoice, -1);
627 
628  // Open linked document, subtract 1 to allow for date_int64
629  if (gtk_tree_view_get_column (GTK_TREE_VIEW (doclink_dialog->view),
630  DISPLAY_URI - 1) == col)
631  gnc_doclink_open_uri (GTK_WINDOW (doclink_dialog->window), uri);
632 
633  if (!invoice)
634  {
635  g_free (uri);
636  return;
637  }
638 
639  // Open Invoice, subtract 1 to allow for date_int64
640  if (gtk_tree_view_get_column (GTK_TREE_VIEW (doclink_dialog->view),
641  DESC_ID - 1) == col)
642  {
643  InvoiceWindow *iw;
644 
645  iw = gnc_ui_invoice_edit (GTK_WINDOW (doclink_dialog->window),
646  invoice);
648  }
649 
650  // Open Invoice document link dialog, subtract 1 to allow for date_int64
651  if (gtk_tree_view_get_column (GTK_TREE_VIEW (doclink_dialog->view),
652  AVAILABLE - 1) == col)
653  {
654  gchar *ret_uri = NULL;
655 
656  if (doclink_dialog->book_ro)
657  {
658  gnc_warning_dialog (GTK_WINDOW (doclink_dialog->window), "%s",
659  _("Business item can not be modified."));
660  g_free (uri);
661  return;
662  }
663 
664 /* Translators: This is the title of a dialog box for linking an external
665  file or URI with the current bill, invoice, transaction, or voucher. */
666  ret_uri =
667  gnc_doclink_get_uri_dialog (GTK_WINDOW (doclink_dialog->window),
668  _("Manage Document Link"), uri);
669 
670  if (ret_uri && g_strcmp0 (uri, ret_uri) != 0)
671  {
672  gncInvoiceSetDocLink (invoice, ret_uri);
673 
674  if (g_strcmp0 (ret_uri, "") == 0) // delete uri
675  {
676  // update the asooc parts for invoice window if present
677  gnc_invoice_update_doclink_for_window (invoice, ret_uri);
678  gtk_list_store_remove (GTK_LIST_STORE (doclink_dialog->model),
679  &iter);
680  update_total_entries (doclink_dialog);
681  }
682  else // update uri
683  {
684  gchar *display_uri;
685  gchar *scheme = gnc_uri_get_scheme (ret_uri);
686 
687  display_uri = gnc_doclink_get_unescape_uri (doclink_dialog->path_head, ret_uri, scheme);
688 
689  update_model_with_changes (doclink_dialog, &iter, ret_uri);
690 
691  // update the asooc parts for invoice window if present
692  gnc_invoice_update_doclink_for_window (invoice, display_uri);
693 
694  g_free (scheme);
695  g_free (display_uri);
696  }
697  }
698  g_free (ret_uri);
699  }
700  g_free (uri);
701 }
702 
703 static void
704 row_selected_trans_cb (GtkTreeView *view, GtkTreePath *path,
705  GtkTreeViewColumn *col, gpointer user_data)
706 {
707  DoclinkDialog *doclink_dialog = user_data;
708  GtkTreeIter iter;
709  Split *split;
710  gchar *uri = NULL;
711 
712  // path describes a non-existing row - should not happen
713  g_return_if_fail (gtk_tree_model_get_iter (doclink_dialog->model,
714  &iter, path));
715 
716  gtk_tree_model_get (doclink_dialog->model, &iter, URI,
717  &uri, ITEM_POINTER, &split, -1);
718 
719  // Open linked document, subtract 1 to allow for date_int64
720  if (gtk_tree_view_get_column (GTK_TREE_VIEW (doclink_dialog->view),
721  DISPLAY_URI - 1) == col)
722  gnc_doclink_open_uri (GTK_WINDOW (doclink_dialog->window), uri);
723 
724  if (!split)
725  {
726  g_free (uri);
727  return;
728  }
729 
730  // Open transaction, subtract 1 to allow for date_int64
731  if (gtk_tree_view_get_column (GTK_TREE_VIEW(doclink_dialog->view),
732  DESC_ITEM - 1) == col)
733  {
734  GncPluginPage *page;
735  GNCSplitReg *gsr;
736  Account *account = xaccSplitGetAccount (split);
737 
738  page = gnc_plugin_page_register_new (account, FALSE);
739  gnc_main_window_open_page (NULL, page);
741  gnc_split_reg_raise (gsr);
742 
743  // Test for visibility of split
744  if (gnc_split_reg_clear_filter_for_split (gsr, split))
745  gnc_plugin_page_register_clear_current_filter (GNC_PLUGIN_PAGE(page));
746 
747  gnc_split_reg_jump_to_split (gsr, split);
748  }
749 
750  // Open transaction document link dialog, subtract 1 to allow for date_int64
751  if (gtk_tree_view_get_column (GTK_TREE_VIEW(doclink_dialog->view), AVAILABLE - 1) == col)
752  {
753  Transaction *trans;
754  gchar *ret_uri = NULL;
755 
756  trans = xaccSplitGetParent (split);
757 
758  if (xaccTransIsReadonlyByPostedDate (trans) ||
759  xaccTransGetReadOnly (trans) ||
760  doclink_dialog->book_ro)
761  {
762  gnc_warning_dialog (GTK_WINDOW (doclink_dialog->window), "%s",
763  _("Transaction can not be modified."));
764  g_free (uri);
765  return;
766  }
767  ret_uri =
768  gnc_doclink_get_uri_dialog (GTK_WINDOW (doclink_dialog->window),
769  _("Manage Document Link"), uri);
770 
771  if (ret_uri && g_strcmp0 (uri, ret_uri) != 0)
772  {
773  xaccTransSetDocLink (trans, ret_uri);
774  if (g_strcmp0 (ret_uri, "") == 0) // deleted uri
775  {
776  gtk_list_store_remove (GTK_LIST_STORE (doclink_dialog->model),
777  &iter);
778  update_total_entries (doclink_dialog);
779  }
780  else // updated uri
781  update_model_with_changes (doclink_dialog, &iter, ret_uri);
782  }
783  g_free (ret_uri);
784  }
785  g_free (uri);
786 }
787 
788 static void
789 add_bus_info_to_model (QofInstance* data, gpointer user_data)
790 {
791  DoclinkDialog *doclink_dialog = user_data;
792  GncInvoice *invoice = GNC_INVOICE(data);
793  const gchar *uri = gncInvoiceGetDocLink (invoice);
794  GtkTreeIter iter;
795 
796  if (uri && *uri)
797  {
798  gchar *display_uri;
799  gboolean rel = FALSE;
800  gchar *scheme = gnc_uri_get_scheme (uri);
801  time64 t = gncInvoiceGetDateOpened (invoice);
802  gchar *inv_type;
803  char datebuff[MAX_DATE_LENGTH + 1];
804  memset (datebuff, 0, sizeof(datebuff));
805  if (t == 0)
806  t = gnc_time (NULL);
807  qof_print_date_buff (datebuff, sizeof(datebuff), t);
808 
809  switch (gncInvoiceGetType (invoice))
810  {
811  case GNC_INVOICE_VEND_INVOICE:
812  case GNC_INVOICE_VEND_CREDIT_NOTE:
813  inv_type = _("Bill");
814  break;
815  case GNC_INVOICE_EMPL_INVOICE:
816  case GNC_INVOICE_EMPL_CREDIT_NOTE:
817  inv_type = _("Voucher");
818  break;
819  case GNC_INVOICE_CUST_INVOICE:
820  case GNC_INVOICE_CUST_CREDIT_NOTE:
821  inv_type = _("Invoice");
822  break;
823  default:
824  inv_type = _("Undefined");
825  }
826 
827  if (!scheme) // path is relative
828  rel = TRUE;
829 
830  display_uri = gnc_doclink_get_unescape_uri (doclink_dialog->path_head,
831  uri, scheme);
832 
833  gtk_list_store_append (GTK_LIST_STORE (doclink_dialog->model), &iter);
834 
835  gtk_list_store_set (GTK_LIST_STORE (doclink_dialog->model), &iter,
836  DATE_ITEM, datebuff,
837  DATE_INT64, t, // used just for sorting date column
838  DESC_ID, gncInvoiceGetID (invoice),
839  DESC_ITEM, inv_type,
840  DISPLAY_URI, display_uri, AVAILABLE, _("Unknown"),
841  ITEM_POINTER, invoice, URI, uri,
842  URI_RELATIVE, rel, // used just for sorting relative column
843  URI_RELATIVE_PIX, (rel == TRUE ? "emblem-default" : NULL), -1);
844  g_free (display_uri);
845  g_free (scheme);
846  }
847 }
848 
849 static void
850 add_trans_info_to_model (QofInstance* data, gpointer user_data)
851 {
852  DoclinkDialog *doclink_dialog = user_data;
853  Transaction *trans = GNC_TRANSACTION(data);
854  gchar *uri;
855  GtkTreeIter iter;
856 
857  // fix an earlier error when storing relative paths before version 3.5
858  uri = gnc_doclink_convert_trans_link_uri (trans, doclink_dialog->book_ro);
859 
860  if (uri && *uri)
861  {
862  Split *split = xaccTransGetSplit (trans, 0);
863  gchar *scheme = gnc_uri_get_scheme (uri);
864  gchar *display_uri;
865  gboolean rel = FALSE;
866  time64 t = xaccTransRetDatePosted (trans);
867  char datebuff[MAX_DATE_LENGTH + 1];
868  memset (datebuff, 0, sizeof(datebuff));
869  if (t == 0)
870  t = gnc_time (NULL);
871  qof_print_date_buff (datebuff, MAX_DATE_LENGTH, t);
872  gtk_list_store_append (GTK_LIST_STORE (doclink_dialog->model), &iter);
873 
874  if (!scheme) // path is relative
875  rel = TRUE;
876 
877  display_uri = gnc_doclink_get_unescape_uri (doclink_dialog->path_head,
878  uri, scheme);
879 
880  gtk_list_store_set (GTK_LIST_STORE (doclink_dialog->model), &iter,
881  DATE_ITEM, datebuff,
882  DATE_INT64, t, // used just for sorting date column
883  DESC_ITEM, xaccTransGetDescription (trans),
884  DISPLAY_URI, display_uri, AVAILABLE, _("Unknown"),
885  ITEM_POINTER, split, URI, uri,
886  URI_RELATIVE, rel, // used just for sorting relative column
887  URI_RELATIVE_PIX, (rel == TRUE ? "emblem-default" : NULL), -1);
888  g_free (display_uri);
889  g_free (scheme);
890  g_free (uri);
891  }
892 }
893 
894 static void
895 get_bus_info (DoclinkDialog *doclink_dialog)
896 {
897  QofBook *book = gnc_get_current_book();
898 
899  /* disconnect the model from the treeview */
900  doclink_dialog->model =
901  gtk_tree_view_get_model (GTK_TREE_VIEW (doclink_dialog->view));
902  g_object_ref (G_OBJECT (doclink_dialog->model));
903  gtk_tree_view_set_model (GTK_TREE_VIEW (doclink_dialog->view), NULL);
904 
905  /* Clear the list store */
906  gtk_list_store_clear (GTK_LIST_STORE (doclink_dialog->model));
907 
908  /* Loop through the invoices */
909  qof_collection_foreach (qof_book_get_collection (book, GNC_ID_INVOICE),
910  add_bus_info_to_model, doclink_dialog);
911 
912  update_total_entries (doclink_dialog);
913 
914  /* reconnect the model to the treeview */
915  gtk_tree_view_set_model (GTK_TREE_VIEW (doclink_dialog->view),
916  doclink_dialog->model);
917  g_object_unref (G_OBJECT (doclink_dialog->model));
918 }
919 
920 static void
921 get_trans_info (DoclinkDialog *doclink_dialog)
922 {
923  QofBook *book = gnc_get_current_book();
924 
925  doclink_dialog->book_ro = qof_book_is_readonly (book);
926 
927  /* disconnect the model from the treeview */
928  doclink_dialog->model =
929  gtk_tree_view_get_model (GTK_TREE_VIEW (doclink_dialog->view));
930  g_object_ref (G_OBJECT (doclink_dialog->model));
931  gtk_tree_view_set_model (GTK_TREE_VIEW (doclink_dialog->view), NULL);
932 
933  /* Clear the list store */
934  gtk_list_store_clear (GTK_LIST_STORE (doclink_dialog->model));
935 
936  /* Loop through the transactions */
937  qof_collection_foreach (qof_book_get_collection (book, GNC_ID_TRANS),
938  add_trans_info_to_model, doclink_dialog);
939 
940  update_total_entries (doclink_dialog);
941 
942  /* reconnect the model to the treeview */
943  gtk_tree_view_set_model (GTK_TREE_VIEW (doclink_dialog->view),
944  doclink_dialog->model);
945  g_object_unref (G_OBJECT (doclink_dialog->model));
946 }
947 
948 static void
949 gnc_doclink_dialog_reload_button_cb (GtkWidget *widget, gpointer user_data)
950 {
951  DoclinkDialog *doclink_dialog = user_data;
952  gchar *path_head = gnc_doclink_get_path_head ();
953 
954  if (g_strcmp0 (path_head, doclink_dialog->path_head) != 0)
955  {
956  g_free (doclink_dialog->path_head);
957  doclink_dialog->path_head = g_strdup (path_head);
958 
959  // display path head text and test if present
960  gnc_doclink_set_path_head_label (doclink_dialog->path_head_label,
961  NULL, NULL);
962  }
963  g_free (path_head);
964 
965  if (doclink_dialog->is_list_trans)
966  get_trans_info (doclink_dialog);
967  else
968  get_bus_info (doclink_dialog);
969 }
970 
971 static void
972 gnc_doclink_dialog_reload_check_button_cb (GtkWidget *widget,
973  gpointer user_data)
974 {
975  DoclinkDialog *doclink_dialog = user_data;
976 
977  gnc_doclink_dialog_reload_button_cb (widget, user_data);
978  doclink_dialog_update (doclink_dialog);
979 }
980 
981 static void
982 gnc_doclink_dialog_check_button_cb (GtkWidget *widget, gpointer user_data)
983 {
984  DoclinkDialog *doclink_dialog = user_data;
985  doclink_dialog_update (doclink_dialog);
986 }
987 
988 static void
989 gnc_doclink_dialog_close_button_cb (GtkWidget *widget, gpointer user_data)
990 {
991  DoclinkDialog *doclink_dialog = user_data;
992  gnc_close_gui_component (doclink_dialog->component_id);
993 }
994 
995 static void
996 gnc_doclink_dialog_create (GtkWindow *parent, DoclinkDialog *doclink_dialog)
997 {
998  GtkWidget *window;
999  GtkBuilder *builder;
1000  GtkTreeSelection *selection;
1001  GtkTreeViewColumn *expanding_column;
1002  GtkWidget *button;
1003 
1004  ENTER(" ");
1005  builder = gtk_builder_new();
1006  gnc_builder_add_from_file (builder, "dialog-doclink.glade", "list-store");
1007  gnc_builder_add_from_file (builder, "dialog-doclink.glade",
1008  "linked_doc_window");
1009 
1010  window = GTK_WIDGET(gtk_builder_get_object (builder, "linked_doc_window"));
1011  doclink_dialog->window = window;
1012  doclink_dialog->session = gnc_get_current_session();
1013 
1014  button = GTK_WIDGET(gtk_builder_get_object (builder, "reload_button"));
1015  g_signal_connect (button, "clicked",
1016  G_CALLBACK (gnc_doclink_dialog_reload_button_cb),
1017  doclink_dialog);
1018  button = GTK_WIDGET(gtk_builder_get_object (builder, "reload_and_check_button"));
1019  g_signal_connect (button, "clicked",
1020  G_CALLBACK (gnc_doclink_dialog_reload_check_button_cb),
1021  doclink_dialog);
1022 
1023  button = GTK_WIDGET(gtk_builder_get_object (builder, "check_button"));
1024  g_signal_connect (button, "clicked",
1025  G_CALLBACK (gnc_doclink_dialog_check_button_cb),
1026  doclink_dialog);
1027  button = GTK_WIDGET(gtk_builder_get_object (builder, "close_button"));
1028  g_signal_connect (button, "clicked",
1029  G_CALLBACK (gnc_doclink_dialog_close_button_cb),
1030  doclink_dialog);
1031 
1032  // Set the widget name and style context for this dialog so it can be easily manipulated with css
1033  gtk_widget_set_name (GTK_WIDGET (window), "gnc-id-transaction-doclinks");
1034  gnc_widget_style_context_add_class (GTK_WIDGET (window),
1035  "gnc-class-doclink");
1036 
1037  doclink_dialog->view =
1038  GTK_WIDGET (gtk_builder_get_object (builder, "treeview"));
1039  doclink_dialog->path_head_label =
1040  GTK_WIDGET (gtk_builder_get_object (builder, "path-head"));
1041  doclink_dialog->total_entries_label =
1042  GTK_WIDGET (gtk_builder_get_object (builder, "total_entries_label"));
1043  doclink_dialog->path_head = gnc_doclink_get_path_head ();
1044 
1045  // display path head text and test if present
1046  gnc_doclink_set_path_head_label (doclink_dialog->path_head_label, NULL, NULL);
1047 
1048  // Get the column we want to be the expanding column.
1049  expanding_column =
1050  GTK_TREE_VIEW_COLUMN (gtk_builder_get_object (builder, "doclink"));
1051 
1052  /* default sort order */
1053  gtk_tree_sortable_set_sort_column_id (GTK_TREE_SORTABLE(gtk_tree_view_get_model(
1054  GTK_TREE_VIEW (doclink_dialog->view))),
1055  DATE_INT64, GTK_SORT_ASCENDING);
1056 
1057  // Set grid lines option to preference
1058  gtk_tree_view_set_grid_lines (GTK_TREE_VIEW (doclink_dialog->view),
1059  gnc_tree_view_get_grid_lines_pref ());
1060 
1061  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (doclink_dialog->view));
1062  gtk_tree_selection_set_mode (selection, GTK_SELECTION_SINGLE);
1063 
1064  g_signal_connect (doclink_dialog->window, "destroy",
1065  G_CALLBACK (gnc_doclink_dialog_window_destroy_cb),
1066  doclink_dialog);
1067 
1068  g_signal_connect (doclink_dialog->window, "delete-event",
1069  G_CALLBACK(gnc_doclink_dialog_window_delete_event_cb), doclink_dialog);
1070 
1071  g_signal_connect (doclink_dialog->window, "key_press_event",
1072  G_CALLBACK (gnc_doclink_dialog_window_key_press_cb),
1073  doclink_dialog);
1074 
1075  // Setup the correct parts for each dialog
1076  if (doclink_dialog->is_list_trans)
1077  {
1078  GObject *desc_item_tree_column = G_OBJECT(gtk_builder_get_object (builder, "desc_item"));
1079  GObject *desc_id_tree_column = G_OBJECT(gtk_builder_get_object (builder, "desc_id"));
1080 
1081  /* Translators: This is the label of a dialog box that lists all of the
1082  transaction that have files or URIs linked with them. */
1083  gtk_window_set_title (GTK_WINDOW (window), _("Transaction Document Links"));
1084 
1085  gtk_tree_view_column_set_visible (GTK_TREE_VIEW_COLUMN(desc_id_tree_column), FALSE);
1086  gtk_tree_view_column_set_title (GTK_TREE_VIEW_COLUMN(desc_item_tree_column), _("Description"));
1087 
1088  g_signal_connect (doclink_dialog->view, "row-activated",
1089  G_CALLBACK (row_selected_trans_cb),
1090  (gpointer)doclink_dialog);
1091  get_trans_info (doclink_dialog);
1092  }
1093  else
1094  {
1095  GtkWidget *help_label = GTK_WIDGET(gtk_builder_get_object (builder, "help_label"));
1096  const gchar *item_string = N_(
1097  "Double click on the entry in the Id column to jump to the "
1098  "Business Item.\nDouble click on the entry in the Link column "
1099  "to open the Linked Document.\nDouble click on the entry in "
1100  "the Available column to modify the document link.");
1101 
1102  /* Translators: This is the label of a dialog box that lists all of the
1103  invoices, bills, and vouchers that have files or URIs linked with
1104  them. */
1105  gtk_window_set_title (GTK_WINDOW (doclink_dialog->window),
1106  _("Business Document Links"));
1107  gtk_label_set_text (GTK_LABEL(help_label), gettext (item_string));
1108 
1109  g_signal_connect (doclink_dialog->view, "row-activated",
1110  G_CALLBACK (row_selected_bus_cb),
1111  (gpointer)doclink_dialog);
1112  get_bus_info (doclink_dialog);
1113  }
1114 
1115  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func,
1116  doclink_dialog);
1117 
1118  g_object_unref (G_OBJECT(builder));
1119 
1120  gtk_tree_view_column_set_expand (expanding_column, TRUE);
1121  gtk_tree_view_columns_autosize (GTK_TREE_VIEW(doclink_dialog->view));
1122  LEAVE(" ");
1123 }
1124 
1125 static void
1126 close_handler (gpointer user_data)
1127 {
1128  DoclinkDialog *doclink_dialog = user_data;
1129 
1130  ENTER(" ");
1131  if (doclink_dialog->is_list_trans)
1132  gnc_save_window_size (GNC_PREFS_GROUP_TRANS,
1133  GTK_WINDOW (doclink_dialog->window));
1134  else
1135  gnc_save_window_size (GNC_PREFS_GROUP_BUS,
1136  GTK_WINDOW (doclink_dialog->window));
1137  gtk_widget_destroy (GTK_WIDGET (doclink_dialog->window));
1138  LEAVE(" ");
1139 }
1140 
1141 static void
1142 refresh_handler (GHashTable *changes, gpointer user_data)
1143 {
1144  ENTER(" ");
1145  LEAVE(" ");
1146 }
1147 
1148 static gboolean
1149 show_handler (const char *klass, gint component_id,
1150  gpointer user_data, gpointer iter_data)
1151 {
1152  DoclinkDialog *doclink_dialog = user_data;
1153  gboolean is_bus = GPOINTER_TO_INT(iter_data);
1154 
1155  ENTER(" ");
1156  if (!doclink_dialog)
1157  {
1158  LEAVE("No data structure");
1159  return (FALSE);
1160  }
1161 
1162  // test if the dialog is the right one
1163  if (is_bus == doclink_dialog->is_list_trans)
1164  return (FALSE);
1165 
1166  gtk_window_present (GTK_WINDOW(doclink_dialog->window));
1167  LEAVE(" ");
1168  return (TRUE);
1169 }
1170 
1171 void
1172 gnc_doclink_business_dialog (GtkWindow *parent)
1173 {
1174  DoclinkDialog *doclink_dialog;
1175 
1176  ENTER(" ");
1177  if (gnc_forall_gui_components (DIALOG_DOCLINK_CM_CLASS,
1178  show_handler, GINT_TO_POINTER(1)))
1179  {
1180  LEAVE("Existing dialog raised");
1181  return;
1182  }
1183  doclink_dialog = g_new0 (DoclinkDialog, 1);
1184 
1185  doclink_dialog->is_list_trans = FALSE;
1186 
1187  gnc_doclink_dialog_create (parent, doclink_dialog);
1188 
1189  doclink_dialog->component_id =
1190  gnc_register_gui_component (DIALOG_DOCLINK_CM_CLASS,
1191  refresh_handler, close_handler,
1192  doclink_dialog);
1193 
1194  gnc_gui_component_set_session (doclink_dialog->component_id,
1195  doclink_dialog->session);
1196 
1197  gnc_restore_window_size (GNC_PREFS_GROUP_BUS,
1198  GTK_WINDOW(doclink_dialog->window), parent);
1199  gtk_widget_show_all (GTK_WIDGET(doclink_dialog->window));
1200  LEAVE(" ");
1201 }
1202 
1203 void
1204 gnc_doclink_trans_dialog (GtkWindow *parent)
1205 {
1206  DoclinkDialog *doclink_dialog;
1207 
1208  ENTER(" ");
1209  if (gnc_forall_gui_components (DIALOG_DOCLINK_CM_CLASS,
1210  show_handler, GINT_TO_POINTER(0)))
1211  {
1212  LEAVE("Existing dialog raised");
1213  return;
1214  }
1215  doclink_dialog = g_new0 (DoclinkDialog, 1);
1216  doclink_dialog->is_list_trans = TRUE;
1217 
1218  gnc_doclink_dialog_create (parent, doclink_dialog);
1219 
1220  doclink_dialog->component_id =
1221  gnc_register_gui_component (DIALOG_DOCLINK_CM_CLASS,
1222  refresh_handler, close_handler,
1223  doclink_dialog);
1224 
1225  gnc_gui_component_set_session (doclink_dialog->component_id,
1226  doclink_dialog->session);
1227 
1228  gnc_restore_window_size (GNC_PREFS_GROUP_TRANS,
1229  GTK_WINDOW(doclink_dialog->window), parent);
1230  gtk_widget_show_all (GTK_WIDGET(doclink_dialog->window));
1231  LEAVE(" ");
1232 }
GncPluginPage * gnc_plugin_page_register_new(Account *account, gboolean subaccounts)
Create a new "register" plugin page, given a pointer to an account.
utility functions for the GnuCash UI
Split * xaccTransGetSplit(const Transaction *trans, int i)
Return a pointer to the indexed split in this transaction&#39;s split list.
gchar * gnc_uri_get_scheme(const gchar *uri)
Extracts the scheme from a uri.
The instance data structure for a content plugin.
gboolean xaccTransIsReadonlyByPostedDate(const Transaction *trans)
Returns TRUE if this Transaction is read-only because its posted-date is older than the "auto-readonl...
utility functions for the GnuCash UI
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
gboolean gnc_uri_is_file_scheme(const gchar *scheme)
Checks if the given scheme is used to refer to a file (as opposed to a network service like a databas...
Definition: gnc-uri-utils.c:90
STRUCTS.
const char * xaccTransGetReadOnly(Transaction *trans)
Returns a non-NULL value if this Transaction was marked as read-only with some specific "reason" text...
#define DEBUG(format, args...)
Print a debugging message.
Definition: qoflog.h:264
gchar * gnc_uri_get_path(const gchar *uri)
Extracts the path part from a uri.
Transaction * xaccSplitGetParent(const Split *split)
Returns the parent transaction of the split.
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
void gnc_main_window_open_page(GncMainWindow *window, GncPluginPage *page)
Display a data plugin page in a window.
Functions for adding content to a window.
Functions providing a register page for the GnuCash UI.
Account handling public routines.
GncPluginPage * gnc_plugin_page_invoice_new(InvoiceWindow *iw)
Create a new "invoice" plugin page, given a pointer to an InvoiceWindow data structure.
time64 xaccTransRetDatePosted(const Transaction *trans)
Retrieve the posted date of the transaction.
const char * xaccTransGetDescription(const Transaction *trans)
Gets the transaction Description.
Gnome specific utility functions.
#define MAX_DATE_LENGTH
The maximum length of a string created by the date printers.
Definition: gnc-date.h:108
Generic api to store and retrieve preferences.
gboolean qof_book_is_readonly(const QofBook *book)
Return whether the book is read only.
Definition: qofbook.cpp:497
Account * xaccSplitGetAccount(const Split *split)
Returns the account of this split, which was set through xaccAccountInsertSplit().
Definition: gmock-Split.cpp:53
void xaccTransSetDocLink(Transaction *trans, const char *doclink)
Sets the transaction Document Link.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
time64 gnc_time(time64 *tbuf)
get the current time
Definition: gnc-date.cpp:261
Utility functions for convert uri in separate components and back.
QofCollection * qof_book_get_collection(const QofBook *book, QofIdType entity_type)
Return The table of entities of the given type.
Definition: qofbook.cpp:521
gint64 time64
Most systems that are currently maintained, including Microsoft Windows, BSD-derived Unixes and Linux...
Definition: gnc-date.h:87
void gnc_launch_doclink(GtkWindow *parent, const char *uri)
Launch the default browser and open the provided URI.
void gnc_plugin_page_register_clear_current_filter(GncPluginPage *plugin_page)
This function clears the registers current filter.
File path resolution utility functions.
API for Transactions and Splits (journal entries)
GNCSplitReg * gnc_plugin_page_register_get_gsr(GncPluginPage *plugin_page)
Get the GNCSplitReg data structure associated with this register page.
size_t qof_print_date_buff(char *buff, size_t buflen, time64 secs)
Convenience: calls through to qof_print_date_dmy_buff().
Definition: gnc-date.cpp:573