GnuCash  5.6-150-g038405b370+
gnc-plugin-file-history.c
Go to the documentation of this file.
1 /*
2  * gnc-plugin-file-history.c --
3  * Copyright (C) 2003,2005 David Hampton <hampton@employees.org>
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 
32 #include <config.h>
33 
34 #include <gtk/gtk.h>
35 #include <glib/gi18n.h>
36 #include <glib/gprintf.h>
37 #include <string.h>
38 
39 #include "gnc-gkeyfile-utils.h"
40 #include "gnc-file.h"
41 #include "gnc-main-window.h"
43 #include "gnc-window.h"
44 #include "gnc-engine.h"
45 #include "gnc-prefs.h"
46 #include "gnc-uri-utils.h"
47 #include "gnc-gtk-utils.h"
48 
49 #define FILENAME_STRING "filename"
50 #define MAX_HISTORY_FILES 10 /* May be any number up to 10 */
51 #define GNC_PREFS_GROUP_HISTORY "history"
52 #define GNC_PREF_HISTORY_MAXFILES "maxfiles"
53 #define HISTORY_STRING_FILE_N "file%d"
54 
55 static void gnc_plugin_file_history_finalize (GObject *object);
56 
57 static void gnc_plugin_file_history_add_to_window (GncPlugin *plugin, GncMainWindow *window, GQuark type);
58 static void gnc_plugin_file_history_remove_from_window (GncPlugin *plugin, GncMainWindow *window, GQuark type);
59 
60 
62 static QofLogModule log_module = GNC_MOD_GUI;
63 
64 /* Command callbacks */
65 static void gnc_plugin_file_history_cmd_open_file (GSimpleAction *simple, GVariant *parameter, gpointer user_data);
66 
68 #define PLUGIN_ACTIONS_NAME "gnc-plugin-file-history-actions"
69 
70 #define PLUGIN_UI_FILENAME "gnc-plugin-file-history.ui"
71 
72 #define GNOME1_HISTORY "History"
73 #define GNOME1_MAXFILES "MaxFiles"
74 
80 static GActionEntry gnc_plugin_actions [] =
81 {
82  { "RecentFile0Action", gnc_plugin_file_history_cmd_open_file, NULL, NULL, NULL },
83  { "RecentFile1Action", gnc_plugin_file_history_cmd_open_file, NULL, NULL, NULL },
84  { "RecentFile2Action", gnc_plugin_file_history_cmd_open_file, NULL, NULL, NULL },
85  { "RecentFile3Action", gnc_plugin_file_history_cmd_open_file, NULL, NULL, NULL },
86  { "RecentFile4Action", gnc_plugin_file_history_cmd_open_file, NULL, NULL, NULL },
87  { "RecentFile5Action", gnc_plugin_file_history_cmd_open_file, NULL, NULL, NULL },
88  { "RecentFile6Action", gnc_plugin_file_history_cmd_open_file, NULL, NULL, NULL },
89  { "RecentFile7Action", gnc_plugin_file_history_cmd_open_file, NULL, NULL, NULL },
90  { "RecentFile8Action", gnc_plugin_file_history_cmd_open_file, NULL, NULL, NULL },
91  { "RecentFile9Action", gnc_plugin_file_history_cmd_open_file, NULL, NULL, NULL },
92 };
94 static guint gnc_plugin_n_actions = G_N_ELEMENTS(gnc_plugin_actions);
95 
97 static const gchar *gnc_plugin_load_ui_items [] =
98 {
99  NULL,
100 };
101 
104 {
105  GncPlugin gnc_plugin;
106 };
107 
108 /************************************************************
109  * Other Functions *
110  ************************************************************/
111 
120 static gchar *
121 gnc_history_index_to_pref_name (guint index)
122 {
123  return g_strdup_printf(HISTORY_STRING_FILE_N, index);
124 }
125 
126 
135 static gint
136 gnc_history_pref_name_to_index (const gchar *pref)
137 {
138  gint index, result;
139 
140  result = sscanf(pref, HISTORY_STRING_FILE_N, &index);
141  if (result != 1)
142  return -1;
143  if ((index < 0) || (index >= gnc_plugin_n_actions))
144  return -1;
145  return index;
146 }
147 
148 
149 /* Add a file name to the front of the file "history list". If the
150  * name already exist on the list, then it is moved from its current
151  * location to the front of the list. The "list" is actually a
152  * sequence of up to ten preferences.
153  */
154 void
155 gnc_history_add_file (const char *newfile)
156 {
157  gchar *filename, *from, *to;
158  gint i, last;
159 
160  if (newfile == NULL)
161  return;
162  if (!g_utf8_validate(newfile, -1, NULL))
163  return;
164 
165  /*
166  * Look for the filename in preferences.
167  */
168  last = MAX_HISTORY_FILES - 1;
169  for (i = 0; i < MAX_HISTORY_FILES; i++)
170  {
171  from = gnc_history_index_to_pref_name(i);
172  filename = gnc_prefs_get_string(GNC_PREFS_GROUP_HISTORY, from);
173  g_free(from);
174 
175  if (!filename)
176  {
177  last = i;
178  break;
179  }
180  if (g_utf8_collate(newfile, filename) == 0)
181  {
182  g_free(filename);
183  last = i;
184  break;
185  }
186  g_free(filename);
187  }
188 
189  /*
190  * Shuffle filenames upward through preferences.
191  */
192  to = gnc_history_index_to_pref_name(last);
193  for (i = last - 1; i >= 0; i--)
194  {
195  from = gnc_history_index_to_pref_name(i);
196  filename = gnc_prefs_get_string(GNC_PREFS_GROUP_HISTORY, from);
197  if (filename && *filename)
198  {
199  gnc_prefs_set_string(GNC_PREFS_GROUP_HISTORY, to, filename);
200  }
201  else
202  {
203  gnc_prefs_reset(GNC_PREFS_GROUP_HISTORY, to);
204  }
205  g_free(filename);
206  g_free(to);
207  to = from;
208  }
209 
210  /*
211  * Store the new zero entry.
212  */
213  gnc_prefs_set_string(GNC_PREFS_GROUP_HISTORY, to, newfile);
214  g_free(to);
215 }
216 
217 
223 void
224 gnc_history_remove_file (const char *oldfile)
225 {
226  gchar *filename, *from, *to;
227  gint i, j;
228 
229  if (!oldfile)
230  return;
231  if (!g_utf8_validate(oldfile, -1, NULL))
232  return;
233 
234  for (i = 0, j = 0; i < MAX_HISTORY_FILES; i++)
235  {
236  from = gnc_history_index_to_pref_name(i);
237  filename = gnc_prefs_get_string(GNC_PREFS_GROUP_HISTORY, from);
238 
239  if (filename)
240  {
241  if (g_utf8_collate(oldfile, filename) == 0)
242  {
243  gnc_prefs_reset(GNC_PREFS_GROUP_HISTORY, from);
244  }
245  else
246  {
247  if (i != j)
248  {
249  to = gnc_history_index_to_pref_name(j);
250  gnc_prefs_set_string(GNC_PREFS_GROUP_HISTORY, to, filename);
251  gnc_prefs_reset(GNC_PREFS_GROUP_HISTORY, from);
252  g_free(to);
253  }
254  j++;
255  }
256  g_free (filename);
257  }
258  g_free(from);
259  }
260 }
261 
262 
267 gboolean gnc_history_test_for_file (const char *oldfile)
268 {
269  gchar *filename, *from;
270  gint i;
271  gboolean found = FALSE;
272 
273  if (!oldfile)
274  return FALSE;
275  if (!g_utf8_validate(oldfile, -1, NULL))
276  return FALSE;
277 
278  for (i = 0; i < MAX_HISTORY_FILES; i++)
279  {
280  from = gnc_history_index_to_pref_name(i);
281  filename = gnc_prefs_get_string(GNC_PREFS_GROUP_HISTORY, from);
282  g_free(from);
283 
284  if (!filename)
285  continue;
286 
287  if (g_utf8_collate(oldfile, filename) == 0)
288  {
289  found = TRUE;
290  g_free (filename);
291  break;
292  }
293  g_free (filename);
294  }
295 
296  return found;
297 }
298 
299 
300 /* Retrieve the name of the file most recently accessed. This is the
301  * name at the front of the list. Since the "list" is actually a
302  * sequence of up to ten preference names, this is the value of the first preference.
303  */
304 char *
306 {
307  char *filename, *pref;
308 
309  pref = gnc_history_index_to_pref_name(0);
310  filename = gnc_prefs_get_string(GNC_PREFS_GROUP_HISTORY, pref);
311  g_free(pref);
312 
313  return filename;
314 }
315 
316 
317 /************************************************************
318  * Other Functions *
319  ************************************************************/
320 
330 static gchar *
331 gnc_history_generate_label (int index, const gchar *filename)
332 {
333  gchar *label, *result;
334  gchar **splitlabel;
335 
336  if (gnc_uri_targets_local_fs (filename))
337  {
338  /* for file paths, only display the file name */
339  gchar *filepath = gnc_uri_get_path ( filename );
340  label = g_path_get_basename ( filepath );
341  g_free ( filepath );
342  }
343  else
344  {
345  /* for databases, display the full uri, except for the password */
346  label = gnc_uri_normalize_uri ( filename, FALSE );
347  }
348 
349  /* Escape '_' characters */
350  splitlabel = g_strsplit ( label, "_", 0);
351  g_free (label);
352  label = g_strjoinv ( "__", splitlabel);
353  g_strfreev (splitlabel);
354 
355  result = g_strdup_printf ( "_%d %s", (index + 1) % 10, label);
356  g_free ( label );
357  return result;
358 
359 }
360 
371 static gchar *
372 gnc_history_generate_tooltip (int index, const gchar *filename)
373 {
374 
375  if (gnc_uri_targets_local_fs (filename))
376  /* for file paths, display the full file path */
377  return gnc_uri_get_path ( filename );
378  else
379  /* for databases, display the full uri, except for the password */
380  return gnc_uri_normalize_uri ( filename, FALSE );
381 
382 }
383 
384 
402 static void
403 gnc_history_update_action (GncMainWindow *window,
404  gint index,
405  const gchar *filename)
406 {
407  GncMenuModelSearch *gsm = g_new0 (GncMenuModelSearch, 1);
408  gchar *action_name;
409  gint limit;
410  gboolean add_item = FALSE;
411  gint pos;
412 
413  ENTER("window %p, index %d, filename %s", window, index,
414  filename ? filename : "(null)");
415 
416  action_name = g_strdup_printf ("RecentFile%dAction", index);
417 
418  gsm->search_action_label = NULL;
419  gsm->search_action_name = action_name;
420 
421  if (!gnc_menubar_model_find_item (gnc_main_window_get_menu_model(window), gsm)) // could not find action_name
422  {
423  add_item = TRUE;
424  gsm->search_action_name = "FilePlaceholder6"; // placeholder
425 
427  {
428  LEAVE("Could not find 'menu_item' with action name '%s'", action_name);
429  g_free (gsm);
430  g_free (action_name);
431  return;
432  }
433  else
434  pos = gsm->index + index;
435  }
436  else
437  pos = gsm->index;
438 
439  limit = gnc_prefs_get_int (GNC_PREFS_GROUP_HISTORY,
440  GNC_PREF_HISTORY_MAXFILES);
441 
442  if (filename && (strlen(filename) > 0) && (index < limit))
443  {
444  GMenuItem *item;
445  gchar *label_name = gnc_history_generate_label (index, filename);
446  gchar *tooltip = gnc_history_generate_tooltip (index, filename);
447  gchar *full_action_name = g_strconcat (PLUGIN_ACTIONS_NAME, ".",
448  action_name, NULL);
449 
450  item = g_menu_item_new (label_name, full_action_name);
451 
452  g_menu_item_set_attribute (item, GNC_MENU_ATTRIBUTE_TOOLTIP, "s", tooltip);
453 
454  if (!add_item)
455  g_menu_remove (G_MENU(gsm->model), pos);
456 
457  g_menu_insert_item (G_MENU(gsm->model), pos, item);
458 
459  g_free (full_action_name);
460  g_free (label_name);
461  g_free (tooltip);
462  g_object_unref (item);
463  }
464  g_free (gsm);
465  g_free (action_name);
466  LEAVE("");
467 }
468 
469 
478 static void
479 gnc_history_update_menus (GncMainWindow *window)
480 {
481  gchar *filename, *pref;
482  guint i;
483 
484  ENTER("");
485 
486  for (i = 0; i < MAX_HISTORY_FILES; i++)
487  {
488  pref = gnc_history_index_to_pref_name(i);
489  filename = gnc_prefs_get_string(GNC_PREFS_GROUP_HISTORY, pref);
490  gnc_history_update_action(window, i, filename);
491  g_free(filename);
492  g_free(pref);
493  }
494  LEAVE("");
495 }
496 
497 
509 static void
510 gnc_plugin_history_list_changed (gpointer prefs,
511  gchar *pref,
512  gpointer user_data)
513 {
514  GncMainWindow *window;
515  gchar *filename;
516  gint index;
517 
518  ENTER("");
519  window = GNC_MAIN_WINDOW(user_data);
520 
521  if (strcmp(pref, GNC_PREF_HISTORY_MAXFILES) == 0)
522  {
523  gnc_history_update_menus (window);
524  LEAVE("updated maxfiles");
525  return;
526  }
527  index = gnc_history_pref_name_to_index(pref);
528  if (index < 0)
529  {
530  LEAVE("bad index");
531  return;
532  }
533 
534  filename = gnc_prefs_get_string (GNC_PREFS_GROUP_HISTORY, pref);
535  gnc_history_update_action (window, index, filename);
536  g_free (filename);
537 
538  LEAVE("");
539 }
540 
541 /************************************************************
542  * Object Implementation *
543  ************************************************************/
544 
545 G_DEFINE_TYPE(GncPluginFileHistory, gnc_plugin_file_history, GNC_TYPE_PLUGIN)
546 
547 
548 static void
549 gnc_plugin_file_history_class_init (GncPluginFileHistoryClass *klass)
550 {
551  GObjectClass *object_class = G_OBJECT_CLASS (klass);
552  GncPluginClass *plugin_class = GNC_PLUGIN_CLASS (klass);
553 
554  object_class->finalize = gnc_plugin_file_history_finalize;
555 
556  /* plugin info */
557  plugin_class->plugin_name = GNC_PLUGIN_FILE_HISTORY_NAME;
558 
559  /* function overrides */
560  plugin_class->add_to_window = gnc_plugin_file_history_add_to_window;
561  plugin_class->remove_from_window = gnc_plugin_file_history_remove_from_window;
562 
563  /* widget addition/removal */
564  plugin_class->actions_name = PLUGIN_ACTIONS_NAME;
565  plugin_class->actions = gnc_plugin_actions;
566  plugin_class->n_actions = gnc_plugin_n_actions;
567  plugin_class->ui_filename = PLUGIN_UI_FILENAME;
568  plugin_class->ui_updates = gnc_plugin_load_ui_items;
569 }
570 
571 
573 static void
574 gnc_plugin_file_history_init (GncPluginFileHistory *plugin)
575 {
576  ENTER("plugin %p", plugin);
577  LEAVE("");
578 }
579 
580 
582 static void
583 gnc_plugin_file_history_finalize (GObject *object)
584 {
585  g_return_if_fail (GNC_IS_PLUGIN_FILE_HISTORY (object));
586 
587  ENTER("plugin %p", object);
588  G_OBJECT_CLASS (gnc_plugin_file_history_parent_class)->finalize (object);
589  LEAVE("");
590 }
591 
592 
593 /* Create a new file history plugin. This plugin attaches the file
594  * history menu to any window that is opened.
595  */
596 GncPlugin *
598 {
599  GncPlugin *plugin_page = NULL;
600 
601  ENTER("");
602  plugin_page = GNC_PLUGIN (g_object_new (GNC_TYPE_PLUGIN_FILE_HISTORY, NULL));
603  LEAVE("plugin %p", plugin_page);
604  return plugin_page;
605 }
606 
607 /************************************************************
608  * Plugin Function Implementation *
609  ************************************************************/
610 
627 static void
628 gnc_plugin_file_history_add_to_window (GncPlugin *plugin,
629  GncMainWindow *window,
630  GQuark type)
631 {
632  gnc_prefs_register_cb (GNC_PREFS_GROUP_HISTORY, NULL,
633  gnc_plugin_history_list_changed, window);
634  gnc_history_update_menus(window);
635 }
636 
637 
649 static void
650 gnc_plugin_file_history_remove_from_window (GncPlugin *plugin,
651  GncMainWindow *window,
652  GQuark type)
653 {
654  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_HISTORY, NULL,
655  gnc_plugin_history_list_changed, window);
656 }
657 
658 /************************************************************
659  * Command Callbacks *
660  ************************************************************/
661 
674 static void
675 gnc_plugin_file_history_cmd_open_file (GSimpleAction *simple,
676  GVariant *parameter,
677  gpointer user_data)
678 
679 {
680  GncMainWindowActionData *data = user_data;
681  gchar *filename, *pref, *index;
682  const gchar *action_name;
683 
684  g_return_if_fail (G_IS_SIMPLE_ACTION(simple));
685  g_return_if_fail (data != NULL);
686 
687  // action name will be of the form 'RecentFile1Action'
688  action_name = g_action_get_name (G_ACTION(simple));
689 
690  index = g_utf8_substring (action_name, 10, 11);
691 
692  pref = gnc_history_index_to_pref_name (atoi (index));
693  filename = gnc_prefs_get_string (GNC_PREFS_GROUP_HISTORY, pref);
694 
695  PINFO("File to open is '%s' on action '%s'", filename, action_name);
696 
697  gnc_window_set_progressbar_window (GNC_WINDOW(data->window));
698  /* also opens new account page */
699  gnc_file_open_file (GTK_WINDOW (data->window),
700  filename, /*open_readonly*/ FALSE);
701  gnc_window_set_progressbar_window (NULL);
702 
703  g_free (pref);
704  g_free (filename);
705  g_free (index);
706 }
707 
gchar * gnc_prefs_get_string(const gchar *group, const gchar *pref_name)
Get a string value from the preferences backend.
gulong gnc_prefs_register_cb(const char *group, const gchar *pref_name, gpointer func, gpointer user_data)
Register a callback that gets triggered when the given preference changes.
Definition: gnc-prefs.c:128
void gnc_history_add_file(const char *newfile)
Add a file name to the front of the file "history list".
#define PINFO(format, args...)
Print an informational note.
Definition: qoflog.h:256
gtk helper routines.
GMenuModel * gnc_main_window_get_menu_model(GncMainWindow *window)
Return the GMenuModel for the main window menu bar.
gboolean gnc_menubar_model_find_item(GMenuModel *menu_model, GncMenuModelSearch *gsm)
Find a GtkMenu item from the action name.
Functions that are supported by all types of windows.
GncPlugin * gnc_plugin_file_history_new(void)
Create a new file history plugin.
GKeyFile helper routines.
gchar * gnc_uri_get_path(const gchar *uri)
Extracts the path part from a uri.
void gnc_prefs_reset(const gchar *group, const gchar *pref_name)
Reset a preference to its default value in the preferences backend.
Definition: gnc-prefs.c:361
gboolean gnc_prefs_set_string(const gchar *group, const gchar *pref_name, const gchar *value)
Store a string into the preferences backend.
Definition: gnc-prefs.c:319
#define ENTER(format, args...)
Print a function entry debugging message.
Definition: qoflog.h:272
gint gnc_prefs_get_int(const gchar *group, const gchar *pref_name)
Get an integer value from the preferences backend.
Functions for adding content to a window.
char * gnc_history_get_last(void)
Retrieve the name of the file most recently accessed.
gchar * gnc_uri_normalize_uri(const gchar *uri, gboolean allow_password)
Composes a normalized uri starting from any uri (filename, db spec,...).
Functions providing the file history menu.
void gnc_history_remove_file(const char *oldfile)
Remove all occurrences of a file name from the history list.
All type declarations for the whole Gnucash engine.
Generic api to store and retrieve preferences.
gboolean gnc_uri_targets_local_fs(const gchar *uri)
Checks if the given uri is either a valid file uri or a local filesystem path.
gboolean gnc_history_test_for_file(const char *oldfile)
Test for a file name existing in the history list.
#define PLUGIN_ACTIONS_NAME
The label given to the main window for this plugin.
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition: qoflog.h:282
Utility functions for convert uri in separate components and back.
The instance data structure for a main window object.
The instance data structure for a file history plugin.
#define PLUGIN_UI_FILENAME
The name of the UI description file for this plugin.
void gnc_prefs_remove_cb_by_func(const gchar *group, const gchar *pref_name, gpointer func, gpointer user_data)
Remove a function that was registered for a callback when the given preference changed.
Definition: gnc-prefs.c:143