GnuCash  4.11-243-g1cac132214+
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 
48 static GObjectClass *parent_class = NULL;
49 
50 #define FILENAME_STRING "filename"
51 #define MAX_HISTORY_FILES 10 /* May be any number up to 10 */
52 #define GNC_PREFS_GROUP_HISTORY "history"
53 #define GNC_PREF_HISTORY_MAXFILES "maxfiles"
54 #define HISTORY_STRING_FILE_N "file%d"
55 
56 static void gnc_plugin_file_history_class_init (GncPluginFileHistoryClass *klass);
57 static void gnc_plugin_file_history_init (GncPluginFileHistory *plugin);
58 static void gnc_plugin_file_history_finalize (GObject *object);
59 
60 static void gnc_plugin_file_history_add_to_window (GncPlugin *plugin, GncMainWindow *window, GQuark type);
61 static void gnc_plugin_file_history_remove_from_window (GncPlugin *plugin, GncMainWindow *window, GQuark type);
62 
63 
65 static QofLogModule log_module = GNC_MOD_GUI;
66 
67 /* Command callbacks */
68 static void gnc_plugin_file_history_cmd_open_file (GtkAction *action, GncMainWindowActionData *data);
69 
71 #define PLUGIN_ACTIONS_NAME "gnc-plugin-file-history-actions"
72 
73 #define PLUGIN_UI_FILENAME "gnc-plugin-file-history-ui.xml"
74 
75 #define GNOME1_HISTORY "History"
76 #define GNOME1_MAXFILES "MaxFiles"
77 
83 static GtkActionEntry gnc_plugin_actions [] =
84 {
85  { "RecentFile0Action", NULL, "", NULL, NULL, G_CALLBACK (gnc_plugin_file_history_cmd_open_file) },
86  { "RecentFile1Action", NULL, "", NULL, NULL, G_CALLBACK (gnc_plugin_file_history_cmd_open_file) },
87  { "RecentFile2Action", NULL, "", NULL, NULL, G_CALLBACK (gnc_plugin_file_history_cmd_open_file) },
88  { "RecentFile3Action", NULL, "", NULL, NULL, G_CALLBACK (gnc_plugin_file_history_cmd_open_file) },
89  { "RecentFile4Action", NULL, "", NULL, NULL, G_CALLBACK (gnc_plugin_file_history_cmd_open_file) },
90  { "RecentFile5Action", NULL, "", NULL, NULL, G_CALLBACK (gnc_plugin_file_history_cmd_open_file) },
91  { "RecentFile6Action", NULL, "", NULL, NULL, G_CALLBACK (gnc_plugin_file_history_cmd_open_file) },
92  { "RecentFile7Action", NULL, "", NULL, NULL, G_CALLBACK (gnc_plugin_file_history_cmd_open_file) },
93  { "RecentFile8Action", NULL, "", NULL, NULL, G_CALLBACK (gnc_plugin_file_history_cmd_open_file) },
94  { "RecentFile9Action", NULL, "", NULL, NULL, G_CALLBACK (gnc_plugin_file_history_cmd_open_file) },
95 };
97 static guint gnc_plugin_n_actions = G_N_ELEMENTS (gnc_plugin_actions);
98 
99 
103 {
104  gpointer dummy;
106 
107 
108 #define GNC_PLUGIN_FILE_HISTORY_GET_PRIVATE(o) \
109  ((GncPluginFileHistoryPrivate*)gnc_plugin_file_history_get_instance_private((GncPluginFileHistory*)o))
110 
111 /************************************************************
112  * Other Functions *
113  ************************************************************/
114 
123 static gchar *
124 gnc_history_index_to_pref_name (guint index)
125 {
126  return g_strdup_printf(HISTORY_STRING_FILE_N, index);
127 }
128 
129 
138 static gint
139 gnc_history_pref_name_to_index (const gchar *pref)
140 {
141  gint index, result;
142 
143  result = sscanf(pref, HISTORY_STRING_FILE_N, &index);
144  if (result != 1)
145  return -1;
146  if ((index < 0) || (index >= gnc_plugin_n_actions))
147  return -1;
148  return index;
149 }
150 
151 
152 /* Add a file name to the front of the file "history list". If the
153  * name already exist on the list, then it is moved from its current
154  * location to the front of the list. The "list" is actually a
155  * sequence of up to ten preferences.
156  */
157 void
158 gnc_history_add_file (const char *newfile)
159 {
160  gchar *filename, *from, *to;
161  gint i, last;
162 
163  if (newfile == NULL)
164  return;
165  if (!g_utf8_validate(newfile, -1, NULL))
166  return;
167 
168  /*
169  * Look for the filename in preferences.
170  */
171  last = MAX_HISTORY_FILES - 1;
172  for (i = 0; i < MAX_HISTORY_FILES; i++)
173  {
174  from = gnc_history_index_to_pref_name(i);
175  filename = gnc_prefs_get_string(GNC_PREFS_GROUP_HISTORY, from);
176  g_free(from);
177 
178  if (!filename)
179  {
180  last = i;
181  break;
182  }
183  if (g_utf8_collate(newfile, filename) == 0)
184  {
185  g_free(filename);
186  last = i;
187  break;
188  }
189  g_free(filename);
190  }
191 
192  /*
193  * Shuffle filenames upward through preferences.
194  */
195  to = gnc_history_index_to_pref_name(last);
196  for (i = last - 1; i >= 0; i--)
197  {
198  from = gnc_history_index_to_pref_name(i);
199  filename = gnc_prefs_get_string(GNC_PREFS_GROUP_HISTORY, from);
200  if (filename && *filename)
201  {
202  gnc_prefs_set_string(GNC_PREFS_GROUP_HISTORY, to, filename);
203  }
204  else
205  {
206  gnc_prefs_reset(GNC_PREFS_GROUP_HISTORY, to);
207  }
208  g_free(filename);
209  g_free(to);
210  to = from;
211  }
212 
213  /*
214  * Store the new zero entry.
215  */
216  gnc_prefs_set_string(GNC_PREFS_GROUP_HISTORY, to, newfile);
217  g_free(to);
218 }
219 
220 
226 void
227 gnc_history_remove_file (const char *oldfile)
228 {
229  gchar *filename, *from, *to;
230  gint i, j;
231 
232  if (!oldfile)
233  return;
234  if (!g_utf8_validate(oldfile, -1, NULL))
235  return;
236 
237  for (i = 0, j = 0; i < MAX_HISTORY_FILES; i++)
238  {
239  from = gnc_history_index_to_pref_name(i);
240  filename = gnc_prefs_get_string(GNC_PREFS_GROUP_HISTORY, from);
241 
242  if (filename)
243  {
244  if (g_utf8_collate(oldfile, filename) == 0)
245  {
246  gnc_prefs_reset(GNC_PREFS_GROUP_HISTORY, from);
247  }
248  else
249  {
250  if (i != j)
251  {
252  to = gnc_history_index_to_pref_name(j);
253  gnc_prefs_set_string(GNC_PREFS_GROUP_HISTORY, to, filename);
254  gnc_prefs_reset(GNC_PREFS_GROUP_HISTORY, from);
255  g_free(to);
256  }
257  j++;
258  }
259  g_free (filename);
260  }
261  g_free(from);
262  }
263 }
264 
265 
270 gboolean gnc_history_test_for_file (const char *oldfile)
271 {
272  gchar *filename, *from;
273  gint i;
274  gboolean found = FALSE;
275 
276  if (!oldfile)
277  return FALSE;
278  if (!g_utf8_validate(oldfile, -1, NULL))
279  return FALSE;
280 
281  for (i = 0; i < MAX_HISTORY_FILES; i++)
282  {
283  from = gnc_history_index_to_pref_name(i);
284  filename = gnc_prefs_get_string(GNC_PREFS_GROUP_HISTORY, from);
285  g_free(from);
286 
287  if (!filename)
288  continue;
289 
290  if (g_utf8_collate(oldfile, filename) == 0)
291  {
292  found = TRUE;
293  g_free (filename);
294  break;
295  }
296  g_free (filename);
297  }
298 
299  return found;
300 }
301 
302 
303 /* Retrieve the name of the file most recently accessed. This is the
304  * name at the front of the list. Since the "list" is actually a
305  * sequence of up to ten preference names, this is the value of the first preference.
306  */
307 char *
309 {
310  char *filename, *pref;
311 
312  pref = gnc_history_index_to_pref_name(0);
313  filename = gnc_prefs_get_string(GNC_PREFS_GROUP_HISTORY, pref);
314  g_free(pref);
315 
316  return filename;
317 }
318 
319 
320 /************************************************************
321  * Other Functions *
322  ************************************************************/
323 
333 static gchar *
334 gnc_history_generate_label (int index, const gchar *filename)
335 {
336  gchar *label, *result;
337  gchar **splitlabel;
338 
339  if (gnc_uri_targets_local_fs (filename))
340  {
341  /* for file paths, only display the file name */
342  gchar *filepath = gnc_uri_get_path ( filename );
343  label = g_path_get_basename ( filepath );
344  g_free ( filepath );
345  }
346  else
347  {
348  /* for databases, display the full uri, except for the password */
349  label = gnc_uri_normalize_uri ( filename, FALSE );
350  }
351 
352  /* Escape '_' characters */
353  splitlabel = g_strsplit ( label, "_", 0);
354  g_free (label);
355  label = g_strjoinv ( "__", splitlabel);
356  g_strfreev (splitlabel);
357 
358  result = g_strdup_printf ( "_%d %s", (index + 1) % 10, label);
359  g_free ( label );
360  return result;
361 
362 }
363 
374 static gchar *
375 gnc_history_generate_tooltip (int index, const gchar *filename)
376 {
377 
378  if (gnc_uri_targets_local_fs (filename))
379  /* for file paths, display the full file path */
380  return gnc_uri_get_path ( filename );
381  else
382  /* for databases, display the full uri, except for the password */
383  return gnc_uri_normalize_uri ( filename, FALSE );
384 
385 }
386 
387 
405 static void
406 gnc_history_update_action (GncMainWindow *window,
407  gint index,
408  const gchar *filename)
409 {
410  GtkActionGroup *action_group;
411  GtkAction *action;
412  gchar *action_name, *label_name, *tooltip, *old_filename;
413  gint limit;
414 
415  ENTER("window %p, index %d, filename %s", window, index,
416  filename ? filename : "(null)");
417  /* Get the action group */
418  action_group =
420 
421  action_name = g_strdup_printf("RecentFile%dAction", index);
422  action = gtk_action_group_get_action (action_group, action_name);
423 
424  limit = gnc_prefs_get_int (GNC_PREFS_GROUP_HISTORY,
425  GNC_PREF_HISTORY_MAXFILES);
426 
427  if (filename && (strlen(filename) > 0) && (index < limit))
428  {
429  /* set the menu label (w/accelerator) */
430  label_name = gnc_history_generate_label(index, filename);
431  tooltip = gnc_history_generate_tooltip(index, filename);
432  g_object_set(G_OBJECT(action), "label", label_name,
433  "tooltip", tooltip,
434  "visible", TRUE,
435  NULL);
436  g_free(label_name);
437  g_free(tooltip);
438 
439  /* set the filename for the callback function */
440  old_filename = g_object_get_data(G_OBJECT(action), FILENAME_STRING);
441  if (old_filename)
442  g_free(old_filename);
443  g_object_set_data(G_OBJECT(action), FILENAME_STRING, g_strdup(filename));
444  }
445  else
446  {
447  gtk_action_set_visible(action, FALSE);
448  }
449  g_free(action_name);
450  LEAVE("");
451 }
452 
453 
462 static void
463 gnc_history_update_menus (GncMainWindow *window)
464 {
465  gchar *filename, *pref;
466  guint i;
467 
468  ENTER("");
469  for (i = 0; i < MAX_HISTORY_FILES; i++)
470  {
471  pref = gnc_history_index_to_pref_name(i);
472  filename = gnc_prefs_get_string(GNC_PREFS_GROUP_HISTORY, pref);
473  gnc_history_update_action(window, i, filename);
474  g_free(filename);
475  g_free(pref);
476  }
477  LEAVE("");
478 }
479 
480 
492 static void
493 gnc_plugin_history_list_changed (gpointer prefs,
494  gchar *pref,
495  gpointer user_data)
496 {
497  GncMainWindow *window;
498  gchar *filename;
499  gint index;
500 
501  ENTER("");
502  window = GNC_MAIN_WINDOW(user_data);
503 
504  if (strcmp(pref, GNC_PREF_HISTORY_MAXFILES) == 0)
505  {
506  gnc_history_update_menus (window);
507  LEAVE("updated maxfiles");
508  return;
509  }
510  index = gnc_history_pref_name_to_index(pref);
511  if (index < 0)
512  {
513  LEAVE("bad index");
514  return;
515  }
516 
517  filename = gnc_prefs_get_string (GNC_PREFS_GROUP_HISTORY, pref);
518  gnc_history_update_action (window, index, filename);
519  g_free (filename);
520 
522  LEAVE("");
523 }
524 
525 /************************************************************
526  * Object Implementation *
527  ************************************************************/
528 
530 static void
531 gnc_plugin_file_history_class_init (GncPluginFileHistoryClass *klass)
532 {
533  GObjectClass *object_class = G_OBJECT_CLASS (klass);
534  GncPluginClass *plugin_class = GNC_PLUGIN_CLASS (klass);
535 
536  parent_class = g_type_class_peek_parent (klass);
537 
538  object_class->finalize = gnc_plugin_file_history_finalize;
539 
540  /* plugin info */
541  plugin_class->plugin_name = GNC_PLUGIN_FILE_HISTORY_NAME;
542 
543  /* function overrides */
544  plugin_class->add_to_window = gnc_plugin_file_history_add_to_window;
545  plugin_class->remove_from_window =
546  gnc_plugin_file_history_remove_from_window;
547 
548  /* widget addition/removal */
549  plugin_class->actions_name = PLUGIN_ACTIONS_NAME;
550  plugin_class->actions = gnc_plugin_actions;
551  plugin_class->n_actions = gnc_plugin_n_actions;
552  plugin_class->ui_filename = PLUGIN_UI_FILENAME;
553 }
554 
555 G_DEFINE_TYPE_WITH_PRIVATE(GncPluginFileHistory, gnc_plugin_file_history, GNC_TYPE_PLUGIN)
556 
557 
558 static void
559 gnc_plugin_file_history_init (GncPluginFileHistory *plugin)
560 {
561  ENTER("plugin %p", plugin);
562  LEAVE("");
563 }
564 
565 
567 static void
568 gnc_plugin_file_history_finalize (GObject *object)
569 {
570  g_return_if_fail (GNC_IS_PLUGIN_FILE_HISTORY (object));
571 
572  ENTER("plugin %p", object);
573  G_OBJECT_CLASS (parent_class)->finalize (object);
574  LEAVE("");
575 }
576 
577 
578 /* Create a new file history plugin. This plugin attaches the file
579  * history menu to any window that is opened.
580  */
581 GncPlugin *
583 {
584  GncPlugin *plugin_page = NULL;
585 
586  ENTER("");
587  plugin_page = GNC_PLUGIN (g_object_new (GNC_TYPE_PLUGIN_FILE_HISTORY, NULL));
588  LEAVE("plugin %p", plugin_page);
589  return plugin_page;
590 }
591 
592 /************************************************************
593  * Plugin Function Implementation *
594  ************************************************************/
595 
612 static void
613 gnc_plugin_file_history_add_to_window (GncPlugin *plugin,
614  GncMainWindow *window,
615  GQuark type)
616 {
617  gnc_prefs_register_cb (GNC_PREFS_GROUP_HISTORY, NULL,
618  gnc_plugin_history_list_changed, window);
619  gnc_history_update_menus(window);
620 }
621 
622 
634 static void
635 gnc_plugin_file_history_remove_from_window (GncPlugin *plugin,
636  GncMainWindow *window,
637  GQuark type)
638 {
639  gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_HISTORY, NULL,
640  gnc_plugin_history_list_changed, window);
641 }
642 
643 /************************************************************
644  * Command Callbacks *
645  ************************************************************/
646 
659 static void
660 gnc_plugin_file_history_cmd_open_file (GtkAction *action,
662 {
663  gchar *filename;
664 
665  g_return_if_fail(GTK_IS_ACTION(action));
666  g_return_if_fail(data != NULL);
667 
668  /* DRH - Do we need to close all open windows but the first?
669  * Which progress bar should we be using? One in a window, or
670  * in a new "file loading" dialog???
671  */
672  filename = g_object_get_data(G_OBJECT(action), FILENAME_STRING);
673  gnc_window_set_progressbar_window (GNC_WINDOW(data->window));
674  /* also opens new account page */
675  gnc_file_open_file (GTK_WINDOW (data->window),
676  filename, /*open_readonly*/ FALSE);
677  gnc_window_set_progressbar_window (NULL);
678 }
679 
guint n_actions
The number of actions in the actions array.
Definition: gnc-plugin.h:124
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".
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.
The instance data structure for a file history plugin.
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
The class data structure for a menu-only plugin.
Definition: gnc-plugin.h:107
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
const gchar * ui_filename
The relative name of the XML file describing the menu/toolbar action items.
Definition: gnc-plugin.h:137
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.
GtkActionEntry * actions
An array of actions that should automatically be added to any GnuCash "main" content window that is o...
Definition: gnc-plugin.h:122
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.
void(* remove_from_window)(GncPlugin *plugin, GncMainWindow *window, GQuark type)
A callback that will be invoked when this plugin is removed from a window.
Definition: gnc-plugin.h:171
All type declarations for the whole Gnucash engine.
void(* add_to_window)(GncPlugin *plugin, GncMainWindow *window, GQuark type)
A callback that will be invoked when this plugin is added to a window.
Definition: gnc-plugin.h:155
const gchar * actions_name
A name for the set of actions that will be added by this plugin.
Definition: gnc-plugin.h:119
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.
GtkActionGroup * gnc_main_window_get_action_group(GncMainWindow *window, const gchar *group_name)
Retrieve a specific set of user interface actions from a window.
The class data structure for a file history plugin.
const gchar * plugin_name
The textual name of this plugin.
Definition: gnc-plugin.h:112
The instance private data for a file history plugin.
The instance data structure for a menu-only plugin.
Definition: gnc-plugin.h:100
#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.
void gnc_main_window_actions_updated(GncMainWindow *window)
Force a full update of the user interface for the specified window.
#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