Back to index

tetex-bin  3.0
pagehist.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   Very simple page history (i.e. stack of visited pages) for xdvik
00025 */
00026 
00027 #include "xdvi-config.h"
00028 #include "xdvi.h"
00029 #include "util.h"
00030 #include "string-utils.h"
00031 #include "events.h"
00032 #include "dvi-init.h"
00033 #include "statusline.h"
00034 #include "message-window.h"
00035 #include "xm_toolbar.h"
00036 #include "pagehist.h"
00037 
00038 /****************************************************************************
00039  *
00040  * File-scope globals
00041  *
00042  ****************************************************************************/
00043 
00044 /* maximum number of items to print before truncating (10 left and right) */
00045 static const int HISTORY_MAX_CONTEXT = 21;
00046 
00047 /* list of pages in history */
00048 static struct dl_list *m_page_history = NULL;
00049 
00050 /* pointer to the head of the list, which only gets changed when
00051    truncating the head (when list has reached its max size). */
00052 static struct dl_list *m_page_history_head = NULL;
00053 
00054 /* current size of the list */
00055 static int m_page_history_length = 0;
00056 
00057 /* current position in the list */
00058 static int m_page_history_currpos = 0;
00059 
00060 /* lookup table of filenames visited so far */
00061 static char **m_filename_list = NULL;
00062 static size_t m_filename_size = 0;
00063 
00064 /* item in above list */
00065 struct page_history {
00066     int pageno;
00067     int file_idx; /* index in file_list */
00068 };
00069 
00070 #define DEBUG 0
00071 
00072 /****************************************************************************
00073  *
00074  * Private functions
00075  *
00076  ****************************************************************************/
00077 
00078 static void
00079 page_history_show(struct dl_list *head, const struct dl_list *curr)
00080 {
00081 #if DEBUG
00082     int n;
00083     for (n = 0; head != NULL; head = head->next, n++) {
00084        struct page_history *item = (struct page_history *)(head->item);
00085        if (head == curr) {
00086            fprintf(stderr, "item %d: <%d>\n", n, item->pageno);
00087        }
00088        else {
00089            fprintf(stderr, "item %d: %d\n", n, item->pageno);
00090        }
00091     }
00092 #else
00093     UNUSED(head);
00094     UNUSED(curr);
00095 #endif /* DEBUG */
00096 }
00097 
00098 /* like above, but output goes to the statusline, and it prints only up to HISTORY_MAX_CONTEXT
00099    items around the current one.
00100 */
00101 static void
00102 page_history_show_statusline(struct dl_list *head,
00103                           const struct dl_list *curr,
00104                           const char *msg)
00105 {
00106 #define HIST_LEN 1024 /* should be ample since statusline is limited to 512 MAX_LEN */
00107 
00108     int file_idx = 0;
00109     
00110     int n;
00111     char history[HIST_LEN];
00112     char *ptr = history;
00113     int tot_len = m_page_history_length;
00114     int curr_pos = m_page_history_currpos;
00115     int initial_offset = 0;
00116     int printed_len = 0;
00117     
00118 #if DEBUG
00119     fprintf(stderr, "tot_len: %d, curr_pos: %d\n", tot_len, curr_pos);
00120 #endif
00121     
00122     if (head == NULL){
00123        strcpy(ptr, "Page history empty.");
00124        ptr += strlen("Page history empty.");
00125     }
00126     else {
00127        strcpy(ptr, "Page history:");
00128        ptr += strlen("Page history:");
00129     }
00130 
00131     /* check if we need to truncate at the beginning or end */
00132     if (tot_len > HISTORY_MAX_CONTEXT) {
00133        /* need to truncate, try to make first and second chunk around current position of same length */
00134        int good_pos = HISTORY_MAX_CONTEXT / 2.0 + 0.5;
00135        while (curr_pos > good_pos /*  && */
00136 /*            m_page_history_length - m_page_history_currpos > good_pos */) {
00137 #if DEBUG
00138            fprintf(stderr, "%d > %d; %d > %d\n", curr_pos, good_pos,
00139                   m_page_history_length - m_page_history_currpos, good_pos);
00140 #endif
00141            curr_pos--;
00142            initial_offset++;
00143        }
00144 #if DEBUG
00145        fprintf(stderr, "initial offset: %d\n", initial_offset);
00146 #endif
00147        /* if we're more to the end, adjust good_pos and initial_offset */
00148        while (good_pos - 1 > m_page_history_length - m_page_history_currpos) {
00149 #if DEBUG
00150            fprintf(stderr, "%d > %d\n", m_page_history_length - m_page_history_currpos, good_pos);
00151 #endif
00152            initial_offset--;
00153            good_pos--;
00154        }
00155 #if DEBUG
00156        fprintf(stderr, "initial offset adjusted: %d\n", initial_offset);
00157 #endif
00158     }
00159 
00160     for (n = 0; head != NULL; head = head->next, n++) {
00161        struct page_history *item;
00162        /* skip initial offset, and insert truncation marker at beginning/end */
00163        if (initial_offset == 1 || printed_len >= HISTORY_MAX_CONTEXT) {
00164            strcpy(ptr, " ...");
00165            ptr += strlen(" ...");
00166            if (printed_len >= HISTORY_MAX_CONTEXT)
00167               break;
00168        }
00169        if (initial_offset > 0) {
00170            initial_offset--;
00171            continue;
00172        }
00173 
00174        printed_len++;
00175 
00176        item = (struct page_history *)(head->item);
00177 
00178        /* insert a marker if item is in different file ... */
00179        if (item->file_idx != file_idx) {
00180            if (n > 0) { /* ... but only if we're not at the beginning of the list */
00181 #if 1
00182               strcpy(ptr, " -");
00183               ptr += 2;
00184 #else
00185               char *fname = m_filename_list[item->file_idx];
00186               char *tmp;
00187               if ((tmp = strrchr(m_filename_list[item->file_idx], '/')) != NULL)
00188                   fname = tmp + 1;
00189               strcpy(ptr, " [");
00190               ptr += 2;
00191               strcpy(ptr, fname);
00192               ptr += strlen(fname);
00193               strcpy(ptr, "]");
00194               ptr++;
00195 #endif
00196            }
00197            file_idx = item->file_idx;
00198        }
00199        
00200        if (head == curr) {
00201            ASSERT(m_page_history_currpos == n + 1, "Consistency check for pagelist position failed!");
00202            sprintf(ptr, " [%d]", item->pageno + 1);
00203            ptr += 3 + length_of_int(item->pageno + 1);
00204        }
00205        else {
00206            sprintf(ptr, " %d", item->pageno + 1);
00207            ptr += 1 + length_of_int(item->pageno + 1);
00208        }
00209     }
00210 #if DEBUG
00211     fprintf(stderr, "Statusline string: |%s|; printed len: %d\n", history, printed_len);
00212 #endif
00213     statusline_print(STATUS_LONG, "%s %s", history, msg ? msg : "");
00214 #undef HIST_LEN
00215 }
00216 
00217 static void
00218 goto_location(const char *filename, int pageno)
00219 {
00220 #if DEBUG
00221     fprintf(stderr, "going to page %d of file %s\n", pageno, filename);
00222 #endif
00223     if (strcmp(globals.dvi_name, filename) != 0) { /* it's a different file */
00224        Boolean tried_dvi_ext = True;
00225        char *new_dvi_name;
00226 #if DEBUG
00227        fprintf(stderr, "different file: |%s|\n", filename);
00228 #endif
00229        if ((new_dvi_name = open_dvi_file_wrapper(filename, True, False,
00230                                             &tried_dvi_ext, True)) == NULL) {
00231            statusline_append(STATUS_MEDIUM,
00232                            "Re-opening file",
00233                            "Re-opening file \"%s\" failed!", filename);
00234 #if DEBUG
00235            fprintf(stderr, "Re-opening file \"%s\" failed!\n", filename);
00236 #endif
00237            page_history_delete(1);
00238            return;
00239        }
00240        else {
00241            dviErrFlagT errflag;
00242            if (load_dvi_file(True, &errflag)) {
00243               set_dvi_name(new_dvi_name);
00244 
00245               globals.ev.flags |= EV_NEWDOC;
00246 #if DEBUG
00247               fprintf(stderr, "Back to file: \"%s\"\n", globals.dvi_name);
00248 #endif
00249            }
00250            else { /* re-open old file */
00251               popup_message(globals.widgets.top_level,
00252                            MSG_ERR,
00253                            NULL,
00254                            "Could not open `%s': %s.\n"
00255                            /* "Removing this file from the history." */,
00256                            globals.dvi_name, get_dvi_error(errflag));
00257 
00258               if (!internal_open_dvi(globals.dvi_name, &errflag, True)) {
00259                   popup_message(globals.widgets.top_level,
00260                               MSG_ERR,
00261                               NULL,
00262                               "Couldn't reopen `%s': %s.\n"
00263                               /* "Removing this file from the history." */,
00264                               globals.dvi_name, get_dvi_error(errflag));
00265               }
00266               else {
00267                   globals.ev.flags |= EV_NEWPAGE;
00268               }
00269               page_history_delete(1);
00270            }
00271        }
00272     }
00273     if (pageno != current_page) {
00274        if (pageno < total_pages) {
00275            goto_page(pageno, resource.keep_flag ? NULL : home, False);
00276        }
00277        else {
00278            XDVI_WARNING((stderr, "Could not go to page %d (file has shrunken)", pageno));
00279            statusline_print(STATUS_MEDIUM,
00280                           "Could not go to page %d (file has shrunken)", pageno);
00281        }
00282     }
00283 }
00284 
00285 /****************************************************************************
00286  *
00287  * Exported functions
00288  *
00289  ****************************************************************************/
00290 
00291 /*
00292   Move n elements in page history; n == 0 doesn't move,
00293   n < 0 moves n items back, n > 0 moves n items forward.
00294 */
00295 void page_history_move(int n)
00296 {
00297     struct dl_list *pos;
00298     struct page_history *item;
00299     const char *msg = NULL;
00300 
00301     page_history_show(m_page_history_head, m_page_history);
00302     
00303     if (resource.page_history_size == 0)
00304        return;
00305     
00306     if (m_page_history_head == NULL)
00307        m_page_history_head = m_page_history;
00308     
00309     if (n < 0) { /* move backwards */
00310        for (pos = NULL; n < 0; n++) {
00311            if (m_page_history != NULL)
00312               pos = m_page_history->prev;
00313            if (pos == NULL) {
00314               XBell(DISP, 0);
00315               msg = " - at begin of page history.";
00316               break;
00317            }
00318            else {
00319               m_page_history = pos;
00320               m_page_history_currpos--;
00321            }
00322        }
00323     }
00324     else { /* move forward */
00325        for (pos = NULL; n > 0; n--) {
00326            if (m_page_history != NULL)
00327               pos = m_page_history->next;
00328            if (pos == NULL) {
00329               XBell(DISP, 0);
00330               msg = " - at end of page history.";
00331               break;
00332            }
00333            else {
00334               m_page_history = pos;
00335               m_page_history_currpos++;
00336            }
00337        }
00338     }
00339     item = (struct page_history *)m_page_history->item;
00340 #if DEBUG
00341     fprintf(stderr, "going to page %d\n", item->pageno);
00342 #endif
00343     goto_location(m_filename_list[item->file_idx], item->pageno);
00344 
00345 #if defined(MOTIF) && HAVE_XPM
00346     tb_set_pagehist_back_sensitivity(m_page_history->prev != NULL);
00347     tb_set_pagehist_forward_sensitivity(m_page_history->next != NULL);
00348 #endif
00349     
00350     page_history_show(m_page_history_head, m_page_history);
00351     page_history_show_statusline(m_page_history_head, m_page_history, msg);
00352     page_history_update_toolbar_navigation();
00353 }
00354 
00355 /* add page n to the page history */
00356 void page_history_insert(int n)
00357 {
00358     struct page_history *item = NULL;
00359     static char *current_filename = NULL;
00360     static size_t filename_idx = 0; /* index of current filename */
00361 
00362 #if DEBUG
00363     fprintf(stderr, "inserting into history: %d\n", n);
00364 #endif
00365     page_history_show(m_page_history_head, m_page_history);
00366     /* do nothing if no history is used */
00367     if (resource.page_history_size == 0)
00368        return;
00369 
00370     if (m_page_history_head == NULL)
00371        m_page_history_head = m_page_history;
00372     
00373     item = xmalloc(sizeof *item);
00374     /* first call, or filename changed -> update file_list */
00375     if (current_filename == NULL || strcmp(current_filename, globals.dvi_name) != 0) {
00376        size_t i;
00377        current_filename = xstrdup(globals.dvi_name);
00378 
00379        for (i = 0; i < m_filename_size; i++) {
00380 #if DEBUG
00381            fprintf(stderr, "comparing %d: |%s|%s|\n", i, current_filename, m_filename_list[i]);
00382 #endif
00383            if (strcmp(current_filename, m_filename_list[i]) == 0) { /* found */
00384               filename_idx = i;
00385               break;
00386            }
00387        }
00388 
00389        if (i >= m_filename_size) { /* not found, insert into file list */
00390            m_filename_list = xrealloc(m_filename_list, (m_filename_size + 1) * sizeof *m_filename_list);
00391            m_filename_list[m_filename_size] = filename_append_dvi(current_filename);
00392            filename_idx = m_filename_size++;
00393 #if DEBUG
00394            fprintf(stderr, "NEW file %d: %s\n", filename_idx, current_filename);
00395 #endif
00396        }
00397     }
00398     
00399 #if DEBUG
00400     fprintf(stderr, "-------- %d >= %d?\n", m_page_history_length, resource.page_history_size - 1);
00401 #endif
00402     if (m_page_history_length >= resource.page_history_size - 1) { /* truncate history */
00403        free(m_page_history_head->item);
00404        m_page_history_head = dl_list_truncate_head(m_page_history_head);
00405     }
00406     else {
00407        m_page_history_length++;
00408     }
00409     
00410     item->pageno = n;
00411     item->file_idx = filename_idx;
00412     
00413 #if DEBUG
00414     fprintf(stderr, "inserting %d\n", item->pageno);
00415 #endif
00416     m_page_history = dl_list_insert(m_page_history, item);
00417     m_page_history_currpos++;
00418 
00419 #if DEBUG
00420     fprintf(stderr, "head: %p, curr: %p\n", (void *)m_page_history_head, (void *)m_page_history);
00421 #endif
00422     page_history_show(m_page_history_head, m_page_history);
00423     page_history_update_toolbar_navigation();
00424 }
00425 
00426 void
00427 page_history_update_toolbar_navigation(void)
00428 {
00429 #if defined(MOTIF) && HAVE_XPM
00430     tb_set_htex_back_sensitivity(m_page_history->prev != NULL);
00431     tb_set_htex_forward_sensitivity(m_page_history->next != NULL);
00432 #endif
00433 }
00434 
00435 void
00436 page_history_clear()
00437 {
00438     struct dl_list *pos, *curr;
00439 
00440     if (resource.page_history_size == 0)
00441        return;
00442     
00443     m_page_history_head = m_page_history;
00444 
00445     for (curr = m_page_history; curr != NULL && curr->prev != NULL; curr = pos) {
00446        pos = curr->prev;
00447        free(curr->item);
00448        (void)dl_list_remove_item(&curr);
00449        m_page_history_length--;
00450        page_history_show(m_page_history_head, m_page_history);
00451        page_history_show_statusline(m_page_history_head, m_page_history, NULL);
00452     }
00453 
00454 /*     for (curr = m_page_history; curr != NULL && curr->next != NULL; curr = pos) { */
00455 /*     pos = curr->next; */
00456 /*     free(curr->item); */
00457 /*     (void)dl_list_remove_item(&curr); */
00458 /*     m_page_history_length--; */
00459 /*     page_history_show(m_page_history_head, m_page_history); */
00460 /*     } */
00461     
00462 
00463 }
00464 
00465 /*
00466   Delete n elements from the page history.
00467   If n < 0, delete current and n-1 previous items and move to the item before them.
00468   If n > 0, delete current and n-1 next items and move to the item after them.
00469   E.g. (with current position marked by `<>'):
00470 
00471   a b <c> d e
00472   -> page_history_delete(-2)
00473   -> <a> d e
00474 
00475   a b <c> d e
00476   -> page_history_delete(2)
00477   -> a b <e>
00478 
00479   Prints an error to the statusline if number of deletions exceeds the limits
00480   of the list.
00481 */
00482 void page_history_delete(int n)
00483 {
00484     struct dl_list *pos;
00485     struct page_history *item;
00486     const char *msg = NULL;
00487     
00488     if (resource.page_history_size == 0)
00489        return;
00490     
00491     if (m_page_history_head == NULL)
00492        m_page_history_head = m_page_history;
00493 
00494     /*      fprintf(stderr, "deleting items: |%d|\n", n); */
00495     
00496     if (n < 0) { /* delete backwards */
00497        for (pos = NULL; n < 0; n++) {
00498            if (m_page_history != NULL)
00499               pos = m_page_history->prev;
00500            if (pos == NULL) {
00501               XBell(DISP, 0);
00502               msg = " - at begin of page history.";
00503               break;
00504            }
00505            else {
00506               /* remove item */
00507               free(m_page_history->item);
00508               (void)dl_list_remove_item(&m_page_history);
00509               m_page_history = pos;
00510               m_page_history_currpos--;
00511               m_page_history_length--;
00512            }
00513        }
00514     }
00515     else { /* delete forward */
00516        for (pos = NULL; n > 0; n--) {
00517            if (m_page_history != NULL)
00518               pos = m_page_history->next;
00519            if (pos == NULL) {
00520               XBell(DISP, 0);
00521               msg = " - at end of page history.";
00522               break;
00523            }
00524            else {
00525               /* remove item */
00526               free(m_page_history->item);
00527               if (m_page_history->prev == NULL) { /* at head */
00528                   m_page_history_head = m_page_history = dl_list_truncate_head(m_page_history);
00529               }
00530               else {
00531                   (void)dl_list_remove_item(&m_page_history);
00532                   m_page_history = pos;
00533               }
00534               /* Note: m_page_history_currpos remains unchanged here */
00535               m_page_history_length--;
00536            }
00537        }
00538     }
00539     item = (struct page_history *)m_page_history->item;
00540 #if DEBUG
00541     fprintf(stderr, "going to page %d\n", item->pageno);
00542 #endif
00543     goto_location(m_filename_list[item->file_idx], item->pageno);
00544     page_history_show(m_page_history_head, m_page_history);
00545     page_history_show_statusline(m_page_history_head, m_page_history, msg);
00546 }
00547