Back to index

tetex-bin  3.0
pagesel.c
Go to the documentation of this file.
00001 /*
00002  * Page selector for xdvi
00003  *
00004  * Copyright (c) 2001-2004 xdvik development team
00005  *
00006  * This code is derived from the page selector in xdvik-j, and
00007  * parts of it are Copyright (c) 1993, 1995
00008  *      MATSUURA Syun           syun@fuka.info.waseda.ac.jp
00009  *      HIRAHARA Atsushi        hirahara@fuka.info.waseda.ac.jp
00010  *      ONO Kouichi             onono@fuka.info.waseda.ac.jp
00011  * All rights reserved.
00012  *
00013  *
00014  * (SU: I was unsure how to interpret the `All rights reserved' in the
00015  * previous line, so emailed Ono Kouichi about this.  Here's a
00016  * verbatim quote of the relevant part of his answer (which was CC'ed
00017  * to Hirahara Atsushi - all three of them had left Waseda university
00018  * around '95):
00019  *
00020  *    You can modify, embed, copy and distribute a part of or the
00021  *    entire of our source code when you specify our copyright in your
00022  *    xdvik version.
00023  *
00024  * IANAL, but I think this is compatible with the X consortium
00025  * license, which follows.)
00026  *
00027  * Permission is hereby granted, free of charge, to any person obtaining a copy
00028  * of this software and associated documentation files (the "Software"), to
00029  * deal in the Software without restriction, including without limitation the
00030  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
00031  * sell copies of the Software, and to permit persons to whom the Software is
00032  * furnished to do so, subject to the following conditions:
00033  *
00034  * The above copyright notice and this permission notice shall be included in
00035  * all copies or substantial portions of the Software.
00036  *
00037  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00038  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00039  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
00040  * PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
00041  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
00042  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
00043  * OTHER DEALINGS IN THE SOFTWARE.
00044  *
00045  */
00046 
00047 /*
00048   BUGS:
00049 
00050    - with Xaw, the highlighting for the selected page (XawListHighlight)
00051      vanishes when mouse is dragged outside the widget and then released
00052      (but the respective page is selected, which is IMHO the desired behaviour).
00053      
00054    - with Xaw, scrolling the list with PgUp/PgDown until the current page
00055      gets `out of focus' should un-highlight the current page number
00056      
00057    - The ASCII-based marks are *ugly*. Pixmaps (for the marked state)
00058      would be better. The viewer gv has one (but it's Xaw only). Some
00059      file directory widgets like
00060      e.g. http://ftp.xfree86.org/pub/X.Org/contrib/widgets/ListTree-3.0b3.tar.gz
00061      also have facilities for that, but most suffer from other
00062      inadequacies (e.g. no such ting as browseSelection) and all kinds
00063      of bitrot ...  Another alternative would be using XmContainer
00064      (see e.g. the `filemanager' example in demos/programs/filemanagers
00065      in the openmotif distribution), but that's available for Motif >= 2.1 only.
00066 
00067 */
00068 
00069 #include "xdvi-config.h"
00070 #include "xdvi.h"
00071 
00072 #include <stdio.h>
00073 #include <stdarg.h>
00074 #include <stdlib.h>
00075 #include <X11/Intrinsic.h>
00076 #include <X11/StringDefs.h>
00077 #include <X11/Shell.h>
00078 
00079 #include "xm_toolbar.h"
00080 #include "xm_menu.h"
00081 #include "xaw_menu.h"
00082 
00083 #include "x_util.h"
00084 
00085 #ifdef MOTIF
00086 # include <Xm/Xm.h>
00087 # include <Xm/List.h>
00088 # include <Xm/ScrollBar.h> /* for XmScrollBarGetValues */
00089 #else /* MOTIF */
00090 # include <X11/Xaw/Dialog.h>
00091 # include <X11/Xaw/Cardinals.h>
00092 # include <X11/Xaw/Command.h>
00093 # include <X11/Xaw/List.h>
00094 # include <X11/Xaw/Viewport.h>
00095 #endif /* MOTIF */
00096 
00097 #include "message-window.h"
00098 #include "pagesel.h"
00099 #include "util.h"
00100 #include "string-utils.h"
00101 #include "dvi-init.h"
00102 #include "statusline.h"
00103 #include "events.h"
00104 #include "print-dialog.h"
00105 #include "search-internal.h"
00106 #include "pagehist.h"
00107 
00108 #define       PAGENUMLEN 128
00109 #define SCROLL_LIST_SCROLLBAR 0
00110 
00111 #ifndef       MAX_PAGE
00112 # define MAX_PAGE    (1024)
00113 #endif /* MAX_PAGE */
00114 
00115 #define       LONGESTPAGENUM  55
00116 
00117 
00118 /* for saving the GC of the pagelist widget
00119    (when un-highlighting items in highlight_page_callback, and
00120    drawing the `current' marker)
00121 */
00122 static struct page_gc {
00123     GC fore;
00124     GC back;
00125 } m_page_gc;
00126 
00127 #define MOTIF_IDX_OFFSET 1 /* motif index starts at 1, not 0 */
00128 #if !defined(LESSTIF_VERSION)
00129 static Boolean my_list_pos_to_bounds(Widget widget, int idx, Position *x, Position *y, Dimension *w, Dimension *h);
00130 static void refresh_highlight_marker(Widget widget, GC gc, Position x, Position y, Dimension w, Dimension h);
00131 #endif /* !defined(LESSTIF_VERSION) */
00132 
00133 
00134 
00135 #ifdef MOTIF
00136 
00137 /* make button2 mark instead of drag&drop */
00138 static void xm_list_set_mark(Widget widget, XEvent *event, String *params, Cardinal *num_params);
00139 static void xm_list_drag_mark(Widget widget, XEvent *event, String *params, Cardinal *num_params);
00140 static XtActionsRec CustomListActions[] = {
00141     { "ListSetMark",        xm_list_set_mark     },
00142     { "ListDragMark",              xm_list_drag_mark    },
00143 };
00144 static char *motif_custom_translations =
00145     "#override \n"
00146     "s <Btn2Down>:                   ListDragMark(ListButtonMotion)\n"
00147     "<Btn2Motion>:                   ListSetMark(ListButtonMotion)\n"
00148     "<Btn2Up>:                       ListSetMark(ListButtonMotion)\n"
00149     "<Btn2Down>:                     ListSetMark(ListButtonMotion)\n"
00150 /* /\*     "s ~m ~a <Btn2Down>:             ListMyProcessBtn2(ListBeginExtend)\n" *\/ */
00151 /* /\*     "s ~m ~a <Btn2Up>:               ListMyProcessBtn2(ListEndExtend)\n" *\/ */
00152 /* /\*     "~c ~s ~m ~a <Btn2Down>:         ListMyProcessBtn2(ListBeginSelect)\n" *\/ */
00153 /* /\*     "~c ~s ~m ~a <Btn2Up>:           ListMyProcessBtn2(ListEndSelect)\n" *\/ */
00154 /* /\*     "c ~s ~m ~a <Btn2Down>:          ListMyProcessBtn2(ListBeginToggle)\n" *\/ */
00155 /* /\*     "c ~s ~m ~a <Btn2Up>:            ListMyProcessBtn2(ListEndToggle)\n" *\/ */
00156 /* /\*     "c ~s ~m a <Btn2Down>:           ListProcessDrag()\n" *\/ */
00157 /* /\*     "~c s ~m a <Btn2Down>:           ListProcessDrag()\n" *\/ */
00158 ;
00159 
00160 #define LIST_WIDGET page_list
00161 /* motif pagenumber is a string */
00162 static const char* const pageno_format = "%c %s  ";
00163 
00164 #else /* MOTIF */
00165 
00166 static int view_y;
00167 extern Widget panel_widget;
00168 static Widget list_widget = NULL;
00169 static Widget viewport = NULL;
00170 #define LIST_WIDGET list_widget
00171 /* Xaw pagenumber is an integer, and we need to left-pad it */
00172 static const char* const pageno_format = "%c %*d  ";
00173 
00174 static int xaw_maybe_scroll_pagelist(int new_page, Boolean force_recenter, int old);
00175 
00176 #define REDRAW_CURRENT_MARKER_HACK 1
00177 
00178 
00179 /*
00180   The following hack tries to address the following 2 bugs with the
00181   self-made page highlighting marker:
00182   
00183   - the marker overlaps with the ordinary XawListHighlight marker;
00184     when un-highlighting a page, 1 pixel (vertically) at the edge of
00185     the ordinary marker is overdrawn.
00186 
00187   - When the XawListHighlight crosses the self-drawn rectangle,
00188     the vertical bars remain visible, but the horizontal bars
00189     are erased.
00190 
00191   The hack just redraws the appropriate items whenever one of
00192   the above can happen, i.e. when the two markers are 2 or less
00193   pages apart from each other.
00194 */
00195 #if REDRAW_CURRENT_MARKER_HACK
00196 /* Store index of currently marked (with our own marker) list
00197    item, or -1 if none is marked. */
00198 static int g_current_highlighted = -1;
00199 
00200 /* redraw the default Xaw list highlight (XawListHighlight()) */
00201 static void
00202 xaw_maybe_redraw_highlight(int idx)
00203 {
00204     XawListReturnStruct *ret;
00205     int high;
00206     
00207     if (LIST_WIDGET == NULL)
00208        return;
00209     
00210     ret = XawListShowCurrent(LIST_WIDGET);
00211     high = ret->list_index;
00212     if (high != XAW_LIST_NONE && abs(idx - (high + MOTIF_IDX_OFFSET)) <= 2) {
00213        /* re-highlight it */
00214        XawListHighlight(LIST_WIDGET, high);
00215     }
00216     g_current_highlighted = -1;
00217 }
00218 
00219 /* redraw our own rectangle highlight marker: */
00220 static void
00221 xaw_maybe_redraw_current_marker(int idx)
00222 {
00223     Position x, y;
00224     Dimension w, h;
00225 
00226 /*     fprintf(stderr, "idx: %d, high: %d; diff: %d\n", idx + MOTIF_IDX_OFFSET, g_current_highlighted, */
00227 /*         abs(idx + MOTIF_IDX_OFFSET - g_current_highlighted)); */
00228     if (abs((idx + MOTIF_IDX_OFFSET) - g_current_highlighted) <= 2
00229        && my_list_pos_to_bounds(LIST_WIDGET, g_current_highlighted, &x, &y, &w, &h)) {
00230        refresh_highlight_marker(LIST_WIDGET, m_page_gc.fore, x, y, w, h);
00231     }
00232 }
00233 #endif /* REDRAW_CURRENT_MARKER_HACK */
00234 
00235 #endif /* MOTIF */
00236 
00237 /*
00238  * Table of page offsets in DVI file, indexed by page number - 1,
00239  * marked pages, and page sizes.
00240  * Initialized in prepare_pages().
00241  */
00242 struct page_index {
00243     long offset;
00244     int number;
00245     Boolean marked;
00246     unsigned int pw, ph; /* page size */
00247     unsigned int ww, wh; /* window size */
00248 };
00249 
00250 struct page_index_info {
00251     struct page_index *index; /* above struct */
00252     size_t index_size;       /* size of currently allocated index */
00253     char **page_labels;  /* label strings */
00254 };
00255 
00256 static struct page_index_info page_info;
00257 
00258 /* access functions used by dvi-draw.c and dvi-init.c */
00259 long
00260 pageinfo_get_offset(int page)
00261 {
00262     ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
00263 /*      fprintf(stderr, "offset for page %d is %ld\n", page, page_info.index[page].offset); */
00264     return page_info.index[page].offset;
00265 }
00266 
00267 /* access functions used by dvi-draw.c and dvi-init.c */
00268 unsigned int
00269 pageinfo_get_page_width(int page)
00270 {
00271     ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
00272     return page_info.index[page].pw;
00273 }
00274 
00275 unsigned int
00276 pageinfo_get_page_height(int page)
00277 {
00278     ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
00279     return page_info.index[page].ph;
00280 }
00281 
00282 unsigned int
00283 pageinfo_get_window_width(int page)
00284 {
00285     ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
00286     return page_info.index[page].ww;
00287 }
00288 
00289 unsigned int
00290 pageinfo_get_window_height(int page)
00291 {
00292     ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
00293     return page_info.index[page].wh;
00294 }
00295 
00296 void
00297 pageinfo_set_page_width(int page, unsigned int width)
00298 {
00299     ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
00300     page_info.index[page].pw = width;
00301 }
00302 
00303 void
00304 pageinfo_set_page_height(int page, unsigned int height)
00305 {
00306     ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
00307     page_info.index[page].ph = height;
00308 }
00309 
00310 void
00311 pageinfo_set_window_width(int page, unsigned int width)
00312 {
00313     ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
00314     page_info.index[page].ww = width;
00315 }
00316 
00317 void
00318 pageinfo_set_window_height(int page, unsigned int height)
00319 {
00320     ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
00321     page_info.index[page].wh = height;
00322 }
00323 
00324 int
00325 pageinfo_get_number(int page)
00326 {
00327     ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
00328     return page_info.index[page].number;
00329 }
00330 
00331 /* search for page with TeX number `number', and return its index, or -1 if it's not found. */
00332 int
00333 pageinfo_get_index_of_number(int number)
00334 {
00335     size_t i;
00336     for (i = 0; i < page_info.index_size - 1; i++) {
00337        if (number == page_info.index[i].number)
00338            return i;
00339     }
00340     return -1;
00341 }
00342 
00343 void
00344 pageinfo_set_offset(int index, long offset)
00345 {
00346     ASSERT(index >= 0 && index < (int)page_info.index_size, "");
00347     page_info.index[index].offset = offset;
00348 }
00349 
00350 void
00351 pageinfo_set_number(int index, int number)
00352 {
00353     ASSERT(index >= 0 && index < (int)page_info.index_size, "");
00354     page_info.index[index].number = number;
00355 }
00356 
00357 void
00358 pageinfo_allocate(int total_pages)
00359 {
00360     int i;
00361     page_info.index = xmalloc(total_pages * sizeof *(page_info.index));
00362     for (i = 0; i < total_pages; i++) {
00363        page_info.index[i].marked = False;
00364     }
00365     /* following initializations are handled by the respective Motif/Xaw functions */
00366     page_info.page_labels = NULL;
00367     page_info.index_size = total_pages;
00368 }
00369 
00370 /*
00371   Deallocate page_info. NOTE: We mustn't free the page_labels here,
00372   since the page list might survive quite some time (e.g. while fonts
00373   for the new file are being generated) and needs the labels.
00374 */
00375 void
00376 pageinfo_deallocate(void)
00377 {
00378     free(page_info.index);
00379     page_info.index_size = 0;
00380     page_info.index = NULL;
00381 }
00382 
00383 #ifdef MOTIF
00384 void
00385 toggle_pagelist(void)
00386 {
00387     Dimension curr_w, curr_x;
00388     XtVaGetValues(globals.widgets.main_window,
00389                 XmNwidth, &curr_w,
00390                 XmNx, &curr_x,
00391                 NULL);
00392 
00393     if ((resource.expert_mode & XPRT_SHOW_PAGELIST) != 0) {
00394        XtManageChild(XtParent(page_list));
00395        XtManageChild(page_list);
00396        
00397        curr_x += resource.pagelist_width;
00398        curr_w -= resource.pagelist_width;
00399        XtVaSetValues(globals.widgets.main_window,
00400                     XtNwidth, curr_w,
00401                     XtNx, curr_x,
00402                     XmNleftAttachment, XmATTACH_WIDGET,
00403                     XmNleftWidget, XtParent(page_list),
00404                     NULL);
00405     }
00406     else {
00407        XtUnmanageChild(XtParent(page_list));
00408        XtUnmanageChild(page_list);
00409        
00410        curr_x -= resource.pagelist_width;
00411        curr_w += resource.pagelist_width;
00412        XtVaSetValues(globals.widgets.main_window,
00413                     XmNwidth, curr_w,
00414                     XmNx, curr_x,
00415                     XmNleftAttachment, XmATTACH_FORM,
00416                     NULL);
00417     }
00418 
00419 #if defined(NEW_MENU_CREATION) || defined(MOTIF)
00420     set_menu(&resource.expert_mode, Act_set_expert_mode, check_resource_expert);
00421 #else
00422     set_show_pagelist_option();
00423 #endif
00424 }
00425 #endif
00426 
00427 
00428 Boolean
00429 pageinfo_have_marked_pages(void)
00430 {
00431     int i;
00432     for (i = 0; i < total_pages; i++) {
00433        if (page_info.index[i].marked) {
00434            return True;
00435        }
00436     }
00437     return False;
00438 }
00439 
00440 
00441 /* return True if page i is marked, False else */
00442 Boolean
00443 pageinfo_is_marked(int i)
00444 {
00445     ASSERT(i <= (int)page_info.index_size, "");
00446     return page_info.index[i].marked;
00447 }
00448 
00449 
00450 typedef enum { SCROLL_UP, SCROLL_DOWN, CLICK } saveCmdT;
00451 
00452 
00453 static void internal_process_button2(Widget widget, XEvent *event);
00454 static int get_item_index(Widget w, int mouse_y);
00455 
00456 static int
00457 get_page_size(void)
00458 {
00459     int offset = 0;
00460     int min_page = 0;
00461     int max_page = 0;
00462     int min_pageno_len = 0;
00463     int max_pageno_len = 0;
00464     int i;
00465 
00466     if (globals.dvi_file.bak_fp == NULL)
00467        return 0;
00468     
00469     for (i = 0; i < total_pages; i++) {
00470        max_page = MAX(page_info.index[i].number, max_page);
00471        min_page = MIN(page_info.index[i].number, min_page);
00472     }
00473 
00474     if (min_page >= 0) {
00475        offset = 0;   /* plus symbol is hidden */
00476     } else {
00477        offset = 1;   /* offset for minus symbol */
00478        min_page = -min_page;
00479     }
00480     for (min_pageno_len = offset; min_page > 0;
00481         min_page /= 10, min_pageno_len++);
00482 
00483     if (max_page >= 0) {
00484        offset = 0;   /* plus symbol is hidden */
00485     } else {
00486        offset = 1;   /* offset for minus symbol */
00487        max_page = -max_page;
00488     }
00489     for (max_pageno_len = offset; max_page > 0;
00490         max_page /= 10, max_pageno_len++);
00491 
00492     return MAX(min_pageno_len, max_pageno_len);
00493   /* Plus 1 for minus symbol */
00494 }
00495 
00496 #if !defined(LESSTIF_VERSION)
00497 /* draw or erase highlight marker at position x, y with widht w an height h,
00498    using suitable offsets (for sake of consistency of the latter, and because
00499    of the differences Xaw/Motif, this is a separate function).
00500  */
00501 static void
00502 refresh_highlight_marker(Widget widget, GC gc,
00503                       Position x, Position y, Dimension w, Dimension h)
00504 {
00505     XDrawRectangle(XtDisplay(widget), XtWindow(widget), gc,
00506 #ifdef MOTIF
00507                  x + 1, y + 1, w - 3, h - 3
00508 #else
00509                  x + 2, y, w - 1, h - 1
00510 #endif
00511                  );
00512 }
00513 #endif /* !defined(LESSTIF_VERSION) */
00514 
00515 #ifndef MOTIF
00516 
00517 /* update (redisplay) the list, saving the currently highlighted item.
00518  * This is invoked whenever marking the page, and the re-construction of the
00519  * entire list causes considerable flicker; but I guess that's unavoidable
00520  * with the current simplistic labelling scheme (with changing the list items
00521  * themselves). Gv does this considerably better (using custom widgets).
00522  */
00523 static void
00524 xaw_update_list(void)
00525 {
00526     static int pagelist_width = -1;
00527     static int total_pages_bak = -1;
00528 
00529     XawListReturnStruct *ret;
00530     int idx, button_width;
00531     
00532     if (pagelist_width == -1 || total_pages != total_pages_bak) {
00533        pagelist_width = xaw_get_pagelist_size();
00534        total_pages_bak = total_pages;
00535     }
00536     
00537     /* save selected item */
00538     ret = XawListShowCurrent(LIST_WIDGET);
00539     idx = ret->list_index;
00540     button_width = get_panel_width() - 2 * (resource.btn_side_spacing + resource.btn_border_width);
00541     /* delete and re-create list */
00542     ASSERT(total_pages <= (int)page_info.index_size, "");
00543     XawListChange(LIST_WIDGET, page_info.page_labels, 0,
00544                 MAX(button_width, pagelist_width), False);
00545     /* restore selected item */
00546     if (idx != XAW_LIST_NONE) {
00547        XawListHighlight(LIST_WIDGET, idx);
00548     }
00549 }
00550 
00551 /*
00552   return height of a row in the list widget, and the initial offset of XtNinternalHeight
00553   in parameters row_height and internal_h
00554 */
00555 static void
00556 xaw_get_row_height(Widget w, Dimension *row_height, Dimension *internal_h)
00557 {
00558     Dimension row_space;
00559     XFontStruct *font;
00560     Arg arglist[5];
00561     int i = 0;
00562 
00563     if (w == NULL || !XtIsRealized(w))
00564        return;
00565 
00566     XtSetArg(arglist[i], XtNfont, &font); ++i;
00567     XtSetArg(arglist[i], XtNrowSpacing, &row_space); ++i;
00568     XtSetArg(arglist[i], XtNinternalHeight, internal_h); i++;
00569     XtGetValues(w, arglist, i);
00570     *row_height = font->max_bounds.ascent + font->max_bounds.descent + row_space;
00571 }
00572 
00573 /*
00574  * Get pagelist width.
00575  */
00576 int
00577 xaw_get_pagelist_size(void)
00578 {
00579     Widget w;
00580     XFontStruct *font;
00581 
00582     w = XtVaCreateWidget("list", listWidgetClass, globals.widgets.top_level, NULL);
00583     XtVaGetValues(w, XtNfont, &font, NULL);
00584     XtDestroyWidget(w);
00585 
00586     /* have space for max. pageno + space + current-marker,
00587        plus a few pixels for right margin */
00588     return (get_page_size() + 2) * get_avg_font_width(font) + 6;
00589 }
00590 
00591 /* auto-scroll pagelist when mouse-1 is down and moved above top or below bottom of
00592    page list.
00593 */
00594 static void
00595 xaw_drag_page_callback(Widget widget, XtPointer data, XEvent *event, Boolean *cont)
00596 {
00597     int y, idx, actual_idx = 0;
00598 
00599     UNUSED(data);
00600     UNUSED(cont);
00601     
00602     if (event->xany.type == ButtonPress || ((event->xbutton.state & Button1Mask) == 0))
00603        return;
00604     if (event->xany.type == MotionNotify)
00605        y = (int)event->xmotion.y;
00606     else
00607        y = (int)event->xbutton.y;
00608 
00609     idx = get_item_index(widget, y);
00610     
00611     if (idx <= 0) {
00612        idx = 1;
00613     }
00614     else if (idx > total_pages) {
00615        idx = total_pages;
00616     }
00617 
00618     actual_idx = xaw_maybe_scroll_pagelist(idx, False, actual_idx);
00619     XawListHighlight(LIST_WIDGET, idx - MOTIF_IDX_OFFSET);
00620 
00621 /*      if (event->xany.type == ButtonRelease) { */
00622 /*     fprintf(stderr, "1\n"); */
00623 /*     page_history_insert(idx - MOTIF_IDX_OFFSET); */
00624 /*     goto_page(idx - MOTIF_IDX_OFFSET, resource.keep_flag ? NULL : home); */
00625 /*     search_signal_page_changed(); */
00626 /*      } */
00627 }
00628 
00629 static void
00630 xaw_SendReportProc(Widget w, XtPointer closure, XtPointer call_data)
00631 {
00632     XawPannerReport *rep = (XawPannerReport *) call_data;
00633 
00634     UNUSED(w);
00635     UNUSED(closure);
00636     
00637     view_y = rep->slider_y;
00638 }
00639 
00640 #endif /* not MOTIF */
00641 
00642 
00643 
00644 /* returns the index of the current item in `Motif'-style, i.e. first item has index 1, not 0 */
00645 static int
00646 get_item_index(Widget w, int mouse_y)
00647 {
00648 #ifdef MOTIF
00649     return XmListYToPos(w, mouse_y);
00650 #else
00651     Dimension row_height, internal_height;
00652     
00653     xaw_get_row_height(w, &row_height, &internal_height);
00654     return (mouse_y - internal_height) / row_height + MOTIF_IDX_OFFSET;
00655 #endif
00656 }
00657 
00658 #if !defined(LESSTIF_VERSION)
00659 /* idx is Motif-style index, i.e. 1 for 1st item, not 0 */
00660 static Boolean
00661 my_list_pos_to_bounds(Widget widget, int idx, Position *x, Position *y, Dimension *w, Dimension *h)
00662 {
00663 #ifdef MOTIF
00664     Position x1, y1;
00665     Dimension w1, h1;
00666     if (XmListPosToBounds(widget, idx, &x1, &y1, &w1, &h1)) {
00667        *x = x1;
00668        *y = y1;
00669        *w = w1;
00670        *h = h1;
00671        return True;
00672     }
00673     return False;
00674 #else
00675     Dimension row_height, internal_height;
00676     /* FIXME: Remove this hard-coded offset! */
00677     const int X_OFFSET = 9;
00678     const int RULE_OFFSET = 2;
00679 
00680     if (idx <= 0 || idx > total_pages) {
00681        return False;
00682     }
00683 
00684     if (viewport != NULL && XtIsRealized(viewport))
00685        XtVaGetValues(viewport, XtNx, x, NULL);
00686     xaw_get_row_height(widget, &row_height, &internal_height);
00687     XtVaGetValues(widget, XtNwidth, w, NULL);
00688     
00689     *x -= X_OFFSET;
00690     *y = row_height * idx + internal_height - row_height - 1;
00691     *w -= RULE_OFFSET;
00692     *h = row_height + RULE_OFFSET;
00693     
00694     return True;
00695 #endif
00696 }
00697 #endif /* !defined(LESSTIF_VERSION) */
00698 
00699 /* it seems that in order to support scrolling of the page list with wheel mice,
00700    we need to program this explicitly.
00701  */
00702 static void
00703 wheel_scroll_list_callback(Widget widget, XtPointer data, XEvent *event, Boolean *cont)
00704 {
00705     int button = event->xbutton.button;
00706 
00707     UNUSED(widget);
00708     UNUSED(data);
00709     UNUSED(cont);
00710 
00711     if (event->xany.type == ButtonPress && (button == 4 || button == 5)) {
00712 #if SCROLL_LIST_SCROLLBAR
00713        Widget vert = XtNameToWidget(viewport, "vertical");
00714        static Dimension row_height = 0, dummy = 0;
00715        int diff_y = 0;
00716 
00717        if (row_height == 0)
00718            xaw_get_row_height(LIST_WIDGET, &row_height, &dummy);
00719               
00720        if (vert == NULL) {
00721            XDVI_WARNING((stderr, "Couldn't get name of pagelist viewport widget!"));
00722            return;
00723        }
00724        if (button == 5) {
00725            diff_y = row_height;
00726        }
00727        else {
00728            diff_y = -row_height;
00729        }
00730        XtCallCallbacks(vert, XtNscrollProc, (XtPointer)diff_y);
00731 #else
00732        int pageno = current_page;
00733        if (button == 5) {
00734            if (current_page >= total_pages - 1) {
00735               XBell(DISP, 0);
00736               statusline_print(STATUS_SHORT, "Last page of DVI file");
00737               return;
00738            }
00739            pageno++;
00740        }
00741        else {
00742            if (current_page == 0) {
00743               XBell(DISP, 0);
00744               statusline_print(STATUS_SHORT, "First page of DVI file");
00745               return;
00746            }
00747            pageno--;
00748        }
00749        goto_page(check_goto_page(pageno), resource.keep_flag ? NULL : home, False);
00750        search_signal_page_changed();
00751 #endif
00752     }
00753 }
00754        
00755 
00756 #if !defined(LESSTIF_VERSION)
00757 /* draw a hightlight rectangle around the page the mouse is currently over, to
00758    make e.g. marking easier.
00759  */
00760 static void
00761 highlight_page_callback(Widget widget, XtPointer data, XEvent *event, Boolean *cont)
00762 {
00763     int curr_idx = get_item_index(widget, event->xmotion.y);
00764     Position x, y;
00765     Dimension w, h;
00766     static int idx_bak = -1;
00767     
00768     UNUSED(data);
00769     UNUSED(cont);
00770 
00771     switch(event->xany.type) {
00772     case ButtonPress:
00773     case ButtonRelease:
00774     case MotionNotify:
00775        /* might need to un-highlight previous one */
00776        if (idx_bak >= 0 && idx_bak != curr_idx
00777            && my_list_pos_to_bounds(widget, idx_bak, &x, &y, &w, &h)) {
00778 /*         fprintf(stderr, "index: %d, %d, h: %d, w: %d\n", x, y, h, w); */
00779            refresh_highlight_marker(widget, m_page_gc.back, x, y, w, h);
00780 #if REDRAW_CURRENT_MARKER_HACK
00781            xaw_maybe_redraw_highlight(curr_idx);
00782 #endif
00783        }
00784        idx_bak = curr_idx;
00785        /* redraw unless out of bounds (when pagelist is shorter than view area) */
00786        if (my_list_pos_to_bounds(widget, curr_idx, &x, &y, &w, &h)) {
00787            refresh_highlight_marker(widget, m_page_gc.fore, x, y, w, h);
00788 #if REDRAW_CURRENT_MARKER_HACK
00789            g_current_highlighted = curr_idx;
00790 #endif
00791        }
00792        break;
00793     case LeaveNotify:
00794        /* this might look overly complicated, but is neccessary to cover all
00795           cases of 1-pixel movement up/down before leaving the list, or no
00796           movement at all before leaving it. */
00797        if ((idx_bak >= 0 && idx_bak != curr_idx && my_list_pos_to_bounds(widget, idx_bak, &x, &y, &w, &h))
00798            || my_list_pos_to_bounds(widget, curr_idx, &x, &y, &w, &h)) {
00799            refresh_highlight_marker(widget, m_page_gc.back, x, y, w, h);
00800 #if REDRAW_CURRENT_MARKER_HACK
00801            xaw_maybe_redraw_highlight(curr_idx);
00802 #endif
00803        }
00804        break;
00805     default:
00806        break;
00807     }
00808 }
00809 #endif /* !defined(LESSTIF_VERSION) */
00810 
00811 /*
00812  * invoked on Button-1 Down.
00813  */
00814 static void
00815 select_page_callback(Widget w, XtPointer closure, XtPointer call_data)
00816 {
00817 #ifdef MOTIF
00818     XmListCallbackStruct *cbs = (XmListCallbackStruct *) call_data;
00819     int new = cbs->item_position;
00820 
00821     UNUSED(w);
00822     UNUSED(closure);
00823 
00824     maybe_scroll_pagelist(new - MOTIF_IDX_OFFSET, False);
00825     page_history_insert(new - MOTIF_IDX_OFFSET);
00826     goto_page(new - MOTIF_IDX_OFFSET, resource.keep_flag ? NULL : home, False);
00827 #else
00828     XawListReturnStruct *item = (XawListReturnStruct *) call_data;
00829     int new = item->list_index;
00830 
00831     UNUSED(w);
00832     UNUSED(closure);
00833 
00834     if (globals.debug & DBG_EVENT)
00835        fprintf(stderr, "got: button-1 for `%d'\n", new);
00836 
00837 #if 0
00838     fprintf(stderr, "select page: %d\n", new);
00839 #endif
00840     maybe_scroll_pagelist(new, False);
00841     page_history_insert(new);
00842     goto_page(new, resource.keep_flag ? NULL : home, False);
00843 #endif
00844     search_signal_page_changed();
00845 }
00846 
00847 
00848 static void
00849 init_pagelabels(int start, int end)
00850 {
00851     int i;
00852     char s[PAGENUMLEN];
00853 #if 0
00854     fprintf(stderr, "===== init_pagelabels from %d to %d\n", start, end);
00855 #endif
00856     ASSERT(end < (int)page_info.index_size, "");
00857     
00858     page_info.page_labels = xrealloc(page_info.page_labels, sizeof *(page_info.page_labels) * (end + 2));
00859     for (i = start; i < end; i++) {
00860        if (page_info.index[i].marked)
00861            sprintf(s, "* %*d  ", get_page_size(),
00862                   resource.use_tex_pages ? page_info.index[i].number : i + 1);
00863        else
00864            sprintf(s, "  %*d  ", get_page_size(),
00865                   resource.use_tex_pages ? page_info.index[i].number : i + 1);
00866 
00867        page_info.page_labels[i] = xstrdup(s);
00868     }
00869     page_info.page_labels[i] = NULL; /* terminate - important for creating the widget. */
00870 }
00871 
00872 
00873 #ifdef MOTIF
00874 
00875 
00876 static int
00877 xm_get_top_visible(int start)
00878 {
00879     int top = start;
00880     while (top < total_pages && !XmListPosToBounds(LIST_WIDGET, top, NULL, NULL, NULL, NULL))
00881         top++;
00882     return top;
00883 }
00884 
00885 static int
00886 xm_get_bottom_visible(int start)
00887 {
00888     int bot = start;
00889     while (bot < total_pages && XmListPosToBounds(LIST_WIDGET, bot, NULL, NULL, NULL, NULL))
00890         bot++;
00891     bot--;
00892     return bot;
00893 }
00894 
00895 /* Scroll pagelist down or up if needed, and update top_visible and
00896    bot_visible.  List is always scrolled so that 1 element is still
00897    visible below or above pointer, to make it possible to flip through
00898    document by repeatedly clicking on first/last.
00899  */
00900 static void
00901 xm_maybe_scroll_pagelist(int current, saveCmdT curr_cmd, int *top_visible, int *bot_visible)
00902 {
00903 #if 0
00904     fprintf(stderr, "topmost visible: %d, bottom: %d, current: %d, total: %d\n",
00905            top_visible, bottom_visible, current, total_pages); 
00906 #endif
00907 
00908     if (current < *top_visible && curr_cmd != SCROLL_DOWN) {
00909        XmListSetPos(LIST_WIDGET,
00910                    current < 1
00911                    ? 1
00912                    : current);
00913        (*top_visible)--;
00914        (*bot_visible)--;
00915     }
00916     else if (current + MOTIF_IDX_OFFSET >= *bot_visible && curr_cmd != SCROLL_UP) {
00917        XmListSetBottomPos(LIST_WIDGET,
00918                         current + MOTIF_IDX_OFFSET >= total_pages
00919                         ? current + MOTIF_IDX_OFFSET
00920                         : current + MOTIF_IDX_OFFSET + 1);
00921        (*top_visible)++;
00922        (*bot_visible)++;
00923     }
00924 }
00925 
00926 static void
00927 xm_set_page_labels(void)
00928 {
00929     int i;
00930     char buf[PAGENUMLEN];
00931 
00932     XmString *motif_page_labels = xmalloc((total_pages + 2) * sizeof *motif_page_labels);
00933     
00934     for (i = 0; i < total_pages; ++i) {
00935        sprintf(buf, pageno_format, ' ', page_info.page_labels[i]);
00936        motif_page_labels[i] = XmStringCreateLocalized(buf);
00937     }
00938     XmListDeleteAllItems(LIST_WIDGET);
00939     XmListAddItems(LIST_WIDGET, motif_page_labels, total_pages, 0);
00940 
00941     XmListSelectPos(LIST_WIDGET, current_page + MOTIF_IDX_OFFSET, False);
00942     for (i = 0; i < total_pages; ++i) {
00943        XmStringFree(motif_page_labels[i]);
00944     }
00945     free(motif_page_labels);
00946 }
00947 
00948 static void
00949 xm_toggle_label(Widget widget, int idx, Boolean update)
00950 {
00951     /* TODO: use `update' to update all labels at once when toggling multiple */
00952     XmString str;
00953     char *mark_font, buf[128];
00954 
00955     if (widget == NULL)
00956        return;
00957     
00958     UNUSED(update);
00959 
00960 /*     ensure_labelinfo_size(idx); */
00961     ASSERT(idx < (int)page_info.index_size, "");
00962     if (!page_info.index[idx].marked) {
00963        sprintf(buf, pageno_format, '*', page_info.page_labels[idx]);
00964        mark_font = "MARKED";
00965        page_info.index[idx].marked = True;
00966     }
00967     else {
00968        sprintf(buf, pageno_format, ' ', page_info.page_labels[idx]);
00969        mark_font = "UNMARKED";
00970        page_info.index[idx].marked = False;
00971     }
00972 /*     str = XmStringCreateLocalized(buf); */
00973     str = XmStringCreateLtoR(buf, mark_font);
00974     XmListReplaceItemsPos(widget, &str, 1, idx + MOTIF_IDX_OFFSET);
00975     XmStringFree(str);
00976 }
00977 
00978 
00979 #else /* MOTIF */
00980 
00981 static void
00982 mark_page_callback(Widget w, XtPointer data, XEvent *event, Boolean *cont)
00983 {
00984     UNUSED(data);
00985     UNUSED(cont);
00986 
00987     /* moving button2 generates MotionNotify events for button0 */
00988     if (event->type == MotionNotify || event->xbutton.button == Button2)
00989        internal_process_button2(w, event);
00990 
00991     if (pageinfo_have_marked_pages()) {
00992        notify_print_dialog_have_marked(True);
00993     }
00994     else {
00995        notify_print_dialog_have_marked(False);
00996     }
00997 
00998 }
00999 
01000 void
01001 xaw_create_pagelist_widgets(Dimension height, Dimension width, Position y, Widget parent)
01002 {
01003     viewport = XtVaCreateWidget("viewport",
01004                             viewportWidgetClass, parent,
01005                             XtNallowVert, True,
01006                             /* this is not related to the scroll bar: */
01007                             /* XtNforceBars, True, */
01008                             XtNx, resource.btn_side_spacing,
01009                             XtNy, y,
01010                             XtNheight, height,
01011                             XtNwidth, width,
01012                             NULL);
01013     LIST_WIDGET = XtVaCreateWidget("list",
01014                                listWidgetClass, viewport,
01015                                XtNlist, page_info.page_labels,
01016                                XtNdefaultColumns, 1,
01017                                XtNforceColumns, True,
01018                                XtNx, 10,
01019                                XtNy, 10,
01020                                XtNheight, height,
01021                                XtNwidth, width - 10,
01022                                XtNlongest, LONGESTPAGENUM,
01023                                XtNverticalList, True,
01024                                NULL);
01025     XtManageChild(LIST_WIDGET);
01026     XtManageChild(viewport);
01027     XtAddCallback(LIST_WIDGET, XtNcallback, select_page_callback,
01028                 (XtPointer) NULL);
01029     /* for scrolling the list */
01030     XtAddCallback(viewport, XtNreportCallback, xaw_SendReportProc,
01031                 (XtPointer) NULL);
01032     XtAddEventHandler(LIST_WIDGET,
01033                     ButtonPressMask | ButtonReleaseMask | Button2MotionMask,
01034                     False, mark_page_callback, (XtPointer)NULL);
01035 
01036     if (resource.pagelist_highlight_current)
01037        XtAddEventHandler(LIST_WIDGET,
01038                        ButtonPressMask | ButtonReleaseMask | PointerMotionMask | LeaveWindowMask,
01039                        False, highlight_page_callback, (XtPointer)NULL);
01040 
01041     
01042     XtAddEventHandler(LIST_WIDGET, ButtonPressMask | ButtonReleaseMask,
01043                     False, wheel_scroll_list_callback, (XtPointer)NULL);
01044     
01045     
01046     XtAddEventHandler(LIST_WIDGET,
01047                     /* FIXME: We should add PointerMotionMask here, but handling PointerMotionMask
01048                       currently doesn't work with the Xaw list widget: the auto-scrolling code doesn't
01049                       realize when the mouse direction of the pointer movement changes, and continues
01050                       to scroll into the same direction. This will be rather annoying for users, so
01051                       we disabled PointerMotionMask for the time being.
01052                     */
01053                     ButtonReleaseMask /* | PointerMotionMask */ | Button1MotionMask,
01054                     False, xaw_drag_page_callback, (XtPointer)NULL);
01055 }
01056 
01057 static void
01058 xaw_toggle_label(Widget w, int idx, Boolean update)
01059 {
01060     if (w == NULL)
01061        return;
01062 
01063 /*     ensure_labelinfo_size(idx); */
01064     ASSERT(idx < (int)page_info.index_size, "");
01065     if (!page_info.index[idx].marked) {
01066 /*     sprintf(toc[idx], "* %*d  ", get_page_size(), page_index[idx].number);  */
01067        sprintf(page_info.page_labels[idx], pageno_format, '*', get_page_size(),
01068               resource.use_tex_pages ? page_info.index[idx].number : idx + 1);
01069        page_info.index[idx].marked = True;
01070     }
01071     else {
01072        sprintf(page_info.page_labels[idx], pageno_format, ' ', get_page_size(),
01073               resource.use_tex_pages ? page_info.index[idx].number : idx + 1);
01074 /*     sprintf(toc[idx], "  %*d  ", get_page_size(), page_index[idx].number);  */
01075        page_info.index[idx].marked = False;
01076     }
01077     
01078     if (update)
01079        xaw_update_list();
01080 }
01081 
01082 static int
01083 xaw_maybe_scroll_pagelist(int new_page, Boolean force_recenter, int idx_bak)
01084 {
01085     Position x;
01086     Position y, new_y, bot_y;
01087     Dimension view_height, row_height, internal_height;
01088     /* Dimension clip_height; */
01089     /* static Widget list_clip = 0; */
01090     
01091     if (LIST_WIDGET == NULL || (resource.expert_mode & XPRT_SHOW_BUTTONS) == 0)
01092        return idx_bak;
01093 
01094 /*     if (list_clip == 0) { */
01095 /*     list_clip = XtNameToWidget(viewport, "clip"); */
01096 /*     } */
01097 /*     if (XtIsRealized(list_clip)) */
01098 /*     XtVaGetValues(list_clip, XtNheight, &clip_height, XtNx, &cx, XtNy, &y1, NULL); */
01099     if (viewport != NULL && XtIsRealized(viewport))
01100        XtVaGetValues(viewport, XtNheight, &view_height, XtNx, &x, NULL);
01101 /*     fprintf(stderr, "diff: %d, %d, %d, %d, %d\n", cx - x, clip_height, view_height, y1, (int)y2); */
01102     xaw_get_row_height(LIST_WIDGET, &row_height, &internal_height);
01103     y = row_height * new_page;
01104 
01105 #if DEBUG
01106     fprintf(stderr, "###### xaw_maybe_scroll_pagelist: y %d, view_y %d, view_height %d, row_height %d, internal_height %d; actual %d\n",
01107            y, view_y, view_height, row_height, internal_height, idx_bak);
01108 #endif
01109     /*FIXME: when page list is destroyed, view_y will be 0 until user scrolls page list */
01110     bot_y = view_y + view_height;
01111     if (force_recenter || ((y >= bot_y - row_height))) {
01112 #if DEBUG
01113        fprintf(stderr, "scrolled below bottom; incrementing %d to %d\n", view_y, view_y + row_height);
01114 #endif
01115        y += row_height;
01116        XawViewportSetCoordinates(viewport, x, y - view_height > 0 ? y - view_height : 0);
01117        return new_page + 1;
01118     }
01119     else if (force_recenter || ((y <= view_y + row_height + internal_height))) {
01120 #if DEBUG
01121        fprintf(stderr, "scrolled over top; new_y: %d\n", y - row_height);
01122 #endif
01123        new_y = y - 2 * row_height;
01124        XawViewportSetCoordinates(viewport, x, new_y);
01125        return new_page - 1;
01126     }
01127     /* not scrolled */
01128     return -2;
01129 }
01130 
01131 #endif /* MOTIF */
01132 
01133 
01134 /* idx is C-style index (0-based), not Motif one (1-based) */
01135 static void
01136 toggle_label(Widget widget, int idx, Boolean update)
01137 {
01138     if (idx >= total_pages)
01139        return;
01140     ASSERT(idx < total_pages, "");
01141     ASSERT(idx >= 0, "");
01142 #ifdef MOTIF
01143     xm_toggle_label(widget, idx, update);
01144 #else
01145     xaw_toggle_label(widget, idx, update);
01146 #endif
01147 }
01148 
01149 
01150 void
01151 list_toggle_marks(int arg)
01152 {
01153     
01154     int i;
01155 
01156     if (arg < 0) { /* mark all */
01157        for (i = 0; i < total_pages; i++) {
01158            ASSERT(i < (int)page_info.index_size, "");
01159 /*         ensure_labelinfo_size(i); */
01160            if (!page_info.index[i].marked) {
01161               toggle_label(LIST_WIDGET, i, False);
01162            }
01163        }
01164     }
01165     else if (arg == 0) { /* unmark all */
01166        for (i = 0; i < total_pages; i++) {
01167            ASSERT(i < (int)page_info.index_size, "");
01168 /*         ensure_labelinfo_size(i); */
01169            if (page_info.index[i].marked) {
01170               toggle_label(LIST_WIDGET, i, False);
01171            }
01172        }
01173     }
01174     else { /* toggle odd/even */
01175        if (arg == 2) /* toggle even */
01176            arg = 0;
01177        for (i = 0; i < total_pages; i++) {
01178            if ((i + 1) % 2 == arg) {
01179               toggle_label(LIST_WIDGET, i, False);
01180            }
01181        }
01182     }
01183     /* TODO: update widget once for Motif as well! */
01184 #ifndef MOTIF
01185     xaw_update_list();
01186 #endif
01187 }
01188 
01189 static Boolean PagelistInitialized = False;
01190 
01191 #ifndef MOTIF
01192 void
01193 handle_pagelist_resize(void)
01194 {
01195     /* TODO: the following will mess up the geometry of the list
01196        (doesn't increase height, and incrementally decreases width):
01197     if (list_widget) {
01198        Dimension height;
01199        --- without the (un)manage, I get an X Error:
01200        XtMakeGeometryRequest - parent has no geometry manager
01201        ---
01202        XtUnmanageChild(viewport);
01203        XtUnmanageChild(LIST_WIDGET);
01204        XtVaGetValues(globals.widgets.clip_widget, XtNheight, &height, NULL);
01205        height -= resource.btn_top_spacing + resource.btn_border_width + global_y_pos;
01206        XtVaSetValues(viewport, XtNheight, height, NULL);
01207        XtManageChild(LIST_WIDGET);
01208         XtManageChild(viewport);
01209     }
01210     ... so we use brute force instead: */
01211     handle_destroy_pagelist(LIST_WIDGET, NULL, NULL);
01212     create_pagelist();
01213 }
01214 
01215 void
01216 handle_destroy_pagelist(Widget w, XtPointer client_data, XtPointer call_data)
01217 {
01218     UNUSED(w);
01219     UNUSED(client_data);
01220     UNUSED(call_data);
01221     
01222     if (viewport != NULL) {
01223        XtDestroyWidget(viewport);
01224        viewport = NULL;
01225        LIST_WIDGET = NULL;
01226     }
01227     PagelistInitialized = False;
01228 }
01229 #endif /* MOTIF */
01230 
01231 void
01232 create_pagelist(void)
01233 {
01234     Pixel background, foreground;
01235 #ifdef MOTIF
01236     
01237 /*     items = xrealloc(items, sizeof *items * (total_pages + 2)); */
01238     init_pagelabels(0, total_pages);
01239     xm_set_page_labels();
01240     if (!PagelistInitialized) {
01241        XtAppContext app;
01242 
01243        XtVaGetValues(LIST_WIDGET, XmNforeground, &foreground, XmNbackground, &background, NULL);
01244        m_page_gc.back = set_or_make_gc(NULL, GXcopy, background, foreground);
01245        m_page_gc.fore = set_or_make_gc(NULL, GXcopy, foreground, background);
01246        XtManageChild(LIST_WIDGET);
01247 
01248        XtAddCallback(LIST_WIDGET, XmNbrowseSelectionCallback, select_page_callback, NULL);
01249 #if !defined(LESSTIF_VERSION)
01250        /*
01251          Don't use the highlighting hack with LessTif, since its XmListPosToBounds()
01252          is too broken to be usable (as of 0.93.36):
01253          - it returns generally too low values, apparently it doesn't take
01254            XmNlistSpacing into account;
01255          - it doesn't take scrollbar position into account.
01256        */
01257        if (resource.pagelist_highlight_current)
01258            XtAddEventHandler(LIST_WIDGET,
01259                            ButtonPressMask | ButtonReleaseMask | PointerMotionMask | LeaveWindowMask,
01260                            False, highlight_page_callback, (XtPointer)NULL);
01261 #endif /* !defined(LESSTIF_VERSION) */
01262        XtAddEventHandler(LIST_WIDGET, ButtonPressMask | ButtonReleaseMask,
01263                        False, wheel_scroll_list_callback, (XtPointer)NULL);
01264        
01265        app = XtWidgetToApplicationContext(globals.widgets.top_level);
01266        XtAppAddActions(app, CustomListActions, XtNumber(CustomListActions));
01267        XtOverrideTranslations(LIST_WIDGET, XtParseTranslationTable(motif_custom_translations));
01268        PagelistInitialized = True;
01269     }
01270 #else /* MOTIF */
01271     if ((resource.expert_mode & XPRT_SHOW_BUTTONS) == 0) {
01272        PagelistInitialized = False; /* might need to re-create widgets in this case */
01273        return;
01274     }
01275 
01276     if (globals.debug & DBG_GUI)
01277        fprintf(stderr, "allocating list with %d pages\n", total_pages);
01278 
01279     init_pagelabels(0, total_pages);
01280     if (!PagelistInitialized) {
01281        xaw_create_pagelist();
01282        XtVaGetValues(LIST_WIDGET, XtNforeground, &foreground, XtNbackground, &background, NULL);
01283        m_page_gc.back = set_or_make_gc(NULL, GXcopy, background, foreground);
01284        m_page_gc.fore = set_or_make_gc(NULL, GXcopy, foreground, background);
01285        PagelistInitialized = True;
01286     }
01287 #endif /* MOTIF */
01288     /* scroll to the current page if needed */
01289     maybe_scroll_pagelist(current_page, False);
01290 }
01291 
01292 #ifndef MOTIF
01293 static void
01294 free_pagelabels(void)
01295 {
01296     int i;
01297     for (i = 0; page_info.page_labels != NULL && page_info.page_labels[i] != NULL; i++) {
01298        free(page_info.page_labels[i]);
01299     }
01300     free(page_info.page_labels);
01301     page_info.page_labels = NULL;
01302 }
01303 #endif /* not MOTIF */
01304 
01305 
01306 void
01307 refresh_pagelist(int newsize, int newpage)
01308 {
01309     if (
01310 #ifndef MOTIF
01311        (resource.expert_mode & XPRT_SHOW_BUTTONS) == 0 ||
01312 #endif
01313        !XtIsRealized(globals.widgets.top_level))
01314        return;
01315 
01316 #ifdef DEBUG
01317     fprintf(stderr, "=== refresh_pagelist: newsize %d, newpage %d\n", newsize, newpage);
01318 #endif
01319 #ifdef MOTIF
01320 /*     items = xrealloc(items, sizeof *items * (newsize + 2)); */
01321     init_pagelabels(0, newsize);
01322     xm_set_page_labels();
01323 #else /* MOTIF */
01324     if ((resource.expert_mode & XPRT_SHOW_BUTTONS) == 0)
01325        return;
01326 
01327     /* FIXME - is this really neccessary?? The alternative:
01328           XawListChange(LIST_WIDGET, page_info.page_labels, newsize, 0, True);
01329        has problems when freeing the page labels afterwards.
01330     */
01331     handle_destroy_pagelist(LIST_WIDGET, NULL, NULL);
01332 
01333     free_pagelabels();
01334     init_pagelabels(0, newsize);
01335 
01336     xaw_create_pagelist();
01337 #endif /* MOTIF */
01338     /* `True' since the pagelist is newly created */
01339     maybe_scroll_pagelist(newpage, True);
01340 }
01341 
01342 void
01343 maybe_scroll_pagelist(int newpage, Boolean force_recenter)
01344 {
01345 #ifdef MOTIF
01346     int top_visible, bot_visible;
01347     UNUSED(force_recenter);
01348 #endif
01349 
01350     if (
01351 #ifndef MOTIF
01352        (resource.expert_mode & XPRT_SHOW_BUTTONS) == 0 ||
01353 #endif
01354        !XtIsRealized(globals.widgets.top_level))
01355        return;
01356 
01357 #ifdef MOTIF
01358     XmListSelectPos(LIST_WIDGET, newpage + MOTIF_IDX_OFFSET, False);
01359 
01360     top_visible = xm_get_top_visible(1);
01361     bot_visible = xm_get_bottom_visible(top_visible);
01362 
01363     xm_maybe_scroll_pagelist(newpage, CLICK, &top_visible, &bot_visible);
01364 #if HAVE_XPM
01365     tb_check_navigation_sensitivity(current_page);
01366 #endif
01367 #else
01368 
01369     if (LIST_WIDGET == NULL)
01370        return;
01371     
01372     (void)xaw_maybe_scroll_pagelist(newpage + 1, force_recenter, 0);
01373 
01374     XawListHighlight(LIST_WIDGET, newpage);
01375 #if REDRAW_CURRENT_MARKER_HACK
01376     /* if the XawListHighlight happens adjacent to the page that was
01377        last highlighted with our home-made `current selected'
01378        rectangle, it might overdraw that rectangle. In this case,
01379        restore it:
01380      */
01381     xaw_maybe_redraw_current_marker(newpage);
01382 #endif
01383 #endif
01384 }
01385 
01386 #ifdef MOTIF
01387 static void
01388 set_all_marks(int from, int to)
01389 {
01390     int i;
01391     for (i = from; i < to; i++) {
01392 /*     ensure_labelinfo_size(i); */
01393        page_info.index[i].marked = True;
01394        toggle_label(LIST_WIDGET, i, False);
01395     }
01396 }
01397 
01398 static void
01399 internal_process_button2_drag(Widget widget, XEvent *event)
01400 {
01401     int i, idx, min = total_pages, max = 0;
01402     idx = get_item_index(widget, event->xbutton.y);
01403 
01404     for (i = 0; i < total_pages; i++) {
01405 /*     ensure_labelinfo_size(i); */
01406        if (page_info.index[i].marked && i > max)
01407            max = i;
01408     }
01409     for (i = total_pages; i > 0; i--) {
01410 /*     ensure_labelinfo_size(i); */
01411        if (page_info.index[i].marked && i < min)
01412            min = i;
01413     }
01414 
01415     if (min == total_pages)
01416        min = 0;
01417     if (max == 0)
01418        max = total_pages;
01419 
01420     if (idx < min) {
01421        set_all_marks(idx, min);
01422     }
01423     else if (idx > max) {
01424        set_all_marks(max + 1, idx);
01425     }
01426     else {
01427        set_all_marks(0, idx);
01428     }
01429 }
01430 #endif /* MOTIF */
01431 
01432 static void
01433 internal_process_button2(Widget widget, XEvent *event)
01434 {
01435     int curr_idx;
01436     static int prev_idx = 0;
01437 #ifndef MOTIF
01438     static int actual_idx = -2;
01439 #endif
01440     static int top_visible = 0, bot_visible = 0;
01441     static int prev_y = 0, curr_y = 0;
01442     static saveCmdT prev_cmd = 0;  /* previous command (CLICK/SCROLL_UP/SCROLL_DOWN) */
01443     static saveCmdT curr_cmd = 0;  /* current command (CLICK/SCROLL_UP/SCROLL_DOWN) */
01444     static saveCmdT bak_cmd = 0;   /* last command that started inside the pagelist (CLICK/SCROLL_UP/SCROLL_DOWN) */
01445     static Boolean change_scroll_direction = False;
01446     
01447     switch(event->xany.type) {
01448     case ButtonPress:
01449        prev_y = event->xbutton.y;
01450        prev_idx = curr_idx = get_item_index(widget, prev_y);
01451 #ifdef MOTIF
01452        top_visible = xm_get_top_visible(1);
01453        bot_visible = xm_get_bottom_visible(top_visible);
01454 #endif
01455        toggle_label(widget, curr_idx - 1, True);
01456        prev_cmd = CLICK;
01457 #ifdef MOTIF
01458        xm_maybe_scroll_pagelist(curr_idx - 1, CLICK, &top_visible, &bot_visible);
01459 #else
01460        actual_idx = xaw_maybe_scroll_pagelist(curr_idx, False, actual_idx);
01461 #endif
01462        break;
01463     case ButtonRelease:
01464        prev_cmd = CLICK;
01465        break;
01466     case MotionNotify:
01467        curr_y = (int)event->xmotion.y;
01468        curr_idx = get_item_index(widget, event->xmotion.y);
01469 #ifndef MOTIF
01470        if (actual_idx > 0) {
01471            curr_idx = actual_idx;
01472        }
01473 #endif
01474 
01475        if (curr_y < prev_y)
01476            curr_cmd = SCROLL_UP;
01477        else if (curr_y > prev_y)
01478            curr_cmd = SCROLL_DOWN;
01479        prev_y = curr_y;
01480 
01481        if (prev_cmd != CLICK && curr_cmd != prev_cmd)
01482            change_scroll_direction = True;
01483 
01484        if ((curr_idx != prev_idx && !change_scroll_direction)
01485            || (change_scroll_direction
01486               /* last or first visible are always spared, unless they are really
01487                  the first or last page; this way, always 1 more page is visible
01488                  than is currently marked/selected
01489               */
01490               && !(curr_idx == top_visible) && !(curr_idx == bot_visible))) {
01491            if (curr_idx <= 0) {
01492               /* When user has scrolled off, mark this by setting curr_idx to 1 more or less
01493                  than the pagelist has so that the last/first page don't oscillate between
01494                  marked/unmarked state when user continues to scroll.
01495                  Also, we continue scrolling as long as user drags in the same direction as
01496                  the last `real' scrolling event (saved as bak_cmd).
01497               */
01498               if (curr_cmd == SCROLL_DOWN && bak_cmd == SCROLL_DOWN && prev_idx <= total_pages) {
01499                   curr_idx = prev_idx + 1;
01500               }
01501               else if (curr_cmd == SCROLL_UP && bak_cmd == SCROLL_UP && prev_idx > 0) {
01502                   curr_idx = prev_idx - 1;
01503               }
01504            }
01505 
01506 
01507            if (curr_idx > 0 && curr_idx <= total_pages) {
01508               toggle_label(widget, curr_idx - 1, True);
01509 #ifdef MOTIF
01510               xm_maybe_scroll_pagelist(curr_idx - 1, curr_cmd, &top_visible, &bot_visible);
01511 #else
01512               actual_idx = xaw_maybe_scroll_pagelist(curr_idx, False, actual_idx);
01513 #endif
01514               prev_idx = curr_idx;
01515               bak_cmd = curr_cmd;
01516               change_scroll_direction = False;
01517            }
01518            else {
01519 #ifndef MOTIF
01520               if (curr_idx > total_pages)
01521                   actual_idx = -2;
01522               else
01523                   actual_idx = xaw_maybe_scroll_pagelist(curr_idx, False, actual_idx);
01524 #endif
01525            }
01526            prev_cmd = curr_cmd;
01527        }
01528        break;
01529     default:
01530        break;
01531     }
01532 }
01533 
01534 void
01535 list_toggle_current(int arg)
01536 {
01537     toggle_label(LIST_WIDGET, arg, True);
01538 
01539     if (pageinfo_have_marked_pages()) {
01540        notify_print_dialog_have_marked(True);
01541     }
01542     else {
01543        notify_print_dialog_have_marked(False);
01544     }
01545 }
01546 
01547 #ifdef MOTIF
01548 
01549 static void
01550 xm_list_set_mark(Widget widget,
01551                    XEvent *event,
01552                    String *params,
01553                    Cardinal *num_params)
01554 {
01555     UNUSED(params);
01556     
01557     if ((*num_params != 1) || !XmIsList(widget))
01558        return;
01559 
01560     internal_process_button2(widget, event);
01561 
01562     if (pageinfo_have_marked_pages()) {
01563        notify_print_dialog_have_marked(True);
01564     }
01565     else {
01566        notify_print_dialog_have_marked(False);
01567     }
01568 }
01569 
01570 static void
01571 xm_list_drag_mark(Widget widget,
01572                 XEvent *event,
01573                 String *params,
01574                 Cardinal *num_params)
01575 {
01576     UNUSED(params);
01577     
01578     if ((*num_params != 1) || !XmIsList(widget))
01579        return;
01580 
01581     internal_process_button2_drag(widget, event);
01582 
01583     if (pageinfo_have_marked_pages()) {
01584        notify_print_dialog_have_marked(True);
01585     }
01586     else {
01587        notify_print_dialog_have_marked(False);
01588     }
01589 }
01590 
01591 #endif /* MOTIF */