Back to index

tetex-bin  3.0
filehist.c
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2004 Stefan Ulrich
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy
00005  * of this software and associated documentation files (the "Software"), to
00006  * deal in the Software without restriction, including without limitation the
00007  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
00008  * sell copies of the Software, and to permit persons to whom the Software is
00009  * furnished to do so, subject to the following conditions:
00010  * 
00011  * The above copyright notice and this permission notice shall be included in
00012  * all copies or substantial portions of the Software.
00013  * 
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00015  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00016  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
00017  * IN NO EVENT SHALL PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE
00018  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
00019  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00020  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00021 */
00022 
00023 /*
00024   List of recently visited files, initialized from X resource
00025   fileHistory and displayed in `File -> Open Recent' menu.
00026 */
00027 
00028 #include <ctype.h>
00029 
00030 #include "xdvi-config.h"
00031 #include "xdvi.h"
00032 #include "util.h"
00033 #include "my-snprintf.h"
00034 #include "string-utils.h"
00035 #include "dvi-init.h"
00036 #include "events.h"
00037 #include "message-window.h"
00038 #include "xm_menu.h"
00039 #include "xaw_menu.h"
00040 #include "filehist.h"
00041 
00042 /****************************************************************************
00043  *
00044  * File-scope globals
00045  *
00046  ****************************************************************************/
00047 
00048 /* list of files */
00049 static struct dl_list *m_file_history = NULL;
00050 
00051 /* current size of the list */
00052 static int m_file_history_length = 0;
00053 
00054 /* /\* current position in the list *\/ */
00055 /* static int m_file_history_currpos = 0; */
00056 
00057 /* item in above list */
00058 struct file_history {
00059     char *filename;
00060     int pageno;
00061 };
00062 
00063 #define DEBUG 1
00064 
00065 /****************************************************************************
00066  *
00067  * Private functions
00068  *
00069  ****************************************************************************/
00070 
00071 static void
00072 file_history_show(struct dl_list *list)
00073 {
00074     if (globals.debug & DBG_FILES) {
00075        int n;
00076        fprintf(stderr, "======= File history:\n");
00077        for (n = 0; list != NULL; list = list->next, n++) {
00078            struct file_history *item = (struct file_history *)(list->item);
00079            if (item == NULL) {
00080               fprintf(stderr, "item %d is NULL!\n", n);
00081               continue;
00082            }
00083            fprintf(stderr, "item %d: %d:%s\n", n, item->pageno, item->filename);
00084        }
00085     }
00086 }
00087 
00088 
00089 /****************************************************************************
00090  *
00091  * Exported functions
00092  *
00093  ****************************************************************************/
00094 
00095 void
00096 file_history_init(void)
00097 {
00098     char **fileinfo;
00099     size_t i;
00100     struct dl_list *head = NULL;
00101     
00102     m_file_history_length = 0;
00103 
00104     if (resource.file_history == NULL)
00105        return;
00106        
00107     fileinfo = get_separated_list(resource.file_history, "\n", False);
00108     
00109     for (i = 0; fileinfo[i] != NULL && i < (size_t)resource.file_history_size; i++) {
00110        struct file_history *item = xmalloc(sizeof *item);
00111        char *ptr;
00112        int pageno = strtol(fileinfo[i], &ptr, 10);
00113        TRACE_FILES((stderr, "FILEINFO: %s", fileinfo[i]));
00114        if (ptr == fileinfo[i]) {
00115            XDVI_WARNING((stderr, "Missing page number in resource line `%s'!\n",
00116                        fileinfo[i]));
00117            pageno = 0;
00118        }
00119        else {
00120            while (isspace((int)*ptr))
00121               ptr++;
00122        }
00123 
00124        item->pageno = pageno;
00125        item->filename = xstrdup(ptr);
00126 
00127        TRACE_FILES((stderr, "FILE: %d:%s", item->pageno, item->filename));
00128        
00129        m_file_history = dl_list_insert(m_file_history, item);
00130        if (head == NULL) /* remember head position */
00131            head = m_file_history;
00132        
00133        TRACE_FILES((stderr, "NEW ELEM: %p", (void *)m_file_history));
00134 
00135        m_file_history_length++;
00136 
00137        free(fileinfo[i]);
00138     }
00139     free(fileinfo);
00140 
00141     m_file_history = head;    
00142     file_history_show(m_file_history);
00143 }
00144 
00145 static Boolean
00146 equals_filename(const void *it, const void *fname)
00147 {
00148     const struct file_history *item = (const struct file_history *)it;
00149     const char *filename = (const char *)fname;
00150 
00151     return strcmp(item->filename, filename) == 0;
00152 }
00153 
00154 
00155 /* put new elem for filename `filename' and page number `page' at the front
00156    of the list. Return True if the addition caused the history to grow, else
00157    False (in case the item was only moved to the front).
00158 */
00159 Boolean
00160 file_history_push(const char *filename)
00161 {
00162     int i;
00163     struct file_history *item = NULL;
00164     void *removed_data = NULL;
00165     struct file_history *removed_item = NULL;
00166     int len_bak = m_file_history_length;
00167     int count = 0;
00168 
00169     TRACE_FILES((stderr, "Pushing: |%s|", filename));
00170     
00171     /* if list already contains an item with same filename, remove it */
00172     m_file_history = dl_list_remove(m_file_history, filename, &count, &removed_data, equals_filename);
00173     if (count == 0)
00174        m_file_history_length++;
00175 
00176     removed_item = (struct file_history *)removed_data;
00177     
00178     file_history_show(m_file_history); 
00179 
00180     TRACE_FILES((stderr, "current length: %d, max: %d", m_file_history_length, resource.file_history_size));
00181     
00182     /* truncate list if it has reached its max length */
00183     if (m_file_history_length > resource.file_history_size) {
00184        struct dl_list *listpos = m_file_history;
00185        struct dl_list *last_pos = listpos;
00186        for (i = 0; listpos != NULL && i < m_file_history_length; i++) {
00187            last_pos = listpos;
00188            listpos = listpos->next;
00189        }
00190 
00191        item = last_pos->item; /* reuse item */
00192        TRACE_FILES((stderr, "Re-using item: |%s| -> |%s|", item->filename, filename));
00193        item->filename = xrealloc(item->filename, strlen(filename) + 1);
00194        strcpy(item->filename, filename);
00195        
00196        (void)dl_list_remove_item(&last_pos);
00197        m_file_history_length--;
00198     }
00199     else if (removed_item != NULL) {
00200        TRACE_FILES((stderr, "Re-using item: |%s|\n", removed_item->filename));
00201        item = removed_item; /* reuse item */
00202     }
00203     else {
00204        item = xmalloc(sizeof *item);
00205        TRACE_FILES((stderr, "NEW item: |%s|\n", filename));
00206        item->filename = xstrdup(filename);
00207        item->pageno = 0;
00208     }
00209 
00210     /* add new element at front of list */
00211     m_file_history = dl_list_push_front(m_file_history, item);
00212     
00213     file_history_show(m_file_history);
00214 
00215     TRACE_FILES((stderr, "returning: %d < %d", len_bak, m_file_history_length));
00216     return len_bak < m_file_history_length;
00217 }
00218 
00219 size_t file_history_size(void)
00220 {
00221     return m_file_history_length;
00222 }
00223 
00224 void file_history_set_page(int pageno)
00225 {
00226     struct dl_list *head;
00227 
00228     TRACE_FILES((stderr, "SETTING HEAD to %d", pageno));
00229     file_history_show(m_file_history);
00230     head = dl_list_head(m_file_history);
00231     if (head != NULL) {
00232        struct file_history *item = (struct file_history *)head->item;
00233        TRACE_FILES((stderr, "Setting page of |%s| to %d", item->filename, pageno));
00234        item->pageno = pageno;
00235     }
00236 }
00237 
00238 void file_history_set_page_at(int idx, int pageno)
00239 {
00240     int i;
00241     struct dl_list *listpos = dl_list_head(m_file_history);
00242     struct file_history *item;
00243     
00244     for (i = 0; listpos != NULL && i < idx; i++) {
00245        listpos = listpos->next;
00246     }
00247     if (listpos == NULL) {
00248        TRACE_FILES((stderr, "Asked for file at position %d, but only %d elements in list",
00249                    idx, i - 1));
00250        return;
00251     }
00252     item = (struct file_history *)listpos->item;
00253     TRACE_FILES((stderr, "set_page_at index %d: Setting page of |%s| to %d", idx, item->filename, pageno));
00254     item->pageno = pageno;
00255 }
00256 
00257 int file_history_get_page(void)
00258 {
00259     struct dl_list *head = dl_list_head(m_file_history);
00260     if (head != NULL) {
00261        struct file_history *item = (struct file_history *)head->item;
00262        TRACE_FILES((stderr, "Getting page of |%s|: %d", item->filename, item->pageno));
00263        return item->pageno;
00264     }
00265     return 0;
00266 }
00267 
00268 /*
00269  * Invoke `callback' for each elem of file history, passing
00270  * the current index, filename, page number, and data passed to
00271  * this function.
00272  */
00273 void
00274 file_history_enumerate(filehistCallbackT callback, void *data)
00275 {
00276     int i;
00277     struct dl_list *listpos = dl_list_head(m_file_history);
00278 
00279     for (i = 0; listpos != NULL; i++) {
00280        struct file_history *item = (struct file_history *)listpos->item;
00281        callback(i, item->filename, item->pageno, data);
00282        listpos = listpos->next;
00283     }
00284 }
00285 
00286 char *
00287 file_history_get_elem(int idx, int *ret_page)
00288 {
00289     int i;
00290     struct dl_list *listpos = dl_list_head(m_file_history);
00291     struct file_history *item;
00292     
00293     for (i = 0; listpos != NULL && i < idx; i++) {
00294        listpos = listpos->next;
00295     }
00296     if (listpos == NULL) {
00297        XDVI_WARNING((stderr, "Asked for file at position %d, but only %d elements in list",
00298                     idx, i - 1));
00299        return NULL;
00300     }
00301     item = (struct file_history *)listpos->item;
00302     *ret_page = item->pageno;
00303     file_history_show(m_file_history);
00304     return item->filename;
00305 }
00306 
00307 char *
00308 file_history_get_list(void)
00309 {
00310     char buf[LENGTH_OF_INT];
00311     char *ret = xstrdup("");
00312     struct dl_list *listpos;
00313     
00314     for (listpos = dl_list_head(m_file_history);
00315         listpos != NULL;
00316         listpos = listpos->next) {
00317        
00318        struct file_history *item = (struct file_history *)listpos->item;
00319        SNPRINTF(buf, LENGTH_OF_INT, "%d ", item->pageno);
00320        ret = xstrcat(ret, buf);
00321        ret = xstrcat(ret, item->filename);
00322        ret = xstrcat(ret, "\n");   
00323     }
00324 
00325     ret[strlen(ret) - 1] = '\0'; /* chop off excess \n at end */
00326     return ret;
00327 }
00328 
00329 void
00330 file_history_open(const char *fname)
00331 {
00332     Boolean tried_dvi_ext = True;
00333     char *new_dvi_name = NULL;
00334 
00335     int dummy_cnt = 0;
00336     void *dummy_data = NULL;
00337     
00338     file_history_set_page(current_page);
00339     if ((new_dvi_name = open_dvi_file_wrapper(fname,
00340                                          True, /* pretend file is from commandline, otherwise xdvi will
00341                                                  try to fork for non-existing files, leading to confusing
00342                                                  popup error messages */
00343                                          False, &tried_dvi_ext,
00344                                          True /* don't exit on error */)) == NULL) {
00345        /* remove this item from the file history */
00346        m_file_history = dl_list_remove(m_file_history, fname, &dummy_cnt, &dummy_data, equals_filename);
00347        m_file_history_length--;
00348        file_history_show(m_file_history); 
00349 
00350        filehist_menu_refresh();
00351        return;
00352     }
00353     else {
00354        dviErrFlagT errflag;
00355        if (load_dvi_file(True, &errflag)) {
00356            /* page_history_insert(pageno); */
00357            set_dvi_name(new_dvi_name);
00358            
00359            globals.ev.flags |= EV_NEWDOC;
00360            globals.ev.flags |= EV_FILEHIST_GOTO_PAGE;
00361            globals.ev.flags |= EV_PAGEHIST_INSERT;
00362        }
00363        else { /* re-open old file */
00364            popup_message(globals.widgets.top_level,
00365                        MSG_ERR,
00366                        NULL,
00367                        "Could not open `%s': %s.\n"
00368                        /* "Removing this file from the history." */,
00369                        globals.dvi_name, get_dvi_error(errflag));
00370            /* remove this item from the file history */
00371            m_file_history = dl_list_remove(m_file_history, globals.dvi_name,
00372                                        &dummy_cnt, &dummy_data,
00373                                        equals_filename);
00374            m_file_history_length--;
00375            filehist_menu_refresh();
00376            
00377            if (!internal_open_dvi(globals.dvi_name, &errflag, True)) {
00378 /*            || !internal_init_dvi(&errflag, True)) { */
00379               popup_message(globals.widgets.top_level,
00380                            MSG_ERR,
00381                            NULL,
00382                            "Couldn't reopen `%s': %s.\n"
00383                            /* "Removing this file from the history." */,
00384                            globals.dvi_name, get_dvi_error(errflag));
00385               /* remove this item from the file history */
00386               m_file_history = dl_list_remove(m_file_history, globals.dvi_name, &dummy_cnt, &dummy_data, equals_filename);
00387               m_file_history_length--;
00388               filehist_menu_refresh();
00389            }
00390            else {
00391               globals.ev.flags |= EV_NEWPAGE;
00392            }
00393        }
00394     }
00395 }