/* * misc.c -- Everything that isn't a callback or action. * Copyright (C) 1992 Timothy O. Theisen * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * Author: Tim Theisen Systems Programmer * Internet: tim@cs.wisc.edu Department of Computer Sciences * UUCP: uwvax!tim University of Wisconsin-Madison * Phone: (608)262-0438 1210 West Dayton Street * FAX: (608)262-9777 Madison, WI 53706 */ /* This file is part of the hacked version of the ghostview package */ /* which is distributed under the terms of the gnu license. The */ /* modification referred to above is by Tanmoy Bhattacharya, */ /* on Nov 17, 1994. Neither the modification, */ /* nor the original program provides any warranty. */ #include #ifndef SEEK_SET #define SEEK_SET 0 #endif #include #include #ifdef SIGNALRETURNSINT #define SIGVAL int #else #define SIGVAL void #endif #include #include #include #include #include #include #include #include #include #include #include #include /* Yuck, cannot get vScrollbar via the usual methods */ #include #include #include #include /* BSD 4.3 errno.h does not declare errno */ extern int errno; #ifdef VMS #include #else extern int sys_nerr; extern char *sys_errlist[]; #endif #include "Ghostview.h" #include "gv.h" #include "ps.h" #include "pdf.h" #ifndef max #define max(a, b) ((a) > (b) ? (a) : (b)) #endif /* Translate orientations defined by the enum in "ps.h" to * XtPageOrientations defined in "Ghostview.h". */ static XtPageOrientation xorient(psorient) int psorient; { switch (psorient) { case PORTRAIT: return XtPageOrientationPortrait; case LANDSCAPE: if (app_res.swap_landscape) { return XtPageOrientationSeascape; } else { return XtPageOrientationLandscape; } } } static void break_chains() { Arg args[2]; XtSetArg(args[0], XtNbottom, XtChainTop); XtSetArg(args[1], XtNright, XtChainLeft); XtSetValues(toc, args, ONE); XtSetValues(pageview, args, TWO); } static void set_chains() { Arg args[2]; XtSetArg(args[0], XtNbottom, XtChainBottom); XtSetArg(args[1], XtNright, XtChainRight); XtSetValues(toc, args, ONE); XtSetValues(pageview, args, TWO); } static void reset_size_hints() { Arg args[4]; if (app_res.ncdwm) return; XtSetArg(args[0], XtNmaxWidth, XtUnspecifiedShellInt); XtSetArg(args[1], XtNmaxHeight, XtUnspecifiedShellInt); XtSetArg(args[2], XtNminWidth, XtUnspecifiedShellInt); XtSetArg(args[3], XtNminHeight, XtUnspecifiedShellInt); XtSetValues(toplevel, args, FOUR); } static void set_size_hints(minw, minh, maxw, maxh) Dimension minw, minh, maxw, maxh; { Arg args[4]; XtSetArg(args[0], XtNminWidth, minw); XtSetArg(args[1], XtNminHeight, minh); XtSetArg(args[2], XtNmaxWidth, maxw); XtSetArg(args[3], XtNmaxHeight, maxh); XtSetValues(toplevel, args, FOUR); } static Boolean horiz_scroll_saved = False; static Boolean vert_scroll_saved = False; static float horiz_top; static float vert_top; static void reset_scroll_bars() { Arg args[1]; Widget scroll; float zero = 0.0; if (horiz_scroll_saved || vert_scroll_saved) return; scroll = XtNameToWidget(pageview, "horizontal"); if (scroll) { XtSetArg(args[0], XtNtopOfThumb, &horiz_top); XtGetValues(scroll, args, ONE); XtCallCallbacks(scroll, XtNjumpProc, &zero); horiz_scroll_saved = True; } scroll = XtNameToWidget(pageview, "vertical"); if (scroll) { XtSetArg(args[0], XtNtopOfThumb, &vert_top); XtGetValues(scroll, args, ONE); XtCallCallbacks(scroll, XtNjumpProc, &zero); vert_scroll_saved = True; } } static void set_scroll_bars() { Arg args[1]; Widget scroll; float shown; if (horiz_scroll_saved) { scroll = XtNameToWidget(pageview, "horizontal"); if (scroll) { XtSetArg(args[0], XtNshown, &shown); XtGetValues(scroll, args, ONE); if (horiz_top > (1.0 - shown)) horiz_top = (1.0 - shown); XtCallCallbacks(scroll, XtNjumpProc, &horiz_top); } } if (vert_scroll_saved) { scroll = XtNameToWidget(pageview, "vertical"); if (scroll) { XtSetArg(args[0], XtNshown, &shown); XtGetValues(scroll, args, ONE); if (vert_top > (1.0 - shown)) vert_top = (1.0 - shown); XtCallCallbacks(scroll, XtNjumpProc, &vert_top); } } horiz_scroll_saved = vert_scroll_saved = False; } /* Start rendering a new page */ void show_page(number) int number; { struct stat sbuf; int i; if (!filename) return; /* Unmark current_page as current */ if (toc_text && (current_page >= 0)) { int marker = current_page*toc_entry_length + toc_entry_length-2; toc_text[marker] = ' '; XawTextInvalidate(toc, marker, marker+1); } /* If the file has changed, rescan it so that offsets into the file * are still correct. If the file is rescanned, we must setup ghostview * again. Also, force a new copy of ghostscript to start. */ if (psfile) { if (!stat(filename, &sbuf) && mtime != sbuf.st_mtime) { fclose(psfile); psfile = fopen(filename, "r"); mtime = sbuf.st_mtime; if (oldfilename) XtFree(oldfilename); oldfilename = XtNewString(filename); new_file(number); } } /* Coerce page number to fall in range */ if (toc_text) { if (number >= doc->numpages) number = doc->numpages - 1; if (number < 0) number = 0; } pdf_clear(); if (set_new_orientation(number) || set_new_pagemedia(number)) layout_ghostview(); if (toc_text) { int marker; current_page = number; XawTextUnsetSelection(toc); XawTextSetInsertionPoint(toc, current_page * toc_entry_length); marker = current_page*toc_entry_length + toc_entry_length-2; toc_text[marker] = '<'; XawTextInvalidate(toc, marker, marker+1); if (GhostviewIsInterpreterReady(page)) { GhostviewNextPage(page); } else { GhostviewEnableInterpreter(page); GhostviewSendPS(page, psfile, doc->beginprolog, doc->lenprolog, False); GhostviewSendPS(page, psfile, doc->beginsetup, doc->lensetup, False); } if (doc->pageorder == DESCEND) i = (doc->numpages - 1) - current_page; else i = current_page; GhostviewSendPS(page, psfile, doc->pages[i].begin, doc->pages[i].len, False); } else { if (!GhostviewIsInterpreterRunning(page)) GhostviewEnableInterpreter(page); else if (GhostviewIsInterpreterReady(page)) GhostviewNextPage(page); else XBell(XtDisplay(page), 0); } if (toc_text) { XtSetSensitive(prevbutton, current_page != 0); XtSetSensitive(nextbutton, current_page != doc->numpages-1); XtSetSensitive(showbutton, True); } } /* setup ghostview. This includes: * scanning the PostScript file, * setting the title and date labels, * building the pagemedia menu, * building the toc (table of contents) * sensitizing the appropriate menu buttons, * popping down and erasing the infotext popup. */ static Boolean useful_page_labels; Boolean setup_ghostview() { Arg args[20]; Cardinal num_args; int oldtoc_entry_length; char *tocp; XawTextBlock message_block; static String nothing = ""; String label; Pixmap bitmap; /* Reset to a known state. */ psfree(olddoc); olddoc = doc; doc = NULL; current_page = -1; if (toc_text) XtFree(toc_text); oldtoc_entry_length = toc_entry_length; toc_text = NULL; /* Scan document and start setting things up */ if (psfile) doc = psscan(psfile); if (app_res.show_title) { if (doc && doc->title) { label = doc->title; bitmap = menu16_bitmap; } else { if (filename) { label = filename; } else { label = ""; } bitmap = None; } XtSetArg(args[0], XtNlabel, label); XtSetValues(titlebutton, args, ONE); if (titlemenu) XtDestroyWidget(titlemenu); titlemenu = build_label_menu(titlebutton, "title", label, bitmap); } if (app_res.show_date) { if (doc && doc->date) { label = doc->date; bitmap = menu16_bitmap; } else { if (psfile) { label = ctime(&mtime); } else { label = ""; } bitmap = None; } XtSetArg(args[0], XtNlabel, label); XtSetValues(datebutton, args, ONE); if (datemenu) XtDestroyWidget(datemenu); datemenu = build_label_menu(datebutton, "date", label, bitmap); } build_pagemedia_menu(); /* Reset ghostscript and output messages popup */ if (!doc || !olddoc || strcmp(oldfilename, filename) || olddoc->beginprolog != doc->beginprolog || olddoc->endprolog != doc->endprolog || olddoc->beginsetup != doc->beginsetup || olddoc->endsetup != doc->endsetup) { GhostviewDisableInterpreter(page); XtPopdown(infopopup); info_up = False; XtSetArg(args[0], XtNeditType, XawtextEdit); XtSetArg(args[1], XtNinsertPosition, 0); XtSetValues(infotext, args, TWO); message_block.length = 0; XawTextReplace(infotext, 0, info_length, &message_block); info_length = 0; XtSetArg(args[0], XtNeditType, XawtextRead); XtSetValues(infotext, args, ONE); } /* Build table of contents */ if (doc && (!doc->epsf && doc->numpages > 0 || doc->epsf && doc->numpages > 1)) { int maxlen = 0; int i, j; useful_page_labels = False; if (doc->numpages == 1) useful_page_labels = True; for (i = 1; i < doc->numpages; i++) if (useful_page_labels = (useful_page_labels || strcmp(doc->pages[i-1].label, doc->pages[i].label))) break; if (useful_page_labels) { for (i = 0; i < doc->numpages; i++) maxlen = max(maxlen, strlen(doc->pages[i].label)); } else { double x; x = doc->numpages; maxlen = log10(x) + 1; } toc_entry_length = maxlen + 3; toc_length = doc->numpages * toc_entry_length - 1; toc_text = XtMalloc(toc_length + 2); /* include final NULL */ for (i = 0, tocp = toc_text; i < doc->numpages; i++, tocp += toc_entry_length) { if (useful_page_labels) { if (doc->pageorder == DESCEND) { j = (doc->numpages - 1) - i; } else { j = i; } sprintf(tocp, " %*s \n", maxlen, doc->pages[j].label); } else { sprintf(tocp, " %*d \n", maxlen, i+1); } } toc_text[toc_length] = '\0'; num_args = 0; XtSetArg(args[num_args], XtNfilename, NULL); num_args++; XtSetValues(page, args, num_args); } else { toc_length = 0; toc_entry_length = 3; num_args = 0; XtSetArg(args[num_args], XtNfilename, filename); num_args++; XtSetValues(page, args, num_args); } num_args = 0; XtSetArg(args[num_args], XtNlength, toc_length); num_args++; if (toc_text) { XtSetArg(args[num_args], XtNstring, toc_text); num_args++; } else { /* Text widget sometime blows up when given a NULL pointer */ XtSetArg(args[num_args], XtNstring, nothing); num_args++; } XtSetValues(toc, args, num_args); XtSetSensitive(reopenbutton, (psfile != NULL)); XtSetSensitive(printwholebutton, (psfile != NULL)); XtSetSensitive(printmarkedbutton, (psfile != NULL)); XtSetSensitive(savebutton, (toc_text != NULL)); XtSetSensitive(nextbutton, (filename != NULL)); XtSetSensitive(showbutton, (filename != NULL)); XtSetSensitive(prevbutton, (toc_text != NULL)); XtSetSensitive(centerbutton, (filename != NULL)); XtSetSensitive(markbutton, (toc_text != NULL)); XtSetSensitive(unmarkbutton, (toc_text != NULL)); return oldtoc_entry_length != toc_entry_length; } int find_page(label) String label; { int i, j; if (label == NULL || doc == NULL) return 0; if (useful_page_labels) { for (i = 0; i < doc->numpages; i++) { if (doc->pageorder == DESCEND) { j = (doc->numpages - 1) - i; } else { j = i; } if (!strcmp(label, doc->pages[j].label)) return i; } return 0; } else { return atoi(label) - 1; } } /* try_try_again sets the geometry of the form when the form failed * to do it earlier. It uses activity check with exponential backoff * to make sure that the dust has settled before trying again. */ static unsigned int delay = 125; /* Start with 1/8 second delay */ static void try_try_again(client_data, timer) XtPointer client_data; XtIntervalId *timer; { XSync(XtDisplay(toplevel), False); /* Push everything out */ if (XtAppPending(app_con)) { XtAppAddTimeOut(app_con, delay, try_try_again, NULL); /* fprintf(stderr, "Delaying(%d)...\n",delay); */ delay *= 2; } else { /* fprintf(stderr, "Trying again...\n"); */ layout_ghostview(); } } /* set the dimensions for items in the main form widget. */ /* set foreground and background color in scrollbars. */ /* (The scroll bars come and go as size changes.) */ /* Set window manager hints to keep window manager from causing main */ /* viewport from growing too large */ void layout_ghostview() { Arg args[20]; Dimension min_width, min_height; Dimension max_width, max_height; Dimension form_width, form_height; Dimension title_height, title_border; Dimension date_height, date_border; Dimension locator_height, locator_border; Dimension box_width, box_height, box_border; Dimension label_width; Dimension toc_width, toc_height, toc_border; Dimension view_width, view_height, view_border; Dimension page_width, page_height; Dimension leftMargin, rightMargin; Dimension width, height; Boolean correct = True; int distance; int a_label; XFontStruct *font; XawFormDoLayout(form, False); reset_size_hints(); reset_scroll_bars(); break_chains(); XtSetArg(args[0], XtNdefaultDistance, &distance); XtGetValues(form, args, ONE); a_label = 0; if (app_res.show_title) { XtSetArg(args[0], XtNheight, &title_height); XtSetArg(args[1], XtNborderWidth, &title_border); XtGetValues(titlebutton, args, TWO); a_label = 1; } else { title_height = title_border = 0; } if (app_res.show_date) { XtSetArg(args[0], XtNheight, &date_height); XtSetArg(args[1], XtNborderWidth, &date_border); XtGetValues(datebutton, args, TWO); a_label = 1; } else { date_height = date_border = 0; } if (app_res.show_locator) { XtSetArg(args[0], XtNheight, &locator_height); XtSetArg(args[1], XtNborderWidth, &locator_border); XtGetValues(locator, args, TWO); a_label = 1; } else { locator_height = locator_border = 0; } XtSetArg(args[0], XtNwidth, &box_width); XtSetArg(args[1], XtNheight, &box_height); XtSetArg(args[2], XtNborderWidth, &box_border); XtGetValues(box, args, THREE); XtSetArg(args[0], XtNfont, &font); XtSetArg(args[1], XtNleftMargin, &leftMargin); XtSetArg(args[2], XtNrightMargin, &rightMargin); XtSetArg(args[3], XtNborderWidth, &toc_border); XtGetValues(toc, args, FOUR); toc_width = font->max_bounds.width * (toc_entry_length - 1) + leftMargin + rightMargin; XtSetArg(args[0], XtNwidth, &page_width); XtSetArg(args[1], XtNheight, &page_height); XtGetValues(page, args, TWO); XtSetArg(args[0], XtNborderWidth, &view_border); XtGetValues(pageview, args, ONE); view_width = page_width; view_height = page_height; min_width = box_width + 2*box_border + toc_width + 2*toc_border + 2*view_border + 4*distance; min_height = title_height + 2*title_border + date_height + 2*date_border + locator_height + 2*locator_border + box_height + 2*box_border + (2+a_label)*distance; max_width = WidthOfScreen(XtScreen(toplevel)) - app_res.wm_horiz_margin; max_height = HeightOfScreen(XtScreen(toplevel)) - app_res.wm_vert_margin; if (min_width + view_width > max_width) view_width = max_width - min_width; if (2*(view_border + distance) + view_height > max_height) view_height = max_height - 2*(view_border + distance); form_width = view_width + min_width; form_height = max(view_height + 2*(view_border + distance), min_height); toc_height = view_height - (title_height + 2*title_border + date_height + 2*date_border + locator_height + 2*locator_border + a_label*distance); label_width = box_width + 2*box_border + distance + toc_width + 2*toc_border; XtSetArg(args[0], XtNwidth, form_width); XtSetArg(args[1], XtNheight, form_height); XtSetValues(form, args, TWO); XtSetArg(args[0], XtNwidth, label_width); if (app_res.show_title) XtSetValues(titlebutton, args, ONE); if (app_res.show_date) XtSetValues(datebutton, args, ONE); if (app_res.show_locator) XtSetValues(locator, args, ONE); XtSetArg(args[0], XtNwidth, toc_width); XtSetArg(args[1], XtNheight, toc_height); XtSetValues(toc, args, TWO); XtSetArg(args[0], XtNwidth, view_width); XtSetArg(args[1], XtNheight, view_height); XtSetValues(pageview, args, TWO); XawFormDoLayout(form, True); /* Check to make sure everything was done as planned. */ XtSetArg(args[0], XtNwidth, &width); XtSetArg(args[1], XtNheight, &height); XtGetValues(form, args, TWO); if (width != form_width || height != form_height) { correct = False; /* fprintf(stderr, "Oops, %dx%d form was supposed to be %dx%d.\n", width, height, form_width, form_height); */ } if (app_res.show_title) { XtGetValues(titlebutton, args, ONE); if (width != label_width) { correct = False; /* fprintf(stderr, "Oops, %d wide title was supposed to be %d wide.\n", width, label_width); */ } } if (app_res.show_date) { XtGetValues(datebutton, args, ONE); if (width != label_width) { correct = False; /* fprintf(stderr, "Oops, %d wide date was supposed to be %d wide.\n", width, label_width); */ } } if (app_res.show_locator) { XtGetValues(locator, args, ONE); if (width != label_width) { correct = False; /* fprintf(stderr, "Oops, %d wide locator was supposed to be %d wide.\n", width, label_width); */ } } XtGetValues(toc, args, TWO); if (width != toc_width || height != toc_height) { correct = False; /* fprintf(stderr, "Oops, %dx%d toc was supposed to be %dx%d.\n", width, height, toc_width, toc_height); */ } XtGetValues(pageview, args, TWO); if (width != view_width || height != view_height) { correct = False; /* fprintf(stderr, "Oops, %dx%d pageview was supposed to be %dx%d.\n", width, height, view_width, view_height); */ } if (correct) { set_size_hints(min_width, min_height, min_width+page_width, max(form_height, page_height + 2*(view_border + distance))); if (app_res.auto_center) { horiz_scroll_saved = vert_scroll_saved = False; center_page(form, NULL, NULL); } else { set_scroll_bars(); } set_chains(); delay = 125; /* Reset to 1/8 second delay */ /* fprintf(stderr, "Layout correct.\n"); */ } else { XSync(XtDisplay(toplevel), False); XtAppAddTimeOut(app_con, delay, try_try_again, NULL); /* fprintf(stderr, "Didn't work, scheduling(%d)...\n",delay); */ } } /* Compute new dpi from magstep */ void magnify(dpi, magstep) float *dpi; int magstep; { if (magstep < 0) { while (magstep++) *dpi /= 1.2; } else { while (magstep--) *dpi *= 1.2; } } /* Attempt to open file, return error message string on failure */ String open_file(name) String name; { FILE *fp; struct stat sbuf; if (*name == '\0') { /* Null filename */ return(NULL); } if (strcmp(name, "-")) { if ((fp = fopen(name, "r")) == NULL) { String buf = XtMalloc(strlen(app_res.open_fail) + strlen(sys_errlist[errno]) + 1); strcpy(buf, app_res.open_fail); if (errno <= sys_nerr) strcat(buf, sys_errlist[errno]); return buf; } else { if (oldfilename) XtFree(oldfilename); oldfilename = filename; filename = XtNewString(name); if (psfile) fclose(psfile); psfile = fp; stat(filename, &sbuf); mtime = sbuf.st_mtime; new_file(0); show_page(0); return(NULL); } } else { if (oldfilename) XtFree(oldfilename); oldfilename = filename; filename = XtNewString(name); if (psfile) fclose(psfile); psfile = NULL; new_file(0); show_page(0); return(NULL); } } /* Attempt to save file, return error message string on failure */ String save_file(name) String name; { FILE *pswrite; if (*name == '\0') { /* Null filename */ return(NULL); } if ((pswrite = fopen(name, "w")) == NULL) { String buf = XtMalloc(strlen(app_res.save_fail) + strlen(sys_errlist[errno]) + 1); strcpy(buf, app_res.save_fail); if (errno <= sys_nerr) strcat(buf, sys_errlist[errno]); return buf; } else { pscopydoc(pswrite); fclose(pswrite); return(NULL); } } /* Attempt to print file. Return error string on failure */ String print_file(name, whole_mode) String name; Boolean whole_mode; { FILE *printer; SIGVAL (*oldsig)(); int bytes; char buf[BUFSIZ]; #ifdef VMS char fnam[64], *p; #endif Boolean failed; String ret_val; #ifdef VMS sprintf(fnam, "sys$scratch:%s.tmp", tmpnam(NULL)); printer = fopen(fnam, "w"); #else /* VMS */ if (*name != '\0') { setenv(app_res.printer_variable, name, True); } oldsig = signal(SIGPIPE, SIG_IGN); printer = popen(app_res.print_command, "w"); #endif /* VMS */ if (toc_text && !whole_mode) { pscopydoc(printer); } else { FILE *psfile = fopen(filename, "r"); while (bytes = read(fileno(psfile), buf, BUFSIZ)) bytes = write(fileno(printer), buf, bytes); fclose(psfile); } #ifdef VMS sprintf(buf, "%s %s %s", app_res.print_command, name, fnam); failed = fclose(printer) != 0 || system(buf) != 1; #else /* VMS */ failed = pclose(printer) != 0; #endif /* VMS */ if (failed) { sprintf(buf, app_res.print_fail, app_res.print_command); ret_val = XtNewString(buf); } else { ret_val = NULL; } #ifndef VMS signal(SIGPIPE, oldsig); #endif /* VMS */ return(ret_val); } /* length calculates string length at compile time */ /* can only be used with character constants */ #define length(a) (sizeof(a)-1) /* Copy the headers, marked pages, and trailer to fp */ void pscopydoc(fp) FILE *fp; { FILE *psfile; char text[PSLINELENGTH]; char *comment; Boolean pages_written = False; Boolean pages_atend = False; Boolean marked_pages = False; int pages = 0; int page = 1; int i, j; long here; psfile = fopen(filename, "r"); for (i = 0; i < doc->numpages; i++) { if (toc_text[toc_entry_length * i] == '*') pages++; } if (pages == 0) { /* User forgot to mark the pages */ mark_page(form, NULL, NULL); marked_pages = True; for (i = 0; i < doc->numpages; i++) { if (toc_text[toc_entry_length * i] == '*') pages++; } } here = doc->beginheader; while (comment = pscopyuntil(psfile, fp, here, doc->endheader, "%%Pages:")) { here = ftell(psfile); if (pages_written || pages_atend) { free(comment); continue; } sscanf(comment+length("%%Pages:"), "%s", text); if (strcmp(text, "(atend)") == 0) { fputs(comment, fp); pages_atend = True; } else { switch (sscanf(comment+length("%%Pages:"), "%*d %d", &i)) { case 1: fprintf(fp, "%%%%Pages: %d %d\n", pages, i); break; default: fprintf(fp, "%%%%Pages: %d\n", pages); break; } pages_written = True; } free(comment); } pscopy(psfile, fp, doc->beginpreview, doc->endpreview); pscopy(psfile, fp, doc->begindefaults, doc->enddefaults); pscopy(psfile, fp, doc->beginprolog, doc->endprolog); pscopy(psfile, fp, doc->beginsetup, doc->endsetup); for (i = 0; i < doc->numpages; i++) { if (doc->pageorder == DESCEND) j = (doc->numpages - 1) - i; else j = i; if (toc_text[toc_entry_length * j] == '*') { comment = pscopyuntil(psfile, fp, doc->pages[i].begin, doc->pages[i].end, "%%Page:"); fprintf(fp, "%%%%Page: %s %d\n", doc->pages[i].label, page++); free(comment); pscopy(psfile, fp, -1, doc->pages[i].end); } } here = doc->begintrailer; while (comment = pscopyuntil(psfile, fp, here, doc->endtrailer, "%%Pages:")) { here = ftell(psfile); if (pages_written) { free(comment); continue; } switch (sscanf(comment+length("%%Pages:"), "%*d %d", &i)) { case 1: fprintf(fp, "%%%%Pages: %d %d\n", pages, i); break; default: fprintf(fp, "%%%%Pages: %d\n", pages); break; } pages_written = True; free(comment); } fclose(psfile); if (marked_pages) unmark_page(form, NULL, NULL); } #undef length /* position popup window under the cursor */ void positionpopup(w) Widget w; { Arg args[3]; Cardinal num_args; Dimension width, height, b_width; int x, y, max_x, max_y; Window root, child; int dummyx, dummyy; unsigned int dummymask; XQueryPointer(XtDisplay(w), XtWindow(w), &root, &child, &x, &y, &dummyx, &dummyy, &dummymask); num_args = 0; XtSetArg(args[num_args], XtNwidth, &width); num_args++; XtSetArg(args[num_args], XtNheight, &height); num_args++; XtSetArg(args[num_args], XtNborderWidth, &b_width); num_args++; XtGetValues(w, args, num_args); width += 2 * b_width; height += 2 * b_width; x -= ( (Position) width/2 ); if (x < 0) x = 0; if ( x > (max_x = (Position) (XtScreen(w)->width - width)) ) x = max_x; y -= ( (Position) height/2 ); if (y < 0) y = 0; if ( y > (max_y = (Position) (XtScreen(w)->height - height)) ) y = max_y; num_args = 0; XtSetArg(args[num_args], XtNx, x); num_args++; XtSetArg(args[num_args], XtNy, y); num_args++; XtSetValues(w, args, num_args); } /* Set new magstep */ Boolean set_new_magstep() { int new_magstep; Boolean changed = False; Arg args[20]; Cardinal num_args; float xdpi, ydpi; new_magstep = app_res.magstep; /* If magstep changed, stop interpreter and setup for new dpi. */ if (new_magstep != current_magstep) { GhostviewDisableInterpreter(page); XawFormDoLayout(form, False); reset_size_hints(); reset_scroll_bars(); break_chains(); changed = True; xdpi = default_xdpi; ydpi = default_ydpi; magnify(&xdpi, new_magstep); magnify(&ydpi, new_magstep); num_args = 0; XtSetFloatArg(args[num_args], XtNxdpi, xdpi); num_args++; XtSetFloatArg(args[num_args], XtNydpi, ydpi); num_args++; XtSetValues(page, args, num_args); XtSetArg(args[0], XtNleftBitmap, None); XtSetValues(magstepentry[current_magstep - app_res.minimum_magstep], args, ONE); current_magstep = new_magstep; } XtSetArg(args[0], XtNleftBitmap, dot_bitmap); XtSetValues(magstepentry[current_magstep - app_res.minimum_magstep], args, ONE); return changed; } /* Set new orientation */ Boolean set_new_orientation(number) int number; { Boolean changed = False; Boolean from_doc = False; Arg args[1]; XtPageOrientation new_orientation; if (app_res.force_orientation) { new_orientation = app_res.orientation; } else { if (doc) { if (toc_text && doc->pages[number].orientation != NONE) { new_orientation = xorient(doc->pages[number].orientation); from_doc = True; } else if (doc->default_page_orientation != NONE) { new_orientation = xorient(doc->default_page_orientation); from_doc = True; } else if (doc->orientation != NONE) { new_orientation = xorient(doc->orientation); from_doc = True; } else { new_orientation = app_res.orientation; } } else { new_orientation = app_res.orientation; } } /* If orientation changed, * stop interpreter and setup for new orientation. */ if (new_orientation != current_orientation) { GhostviewDisableInterpreter(page); XawFormDoLayout(form, False); reset_size_hints(); reset_scroll_bars(); break_chains(); changed = True; XtSetArg(args[0], XtNorientation, new_orientation); XtSetValues(page, args, ONE); XtSetArg(args[0], XtNleftBitmap, None); if (current_orientation == XtPageOrientationPortrait) XtSetValues(portraitbutton, args, ONE); else if (current_orientation == XtPageOrientationLandscape) XtSetValues(landscapebutton, args, ONE); else if (current_orientation == XtPageOrientationUpsideDown) XtSetValues(upsidedownbutton, args, ONE); else if (current_orientation == XtPageOrientationSeascape) XtSetValues(seascapebutton, args, ONE); current_orientation = new_orientation; } /* mark forced orientation with tie fighter. ("Use the force, Luke") */ if (app_res.force_orientation) { XtSetArg(args[0], XtNleftBitmap, tie_fighter_bitmap); } else if (from_doc) { XtSetArg(args[0], XtNleftBitmap, menu16_bitmap); } else { XtSetArg(args[0], XtNleftBitmap, dot_bitmap); } if (current_orientation == XtPageOrientationPortrait) XtSetValues(portraitbutton, args, ONE); else if (current_orientation == XtPageOrientationLandscape) XtSetValues(landscapebutton, args, ONE); else if (current_orientation == XtPageOrientationUpsideDown) XtSetValues(upsidedownbutton, args, ONE); else if (current_orientation == XtPageOrientationSeascape) XtSetValues(seascapebutton, args, ONE); return changed; } /* Set new pagemedia */ Boolean set_new_pagemedia(number) int number; { int new_pagemedia; int new_llx; int new_lly; int new_urx; int new_ury; Boolean changed = False; Boolean from_doc = False; Arg args[4]; if (force_document_media) { new_pagemedia = document_media; } else if (app_res.force_pagemedia) { new_pagemedia = default_pagemedia; } else { if (doc) { if (toc_text && doc->pages[number].media != NULL) { new_pagemedia = doc->pages[number].media - doc->media; from_doc = True; } else if (doc->default_page_media != NULL) { new_pagemedia = doc->default_page_media - doc->media; from_doc = True; } else { new_pagemedia = default_pagemedia; } } else { new_pagemedia = default_pagemedia; } } /* If pagemedia changed, remove the old marker. */ if (new_pagemedia != current_pagemedia) { XtSetArg(args[0], XtNleftBitmap, None); if (pagemediaentry[current_pagemedia]) XtSetValues(pagemediaentry[current_pagemedia], args, ONE); else XtSetValues(pagemediaentry[current_pagemedia-1], args, ONE); current_pagemedia = new_pagemedia; } /* mark forced page media with tie fighter. ("Use the force, Luke") */ if (force_document_media || app_res.force_pagemedia) { XtSetArg(args[0], XtNleftBitmap, tie_fighter_bitmap); } else if (from_doc) { XtSetArg(args[0], XtNleftBitmap, menu16_bitmap); } else { XtSetArg(args[0], XtNleftBitmap, dot_bitmap); } if (pagemediaentry[current_pagemedia]) XtSetValues(pagemediaentry[current_pagemedia], args, ONE); else XtSetValues(pagemediaentry[current_pagemedia-1], args, ONE); /* Compute bounding box */ if (!force_document_media && !app_res.force_pagemedia && doc && doc->epsf && /* Ignore malformed bounding boxes */ (doc->boundingbox[URX] > doc->boundingbox[LLX]) && (doc->boundingbox[URY] > doc->boundingbox[LLY])) { new_llx = doc->boundingbox[LLX]; new_lly = doc->boundingbox[LLY]; new_urx = doc->boundingbox[URX]; new_ury = doc->boundingbox[URY]; } else { new_llx = new_lly = 0; if (new_pagemedia < base_papersize) { new_urx = doc->media[new_pagemedia].width; new_ury = doc->media[new_pagemedia].height; } else { new_urx = papersizes[new_pagemedia-base_papersize].width; new_ury = papersizes[new_pagemedia-base_papersize].height; } } /* If bounding box changed, setup for new size. */ if ((new_llx != current_llx) || (new_lly != current_lly) || (new_urx != current_urx) || (new_ury != current_ury)) { GhostviewDisableInterpreter(page); XawFormDoLayout(form, False); reset_size_hints(); reset_scroll_bars(); break_chains(); changed = True; current_llx = new_llx; current_lly = new_lly; current_urx = new_urx; current_ury = new_ury; XtSetArg(args[0], XtNllx, current_llx); XtSetArg(args[1], XtNlly, current_lly); XtSetArg(args[2], XtNurx, current_urx); XtSetArg(args[3], XtNury, current_ury); XtSetValues(page, args, FOUR); } return changed; } static Boolean same_document_media() { int i; if (olddoc == NULL && doc == NULL) return True; if (olddoc == NULL || doc == NULL) return False; if (olddoc->nummedia != doc->nummedia) return False; for (i = 0; i < doc->nummedia; i++) if (strcmp(olddoc->media[i].name, doc->media[i].name)) return False; return True; } void build_pagemedia_menu() { Arg args[20]; Cardinal num_args; Widget w; int i; if (pagemediamenu && same_document_media()) return; if (pagemediamenu) XtDestroyWidget(pagemediamenu); force_document_media = False; pagemediamenu = XtCreatePopupShell("menu", simpleMenuWidgetClass, pagemediabutton, NULL, ZERO); /* Build the Page Media menu */ /* the Page media menu has two parts. * - the document defined page medias * - the standard page media defined from Adobe's PPD */ base_papersize = 0; if (doc) base_papersize = doc->nummedia; for (i = 0; papersizes[i].name; i++) {} /* Count the standard entries */ i += base_papersize; pagemediaentry = (Widget *) XtMalloc(i * sizeof(Widget)); if (doc && doc->nummedia) { for (i = 0; i < doc->nummedia; i++) { num_args = 0; XtSetArg(args[num_args], XtNleftMargin, 20); num_args++; pagemediaentry[i] = XtCreateManagedWidget(doc->media[i].name, smeBSBObjectClass, pagemediamenu, args, num_args); XtAddCallback(pagemediaentry[i], XtNcallback, set_pagemedia, (XtPointer)i); } num_args = 0; w = XtCreateManagedWidget("line", smeLineObjectClass, pagemediamenu, args, num_args); } for (i = 0; papersizes[i].name; i++) { pagemediaentry[i+base_papersize] = NULL; if (i > 0) { /* Skip over same paper size with small imageable area */ if ((papersizes[i].width == papersizes[i-1].width) && (papersizes[i].height == papersizes[i-1].height)) { continue; } } num_args = 0; XtSetArg(args[num_args], XtNleftMargin, 20); num_args++; pagemediaentry[i+base_papersize] = XtCreateManagedWidget( papersizes[i].name, smeBSBObjectClass, pagemediamenu, args, num_args); XtAddCallback(pagemediaentry[i+base_papersize], XtNcallback, set_pagemedia, (XtPointer)(i+base_papersize)); } } Widget build_label_menu(parent, name, label, bitmap) Widget parent; String name, label; Pixmap bitmap; { Arg args[20]; Cardinal num_args; Widget menu, entry; num_args = 0; menu = XtCreatePopupShell("menu", simpleMenuWidgetClass, parent, args, num_args); num_args = 0; XtSetArg(args[num_args], XtNlabel, label); num_args++; if (bitmap) { XtSetArg(args[num_args], XtNleftMargin, 20); num_args++; XtSetArg(args[num_args], XtNleftBitmap, bitmap); num_args++; } entry = XtCreateManagedWidget(name, smeBSBObjectClass, menu, args, num_args); return menu; } void new_file(number) int number; { Boolean layout_changed = False; if (setup_ghostview()) layout_changed = True; /* Coerce page number to fall in range */ if (toc_text) { if (number >= doc->numpages) number = doc->numpages - 1; if (number < 0) number = 0; } if (set_new_orientation(number)) layout_changed = True; if (set_new_pagemedia(number)) layout_changed = True; if (layout_changed) layout_ghostview(); } /* Catch X errors die gracefully if one occurs */ int catch_Xerror(dpy, err) Display *dpy; XErrorEvent *err; { if (err->error_code == BadImplementation) { old_Xerror(dpy, err); return 0; } if (dying) return 0; dying = True; bomb = *err; XtDestroyWidget(toplevel); return 0; }