summaryrefslogtreecommitdiff
path: root/Build/source/texk/xdvik/events.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/xdvik/events.c')
-rw-r--r--Build/source/texk/xdvik/events.c5645
1 files changed, 5645 insertions, 0 deletions
diff --git a/Build/source/texk/xdvik/events.c b/Build/source/texk/xdvik/events.c
new file mode 100644
index 00000000000..417e09d4b18
--- /dev/null
+++ b/Build/source/texk/xdvik/events.c
@@ -0,0 +1,5645 @@
+/*======================================================================*\
+
+Copyright (c) 1990-2004 Paul Vojta and others
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to
+deal in the Software without restriction, including without limitation the
+rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+sell copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+PAUL VOJTA OR ANY OTHER AUTHOR OF THIS SOFTWARE BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+NOTE:
+ xdvi is based on prior work, as noted in the modification history
+ in xdvi.c.
+
+\*========================================================================*/
+
+#include "xdvi-config.h"
+
+#ifdef STDC_HEADERS
+# include <unistd.h>
+# include <fcntl.h>
+#endif
+#include <signal.h>
+#include <sys/file.h> /* this defines FASYNC */
+#ifdef HAVE_SYS_FCNTL_H
+# include <sys/fcntl.h> /* Or this might define FASYNC */
+#endif
+#include <sys/ioctl.h> /* this defines SIOCSPGRP and FIOASYNC */
+#include <sys/wait.h> /* ignore HAVE_SYS_WAIT_H -- we always need WNOHANG */
+
+#include "xdvi.h" /* this includes Xlib and Xutil are already included */
+#include "xdvi-debug.h"
+
+
+#ifndef MOTIF
+#include <X11/Xaw/Form.h> /* for XtNresizable */
+#endif
+
+#include <setjmp.h>
+#include <sys/time.h> /* for gettimeofday */
+
+#include <X11/IntrinsicP.h>
+
+#include <X11/Xatom.h>
+#include <X11/StringDefs.h>
+#include <X11/Shell.h> /* needed for def. of XtNiconX(??) */
+
+#include "pagesel.h"
+#include "filehist.h"
+#include "special.h"
+#include "psgs.h"
+
+
+#include <errno.h>
+
+#include <ctype.h>
+
+#include "util.h"
+#include "x_util.h"
+#include "string-utils.h"
+#include "print-dialog.h"
+#include "search-dialog.h"
+#include "sfSelFile.h"
+#include "mag.h"
+#include "help-window.h"
+#include "message-window.h"
+#include "dvi-draw.h"
+#include "statusline.h"
+#include "hypertex.h"
+#include "dvi-init.h"
+#include "Tip.h"
+#include "browser.h"
+#include "search-internal.h"
+#include "my-snprintf.h"
+
+#include "events.h"
+#include "selection.h"
+#include "encodings.h"
+#include "pagehist.h"
+#include "xm_colorsel.h"
+#include "xm_toolbar.h"
+#include "xaw_menu.h"
+#include "xm_menu.h"
+#include "xm_prefs.h"
+
+#include "xm_prefs_appearance.h" /* for update_preferences_expert() */
+#include "xm_prefs_fonts.h" /* for update_preferences_color() */
+#include "xm_prefs_page.h" /* for update_preferences_shrink() */
+
+#ifdef X_NOT_STDC_ENV
+extern int errno;
+#endif /* X_NOT_STDC_ENV */
+
+/* Linux prefers O_ASYNC over FASYNC; SGI IRIX does the opposite. */
+#if !defined(FASYNC) && defined(O_ASYNC)
+# define FASYNC O_ASYNC
+#endif
+
+#if !defined(FLAKY_SIGPOLL) && !HAVE_STREAMS && !defined(FASYNC)
+# if !defined(SIOCSPGRP) || !defined(FIOASYNC)
+# define FLAKY_SIGPOLL 1
+# endif
+#endif
+
+#ifndef FLAKY_SIGPOLL
+
+# ifndef SIGPOLL
+# define SIGPOLL SIGIO
+# endif
+
+# ifndef SA_RESTART
+# define SA_RESTART 0
+# endif
+
+# if HAVE_STREAMS
+# include <stropts.h>
+
+# ifndef S_RDNORM
+# define S_RDNORM S_INPUT
+# endif
+
+# ifndef S_RDBAND
+# define S_RDBAND 0
+# endif
+
+# ifndef S_HANGUP
+# define S_HANGUP 0
+# endif
+
+# ifndef S_WRNORM
+# define S_WRNORM S_OUTPUT
+# endif
+# endif /* HAVE_STREAMS */
+
+#endif /* not FLAKY_SIGPOLL */
+
+#if HAVE_SIGACTION && !defined SA_RESETHAND
+# ifdef SA_ONESHOT
+# define SA_RESETHAND SA_ONESHOT
+# else
+# undef HAVE_SIGACTION /* Needed for Mac OS X < 10.2 (9/2002) */
+# endif
+#endif
+
+#if HAVE_POLL
+# include <poll.h>
+#else
+# if HAVE_SYS_SELECT_H
+# include <sys/select.h>
+# else
+# if HAVE_SELECT_H
+# include <select.h>
+# endif
+# endif
+# define XIO_IN 1
+# define XIO_OUT 2
+#endif /* HAVE_POLL */
+
+static sigset_t all_signals;
+
+/*
+ * Interrupt system for receiving events. The program sets a flag
+ * whenever an event comes in, so that at the proper time (i.e., when
+ * reading a new dvi item), we can check incoming events to see if we
+ * still want to go on printing this page. This way, one can stop
+ * displaying a page if it is about to be erased anyway. We try to read
+ * as many events as possible before doing anything and base the next
+ * action on all events read.
+ * Note that the Xlib and Xt routines are not reentrant, so the most we
+ * can do is set a flag in the interrupt routine and check it later.
+ * Also, sometimes the interrupts are not generated (some systems only
+ * guarantee that SIGIO is generated for terminal files, and on the system
+ * I use, the interrupts are not generated if I use "(xdvi foo &)" instead
+ * of "xdvi foo"). Therefore, there is also a mechanism to check the
+ * event queue every 70 drawing operations or so. This mechanism is
+ * disabled if it turns out that the interrupts do work.
+ * For a fuller discussion of some of the above, see xlife in
+ * comp.sources.x.
+ */
+
+/*
+ * Signal flags
+ */
+
+/* This could be static volatile, but we want to avoid any optimizer bugs. */
+VOLATILE unsigned int sig_flags = 0;
+
+#define SF_USR 1
+#define SF_ALRM 2
+#define SF_POLL 4
+#define SF_CHLD 8
+#define SF_TERM 16
+#define SF_SEGV 32
+
+static void do_sigusr(void);
+static void do_sigalrm(void);
+static void do_sigpoll(void);
+static void do_sigchld(void);
+static void do_sigterm(void);
+static void do_sigsegv(void);
+
+/* these must be in the same order as SF_*.
+ The higher flags have higher priority, since these are
+ checked first when resolving flags_to_sigproc[sig_flags].
+
+ Example: flag := SF_TERM | SF_CHLD = 24
+
+ flags_to_sigproc[24] =>
+ do_sigterm, which sets flag to 24 & ~SF_TERM == 8
+ then, in next check:
+ flags_to_sigproc[8] =>
+ do_sigchld, which sets flag 8 & ~SF_CHLD == 0.
+ */
+#define SP0 do_sigusr
+#define SP1 do_sigalrm
+#define SP2 do_sigpoll
+#define SP3 do_sigchld
+#define SP4 do_sigterm
+#define SP5 do_sigsegv
+
+typedef void (*signalproc)(void);
+
+static const signalproc flags_to_sigproc[64] = {
+ NULL,
+ /* 1 */
+ SP0,
+ /* 2 - 3 */
+ SP1, SP1,
+ /* 4 - 7 */
+ SP2, SP2, SP2, SP2,
+ /* 8 - 15 */
+ SP3, SP3, SP3, SP3, SP3, SP3, SP3, SP3,
+ /* 16 - 31 */
+ SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4,
+ SP4, SP4, SP4, SP4, SP4, SP4, SP4, SP4,
+ /* 32 - 63 */
+ SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5,
+ SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5,
+ SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5,
+ SP5, SP5, SP5, SP5, SP5, SP5, SP5, SP5
+};
+
+#undef SP0
+#undef SP2
+#undef SP3
+#undef SP4
+#undef SP5
+
+/* file-static variables for prefix argument mechanism */
+static Boolean m_have_arg = False; /* flag whether we have a possible prefix arg */
+static int m_number = 0; /* prefix arg value, without the sign */
+static int m_sign = 1; /* is prefix arg negative? */
+
+
+/* to remember the scrollbar positions so that we can restore them
+ when `keep position' is active and window is resized to full size
+ (removing scrollbars) and then back (#810501) */
+static int m_x_scroll = 0, m_y_scroll = 0;
+
+static int source_reverse_x, source_reverse_y;
+static int source_show_all;
+
+/* globals for color stuff */
+Pixel plane_masks[4];
+XColor color_data[2];
+struct pagecolor_info page_colors = { 0, NULL };
+
+struct rgb *color_bottom;
+unsigned int color_bot_size; /* number of entries */
+struct colorframe *rcs_top;
+struct rgb fg_initial; /* Initial fg (from command line) */
+struct rgb bg_initial; /* Initial bg */
+struct bgrec *bg_head = NULL; /* head of list */
+struct bgrec *bg_current = NULL; /* current bg value */
+struct fgrec *fg_current; /* current fg value */
+struct fgrec *fg_active = NULL; /* where the GCs are */
+Pixel *color_list; /* list of colors */
+unsigned int color_list_len = 0; /* current len of list*/
+unsigned int color_list_max = 0; /* allocated size */
+Boolean color_warned = False;
+/*
+ FIXME: the following file-static flag is used to control whether
+ a mouse-related Act_* should block further actions that have
+ been registered for the same event.
+
+ It is used for the Act_href_* routines.
+
+ This used to be a
+ longjmp(somewehre_in_the_event_loop);
+ but that was pretty dreadful as well. Surely there must
+ be some other (X(t)-specific?) way to do this?? (Apart from
+ de-registering the action and re-registering it again.)
+*/
+static Boolean m_block_mouse_event = False;
+
+/* for calling it from mag.c */
+void
+block_next_mouse_event(void) {
+ m_block_mouse_event = True;
+}
+
+Boolean
+block_this_mouse_event(void) {
+ if (m_block_mouse_event == True) {
+ m_block_mouse_event = False;
+ return True;
+ }
+ return False;
+}
+
+
+void null_mouse(XEvent *event)
+{
+ UNUSED(event);
+}
+
+mouse_proc mouse_motion = null_mouse;
+mouse_proc mouse_release = null_mouse;
+
+static void Act_digit(Widget, XEvent *, String *, Cardinal *);
+static void Act_find(Widget, XEvent *, String *, Cardinal *);
+static void Act_find_next(Widget, XEvent *, String *, Cardinal *);
+static void Act_minus(Widget, XEvent *, String *, Cardinal *);
+static void Act_quit(Widget, XEvent *, String *, Cardinal *);
+static void Act_quit_confirm(Widget, XEvent *, String *, Cardinal *);
+static void Act_print(Widget, XEvent *, String *, Cardinal *);
+static void Act_save(Widget, XEvent *, String *, Cardinal *);
+static void Act_help(Widget, XEvent *, String *, Cardinal *);
+static void Act_goto_page(Widget, XEvent *, String *, Cardinal *);
+static void Act_declare_page_number(Widget, XEvent *, String *, Cardinal *);
+static void Act_toggle_mark(Widget, XEvent *, String *, Cardinal *);
+static void Act_home(Widget, XEvent *, String *, Cardinal *);
+static void Act_home_or_top(Widget, XEvent *, String *, Cardinal *);
+static void Act_end_or_bottom(Widget, XEvent *, String *, Cardinal *);
+static void Act_center(Widget, XEvent *, String *, Cardinal *);
+static void Act_left(Widget, XEvent *, String *, Cardinal *);
+static void Act_right(Widget, XEvent *, String *, Cardinal *);
+static void Act_up(Widget, XEvent *, String *, Cardinal *);
+static void Act_down(Widget, XEvent *, String *, Cardinal *);
+static void Act_up_or_previous(Widget, XEvent *, String *, Cardinal *);
+static void Act_down_or_next(Widget, XEvent *, String *, Cardinal *);
+static void Act_set_margins(Widget, XEvent *, String *, Cardinal *);
+static void Act_show_display_attributes(Widget, XEvent *, String *, Cardinal *);
+static void Act_set_density(Widget, XEvent *, String *, Cardinal *);
+static void Act_change_density(Widget, XEvent *, String *, Cardinal *);
+static void Act_fullscreen(Widget, XEvent *, String *, Cardinal *);
+#ifdef GREY
+static void Act_set_greyscaling(Widget, XEvent *, String *, Cardinal *);
+#endif
+#if COLOR
+static void Act_set_color(Widget, XEvent *, String *, Cardinal *);
+#endif
+
+static void Act_htex_anchorinfo(Widget, XEvent *, String *, Cardinal *);
+
+static void Act_reread_dvi_file(Widget, XEvent *, String *, Cardinal *);
+static void Act_select_dvi_file(Widget, XEvent *, String *, Cardinal *);
+static void Act_discard_number(Widget, XEvent *, String *, Cardinal *);
+static void Act_drag(Widget, XEvent *, String *, Cardinal *);
+static void Act_wheel(Widget, XEvent *, String *, Cardinal *);
+static void Act_motion(Widget, XEvent *, String *, Cardinal *);
+static void Act_release(Widget, XEvent *, String *, Cardinal *);
+static void Act_source_special(Widget, XEvent *, String *, Cardinal *);
+static void Act_show_source_specials(Widget, XEvent *, String *, Cardinal *);
+static void Act_source_what_special(Widget, XEvent *, String *, Cardinal *);
+static void Act_unpause_or_next(Widget, XEvent *, String *, Cardinal *);
+static void Act_ruler_snap_origin(Widget w, XEvent *event, String *params, Cardinal *num_params);
+static void Act_text_mode(Widget w, XEvent *event, String *params, Cardinal *num_params);
+static void Act_load_url(Widget w, XEvent *event, String *params, Cardinal *num_params);
+#ifdef MOTIF
+static void Act_prefs_dialog(Widget w, XEvent *event, String *params, Cardinal *num_params);
+#endif
+
+static XtActionsRec m_actions[] = {
+ {"digit", Act_digit},
+ {"switch-mode", Act_switch_mode},
+ {"ruler-mode", Act_ruler_mode},
+ {"text-mode", Act_text_mode},
+ {"minus", Act_minus},
+ {"recent-files", Act_recent_files},
+ {"quit", Act_quit},
+ {"quit-confirm", Act_quit_confirm},
+ {"print", Act_print},
+ {"save", Act_save},
+ {"find", Act_find},
+ {"find-next", Act_find_next},
+ {"help", Act_help},
+ {"goto-page", Act_goto_page},
+ {"use-tex-pages", Act_use_tex_pages},
+ {"forward-page", Act_forward_page},
+ {"back-page", Act_back_page},
+ {"toggle-mark", Act_toggle_mark},
+ {"declare-page-number", Act_declare_page_number},
+ {"home", Act_home},
+ {"home-or-top", Act_home_or_top},
+ {"end-or-bottom", Act_end_or_bottom},
+ {"center", Act_center},
+ {"set-keep-flag", Act_set_keep_flag},
+ {"ruler-snap-origin", Act_ruler_snap_origin},
+ {"left", Act_left},
+ {"right", Act_right},
+ {"up", Act_up},
+ {"down", Act_down},
+ {"up-or-previous", Act_up_or_previous},
+ {"down-or-next", Act_down_or_next},
+ {"set-margins", Act_set_margins},
+ {"show-display-attributes", Act_show_display_attributes},
+ {"set-shrink-factor", Act_set_shrink_factor},
+ {"shrink-to-dpi", Act_shrink_to_dpi},
+ {"set-density", Act_set_density},
+ {"change-density", Act_change_density},
+ {"fullscreen", Act_fullscreen},
+#ifdef GREY
+ {"set-greyscaling", Act_set_greyscaling},
+#endif
+#if COLOR
+ {"set-color", Act_set_color},
+#endif
+#ifdef PS
+ {"set-ps", Act_set_ps},
+#endif
+ {"htex-back", Act_htex_back},
+ {"htex-forward", Act_htex_forward},
+ {"htex-anchorinfo", Act_htex_anchorinfo},
+#ifdef PS_GS
+ {"set-gs-alpha", Act_set_gs_alpha},
+#endif
+ {"set-expert-mode", Act_set_expert_mode},
+ {"reread-dvi-file", Act_reread_dvi_file},
+ {"select-dvi-file", Act_select_dvi_file},
+ {"discard-number", Act_discard_number},
+ {"drag", Act_drag},
+ {"wheel", Act_wheel},
+ {"motion", Act_motion},
+ {"release", Act_release},
+ {"source-special", Act_source_special},
+ {"show-source-specials", Act_show_source_specials},
+ {"source-what-special", Act_source_what_special},
+ {"unpause-or-next", Act_unpause_or_next},
+#if defined(NEW_MENU_CREATION) || defined(MOTIF)
+ {"set-papersize", Act_set_papersize},
+ {"set-paper-landscape", Act_set_paper_landscape},
+#endif /* NEW_MENU_CREATION */
+ {"load-url", Act_load_url},
+ {"pagehistory-clear", Act_pagehistory_clear},
+ {"pagehistory-back", Act_pagehistory_back},
+ {"pagehistory-forward", Act_pagehistory_forward},
+ {"pagehistory-delete-backward", Act_pagehistory_delete_backward},
+ {"pagehistory-delete-forward", Act_pagehistory_delete_forward},
+#ifdef MOTIF
+ {"prefs-dialog", Act_prefs_dialog},
+#endif
+};
+
+/*
+ * Access to m_actions
+ */
+int get_num_actions(void)
+{
+ return XtNumber(m_actions);
+}
+
+XtActionsRec *get_actions(void)
+{
+ return m_actions;
+}
+
+/*
+ * Data for buffered events.
+ */
+
+#ifndef FLAKY_SIGPOLL
+static VOLATILE int event_freq = 70;
+#else
+#define event_freq 70
+#endif
+
+static void can_exposures(struct WindowRec *windowrec);
+
+
+/*
+ * Set the flag so that termination occurs, via the above routine.
+ * This should be used in place of xdvi_exit() when there may be a
+ * non-killable process running (e.g., anytime within read_events()).
+ */
+
+static void
+xdvi_normal_exit(void)
+{
+ sig_flags |= SF_TERM;
+}
+
+static void
+xdvi_normal_exit_cb(XtPointer arg)
+{
+ UNUSED(arg);
+ sig_flags |= SF_TERM;
+}
+
+void
+xdvi_exit_callback(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ UNUSED(w);
+ UNUSED(client_data);
+ UNUSED(call_data);
+
+ sig_flags |= SF_TERM;
+}
+
+/*
+ * Event-handling routines.
+ */
+
+
+void
+expose(struct WindowRec *windowrec,
+ int x, int y,
+ unsigned int w, unsigned int h)
+{
+ if (windowrec->min_x > x)
+ windowrec->min_x = x;
+ if (windowrec->max_x < (int)(x + w))
+ windowrec->max_x = x + w;
+ if (windowrec->min_y > y)
+ windowrec->min_y = y;
+ if (windowrec->max_y < (int)(y + h))
+ windowrec->max_y = y + h;
+
+ globals.ev.flags |= EV_EXPOSE;
+}
+
+void
+clearexpose(struct WindowRec *windowrec,
+ int x, int y,
+ unsigned w, unsigned h)
+{
+ XClearArea(DISP, windowrec->win, x, y, w, h, False);
+ expose(windowrec, x, y, w, h);
+}
+
+/*
+ * Routines for X11 toolkit.
+ */
+
+static Position window_x, window_y;
+static Arg arg_xy[] = {
+ {XtNx, (XtArgVal) &window_x},
+ {XtNy, (XtArgVal) &window_y},
+};
+
+#define get_xy() XtGetValues(globals.widgets.draw_widget, arg_xy, XtNumber(arg_xy))
+
+
+static void
+warn_num_params(const char *act_name, String *params, int num_params, int max_params)
+{
+ if (num_params > max_params) {
+ XDVI_WARNING((stderr, "Too many parameters (%d) for action \"%s\", ignoring all after \"%s\"",
+ num_params, act_name, params[max_params - 1]));
+ }
+}
+
+
+struct xdvi_action *
+compile_action(const char *str)
+{
+ const char *p, *p1, *p2;
+ XtActionsRec *actp;
+ struct xdvi_action *ap;
+
+ while (*str == ' ' || *str == '\t')
+ ++str;
+
+ if (*str == '\0' || *str == '\n')
+ return NULL;
+
+ p = str;
+
+ /* find end of command name */
+ while (isalnum((int)*p) || *p == '-' || *p == '_')
+ ++p;
+
+ for (actp = m_actions; ; ++actp) {
+ if (actp >= m_actions + XtNumber(m_actions)) {
+ const char *tmp = strchr(str, '\0');
+ if (tmp == NULL) {
+ tmp = p;
+ }
+ XDVI_WARNING((stderr, "Cannot compile action \"%.*s\".", (int)(tmp - str), str));
+
+ return NULL;
+ }
+ if (memcmp(str, actp->string, p - str) == 0 && actp->string[p - str] == '\0')
+ break;
+ }
+
+ while (*p == ' ' || *p == '\t')
+ ++p;
+ if (*p != '(') {
+ while (*p != '\0' && *p != '\n')
+ ++p;
+ XDVI_WARNING((stderr, "Syntax error in action %.*s.", (int)(p - str), str));
+
+ return NULL;
+ }
+ ++p;
+ while (*p == ' ' || *p == '\t')
+ ++p;
+ for (p1 = p;; ++p1) {
+ if (*p1 == '\0' || *p1 == '\n') {
+ XDVI_WARNING((stderr, "Syntax error in action %.*s.", (int)(p1 - str), str));
+ return NULL;
+ }
+ if (*p1 == ')')
+ break;
+ }
+
+ ap = xmalloc(sizeof *ap);
+ ap->proc = actp->proc;
+ for (p2 = p1;; --p2) {
+ if (p2 <= p) { /* if no args */
+ ap->num_params = 0;
+ ap->param = NULL;
+ break;
+ }
+ else if (p2[-1] != ' ' && p2[-1] != '\t') {
+ char *arg;
+
+ arg = xmalloc(p2 - p + 1);
+ memcpy(arg, p, p2 - p);
+ arg[p2 - p] = '\0';
+ ap->num_params = 1;
+ ap->param = arg;
+ break;
+ }
+ }
+
+ ap->next = compile_action(p1 + 1);
+
+ return ap;
+}
+
+void
+handle_command(Widget widget, XtPointer client_data, XtPointer call_data)
+{
+ struct xdvi_action *actp;
+
+ UNUSED(call_data);
+
+ /* call all actions registered for this event */
+ for (actp = (struct xdvi_action *)client_data; actp != NULL; actp = actp->next) {
+ if (globals.debug & DBG_EVENT)
+ fprintf(stderr, "calling action with param: %s\n", actp->param);
+ (actp->proc) (widget, NULL, &actp->param, &actp->num_params);
+ }
+}
+
+#ifdef MOTIF
+int
+set_bar_value(Widget bar, int value, int max)
+{
+ XmScrollBarCallbackStruct call_data;
+
+#ifdef TEST_SCROLLING
+ fprintf(stderr, "set_bar_value: val %d, max %d\n", value, max);
+#endif
+ if (value > max)
+ value = max;
+ if (value < 0)
+ value = 0;
+ call_data.value = value;
+ XtVaSetValues(bar, XmNvalue, value, NULL);
+ XtCallCallbacks(bar, XmNvalueChangedCallback, &call_data);
+ return value;
+}
+#endif
+
+void
+home(wide_bool scrl)
+{
+ if (!scrl)
+ XUnmapWindow(DISP, mane.win);
+# ifdef MOTIF
+ {
+ int value;
+
+ value = (globals.page.w - mane.width) / 2;
+ if (value > resource.sidemargin_int / mane.shrinkfactor)
+ value = resource.sidemargin_int / mane.shrinkfactor;
+ (void)set_bar_value(globals.widgets.x_bar, value, (int)(globals.page.w - mane.width));
+
+ value = (globals.page.h - mane.height) / 2;
+ if (value > resource.topmargin_int / mane.shrinkfactor)
+ value = resource.topmargin_int / mane.shrinkfactor;
+ (void)set_bar_value(globals.widgets.y_bar, value, (int)(globals.page.h - mane.height));
+ }
+# else
+ get_xy();
+ if (globals.widgets.x_bar != NULL) {
+ int coord = (globals.page.w - mane.width) / 2;
+
+ if (coord > resource.sidemargin_int / mane.shrinkfactor)
+ coord = resource.sidemargin_int / mane.shrinkfactor;
+ XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer) (window_x + coord));
+ }
+ if (globals.widgets.y_bar != NULL) {
+ int coord = (globals.page.h - mane.height) / 2;
+
+ if (coord > resource.topmargin_int / mane.shrinkfactor)
+ coord = resource.topmargin_int / mane.shrinkfactor;
+ XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer) (window_y + coord));
+ }
+# endif /* MOTIF */
+ if (!scrl) {
+ XMapWindow(DISP, mane.win);
+ /* Wait for the server to catch up---this eliminates flicker. */
+ XSync(DISP, False);
+ }
+ handle_x_scroll(NULL, NULL, NULL, NULL);
+ handle_y_scroll(NULL, NULL, NULL, NULL);
+}
+
+/*
+ * Same as home(), except move to the bottom of the page.
+ */
+
+static void
+home_bottom(wide_bool scrl)
+{
+ UNUSED(scrl);
+ XUnmapWindow(DISP, mane.win);
+#ifdef MOTIF
+ {
+ int value;
+
+ value = (globals.page.w - mane.width) / 2;
+ if (value > resource.sidemargin_int / mane.shrinkfactor)
+ value = resource.sidemargin_int / mane.shrinkfactor;
+ (void)set_bar_value(globals.widgets.x_bar, value, (int)(globals.page.w - mane.width));
+
+ (void)set_bar_value(globals.widgets.y_bar, (int)(globals.page.h - mane.height), (int)(globals.page.h - mane.height));
+ }
+#else /* MOTIF */
+ get_xy();
+ if (globals.widgets.x_bar != NULL) {
+ int coord = (globals.page.w - mane.width) / 2;
+
+ if (coord > resource.sidemargin_int / mane.shrinkfactor)
+ coord = resource.sidemargin_int / mane.shrinkfactor;
+ XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer) (window_x + coord));
+ }
+ if (globals.widgets.y_bar != NULL)
+ XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer)(window_y + (globals.page.h - mane.height)));
+#endif /* MOTIF */
+ XMapWindow(DISP, mane.win);
+ /* Wait for the server to catch up---this eliminates flicker. */
+ XSync(DISP, False);
+
+ handle_x_scroll(NULL, NULL, NULL, NULL);
+ handle_y_scroll(NULL, NULL, NULL, NULL);
+}
+
+
+#ifndef MOTIF
+static void
+handle_destroy_bar(Widget w, XtPointer client_data, XtPointer call_data)
+{
+ UNUSED(w);
+ UNUSED(call_data);
+ *(Widget *) client_data = NULL;
+}
+#endif
+
+static Boolean resized = False;
+
+static void
+get_geom(void)
+{
+ static Dimension new_clip_w, new_clip_h;
+
+ static Arg arg_wh_clip[] = {
+ {XtNwidth, (XtArgVal) &new_clip_w},
+ {XtNheight, (XtArgVal) &new_clip_h},
+ };
+
+ static Dimension window_w, window_h;
+
+ static Arg arg_wh[] = {
+ {XtNwidth, (XtArgVal) &window_w},
+ {XtNheight, (XtArgVal) &window_h},
+ };
+
+
+ int old_clip_w;
+
+#ifdef MOTIF
+ /* event handlers for Motif scrollbars have already been added
+ in create_initialize_widgets(), xdvi.c */
+ XtGetValues(globals.widgets.main_window, arg_wh, XtNumber(arg_wh));
+#else
+ XtGetValues(globals.widgets.vport_widget, arg_wh, XtNumber(arg_wh));
+ /* Note: widgets may be destroyed but not forgotten */
+ if (globals.widgets.x_bar == NULL) {
+ globals.widgets.x_bar = XtNameToWidget(globals.widgets.vport_widget, "horizontal");
+ if (globals.widgets.x_bar != NULL) {
+ XtAddCallback(globals.widgets.x_bar, XtNdestroyCallback, handle_destroy_bar,
+ (XtPointer)&globals.widgets.x_bar);
+ XtAddEventHandler(globals.widgets.x_bar, ButtonMotionMask | ButtonPressMask | ButtonReleaseMask,
+ False, handle_x_scroll, NULL);
+ }
+ }
+ if (globals.widgets.y_bar == NULL) {
+ globals.widgets.y_bar = XtNameToWidget(globals.widgets.vport_widget, "vertical");
+ if (globals.widgets.y_bar != NULL) {
+ XtAddCallback(globals.widgets.y_bar, XtNdestroyCallback, handle_destroy_bar,
+ (XtPointer)&globals.widgets.y_bar);
+ XtAddEventHandler(globals.widgets.y_bar, ButtonMotionMask | ButtonPressMask | ButtonReleaseMask,
+ False, handle_y_scroll, NULL);
+ }
+ }
+#endif
+ XtGetValues(globals.widgets.clip_widget, arg_wh_clip, XtNumber(arg_wh_clip));
+
+ old_clip_w = mane.width;
+
+ /* we need to do this because
+ sizeof(Dimension) != sizeof(int)
+ */
+ mane.width = new_clip_w;
+ mane.height = new_clip_h;
+ if (old_clip_w == 0) {
+ globals.ev.flags |= EV_NEWPAGE;
+ }
+
+ if (resource.keep_flag) {
+#ifndef MOTIF
+ Dimension d;
+ int curr_scroll;
+ if ((globals.widgets.x_bar != NULL && m_x_scroll != 0) || (globals.widgets.y_bar != NULL && m_y_scroll != 0)) {
+ get_xy();
+ }
+#endif
+ if (globals.widgets.x_bar != NULL && m_x_scroll != 0) {
+#ifdef MOTIF
+ if (m_x_scroll > 0)
+ (void)set_bar_value(globals.widgets.x_bar, m_x_scroll, (int)(globals.page.w - mane.width));
+#else
+ XtVaGetValues(globals.widgets.clip_widget, XtNy, &d, NULL);
+ curr_scroll = d - window_x;
+ if (m_x_scroll > curr_scroll) {
+ TRACE_GUI((stderr, "======== diff: %d", m_x_scroll - curr_scroll));
+ XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer)(m_x_scroll - curr_scroll));
+ }
+#endif
+ }
+ if (globals.widgets.y_bar != NULL && m_y_scroll != 0) {
+#ifdef MOTIF
+ if (m_y_scroll > 0)
+ (void)set_bar_value(globals.widgets.y_bar, m_y_scroll, (int)(globals.page.h - mane.height));
+#else
+ XtVaGetValues(globals.widgets.clip_widget, XtNy, &d, NULL);
+ curr_scroll = d - window_y;
+ if (m_y_scroll > curr_scroll) {
+ TRACE_GUI((stderr, "======== diff: %d", m_y_scroll - curr_scroll));
+ XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer)(m_y_scroll - curr_scroll));
+ }
+#endif
+ }
+ }
+ /* home(False); */
+ resized = False;
+}
+
+/*
+ * Callback routines
+ */
+
+void
+handle_resize(Widget widget, XtPointer junk, XEvent *event, Boolean *cont)
+{
+ UNUSED(widget);
+ UNUSED(junk);
+ UNUSED(event);
+ UNUSED(cont);
+
+ resized = True;
+#ifndef MOTIF
+ handle_statusline_resize();
+ handle_pagelist_resize();
+#endif
+}
+
+void
+reconfig(void)
+{
+/* Dimension x, y; */
+
+ if (globals.dvi_file.bak_fp == NULL)
+ return;
+
+#ifndef MOTIF
+ XtVaSetValues(globals.widgets.vport_widget, XtNresizable, (XtArgVal)False, NULL);
+#endif
+ TRACE_GUI((stderr, "globals.widgets.draw_widget: w %d, h %d", globals.page.w, globals.page.h));
+ XtVaSetValues(globals.widgets.draw_widget, XtNwidth, (XtArgVal)globals.page.w, XtNheight, (XtArgVal)globals.page.h, NULL);
+
+#ifdef TEST_SCROLLING
+/* XtVaSetValues(globals.widgets.draw_background, XtNwidth, (XtArgVal)globals.page.w, XtNheight, (XtArgVal)globals.page.h, NULL); */
+#endif
+
+#ifndef MOTIF
+ handle_statusline_resize(); /* without this, statusline will disappear */
+ /* following not needed? */
+ /* handle_pagelist_resize(); */
+#endif
+
+ get_geom();
+
+/* set_windowsize(&x, &y */
+/* #ifndef MOTIF */
+/* , get_panel_width() */
+/* #endif */
+/* ); */
+/* XResizeWindow(DISP, XtWindow(globals.widgets.top_level), x, y); */
+/* reconfigure_window(False, x, y, True); */
+/* reconfig_window(); */
+}
+
+
+int
+check_goto_page(int pageno)
+{
+ int retval;
+ if (pageno < 0) {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT, "Can't go to page %d, going to first page instead", pageno + 1);
+ retval = 0;
+ }
+ else if (pageno >= total_pages) {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT,
+ "Can't go to page %d, going to last page (%d) instead",
+ pageno + 1, total_pages);
+ retval = total_pages - 1;
+ }
+ else
+ retval = pageno;
+
+ page_history_insert(retval);
+ return retval;
+}
+
+
+static int
+check_goto_tex_page(int pageno)
+{
+ /*
+ Translate from TeX page number to `real' page number if
+ needed. Note that pageno is a C-style 0-based number, hence we
+ add 1 for the argument of pageinfo_get_index_of_number().
+ */
+ int retval;
+ if (resource.use_tex_pages) {
+ int res = pageinfo_get_index_of_number(pageno + 1);
+ if (res >= 0)
+ retval = res;
+ else {
+ XBell(DISP, 0);
+ if (pageno < 1) {
+ statusline_print(STATUS_SHORT, "Can't go to page %d, going to first page instead", pageno + 1);
+ retval = 0;
+ }
+ else {
+ /* there is no quick way to determine the last page number in the TeX page index */
+ statusline_print(STATUS_SHORT,
+ "Can't go to page %d, going to last page instead",
+ pageno + 1);
+ retval = total_pages - 1;
+ }
+ }
+ page_history_insert(retval);
+ }
+ else {
+ retval = check_goto_page(pageno);
+ }
+ return retval;
+}
+
+/* |||
+ * Currently the event handler does not coordinate XCopyArea requests
+ * with GraphicsExpose events. This can lead to problems if the window
+ * is partially obscured and one, for example, drags a scrollbar.
+ */
+
+/*
+ * Actions for the translation mechanism.
+ */
+
+/* if there are global prefixes, return them in res and reset them to defaults */
+static Boolean
+get_prefix_arg(int *res)
+{
+ Boolean ret;
+
+ *res = m_sign * m_number;
+ ret = m_have_arg;
+ /* reset global flags */
+ m_have_arg = False;
+ m_number = 0;
+ m_sign = 1;
+ return ret;
+}
+
+Boolean
+get_int_arg(String *param, Cardinal *num_params, int *res)
+{
+ if (*num_params > 0) {
+ *res = atoi(*param);
+ return True;
+ }
+ else {
+ if (get_prefix_arg(res)) { /* prefix argument? */
+ return True;
+ }
+ }
+ return False;
+}
+
+
+Boolean
+toggle_arg(int arg, String *param, Cardinal *num_params)
+{
+ if (*num_params > 0) {
+ if (**param != 't' && (atoi(*param) != 0) == arg)
+ return False;
+ }
+ else {
+ if (m_have_arg) {
+ int tmparg = m_number;
+
+ m_have_arg = False;
+ m_number = 0;
+ m_sign = 1;
+
+ if ((tmparg != 0) == arg)
+ return False;
+ }
+ }
+ return True;
+}
+
+#if defined(NEW_MENU_CREATION) || defined(MOTIF)
+Boolean
+check_resource_expert(void *val, const char *param)
+{
+ int j = strtol(param, (char **)NULL, 10);
+ /* check if the j-1th bit is set: */
+ return (*(int *)val >> (j - 1)) & 1;
+}
+
+Boolean
+check_papersize(void *val, const char *param)
+{
+ UNUSED(val);
+ UNUSED(param);
+ return False; /* TODO */
+}
+
+Boolean
+check_paper_landscape(void *val, const char *param)
+{
+ UNUSED(val);
+ UNUSED(param);
+ return False; /* TODO */
+}
+
+/* comparison functions for the menu setting code */
+Boolean
+check_toggle(void *val, const char *param)
+{
+ Boolean *on = val;
+ if (strcmp(param, "toggle") == 0) {
+ return *on;
+ }
+ else {
+ fprintf(stderr, "TODO: check_toggle: arg |%s|, curr: %d\n", param, *(int *)val);
+ return *on;
+ }
+}
+
+Boolean
+check_int(void *val, const char *param)
+{
+ int i = strtol(param, (char **)NULL, 10);
+ return i == *(int *)val;
+}
+#endif /* NEW_MENU_CREATION */
+
+
+static void
+Act_digit(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int digit;
+ /* for overflow checks */
+ static const int MAXINT_QUOT = INT_MAX / 10;
+ static const int MAXINT_MOD = INT_MAX % 10;
+
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (*num_params != 1 || (digit = **params - '0') > 9) {
+ XBell(DISP, 0);
+ return;
+ }
+ m_have_arg = True;
+
+ /* don't increment m_number if it would overflow */
+ if (m_number < MAXINT_QUOT || (m_number == MAXINT_QUOT && digit <= MAXINT_MOD)) {
+ m_number = m_number * 10 + digit;
+ if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */
+ statusline_print(STATUS_SHORT, "numerical prefix: %s%d", m_sign < 0 ? "-" : "", m_number);
+ }
+ else {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT, "numerical prefix: %s%d: no larger value possible", m_sign < 0 ? "-" : "", m_number);
+ }
+}
+
+static void
+Act_minus(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ m_have_arg = True;
+ m_sign = -m_sign;
+ if (m_number > 0) {
+ if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */
+ statusline_print(STATUS_SHORT, "numerical prefix: %s%d", m_sign < 0 ? "-" : "", m_number);
+ }
+ else {
+ if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */
+ statusline_print(STATUS_SHORT, "numerical prefix: %s", m_sign < 0 ? "-" : "");
+ }
+}
+
+static void
+Act_quit(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+#ifndef FLAKY_SIGPOLL
+ if (globals.debug & DBG_EVENT)
+ puts(event_freq < 0
+ ? "SIGPOLL is working"
+ : "no SIGPOLL signals received");
+#endif
+ xdvi_normal_exit();
+}
+
+static void
+Act_quit_confirm(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ static Widget dialog = 0;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+#ifndef FLAKY_SIGPOLL
+ if (globals.debug & DBG_EVENT)
+ puts(event_freq < 0
+ ? "SIGPOLL is working"
+ : "no SIGPOLL signals received");
+#endif
+
+ /* already a quit dialog open? */
+ if (dialog != 0) {
+ /* HACK ALERT: use brute force, since tests for XtIsRealized()
+ or XtIsMapped() don't work?? Grabbing the server apparently
+ has problems with Xaw, and it's not a nice solution anyway ... */
+ if (kill_message_window(dialog))
+ XBell(DISP, 0);
+ }
+
+ dialog = choice_dialog_sized(globals.widgets.top_level,
+ MSG_QUESTION,
+ SIZE_SMALL,
+ NULL,
+#ifndef MOTIF
+ "quit",
+#endif
+ NULL, NULL, /* no pre_callbacks */
+ "OK", xdvi_normal_exit_cb, NULL,
+ "Cancel", NULL, NULL,
+ "Really quit xdvi?");
+}
+
+static void
+Act_load_url(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(num_params);
+ launch_browser(*params);
+}
+
+static void
+Act_print(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ static struct save_or_print_info info;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ info.act = FILE_PRINT;
+
+ save_or_print_callback(&info);
+}
+
+static void
+Act_save(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ static struct save_or_print_info info;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ info.act = FILE_SAVE;
+
+ save_or_print_callback(&info);
+}
+
+static void
+Act_find(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+ dvi_find_string(NULL, False);
+}
+
+static void
+Act_find_next(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+ dvi_find_string(NULL, True);
+}
+
+static void
+Act_help(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ char *arg = NULL;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (*num_params > 0)
+ arg = *params;
+
+ show_help(globals.widgets.top_level, arg);
+}
+
+static home_proc home_action = NULL;
+
+void
+goto_page(int new_page, home_proc proc, Boolean force)
+{
+ /* SU: added clearing the window here, else old window content
+ will survive switching pages as long as globals.pausing.flag is active
+ (i.e. when inadvertedly changing page instead of unpausing)
+ */
+ if (globals.pausing.flag) {
+ XClearWindow(DISP, mane.win);
+ }
+
+ if (globals.dvi_file.bak_fp == NULL) {
+ current_page = new_page; /* so that xdvi gets the page change */
+ return;
+ }
+
+ ASSERT(new_page >= 0 && new_page < total_pages, "new_page in goto_page() out of range");
+ if (current_page != new_page || force) {
+ globals.cursor.flags &= ~CURSOR_LINK; /* disable link cursor if needed */
+
+ current_page = new_page;
+ home_action = proc;
+ globals.warn_spec_now = resource.warn_spec;
+ /* this seems unneccessary */
+/* if (!resource.keep_flag) */
+/* home(False); */
+#if defined(MOTIF) && HAVE_XPM
+ tb_check_navigation_sensitivity(current_page);
+/* page_history_update_toolbar_navigation(); */
+#endif
+ maybe_scroll_pagelist(current_page, False);
+
+ if (globals.pausing.num_save)
+ globals.pausing.num = globals.pausing.num_save[new_page];
+ else
+ globals.pausing.num = 0;
+ /* Control-L (and changing the page) clears this box */
+ globals.src.fwd_box_page = -1;
+
+ globals.ev.flags |= EV_NEWPAGE;
+ XFlush(DISP);
+ }
+}
+
+
+static void
+Act_goto_page(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int arg;
+ Boolean clear_statusline = False;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ warn_num_params("goto-page()", params, *num_params, 1);
+
+ if (*num_params > 0) {
+ if (**params == 'e') {
+ arg = total_pages - 1;
+ }
+ else
+ arg = atoi(*params) - globals.pageno_correct;
+ }
+ else {
+ if (get_prefix_arg(&arg)) {
+ clear_statusline = True;
+ arg -= globals.pageno_correct;
+ }
+ else {
+ arg = total_pages - 1;
+ }
+ }
+ if (arg == total_pages - 1) {
+ /* with TeX numbers, total_pages - 1 might not be found in the page list,
+ so don't use check_goto_tex_page() in this case */
+ goto_page(check_goto_page(arg), resource.keep_flag ? NULL : home, False);
+ }
+ else {
+ goto_page(check_goto_tex_page(arg), resource.keep_flag ? NULL : home, False);
+ }
+ search_signal_page_changed();
+ if (clear_statusline)
+ statusline_clear();
+}
+
+void
+Act_recent_files(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+}
+
+void
+Act_use_tex_pages(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ UNUSED(w);
+ UNUSED(event);
+
+ if (block_this_mouse_event())
+ return;
+
+ if (*num_params == 0) {
+ if (m_have_arg) {
+ resource.use_tex_pages = (m_number != 0);
+ m_have_arg = False;
+ m_number = 0;
+ m_sign = 1;
+ }
+ else {
+ resource.use_tex_pages = !resource.use_tex_pages;
+ }
+ }
+ else
+ resource.use_tex_pages = (**params == 't' ? !resource.use_tex_pages : atoi(*params));
+
+ if (resource.use_tex_pages) {
+ statusline_print(STATUS_SHORT, "Using TeX page numbers for \"g\", goto-page()");
+ }
+ else {
+ statusline_print(STATUS_SHORT, "Using physical page numbers for \"g\", goto-page()");
+ }
+
+ store_preference(NULL, "useTeXPages", "%s", resource.use_tex_pages ? "True" : "False");
+
+#if defined(NEW_MENU_CREATION) || defined(MOTIF)
+ set_menu(&resource.use_tex_pages, Act_use_tex_pages, check_toggle);
+#else
+#ifdef MOTIF
+ set_use_tex_option();
+#else
+ toggle_menu(resource.use_tex_pages, Act_use_tex_pages);
+#endif
+#endif /* NEW_MENU_CREATION */
+ refresh_pagelist(total_pages, current_page);
+}
+
+void
+Act_forward_page(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int arg;
+ Boolean clear_statusline = False;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (!get_int_arg(params, num_params, &arg))
+ arg = 1;
+ else
+ clear_statusline = True;
+
+ arg += current_page;
+
+ if (arg == current_page) { /* zero argument -> redraw page */
+ globals.src.fwd_box_page = -1;
+ search_reset_info();
+ globals.ev.flags |= EV_NEWPAGE;
+ XFlush(DISP);
+ statusline_print(STATUS_SHORT, "Page redrawn.");
+ return;
+ }
+ else if (current_page >= total_pages - 1) {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT, "Last page of DVI file");
+ return;
+ }
+
+ goto_page(check_goto_page(arg), resource.keep_flag ? NULL : home, False);
+ if (clear_statusline)
+ statusline_clear();
+ search_signal_page_changed();
+}
+
+void
+Act_back_page(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int arg;
+ Boolean clear_statusline = False;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (!get_int_arg(params, num_params, &arg))
+ arg = 1;
+ else
+ clear_statusline = True;
+
+ arg = current_page - arg;
+
+ if (current_page == 0) {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT, "First page of DVI file");
+ return;
+ }
+
+ goto_page(check_goto_page(arg), resource.keep_flag ? NULL : home, False);
+ if (clear_statusline)
+ statusline_clear();
+ search_signal_page_changed();
+}
+
+static void
+Act_declare_page_number(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int arg;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (resource.mouse_mode == MOUSE_RULER_MODE) {
+ show_distance_from_ruler(event, True);
+ return;
+ }
+
+ if (!get_int_arg(params, num_params, &arg)) {
+ arg = 0;
+ }
+ globals.pageno_correct = arg - current_page;
+ statusline_print(STATUS_SHORT, "Current page number set to %d", arg);
+}
+
+static void
+Act_toggle_mark(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int arg;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (globals.dvi_file.bak_fp == NULL)
+ return;
+
+ if (
+#ifdef MOTIF
+ (resource.expert_mode & XPRT_SHOW_PAGELIST) == 0
+#else
+ (resource.expert_mode & XPRT_SHOW_BUTTONS) == 0
+#endif
+ )
+ return;
+
+ if (*num_params > 0) {
+ arg = atoi(*params);
+ if (arg < -1 || arg > 2) {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT,
+ "Possible arguments: none (toggle current), "
+ "-1 (mark all), 0 (unmark all), 1 (toggle odd), 2 (toggle even)");
+ }
+ list_toggle_marks(arg);
+ }
+ else if (get_prefix_arg(&arg)) {
+ list_toggle_marks(arg);
+ statusline_clear();
+ }
+ else {
+ arg = current_page;
+ list_toggle_current(arg);
+ }
+}
+
+static void
+Act_home(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ if (block_this_mouse_event())
+ return;
+
+ home(True);
+}
+
+static void
+Act_home_or_top(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ if (block_this_mouse_event())
+ return;
+
+ if (resource.keep_flag) {
+ String args[1];
+ args[0] = "10"; /* should be large enough ... */
+ XtCallActionProc(globals.widgets.top_level, "up", NULL, args, 1);
+ }
+ else {
+ home(True);
+ }
+}
+
+static void
+Act_end_or_bottom(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ String args[1];
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ args[0] = "10"; /* should be large enough ... */
+ XtCallActionProc(globals.widgets.top_level, "down", NULL, args, 1);
+
+ if (!resource.keep_flag
+#ifndef MOTIF
+ && globals.widgets.x_bar != NULL
+#endif
+ ) {
+ XtCallActionProc(globals.widgets.top_level, "right", NULL, args, 1);
+ }
+}
+
+static void
+Act_center(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int x, y;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(params);
+ UNUSED(num_params);
+
+#ifdef MOTIF
+ /* The clip widget gives a more exact value. */
+ x = event->xkey.x - mane.width / 2;
+ y = event->xkey.y - mane.height / 2;
+
+ x = set_bar_value(globals.widgets.x_bar, x, (int)(globals.page.w - mane.width));
+ y = set_bar_value(globals.widgets.y_bar, y, (int)(globals.page.h - mane.height));
+ get_xy();
+ XWarpPointer(DISP, None, None, 0, 0, 0, 0, -x - window_x, -y - window_y);
+#else
+
+ if (event == NULL)
+ return; /* button actions do not provide events */
+
+ x = event->xkey.x - mane.width / 2;
+ y = event->xkey.y - mane.height / 2;
+ /* The clip widget gives a more exact value. */
+ if (globals.widgets.x_bar != NULL)
+ XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer) x);
+ if (globals.widgets.y_bar != NULL)
+ XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer) y);
+ XWarpPointer(DISP, None, None, 0, 0, 0, 0, -x, -y);
+#endif
+ handle_x_scroll(NULL, NULL, NULL, NULL);
+ handle_y_scroll(NULL, NULL, NULL, NULL);
+}
+
+void
+Act_ruler_snap_origin(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ if (resource.mouse_mode == MOUSE_RULER_MODE)
+ ruler_snap_origin(event);
+}
+
+#if defined(NEW_MENU_CREATION) || defined(MOTIF)
+void
+Act_set_papersize(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(num_params);
+
+ fprintf(stderr, "set_paperformat: %s\n", *params);
+ resource.paper = *params;
+ set_menu((void *)resource.paper, Act_set_papersize, check_papersize);
+}
+
+void
+Act_set_paper_landscape(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(num_params);
+
+ resource.paper_landscape = !resource.paper_landscape;
+ fprintf(stderr, "set_paper_landscape: %s\n", *params);
+
+ set_menu((char *)resource.paper, Act_set_paper_landscape, check_paper_landscape);
+}
+#endif /* NEW_MENU_CREATION */
+
+void
+Act_set_keep_flag(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(event);
+ UNUSED(w);
+
+ if (*num_params == 0) {
+ if (m_have_arg) {
+ resource.keep_flag = (m_number != 0);
+ m_have_arg = False;
+ m_number = 0;
+ m_sign = 1;
+ }
+ else {
+ resource.keep_flag = !resource.keep_flag;
+ }
+ }
+ else
+ resource.keep_flag = (**params == 't'
+ ? !resource.keep_flag : atoi(*params));
+ if (resource.keep_flag) {
+ statusline_print(STATUS_SHORT, "Keeping position when switching pages");
+ }
+ else {
+ statusline_print(STATUS_SHORT,
+ "Not keeping position when switching pages");
+ }
+
+ store_preference(NULL, "keepPosition", "%s", resource.keep_flag ? "True" : "False");
+
+#if defined(NEW_MENU_CREATION) || defined(MOTIF)
+ set_menu(&resource.keep_flag, Act_set_keep_flag, check_toggle);
+#else
+ toggle_menu(resource.keep_flag, Act_set_keep_flag);
+#endif /* NEW_MENU_CREATION */
+}
+
+static void
+Act_left(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ do_autoscroll = False;
+
+ warn_num_params("left()", params, *num_params, 1);
+
+#ifdef MOTIF
+ get_xy();
+#ifdef TEST_SCROLLING
+ fprintf(stderr, "left for %p\n", (void *)w);
+ fprintf(stderr, "window x: %d, y: %d\n", window_x, window_y);
+#endif
+
+ (void)set_bar_value(globals.widgets.x_bar, (*num_params == 0 ? (-2 * (int)mane.width / 3)
+ : (int)(-my_atof(*params) * mane.width)) - window_x,
+ (int)(globals.page.w - mane.width));
+#else
+ if (globals.widgets.x_bar != NULL)
+ XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc,
+ (XtPointer) (*num_params == 0 ? (-2 * (int)mane.width / 3)
+ : (int)(-my_atof(*params) * mane.width)));
+ else {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT, "Horizontal scrolling not possible");
+ }
+#endif
+ handle_x_scroll(NULL, NULL, NULL, NULL);
+}
+
+static void
+Act_right(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ do_autoscroll = False;
+
+ warn_num_params("right()", params, *num_params, 1);
+
+#ifdef MOTIF
+ get_xy();
+#ifdef TEST_SCROLLING
+ fprintf(stderr, "right for %p\n", (void *)w);
+ fprintf(stderr, "window x: %d, y: %d\n", window_x, window_y);
+#endif
+
+ (void)set_bar_value(globals.widgets.x_bar, (*num_params == 0 ? (2 * (int)mane.width / 3)
+ : (int)(my_atof(*params) * mane.width)) - window_x,
+ (int)(globals.page.w - mane.width));
+#else
+ if (globals.widgets.x_bar != NULL)
+ XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc,
+ (XtPointer) (*num_params == 0 ? (2 * (int)mane.width / 3)
+ : (int)(my_atof(*params) * mane.width)));
+ else {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT, "Horizontal scrolling not possible");
+ }
+#endif
+ handle_x_scroll(NULL, NULL, NULL, NULL);
+}
+
+static void
+Act_up(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ do_autoscroll = False;
+
+ warn_num_params("up()", params, *num_params, 1);
+
+#ifdef MOTIF
+#ifdef TEST_SCROLLING
+ fprintf(stderr, "up for %p\n", (void *)w);
+#endif
+ get_xy();
+ (void)set_bar_value(globals.widgets.y_bar, (*num_params == 0 ? (-2 * (int)mane.height / 3)
+ : (int)(-my_atof(*params) * mane.height)) - window_y,
+ (int)(globals.page.h - mane.height));
+#else
+ if (globals.widgets.y_bar != NULL)
+ XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
+ (XtPointer) (*num_params == 0 ? (-2 * (int)mane.height / 3)
+ : (int)(-my_atof(*params) * mane.height)));
+ else {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT, "Vertical scrolling not possible");
+ }
+#endif
+ handle_y_scroll(NULL, NULL, NULL, NULL);
+}
+
+static void
+Act_down(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ do_autoscroll = False;
+
+ warn_num_params("down()", params, *num_params, 1);
+
+#ifdef MOTIF
+#ifdef TEST_SCROLLING
+ fprintf(stderr, "down for %p\n", (void *)w);
+#endif
+ get_xy();
+ (void)set_bar_value(globals.widgets.y_bar, (*num_params == 0 ? (2 * (int)mane.height / 3)
+ : (int)(my_atof(*params) * mane.height)) - window_y,
+ (int)(globals.page.h - mane.height));
+#else
+ if (globals.widgets.y_bar != NULL)
+ XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
+ (XtPointer) (*num_params == 0 ? (2 * (int)mane.height / 3)
+ : (int)(my_atof(*params) * mane.height)));
+ else {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT, "Vertical scrolling not possible");
+ }
+#endif
+ handle_y_scroll(NULL, NULL, NULL, NULL);
+}
+
+static void
+Act_down_or_next(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ do_autoscroll = False;
+
+ warn_num_params("down-or-next()", params, *num_params, 1);
+
+#ifdef TEST_SCROLLING
+ fprintf(stderr, "down-or-next for %p\n", (void *)w);
+#endif
+
+#ifdef MOTIF
+ get_xy();
+ if (window_y > (int)mane.height - (int)globals.page.h) {
+ (void)set_bar_value(globals.widgets.y_bar, (*num_params == 0 ? (2 * (int)mane.height / 3)
+ : (int)(my_atof(*params) * mane.height)) -
+ window_y, (int)(globals.page.h - mane.height));
+ return;
+ }
+#else
+ if (globals.widgets.y_bar != NULL) {
+ get_xy();
+ if (window_y > (int)mane.height - (int)globals.page.h) {
+ XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
+ (XtPointer) (*num_params ==
+ 0 ? (2 * (int)mane.height / 3)
+ : (int)(my_atof(*params) * mane.height)));
+ return;
+ }
+ }
+#endif
+
+ if (current_page < total_pages - 1) {
+ goto_page(current_page + 1, home, False);
+ search_signal_page_changed();
+ }
+ else {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT, "At bottom of last page");
+ }
+ handle_y_scroll(NULL, NULL, NULL, NULL);
+}
+
+
+static void
+Act_up_or_previous(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ do_autoscroll = False;
+
+ warn_num_params("up-or-previous()", params, *num_params, 1);
+
+#ifdef TEST_SCROLLING
+ fprintf(stderr, "up-or-previous for %p\n", (void *)w);
+#endif
+#ifdef MOTIF
+ get_xy();
+ if (window_y < 0) {
+ (void)set_bar_value(globals.widgets.y_bar,
+ (*num_params == 0 ? (-2 * (int)mane.height / 3)
+ : (int)(-my_atof(*params) * mane.height)) - window_y,
+ (int)(globals.page.h - mane.height));
+ return;
+ }
+#else
+ if (globals.widgets.y_bar != NULL) {
+ get_xy();
+ if (window_y < 0) {
+ XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
+ (XtPointer) (*num_params ==
+ 0 ? (-2 * (int)mane.height / 3)
+ : (int)(-my_atof(*params) * mane.height)));
+ return;
+ }
+ }
+#endif
+
+ if (current_page > 0) {
+ goto_page(current_page - 1, home_bottom, False);
+ search_signal_page_changed();
+ }
+ else {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT, "At top of first page");
+ }
+ handle_y_scroll(NULL, NULL, NULL, NULL);
+}
+
+
+static void
+Act_set_margins(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ Window dummy;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(params);
+ UNUSED(num_params);
+
+#ifndef MOTIF
+ if (event == NULL)
+ return; /* button actions do not provide events */
+#endif
+
+ (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
+ event->xkey.x, event->xkey.y, &resource.sidemargin_int, &resource.topmargin_int, &dummy); /* throw away last argument */
+ statusline_print(STATUS_SHORT, "Margins set to cursor position (%d, %d)", resource.sidemargin_int, resource.topmargin_int);
+ resource.sidemargin_int *= mane.shrinkfactor;
+ resource.topmargin_int *= mane.shrinkfactor;
+}
+
+static void
+Act_show_display_attributes(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ statusline_print(STATUS_SHORT, "Unit = %d, bitord = %d, byteord = %d",
+ BitmapUnit(DISP), BitmapBitOrder(DISP),
+ ImageByteOrder(DISP));
+}
+
+int
+shrink_to_fit(void)
+{
+ int value1;
+ int value2;
+ static Dimension window_w;
+#ifndef MOTIF
+ static Dimension window_h;
+#endif
+
+ static Arg arg_wh[] = {
+ {XtNwidth, (XtArgVal) &window_w}
+#ifndef MOTIF
+ , {XtNheight, (XtArgVal) &window_h}
+#endif
+ };
+
+
+#ifdef MOTIF
+ XtGetValues(globals.widgets.main_window, arg_wh, XtNumber(arg_wh));
+#else
+ XtGetValues(globals.widgets.vport_widget, arg_wh, XtNumber(arg_wh));
+#endif
+
+ value1 = ROUNDUP(globals.page.unshrunk_w, window_w - 2);
+
+#ifdef MOTIF
+ { /* account for menubar */
+ static Dimension new_h;
+
+ /* get rid of scrollbar */
+ XtVaSetValues(globals.widgets.draw_widget, XtNwidth, (XtArgVal)1, XtNheight, (XtArgVal)1, NULL);
+ XtVaGetValues(globals.widgets.clip_widget, XtNheight, &new_h, NULL);
+ value2 = ROUNDUP(globals.page.unshrunk_h, new_h - 2);
+ }
+#else
+ /* FIXME: value seems too small here! */
+ value2 = ROUNDUP(globals.page.unshrunk_h, window_h - 2);
+#endif
+
+ return value1 > value2 ? value1 : value2;
+}
+
+void
+do_set_shrinkfactor(int arg, Boolean set_resource)
+{
+ static int shrink_bak = -1;
+
+ /* We don't store this as preference since it may affect initial window geometry. */
+ /* store_preference(NULL, "shrinkFactor", "%d", arg); */
+
+ if (resource.mouse_mode == MOUSE_TEXT_MODE) {
+ text_change_region(TEXT_SEL_CLEAR, NULL);
+ }
+
+ mane.shrinkfactor = arg;
+ if (set_resource)
+ resource.shrinkfactor = mane.shrinkfactor;
+
+#if defined(NEW_MENU_CREATION) || defined(MOTIF)
+ set_menu(&arg, Act_set_shrink_factor, check_int);
+#if HAVE_XMP
+ tb_set_zoom_sensitivity(arg != 1);
+#endif
+#else
+ toggle_menu(arg, Act_set_shrink_factor);
+#endif /* NEW_MENU_CREATION */
+ if (arg != 1 && arg != shrink_bak) {
+ shrink_bak = arg;
+#if GREY
+#if COLOR
+ if (resource.use_grey)
+ fg_active = NULL;
+#else
+ if (resource.use_grey)
+ init_pix();
+#endif
+#endif /* GREY */
+ reset_fonts();
+ }
+ init_page();
+ reconfig();
+
+ htex_resize_page();
+
+ /* this seems unneccessary */
+/* if (!resource.keep_flag) */
+/* home(False); */
+
+ globals.ev.flags |= EV_NEWPAGE;
+ XFlush(DISP);
+}
+
+void
+Act_set_shrink_factor(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int arg;
+ /* Set a limit to the shrink factors allowed; otherwise xdvi may
+ consume a lot of memory (even run out of memory in malloc) in
+ the allocation for the subpixel lookup table pixeltbl,
+ dvi-draw.c. The `correct' solution would be to recognize that at a
+ certain shrink, the only pixel for a character will be always
+ set to `on' anyway, so that we could do without the lookup table for
+ very large shrink factors ... */
+ static const int SHRINK_MAX = 999;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ warn_num_params("set-shrink-factor()", params, *num_params, 1);
+
+ if (*num_params > 0) {
+ if (**params == 'a')
+ arg = shrink_to_fit();
+ else if (**params == '-')
+ arg = mane.shrinkfactor + 1;
+ else if (**params == '+')
+ arg = mane.shrinkfactor - 1;
+ else
+ arg = atoi(*params);
+ }
+ else if (!get_prefix_arg(&arg)) {
+ arg = shrink_to_fit();
+ }
+
+ if (arg <= 0) {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT,
+ "No more enlarging possible");
+ return;
+ }
+
+ if (arg > SHRINK_MAX) {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT,
+ "Shrink factor %d too large (maximum: %d)", arg, SHRINK_MAX);
+ return;
+ }
+
+ /* SU: added clearing the window here, else old window content
+ will survive changing shrink
+ */
+ if (globals.pausing.flag) {
+ XClearWindow(DISP, mane.win);
+ }
+
+ statusline_print(STATUS_SHORT, "shrink factor: %d", arg);
+#if 0
+ /* Uncommented this, otherwise selecting `shrink to fit' with same value as
+ current mane.shrinkfactor will clear the canvas (only fixed by changing shrink
+ factor again). That bug is also present in xdvik-22.48-alpha3. */
+ if (arg == mane.shrinkfactor) {
+ return;
+ }
+#endif
+
+ do_set_shrinkfactor(arg, True);
+
+#if MOTIF
+ /* note: mustn't do that inside do_set_shrinkfactor(),
+ or we get into an endless loop! */
+ update_preferences_shrink();
+#endif
+}
+
+void
+Act_shrink_to_dpi(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int arg, arg_bak;
+ Boolean clear_statusline = False;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ warn_num_params("shrink-to-dpi()", params, *num_params, 1);
+
+ if (!get_int_arg(params, num_params, &arg))
+ arg = 0;
+ else
+ clear_statusline = True;
+
+ arg_bak = arg;
+
+ if (arg > 0)
+ arg = (double)resource.pixels_per_inch / arg + 0.5;
+
+ if (arg <= 0) {
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT,
+ "shrink-to-dpi requires a positive argument");
+ return;
+ }
+
+ /* like elsewhere */
+ if (globals.pausing.flag) {
+ XClearWindow(DISP, mane.win);
+ }
+#if 0
+ /* Uncommented this, otherwise selecting `shrink to fit' with same value as
+ current mane.shrinkfactor will clear the canvas (only fixed by changing shrink
+ factor again). That bug is also present in xdvik-22.48-alpha3. */
+ if (arg == mane.shrinkfactor)
+ return;
+#endif
+ do_set_shrinkfactor(arg, True);
+
+#if MOTIF
+ /* note: mustn't do that inside do_set_shrinkfactor(),
+ or we get into an endless loop! */
+ update_preferences_shrink();
+#endif
+}
+
+void
+do_set_density(double newgamma, Boolean force, Boolean update_resource)
+{
+
+ /* like elsewhere */
+ if (globals.pausing.flag) {
+ XClearWindow(DISP, mane.win);
+ }
+
+#ifdef GREY
+ if (resource.use_grey) {
+ if (newgamma == resource.gamma && !force) {
+ statusline_print(STATUS_SHORT, "density value: %.3f", newgamma);
+ return;
+ }
+ resource.gamma = newgamma;
+ if (update_resource)
+ globals.curr_gamma = resource.gamma;
+
+#if COLOR
+ fg_active = NULL;
+ reset_colors();
+#else
+ init_pix();
+ if (G_visual->class != TrueColor) {
+ return;
+ }
+ reset_fonts();
+#endif /* COLOR */
+ statusline_print(STATUS_SHORT, "density value: %.3f", newgamma);
+ } else
+#endif /* GREY */
+ {
+ reset_fonts();
+ if (mane.shrinkfactor == 1) {
+ statusline_print(STATUS_SHORT,
+ "set-density ignored at magnification 1");
+ return;
+ }
+ statusline_print(STATUS_SHORT, "density value: %.3f", newgamma);
+ }
+
+ store_preference(NULL, "gamma", "%f", resource.gamma);
+
+ globals.ev.flags |= EV_NEWPAGE;
+ XFlush(DISP);
+}
+
+static void
+Act_set_density(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int arg;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ warn_num_params("set-density()", params, *num_params, 1);
+
+ if (!get_int_arg(params, num_params, &arg)) {
+ XBell(DISP, 0);
+ return;
+ }
+/* if (arg < 0) { */
+/* XBell(DISP, 0); */
+/* statusline_print(STATUS_SHORT, */
+/* "set-density requires a positive value"); */
+/* return; */
+/* } */
+ do_set_density(arg != 0 ? arg / 100.0 : 1.0, False, True);
+#if MOTIF
+ update_preferences_darkness();
+#else
+ store_preference(NULL, "gamma", "%f", resource.gamma);
+#endif
+}
+
+
+static void
+Act_change_density(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int arg;
+ double diff;
+ double oldgamma = resource.gamma;
+ UNUSED(w);
+ UNUSED(event);
+
+ if (block_this_mouse_event())
+ return;
+
+ warn_num_params("change-density()", params, *num_params, 1);
+
+ if (!get_int_arg(params, num_params, &arg)) {
+ XBell(DISP, 0);
+ return;
+ }
+ diff = oldgamma / arg;
+ if (oldgamma + diff <= 0.0)
+ oldgamma = 0.0;
+ else
+ oldgamma += diff;
+ do_set_density(oldgamma, False, True);
+#if MOTIF
+ update_preferences_darkness();
+#endif
+}
+
+static void
+Act_fullscreen(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ Dimension main_win_w, main_win_h;
+ static int orig_x = 0, orig_y = 0;
+#ifndef MOTIF
+ static Dimension get_x, get_y;
+#endif
+ static Dimension old_w = 0, old_h = 0;
+ Dimension panel_width = 0;
+#if 0
+ static Dimension w_old = 0, h_old = 0;
+#endif
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (!toggle_arg(resource.fullscreen, params, num_params)) {
+ return;
+ }
+
+ resource.fullscreen = !resource.fullscreen;
+
+#ifndef MOTIF
+ panel_width = get_panel_width();
+#endif
+
+ if (resource.fullscreen) {
+ /* when toggling to fullscreen, save current scrollbar values
+ and window geometry */
+ XtVaGetValues(globals.widgets.top_level, XtNwidth, &old_w, XtNheight, &old_h, NULL);
+ /* fprintf(stderr, "saved geometry: %dx%d\n", old_w, old_h); */
+#ifdef MOTIF
+ if (globals.widgets.x_bar != NULL)
+ XtVaGetValues(globals.widgets.x_bar, XmNvalue, &orig_x, NULL);
+ if (globals.widgets.y_bar != NULL)
+ XtVaGetValues(globals.widgets.y_bar, XmNvalue, &orig_y, NULL);
+#else
+ get_xy();
+ if (globals.widgets.x_bar != NULL)
+ XtVaGetValues(globals.widgets.clip_widget, XtNx, &get_x, NULL);
+ if (globals.widgets.y_bar != NULL)
+ XtVaGetValues(globals.widgets.clip_widget, XtNy, &get_y, NULL);
+ orig_x = -(get_x - window_x);
+ orig_y = -(get_y - window_y);
+#endif /* MOTIF */
+ /* fprintf(stderr, "Current offsets: %d, %d, %d, %d\n", orig_x, window_x, orig_y, window_y); */
+ }
+#if 0 /* TODO: don't hardcode w/h values -
+ what's wrong with the set_windowsize() call below? */
+ if (resource.fullscreen) {
+ XWindowAttributes attr;
+ if (XGetWindowAttributes(DISP, XtWindow(globals.widgets.top_level), &attr)) {
+ w_old = attr.width;
+ h_old = attr.height;
+ }
+ main_win_w = WidthOfScreen(SCRN);
+ main_win_h = HeightOfScreen(SCRN);
+ }
+ else if (w_old > 0 && h_old > 0) {
+ main_win_w = w_old;
+ main_win_h = h_old;
+ }
+ else {
+ main_win_w = 800;
+ main_win_h = 600;
+ }
+#else
+ if (resource.fullscreen) {
+ set_windowsize(&main_win_w, &main_win_h, panel_width, 0, False);
+ }
+ else { /* re-use old window sizes, or initialize them if started in fullscreen mode */
+ if (old_w == 0 || old_h == 0) /* started in fullscreen mode */
+ set_windowsize(&old_w, &old_h, panel_width, 0, False);
+ else
+ set_windowsize(&old_w, &old_h, panel_width, 0, True);
+ }
+#endif
+
+/* fprintf(stderr, "reconfigure window!\n"); */
+ if (resource.fullscreen)
+ reconfigure_window(resource.fullscreen, main_win_w, main_win_h, True);
+ else {
+ reconfigure_window(resource.fullscreen, old_w, old_h, True);
+ }
+
+ /* fprintf(stderr, "orig values: %d, %d\n", orig_x, orig_y); */
+ /* restore horizontal scroll position */
+ if (resource.keep_flag && orig_x != 0 && globals.widgets.x_bar != NULL) {
+#ifdef MOTIF
+ if (!resource.fullscreen) {
+ /* only scroll back in non-fullscreen mode. This was:
+ if (resource.fullscreen)
+ (void)set_bar_value(globals.widgets.y_bar, orig_y, (int)(globals.page.h - mane.height));
+ else ...
+ but that was broken (bogus values in globals.page.h - mane.height).
+ */
+ (void)set_bar_value(globals.widgets.x_bar, orig_x, INT_MAX);
+ }
+#else
+ int curr_x;
+ get_xy();
+ XtVaGetValues(globals.widgets.clip_widget, XtNx, &get_x, NULL);
+ curr_x = -(get_x - window_x);
+ if (curr_x - orig_x > 0)
+ XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer)(curr_x - orig_x));
+#endif /* MOTIF */
+ }
+
+ /* restore vertical scroll position */
+ if (resource.keep_flag && orig_y != 0 && globals.widgets.y_bar != NULL) {
+#ifdef MOTIF
+ if (!resource.fullscreen) {
+ /* only scroll back in non-fullscreen mode. This was:
+ if (resource.fullscreen)
+ (void)set_bar_value(globals.widgets.y_bar, orig_y, (int)(globals.page.h - mane.height));
+ else ...
+ but that was broken (bogus values in globals.page.h - mane.height).
+ */
+ (void)set_bar_value(globals.widgets.y_bar, orig_y, INT_MAX);
+ }
+#else
+ int curr_y;
+ get_xy();
+ XtVaGetValues(globals.widgets.clip_widget, XtNy, &get_y, NULL);
+ curr_y = -(get_y - window_y);
+ if (curr_y - orig_y > 0)
+ XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc, (XtPointer)(curr_y - orig_y));
+#endif /* MOTIF */
+ }
+ handle_x_scroll(NULL, NULL, NULL, NULL);
+ handle_y_scroll(NULL, NULL, NULL, NULL);
+}
+
+#ifdef GREY
+
+static void
+Act_set_greyscaling(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int arg;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ warn_num_params("set-greyscaling()", params, *num_params, 1);
+
+ if (!get_int_arg(params, num_params, &arg)) { /* no arg, toggle */
+ if (!toggle_arg(resource.use_grey, params, num_params)) {
+ return;
+ }
+ resource.use_grey = !resource.use_grey;
+ if (resource.use_grey) {
+ statusline_print(STATUS_SHORT, "greyscaling on");
+ }
+ else {
+ statusline_print(STATUS_SHORT, "greyscaling off");
+ }
+ globals.ev.flags |= EV_NEWPAGE;
+ XFlush(DISP);
+ return;
+ }
+
+ switch (arg) {
+ case 0:
+ resource.use_grey = False;
+ statusline_print(STATUS_SHORT, "greyscaling off");
+ break;
+ case 1:
+ resource.use_grey = True;
+ statusline_print(STATUS_SHORT, "greyscaling on");
+ break;
+ default:
+ {
+ float newgamma = arg != 0 ? arg / 100.0 : 1.0;
+ resource.use_grey = newgamma;
+ statusline_print(STATUS_SHORT, "greyscale value: %.1f", newgamma);
+ }
+ }
+
+ /* like elsewhere */
+ if (globals.pausing.flag) {
+ XClearWindow(DISP, mane.win);
+ }
+
+ if (resource.use_grey) {
+ if (G_visual->class != TrueColor)
+ init_plane_masks();
+#if COLOR
+ fg_active = NULL;
+#else
+ init_pix();
+#endif
+ }
+ reset_fonts();
+ globals.ev.flags |= EV_NEWPAGE;
+ XFlush(DISP);
+}
+
+#endif
+
+#if COLOR
+
+void
+do_toggle_color(Boolean update_resource)
+{
+ if (resource.use_color) {
+ resource.use_color = False;
+ full_reset_colors();
+ scanned_page_color = total_pages;
+#if PS
+ if (ignore_papersize_specials || scanned_page_ps <= total_pages) {
+ scanned_page = scanned_page_ps;
+ }
+#endif
+ statusline_print(STATUS_SHORT, "color specials off");
+ }
+ else {
+ resource.use_color = True;
+ scanned_page = scanned_page_color = scanned_page_reset;
+ statusline_print(STATUS_SHORT, "color specials on");
+ }
+ if (update_resource)
+ globals.curr_use_color = resource.use_color;
+
+#ifdef MOTIF
+ update_preferences_color();
+#endif
+ globals.ev.flags |= EV_NEWPAGE;
+}
+
+static void
+Act_set_color(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ UNUSED(w);
+ UNUSED(event);
+
+ if (block_this_mouse_event())
+ return;
+
+ if (!toggle_arg(resource.use_color, params, num_params)) {
+ return;
+ };
+
+ /* like elsewhere */
+ if (globals.pausing.flag) {
+ XClearWindow(DISP, mane.win);
+ }
+
+ do_toggle_color(True);
+}
+
+#endif /* COLOR */
+
+#if PS
+
+void
+Act_set_ps(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int arg;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(event);
+ UNUSED(w);
+
+ if (!get_int_arg(params, num_params, &arg))
+ resource.postscript++;
+ else
+ resource.postscript = arg;
+
+ if (resource.postscript > 2)
+ resource.postscript = 0;
+
+#ifdef PS_GS
+ if (!resource.useGS) {
+ if (resource.postscript > 0) {
+ popup_message(globals.widgets.top_level,
+ MSG_WARN,
+ "This version of xdvi depends on ghostscript for rendering Postscript images. "
+ "Postscript rendering cannot be activated if the option ``-noghostscript'' is used "
+ "or if the resource ``Ghostscript'' is set to false.",
+ /* message */
+ "Option ``-noghostscript'' is active; "
+ "cannot enable Postscript rendering without ghostscript.");
+ }
+ resource.postscript = 0;
+ }
+#endif
+ if (resource.postscript > 0) {
+ scanned_page_ps = scanned_page_ps_bak;
+ if (scanned_page > scanned_page_ps)
+ scanned_page = scanned_page_ps;
+ if (resource.postscript == 1) {
+ statusline_print(STATUS_SHORT, "Postscript rendering on");
+ }
+ else {
+ statusline_print(STATUS_SHORT, "Postscript rendering on (with bounding box)");
+ }
+ }
+ else {
+ scanned_page_ps_bak = scanned_page_ps;
+ scanned_page_ps = total_pages;
+#if COLOR
+ if (ignore_papersize_specials || scanned_page_color <= total_pages) {
+ scanned_page = scanned_page_color;
+ }
+#endif
+ statusline_print(STATUS_SHORT, "Postscript rendering off; displaying bounding box instead");
+ }
+
+ store_preference(NULL, "postscript", "%d", resource.postscript);
+
+ psp.toggle(resource.postscript);
+#if defined(NEW_MENU_CREATION) || defined(MOTIF)
+ set_menu(&resource.postscript, Act_set_ps, check_int);
+#else
+ toggle_menu(resource.postscript, Act_set_ps);
+#endif /* NEW_MENU_CREATION */
+ /* like elsewhere */
+ if (globals.pausing.flag) {
+ XClearWindow(DISP, mane.win);
+ }
+
+ globals.ev.flags |= EV_PS_TOGGLE;
+ XFlush(DISP);
+}
+
+#endif /* PS */
+
+void
+Act_htex_back(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ if (block_this_mouse_event())
+ return;
+
+ htex_back();
+}
+
+void
+Act_htex_forward(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ if (block_this_mouse_event())
+ return;
+
+ htex_forward();
+}
+
+static void
+Act_htex_anchorinfo(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int x, y;
+ Window dummy;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
+ event->xkey.x, event->xkey.y, &x, &y, &dummy);
+ htex_displayanchor(x, y);
+}
+
+#ifdef PS_GS
+void
+Act_set_gs_alpha(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ UNUSED(w);
+ UNUSED(event);
+
+ if (block_this_mouse_event())
+ return;
+
+ if (!toggle_arg(resource.gs_alpha, params, num_params)) {
+ return;
+ }
+ resource.gs_alpha = !resource.gs_alpha;
+
+ if (resource.gs_alpha) {
+ statusline_print(STATUS_SHORT, "ghostscript alpha active");
+ }
+ else {
+ statusline_print(STATUS_SHORT, "ghostscript alpha inactive");
+ }
+
+ store_preference(NULL, "gsAlpha", "%s", resource.gs_alpha ? "True" : "False");
+
+#if defined(NEW_MENU_CREATION) || defined(MOTIF)
+ set_menu(&resource.gs_alpha, Act_set_gs_alpha, check_toggle);
+#else
+ toggle_menu(resource.gs_alpha, Act_set_gs_alpha);
+#endif /* NEW_MENU_CREATION */
+#if GS_PIXMAP_CLEARING_HACK
+ had_ps_specials = False; /* else infinite loop and/or crash in redraw! */
+#endif
+ /* like elsewhere */
+ if (globals.pausing.flag) {
+ XClearWindow(DISP, mane.win);
+ }
+
+ globals.ev.flags |= EV_PS_TOGGLE;
+ XFlush(DISP);
+}
+#endif
+
+static void
+Act_reread_dvi_file(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ if (block_this_mouse_event())
+ return;
+
+/* fprintf(stderr, "reread file!\n"); */
+ globals.ev.flags |= EV_RELOAD;
+}
+
+static void
+Act_discard_number(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ if (block_this_mouse_event())
+ return;
+
+ m_have_arg = False;
+ m_number = 0;
+ m_sign = 1;
+ if (resource.expert_mode & XPRT_SHOW_STATUSLINE) /* too distracting for stdout */
+ statusline_print(STATUS_SHORT, "numerical prefix discarded");
+}
+
+/*
+ * Actions to support dragging the image.
+ */
+
+static int drag_last_x, drag_last_y; /* last position of cursor */
+
+static int text_last_x = -1;
+static int text_last_y = -1;
+static int text_last_page = -1;
+
+static void drag_motion(XEvent *);
+static void drag_release(XEvent *);
+
+/* access for hyperref.c */
+Boolean dragging_text_selection(void)
+{
+ MYTRACE((stderr, "dragging selection? %d", mouse_motion == text_motion));
+ return mouse_motion == text_motion;
+}
+
+static void
+text_release(XEvent * event)
+{
+ int ulx, uly, w, h;
+ static char *text;
+ int text_len = 0;
+
+ UNUSED(event);
+
+ MYTRACE((stderr, "text_release!"));
+
+ if (mouse_motion == null_mouse)
+ return;
+ mouse_motion = mouse_release = null_mouse;
+
+ w = text_last_x - drag_last_x;
+ h = text_last_y - drag_last_y;
+
+ if (w == 0 || h == 0) {
+ text_last_x = text_last_y = -1;
+ drag_last_x = drag_last_y = -1;
+ text_last_page = -1;
+ return;
+ }
+
+ if (w < 0) {
+ ulx = drag_last_x + w;
+ w = -w;
+ }
+ else
+ ulx = drag_last_x;
+
+ if (h < 0) {
+ uly = drag_last_y + h;
+ h = -h;
+ }
+ else
+ uly = drag_last_y;
+
+ free(text);
+ /* this allocates text */
+ text = get_text_selection(&text_len,
+ ulx * currwin.shrinkfactor,
+ uly * currwin.shrinkfactor,
+ (ulx + w) * currwin.shrinkfactor,
+ (uly + h) * currwin.shrinkfactor);
+ if (text[0] == '\0') {
+ /* fprintf(stdout, "Text selection empty.\n"); */
+ return;
+ }
+
+ TRACE_GUI((stderr, "Selected `%s'", text));
+
+ if (text_len > 4 * XMaxRequestSize(DISP) - 32) {
+ MYTRACE((stderr, "selection too large: %d bytes", text_len));
+ XBell(DISP, 10);
+ statusline_print(STATUS_MEDIUM, "Selection too large (%d bytes, maximum %d bytes)",
+ text_len, 4 * XMaxRequestSize(DISP) - 32);
+ MYTRACE((stderr, "after statusline_print1"));
+ return;
+ }
+
+ if (!set_selection(text, globals.widgets.top_level)) {
+ MYTRACE((stderr, "setting selection failed"));
+ XBell(DISP, 10);
+ statusline_print(STATUS_MEDIUM, "Could not set primary selection!");
+ MYTRACE((stderr, "after statusline_print2"));
+ text_change_region(TEXT_SEL_CLEAR, NULL);
+ MYTRACE((stderr, "after text_change_region"));
+ }
+ MYTRACE((stderr, "End of text_release"));
+}
+
+void
+text_motion(XEvent *event)
+{
+ text_change_region(TEXT_SEL_MOVE, event);
+}
+
+static void
+get_rectangle(XRectangle *rect, int x, int y, int last_x, int last_y)
+{
+ rect->width = ABS(x - last_x);
+ rect->height = ABS(y - last_y);
+ rect->x = (x < last_x) ? x : last_x;
+ rect->y = (y < last_y) ? y : last_y;
+}
+
+static void
+crop_to_window(int *x, int *y)
+{
+ if (*x < -currwin.base_x + 1)
+ *x = -currwin.base_x + 1;
+ else if (*x > -currwin.base_x + (int)ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 1)
+ *x = -currwin.base_x + ROUNDUP(pageinfo_get_page_width(current_page), currwin.shrinkfactor) + 1;
+
+ if (*y < -currwin.base_y + 1)
+ *y = -currwin.base_y + 1;
+ else if (*y > -currwin.base_y + (int)ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 1)
+ *y = -currwin.base_y + (int)ROUNDUP(pageinfo_get_page_height(current_page), currwin.shrinkfactor) + 1;
+}
+
+void
+text_change_region(textSelectionT mode, XEvent *event)
+{
+ static GC bboxGC = 0;
+ static GC redrawGC = 0; /* needed since bboxGC is clipped to diff of old and new region */
+
+ MYTRACE((stderr, "text_change_region: %d", mode));
+
+ if (bboxGC == 0) {
+ XGCValues values;
+ unsigned long valuemask;
+
+ values.function = GXinvert; /* as in search code */
+ if (values.function == GXinvert) {
+ valuemask = GCFunction;
+ }
+ else {
+ values.foreground = WhitePixelOfScreen(SCRN) ^ BlackPixelOfScreen(SCRN);
+ /* fprintf(stderr, "foreground: 0x%lx, white pixel: 0x%lx, black pixel: 0x%lx\n", */
+ /* values.foreground, WhitePixelOfScreen(SCRN), BlackPixelOfScreen(SCRN)); */
+ valuemask = GCFunction | GCForeground;
+ }
+ values.line_width = 1;
+ valuemask |= GCLineWidth;
+ bboxGC = XCreateGC(DISP, XtWindow(globals.widgets.top_level), valuemask, &values);
+ redrawGC = XCreateGC(DISP, XtWindow(globals.widgets.top_level), valuemask, &values);
+ }
+
+ MYTRACE((stderr, "text_change_region: after bboxGC"));
+ switch (mode) {
+ case TEXT_SEL_MOVE:
+ {
+ int x, y;
+
+ Window dummy;
+
+ XRectangle redraw = { -1, -1, 0, 0 };
+
+ ASSERT(event != NULL, "event in text_change_region() musn't be NULL for TEXT_SEL_MOVE");
+
+ (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
+ event->xkey.x, event->xkey.y,
+ &x, &y,
+ &dummy);
+
+ crop_to_window(&x, &y);
+
+ get_rectangle(&redraw, x, y, drag_last_x, drag_last_y);
+
+ /*
+ * If we have an old region, we want to clip the GC to the area:
+ *
+ * (clip \cup redraw) - (clip \cap redraw)
+ *
+ * and redraw both rectangle areas; otherwise, just draw the redraw area.
+ */
+
+ if (text_last_x != -1 && text_last_y != -1) {
+
+ XRectangle clip = { -1, -1, 0, 0 };
+
+ Region clip_region = XCreateRegion();
+ Region redraw_region = XCreateRegion();
+ Region union_region = XCreateRegion();
+ Region intersect_region = XCreateRegion();
+
+ get_rectangle(&clip, text_last_x, text_last_y, drag_last_x, drag_last_y);
+
+ XUnionRectWithRegion(&clip, clip_region, clip_region);
+ XUnionRectWithRegion(&redraw, redraw_region, redraw_region);
+
+ XUnionRegion(clip_region, redraw_region, union_region);
+ XIntersectRegion(clip_region, redraw_region, intersect_region);
+
+ XSubtractRegion(union_region, intersect_region, redraw_region);
+
+ XSetRegion(DISP, bboxGC, redraw_region);
+
+ XDestroyRegion(clip_region);
+ XDestroyRegion(redraw_region);
+ XDestroyRegion(union_region);
+ XDestroyRegion(intersect_region);
+
+ XFillRectangle(DISP, mane.win, bboxGC, clip.x, clip.y, clip.width, clip.height);
+ }
+
+ XFillRectangle(DISP, mane.win, bboxGC, redraw.x, redraw.y, redraw.width, redraw.height);
+
+ text_last_x = x;
+ text_last_y = y;
+ text_last_page = current_page;
+ }
+ break;
+ case TEXT_SEL_CLEAR:
+ unset_selection(globals.widgets.top_level);
+ case TEXT_SEL_ERASE:
+ case TEXT_SEL_REDRAW:
+ if (text_last_page == current_page
+ && text_last_x != -1 && text_last_y != -1
+ && drag_last_x != -1 && drag_last_y != -1) {
+
+ XRectangle clear = { -1, -1, 0, 0 };
+
+ get_rectangle(&clear, text_last_x, text_last_y, drag_last_x, drag_last_y);
+
+ if (mode == TEXT_SEL_CLEAR) {
+ text_last_x = text_last_y = drag_last_x = drag_last_y = -1;
+ text_last_page = -1;
+ /* Note ZLB: the region is erased instead of inverted to avoid
+ * multiple inverting problem. An exposure is generated to
+ * make the region redrawn */
+ clearexpose(&mane, clear.x, clear.y, clear.width, clear.height);
+ }
+ else if (clip_region_to_rect(&clear)) {
+ if (mode == TEXT_SEL_ERASE) {
+ /* If width or height are 0, XClearArea will clear entire window
+ * from coordinates x, y, so check for that: */
+ if (clear.width > 0 && clear.height > 0)
+ XClearArea(DISP, mane.win, clear.x, clear.y, clear.width, clear.height, False);
+ }
+ else
+ XFillRectangle(DISP, mane.win, redrawGC, clear.x, clear.y, clear.width, clear.height);
+ }
+ }
+ break;
+ }
+ MYTRACE((stderr, "text_change_region: done"));
+}
+
+void
+text_selection_start(XEvent *event)
+{
+ int x, y;
+ Window dummy;
+
+ /* erase existing region */
+ text_change_region(TEXT_SEL_CLEAR, NULL);
+
+ (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
+ event->xkey.x, event->xkey.y,
+ &x, &y,
+ &dummy); /* throw away last argument */
+
+ crop_to_window(&x, &y);
+
+ drag_last_x = x;
+ drag_last_y = y;
+
+ text_last_x = text_last_y = -1;
+
+ MYTRACE((stderr, "selection start; mouse_release: %p; null: %p!",
+ (void *)mouse_release, (void *)null_mouse));
+ if (mouse_release == null_mouse) {
+ MYTRACE((stderr, "init text_motion!"));
+ mouse_motion = text_motion;
+ mouse_release = text_release;
+ }
+}
+
+/*
+ * If action list for <Btn1Down> does NOT contain a magnifier() binding,
+ * return that action list; else return NULL.
+ */
+static char *
+btn1_no_magnifier_binding(void)
+{
+ static Boolean initialized = False;
+ static char *btn1_binding = NULL;
+
+ if (!initialized) { /* cache value of previous calls */
+ if (resource.main_translations != NULL) {
+ if ((btn1_binding = strstr(resource.main_translations, "<Btn1Down>")) != NULL) {
+ char *eol, *test;
+ btn1_binding = strchr(btn1_binding, ':');
+ if (btn1_binding != NULL)
+ btn1_binding++;
+ while (isspace((int)*btn1_binding))
+ btn1_binding++;
+ eol = strchr(btn1_binding, '\n');
+ if (eol == NULL)
+ eol = btn1_binding + strlen(btn1_binding);
+ if ((test = strstr(btn1_binding, "magnifier(")) == NULL || test > eol) {
+ /* binding not found */
+ return btn1_binding;
+ }
+ }
+ }
+/* fprintf(stderr, "MAG: %d\n", btn1_has_magnifier); */
+ initialized = True;
+ }
+ return btn1_binding;
+}
+
+void
+Act_switch_mode(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ mouseModeT prev_mode = resource.mouse_mode;
+ int arg = -1;
+ char *ptr;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+
+ if ((ptr = btn1_no_magnifier_binding()) != NULL) {
+ XBell(DISP, 0);
+ statusline_print(STATUS_MEDIUM,
+ "Cannot switch modes: Action list for Btn1Down (%s) "
+ "does not contain magnifier() action.", ptr);
+ return;
+ }
+
+/* fprintf(stderr, "prev mode: %d\n", prev_mode); */
+
+ if (get_int_arg(params, num_params, &arg)) {
+ if (arg < MOUSE_MAGNIFIER_MODE || arg >= MOUSE_MAX_MODE) {
+ statusline_print(STATUS_SHORT, "Argument for Act_switch_mode outside of range from %d to %d",
+ MOUSE_MAGNIFIER_MODE, MOUSE_MAX_MODE - 1);
+ resource.mouse_mode++;
+ }
+ else
+ resource.mouse_mode = (mouseModeT)arg;
+ }
+ else
+ resource.mouse_mode++;
+
+ /* check if wrapped */
+ if (resource.mouse_mode >= MOUSE_MAX_MODE)
+ resource.mouse_mode = MOUSE_MAGNIFIER_MODE;
+
+ /* undo effects of previous mode */
+ switch (prev_mode) {
+ case MOUSE_RULER_MODE:
+ clear_ruler();
+ mouse_motion = mouse_release = null_mouse;
+ break;
+ case MOUSE_TEXT_MODE:
+ text_change_region(TEXT_SEL_CLEAR, NULL);
+ mouse_motion = mouse_release = null_mouse;
+ break;
+ default: /* magnifier, nothing to do */
+ break;
+ }
+
+ if (resource.mouse_mode == MOUSE_RULER_MODE) {
+ XDefineCursor(DISP, CURSORWIN, globals.cursor.rule);
+ statusline_print(STATUS_SHORT, "Ruler mode; click Mouse-1 to set ruler");
+ show_ruler(event);
+ show_distance_from_ruler(event, False);
+ }
+ else if (resource.mouse_mode == MOUSE_TEXT_MODE) {
+ statusline_print(STATUS_SHORT, "Text selection mode; click and drag Mouse-1 to select a region");
+ }
+ else { /* default: MOUSE_MAGNIFIER_MODE */
+ statusline_print(STATUS_SHORT, "Magnifier mode; click Mouse-1 to magnify text");
+ mouse_motion = mouse_release = null_mouse;
+ }
+
+ globals.ev.flags |= EV_CURSOR;
+ XFlush(DISP);
+
+#if defined(NEW_MENU_CREATION) || defined(MOTIF)
+ set_menu(&resource.mouse_mode, Act_switch_mode, check_int);
+#else
+ toggle_menu(resource.mouse_mode, Act_switch_mode);
+#endif /* NEW_MENU_CREATION */
+ store_preference(NULL, "mouseMode", "%d", resource.mouse_mode);
+}
+
+static void
+Act_text_mode(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ char *ptr;
+
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ if (block_this_mouse_event())
+ return;
+
+ if ((ptr = btn1_no_magnifier_binding()) != NULL) {
+ XBell(DISP, 0);
+ statusline_print(STATUS_MEDIUM,
+ "Cannot switch modes: Action list for Btn1Down (%s) "
+ "does not contain magnifier() action.", ptr);
+ return;
+ }
+
+ if (resource.mouse_mode == MOUSE_TEXT_MODE) {
+ resource.mouse_mode++;
+ if (resource.mouse_mode >= MOUSE_MAX_MODE)
+ resource.mouse_mode = MOUSE_MAGNIFIER_MODE;
+ }
+ else
+ resource.mouse_mode = MOUSE_TEXT_MODE;
+
+ if (resource.mouse_mode == MOUSE_TEXT_MODE) {
+ if (mouse_release != null_mouse && mouse_release != text_release)
+ return;
+
+ globals.ev.flags |= EV_CURSOR;
+
+ XFlush(DISP);
+ }
+ else {
+ mouse_motion = mouse_release = null_mouse;
+ globals.ev.flags |= EV_CURSOR;
+
+ text_change_region(TEXT_SEL_CLEAR, NULL);
+
+ XFlush(DISP);
+ }
+#if defined(NEW_MENU_CREATION) || defined(MOTIF)
+ set_menu(&resource.mouse_mode, Act_switch_mode, check_int);
+#else
+ toggle_menu(resource.mouse_mode, Act_switch_mode);
+#endif /* NEW_MENU_CREATION || MOTIF */
+}
+
+static void
+Act_drag(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ UNUSED(w);
+
+ if (block_this_mouse_event())
+ return;
+
+ if (mouse_release != null_mouse && mouse_release != drag_release)
+ return;
+
+ if (resource.mouse_mode == MOUSE_TEXT_MODE && text_last_page != -1
+ /* this was:
+ && (text_last_x != drag_last_x || text_last_y != drag_last_y)
+ but that isn't restrictive enough */
+ ) { /* dragging would mess up region drawing */
+ XBell(DISP, 0);
+ statusline_print(STATUS_SHORT, "Dragging page not possible while selection is active");
+ return;
+ }
+
+ if (*num_params != 1) {
+ XDVI_WARNING((stderr, "drag() requires 1 argument (got %d)", *num_params));
+ return;
+ }
+ switch (**params) {
+ case '|':
+ globals.cursor.flags |= CURSOR_DRAG_V;
+ break;
+ case '-':
+ globals.cursor.flags |= CURSOR_DRAG_H;
+ break;
+ case '+':
+ globals.cursor.flags |= CURSOR_DRAG_A;
+ break;
+ default:
+ XDVI_WARNING((stderr, "drag(): Valid arguments are `+', `|' or `-'"));
+ }
+
+ globals.ev.flags |= EV_CURSOR;
+
+ if (mouse_release == null_mouse) {
+ mouse_motion = drag_motion;
+ mouse_release = drag_release;
+ drag_last_x = event->xbutton.x_root;
+ drag_last_y = event->xbutton.y_root;
+ }
+ else
+ drag_motion(event);
+
+ XFlush(DISP);
+}
+
+
+static void
+drag_motion(XEvent * event)
+{
+#ifdef MOTIF
+ get_xy();
+#endif
+
+ if (globals.cursor.flags & (CURSOR_DRAG_H | CURSOR_DRAG_A)) { /* horizontal motion */
+#ifdef MOTIF
+ (void)set_bar_value(globals.widgets.x_bar,
+ drag_last_x - event->xbutton.x_root - window_x,
+ (int)(globals.page.w - mane.width));
+#else
+ if (globals.widgets.x_bar != NULL) {
+ XtCallCallbacks(globals.widgets.x_bar, XtNscrollProc,
+ (XtPointer) (drag_last_x - event->xbutton.x_root));
+ }
+#endif
+ drag_last_x = event->xbutton.x_root;
+ }
+
+ if (globals.cursor.flags & (CURSOR_DRAG_V | CURSOR_DRAG_A)) { /* vertical motion */
+#ifdef MOTIF
+ (void)set_bar_value(globals.widgets.y_bar,
+ drag_last_y - event->xbutton.y_root - window_y,
+ (int)(globals.page.h - mane.height));
+#else
+ if (globals.widgets.y_bar != NULL) {
+ XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc,
+ (XtPointer) (drag_last_y - event->xbutton.y_root));
+ }
+#endif
+ drag_last_y = event->xbutton.y_root;
+ }
+ handle_x_scroll(NULL, NULL, NULL, NULL);
+ handle_y_scroll(NULL, NULL, NULL, NULL);
+}
+
+
+static void
+drag_release(XEvent * event)
+{
+ drag_motion(event);
+ mouse_motion = mouse_release = null_mouse;
+
+ globals.cursor.flags &= ~(CURSOR_DRAG_H | CURSOR_DRAG_V | CURSOR_DRAG_A);
+ globals.ev.flags |= EV_CURSOR;
+ XFlush(DISP);
+}
+
+
+
+/* Wheel mouse support. */
+
+static int wheel_button = -1;
+
+static void
+Act_wheel(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int dist;
+
+ UNUSED(w);
+
+ if (block_this_mouse_event())
+ return;
+
+ if (*num_params != 1) {
+ XDVI_WARNING((stderr, "wheel() requires 1 argument (got %d)", *num_params));
+ return;
+ }
+ dist = (strchr(*params, '.') == NULL) ? atoi(*params)
+ : (int)(my_atof(*params) * resource.wheel_unit);
+#ifdef MOTIF
+ get_xy();
+ set_bar_value(globals.widgets.y_bar, dist - window_y, (int)(globals.page.h - mane.height));
+#else
+ if (globals.widgets.y_bar != NULL)
+ XtCallCallbacks(globals.widgets.y_bar, XtNscrollProc, (XtPointer) dist);
+#endif
+
+ wheel_button = event->xbutton.button;
+
+ handle_y_scroll(NULL, NULL, NULL, NULL);
+}
+
+
+/* Internal mouse actions. */
+
+static void
+Act_motion(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ /* remember last position, to do this only when pointer is actually moved */
+ static int old_x = -1, old_y = -1;
+ int x, y;
+
+ UNUSED(w);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ if (block_this_mouse_event()) {
+ MYTRACE((stderr, "returning!"));
+ return;
+ }
+
+ if (resource.mouse_mode == MOUSE_RULER_MODE) {
+ show_distance_from_ruler(event, False);
+ }
+ /* This used to be:
+ (abs(x - old_x) > x_threshold || abs(y - old_y) > y_threshold))
+ but that didn't work too well either. Just change it whenever user
+ moves the mouse. */
+ if (!MAGNIFIER_ACTIVE && resource.mouse_mode != MOUSE_RULER_MODE
+ && pointerlocate(&x, &y) && (x != old_x || y != old_y)) {
+ htex_displayanchor(x, y);
+ old_x = x;
+ old_y = y;
+ }
+
+ if ((int)(event->xbutton.button) != wheel_button) {
+ mouse_motion(event);
+ }
+}
+
+
+static void
+Act_release(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ UNUSED(w);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ if (block_this_mouse_event())
+ return;
+
+ if ((int)(event->xbutton.button) == wheel_button) {
+ wheel_button = -1;
+ return;
+ }
+
+ mouse_release(event);
+}
+
+void
+Act_ruler_mode(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ char *ptr;
+
+ UNUSED(w);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ if (block_this_mouse_event())
+ return;
+
+ if ((ptr = btn1_no_magnifier_binding()) != NULL) {
+ XBell(DISP, 0);
+ statusline_print(STATUS_MEDIUM,
+ "Cannot switch modes: Action list for Btn1Down (%s) "
+ "does not contain magnifier() action.", ptr);
+ return;
+ }
+
+ if (resource.mouse_mode == MOUSE_RULER_MODE) {
+ resource.mouse_mode++;
+ if (resource.mouse_mode >= MOUSE_MAX_MODE)
+ resource.mouse_mode = MOUSE_MAGNIFIER_MODE;
+ }
+ else
+ resource.mouse_mode = MOUSE_RULER_MODE;
+
+ if (resource.mouse_mode == MOUSE_RULER_MODE) {
+ XDefineCursor(DISP, CURSORWIN, globals.cursor.rule);
+ statusline_print(STATUS_SHORT, "Ruler mode on; use Mouse-1 to set/drag ruler");
+ show_ruler(event);
+ show_distance_from_ruler(event, False);
+ }
+ else {
+ if (globals.cursor.flags & CURSOR_LINK) {
+ XDefineCursor(DISP, CURSORWIN, globals.cursor.link);
+ }
+ else {
+ XDefineCursor(DISP, CURSORWIN, globals.cursor.ready);
+ }
+ statusline_print(STATUS_SHORT, "Ruler mode off");
+ clear_ruler();
+ mouse_motion = mouse_release = null_mouse;
+ }
+#if defined(NEW_MENU_CREATION) || defined(MOTIF)
+ set_menu(&resource.mouse_mode, Act_switch_mode, check_int);
+#else
+ toggle_menu(resource.mouse_mode, Act_switch_mode);
+#endif /* NEW_MENU_CREATION || MOTIF */
+}
+
+
+/* Actions for source specials. */
+
+void
+Act_source_special(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ Window dummy;
+
+ UNUSED(w);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ if (block_this_mouse_event())
+ return;
+
+ if ((event->type == ButtonPress && mouse_release != null_mouse)
+ || MAGNIFIER_ACTIVE) {
+ XBell(DISP, 0);
+ return;
+ }
+
+ source_reverse_x = event->xbutton.x;
+ source_reverse_y = event->xbutton.y;
+ if (event->xbutton.window != mane.win)
+ (void)XTranslateCoordinates(DISP,
+ RootWindowOfScreen(SCRN), mane.win,
+ event->xbutton.x_root,
+ event->xbutton.y_root,
+ &source_reverse_x,
+ &source_reverse_y,
+ &dummy); /* throw away last argument */
+
+ source_reverse_x = (source_reverse_x + mane_base_x) * mane.shrinkfactor;
+ source_reverse_y = (source_reverse_y + mane_base_y) * mane.shrinkfactor;
+
+ globals.ev.flags |= EV_SRC;
+}
+
+void
+Act_show_source_specials(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int arg;
+ Boolean clear_statusline = False;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+
+ if (!get_int_arg(params, num_params, &arg))
+ arg = -1;
+ else
+ clear_statusline = True;
+
+ if ((event->type == ButtonPress && mouse_release != null_mouse)
+ || magnifier.win != (Window) 0) {
+ XBell(DISP, 0);
+ return;
+ }
+
+ if (!(globals.ev.flags & EV_SRC)) {
+ source_reverse_x = -1;
+ source_show_all = (arg == 1 ? True : False);
+
+ globals.ev.flags |= EV_SRC;
+ }
+ if (clear_statusline)
+ statusline_clear();
+}
+
+void
+Act_source_what_special(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int my_x, my_y;
+ Window dummy;
+
+ UNUSED(w);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ if (block_this_mouse_event())
+ return;
+
+ (void)XTranslateCoordinates(DISP, event->xkey.window, mane.win,
+ event->xkey.x, event->xkey.y, &my_x, &my_y, &dummy); /* throw away last argument */
+ my_x = (my_x + mane_base_x) * mane.shrinkfactor;
+ my_y = (my_y + mane_base_y) * mane.shrinkfactor;
+ source_reverse_search(my_x, my_y, False);
+}
+
+static void
+select_cb(const char *filename, void *data)
+{
+ UNUSED(data);
+ if (filename != NULL) {
+ TRACE_FILES((stderr, "new filename: |%s|", filename));
+
+ set_dvi_name_expand(filename);
+ current_page = 0; /* switch to first page */
+ close_old_filep();
+ globals.ev.flags |= EV_NEWDOC;
+ globals.ev.flags |= EV_PAGEHIST_INSERT;
+ }
+}
+
+static void
+Act_select_dvi_file(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ static struct filesel_callback cb; /* static so that we can pass its address */
+ int arg = -1;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (block_this_mouse_event())
+ return;
+
+ if (get_int_arg(params, num_params, &arg)) { /* try item from file history */
+ char *fname;
+ int dummy_page;
+
+ if (arg < 1) {
+ XBell(DISP, 0);
+ statusline_print(STATUS_MEDIUM, "Error: File history number must be >= 1");
+ }
+ else if ((fname = file_history_get_elem(arg - 1, &dummy_page)) == NULL) {
+ statusline_print(STATUS_MEDIUM, "No file number %d in history (history size: %lu)",
+ arg, (unsigned long)file_history_size());
+ }
+ else {
+ file_history_open(fname);
+ }
+ return;
+ }
+
+ cb.title = "Xdvi: Open file";
+ cb.prompt = "Open file:";
+ cb.ok = "OK";
+ cb.cancel = "Cancel";
+ cb.init_path = NULL;
+ cb.filemask = "*.dvi";
+ cb.must_exist = True;
+ cb.exit_on_cancel = False;
+ cb.func_ptr = select_cb;
+ cb.data = NULL;
+
+ file_history_set_page(current_page);
+
+ XsraSelFile(globals.widgets.top_level, &cb);
+}
+
+/*
+ * If all GUI elements have been turned on/off, make this synonymous
+ * with expert mode off/on, so that next `x' keystroke does something
+ * reasonable.
+ */
+void
+update_expert_mode(void)
+{
+ if ((resource.expert_mode & (XPRT_SHOW_STATUSLINE | XPRT_SHOW_SCROLLBARS
+#ifdef MOTIF
+ | XPRT_SHOW_PAGELIST | XPRT_SHOW_TOOLBAR | XPRT_SHOW_MENUBAR
+#else
+ | XPRT_SHOW_BUTTONS
+#endif
+ )) == XPRT_SHOW_ALL) {
+ resource.expert = False;
+ }
+ else if ((resource.expert_mode & (XPRT_SHOW_STATUSLINE
+#ifdef MOTIF
+ | XPRT_SHOW_SCROLLBARS | XPRT_SHOW_PAGELIST
+ | XPRT_SHOW_TOOLBAR | XPRT_SHOW_MENUBAR
+#else
+ | BROKEN_RECONFIG ? 0 : XPRT_SHOW_SCROLLBARS | XPRT_SHOW_BUTTONS
+#endif
+ )) == XPRT_SHOW_NONE) {
+ resource.expert = True;
+ }
+}
+
+void
+Act_set_expert_mode(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ int arg;
+ Boolean clear_statusline = False;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (!get_int_arg(params, num_params, &arg))
+ arg = -1;
+ else
+ clear_statusline = True;
+
+ switch(arg) {
+ case 1:
+ resource.expert_mode ^= XPRT_SHOW_STATUSLINE;
+ toggle_statusline();
+ update_expert_mode();
+ break;
+ case 2:
+#ifndef MOTIF
+ /* show this warning only when not toggling global expert mode
+ (that's why it can't be inside toggle_scrollbars) */
+ if (BROKEN_RECONFIG) {
+ popup_message(globals.widgets.top_level,
+ MSG_WARN,
+ NULL,
+ "Sorry - cannot toggle scrollbars with this X Version.\n"
+ "This version of XFree has a broken implementation of the viewportWidget, "
+ "which would break the layout if the scrollbars are toggled. "
+ "Versions that are known to work have a VendorRelease version below 4000 or above 4002. "
+ "You will need to update your XFree server to fix this.");
+ return;
+ }
+#endif
+ resource.expert_mode ^= XPRT_SHOW_SCROLLBARS;
+ toggle_scrollbars();
+ update_expert_mode();
+ break;
+
+#ifdef MOTIF
+ case 3:
+ resource.expert_mode ^= XPRT_SHOW_PAGELIST;
+ toggle_pagelist();
+ update_expert_mode();
+ break;
+
+ case 4: /* toolbar */
+ resource.expert_mode ^= XPRT_SHOW_TOOLBAR;
+ toggle_toolbar();
+ update_expert_mode();
+ break;
+
+ case 5:
+ resource.expert_mode ^= XPRT_SHOW_MENUBAR;
+ toggle_menubar();
+ update_expert_mode();
+ break;
+#else
+ case 3:
+/* fprintf(stderr, "sidebar\n"); */
+ resource.expert_mode ^= XPRT_SHOW_BUTTONS;
+ toggle_buttons();
+ update_expert_mode();
+ break;
+#endif
+ default:
+ /* warn 'em */
+ if (
+#ifdef MOTIF
+ arg > 5
+#else
+ arg > 3
+#endif
+ ) {
+ statusline_print(STATUS_SHORT, "Number %d too large for `set-expert-mode', using 0 (= toggle) instead.",
+ arg);
+ }
+ /* toggle all items */
+ resource.expert = !resource.expert;
+ if (resource.expert)
+ resource.expert_mode = XPRT_SHOW_NONE;
+ else
+ resource.expert_mode = XPRT_SHOW_ALL;
+
+ toggle_statusline();
+#ifndef MOTIF
+ if (!BROKEN_RECONFIG)
+ toggle_scrollbars();
+#else
+ toggle_scrollbars();
+#endif
+
+#ifdef MOTIF
+ toggle_pagelist();
+ toggle_toolbar();
+ toggle_menubar();
+#else
+ toggle_buttons();
+#endif
+ }
+
+#ifdef MOTIF
+ update_preferences_expert();
+#endif
+
+ store_preference(NULL, "expertMode", "%d", resource.expert_mode);
+
+ if (clear_statusline)
+ statusline_clear();
+}
+
+Boolean have_src_specials = False;
+static Boolean do_update_property = False;
+
+void
+handle_x_scroll(Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
+{
+#ifndef MOTIF
+ Dimension get_x = 0;
+#endif
+
+ UNUSED(w);
+ UNUSED(closure);
+ UNUSED(ev);
+ UNUSED(cont);
+
+ if (/* !resource.keep_flag || */ globals.widgets.x_bar == NULL)
+ return;
+
+#ifdef MOTIF
+ XtVaGetValues(globals.widgets.x_bar, XmNvalue, &m_x_scroll, NULL);
+#else
+ get_xy();
+ XtVaGetValues(globals.widgets.clip_widget, XtNx, &get_x, NULL);
+ m_x_scroll = get_x - window_x;
+ scroll_x_panner(m_x_scroll);
+#endif
+}
+
+void
+handle_y_scroll(Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
+{
+#ifndef MOTIF
+ Dimension get_y = 0;
+#endif
+
+ UNUSED(w);
+ UNUSED(closure);
+ UNUSED(ev);
+ UNUSED(cont);
+
+ if (/* !resource.keep_flag || */ globals.widgets.y_bar == NULL)
+ return;
+
+#ifdef MOTIF
+ XtVaGetValues(globals.widgets.y_bar, XmNvalue, &m_y_scroll, NULL);
+#else
+ get_xy();
+ XtVaGetValues(globals.widgets.clip_widget, XtNy, &get_y, NULL);
+ m_y_scroll = get_y - window_y;
+ scroll_y_panner(m_y_scroll);
+#endif
+}
+
+void
+handle_expose(Widget w, XtPointer closure, XEvent *ev, Boolean *cont)
+{
+ struct WindowRec *windowrec = (struct WindowRec *)closure;
+
+ UNUSED(w);
+ UNUSED(cont);
+
+ if (windowrec == &magnifier) {
+ if (magnifier_stat < 0) { /* destroy upon exposure */
+ magnifier_stat = 0;
+ mag_release(ev);
+ return;
+ }
+ else
+ magnifier_stat = 0;
+ }
+
+ do_update_property = False;
+
+ if (have_src_specials && !MAGNIFIER_ACTIVE) {
+ do_update_property = True;
+ }
+
+ MYTRACE((stderr, "Expose!"));
+ expose(windowrec, (&(ev->xexpose))->x, (&(ev->xexpose))->y,
+ (unsigned int)(&(ev->xexpose))->width, (unsigned int)(&(ev->xexpose))->height);
+}
+
+
+void
+handle_property_change(Widget w, XtPointer junk,
+ XEvent *ev, Boolean *cont)
+{
+ char *prop_ret;
+ size_t prop_len;
+
+ UNUSED(w);
+ UNUSED(junk);
+ UNUSED(cont);
+
+ if ((&(ev->xproperty))->window != XtWindow(globals.widgets.top_level)) /* if spurious event */
+ return;
+
+ if ((&(ev->xproperty))->atom == atom_src_goto()) {
+ /* forward search requested */
+ if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_src_goto(),
+ &prop_ret,
+ XGetWindowProperty)) == 0) {
+ TRACE_CLIENT((stderr, "property_get_data() failed for atom_src_goto()!"));
+ return;
+ }
+ TRACE_CLIENT((stderr, "got back atom_src_goto: |%s|", prop_ret));
+ globals.src.fwd_string = prop_ret;
+ globals.ev.flags |= EV_SRC;
+ }
+ else if ((&(ev->xproperty))->atom == atom_find_string()) {
+ /* string search requested */
+ if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_find_string(),
+ &prop_ret,
+ XGetWindowProperty)) == 0) {
+ TRACE_CLIENT((stderr, "property_get_data() failed for atom_find_string()!"));
+ return;
+ }
+ TRACE_FIND((stderr, "got back atom_find_string: |%s|", prop_ret));
+ resource.find_string = prop_ret;
+ globals.ev.flags |= EV_FIND;
+ }
+ else if ((&(ev->xproperty))->atom == atom_reload()) {
+ /* like do_sigusr(); there's no data in this case. */
+ TRACE_CLIENT((stderr, "atom_reload()"));
+ globals.ev.flags |= EV_RELOAD;
+ }
+ else if ((&(ev->xproperty))->atom == atom_newdoc()) {
+ /* loading a new file */
+ FILE *new_fp;
+ if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_newdoc(),
+ &prop_ret,
+ XGetWindowProperty)) == 0) {
+ TRACE_CLIENT((stderr, "property_get_data() returned zero length for atom_newdoc()"));
+ /* just raise it in this case */
+ XMapRaised(XtDisplay(globals.widgets.top_level), XtWindow(globals.widgets.top_level));
+ raise_message_windows();
+ return;
+ }
+ TRACE_CLIENT((stderr, "got back atom_newdoc: |%s|", prop_ret));
+ if ((new_fp = XFOPEN(prop_ret, "r")) == NULL) {
+ popup_message(globals.widgets.top_level,
+ MSG_ERR, NULL, "Loading %s failed: %s",
+ prop_ret, strerror(errno));
+ return;
+ }
+ set_dvi_name_expand(prop_ret);
+ /*FIXME: Here we would like to insert the first page of the new file
+ into the page history:
+
+ page_history_insert(0);
+
+ However, if the commandline had been:
+ xdvi -unique +5 file.dvi
+ then the following property change will change to page 5 and insert
+ page 5, so in this case two pages will be inserted by the xdvi invocation.
+ I guess to fix this we'd need a bitmask instead of 2 atoms to set both
+ features at the same time ...
+ */
+ globals.ev.flags |= EV_NEWDOC;
+ }
+ else if ((&(ev->xproperty))->atom == atom_newpage()) {
+ /* jumping to a different page */
+ int newpage;
+ char *testptr;
+ if ((prop_len = property_get_data(XtWindow(globals.widgets.top_level), atom_newpage(),
+ &prop_ret,
+ XGetWindowProperty)) == 0) {
+ TRACE_CLIENT((stderr, "property_get_data() failed for atom_newpage(): |%s|", prop_ret));
+ return;
+ }
+ TRACE_CLIENT((stderr, "got back atom_newpage: |%s|", prop_ret));
+ if (strcmp(prop_ret, "+") == 0) { /* special case: treat `+' as last page */
+ newpage = total_pages - 1;
+ }
+ else {
+ newpage = strtol(prop_ret, &testptr, 10) - 1;
+ if (*testptr != '\0') {
+ XDVI_FATAL((stderr, "Invalid page number: `%s'.", prop_ret));
+ }
+ }
+
+ if (newpage == total_pages - 1) { /* as in Act_goto_page() */
+ goto_page(check_goto_page(newpage), resource.keep_flag ? NULL : home, False);
+ search_signal_page_changed();
+ }
+ else {
+ goto_page(check_goto_tex_page(newpage), resource.keep_flag ? NULL : home, False);
+ search_signal_page_changed();
+ }
+ }
+ else if ((&(ev->xproperty))->atom == atom_raise()) {
+ XMapRaised(XtDisplay(globals.widgets.top_level), XtWindow(globals.widgets.top_level));
+ raise_message_windows();
+ }
+ else if ((&(ev->xproperty))->atom == atom_reread_prefs()) {
+ read_user_preferences(globals.widgets.top_level, ".xdvirc.tmp");
+ }
+}
+
+
+/*
+ * Signal routines. At the signal level, all we do is set flags.
+ */
+
+#ifndef FLAKY_SIGPOLL
+static RETSIGTYPE
+handle_sigpoll(int signo)
+{
+ UNUSED(signo);
+
+ globals.ev.ctr = 1;
+ event_freq = -1; /* forget Plan B */
+ sig_flags |= SF_POLL;
+# ifndef HAVE_SIGACTION
+ (void) signal(SIGPOLL, handle_sigpoll); /* reset the signal */
+# endif
+}
+#endif
+
+static RETSIGTYPE
+handle_sigterm(int signo)
+{
+ UNUSED(signo);
+
+ sig_flags |= SF_TERM;
+}
+
+static RETSIGTYPE
+handle_sigchld(int signo)
+{
+ UNUSED(signo);
+
+ sig_flags |= SF_CHLD;
+}
+
+static RETSIGTYPE
+handle_sigalrm(int signo)
+{
+ UNUSED(signo);
+
+ sig_flags |= SF_ALRM;
+}
+
+static RETSIGTYPE
+handle_sigusr(int signo)
+{
+ UNUSED(signo);
+
+ globals.ev.ctr = 1;
+ sig_flags |= SF_USR;
+}
+
+static RETSIGTYPE
+handle_sigsegv(int signo)
+{
+ UNUSED(signo);
+
+ XDVI_ABORT((stderr, "Segmentation fault - trying to clean up and aborting ..."));
+}
+
+static Boolean sigalarm_initialized = False;
+
+void
+setup_sigalarm(void)
+{
+#if HAVE_SIGACTION
+ struct sigaction a;
+
+ a.sa_handler = handle_sigalrm;
+ (void) sigemptyset(&a.sa_mask);
+ (void) sigaddset(&a.sa_mask, SIGALRM);
+ a.sa_flags = 0;
+ sigaction(SIGALRM, &a, NULL);
+#else /* not HAVE_SIGACTION */
+ (void) signal(SIGALRM, handle_sigalrm);
+#endif /* not HAVE_SIGACTION */
+
+ sigalarm_initialized = True;
+}
+
+/*
+ * Called from main to set up the signal handlers.
+ */
+void
+setup_signal_handlers(void)
+{
+#ifndef FLAKY_SIGPOLL
+ int sock_fd = ConnectionNumber(DISP);
+#endif
+#if HAVE_SIGACTION
+ struct sigaction a;
+#endif
+
+#ifndef FLAKY_SIGPOLL
+#if HAVE_SIGACTION
+ /* Subprocess handling, e.g., MakeTeXPK, fails on the Alpha without
+ this, because SIGPOLL interrupts the call of system(3), since OSF/1
+ doesn't retry interrupted wait calls by default. From code by
+ maj@cl.cam.ac.uk. */
+ a.sa_handler = handle_sigpoll;
+ (void) sigemptyset(&a.sa_mask);
+ (void) sigaddset(&a.sa_mask, SIGPOLL);
+ a.sa_flags = SA_RESTART;
+ sigaction(SIGPOLL, &a, NULL);
+#else /* not HAVE_SIGACTION */
+ (void) signal(SIGPOLL, handle_sigpoll);
+#endif /* not HAVE_SIGACTION */
+
+ prep_fd(sock_fd, False);
+#endif /* not FLAKY_SIGPOLL */
+
+#if HAVE_SIGACTION
+ a.sa_handler = handle_sigterm;
+ (void) sigemptyset(&a.sa_mask);
+ (void) sigaddset(&a.sa_mask, SIGINT);
+ (void) sigaddset(&a.sa_mask, SIGQUIT);
+ (void) sigaddset(&a.sa_mask, SIGTERM);
+ (void) sigaddset(&a.sa_mask, SIGHUP);
+ a.sa_flags = SA_RESETHAND;
+ sigaction(SIGINT, &a, NULL);
+ sigaction(SIGQUIT, &a, NULL);
+ sigaction(SIGTERM, &a, NULL);
+ sigaction(SIGHUP, &a, NULL);
+ a.sa_handler = handle_sigsegv;
+ (void)sigemptyset(&a.sa_mask);
+ (void)sigaddset(&a.sa_mask, SIGSEGV);
+ a.sa_flags = 0;
+ sigaction(SIGSEGV, &a, NULL);
+#else /* not HAVE_SIGACTION */
+ (void) signal(SIGINT, handle_sigterm);
+ (void) signal(SIGQUIT, handle_sigterm);
+ (void) signal(SIGTERM, handle_sigterm);
+ (void) signal(SIGHUP, handle_sigterm);
+ (void)signal(SIGSEGV, handle_sigsegv);
+#endif /* not HAVE_SIGACTION */
+
+#if HAVE_SIGACTION
+ a.sa_handler = handle_sigchld;
+ (void) sigemptyset(&a.sa_mask);
+ (void) sigaddset(&a.sa_mask, SIGCHLD);
+ a.sa_flags = 0;
+ sigaction(SIGCHLD, &a, NULL);
+#else /* not HAVE_SIGACTION */
+ (void) signal(SIGCHLD, handle_sigchld);
+#endif /* not HAVE_SIGACTION */
+
+#if HAVE_SIGACTION
+ a.sa_handler = handle_sigusr;
+ (void) sigemptyset(&a.sa_mask);
+ (void) sigaddset(&a.sa_mask, SIGUSR1);
+ a.sa_flags = 0;
+ sigaction(SIGUSR1, &a, NULL);
+#else /* not HAVE_SIGACTION */
+ (void) signal(SIGUSR1, handle_sigusr);
+#endif /* not HAVE_SIGACTION */
+
+ (void)sigemptyset(&all_signals);
+ (void)sigaddset(&all_signals, SIGPOLL);
+ (void)sigaddset(&all_signals, SIGINT);
+ (void)sigaddset(&all_signals, SIGQUIT);
+ (void)sigaddset(&all_signals, SIGTERM);
+ (void)sigaddset(&all_signals, SIGHUP);
+ (void)sigaddset(&all_signals, SIGCHLD);
+ (void)sigaddset(&all_signals, SIGALRM);
+ (void)sigaddset(&all_signals, SIGUSR1);
+ (void)sigaddset(&all_signals, SIGSEGV);
+
+}
+
+
+/*
+ * Mid-level signal handlers. These are called from within read_events(),
+ * and do the actual work appropriate for the given signal.
+ */
+
+/*
+ * Process-related routines. Call set_chld(xchild *) to indicate
+ * that a given child process should be watched for when it
+ * terminates. Call clear_chld() to remove the process from the
+ * list. When the child terminates, the record is removed from
+ * the list and xchild->proc is called.
+ * The caller can set the `io' member in xchild to a pointer to
+ * an xio structure for reading from the file descriptor; see
+ * psgs.c and util.c for examples.
+ */
+
+static struct xchild *child_recs = NULL; /* head of child process list */
+
+void
+set_chld(struct xchild *cp)
+{
+ cp->next = child_recs;
+ child_recs = cp;
+}
+
+void
+clear_chld(struct xchild *cp)
+{
+ struct xchild **cpp;
+ struct xchild *cp2;
+
+ if (child_recs == NULL) {
+ if (globals.debug & DBG_EVENT)
+ fprintf(stderr, "child_recs: %p\n", (void *)child_recs);
+ return;
+ }
+ for (cpp = &child_recs;;) {
+ cp2 = *cpp;
+ if (cp2 == cp)
+ break;
+ cpp = &cp2->next;
+ }
+ *cpp = cp->next;
+}
+
+static void
+do_sigchld(void)
+{
+ pid_t pid;
+ int status;
+
+ sig_flags &= ~SF_CHLD;
+
+#if ! HAVE_SIGACTION
+ (void) signal(SIGCHLD, handle_sigchld); /* reset the signal */
+#endif
+ for (;;) {
+#if HAVE_WAITPID
+ pid = waitpid(-1, &status, WNOHANG);
+#else
+ pid = wait3(&status, WNOHANG, NULL);
+#endif
+ if (pid == 0) break;
+
+ if (pid != -1) {
+ struct xchild **cpp;
+ struct xchild *cp;
+
+ for (cpp = &child_recs;;) {
+ cp = *cpp;
+ if (cp == NULL)
+ break;
+ if (cp->pid == pid) {
+ *cpp = cp->next; /* unlink it */
+ /* call exit reporting procedure for this child */
+ (cp->proc)(status, cp);
+ break;
+ }
+ cpp = &cp->next;
+ }
+ break;
+ }
+
+ if (errno == EINTR) continue;
+ if (errno == ECHILD) break;
+#if HAVE_WAITPID
+ perror("xdvi: waitpid");
+#else
+ perror("xdvi: wait3");
+#endif
+ break;
+ }
+}
+
+
+/*
+ * File-related routines. Call set_io() to indicate that a given fd
+ * should be watched for ability to input or output data. Call clear_io()
+ * to remove it from the list. When poll()/select() indicates that the fd
+ * is available for the indicated type of i/o, the corresponding routine
+ * is called. Call clear_io() to remove an fd from the list.
+ * Both set_io() and clear_io() can be called from within read_proc or
+ * write_proc (although turning an io descriptor on or off is better
+ * accomplished by setting the events flag in the xio structure, and
+ * in the corresponding pollfd structure if the pfd pointer is not NULL
+ * (it is always non-NULL when read_proc and write_proc are called)).
+ * We allocate space for one additional record in the pollfd array, to
+ * accommodate the fd for the X connection; this is done by initializing
+ * num_fds to 1 instead of zero.
+ */
+
+static struct xio *iorecs = NULL; /* head of xio list */
+
+#if HAVE_POLL
+static struct pollfd *fds = NULL;
+static int num_fds = 1; /* current number of fds */
+static int max_fds = 0; /* max allocated number of fds */
+static Boolean io_dirty= True; /* need to recompute fds[] array */
+#else
+static int numfds = 0;
+static fd_set readfds;
+static fd_set writefds;
+#endif
+
+void
+set_io(struct xio *ip)
+{
+ ip->next = iorecs;
+ iorecs = ip;
+
+#if HAVE_POLL
+ ++num_fds;
+ if (!io_dirty && num_fds <= max_fds) {
+ fds[num_fds - 1].fd = ip->fd;
+ fds[num_fds - 1].events = ip->xio_events;
+ ip->pfd = &fds[num_fds - 1];
+ }
+ else {
+ ip->pfd = NULL;
+ io_dirty = True;
+ }
+#else
+ if (numfds <= ip->fd) numfds = ip->fd + 1;
+#endif
+}
+
+void
+clear_io(struct xio *ip)
+{
+ struct xio **ipp;
+
+ for (ipp = &iorecs;;) {
+ struct xio *ip2;
+
+ ip2 = *ipp;
+ if (ip2 == ip) break;
+ ipp = &ip2->next;
+ }
+ *ipp = ip->next;
+
+#if HAVE_POLL
+ --num_fds;
+ io_dirty = True;
+#else
+# if FLAKY_SIGPOLL
+ numfds = ConnectionNumber(DISP);
+# else
+ numfds = (event_freq < 0 ? -1 : ConnectionNumber(DISP));
+# endif
+ for (ip = iorecs; ip != NULL; ip = ip->next)
+ if (ip->fd > numfds)
+ numfds = ip->fd;
+ ++numfds;
+#endif /* !HAVE_POLL */
+}
+
+static void
+do_sigpoll(void)
+{
+#if FLAKY_SIGPOLL
+ sig_flags &= ~SF_POLL;
+#else
+ struct xio *ip;
+
+ sig_flags &= ~SF_POLL;
+
+# if HAVE_POLL
+
+ if (io_dirty) {
+ struct pollfd *fp;
+
+ if (num_fds > max_fds) {
+ if (fds != NULL) free(fds);
+ fds = xmalloc(num_fds * sizeof *fds);
+ max_fds = num_fds;
+ fds->fd = ConnectionNumber(DISP);
+ fds->events = POLLIN;
+ }
+ fp = fds + 1;
+ for (ip = iorecs; ip != NULL; ip = ip->next) {
+ fp->fd = ip->fd;
+ fp->events = ip->xio_events;
+ ip->pfd = fp;
+ ++fp;
+ }
+ io_dirty = False;
+ }
+
+ for (;;) {
+ if (poll(fds + 1, num_fds - 1, 0) >= 0)
+ break;
+
+ if (errno != EAGAIN && errno != EINTR) {
+ perror("xdvi: poll");
+ return;
+ }
+ }
+
+ for (ip = iorecs; ip != NULL; ip = ip->next) {
+ int revents = ip->pfd->revents;
+ if (revents & POLLIN && ip->read_proc != NULL)
+ (void)(ip->read_proc)(ip->fd);
+ if (revents & POLLOUT && ip->write_proc != NULL)
+ (ip->write_proc)(ip->fd);
+ }
+
+# else
+
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ for (ip = iorecs; ip != NULL; ip = ip->next) {
+ if (ip->xio_events & XIO_IN)
+ FD_SET(ip->fd, &readfds);
+ if (ip->xio_events & XIO_OUT)
+ FD_SET(ip->fd, &writefds);
+ }
+
+ for (;;) {
+ struct timeval tv;
+
+ tv.tv_sec = tv.tv_usec = 0;
+ if (select(numfds, &readfds, &writefds, (fd_set *) NULL, &tv) >= 0)
+ break;
+
+ if (errno != EAGAIN && errno != EINTR) {
+ perror("select (xdvi read_events)");
+ return;
+ }
+ }
+
+ for (ip = iorecs; ip != NULL; ip = ip->next) {
+ if (FD_ISSET(ip->fd, &readfds) && ip->read_proc != NULL)
+ (void)(ip->read_proc)(ip->fd);
+ if (FD_ISSET(ip->fd, &writefds) && ip->write_proc != NULL)
+ (ip->write_proc)(ip->fd);
+ }
+
+# endif
+#endif /* not FLAKY_SIGPOLL */
+}
+
+
+/*
+ * Timer-related routines. Call set_timer() to set a timer a given number
+ * of milliseconds in the future. At that time, the timer will be cleared
+ * and the given procedure will be called with argument set to the struct
+ * passed to set_timer(). The timer routine may call set_timer() or
+ * cancel_timer().
+ */
+
+
+static struct xtimer *timers = NULL; /* head of timer list */
+
+static struct itimerval itv = {{0, 0}, {0, 0}};
+
+#ifndef timercmp
+#define timercmp(a, b, OP) (((a)->tv_sec OP (b)->tv_sec || \
+ ((a)->tv_sec == (b)->tv_sec && (a)->tv_usec OP (b)->tv_usec)))
+#endif /* timercmp */
+
+
+static void
+show_timers(char *what)
+{
+ struct xtimer *tp;
+ fprintf(stderr, "=======%s; timers:\n", what);
+ for (tp = timers; tp != NULL; tp = tp->next) {
+ fprintf(stderr, "timer %p: %lu\n", (void *)tp, (unsigned long)tp->when.tv_sec);
+ }
+ fprintf(stderr, "=======\n");
+}
+
+void
+set_timer(struct xtimer *tp, int ms)
+{
+ struct xtimer **tpp;
+ struct xtimer *tp2;
+
+ if (globals.debug & DBG_EVENT)
+ fprintf(stderr, "%s:%d: set_timer\n", __FILE__, __LINE__);
+
+ gettimeofday(&tp->when, NULL);
+ itv.it_value.tv_sec = ms / 1000;
+ itv.it_value.tv_usec = (ms % 1000) * 1000;
+ tp->when.tv_sec += itv.it_value.tv_sec;
+ tp->when.tv_usec += itv.it_value.tv_usec;
+ if (tp->when.tv_usec >= 1000000) {
+ tp->when.tv_usec -= 1000000;
+ ++tp->when.tv_sec;
+ }
+
+ for (tpp = &timers;;) { /* add timer to list */
+ tp2 = *tpp;
+ if (tp2 == NULL || timercmp(&tp->when, &tp2->when, <))
+ break;
+ tpp = &tp2->next;
+ }
+ tp->next = tp2;
+ *tpp = tp;
+
+ if (tpp == &timers) {
+ setitimer(ITIMER_REAL, &itv, NULL);
+ if (ms == 0)
+ sig_flags |= SF_ALRM;
+ }
+ if (globals.debug & DBG_EVENT)
+ show_timers("after set_timer");
+}
+
+void
+cancel_timer(struct xtimer *tp)
+{
+ struct xtimer **tpp;
+
+ if (globals.debug & DBG_EVENT)
+ show_timers("beginning of cancel_timer");
+
+ if (timers == NULL) {
+ fprintf(stderr, "%s:%d: BUG? timers == NULL!\n", __FILE__, __LINE__);
+ return;
+ }
+
+ if (globals.debug & DBG_EVENT)
+ fprintf(stderr, "%s:%d: cancel_timer %p from %p\n", __FILE__, __LINE__, (void *)&timers, (void *)tp);
+
+ ASSERT(timers != NULL, "timers in cancel_timer() mustn't be NULL");
+ for (tpp = &timers; ; ) { /* remove from list */
+
+ if (*tpp == tp)
+ break;
+ tpp = &(*tpp)->next;
+ }
+
+ *tpp = (*tpp)->next; /* unlink it */
+
+ if (timers == NULL) { /* cancel SIGALRM */
+ itv.it_value.tv_sec = itv.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &itv, NULL);
+ }
+}
+
+#if XDVI_XT_TIMER_HACK
+/*
+ * Original comment by Paul:
+ * Newer versions of the Motif toolkit use the timer facility
+ * (XtAppAddTimeOut(), etc.) in the X Toolkit. Proper functioning of
+ * this mechanism, however, requires that the X Toolkit be in charge of
+ * blocking. Since xdvi does its own blocking, this means that we need
+ * to provide working alternatives to these X Toolkit routines (by
+ * redefining them ...).
+ * One symptom of the above-mentioned bug is that the printlog window
+ * eventually stops showing dvips progress until you move the mouse.
+ *
+ * Comment SU:
+ * Xdvik also uses XtAppAddTimeOut() in other places (hyperref/statusline/...),
+ * so we also need these redefinitions also in the Xaw version.
+ *
+ */
+
+static void xt_alarm (struct xtimer *);
+
+static struct xtimer *xt_free_timers = NULL;
+
+XtIntervalId
+XtAddTimeOut(unsigned long interval, XtTimerCallbackProc proc, XtPointer closure)
+{
+ return XtAppAddTimeOut(NULL, interval, proc, closure);
+}
+
+XtIntervalId
+XtAppAddTimeOut(XtAppContext app, unsigned long interval, XtTimerCallbackProc proc, XtPointer closure)
+{
+ struct xtimer *tp;
+
+ UNUSED(app);
+
+ /* FIXME: better way of checking this instead of static boolean sigalarm_initialized?
+ The following doesn't work, even after
+ sigaddset(&all_signals, SIGALRM);
+
+ static sigset_t sig_set;
+ (void)sigprocmask(0, NULL, &sig_set);
+ if (sigismember(&sig_set, SIGALRM))
+ ... OK ...
+ else
+ ... NOT OK ...
+ */
+ ASSERT(sigalarm_initialized, "Shouldn't invoke XtAppAddTimeOut() before setup_sigalarm()");
+
+ if (globals.debug & DBG_EVENT)
+ fprintf(stderr, "XtAppAddTimeOut: %lu msecs\n", interval);
+
+ if (xt_free_timers == NULL)
+ tp = xmalloc(sizeof *tp);
+ else {
+ tp = xt_free_timers;
+ xt_free_timers = xt_free_timers->next;
+ }
+
+ tp->proc = xt_alarm;
+ tp->xt_proc = proc;
+ tp->closure = closure;
+
+ set_timer(tp, interval);
+
+ if (globals.debug & DBG_EVENT)
+ show_timers("XtAppAddTimeOut");
+
+ return (XtIntervalId)tp;
+}
+
+void
+XtRemoveTimeOut(XtIntervalId id)
+{
+ struct xtimer *tp;
+
+ ASSERT(id != 0, "XtIntervalId argument in XtRemoveTimeOut() mustn't be NULL");
+ tp = (struct xtimer *)id;
+
+ /* Motif (2.1 on Solaris 9, 2003) sometimes calls XtRemoveTimeOut() after
+ the timer event has occurred, so we need to be sure not to remove
+ the timer record twice. */
+ if (tp->proc == NULL)
+ return;
+
+ cancel_timer(tp);
+
+ tp->next = xt_free_timers;
+ xt_free_timers = tp;
+
+ if (globals.debug & DBG_EVENT)
+ show_timers("XtRemoveTimeOut");
+}
+
+void
+xt_alarm(struct xtimer *tp)
+{
+ XtIntervalId id;
+
+ tp->proc = NULL; /* flag timer as used-up */
+ id = (XtIntervalId) tp;
+ (tp->xt_proc)(tp->closure, &id);
+
+ tp->next = xt_free_timers;
+ xt_free_timers = tp;
+
+ if (globals.debug & DBG_EVENT)
+ show_timers("xt_alarm");
+}
+#endif /* XDVI_XT_TIMER_HACK */
+
+static void
+do_sigalrm(void)
+{
+ struct timeval now;
+
+ sig_flags &= ~SF_ALRM;
+#ifndef HAVE_SIGACTION
+ (void) signal(SIGALRM, handle_sigalrm); /* reset the signal */
+#endif
+
+ gettimeofday(&now, NULL);
+
+ while (timers != NULL && timercmp(&timers->when, &now, <=)) {
+ struct xtimer *tp = timers;
+ timers = timers->next; /* unlink it _first_ */
+ (tp->proc)(tp);
+ }
+
+ if (timers != NULL) { /* set next timer */
+ int i;
+ itv.it_value.tv_sec = timers->when.tv_sec - now.tv_sec;
+ i = timers->when.tv_usec - now.tv_usec;
+ if (i < 0) {
+ --itv.it_value.tv_sec;
+ i += 1000000;
+ }
+ itv.it_value.tv_usec = i;
+
+ setitimer(ITIMER_REAL, &itv, NULL);
+ }
+}
+
+
+
+/*
+ * Handle SIGUSR1 signal. Pretty straightforward.
+ */
+
+static void
+do_sigusr(void)
+{
+ sig_flags &= ~SF_USR;
+#ifndef HAVE_SIGACTION
+ (void) signal(SIGUSR1, handle_sigusr); /* reset the signal */
+#endif
+ globals.ev.flags |= EV_RELOAD;
+}
+
+
+static void
+do_sigsegv(void)
+{
+ sig_flags &= ~SF_SEGV;
+#ifndef HAVE_SIGACTION
+ (void) signal(SIGSEGV, handle_sigsegv); /* reset the signal */
+#endif
+ handle_sigsegv(SIGSEGV);
+}
+
+
+/*
+ * Handle termination signals. Kill child processes, if permitted.
+ * Otherwise, leave it up to the caller. This is the only place where
+ * the EV_TERM event flag is set, and it can only happen if there's an
+ * active non-killable process. This should only happen if read_events()
+ * is called from one of a very few select locations.
+ */
+
+static void
+do_sigterm(void)
+{
+ struct xchild *cp;
+
+ sig_flags &= ~SF_TERM;
+
+ /* loop through child processes */
+ for (cp = child_recs; cp != NULL; cp = cp->next) {
+ if (cp->killable)
+ kill(cp->pid, SIGKILL);
+ }
+
+ /* SU: Unlike non-k xdvi, we don't care about whether all children have been
+ killed, since processes forked via fork_process() (e.g. the web browser)
+ may still continue running. So we just exit here.
+ */
+ xdvi_exit(EXIT_SUCCESS);
+
+ /* since xdvi_exit() may return, we mustn't do the following which
+ is in non-k xdvi, else we'll end up in a busy loop! I don't know
+ what's meant by `caller' here anyway ...
+
+ globals.ev.flags |= EV_TERM; /\* otherwise, let the caller handle it *\/
+ */
+}
+
+/*
+ * Since redrawing the screen is (potentially) a slow task, xdvi checks
+ * for incoming events while this is occurring. It does not register
+ * a work proc that draws and returns every so often, as the toolkit
+ * documentation suggests. Instead, it checks for events periodically
+ * (or not, if SIGPOLL can be used instead) and processes them in
+ * a subroutine called by the page drawing routine. This routine (below)
+ * checks to see if anything has happened and processes those events and
+ * signals. (Or, if it is called when there is no redrawing that needs
+ * to be done, it blocks until something happens.)
+ *
+ * Ultimately, the goal is to have this be the only place in xdvi where
+ * blocking occurs.
+ *
+ * The argument to this function should be a mask of event types (EV_*)
+ * indicating which event types should cause read_events to return instead
+ * of waiting for more events. This function will always process all
+ * pending events and signals before returning.
+ * The return value is the value of globals.ev.flags.
+ */
+
+unsigned int
+read_events(unsigned int ret_mask)
+{
+ XEvent event;
+
+#if !HAVE_POLL
+ if (numfds == 0)
+ numfds = ConnectionNumber(DISP) + 1;
+#endif
+
+ if (globals.debug & DBG_EVENT)
+ fprintf(stderr, "%s:%d: read_events %u\n", __FILE__, __LINE__, ret_mask);
+ for (;;) {
+ globals.ev.ctr = event_freq;
+ /*
+ * The above line clears the flag indicating that an event is
+ * pending. So if an event comes in right now, the flag will be
+ * set again needlessly, but we just end up making an extra call.
+ * Also, be careful about destroying the magnifying glass while
+ * drawing on it.
+ */
+
+#if !FLAKY_SIGPOLL
+
+ if (event_freq < 0) { /* if SIGPOLL works */
+ if (!XtPending()) {
+ sigset_t oldsig;
+
+ (void) sigprocmask(SIG_BLOCK, &all_signals, &oldsig);
+ for (;;) {
+#ifdef SHOW_SIG_FLAGS
+ /* this gives HUGE output ... */
+ if (globals.debug & DBG_EVENT)
+ fprintf(stderr, "%s:%d: sig_flags = %d\n",
+ __FILE__, __LINE__, sig_flags);
+#endif
+ while (sig_flags) {
+ flags_to_sigproc[sig_flags]();
+ }
+
+ if (XtPending())
+ break;
+
+ if (globals.ev.flags & ret_mask) {
+ (void) sigprocmask(SIG_SETMASK, &oldsig, (sigset_t *) NULL);
+ return globals.ev.flags;
+ }
+ (void) sigsuspend(&oldsig);
+ }
+ (void) sigprocmask(SIG_SETMASK, &oldsig, (sigset_t *) NULL);
+ }
+ }
+ else
+
+#endif /* not FLAKY_SIGPOLL */
+
+ {
+ for (;;) {
+ struct xio *ip;
+
+ if (globals.debug & DBG_EVENT)
+ fprintf(stderr, "%s:%d: (flaky) sig_flags = %d\n",
+ __FILE__, __LINE__, sig_flags);
+ while (sig_flags) {
+ sigset_t oldsig;
+
+ (void) sigprocmask(SIG_BLOCK, &all_signals, &oldsig);
+
+ while (sig_flags) {
+ flags_to_sigproc[sig_flags]();
+ }
+
+ (void) sigprocmask(SIG_SETMASK, &oldsig,
+ (sigset_t *) NULL);
+ }
+
+ if (XtPending())
+ break;
+
+ if (globals.ev.flags & ret_mask)
+ return globals.ev.flags;
+
+ /* If a SIGUSR1 signal comes right now, then it will wait
+ until an X event or another SIGUSR1 signal arrives. */
+
+#if HAVE_POLL
+ if (globals.debug & DBG_EVENT)
+ fprintf(stderr, "%s:%d: have_poll!\n",
+ __FILE__, __LINE__);
+ if (io_dirty) {
+ struct pollfd *fp;
+
+ if (num_fds > max_fds) {
+ if (fds != NULL) free(fds);
+ fds = xmalloc(num_fds * sizeof *fds);
+ max_fds = num_fds;
+ fds->fd = ConnectionNumber(DISP);
+ fds->events = POLLIN;
+ }
+ fp = fds + 1;
+ for (ip = iorecs; ip != NULL; ip = ip->next) {
+ fp->fd = ip->fd;
+ fp->events = ip->xio_events;
+ ip->pfd = fp;
+ ++fp;
+ }
+ io_dirty = False;
+ }
+
+ for (;;) {
+ if (poll(fds, num_fds, -1) >= 0) {
+ for (ip = iorecs; ip != NULL; ip = ip->next) {
+ int revents = ip->pfd->revents;
+
+ if (revents & POLLIN && ip->read_proc != NULL)
+ (ip->read_proc)(ip->fd);
+ if (revents & POLLOUT && ip->write_proc != NULL)
+ (ip->write_proc)(ip->fd);
+ }
+ break;
+ }
+
+ if (errno == EINTR)
+ break;
+
+ if (errno != EAGAIN) {
+ perror("xdvi: poll");
+ break;
+ }
+ }
+#else /* HAVE_POLL */
+ if (globals.debug & DBG_EVENT)
+ fprintf(stderr, "%s:%d: NOT have_poll!\n",
+ __FILE__, __LINE__);
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ FD_SET(ConnectionNumber(DISP), &readfds);
+ for (ip = iorecs; ip != NULL; ip = ip->next) {
+ if (ip->xio_events & XIO_IN)
+ FD_SET(ip->fd, &readfds);
+ if (ip->xio_events & XIO_OUT)
+ FD_SET(ip->fd, &writefds);
+ }
+
+ for (;;) {
+ if (select(numfds, &readfds, &writefds, (fd_set *) NULL,
+ (struct timeval *) NULL) >= 0) {
+ for (ip = iorecs; ip != NULL; ip = ip->next) {
+ if (FD_ISSET(ip->fd, &readfds) && ip->read_proc != NULL) {
+ if (globals.debug & DBG_EVENT)
+ fprintf(stderr, "%s:%d: reading from %d\n",
+ __FILE__, __LINE__, ip->fd);
+ (ip->read_proc)(ip->fd);
+ }
+ if (FD_ISSET(ip->fd, &writefds) && ip->write_proc != NULL) {
+ if (globals.debug & DBG_EVENT)
+ fprintf(stderr, "%s:%d: writing to %d\n",
+ __FILE__, __LINE__, ip->fd);
+ (ip->write_proc)(ip->fd);
+ }
+ }
+ break;
+ }
+
+ if (errno == EINTR)
+ break;
+
+ if (errno != EAGAIN) {
+ perror("xdvi: select");
+ break;
+ }
+ }
+#endif /* HAVE_POLL */
+ }
+ }
+ XtAppNextEvent(app, &event);
+#ifdef MOTIF
+ if ((resource.expert_mode & XPRT_SHOW_TOOLBAR) != 0)
+ TipAppHandle(app, &event);
+#endif
+
+ if (resized)
+ get_geom();
+
+ if (event.xany.window == magnifier.win && event.type == Expose) {
+ handle_expose((Widget) NULL, (XtPointer) &magnifier, &event,
+ (Boolean *) NULL);
+ continue;
+ }
+ else if (globals.broken_motif_event_handling &&
+ (resource.mouse_mode == MOUSE_RULER_MODE ||
+ resource.mouse_mode == MOUSE_TEXT_MODE)) {
+ /* In this case, Act_motion() and Act_release() are not called properly
+ * for updating the ruler/text selection (it works with the magnifier though),
+ * so we need to invoke them ourselves here: */
+ if (event.type == MotionNotify)
+ Act_motion(NULL, &event, NULL, NULL);
+ else if (event.type == ButtonRelease)
+ Act_release(NULL, &event, NULL, NULL);
+ }
+
+#ifdef MOTIF
+ if (XtIsRealized(globals.widgets.top_level)
+ && event.xany.window == XtWindow(globals.widgets.clip_widget)
+ && event.type == KeyPress) { /* workaround for #610206 */
+ motif_translations_hack();
+ }
+#else
+ if (resource.expert_mode & XPRT_SHOW_BUTTONS)
+ SubMenuHandleEvent(app, &event);
+#endif
+ XtDispatchEvent(&event);
+ }
+}
+
+
+/*
+ * Higher-level routines for managing events.
+ */
+
+static void
+can_exposures(struct WindowRec *windowrec)
+{
+ windowrec->min_x = windowrec->min_y = MAXDIM;
+ windowrec->max_x = windowrec->max_y = 0;
+}
+
+void
+redraw(struct WindowRec *windowrec)
+{
+ currwin = *windowrec;
+ globals.win_expose.min_x = currwin.min_x + currwin.base_x;
+ globals.win_expose.min_y = currwin.min_y + currwin.base_y;
+ globals.win_expose.max_x = currwin.max_x + currwin.base_x;
+ globals.win_expose.max_y = currwin.max_y + currwin.base_y;
+ can_exposures(windowrec);
+
+ /* fix for bug #619070 - the complicated flags (and do_update_property)
+ are needed to avoid too many updates at exposures, especially for
+ a window of another xdvi instance when the magnifier intersects with
+ that window.
+ */
+ if (have_src_specials && do_update_property
+ && globals.win_expose.min_x != 1 && globals.win_expose.max_y - globals.win_expose.min_y != 1
+ && currwin.base_x == 0 && currwin.base_y == 0) {
+ update_window_property(XtWindow(globals.widgets.top_level), True);
+ }
+
+ TRACE_EVENTS((stderr, "Redraw %d x %d at (%d, %d) (base=%d,%d)",
+ globals.win_expose.max_x - globals.win_expose.min_x,
+ globals.win_expose.max_y - globals.win_expose.min_y,
+ globals.win_expose.min_x, globals.win_expose.min_y,
+ currwin.base_x, currwin.base_y));
+
+ /* can't use ev_cursor here, since the event loop might not see this change quick enough */
+ if (!(globals.ev.flags & EV_CURSOR)) {
+ TRACE_EVENTS((stderr, "Cursor: %ld", globals.cursor.flags));
+ if (!(globals.cursor.flags & (CURSOR_DRAG_H | CURSOR_DRAG_V | CURSOR_DRAG_A))) {
+ if (resource.mouse_mode == MOUSE_RULER_MODE)
+ XDefineCursor(DISP, CURSORWIN, globals.cursor.rule);
+ else
+ XDefineCursor(DISP, CURSORWIN, globals.cursor.wait);
+ XFlush(DISP);
+ }
+ globals.ev.flags |= EV_CURSOR;
+ }
+
+ /* No longer needed since windows are correctly transient now */
+ /* raise_message_windows(); */
+ raise_file_selector();
+ draw_page();
+ globals.warn_spec_now = False;
+}
+
+
+void
+redraw_page(void)
+{
+#if COLOR
+ const struct rgb *rgbp;
+#endif
+ TRACE_FILES((stderr, "Redraw page on %p", (void *)globals.dvi_file.bak_fp));
+
+ if (globals.dvi_file.bak_fp == NULL)
+ return;
+
+ if (scanned_page < current_page) {
+ TRACE_FILES((stderr, "redraw_page: scanned_page = %d, current_page = %d, prescanning %p\n",
+ scanned_page, current_page, (void *)globals.dvi_file.bak_fp));
+
+ prescan(globals.dvi_file.bak_fp);
+
+ if (globals.ev.flags & EV_GE_NEWPAGE) { /* if we need to re-prescan */
+ return;
+ }
+ }
+
+ TRACE_FILES((stderr, "redraw_page: current_page = %d", current_page));
+ if (pageinfo_get_window_width(current_page) != globals.page.unshrunk_w
+ || pageinfo_get_window_height(current_page) != globals.page.unshrunk_h) {
+ TRACE_FILES((stderr, "NEW SIZE: %dx%d",
+ pageinfo_get_window_width(current_page), pageinfo_get_window_height(current_page)));
+ init_page();
+ reconfig();
+ }
+
+ /* We can't call home() without proper unshrunk_page_*, which requires
+ * prescan(), which can't be done from within read_events() */
+
+ /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BUG ALERT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ There's some complicated interaction with Postscript specials
+ here: if home_action check comes before the gs stuff, psp.drawfile
+ might not get initialized correctly, resulting in empty PS figures
+ (bounding box instead of figure). This is different in xdvi, due to
+ different handling of the home_action stuff, but at the moment I can't
+ remember the reason for this ...
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BUG ALERT !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ */
+#if PS_GS
+ if (gs_postpone_prescan) {
+ if (!setjmp(globals.ev.canit)) {
+ gs_resume_prescan();
+ }
+ else
+ return;
+ }
+#endif
+ if (home_action != NULL) {
+ home_action(False);
+ home_action = NULL;
+ /* This discards the expose event generated by home()
+ (1 for each page) */
+ if (read_events(EV_NOWAIT) & EV_GE_NEWPAGE) {
+ return;
+ }
+
+ can_exposures(&mane);
+ }
+
+#if COLOR
+ rgbp = &bg_initial;
+ if (page_colors.stack != NULL) {
+ ASSERT(current_page < (int)page_colors.size, "page_colors.size too small");
+ rgbp = &page_colors.stack[current_page].bg;
+ }
+
+ /* Set background color */
+ if (bg_current == NULL
+ || rgbp->r != bg_current->color.r
+ || rgbp->g != bg_current->color.g
+ || rgbp->b != bg_current->color.b) {
+ struct bgrec **bgpp;
+
+ for (bgpp = &bg_head;;) {
+ bg_current = *bgpp;
+ if (bg_current == NULL) { /* if bg is not in list */
+ bg_current = *bgpp = xmalloc(sizeof *bg_current);
+ bg_current->next = NULL;
+ bg_current->color = *rgbp;
+ bg_current->fg_head = NULL;
+ bg_current->pixel_good = False;
+ break;
+ }
+ if (bg_current->color.r == rgbp->r
+ && bg_current->color.g == rgbp->g
+ && bg_current->color.b == rgbp->b)
+ break;
+ bgpp = &bg_current->next;
+ }
+ fg_current = NULL; /* force change of foreground color */
+ /* globals.gc.high is only used in XDrawRectangle, so its background color
+ doesn't need to be changed. */
+ if (globals.debug & DBG_DVI)
+ printf("Changing background color to %5d %5d %5d\n",
+ bg_current->color.r, bg_current->color.g,
+ bg_current->color.b);
+
+ if (!bg_current->pixel_good) {
+ bg_current->pixel = alloc_color(&bg_current->color,
+ color_data[1].pixel);
+ bg_current->pixel_good = True;
+ }
+ XSetWindowBackground(DISP, mane.win, bg_current->pixel);
+#if MOTIF && !FIXED_FLUSHING_PAGING
+ XtVaSetValues(XtParent(globals.widgets.draw_widget), XtNbackground, bg_current->pixel, NULL);
+#endif
+/* XSetWindowBackground(DISP, mane.win, bg_current->pixel); */
+/* XClearWindow(DISP, mane.win); */
+#if 0 /* don't recolor the cursor - gives too low contrast on color backgrounds,
+ and bad appearance when part of the background is white and cursor mask
+ is colored */
+ {
+ XColor bg_Color;
+ bg_Color.pixel = bg_current->pixel;
+ XQueryColor(DISP, G_colormap, &bg_Color);
+ XRecolorCursor(DISP, globals.cursor.ready, &globals.cr_color, &bg_Color);
+ XRecolorCursor(DISP, globals.cursor.wait, &globals.cr_color, &bg_Color);
+ }
+#endif
+ }
+#endif /* COLOR */
+
+ if (!globals.pausing.flag) {
+ XClearWindow(DISP, mane.win);
+ }
+
+ if (G_backing_store != NotUseful) {
+ mane.min_x = mane.min_y = 0;
+ mane.max_x = globals.page.w;
+ mane.max_y = globals.page.h;
+ }
+ else {
+ get_xy();
+ mane.min_x = -window_x;
+ mane.max_x = -window_x + mane.width;
+ mane.min_y = -window_y;
+ mane.max_y = -window_y + mane.height;
+ }
+
+/* update_TOC(); */
+ redraw(&mane);
+}
+
+void
+do_pages(void)
+{
+ if (globals.debug & DBG_BATCH) {
+
+ (void)read_events(EV_GT_IDLE);
+ for (current_page = 0; current_page < total_pages; ++current_page) {
+ if (resource.keep_flag) {
+ home_action = NULL;
+ }
+ else {
+ home_action = home;
+ }
+ globals.warn_spec_now = resource.warn_spec;
+#if PS_GS
+ for (;;) {
+ redraw_page();
+ (void) read_events(EV_NOWAIT);
+ if (!(globals.ev.flags & (EV_NEWPAGE | EV_NEWDOC | EV_RELOAD)))
+ break;
+ globals.ev.flags = EV_IDLE;
+ }
+#else
+ redraw_page();
+#endif
+ }
+ xdvi_exit(EXIT_SUCCESS);
+ }
+ else {
+ for (;;) { /* normal operation */
+ (void) read_events(EV_GT_IDLE);
+ TRACE_EVENTS((stderr, "globals.ev.flags: %d; ev_newpage: %d, ev_newdoc: %d, ev_reload: %d\n",
+ globals.ev.flags, EV_NEWPAGE, EV_NEWDOC, EV_RELOAD));
+ /* NOTE: reloading must be checked first! */
+ if (globals.ev.flags & (EV_NEWPAGE | EV_NEWDOC | EV_RELOAD | EV_PS_TOGGLE)) {
+ TRACE_EVENTS((stderr, "EV_NEWPAGE | ..."));
+ globals.ev.flags &= ~(EV_NEWPAGE | EV_EXPOSE | EV_PS_TOGGLE);
+ if (globals.ev.flags & EV_RELOAD) {
+ dviErrFlagT errflag;
+
+ globals.ev.flags &= ~EV_RELOAD;
+ if (load_dvi_file(True, &errflag)) {
+#if PS
+ ps_clear_cache();
+#if PS_GS
+ if (resource.gs_alpha) {
+ /* restart gs so that user has a method for fixing GS artifacts with gs_alpha
+ by using `reload' (see also GS_PIXMAP_CLEARING_HACK) */
+ ps_destroy();
+ }
+#endif
+#endif
+ statusline_print(STATUS_SHORT, "File reloaded.");
+ }
+/* else { */
+/* statusline_print(STATUS_MEDIUM, "File corrupted, not reloading."); */
+/* } */
+ }
+ if (globals.ev.flags & EV_NEWDOC) {
+ dviErrFlagT errflag;
+ TRACE_EVENTS((stderr, "EV_NEWDOC!"));
+/* fprintf(stderr, "newdoc!\n"); */
+ TRACE_FILES((stderr, "current page: %d", current_page));
+/* file_history_set_page(current_page); */
+ globals.ev.flags &= ~EV_NEWDOC;
+ if (load_dvi_file(True, &errflag)) {
+ statusline_append(STATUS_SHORT, "Opened ", "Opened \"%s\"", globals.dvi_name);
+/* statusline_print(STATUS_SHORT, "Opened \"%s\"", globals.dvi_name); */
+ TRACE_FILES((stderr, "Adding to history: |%s|\n", globals.dvi_name));
+ if (file_history_push(globals.dvi_name)) { /* it's a new file, add to history */
+ TRACE_FILES((stderr, "New entry!"));
+ filehist_menu_add_entry(globals.dvi_name);
+ }
+ else { /* only need to move existing elements to new positions */
+ TRACE_FILES((stderr, "Existing entry!\n"));
+ filehist_menu_refresh();
+ }
+ }
+ }
+
+ can_exposures(&mane);
+ can_exposures(&magnifier);
+
+#if PS && PS_GS && GS_PIXMAP_CLEARING_HACK
+ if (had_ps_specials && !MAGNIFIER_ACTIVE) {
+ erasepage_gs();
+ had_ps_specials = False;
+ }
+#endif /* PS && PS_GS && GS_PIXMAP_CLEARING_HACK */
+
+ if (globals.dvi_file.bak_fp != NULL) {
+ TRACE_EVENTS((stderr, "redraw_page()"));
+ redraw_page();
+ }
+ else {
+ TRACE_EVENTS((stderr, "dvi_file_changed()"));
+ (void)dvi_file_changed();
+ }
+ }
+ else if (globals.ev.flags & EV_FILEHIST_GOTO_PAGE) {
+ int pageno;
+ globals.ev.flags &= ~EV_FILEHIST_GOTO_PAGE;
+ pageno = check_goto_page(file_history_get_page());
+ goto_page(pageno, resource.keep_flag ? NULL : home, False);
+ TRACE_FILES((stderr, "got page: %d", pageno));
+ }
+ else if (globals.ev.flags & EV_PAGEHIST_INSERT) {
+ globals.ev.flags &= ~EV_PAGEHIST_INSERT;
+ page_history_insert(current_page);
+ }
+ else if (globals.ev.flags & EV_FIND_CANCEL) {
+ /* NOTE: This must be done before checking for expose() */
+ globals.ev.flags &= ~EV_FIND_CANCEL;
+ }
+ else if (globals.ev.flags & EV_ANCHOR) {
+ /*
+ * Similar to forward search: search for a htex anchor.
+ * This needs to come before the next case which does the redraw_page(),
+ * otherwise anchors for the current page might not be drawn at all:
+ * anchor_search() sets the info later used by htex_draw_anchormarkers(),
+ * which is invoked by redraw_page().
+ */
+
+ /* switch off the link cursor */
+ globals.cursor.flags &= ~CURSOR_LINK;
+
+ if (dvi_file_changed())
+ continue;
+
+ anchor_search(g_anchor_pos);
+ globals.ev.flags &= ~EV_ANCHOR;
+ }
+ else if (globals.ev.flags & EV_SRC) {
+ /*
+ * Source special operations are deferred to here because
+ * they call geom_scan(), which may call define_font(),
+ * which may call makefont(), which may call read_events()
+ * recursively.
+ */
+ if (globals.src.fwd_string != NULL) {
+ const char *s = globals.src.fwd_string;
+
+ if (dvi_file_changed())
+ continue;
+
+ source_forward_search(s);
+ globals.ev.flags &= ~EV_SRC;
+ globals.src.fwd_string = NULL;
+
+ /* de-iconify window if needed, and raise it */
+ XMapRaised(XtDisplay(globals.widgets.top_level), XtWindow(globals.widgets.top_level));
+ raise_message_windows();
+ }
+ else if (source_reverse_x != -1) {
+ if (dvi_file_changed())
+ continue;
+
+ source_reverse_search(source_reverse_x, source_reverse_y, True);
+ globals.ev.flags &= ~EV_SRC;
+ }
+ else {
+ source_special_show(source_show_all);
+ globals.ev.flags &= ~EV_SRC;
+ }
+ }
+ /* support for `-findstring' */
+ else if (globals.ev.flags & EV_FIND) {
+ if (dvi_file_changed())
+ continue;
+
+ if (resource.find_string != NULL) { /* not first call */
+ dvi_find_string(resource.find_string, False);
+ resource.find_string = NULL;
+ }
+ else { /* actually should never arrive here?? */
+ dvi_find_string(NULL, True);
+ }
+ globals.ev.flags &= ~EV_FIND;
+ }
+ else if (globals.ev.flags & EV_MAG_MOVE) {
+ MYTRACE((stderr, "moving mag!"));
+ move_magnifier();
+ }
+ else if (globals.ev.flags & EV_EXPOSE) {
+ if (magnifier.min_x < MAXDIM) {
+/* fprintf(stderr, "magnifier < maxdim!\n"); */
+ if (mane.min_x >= MAXDIM) {
+/* fprintf(stderr, "mane >= maxdim!\n"); */
+ globals.ev.flags &= ~EV_EXPOSE;
+ }
+ redraw(&magnifier);
+ }
+ else {
+ /* see comment in mag.c */
+ globals.ev.flags &= ~EV_EXPOSE;
+ if (mane.min_x < MAXDIM) {
+ redraw(&mane);
+ }
+ }
+ }
+ else if (globals.ev.flags & EV_CURSOR) {
+ /*
+ * This code eliminates unnecessary calls to XDefineCursor,
+ * since this is a slow operation on some hardware (e.g., S3
+ * chips).
+ */
+ XSync(DISP, False);
+ if (!XtPending()) {
+ Cursor curr;
+
+ if (globals.cursor.flags & CURSOR_DRAG_V)
+ curr = globals.cursor.drag_v;
+ else if (globals.cursor.flags & CURSOR_DRAG_H)
+ curr = globals.cursor.drag_h;
+ else if (globals.cursor.flags & CURSOR_DRAG_A)
+ curr = globals.cursor.drag_a;
+ else if (resource.mouse_mode == MOUSE_RULER_MODE)
+ curr = globals.cursor.rule;
+ else if (resource.mouse_mode == MOUSE_TEXT_MODE && !(globals.cursor.flags & CURSOR_LINK))
+ curr = globals.cursor.text;
+ else if (globals.cursor.flags & CURSOR_LINK)
+ curr = globals.cursor.link;
+ else if (globals.cursor.flags & CURSOR_MAG)
+ curr = globals.cursor.mag;
+ else if (globals.cursor.flags & CURSOR_CORRUPTED)
+ curr = globals.cursor.corrupted;
+ else if (globals.pausing.flag)
+ curr = globals.cursor.pause;
+ else
+ curr = globals.cursor.ready;
+ XDefineCursor(DISP, CURSORWIN, curr);
+ globals.ev.flags &= ~EV_CURSOR;
+ }
+ }
+ XFlush(DISP);
+ }
+ }
+}
+
+static void
+Act_unpause_or_next(Widget w, XEvent *event,
+ String *params, Cardinal *num_params)
+{
+ if (block_this_mouse_event())
+ return;
+
+ if (globals.pausing.flag) {
+ globals.pausing.num++;
+ if (globals.pausing.num_save)
+ globals.pausing.num_save[current_page] = globals.pausing.num;
+ redraw_page();
+ }
+ else {
+ Act_down_or_next(w, event, params, num_params);
+ }
+}
+
+/*
+ * timer callback for watching the DVI file.
+ */
+void
+watch_file_cb(XtPointer client_data, XtIntervalId *id)
+{
+ static XtIntervalId timer = 0;
+
+ UNUSED(client_data);
+ UNUSED(id);
+
+ if (resource.watch_file > 0.0) {
+ unsigned long watch_time_ms;
+
+ (void)dvi_file_changed();
+
+ if (timer) {
+ XtRemoveTimeOut(timer);
+ timer = (XtIntervalId)(XtIntervalId)0;
+ }
+
+ watch_time_ms = (unsigned long)(resource.watch_file * 1000);
+ timer = XtAppAddTimeOut(app, watch_time_ms, watch_file_cb, (XtPointer)NULL);
+ }
+}
+
+void Act_pagehistory_back(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ int arg;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (!get_int_arg(params, num_params, &arg)) {
+ arg = 1;
+ }
+ page_history_move(-arg);
+}
+
+void Act_pagehistory_forward(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ int arg;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (!get_int_arg(params, num_params, &arg)) {
+ arg = 1;
+ }
+ page_history_move(arg);
+}
+
+void Act_pagehistory_clear(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ UNUSED(w);
+ UNUSED(event);
+ UNUSED(params);
+ UNUSED(num_params);
+
+ if (block_this_mouse_event())
+ return;
+
+ page_history_clear();
+}
+
+void Act_pagehistory_delete_backward(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ int arg;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (!get_int_arg(params, num_params, &arg)) {
+ arg = 1;
+ }
+ page_history_delete(-arg);
+}
+
+void Act_pagehistory_delete_forward(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ int arg;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (!get_int_arg(params, num_params, &arg)) {
+ arg = 1;
+ }
+ page_history_delete(arg);
+}
+
+#ifdef MOTIF
+void Act_prefs_dialog(Widget w, XEvent *event, String *params, Cardinal *num_params)
+{
+ int arg;
+
+ if (block_this_mouse_event())
+ return;
+
+ UNUSED(w);
+ UNUSED(event);
+
+ if (!get_int_arg(params, num_params, &arg)) {
+ arg = -1;
+ }
+
+ popup_preferences_dialog(globals.widgets.top_level, arg);
+}
+#endif /* MOTIF */
+