1 /* -*- Mode: C; c-basic-offset: 4 -*- */
2 /* Dia -- an diagram creation/manipulation program
3 * Copyright (C) 1998-2000 Alexander Larsson
5 * recent_files.c: recent files menu dia
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29 #include <gdk/gdkkeysyms.h>
38 #include "recent_files.h"
42 #include "interface.h"
43 #include "layer_dialog.h"
44 #include "preferences.h"
45 #include "../lib/filter.h"
46 #include "../lib/intl.h"
49 static GList
*recent_files
= NULL
;
50 static guint recent_files_length
= 0;
51 static guint recent_files_menuitem_offset
= 0;
52 static GtkTooltips
*tooltips
= 0;
54 /* file and import filter name */
55 typedef struct _RecentFileData
58 gchar
*importfilterdescription
;
61 static void open_recent_file_callback (GtkWidget
*widget
, RecentFileData
*file
);
62 void recent_file_history_remove (const char *fname
);
64 static RecentFileData
*
65 recent_file_filedata_new(const char *fname
, DiaImportFilter
*ifilter
)
67 RecentFileData
*filedata
;
69 filedata
= g_new(RecentFileData
, 1);
70 filedata
->filename
= g_strdup(fname
);
73 filedata
->importfilterdescription
74 = g_strdup(ifilter
->description
);
76 filedata
->importfilterdescription
= NULL
;
82 recent_file_menuitem_create(GtkWidget
*menu
,
83 RecentFileData
*filedata
, guint pos
)
85 gchar
*basename
, *label
;
87 GtkAccelGroup
*accel_group
;
89 basename
= g_strdup(g_basename(filedata
->filename
));
90 basename
= g_strdelimit(basename
, "_", '\\');
91 basename
= g_strescape(basename
, NULL
);
92 basename
= g_strdelimit(basename
, "\\", '_');
94 label
= g_strdup_printf("%d. %s", pos
, basename
);
95 item
= gtk_menu_item_new_with_label(label
);
97 gtk_menu_insert(GTK_MENU(menu
), item
,
98 pos
+ recent_files_menuitem_offset
);
100 gtk_signal_connect(GTK_OBJECT(item
), "activate",
101 GTK_SIGNAL_FUNC(open_recent_file_callback
),filedata
);
103 gtk_tooltips_set_tip(GTK_TOOLTIPS(tooltips
), item
,
104 filedata
->filename
, NULL
);
108 accel_group
= gtk_accel_group_new();
109 gtk_window_add_accel_group(GTK_WINDOW(
110 interface_get_toolbox_shell()),
112 gtk_widget_add_accelerator(item
, "activate", accel_group
,
114 GDK_CONTROL_MASK
, GTK_ACCEL_VISIBLE
);
117 gtk_widget_show(item
);
120 /* gtk_label_set_text() g_strdup's our label, so... */
125 recent_file_compare_fnames(gconstpointer element
, gconstpointer userdata
)
127 /* no g_strcmp() in GLib */
128 return strcmp(((RecentFileData
*)element
)->filename
, userdata
);
132 recent_file_filemenu_get(void)
134 /* Use the Plugins menu item to get a pointer to the File menu,
135 but any item on the File menu will do */
137 return GTK_WIDGET(menus_get_item_from_path(N_("<Toolbox>/File/Plugins"),
142 recent_file_history_add(const char *fname
, DiaImportFilter
*ifilter
,
143 guint is_initial_load
)
145 GtkWidget
*file_menu
;
146 RecentFileData
*filedata
;
147 guint i
, number_of_items
;
148 GList
*menu_items
, *item
, *next
;
150 file_menu
= recent_file_filemenu_get();
152 /* An initial load places filename items in natural order 1, 2, 3,...
153 but new items get inserted in position 1, forcing other items down */
157 filedata
= recent_file_filedata_new(fname
, ifilter
);
158 recent_files
= g_list_append(recent_files
, filedata
);
160 i
= g_list_length(recent_files
);
162 if (i
<= recent_files_length
)
163 recent_file_menuitem_create(file_menu
, filedata
, i
);
167 /* Since the recent filenames on the menu have positional
168 text and accellerators, delete the existing recent file
169 menu items; start fresh each time */
171 menu_items
= GTK_MENU_SHELL(file_menu
)->children
;
172 item
= g_list_first(menu_items
);
174 for (i
= 0; i
<= recent_files_menuitem_offset
; i
++)
175 item
= g_list_next(item
);
177 number_of_items
= MIN(g_list_length(recent_files
),
178 recent_files_length
);
180 for (i
= 0; i
< number_of_items
; i
++)
182 next
= g_list_next(item
);
184 /* Unlink first, then destroy */
186 menu_items
= g_list_remove_link(menu_items
, item
);
187 gtk_widget_destroy(item
->data
);
195 a) The filename is not in the 'recent_files' list--
196 insert it in position 1;
197 b) The filename is in the 'recent_files' list but not
198 displayed because it is clipped by 'recent_files_length'--
199 move it to position 1;
200 c) The filename is displayed on the menu--leave it in place.
203 item
= g_list_find_custom(recent_files
, (gpointer
)fname
,
204 recent_file_compare_fnames
);
207 i
= g_list_position(recent_files
, item
);
209 /* if (i >= recent_files_length) */
211 recent_files
= g_list_remove_link(recent_files
,
213 recent_files
= g_list_concat(item
,
219 filedata
= recent_file_filedata_new(fname
, ifilter
);
220 recent_files
= g_list_prepend(recent_files
, filedata
);
223 number_of_items
= MIN(g_list_length(recent_files
),
224 recent_files_length
);
226 item
= g_list_first(recent_files
);
228 for (i
= 1; i
<= number_of_items
; i
++)
230 recent_file_menuitem_create(file_menu
, item
->data
, i
);
231 item
= g_list_next(item
);
236 /* load the recent file history */
238 recent_file_history_init() {
240 char *buffer
, *history_filename
, *filename
;
241 DiaImportFilter
*ifilter
;
242 GtkWidget
*file_menu
;
243 GtkMenuItem
*menu_item
;
246 /* should be ~/.dia/history */
247 history_filename
= dia_config_filename("history");
248 if((fp
= fopen(history_filename
, "r")) == NULL
) {
249 g_free(history_filename
);
253 /* Must restart dia to use new prefs value */
255 prefs
.recent_documents_list_size
=
256 CLAMP(prefs
.recent_documents_list_size
, 0, 16);
257 recent_files_length
= prefs
.recent_documents_list_size
;
259 menu_item
= menus_get_item_from_path(N_("<Toolbox>/File/Quit"), NULL
);
261 file_menu
= recent_file_filemenu_get();
263 list_item
= g_list_find(GTK_MENU_SHELL(file_menu
)->children
,
264 (gpointer
)menu_item
);
266 recent_files_menuitem_offset
267 = g_list_position(GTK_MENU_SHELL(file_menu
)->children
,
268 list_item
) - 2; /* fudge factor */
270 tooltips
= gtk_tooltips_new();
272 buffer
= g_new(char, 16000);
273 while (fgets(buffer
, 16000, fp
)!=NULL
) {
274 filename
= g_strchomp(buffer
);
275 ifilter
= filter_guess_import_filter(filename
);
276 recent_file_history_add(filename
, ifilter
, 1);
281 g_free(history_filename
);
284 /*save the recent file history */
286 recent_file_history_write() {
287 GList
*recent_list_pointer
;
289 gchar
*history_filename
;
290 RecentFileData
*filedata
;
292 /* should be ~/.dia/history */
293 history_filename
= dia_config_filename("history");
294 if((fp
= fopen(history_filename
,"w")) == NULL
) {
295 message_error(N_("Can't open history file for writing."));
298 recent_list_pointer
= g_list_first(recent_files
);
299 while(recent_list_pointer
!= NULL
) {
300 filedata
= (RecentFileData
*)recent_list_pointer
->data
;
301 fprintf(fp
, "%s\n", filedata
->filename
);
303 if(filedata
->filename
)g_free(filedata
->filename
);
304 if(filedata
->importfilterdescription
)g_free(filedata
->importfilterdescription
);
307 recent_list_pointer
= g_list_next(recent_list_pointer
);
310 g_list_free(recent_files
);
313 /* remove a broken file from the history and update menu accordingly
314 * Xing Wang, 2002.06 */
316 recent_file_history_remove (const char *fname
)
318 GtkWidget
*file_menu
;
319 guint j
, i
, number_of_items
;
320 GList
*file
, *next
, *item
, *menu_items
;
322 number_of_items
= MIN(g_list_length(recent_files
),
323 recent_files_length
);
325 file
= g_list_first (recent_files
);
327 for (j
= 0; j
< number_of_items
; j
++) {
328 next
= g_list_next (file
);
329 if (strcmp (((RecentFileData
*)file
->data
)->filename
, fname
) == 0) {
330 file_menu
= recent_file_filemenu_get ();
331 menu_items
= GTK_MENU_SHELL(file_menu
)->children
;
333 /* remove all menu items after THIS ONE */
335 item
= g_list_nth (menu_items
,
336 recent_files_menuitem_offset
+ j
+ 1);
337 for (i
= j
; i
< number_of_items
; i
++) {
338 next
= g_list_next(item
);
340 /* Unlink first, then destroy */
342 menu_items
= g_list_remove_link(menu_items
, item
);
343 gtk_widget_destroy(item
->data
);
349 recent_files
= g_list_delete_link (recent_files
, file
);
351 /* recreate all menu items after THIS ONE */
353 number_of_items
= MIN(g_list_length(recent_files
),
354 recent_files_length
);
355 item
= g_list_nth (recent_files
, j
);
356 for (i
= j
+ 1; i
<= number_of_items
; i
++) {
357 recent_file_menuitem_create(file_menu
, item
->data
, i
);
358 item
= g_list_next(item
);
367 open_recent_file_callback (GtkWidget
*widget
, RecentFileData
*file
) {
368 GList
*import_filters
;
369 DiaImportFilter
*ifilter
= NULL
;
370 Diagram
*diagram
= NULL
;
372 /* find the import filter */
373 if(file
->importfilterdescription
!= NULL
) {
374 import_filters
= filter_get_import_filters();
376 if(strcmp(((DiaImportFilter
*) import_filters
->data
)->description
,
377 file
->importfilterdescription
) == 0) {
378 ifilter
= import_filters
->data
;
382 while((import_filters
= g_list_next(import_filters
)) != NULL
);
384 diagram
= diagram_load(file
->filename
, ifilter
);
385 if (diagram
!= NULL
) {
386 diagram_update_extents(diagram
);
387 layer_dialog_set_diagram(diagram
);
388 new_display(diagram
);
390 recent_file_history_remove (file
->filename
);