GnuCash c935c2f+
Loading...
Searching...
No Matches
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
55static void gnc_plugin_file_history_finalize (GObject *object);
56
57static void gnc_plugin_file_history_add_to_window (GncPlugin *plugin, GncMainWindow *window, GQuark type);
58static void gnc_plugin_file_history_remove_from_window (GncPlugin *plugin, GncMainWindow *window, GQuark type);
59
60
62static QofLogModule log_module = GNC_MOD_GUI;
63
64/* Command callbacks */
65static 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"
70#define PLUGIN_UI_FILENAME "gnc-plugin-file-history.ui"
71
72#define GNOME1_HISTORY "History"
73#define GNOME1_MAXFILES "MaxFiles"
74
80static 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};
94static guint gnc_plugin_n_actions = G_N_ELEMENTS(gnc_plugin_actions);
95
97static 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
120static gchar *
121gnc_history_index_to_pref_name (guint index)
122{
123 return g_strdup_printf(HISTORY_STRING_FILE_N, index);
124}
125
126
135static gint
136gnc_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 */
154void
155gnc_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
223void
224gnc_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
267gboolean 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 */
304char *
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
330static gchar *
331gnc_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
371static gchar *
372gnc_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
402static void
403gnc_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
411 ENTER("window %p, index %d, filename %s", window, index,
412 filename ? filename : "(null)");
413
414 action_name = g_strdup_printf ("RecentFile%dAction", index);
415
416 gsm->search_action_label = NULL;
417 gsm->search_action_name = action_name;
418
420 {
421 g_menu_remove (G_MENU(gsm->model), gsm->index);
422 }
423 else // could not find action_name
424 {
425 gsm->search_action_name = "FilePlaceholder6"; // placeholder
426
428 {
429 LEAVE("Could not find 'menu_item' with action name '%s'", action_name);
430 g_free (gsm);
431 g_free (action_name);
432 return;
433 }
434 gsm->index += index;
435 }
436
437 limit = gnc_prefs_get_int (GNC_PREFS_GROUP_HISTORY, GNC_PREF_HISTORY_MAXFILES);
438
439 if (filename && *filename && index < limit)
440 {
441 GMenuItem *item;
442 gchar *label_name = gnc_history_generate_label (index, filename);
443 gchar *tooltip = gnc_history_generate_tooltip (index, filename);
444 gchar *full_action_name = g_strconcat (PLUGIN_ACTIONS_NAME, ".",
445 action_name, NULL);
446
447 item = g_menu_item_new (label_name, full_action_name);
448 g_menu_item_set_attribute (item, GNC_MENU_ATTRIBUTE_TOOLTIP, "s", tooltip);
449 g_menu_insert_item (G_MENU(gsm->model), gsm->index, item);
450
451 g_free (full_action_name);
452 g_free (label_name);
453 g_free (tooltip);
454 g_object_unref (item);
455 }
456 g_free (gsm);
457 g_free (action_name);
458 LEAVE("");
459}
460
461
470static void
471gnc_history_update_menus (GncMainWindow *window)
472{
473 gchar *filename, *pref;
474 guint i;
475
476 ENTER("");
477
478 for (i = 0; i < MAX_HISTORY_FILES; i++)
479 {
480 pref = gnc_history_index_to_pref_name(i);
481 filename = gnc_prefs_get_string(GNC_PREFS_GROUP_HISTORY, pref);
482 gnc_history_update_action(window, i, filename);
483 g_free(filename);
484 g_free(pref);
485 }
486 LEAVE("");
487}
488
489
501static void
502gnc_plugin_history_list_changed (gpointer prefs,
503 gchar *pref,
504 gpointer user_data)
505{
506 GncMainWindow *window;
507 gchar *filename;
508 gint index;
509
510 ENTER("");
511 window = GNC_MAIN_WINDOW(user_data);
512
513 if (strcmp(pref, GNC_PREF_HISTORY_MAXFILES) == 0)
514 {
515 gnc_history_update_menus (window);
516 LEAVE("updated maxfiles");
517 return;
518 }
519 index = gnc_history_pref_name_to_index(pref);
520 if (index < 0)
521 {
522 LEAVE("bad index");
523 return;
524 }
525
526 filename = gnc_prefs_get_string (GNC_PREFS_GROUP_HISTORY, pref);
527 gnc_history_update_action (window, index, filename);
528 g_free (filename);
529
530 LEAVE("");
531}
532
533/************************************************************
534 * Object Implementation *
535 ************************************************************/
536
537G_DEFINE_TYPE(GncPluginFileHistory, gnc_plugin_file_history, GNC_TYPE_PLUGIN)
538
539
540static void
541gnc_plugin_file_history_class_init (GncPluginFileHistoryClass *klass)
542{
543 GObjectClass *object_class = G_OBJECT_CLASS (klass);
544 GncPluginClass *plugin_class = GNC_PLUGIN_CLASS (klass);
545
546 object_class->finalize = gnc_plugin_file_history_finalize;
547
548 /* plugin info */
549 plugin_class->plugin_name = GNC_PLUGIN_FILE_HISTORY_NAME;
550
551 /* function overrides */
552 plugin_class->add_to_window = gnc_plugin_file_history_add_to_window;
553 plugin_class->remove_from_window = gnc_plugin_file_history_remove_from_window;
554
555 /* widget addition/removal */
556 plugin_class->actions_name = PLUGIN_ACTIONS_NAME;
557 plugin_class->actions = gnc_plugin_actions;
558 plugin_class->n_actions = gnc_plugin_n_actions;
559 plugin_class->ui_filename = PLUGIN_UI_FILENAME;
560 plugin_class->ui_updates = gnc_plugin_load_ui_items;
561}
562
563
565static void
566gnc_plugin_file_history_init (GncPluginFileHistory *plugin)
567{
568 ENTER("plugin %p", plugin);
569 LEAVE("");
570}
571
572
574static void
575gnc_plugin_file_history_finalize (GObject *object)
576{
577 g_return_if_fail (GNC_IS_PLUGIN_FILE_HISTORY (object));
578
579 ENTER("plugin %p", object);
580 G_OBJECT_CLASS (gnc_plugin_file_history_parent_class)->finalize (object);
581 LEAVE("");
582}
583
584
585/* Create a new file history plugin. This plugin attaches the file
586 * history menu to any window that is opened.
587 */
588GncPlugin *
590{
591 GncPlugin *plugin_page = NULL;
592
593 ENTER("");
594 plugin_page = GNC_PLUGIN (g_object_new (GNC_TYPE_PLUGIN_FILE_HISTORY, NULL));
595 LEAVE("plugin %p", plugin_page);
596 return plugin_page;
597}
598
599/************************************************************
600 * Plugin Function Implementation *
601 ************************************************************/
602
619static void
620gnc_plugin_file_history_add_to_window (GncPlugin *plugin,
621 GncMainWindow *window,
622 GQuark type)
623{
624 gnc_prefs_register_cb (GNC_PREFS_GROUP_HISTORY, NULL,
625 gnc_plugin_history_list_changed, window);
626 gnc_history_update_menus(window);
627}
628
629
641static void
642gnc_plugin_file_history_remove_from_window (GncPlugin *plugin,
643 GncMainWindow *window,
644 GQuark type)
645{
646 gnc_prefs_remove_cb_by_func (GNC_PREFS_GROUP_HISTORY, NULL,
647 gnc_plugin_history_list_changed, window);
648}
649
650/************************************************************
651 * Command Callbacks *
652 ************************************************************/
653
666static void
667gnc_plugin_file_history_cmd_open_file (GSimpleAction *simple,
668 GVariant *parameter,
669 gpointer user_data)
670
671{
672 GncMainWindowActionData *data = user_data;
673 gchar *filename, *pref, *index;
674 const gchar *action_name;
675
676 g_return_if_fail (G_IS_SIMPLE_ACTION(simple));
677 g_return_if_fail (data != NULL);
678
679 if (!gnc_main_window_finish_pending(data->window))
680 return;
681 // action name will be of the form 'RecentFile1Action'
682 action_name = g_action_get_name (G_ACTION(simple));
683
684 index = g_utf8_substring (action_name, 10, 11);
685
686 pref = gnc_history_index_to_pref_name (atoi (index));
687 filename = gnc_prefs_get_string (GNC_PREFS_GROUP_HISTORY, pref);
688
689 PINFO("File to open is '%s' on action '%s'", filename, action_name);
690
691 gnc_window_set_progressbar_window (GNC_WINDOW(data->window));
692 /* also opens new account page */
693 gnc_file_open_file (GTK_WINDOW (data->window),
694 filename, /*open_readonly*/ FALSE);
695 gnc_window_set_progressbar_window (NULL);
696
697 g_free (pref);
698 g_free (filename);
699 g_free (index);
700}
701
All type declarations for the whole Gnucash engine.
GKeyFile helper routines.
gtk helper routines.
Functions for adding content to a window.
Functions providing the file history menu.
Generic api to store and retrieve preferences.
Utility functions for convert uri in separate components and back.
Functions that are supported by all types of windows.
gboolean gnc_main_window_finish_pending(GncMainWindow *window)
Tell a window to finish any outstanding activities.
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.
#define PINFO(format, args...)
Print an informational note.
Definition qoflog.h:256
#define LEAVE(format, args...)
Print a function exit debugging message.
Definition qoflog.h:282
#define ENTER(format, args...)
Print a function entry debugging message.
Definition qoflog.h:272
void gnc_history_add_file(const char *newfile)
Add a file name to the front of the file "history list".
#define PLUGIN_ACTIONS_NAME
The label given to the main window for this plugin.
void gnc_history_remove_file(const char *oldfile)
Remove all occurrences of a file name from the history list.
#define PLUGIN_UI_FILENAME
The name of the UI description file for this plugin.
gboolean gnc_history_test_for_file(const char *oldfile)
Test for a file name existing in the history list.
char * gnc_history_get_last(void)
Retrieve the name of the file most recently accessed.
GncPlugin * gnc_plugin_file_history_new(void)
Create a new file history 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.
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.
gint gnc_prefs_get_int(const gchar *group, const gchar *pref_name)
Get an integer value from the preferences backend.
void gnc_prefs_reset(const gchar *group, const gchar *pref_name)
Reset a preference to its default value in the preferences backend.
gchar * gnc_prefs_get_string(const gchar *group, const gchar *pref_name)
Get a string value from the preferences backend.
gboolean gnc_prefs_set_string(const gchar *group, const gchar *pref_name, const gchar *value)
Store a string into the preferences backend.
gchar * gnc_uri_get_path(const gchar *uri)
Extracts the path part from a uri.
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.
gchar * gnc_uri_normalize_uri(const gchar *uri, gboolean allow_password)
Composes a normalized uri starting from any uri (filename, db spec,...).
The instance data structure for a file history plugin.