1 /*
2 * Copyright (c) 2004 Stefan Ulrich
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy
5 * of this software and associated documentation files (the "Software"), to
6 * deal in the Software without restriction, including without limitation the
7 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8 * sell copies of the Software, and to permit persons to whom the Software is
9 * furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17 * IN NO EVENT SHALL PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE
18 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 */
22
23 /*
24 List of recently visited files, initialized from X resource
25 fileHistory and displayed in `File -> Open Recent' menu.
26 */
27
28 #include <ctype.h>
29
30 #include "xdvi-config.h"
31 #include "xdvi.h"
32 #include "util.h"
33 #include "dl_list.h"
34 #include "my-snprintf.h"
35 #include "string-utils.h"
36 #include "dvi-init.h"
37 #include "events.h"
38 #include "message-window.h"
39 #include "xm_menu.h"
40 #include "xaw_menu.h"
41 #include "filehist.h"
42
43 /****************************************************************************
44 *
45 * File-scope globals
46 *
47 ****************************************************************************/
48
49 /* list of files */
50 static struct dl_list *m_file_history = NULL;
51
52 /* current size of the list */
53 static int m_file_history_length = 0;
54
55 /* /\* current position in the list *\/ */
56 /* static int m_file_history_currpos = 0; */
57
58 /* item in above list */
59 struct file_history {
60 char *filename;
61 int pageno;
62 };
63
64 #define DEBUG 1
65
66 /****************************************************************************
67 *
68 * Private functions
69 *
70 ****************************************************************************/
71
72 static void
file_history_show(struct dl_list * list)73 file_history_show(struct dl_list *list)
74 {
75 if (globals.debug & DBG_FILES) {
76 int n;
77 fprintf(stderr, "======= File history:\n");
78 for (n = 0; list != NULL; list = list->next, n++) {
79 struct file_history *item = (struct file_history *)(list->item);
80 if (item == NULL) {
81 fprintf(stderr, "item %d is NULL!\n", n);
82 continue;
83 }
84 fprintf(stderr, "item %d: %d:%s\n", n, item->pageno, item->filename);
85 }
86 }
87 }
88
89
90 /****************************************************************************
91 *
92 * Exported functions
93 *
94 ****************************************************************************/
95
96 void
file_history_init(void)97 file_history_init(void)
98 {
99 char **fileinfo;
100 size_t i;
101 struct dl_list *head = NULL;
102
103 m_file_history_length = 0;
104
105 if (resource.file_history == NULL)
106 return;
107
108 fileinfo = get_separated_list(resource.file_history, "\n", False);
109
110 for (i = 0; fileinfo[i] != NULL && i < (size_t)resource.file_history_size; i++) {
111 struct file_history *item = xmalloc(sizeof *item);
112 char *ptr;
113 int pageno = strtol(fileinfo[i], &ptr, 10);
114 TRACE_FILES((stderr, "FILEINFO: %s", fileinfo[i]));
115 if (ptr == fileinfo[i]) {
116 XDVI_WARNING((stderr, "Missing page number in resource line `%s'!\n",
117 fileinfo[i]));
118 pageno = 0;
119 }
120 else {
121 while (isspace((int)*ptr))
122 ptr++;
123 }
124
125 item->pageno = pageno;
126 item->filename = xstrdup(ptr);
127
128 TRACE_FILES((stderr, "FILE: %d:%s", item->pageno, item->filename));
129
130 m_file_history = dl_list_insert(m_file_history, item);
131 if (head == NULL) /* remember head position */
132 head = m_file_history;
133
134 TRACE_FILES((stderr, "NEW ELEM: %p", (void *)m_file_history));
135
136 m_file_history_length++;
137
138 free(fileinfo[i]);
139 }
140 free(fileinfo);
141
142 m_file_history = head;
143 file_history_show(m_file_history);
144 }
145
146 static Boolean
equals_filename(const void * it,const void * fname)147 equals_filename(const void *it, const void *fname)
148 {
149 const struct file_history *item = (const struct file_history *)it;
150 const char *filename = (const char *)fname;
151
152 return strcmp(item->filename, filename) == 0;
153 }
154
155
156 /* put new elem for filename `filename' and page number `page' at the front
157 of the list. Return True if the addition caused the history to grow, else
158 False (in case the item was only moved to the front).
159 */
160 Boolean
file_history_push(const char * filename)161 file_history_push(const char *filename)
162 {
163 int i;
164 struct file_history *item = NULL;
165 void *removed_data = NULL;
166 struct file_history *removed_item = NULL;
167 int len_bak = m_file_history_length;
168 int count = 0;
169
170 TRACE_FILES((stderr, "Pushing: |%s|", filename));
171
172 /* if list already contains an item with same filename, remove it */
173 m_file_history = dl_list_remove(m_file_history, filename, &count, &removed_data, equals_filename);
174 if (count == 0)
175 m_file_history_length++;
176
177 removed_item = (struct file_history *)removed_data;
178
179 file_history_show(m_file_history);
180
181 TRACE_FILES((stderr, "current length: %d, max: %d", m_file_history_length, resource.file_history_size));
182
183 /* truncate list if it has reached its max length */
184 if (m_file_history_length > resource.file_history_size) {
185 struct dl_list *listpos = m_file_history;
186 struct dl_list *last_pos = listpos;
187 for (i = 0; listpos != NULL && i < m_file_history_length; i++) {
188 last_pos = listpos;
189 listpos = listpos->next;
190 }
191
192 item = last_pos->item; /* reuse item */
193 TRACE_FILES((stderr, "Re-using item: |%s| -> |%s|", item->filename, filename));
194 item->filename = xrealloc(item->filename, strlen(filename) + 1);
195 strcpy(item->filename, filename);
196
197 (void)dl_list_remove_item(&last_pos);
198 m_file_history_length--;
199 }
200 else if (removed_item != NULL) {
201 TRACE_FILES((stderr, "Re-using item: |%s|\n", removed_item->filename));
202 item = removed_item; /* reuse item */
203 }
204 else {
205 item = xmalloc(sizeof *item);
206 TRACE_FILES((stderr, "NEW item: |%s|\n", filename));
207 item->filename = xstrdup(filename);
208 item->pageno = 0;
209 }
210
211 /* add new element at front of list */
212 m_file_history = dl_list_push_front(m_file_history, item);
213
214 file_history_show(m_file_history);
215
216 TRACE_FILES((stderr, "returning: %d < %d", len_bak, m_file_history_length));
217 return len_bak < m_file_history_length;
218 }
219
file_history_size(void)220 size_t file_history_size(void)
221 {
222 return m_file_history_length;
223 }
224
file_history_set_page(int pageno)225 void file_history_set_page(int pageno)
226 {
227 struct dl_list *head;
228
229 TRACE_FILES((stderr, "SETTING HEAD to %d", pageno));
230 file_history_show(m_file_history);
231 head = dl_list_head(m_file_history);
232 if (head != NULL) {
233 struct file_history *item = (struct file_history *)head->item;
234 TRACE_FILES((stderr, "Setting page of |%s| to %d", item->filename, pageno));
235 item->pageno = pageno;
236 }
237 }
238
file_history_set_page_at(int idx,int pageno)239 void file_history_set_page_at(int idx, int pageno)
240 {
241 int i;
242 struct dl_list *listpos = dl_list_head(m_file_history);
243 struct file_history *item;
244
245 for (i = 0; listpos != NULL && i < idx; i++) {
246 listpos = listpos->next;
247 }
248 if (listpos == NULL) {
249 TRACE_FILES((stderr, "Asked for file at position %d, but only %d elements in list",
250 idx, i - 1));
251 return;
252 }
253 item = (struct file_history *)listpos->item;
254 TRACE_FILES((stderr, "set_page_at index %d: Setting page of |%s| to %d", idx, item->filename, pageno));
255 item->pageno = pageno;
256 }
257
file_history_get_page(void)258 int file_history_get_page(void)
259 {
260 struct dl_list *head = dl_list_head(m_file_history);
261 if (head != NULL) {
262 struct file_history *item = (struct file_history *)head->item;
263 TRACE_FILES((stderr, "Getting page of |%s|: %d", item->filename, item->pageno));
264 return item->pageno;
265 }
266 return 0;
267 }
268
269 /*
270 * Invoke `callback' for each elem of file history, passing
271 * the current index, filename, page number, and data passed to
272 * this function.
273 */
274 void
file_history_enumerate(filehistCallbackT callback,void * data)275 file_history_enumerate(filehistCallbackT callback, void *data)
276 {
277 int i;
278 struct dl_list *listpos = dl_list_head(m_file_history);
279
280 for (i = 0; listpos != NULL; i++) {
281 struct file_history *item = (struct file_history *)listpos->item;
282 callback(i, item->filename, item->pageno, data);
283 listpos = listpos->next;
284 }
285 }
286
287 char *
file_history_get_elem(int idx,int * ret_page)288 file_history_get_elem(int idx, int *ret_page)
289 {
290 int i;
291 struct dl_list *listpos = dl_list_head(m_file_history);
292 struct file_history *item;
293
294 for (i = 0; listpos != NULL && i < idx; i++) {
295 listpos = listpos->next;
296 }
297 if (listpos == NULL) {
298 XDVI_WARNING((stderr, "Asked for file at position %d, but only %d elements in list",
299 idx, i - 1));
300 return NULL;
301 }
302 item = (struct file_history *)listpos->item;
303 *ret_page = item->pageno;
304 file_history_show(m_file_history);
305 return item->filename;
306 }
307
308 char *
file_history_get_list(void)309 file_history_get_list(void)
310 {
311 char buf[LENGTH_OF_INT];
312 char *ret = xstrdup("");
313 struct dl_list *listpos;
314
315 for (listpos = dl_list_head(m_file_history);
316 listpos != NULL;
317 listpos = listpos->next) {
318
319 struct file_history *item = (struct file_history *)listpos->item;
320 SNPRINTF(buf, LENGTH_OF_INT, "%d ", item->pageno);
321 ret = xstrcat(ret, buf);
322 ret = xstrcat(ret, item->filename);
323 ret = xstrcat(ret, "\n");
324 }
325
326 ret[strlen(ret) - 1] = '\0'; /* chop off excess \n at end */
327 return ret;
328 }
329
330 void
file_history_open(const char * fname)331 file_history_open(const char *fname)
332 {
333 Boolean tried_dvi_ext = True;
334 char *new_dvi_name = NULL;
335
336 int dummy_cnt = 0;
337 void *dummy_data = NULL;
338
339 file_history_set_page(current_page);
340 if ((new_dvi_name = open_dvi_file_wrapper(fname,
341 True, /* pretend file is from commandline, otherwise xdvi will
342 try to fork for non-existing files, leading to confusing
343 popup error messages */
344 False, &tried_dvi_ext,
345 True /* don't exit on error */)) == NULL) {
346 /* remove this item from the file history */
347 m_file_history = dl_list_remove(m_file_history, fname, &dummy_cnt, &dummy_data, equals_filename);
348 m_file_history_length--;
349 file_history_show(m_file_history);
350
351 filehist_menu_refresh();
352 return;
353 }
354 else {
355 dviErrFlagT errflag;
356 if (load_dvi_file(
357 #if !DELAYED_MKTEXPK
358 True,
359 #endif
360 &errflag)) {
361 /* page_history_insert(pageno); */
362 set_dvi_name(new_dvi_name);
363
364 globals.ev.flags |= EV_NEWDOC;
365 globals.ev.flags |= EV_FILEHIST_GOTO_PAGE;
366 }
367 else { /* re-open old file */
368 popup_message(globals.widgets.top_level,
369 MSG_ERR,
370 NULL,
371 "Could not open `%s': %s.\n"
372 /* "Removing this file from the history." */,
373 globals.dvi_name, get_dvi_error(errflag));
374 /* remove this item from the file history */
375 m_file_history = dl_list_remove(m_file_history, globals.dvi_name,
376 &dummy_cnt, &dummy_data,
377 equals_filename);
378 m_file_history_length--;
379 filehist_menu_refresh();
380
381 if (!internal_open_dvi(globals.dvi_name, &errflag, True
382 #if DELAYED_MKTEXPK
383 , True
384 #endif
385 )) {
386 popup_message(globals.widgets.top_level,
387 MSG_ERR,
388 NULL,
389 "Couldn't reopen `%s': %s.\n"
390 /* "Removing this file from the history." */,
391 globals.dvi_name, get_dvi_error(errflag));
392 /* remove this item from the file history */
393 m_file_history = dl_list_remove(m_file_history, globals.dvi_name, &dummy_cnt, &dummy_data, equals_filename);
394 m_file_history_length--;
395 filehist_menu_refresh();
396 }
397 else {
398 globals.ev.flags |= EV_NEWPAGE;
399 }
400 }
401 }
402 }
403