1 /*
2 * Page selector for xdvi
3 *
4 * Copyright (c) 2001-2004 xdvik development team
5 *
6 * This code is derived from the page selector in xdvik-j, and
7 * parts of it are Copyright (c) 1993, 1995
8 * MATSUURA Syun syun@fuka.info.waseda.ac.jp
9 * HIRAHARA Atsushi hirahara@fuka.info.waseda.ac.jp
10 * ONO Kouichi onono@fuka.info.waseda.ac.jp
11 * All rights reserved.
12 *
13 *
14 * (SU: I was unsure how to interpret the `All rights reserved' in the
15 * previous line, so emailed Ono Kouichi about this. Here's a
16 * verbatim quote of the relevant part of his answer (which was CC'ed
17 * to Hirahara Atsushi - all three of them had left Waseda university
18 * around '95):
19 *
20 * You can modify, embed, copy and distribute a part of or the
21 * entire of our source code when you specify our copyright in your
22 * xdvik version.
23 *
24 * IANAL, but I think this is compatible with the X consortium
25 * license, which follows.)
26 *
27 * Permission is hereby granted, free of charge, to any person obtaining a copy
28 * of this software and associated documentation files (the "Software"), to
29 * deal in the Software without restriction, including without limitation the
30 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
31 * sell copies of the Software, and to permit persons to whom the Software is
32 * furnished to do so, subject to the following conditions:
33 *
34 * The above copyright notice and this permission notice shall be included in
35 * all copies or substantial portions of the Software.
36 *
37 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
40 * PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
41 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
42 * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
43 * OTHER DEALINGS IN THE SOFTWARE.
44 *
45 */
46
47 /*
48 BUGS:
49
50 - with Xaw, the highlighting for the selected page (XawListHighlight)
51 vanishes when mouse is dragged outside the widget and then released
52 (but the respective page is selected, which is IMHO the desired behaviour).
53
54 - with Xaw, scrolling the list with PgUp/PgDown until the current page
55 gets `out of focus' should un-highlight the current page number
56
57 - The ASCII-based marks are *ugly*. Pixmaps (for the marked state)
58 would be better. The viewer gv has one (but it's Xaw only). Some
59 file directory widgets like
60 e.g. http://ftp.xfree86.org/pub/X.Org/contrib/widgets/ListTree-3.0b3.tar.gz
61 also have facilities for that, but most suffer from other
62 inadequacies (e.g. no such ting as browseSelection) and all kinds
63 of bitrot ... Another alternative would be using XmContainer
64 (see e.g. the `filemanager' example in demos/programs/filemanagers
65 in the openmotif distribution), but that's available for Motif >= 2.1 only.
66
67 */
68
69 #include "xdvi-config.h"
70 #include "xdvi.h"
71
72 #include <stdio.h>
73 #include <stdarg.h>
74 #include <stdlib.h>
75 #include <X11/Intrinsic.h>
76 #include <X11/StringDefs.h>
77 #include <X11/Shell.h>
78
79 #include "xm_toolbar.h"
80 #include "xm_menu.h"
81 #include "xaw_menu.h"
82
83 #include "x_util.h"
84
85 #ifdef MOTIF
86 # include <Xm/Xm.h>
87 # include <Xm/List.h>
88 # include <Xm/ScrollBar.h> /* for XmScrollBarGetValues */
89 #else /* MOTIF */
90 # include <X11/Xaw/Dialog.h>
91 # include <X11/Xaw/Cardinals.h>
92 # include <X11/Xaw/Command.h>
93 # include <X11/Xaw/List.h>
94 # include <X11/Xaw/Viewport.h>
95 #endif /* MOTIF */
96
97 #include "message-window.h"
98 #include "pagesel.h"
99 #include "util.h"
100 #include "string-utils.h"
101 #include "dvi-init.h"
102 #include "statusline.h"
103 #include "events.h"
104 #include "print-dialog.h"
105 #include "search-internal.h"
106 #include "pagehist.h"
107
108 #define PAGENUMLEN 128
109 #define SCROLL_LIST_SCROLLBAR 0
110
111 #ifndef MAX_PAGE
112 # define MAX_PAGE (1024)
113 #endif /* MAX_PAGE */
114
115 #define LONGESTPAGENUM 55
116
117
118 /* for saving the GC of the pagelist widget
119 (when un-highlighting items in highlight_page_callback, and
120 drawing the `current' marker)
121 */
122 static struct page_gc {
123 GC fore;
124 GC back;
125 } m_page_gc;
126
127 #define MOTIF_IDX_OFFSET 1 /* motif index starts at 1, not 0 */
128 #if !defined(LESSTIF_VERSION)
129 static Boolean my_list_pos_to_bounds(Widget widget, int idx, Position *x, Position *y, Dimension *w, Dimension *h);
130 static void refresh_highlight_marker(Widget widget, GC gc, Position x, Position y, Dimension w, Dimension h);
131 #endif /* !defined(LESSTIF_VERSION) */
132
133
134
135 #ifdef MOTIF
136
137 /* make button2 mark instead of drag&drop */
138 static void xm_list_set_mark(Widget widget, XEvent *event, String *params, Cardinal *num_params);
139 static void xm_list_drag_mark(Widget widget, XEvent *event, String *params, Cardinal *num_params);
140 static XtActionsRec CustomListActions[] = {
141 { "ListSetMark", xm_list_set_mark },
142 { "ListDragMark", xm_list_drag_mark },
143 };
144 static char *motif_custom_translations =
145 "#override \n"
146 "s <Btn2Down>: ListDragMark(ListButtonMotion)\n"
147 "<Btn2Motion>: ListSetMark(ListButtonMotion)\n"
148 "<Btn2Up>: ListSetMark(ListButtonMotion)\n"
149 "<Btn2Down>: ListSetMark(ListButtonMotion)\n"
150 "<Btn4Down>,<Btn4Up>: scroll-list-up()\n"
151 "<Btn5Down>,<Btn5Up>: scroll-list-down()\n"
152 /* /\* "s ~m ~a <Btn2Down>: ListMyProcessBtn2(ListBeginExtend)\n" *\/ */
153 /* /\* "s ~m ~a <Btn2Up>: ListMyProcessBtn2(ListEndExtend)\n" *\/ */
154 /* /\* "~c ~s ~m ~a <Btn2Down>: ListMyProcessBtn2(ListBeginSelect)\n" *\/ */
155 /* /\* "~c ~s ~m ~a <Btn2Up>: ListMyProcessBtn2(ListEndSelect)\n" *\/ */
156 /* /\* "c ~s ~m ~a <Btn2Down>: ListMyProcessBtn2(ListBeginToggle)\n" *\/ */
157 /* /\* "c ~s ~m ~a <Btn2Up>: ListMyProcessBtn2(ListEndToggle)\n" *\/ */
158 /* /\* "c ~s ~m a <Btn2Down>: ListProcessDrag()\n" *\/ */
159 /* /\* "~c s ~m a <Btn2Down>: ListProcessDrag()\n" *\/ */
160 ;
161
162 #define LIST_WIDGET page_list
163 /* motif pagenumber is a string */
164 static const char* const pageno_format = "%c %s ";
165
166 #else /* MOTIF */
167
168 static int view_y;
169 extern Widget panel_widget;
170 static Widget list_widget = NULL;
171 static Widget viewport = NULL;
172 #define LIST_WIDGET list_widget
173 /* Xaw pagenumber is an integer, and we need to left-pad it */
174 static const char* const pageno_format = "%c %*d ";
175
176 static int xaw_maybe_scroll_pagelist(int new_page, Boolean force_recenter, int old);
177
178 #define REDRAW_CURRENT_MARKER_HACK 1
179
180
181 /*
182 The following hack tries to address the following 2 bugs with the
183 self-made page highlighting marker:
184
185 - the marker overlaps with the ordinary XawListHighlight marker;
186 when un-highlighting a page, 1 pixel (vertically) at the edge of
187 the ordinary marker is overdrawn.
188
189 - When the XawListHighlight crosses the self-drawn rectangle,
190 the vertical bars remain visible, but the horizontal bars
191 are erased.
192
193 The hack just redraws the appropriate items whenever one of
194 the above can happen, i.e. when the two markers are 2 or less
195 pages apart from each other.
196 */
197 #if REDRAW_CURRENT_MARKER_HACK
198 /* Store index of currently marked (with our own marker) list
199 item, or -1 if none is marked. */
200 static int g_current_highlighted = -1;
201
202 /* redraw the default Xaw list highlight (XawListHighlight()) */
203 static void
xaw_maybe_redraw_highlight(int idx)204 xaw_maybe_redraw_highlight(int idx)
205 {
206 XawListReturnStruct *ret;
207 int high;
208
209 if (LIST_WIDGET == NULL)
210 return;
211
212 ret = XawListShowCurrent(LIST_WIDGET);
213 high = ret->list_index;
214 if (high != XAW_LIST_NONE && abs(idx - (high + MOTIF_IDX_OFFSET)) <= 2) {
215 /* re-highlight it */
216 XawListHighlight(LIST_WIDGET, high);
217 }
218 g_current_highlighted = -1;
219 }
220
221 /* redraw our own rectangle highlight marker: */
222 static void
xaw_maybe_redraw_current_marker(int idx)223 xaw_maybe_redraw_current_marker(int idx)
224 {
225 Position x, y;
226 Dimension w, h;
227
228 /* fprintf(stderr, "idx: %d, high: %d; diff: %d\n", idx + MOTIF_IDX_OFFSET, g_current_highlighted, */
229 /* abs(idx + MOTIF_IDX_OFFSET - g_current_highlighted)); */
230 if (abs((idx + MOTIF_IDX_OFFSET) - g_current_highlighted) <= 2
231 && my_list_pos_to_bounds(LIST_WIDGET, g_current_highlighted, &x, &y, &w, &h)) {
232 refresh_highlight_marker(LIST_WIDGET, m_page_gc.fore, x, y, w, h);
233 }
234 }
235 #endif /* REDRAW_CURRENT_MARKER_HACK */
236
237 #endif /* MOTIF */
238
239 /*
240 * Table of page offsets in DVI file, indexed by page number - 1,
241 * marked pages, and page sizes.
242 * Initialized in prepare_pages().
243 */
244 struct page_index {
245 long offset;
246 int number;
247 Boolean marked;
248 unsigned int pw, ph; /* page size */
249 unsigned int ww, wh; /* window size */
250 };
251
252 struct page_index_info {
253 struct page_index *index; /* above struct */
254 size_t index_size; /* size of currently allocated index */
255 char **page_labels; /* label strings */
256 };
257
258 static struct page_index_info page_info;
259
260 /* access functions used by dvi-draw.c and dvi-init.c */
261 long
pageinfo_get_offset(int page)262 pageinfo_get_offset(int page)
263 {
264 ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
265 /* fprintf(stderr, "offset for page %d is %ld\n", page, page_info.index[page].offset); */
266 return page_info.index[page].offset;
267 }
268
269 /* access functions used by dvi-draw.c and dvi-init.c */
270 unsigned int
pageinfo_get_page_width(int page)271 pageinfo_get_page_width(int page)
272 {
273 ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
274 return page_info.index[page].pw;
275 }
276
277 unsigned int
pageinfo_get_page_height(int page)278 pageinfo_get_page_height(int page)
279 {
280 ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
281 return page_info.index[page].ph;
282 }
283
284 unsigned int
pageinfo_get_window_width(int page)285 pageinfo_get_window_width(int page)
286 {
287 ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
288 return page_info.index[page].ww;
289 }
290
291 unsigned int
pageinfo_get_window_height(int page)292 pageinfo_get_window_height(int page)
293 {
294 ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
295 return page_info.index[page].wh;
296 }
297
298 void
pageinfo_set_page_width(int page,unsigned int width)299 pageinfo_set_page_width(int page, unsigned int width)
300 {
301 ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
302 page_info.index[page].pw = width;
303 }
304
305 void
pageinfo_set_page_height(int page,unsigned int height)306 pageinfo_set_page_height(int page, unsigned int height)
307 {
308 ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
309 page_info.index[page].ph = height;
310 }
311
312 void
pageinfo_set_window_width(int page,unsigned int width)313 pageinfo_set_window_width(int page, unsigned int width)
314 {
315 ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
316 page_info.index[page].ww = width;
317 }
318
319 void
pageinfo_set_window_height(int page,unsigned int height)320 pageinfo_set_window_height(int page, unsigned int height)
321 {
322 ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
323 page_info.index[page].wh = height;
324 }
325
326 int
pageinfo_get_number(int page)327 pageinfo_get_number(int page)
328 {
329 ASSERT(page >= 0 && page < (int)page_info.index_size, "Page number out of range");
330 return page_info.index[page].number;
331 }
332
333 /* search for page with TeX number `number', and return its index, or -1 if it's not found. */
334 int
pageinfo_get_index_of_number(int number)335 pageinfo_get_index_of_number(int number)
336 {
337 size_t i;
338 for (i = 0; i < page_info.index_size - 1; i++) {
339 if (number == page_info.index[i].number)
340 return i;
341 }
342 return -1;
343 }
344
345 void
pageinfo_set_offset(int index,long offset)346 pageinfo_set_offset(int index, long offset)
347 {
348 ASSERT(index >= 0 && index < (int)page_info.index_size, "");
349 page_info.index[index].offset = offset;
350 }
351
352 void
pageinfo_set_number(int index,int number)353 pageinfo_set_number(int index, int number)
354 {
355 ASSERT(index >= 0 && index < (int)page_info.index_size, "");
356 page_info.index[index].number = number;
357 }
358
359 void
pageinfo_allocate(int total_pages)360 pageinfo_allocate(int total_pages)
361 {
362 int i;
363 page_info.index = xmalloc(total_pages * sizeof *(page_info.index));
364 for (i = 0; i < total_pages; i++) {
365 page_info.index[i].marked = False;
366 }
367 /* following initializations are handled by the respective Motif/Xaw functions */
368 page_info.page_labels = NULL;
369 page_info.index_size = total_pages;
370 }
371
372 /*
373 Deallocate page_info. NOTE: We mustn't free the page_labels here,
374 since the page list might survive quite some time (e.g. while fonts
375 for the new file are being generated) and needs the labels.
376 */
377 void
pageinfo_deallocate(void)378 pageinfo_deallocate(void)
379 {
380 free(page_info.index);
381 page_info.index_size = 0;
382 page_info.index = NULL;
383 }
384
385 #ifdef MOTIF
386 void
toggle_pagelist(void)387 toggle_pagelist(void)
388 {
389 Dimension curr_w, curr_x;
390 XtVaGetValues(globals.widgets.main_window,
391 XmNwidth, &curr_w,
392 XmNx, &curr_x,
393 NULL);
394
395 if ((resource.expert_mode & XPRT_SHOW_PAGELIST) != 0) {
396 XtManageChild(XtParent(page_list));
397 XtManageChild(page_list);
398
399 curr_x += resource.pagelist_width;
400 curr_w -= resource.pagelist_width;
401 XtVaSetValues(globals.widgets.main_window,
402 XtNwidth, curr_w,
403 XtNx, curr_x,
404 XmNleftAttachment, XmATTACH_WIDGET,
405 XmNleftWidget, XtParent(page_list),
406 NULL);
407 }
408 else {
409 XtUnmanageChild(XtParent(page_list));
410 XtUnmanageChild(page_list);
411
412 curr_x -= resource.pagelist_width;
413 curr_w += resource.pagelist_width;
414 XtVaSetValues(globals.widgets.main_window,
415 XmNwidth, curr_w,
416 XmNx, curr_x,
417 XmNleftAttachment, XmATTACH_FORM,
418 NULL);
419 }
420
421 set_menu(&resource.expert_mode, Act_set_expert_mode, check_resource_expert);
422 }
423 #endif
424
425
426 Boolean
pageinfo_have_marked_pages(void)427 pageinfo_have_marked_pages(void)
428 {
429 int i;
430 for (i = 0; i < total_pages; i++) {
431 if (page_info.index[i].marked) {
432 return True;
433 }
434 }
435 return False;
436 }
437
438
439 /* return True if page i is marked, False else */
440 Boolean
pageinfo_is_marked(int i)441 pageinfo_is_marked(int i)
442 {
443 ASSERT(i <= (int)page_info.index_size, "");
444 return page_info.index[i].marked;
445 }
446
447
448 typedef enum { SCROLL_UP, SCROLL_DOWN, CLICK } saveCmdT;
449
450
451 static void internal_process_button2(Widget widget, XEvent *event);
452 static int get_item_index(Widget w, int mouse_y);
453
454 static int
get_page_size(void)455 get_page_size(void)
456 {
457 int offset = 0;
458 int min_page = 0;
459 int max_page = 0;
460 int min_pageno_len = 0;
461 int max_pageno_len = 0;
462 int i;
463
464 if (globals.dvi_file.bak_fp == NULL)
465 return 0;
466
467 for (i = 0; i < total_pages; i++) {
468 max_page = MAX(page_info.index[i].number, max_page);
469 min_page = MIN(page_info.index[i].number, min_page);
470 }
471
472 if (min_page >= 0) {
473 offset = 0; /* plus symbol is hidden */
474 } else {
475 offset = 1; /* offset for minus symbol */
476 min_page = -min_page;
477 }
478 for (min_pageno_len = offset; min_page > 0;
479 min_page /= 10, min_pageno_len++);
480
481 if (max_page >= 0) {
482 offset = 0; /* plus symbol is hidden */
483 } else {
484 offset = 1; /* offset for minus symbol */
485 max_page = -max_page;
486 }
487 for (max_pageno_len = offset; max_page > 0;
488 max_page /= 10, max_pageno_len++);
489
490 return MAX(min_pageno_len, max_pageno_len);
491 /* Plus 1 for minus symbol */
492 }
493
494 #if !defined(LESSTIF_VERSION)
495 /* draw or erase highlight marker at position x, y with widht w an height h,
496 using suitable offsets (for sake of consistency of the latter, and because
497 of the differences Xaw/Motif, this is a separate function).
498 */
499 static void
refresh_highlight_marker(Widget widget,GC gc,Position x,Position y,Dimension w,Dimension h)500 refresh_highlight_marker(Widget widget, GC gc,
501 Position x, Position y, Dimension w, Dimension h)
502 {
503 XDrawRectangle(XtDisplay(widget), XtWindow(widget), gc,
504 #ifdef MOTIF
505 x + 1, y + 1, w - 3, h - 3
506 #else
507 x + 2, y, w - 1, h - 1
508 #endif
509 );
510 }
511 #endif /* !defined(LESSTIF_VERSION) */
512
513 #ifndef MOTIF
514
515 /* update (redisplay) the list, saving the currently highlighted item.
516 * This is invoked whenever marking the page, and the re-construction of the
517 * entire list causes considerable flicker; but I guess that's unavoidable
518 * with the current simplistic labelling scheme (with changing the list items
519 * themselves). Gv does this considerably better (using custom widgets).
520 */
521 static void
xaw_update_list(void)522 xaw_update_list(void)
523 {
524 static int pagelist_width = -1;
525 static int total_pages_bak = -1;
526
527 XawListReturnStruct *ret;
528 int idx, button_width;
529
530 if (pagelist_width == -1 || total_pages != total_pages_bak) {
531 pagelist_width = xaw_get_pagelist_size();
532 total_pages_bak = total_pages;
533 }
534
535 /* save selected item */
536 ret = XawListShowCurrent(LIST_WIDGET);
537 idx = ret->list_index;
538 button_width = get_panel_width() - 2 * (resource.btn_side_spacing + resource.btn_border_width);
539 /* delete and re-create list */
540 ASSERT(total_pages <= (int)page_info.index_size, "");
541 XawListChange(LIST_WIDGET, page_info.page_labels, 0,
542 MAX(button_width, pagelist_width), False);
543 /* restore selected item */
544 if (idx != XAW_LIST_NONE) {
545 XawListHighlight(LIST_WIDGET, idx);
546 }
547 }
548
549 /*
550 return height of a row in the list widget, and the initial offset of XtNinternalHeight
551 in parameters row_height and internal_h
552 */
553 static void
xaw_get_row_height(Widget w,Dimension * row_height,Dimension * internal_h)554 xaw_get_row_height(Widget w, Dimension *row_height, Dimension *internal_h)
555 {
556 Dimension row_space;
557 XFontStruct *font;
558 Arg arglist[5];
559 int i = 0;
560
561 if (w == NULL || !XtIsRealized(w))
562 return;
563
564 XtSetArg(arglist[i], XtNfont, &font); ++i;
565 XtSetArg(arglist[i], XtNrowSpacing, &row_space); ++i;
566 XtSetArg(arglist[i], XtNinternalHeight, internal_h); i++;
567 XtGetValues(w, arglist, i);
568 *row_height = font->max_bounds.ascent + font->max_bounds.descent + row_space;
569 }
570
571 /*
572 * Get pagelist width.
573 */
574 int
xaw_get_pagelist_size(void)575 xaw_get_pagelist_size(void)
576 {
577 Widget w;
578 XFontStruct *font;
579
580 w = XtVaCreateWidget("list", listWidgetClass, globals.widgets.top_level, NULL);
581 XtVaGetValues(w, XtNfont, &font, NULL);
582 XtDestroyWidget(w);
583
584 /* have space for max. pageno + space + current-marker,
585 plus a few pixels for right margin */
586 return (get_page_size() + 2) * get_avg_font_width(font) + 6;
587 }
588
589 /* auto-scroll pagelist when mouse-1 is down and moved above top or below bottom of
590 page list.
591 */
592 static void
xaw_drag_page_callback(Widget widget,XtPointer data,XEvent * event,Boolean * cont)593 xaw_drag_page_callback(Widget widget, XtPointer data, XEvent *event, Boolean *cont)
594 {
595 int y, idx, actual_idx = 0;
596
597 UNUSED(data);
598 UNUSED(cont);
599
600 if (event->xany.type == ButtonPress || ((event->xbutton.state & Button1Mask) == 0))
601 return;
602 if (event->xany.type == MotionNotify)
603 y = (int)event->xmotion.y;
604 else
605 y = (int)event->xbutton.y;
606
607 idx = get_item_index(widget, y);
608
609 if (idx <= 0) {
610 idx = 1;
611 }
612 else if (idx > total_pages) {
613 idx = total_pages;
614 }
615
616 actual_idx = xaw_maybe_scroll_pagelist(idx, False, actual_idx);
617 XawListHighlight(LIST_WIDGET, idx - MOTIF_IDX_OFFSET);
618
619 /* if (event->xany.type == ButtonRelease) { */
620 /* fprintf(stderr, "1\n"); */
621 /* page_history_insert(idx - MOTIF_IDX_OFFSET); */
622 /* goto_page(idx - MOTIF_IDX_OFFSET, resource.keep_flag ? NULL : home); */
623 /* search_signal_page_changed(); */
624 /* } */
625 }
626
627 static void
xaw_SendReportProc(Widget w,XtPointer closure,XtPointer call_data)628 xaw_SendReportProc(Widget w, XtPointer closure, XtPointer call_data)
629 {
630 XawPannerReport *rep = (XawPannerReport *) call_data;
631
632 UNUSED(w);
633 UNUSED(closure);
634
635 view_y = rep->slider_y;
636 }
637
638 #endif /* not MOTIF */
639
640
641
642 /* returns the index of the current item in `Motif'-style, i.e. first item has index 1, not 0 */
643 static int
get_item_index(Widget w,int mouse_y)644 get_item_index(Widget w, int mouse_y)
645 {
646 #ifdef MOTIF
647 return XmListYToPos(w, mouse_y);
648 #else
649 Dimension row_height, internal_height;
650
651 xaw_get_row_height(w, &row_height, &internal_height);
652 return (mouse_y - internal_height) / row_height + MOTIF_IDX_OFFSET;
653 #endif
654 }
655
656 #if !defined(LESSTIF_VERSION)
657 /* idx is Motif-style index, i.e. 1 for 1st item, not 0 */
658 static Boolean
my_list_pos_to_bounds(Widget widget,int idx,Position * x,Position * y,Dimension * w,Dimension * h)659 my_list_pos_to_bounds(Widget widget, int idx, Position *x, Position *y, Dimension *w, Dimension *h)
660 {
661 #ifdef MOTIF
662 Position x1, y1;
663 Dimension w1, h1;
664 if (XmListPosToBounds(widget, idx, &x1, &y1, &w1, &h1)) {
665 *x = x1;
666 *y = y1;
667 *w = w1;
668 *h = h1;
669 return True;
670 }
671 return False;
672 #else
673 Dimension row_height, internal_height;
674 /* FIXME: Remove this hard-coded offset! */
675 const int X_OFFSET = 9;
676 const int RULE_OFFSET = 2;
677
678 if (idx <= 0 || idx > total_pages) {
679 return False;
680 }
681
682 if (viewport != NULL && XtIsRealized(viewport))
683 XtVaGetValues(viewport, XtNx, x, NULL);
684 xaw_get_row_height(widget, &row_height, &internal_height);
685 XtVaGetValues(widget, XtNwidth, w, NULL);
686
687 *x -= X_OFFSET;
688 *y = row_height * idx + internal_height - row_height - 1;
689 *w -= RULE_OFFSET;
690 *h = row_height + RULE_OFFSET;
691
692 return True;
693 #endif
694 }
695
696
697 /* draw a hightlight rectangle around the page the mouse is currently over, to
698 make e.g. marking easier.
699 */
700 static void
highlight_page_callback(Widget widget,XtPointer data,XEvent * event,Boolean * cont)701 highlight_page_callback(Widget widget, XtPointer data, XEvent *event, Boolean *cont)
702 {
703 int curr_idx = get_item_index(widget, event->xmotion.y);
704 Position x, y;
705 Dimension w, h;
706 static int idx_bak = -1;
707
708 UNUSED(data);
709 UNUSED(cont);
710
711 switch(event->xany.type) {
712 case ButtonPress:
713 case ButtonRelease:
714 case MotionNotify:
715 /* might need to un-highlight previous one */
716 if (idx_bak >= 0 && idx_bak != curr_idx
717 && my_list_pos_to_bounds(widget, idx_bak, &x, &y, &w, &h)) {
718 /* fprintf(stderr, "index: %d, %d, h: %d, w: %d\n", x, y, h, w); */
719 refresh_highlight_marker(widget, m_page_gc.back, x, y, w, h);
720 #if REDRAW_CURRENT_MARKER_HACK
721 xaw_maybe_redraw_highlight(curr_idx);
722 #endif
723 }
724 idx_bak = curr_idx;
725 /* redraw unless out of bounds (when pagelist is shorter than view area) */
726 if (my_list_pos_to_bounds(widget, curr_idx, &x, &y, &w, &h)) {
727 refresh_highlight_marker(widget, m_page_gc.fore, x, y, w, h);
728 #if REDRAW_CURRENT_MARKER_HACK
729 g_current_highlighted = curr_idx;
730 #endif
731 }
732 break;
733 case LeaveNotify:
734 /* this might look overly complicated, but is neccessary to cover all
735 cases of 1-pixel movement up/down before leaving the list, or no
736 movement at all before leaving it. */
737 if ((idx_bak >= 0 && idx_bak != curr_idx && my_list_pos_to_bounds(widget, idx_bak, &x, &y, &w, &h))
738 || my_list_pos_to_bounds(widget, curr_idx, &x, &y, &w, &h)) {
739 refresh_highlight_marker(widget, m_page_gc.back, x, y, w, h);
740 #if REDRAW_CURRENT_MARKER_HACK
741 xaw_maybe_redraw_highlight(curr_idx);
742 #endif
743 }
744 break;
745 default:
746 break;
747 }
748 }
749 #endif /* !defined(LESSTIF_VERSION) */
750
751 /*
752 * invoked on Button-1 Down.
753 */
754 static void
select_page_callback(Widget w,XtPointer closure,XtPointer call_data)755 select_page_callback(Widget w, XtPointer closure, XtPointer call_data)
756 {
757 #ifdef MOTIF
758 XmListCallbackStruct *cbs = (XmListCallbackStruct *) call_data;
759 int new = cbs->item_position;
760
761 UNUSED(w);
762 UNUSED(closure);
763
764 maybe_scroll_pagelist(new - MOTIF_IDX_OFFSET, False);
765 page_history_insert(new - MOTIF_IDX_OFFSET);
766 goto_page(new - MOTIF_IDX_OFFSET, resource.keep_flag ? NULL : home, False);
767 #else
768 XawListReturnStruct *item = (XawListReturnStruct *) call_data;
769 int new = item->list_index;
770
771 UNUSED(w);
772 UNUSED(closure);
773
774 if (globals.debug & DBG_EVENT)
775 fprintf(stderr, "got: button-1 for `%d'\n", new);
776
777 #if 0
778 fprintf(stderr, "select page: %d\n", new);
779 #endif
780 maybe_scroll_pagelist(new, False);
781 page_history_insert(new);
782 goto_page(new, resource.keep_flag ? NULL : home, False);
783 statusline_erase("Page history:");
784 #endif
785 search_signal_page_changed();
786 }
787
788
789 static void
init_pagelabels(int start,int end)790 init_pagelabels(int start, int end)
791 {
792 int i;
793 char s[PAGENUMLEN];
794 #if 0
795 fprintf(stderr, "===== init_pagelabels from %d to %d\n", start, end);
796 #endif
797 ASSERT(end < (int)page_info.index_size, "");
798
799 page_info.page_labels = xrealloc(page_info.page_labels, sizeof *(page_info.page_labels) * (end + 2));
800 for (i = start; i < end; i++) {
801 if (page_info.index[i].marked)
802 sprintf(s, "* %*d ", get_page_size(),
803 resource.use_tex_pages ? page_info.index[i].number : i + 1);
804 else
805 sprintf(s, " %*d ", get_page_size(),
806 resource.use_tex_pages ? page_info.index[i].number : i + 1);
807
808 page_info.page_labels[i] = xstrdup(s);
809 }
810 page_info.page_labels[i] = NULL; /* terminate - important for creating the widget. */
811 }
812
813
814 #ifdef MOTIF
815
816
817 static int
xm_get_top_visible(int start)818 xm_get_top_visible(int start)
819 {
820 int top = start;
821 while (top < total_pages && !XmListPosToBounds(LIST_WIDGET, top, NULL, NULL, NULL, NULL))
822 top++;
823 return top;
824 }
825
826 static int
xm_get_bottom_visible(int start)827 xm_get_bottom_visible(int start)
828 {
829 int bot = start;
830 while (bot < total_pages && XmListPosToBounds(LIST_WIDGET, bot, NULL, NULL, NULL, NULL))
831 bot++;
832 bot--;
833 return bot;
834 }
835
836 /* Scroll pagelist down or up if needed, and update top_visible and
837 bot_visible. List is always scrolled so that 1 element is still
838 visible below or above pointer, to make it possible to flip through
839 document by repeatedly clicking on first/last.
840 */
841 static void
xm_maybe_scroll_pagelist(int current,saveCmdT curr_cmd,int * top_visible,int * bot_visible)842 xm_maybe_scroll_pagelist(int current, saveCmdT curr_cmd, int *top_visible, int *bot_visible)
843 {
844 #if 0
845 fprintf(stderr, "topmost visible: %d, bottom: %d, current: %d, total: %d\n",
846 top_visible, bottom_visible, current, total_pages);
847 #endif
848
849 if (current < *top_visible && curr_cmd != SCROLL_DOWN) {
850 XmListSetPos(LIST_WIDGET,
851 current < 1
852 ? 1
853 : current);
854 (*top_visible)--;
855 (*bot_visible)--;
856 }
857 else if (current + MOTIF_IDX_OFFSET >= *bot_visible && curr_cmd != SCROLL_UP) {
858 XmListSetBottomPos(LIST_WIDGET,
859 current + MOTIF_IDX_OFFSET >= total_pages
860 ? current + MOTIF_IDX_OFFSET
861 : current + MOTIF_IDX_OFFSET + 1);
862 (*top_visible)++;
863 (*bot_visible)++;
864 }
865 }
866
867 static void
xm_set_page_labels(void)868 xm_set_page_labels(void)
869 {
870 int i;
871 char buf[PAGENUMLEN];
872
873 XmString *motif_page_labels = xmalloc((total_pages + 2) * sizeof *motif_page_labels);
874
875 for (i = 0; i < total_pages; ++i) {
876 sprintf(buf, pageno_format, ' ', page_info.page_labels[i]);
877 motif_page_labels[i] = XmStringCreateLocalized(buf);
878 }
879 XmListDeleteAllItems(LIST_WIDGET);
880 XmListAddItems(LIST_WIDGET, motif_page_labels, total_pages, 0);
881
882 XmListSelectPos(LIST_WIDGET, current_page + MOTIF_IDX_OFFSET, False);
883 for (i = 0; i < total_pages; ++i) {
884 XmStringFree(motif_page_labels[i]);
885 }
886 free(motif_page_labels);
887 }
888
889 static void
xm_toggle_label(Widget widget,int idx,Boolean update)890 xm_toggle_label(Widget widget, int idx, Boolean update)
891 {
892 /* TODO: use `update' to update all labels at once when toggling multiple */
893 XmString str;
894 char *mark_font, buf[128];
895
896 if (widget == NULL)
897 return;
898
899 UNUSED(update);
900
901 /* ensure_labelinfo_size(idx); */
902 ASSERT(idx < (int)page_info.index_size, "");
903 if (!page_info.index[idx].marked) {
904 sprintf(buf, pageno_format, '*', page_info.page_labels[idx]);
905 mark_font = "MARKED";
906 page_info.index[idx].marked = True;
907 }
908 else {
909 sprintf(buf, pageno_format, ' ', page_info.page_labels[idx]);
910 mark_font = "UNMARKED";
911 page_info.index[idx].marked = False;
912 }
913 /* str = XmStringCreateLocalized(buf); */
914 str = XmStringCreateLtoR(buf, mark_font);
915 XmListReplaceItemsPos(widget, &str, 1, idx + MOTIF_IDX_OFFSET);
916 XmStringFree(str);
917 }
918
919
920 #else /* MOTIF */
921
922 static void
mark_page_callback(Widget w,XtPointer data,XEvent * event,Boolean * cont)923 mark_page_callback(Widget w, XtPointer data, XEvent *event, Boolean *cont)
924 {
925 UNUSED(data);
926 UNUSED(cont);
927
928 /* moving button2 generates MotionNotify events for button0 */
929 if (event->type == MotionNotify || event->xbutton.button == Button2)
930 internal_process_button2(w, event);
931
932 if (event->type != ButtonPress)
933 notify_print_dialog_have_marked();
934 }
935
936 void
xaw_create_pagelist_widgets(Dimension height,Dimension width,Position y,Widget parent)937 xaw_create_pagelist_widgets(Dimension height, Dimension width, Position y, Widget parent)
938 {
939 viewport = XtVaCreateWidget("viewport",
940 viewportWidgetClass, parent,
941 XtNallowVert, True,
942 /* this is not related to the scroll bar: */
943 /* XtNforceBars, True, */
944 XtNx, resource.btn_side_spacing,
945 XtNy, y,
946 XtNheight, height,
947 XtNwidth, width,
948 NULL);
949 LIST_WIDGET = XtVaCreateWidget("list",
950 listWidgetClass, viewport,
951 XtNlist, page_info.page_labels,
952 XtNdefaultColumns, 1,
953 XtNforceColumns, True,
954 XtNx, 10,
955 XtNy, 10,
956 XtNheight, height,
957 XtNwidth, width - 10,
958 XtNlongest, LONGESTPAGENUM,
959 XtNverticalList, True,
960 NULL);
961 XtManageChild(LIST_WIDGET);
962 XtManageChild(viewport);
963 XtAddCallback(LIST_WIDGET, XtNcallback, select_page_callback,
964 (XtPointer) NULL);
965 /* for scrolling the list */
966 XtAddCallback(viewport, XtNreportCallback, xaw_SendReportProc,
967 (XtPointer) NULL);
968 XtAddEventHandler(LIST_WIDGET,
969 ButtonPressMask | ButtonReleaseMask | Button2MotionMask,
970 False, mark_page_callback, (XtPointer)NULL);
971
972 if (resource.pagelist_highlight_current)
973 XtAddEventHandler(LIST_WIDGET,
974 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | LeaveWindowMask,
975 False, highlight_page_callback, (XtPointer)NULL);
976
977
978 {
979 Widget y_bar;
980 XtTranslations xlats = XtParseTranslationTable(
981 "<Btn4Down>,<Btn4Up>: scroll-list-up()\n"
982 "<Btn5Down>,<Btn5Up>: scroll-list-down()\n");
983
984 XtOverrideTranslations(LIST_WIDGET, xlats);
985
986 y_bar = XtNameToWidget(viewport, "vertical");
987 if (y_bar != NULL)
988 XtOverrideTranslations(y_bar, xlats);
989 }
990
991 XtAddEventHandler(LIST_WIDGET,
992 /* FIXME: We should add PointerMotionMask here, but handling PointerMotionMask
993 currently doesn't work with the Xaw list widget: the auto-scrolling code doesn't
994 realize when the mouse direction of the pointer movement changes, and continues
995 to scroll into the same direction. This will be rather annoying for users, so
996 we disabled PointerMotionMask for the time being.
997 */
998 ButtonReleaseMask /* | PointerMotionMask */ | Button1MotionMask,
999 False, xaw_drag_page_callback, (XtPointer)NULL);
1000 }
1001
1002 static void
xaw_toggle_label(Widget w,int idx,Boolean update)1003 xaw_toggle_label(Widget w, int idx, Boolean update)
1004 {
1005 if (w == NULL)
1006 return;
1007
1008 /* ensure_labelinfo_size(idx); */
1009 ASSERT(idx < (int)page_info.index_size, "");
1010 if (!page_info.index[idx].marked) {
1011 /* sprintf(toc[idx], "* %*d ", get_page_size(), page_index[idx].number); */
1012 sprintf(page_info.page_labels[idx], pageno_format, '*', get_page_size(),
1013 resource.use_tex_pages ? page_info.index[idx].number : idx + 1);
1014 page_info.index[idx].marked = True;
1015 }
1016 else {
1017 sprintf(page_info.page_labels[idx], pageno_format, ' ', get_page_size(),
1018 resource.use_tex_pages ? page_info.index[idx].number : idx + 1);
1019 /* sprintf(toc[idx], " %*d ", get_page_size(), page_index[idx].number); */
1020 page_info.index[idx].marked = False;
1021 }
1022
1023 if (update)
1024 xaw_update_list();
1025 }
1026
1027 static int
xaw_maybe_scroll_pagelist(int new_page,Boolean force_recenter,int idx_bak)1028 xaw_maybe_scroll_pagelist(int new_page, Boolean force_recenter, int idx_bak)
1029 {
1030 Position x;
1031 Position y, new_y, bot_y;
1032 Dimension view_height, row_height, internal_height;
1033 /* Dimension clip_height; */
1034 /* static Widget list_clip = 0; */
1035
1036 if (LIST_WIDGET == NULL || (resource.expert_mode & XPRT_SHOW_BUTTONS) == 0)
1037 return idx_bak;
1038
1039 /* if (list_clip == 0) { */
1040 /* list_clip = XtNameToWidget(viewport, "clip"); */
1041 /* } */
1042 /* if (XtIsRealized(list_clip)) */
1043 /* XtVaGetValues(list_clip, XtNheight, &clip_height, XtNx, &cx, XtNy, &y1, NULL); */
1044 if (viewport != NULL && XtIsRealized(viewport))
1045 XtVaGetValues(viewport, XtNheight, &view_height, XtNx, &x, NULL);
1046 /* fprintf(stderr, "diff: %d, %d, %d, %d, %d\n", cx - x, clip_height, view_height, y1, (int)y2); */
1047 xaw_get_row_height(LIST_WIDGET, &row_height, &internal_height);
1048 y = row_height * new_page;
1049
1050 #if DEBUG
1051 fprintf(stderr, "###### xaw_maybe_scroll_pagelist: y %d, view_y %d, view_height %d, row_height %d, internal_height %d; actual %d\n",
1052 y, view_y, view_height, row_height, internal_height, idx_bak);
1053 #endif
1054 /*FIXME: when page list is destroyed, view_y will be 0 until user scrolls page list */
1055 bot_y = view_y + view_height;
1056 if (force_recenter || ((y >= bot_y - row_height))) {
1057 #if DEBUG
1058 fprintf(stderr, "scrolled below bottom; incrementing %d to %d\n", view_y, view_y + row_height);
1059 #endif
1060 y += row_height;
1061 XawViewportSetCoordinates(viewport, x, y - view_height > 0 ? y - view_height : 0);
1062 return new_page + 1;
1063 }
1064 else if (force_recenter || ((y <= view_y + row_height + internal_height))) {
1065 #if DEBUG
1066 fprintf(stderr, "scrolled over top; new_y: %d\n", y - row_height);
1067 #endif
1068 new_y = y - 2 * row_height;
1069 XawViewportSetCoordinates(viewport, x, new_y);
1070 return new_page - 1;
1071 }
1072 /* not scrolled */
1073 return -2;
1074 }
1075
1076 #endif /* MOTIF */
1077
1078
1079 /* idx is C-style index (0-based), not Motif one (1-based) */
1080 static void
toggle_label(Widget widget,int idx,Boolean update)1081 toggle_label(Widget widget, int idx, Boolean update)
1082 {
1083 if (idx >= total_pages)
1084 return;
1085 ASSERT(idx < total_pages, "");
1086 ASSERT(idx >= 0, "");
1087 #ifdef MOTIF
1088 xm_toggle_label(widget, idx, update);
1089 #else
1090 xaw_toggle_label(widget, idx, update);
1091 #endif
1092 }
1093
1094
1095 void
list_toggle_marks(int arg)1096 list_toggle_marks(int arg)
1097 {
1098 int i;
1099
1100 if (arg < 0) { /* mark all */
1101 for (i = 0; i < total_pages; i++) {
1102 ASSERT(i < (int)page_info.index_size, "");
1103 /* ensure_labelinfo_size(i); */
1104 if (!page_info.index[i].marked) {
1105 toggle_label(LIST_WIDGET, i, False);
1106 }
1107 }
1108 }
1109 else if (arg == 0) { /* unmark all */
1110 for (i = 0; i < total_pages; i++) {
1111 ASSERT(i < (int)page_info.index_size, "");
1112 /* ensure_labelinfo_size(i); */
1113 if (page_info.index[i].marked) {
1114 toggle_label(LIST_WIDGET, i, False);
1115 }
1116 }
1117 }
1118 else { /* toggle odd/even */
1119 if (arg == 2) /* toggle even */
1120 arg = 0;
1121 for (i = 0; i < total_pages; i++) {
1122 if ((i + 1) % 2 == arg) {
1123 toggle_label(LIST_WIDGET, i, False);
1124 }
1125 }
1126 }
1127 /* TODO: update widget once for Motif as well! */
1128 #ifndef MOTIF
1129 xaw_update_list();
1130 #endif
1131 notify_print_dialog_have_marked();
1132 }
1133
1134 static Boolean PagelistInitialized = False;
1135
1136 #ifndef MOTIF
1137 void
handle_pagelist_resize(void)1138 handle_pagelist_resize(void)
1139 {
1140 /* TODO: the following will mess up the geometry of the list
1141 (doesn't increase height, and incrementally decreases width):
1142 if (list_widget) {
1143 Dimension height;
1144 --- without the (un)manage, I get an X Error:
1145 XtMakeGeometryRequest - parent has no geometry manager
1146 ---
1147 XtUnmanageChild(viewport);
1148 XtUnmanageChild(LIST_WIDGET);
1149 XtVaGetValues(globals.widgets.clip_widget, XtNheight, &height, NULL);
1150 height -= resource.btn_top_spacing + resource.btn_border_width + global_y_pos;
1151 XtVaSetValues(viewport, XtNheight, height, NULL);
1152 XtManageChild(LIST_WIDGET);
1153 XtManageChild(viewport);
1154 }
1155 ... so we use brute force instead: */
1156 handle_destroy_pagelist(LIST_WIDGET, NULL, NULL);
1157 create_pagelist();
1158 }
1159
1160 void
handle_destroy_pagelist(Widget w,XtPointer client_data,XtPointer call_data)1161 handle_destroy_pagelist(Widget w, XtPointer client_data, XtPointer call_data)
1162 {
1163 UNUSED(w);
1164 UNUSED(client_data);
1165 UNUSED(call_data);
1166
1167 if (viewport != NULL) {
1168 XtDestroyWidget(viewport);
1169 viewport = NULL;
1170 LIST_WIDGET = NULL;
1171 }
1172 PagelistInitialized = False;
1173 }
1174 #endif /* MOTIF */
1175
1176 void
create_pagelist(void)1177 create_pagelist(void)
1178 {
1179 Pixel background, foreground;
1180 #ifdef MOTIF
1181
1182 /* items = xrealloc(items, sizeof *items * (total_pages + 2)); */
1183 init_pagelabels(0, total_pages);
1184 xm_set_page_labels();
1185 if (!PagelistInitialized) {
1186 XtAppContext app;
1187
1188 XtVaGetValues(LIST_WIDGET, XmNforeground, &foreground, XmNbackground, &background, NULL);
1189 m_page_gc.back = set_or_make_gc(NULL, GXcopy, background, foreground);
1190 m_page_gc.fore = set_or_make_gc(NULL, GXcopy, foreground, background);
1191 XtManageChild(LIST_WIDGET);
1192
1193 XtAddCallback(LIST_WIDGET, XmNbrowseSelectionCallback, select_page_callback, NULL);
1194 #if !defined(LESSTIF_VERSION)
1195 /*
1196 Don't use the highlighting hack with LessTif, since its XmListPosToBounds()
1197 is too broken to be usable (as of 0.93.36):
1198 - it returns generally too low values, apparently it doesn't take
1199 XmNlistSpacing into account;
1200 - it doesn't take scrollbar position into account.
1201 */
1202 if (resource.pagelist_highlight_current)
1203 XtAddEventHandler(LIST_WIDGET,
1204 ButtonPressMask | ButtonReleaseMask | PointerMotionMask | LeaveWindowMask,
1205 False, highlight_page_callback, (XtPointer)NULL);
1206 #endif /* !defined(LESSTIF_VERSION) */
1207 app = XtWidgetToApplicationContext(globals.widgets.top_level);
1208 XtAppAddActions(app, CustomListActions, XtNumber(CustomListActions));
1209 XtOverrideTranslations(LIST_WIDGET, XtParseTranslationTable(motif_custom_translations));
1210 PagelistInitialized = True;
1211 }
1212 #else /* MOTIF */
1213 if ((resource.expert_mode & XPRT_SHOW_BUTTONS) == 0) {
1214 PagelistInitialized = False; /* might need to re-create widgets in this case */
1215 return;
1216 }
1217
1218 if (globals.debug & DBG_GUI)
1219 fprintf(stderr, "allocating list with %d pages\n", total_pages);
1220
1221 init_pagelabels(0, total_pages);
1222 if (!PagelistInitialized) {
1223 xaw_create_pagelist();
1224 XtVaGetValues(LIST_WIDGET, XtNforeground, &foreground, XtNbackground, &background, NULL);
1225 m_page_gc.back = set_or_make_gc(NULL, GXcopy, background, foreground);
1226 m_page_gc.fore = set_or_make_gc(NULL, GXcopy, foreground, background);
1227 PagelistInitialized = True;
1228 }
1229 #endif /* MOTIF */
1230 /* scroll to the current page if needed */
1231 maybe_scroll_pagelist(current_page, False);
1232 }
1233
1234 #ifndef MOTIF
1235 static void
free_pagelabels(void)1236 free_pagelabels(void)
1237 {
1238 int i;
1239 for (i = 0; page_info.page_labels != NULL && page_info.page_labels[i] != NULL; i++) {
1240 free(page_info.page_labels[i]);
1241 }
1242 free(page_info.page_labels);
1243 page_info.page_labels = NULL;
1244 }
1245 #endif /* not MOTIF */
1246
1247
1248 void
refresh_pagelist(int newsize,int newpage)1249 refresh_pagelist(int newsize, int newpage)
1250 {
1251 if (
1252 #ifndef MOTIF
1253 (resource.expert_mode & XPRT_SHOW_BUTTONS) == 0 ||
1254 #endif
1255 !XtIsRealized(globals.widgets.top_level))
1256 return;
1257
1258 #ifdef DEBUG
1259 fprintf(stderr, "=== refresh_pagelist: newsize %d, newpage %d\n", newsize, newpage);
1260 #endif
1261 #ifdef MOTIF
1262 /* items = xrealloc(items, sizeof *items * (newsize + 2)); */
1263 init_pagelabels(0, newsize);
1264 xm_set_page_labels();
1265 #else /* MOTIF */
1266 if ((resource.expert_mode & XPRT_SHOW_BUTTONS) == 0)
1267 return;
1268
1269 /* FIXME - is this really neccessary?? The alternative:
1270 XawListChange(LIST_WIDGET, page_info.page_labels, newsize, 0, True);
1271 has problems when freeing the page labels afterwards.
1272 */
1273 handle_destroy_pagelist(LIST_WIDGET, NULL, NULL);
1274
1275 free_pagelabels();
1276 init_pagelabels(0, newsize);
1277
1278 xaw_create_pagelist();
1279 #endif /* MOTIF */
1280 /* `True' since the pagelist is newly created */
1281 maybe_scroll_pagelist(newpage, True);
1282 }
1283
1284 void
maybe_scroll_pagelist(int newpage,Boolean force_recenter)1285 maybe_scroll_pagelist(int newpage, Boolean force_recenter)
1286 {
1287 #ifdef MOTIF
1288 int top_visible, bot_visible;
1289 UNUSED(force_recenter);
1290 #endif
1291
1292 if (
1293 #ifndef MOTIF
1294 (resource.expert_mode & XPRT_SHOW_BUTTONS) == 0 ||
1295 #endif
1296 !XtIsRealized(globals.widgets.top_level))
1297 return;
1298
1299 #ifdef MOTIF
1300 XmListSelectPos(LIST_WIDGET, newpage + MOTIF_IDX_OFFSET, False);
1301
1302 top_visible = xm_get_top_visible(1);
1303 bot_visible = xm_get_bottom_visible(top_visible);
1304
1305 xm_maybe_scroll_pagelist(newpage, CLICK, &top_visible, &bot_visible);
1306 #if HAVE_XPM
1307 tb_check_navigation_sensitivity(current_page);
1308 #endif
1309 #else
1310
1311 if (LIST_WIDGET == NULL)
1312 return;
1313
1314 (void)xaw_maybe_scroll_pagelist(newpage + 1, force_recenter, 0);
1315
1316 XawListHighlight(LIST_WIDGET, newpage);
1317 #if REDRAW_CURRENT_MARKER_HACK
1318 /* if the XawListHighlight happens adjacent to the page that was
1319 last highlighted with our home-made `current selected'
1320 rectangle, it might overdraw that rectangle. In this case,
1321 restore it:
1322 */
1323 xaw_maybe_redraw_current_marker(newpage);
1324 #endif
1325 #endif
1326 }
1327
1328 #ifdef MOTIF
1329 static void
set_all_marks(int from,int to)1330 set_all_marks(int from, int to)
1331 {
1332 int i;
1333 for (i = from; i < to; i++) {
1334 /* ensure_labelinfo_size(i); */
1335 page_info.index[i].marked = True;
1336 toggle_label(LIST_WIDGET, i, False);
1337 }
1338 }
1339
1340 static void
internal_process_button2_drag(Widget widget,XEvent * event)1341 internal_process_button2_drag(Widget widget, XEvent *event)
1342 {
1343 int i, idx, min = total_pages, max = 0;
1344 idx = get_item_index(widget, event->xbutton.y);
1345
1346 for (i = 0; i < total_pages; i++) {
1347 /* ensure_labelinfo_size(i); */
1348 if (page_info.index[i].marked && i > max)
1349 max = i;
1350 }
1351 for (i = total_pages; i > 0; i--) {
1352 /* ensure_labelinfo_size(i); */
1353 if (page_info.index[i].marked && i < min)
1354 min = i;
1355 }
1356
1357 if (min == total_pages)
1358 min = 0;
1359 if (max == 0)
1360 max = total_pages;
1361
1362 if (idx < min) {
1363 set_all_marks(idx, min);
1364 }
1365 else if (idx > max) {
1366 set_all_marks(max + 1, idx);
1367 }
1368 else {
1369 set_all_marks(0, idx);
1370 }
1371 }
1372 #endif /* MOTIF */
1373
1374 static void
internal_process_button2(Widget widget,XEvent * event)1375 internal_process_button2(Widget widget, XEvent *event)
1376 {
1377 int curr_idx;
1378 static int prev_idx = 0;
1379 #ifndef MOTIF
1380 static int actual_idx = -2;
1381 #endif
1382 static int top_visible = 0, bot_visible = 0;
1383 static int prev_y = 0, curr_y = 0;
1384 static saveCmdT prev_cmd = 0; /* previous command (CLICK/SCROLL_UP/SCROLL_DOWN) */
1385 static saveCmdT curr_cmd = 0; /* current command (CLICK/SCROLL_UP/SCROLL_DOWN) */
1386 static saveCmdT bak_cmd = 0; /* last command that started inside the pagelist (CLICK/SCROLL_UP/SCROLL_DOWN) */
1387 static Boolean change_scroll_direction = False;
1388
1389 switch(event->xany.type) {
1390 case ButtonPress:
1391 prev_y = event->xbutton.y;
1392 prev_idx = curr_idx = get_item_index(widget, prev_y);
1393 #ifdef MOTIF
1394 top_visible = xm_get_top_visible(1);
1395 bot_visible = xm_get_bottom_visible(top_visible);
1396 #endif
1397 toggle_label(widget, curr_idx - 1, True);
1398 prev_cmd = CLICK;
1399 #ifdef MOTIF
1400 xm_maybe_scroll_pagelist(curr_idx - 1, CLICK, &top_visible, &bot_visible);
1401 #else
1402 actual_idx = xaw_maybe_scroll_pagelist(curr_idx, False, actual_idx);
1403 #endif
1404 break;
1405 case ButtonRelease:
1406 prev_cmd = CLICK;
1407 break;
1408 case MotionNotify:
1409 curr_y = (int)event->xmotion.y;
1410 curr_idx = get_item_index(widget, event->xmotion.y);
1411 #ifndef MOTIF
1412 if (actual_idx > 0) {
1413 curr_idx = actual_idx;
1414 }
1415 #endif
1416
1417 if (curr_y < prev_y)
1418 curr_cmd = SCROLL_UP;
1419 else if (curr_y > prev_y)
1420 curr_cmd = SCROLL_DOWN;
1421 prev_y = curr_y;
1422
1423 if (prev_cmd != CLICK && curr_cmd != prev_cmd)
1424 change_scroll_direction = True;
1425
1426 if ((curr_idx != prev_idx && !change_scroll_direction)
1427 || (change_scroll_direction
1428 /* last or first visible are always spared, unless they are really
1429 the first or last page; this way, always 1 more page is visible
1430 than is currently marked/selected
1431 */
1432 && !(curr_idx == top_visible) && !(curr_idx == bot_visible))) {
1433 if (curr_idx <= 0) {
1434 /* When user has scrolled off, mark this by setting curr_idx to 1 more or less
1435 than the pagelist has so that the last/first page don't oscillate between
1436 marked/unmarked state when user continues to scroll.
1437 Also, we continue scrolling as long as user drags in the same direction as
1438 the last `real' scrolling event (saved as bak_cmd).
1439 */
1440 if (curr_cmd == SCROLL_DOWN && bak_cmd == SCROLL_DOWN && prev_idx <= total_pages) {
1441 curr_idx = prev_idx + 1;
1442 }
1443 else if (curr_cmd == SCROLL_UP && bak_cmd == SCROLL_UP && prev_idx > 0) {
1444 curr_idx = prev_idx - 1;
1445 }
1446 }
1447
1448
1449 if (curr_idx > 0 && curr_idx <= total_pages) {
1450 toggle_label(widget, curr_idx - 1, True);
1451 #ifdef MOTIF
1452 xm_maybe_scroll_pagelist(curr_idx - 1, curr_cmd, &top_visible, &bot_visible);
1453 #else
1454 actual_idx = xaw_maybe_scroll_pagelist(curr_idx, False, actual_idx);
1455 #endif
1456 prev_idx = curr_idx;
1457 bak_cmd = curr_cmd;
1458 change_scroll_direction = False;
1459 }
1460 else {
1461 #ifndef MOTIF
1462 if (curr_idx > total_pages)
1463 actual_idx = -2;
1464 else
1465 actual_idx = xaw_maybe_scroll_pagelist(curr_idx, False, actual_idx);
1466 #endif
1467 }
1468 prev_cmd = curr_cmd;
1469 }
1470 break;
1471 default:
1472 break;
1473 }
1474 }
1475
1476 void
list_toggle_current(int arg)1477 list_toggle_current(int arg)
1478 {
1479 toggle_label(LIST_WIDGET, arg, True);
1480
1481 notify_print_dialog_have_marked();
1482 }
1483
1484 #ifdef MOTIF
1485
1486 static void
xm_list_set_mark(Widget widget,XEvent * event,String * params,Cardinal * num_params)1487 xm_list_set_mark(Widget widget,
1488 XEvent *event,
1489 String *params,
1490 Cardinal *num_params)
1491 {
1492 UNUSED(params);
1493
1494 if ((*num_params != 1) || !XmIsList(widget))
1495 return;
1496
1497 internal_process_button2(widget, event);
1498
1499 if (event->type != ButtonPress)
1500 notify_print_dialog_have_marked();
1501 }
1502
1503 static void
xm_list_drag_mark(Widget widget,XEvent * event,String * params,Cardinal * num_params)1504 xm_list_drag_mark(Widget widget,
1505 XEvent *event,
1506 String *params,
1507 Cardinal *num_params)
1508 {
1509 UNUSED(params);
1510
1511 if ((*num_params != 1) || !XmIsList(widget))
1512 return;
1513
1514 internal_process_button2_drag(widget, event);
1515
1516 if (event->type != ButtonPress)
1517 notify_print_dialog_have_marked();
1518 }
1519
1520 #endif /* MOTIF */
1521