⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 gvx.c

📁 GSview 4.6 PostScript previewer。Ghostscript在MS-Windows, OS/2 and Unix下的图形化接口
💻 C
📖 第 1 页 / 共 4 页
字号:
/* Copyright (C) 2000-2002, Ghostgum Software Pty Ltd.  All rights reserved.
  
  This file is part of GSview.
  
  This program is distributed with NO WARRANTY OF ANY KIND.  No author
  or distributor accepts any responsibility for the consequences of using it,
  or for whether it serves any particular purpose or works at all, unless he
  or she says so in writing.  Refer to the GSview Free Public Licence 
  (the "Licence") for full details.
  
  Every copy of GSview must include a copy of the Licence, normally in a 
  plain ASCII text file named LICENCE.  The Licence grants you the right 
  to copy, modify and redistribute GSview, but only under certain conditions 
  described in the Licence.  Among other things, the Licence requires that 
  the copyright notice and this notice be preserved on all copies.
*/

/* gvx.cpp */
/* Main routines for X11 GSview */
#include "gvx.h"
#include "gdk/gdkkeysyms.h"

GtkWidget *window;		/* main window */
GtkWidget *main_vbox;
GtkWidget *menubar;
GtkWidget *buttonbar;
GtkWidget *scroll_window;	/* this scrolls the image window */
GtkWidget *img;			/* drawing area, image window */
GtkWidget *statusbar;
GtkWidget *statusfile;
GtkWidget *statuscoord;
GtkWidget *statuspage;
char *pszLocale;
BOOL have_selection;

#ifdef MULTITHREAD
pthread_mutex_t hmutex_ps; 	/* for protecting psfile and pending */
#endif

char szAppName[MAXSTR] = GSVIEW_PRODUCT;  /* application name - for title bar */
int nHelpTopic;
char szWait[MAXSTR];
char szDocPath[MAXSTR] = GSVIEW_DOCPATH;
char szEtcPath[MAXSTR] = GSVIEW_ETCPATH;
char szHelpName[MAXSTR];
char szFindText[MAXSTR];
char szIniFile[MAXSTR];
char previous_filename[MAXSTR];
char selectname[MAXSTR];
const char szScratch[] = "gsvx";	/* temporary filename prefix */

const char *szSpoolPrefix = "%pipe%";	/* GS 6.0 and later should use %pipe% */
char coord_text[64];		/* last text displayed as coordinate */
BOOL multithread;
int geometry_width;
int geometry_height;
int geometry_xoffset;
int geometry_yoffset;

int on_link;			/* TRUE if we were or are over link */
int on_link_page;		/* page number of link target */
const char *on_link_action;	/* action of link target */
long gsbytes_size;		/* number of bytes for this page */
long gsbytes_done;		/* number of byte written */
BOOL quitnow = FALSE;		/* Used to cause exit from nested message loops */

int percent_done;		/* percentage of document processed */
int percent_pending;		/* TRUE if WM_GSPERCENT is pending */
BOOL fit_page_enabled = FALSE;	/* next WM_SIZE is allowed to resize window */

#define MAXARGS 64
int nargc;
char *nargv[MAXARGS];
GSVIEW_ARGS args;		/* Parsed arguments */
PSFILE psfile;		/* Postscript file structure */
OPTIONS option;		/* GSview options (saved in INI file) */
DISPLAY display;	/* Display parameters */
PRINTER printer;	/* Printer GS parameters */
char last_files[4][MAXSTR];	/* last 4 files used */
GtkWidget *last_file_widget[4];
int last_files_count;		/* number of files known */
HISTORY history;		/* history of pages displayed */
BOOL fullscreen = FALSE;
int command_on_done;		/* execute this command when GS finishes */

int page_skip = 5;		/* number of pages to skip in IDM_NEXTSKIP or IDM_PREVSKIP */
BOOL zoom = FALSE;		/* true if display zoomed */
BOOL print_silent = FALSE;	/* /P or /F command line option used */
BOOL print_exit = FALSE;	/* exit on completion of printing */
int print_count = 0;		/* number of current print jobs */
				/* It is safe to exit GSview when this is 0 */
int disable_gsview_wcmd;	/* to avoid recursive messages */
BOOL getting_bbox;		/* PS to EPS get Bounding Box dialog is shown */
int debug = 0;			/* /D command line option used */
struct sound_s sound[NUMSOUND] = {
	{"SoundOutputPage", IDS_SNDPAGE, ""},
	{"SoundNoPage", IDS_SNDNOPAGE, BEEP},
	{"SoundNoNumbering", IDS_SNDNONUMBER, ""},
	{"SoundNotOpen", IDS_SNDNOTOPEN, ""},
	{"SoundError", IDS_SNDERROR, BEEP},
	{"SoundStart", IDS_SNDSTART, ""},
	{"SoundExit", IDS_SNDEXIT, ""},
	{"SoundBusy", IDS_SNDBUSY, BEEP},
};
USERMEDIA usermedia[IDM_USERSIZE13 - IDM_USERSIZE1 + 1];
FILE *pstotextOutfile;

typedef struct MESSAGE_s {
    int message;
    int param;
} MESSAGE;

#define MESSAGE_MAX 32
const int message_max = MESSAGE_MAX;
int message_count = 0;
MESSAGE messages[MESSAGE_MAX];
int xdisplay_local;

char workdir[MAXSTR];
BOOL in_img_window = FALSE;

void map_pt_to_pixel(float *x, float *y);
BOOL get_cursorpos(float *x, float *y);
void statuscoord_update(void);
BOOL draghand;
int draghand_x;
int draghand_y;
void highlight_links(void);
void highlight_words(int first, int last, BOOL marked);
BOOL text_marking = FALSE;
int text_mark_first = -1;
int text_mark_last = -1;
void info_link(void);
void selection_add(void);
void selection_release(void);
void *gs_thread(void *arg);
void do_img_message(int message, int param);
int find_img_message(int message);
int process_img_message(void);
int read_img_message(void);
void read_message_pipe_fn(gpointer data, gint fd, GdkInputCondition condition);
void set_menu_sensitive(void);
void selection_handle(GtkWidget *widget, GtkSelectionData *selection_data,
	guint info, guint time_stamp, gpointer data);
gint do_args(gpointer data);
void key_scroll_horz(int key);
void key_scroll_vert(int key);

/**********************************************************/

/* To communicate from the GS thread to the main GUI thread
 * we use a pipe.  This passes 8 byte messages containing
 * a message ID and an integer parameter.
 * GS thread sends a message with post_img_message().
 */

int message_pipe[2];	/* file descriptors */
gint message_pipe_tag;	/* for gtk_input_add */

/* stop listening for messages form GS thread */
void
close_img_message(void)
{
    if (debug & DEBUG_GENERAL)
	gs_addmess("close_img_message:\n");
    if (message_pipe_tag >= 0)
        gdk_input_remove(message_pipe_tag);
    message_pipe_tag = -1;
    if (message_pipe[0] >= 0)
	close(message_pipe[0]);
    message_pipe[0] = -1;
    if (message_pipe[1] >= 0)
	close(message_pipe[1]);
    message_pipe[1] = -1;
}

/* process a message from GS thread*/
void
do_img_message(int message, int param)
{
    if (message == WM_QUIT) {
	quit_gsview(window, NULL);
        gtk_main_quit();
    }
    else if (message == WM_CLOSE) {
	if (window->window) {
	    /* save window location */
	    /* This doesn't work very well, because different X servers
	     * and combinations of local/remote give inconsistent
	     * values for window location.
	     */
	    gint x, y;
	    gdk_window_get_root_origin(window->window, &x, &y);
	    option.img_origin.x = x;
	    option.img_origin.y = y;
	}
	quit_gsview(window, NULL);
    }
    else if (quitnow) {
	/* don't do draw on any windows */
    }
    else if (message == WM_COMMAND) {
	gsview_wcmd(NULL, (gpointer)param);
    }
    else if (message == WM_GSDEVICE) {
	/* hide window if closed */
	if (!image.open) {
	    if ((GTK_WIDGET_FLAGS(img) & GTK_VISIBLE))
		gtk_widget_hide_all(img);
	}
    }
    else if (message == WM_GSSYNC) {
	int td;
	struct timeval tv1;
	struct timeval tv2;
	if (!(GTK_WIDGET_FLAGS(img) & GTK_VISIBLE))
	    gtk_widget_show_all(img);
	gettimeofday(&tv1, NULL);
	gtk_widget_draw(img, NULL);
	gettimeofday(&tv2, NULL);
	td = (tv2.tv_sec - tv1.tv_sec) * 1000 
	    + (tv2.tv_usec - tv1.tv_usec) / 1000;
	image_lock(&image);
	if (image.open && (td * 2 > image.tile_interval))
	    image.tile_interval += td + 100;
	image_unlock(&image);
    }
    else if (message == WM_GSPAGE) {
	int td;
	struct timeval tv1;
	struct timeval tv2;
	gsdll.state = GS_PAGE;
	if (display.show_find)
	    scroll_to_find();
	gettimeofday(&tv1, NULL);
	gtk_widget_draw(img, NULL);
	gettimeofday(&tv2, NULL);
	td = (tv2.tv_sec - tv1.tv_sec) * 1000 
	    + (tv2.tv_usec - tv1.tv_usec) / 1000;
	image_lock(&image);
	if (image.open && (td * 2 > image.tile_interval))
		image.tile_interval += td + 100;
	image_unlock(&image);
	info_wait(IDS_NOWAIT);
	selection_release();
    }
    else if (message == WM_GSSIZE) {
	if (image.open) {
	    gtk_drawing_area_size(GTK_DRAWING_AREA (img), 
		image.width, image.height);
	    if (!(GTK_WIDGET_FLAGS(img) & GTK_VISIBLE))
		gtk_widget_show(img);
	}
    }
    else if (message == WM_GSWAIT) {
	info_wait(param);
    }
    else if (message == WM_GSMESSBOX) {
	/* delayed message box, usually from other thread */
	char buf[MAXSTR];
	load_string(param, buf, sizeof(buf));
	message_box(buf, 0);
    }
    else if (message == WM_GSSHOWMESS) {
	gs_showmess();
    }
    else if (message == WM_GSREDISPLAY) {
	  gsview_wcmd(NULL, (gpointer)IDM_REDISPLAY);
    }
    else if (message == WM_GSTITLE) {
	/* update title */
	if (psfile.name[0] != '\0') {
	    char buf[256];
	    char *p;
	    p = strrchr(psfile.name, '/');
	    if (p == NULL)
		p = psfile.name;
	    else
		p++;
	    sprintf(buf, "%s - %s", p, szAppName);
	    gtk_window_set_title(GTK_WINDOW(window), buf);
	}
	else
	    gtk_window_set_title(GTK_WINDOW(window), szAppName);
    }
    else if (message == WM_GSPERCENT) {
	char buf[MAXSTR];
	percent_pending = FALSE;
	load_string(IDS_WAITDRAW_PC, szWait, sizeof(szWait));
	sprintf(buf, szWait, percent_done);
        gtk_label_set_text(GTK_LABEL(statuspage), buf);
	while (g_main_iteration(FALSE)); /* flush display */
    }
    else if (message == WM_GSTEXTINDEX) {
	gs_addmess("WM_GSTEXTINDEX not implemented\n");
	make_text_index();
	text_marking = FALSE;
	text_mark_first = text_mark_last = -1;
	selection_release();
    }
    else if (message == WM_GSTILE) {
	GdkRectangle area;
	image_lock(&image);
	if (image.open) {
	    int td;
	    struct timeval tv1;
	    struct timeval tv2;
	    int tx1 = (param >> 24) & 0xff;
	    int ty1 = (param >> 16) & 0xff;
	    int tx2 = (param >> 8) & 0xff;
	    int ty2 = (param) & 0xff;
	    area.x = (tx1 * image.tile_width) + display.offset.x;
	    area.y = (ty1 * image.tile_height) + display.offset.y;
	    area.width = (tx2 - tx1 + 1) * image.tile_width;
	    area.height = (ty2 - ty1 + 1) * image.tile_height;

	    if ((option.update == 2) || 
		((option.update == 1) && (xdisplay_local))) {
		image_unlock(&image);
		gettimeofday(&tv1, NULL);
		gtk_widget_draw(img, &area);
		gettimeofday(&tv2, NULL);
		image_lock(&image);
		td = (tv2.tv_sec - tv1.tv_sec) * 1000 
		    + (tv2.tv_usec - tv1.tv_usec) / 1000;
	    }
	    else {
		/* make sure we don't called too often */
		td = 2000;
	    }

	    if (image.open && (td * 5 > image.tile_interval))
		image.tile_interval += td + 100;
	}
	image_unlock(&image);
    }
    else
	gs_addmessf("Unknown post_img_message %d\n", message);
}

/* return index of the first message in the queue which contains the 
 * message id, or 0 if none in queue.
 */
int 
find_img_message(int message)
{
    int i;
    int idx = 0;
    for (i=1; i<message_count-1; i++) {
	if (messages[i].message == message) {
	    idx = i;
	    break;
	}
    }
    return idx;
}

/* process messages in the queue */
int
process_img_message(void)
{
    int message, param;
    int idx;
    BOOL ignore;
    if (message_count) {
	message = messages[0].message;
	param = messages[0].param;
	ignore = FALSE;
	/* Avoid doing potentially expensive drawing operations
	 * if there is another one in the queue.
	 */
	if (message == WM_GSSYNC) {
	    if (find_img_message(WM_GSSYNC) || find_img_message(WM_GSPAGE))
		ignore = TRUE;
	}
	else if (message == WM_GSPAGE) {
	    if (find_img_message(WM_GSPAGE))
		ignore = TRUE;
	}
	else if (message == WM_GSTILE) {
	    if (find_img_message(WM_GSSYNC) || find_img_message(WM_GSPAGE))
		ignore = TRUE;
	    else if ((idx = find_img_message(WM_GSTILE)) != 0) {
		/* merge tile updates */
		int nparam = messages[idx].param;
		int tx1 = (param >> 24) & 0xff;
		int ty1 = (param >> 16) & 0xff;
		int tx2 = (param >> 8) & 0xff;
		int ty2 = (param) & 0xff;
		int nx1 = (nparam >> 24) & 0xff;
		int ny1 = (nparam >> 16) & 0xff;
		int nx2 = (nparam >> 8) & 0xff;
		int ny2 = (nparam) & 0xff;
		nx1 = min(tx1, nx1);
		ny1 = min(ty1, ny1);
		nx2 = max(tx2, nx2);
		ny2 = max(ty2, ny2);
		nparam = (nx1<<24) + (ny1<<16) + (nx2<<8) + ny2;
		messages[idx].param = nparam;
		ignore = TRUE;
	    }
	}
	if (!ignore)
            do_img_message(message, param);
	/* BEWARE: the previous call can change message count if it lets
	 * the message loop run
	 */ 
	if (message_count > 0)
	    --message_count;
	else
	    message_count = 0;
	if (message_count > 0) {
	    memmove(messages, messages+1, message_count * sizeof(messages[0]));
	}
    }
    return 0;
}

/* read a message from the GS thread to us the GUI thread */
int
read_img_message(void)
{
    int bytes_read;
    int message, param;
    bytes_read = read(message_pipe[0], &message, sizeof(message));
    if (bytes_read > 0)
        bytes_read = read(message_pipe[0], &param, sizeof(param));
    if (bytes_read == -1) {
	if (errno == EAGAIN) {
	    return 1;	/* come back later */
	}
	else {
	    if (debug & DEBUG_GENERAL)
		gs_addmessf("read_img_message: read failed, errno=%d\n", errno);
	    return -1;
	}
    }
    if (message_count < message_max) {
	messages[message_count].message = message;
	messages[message_count].param = param;
	message_count++;
    }
    return 0;
}


/* Asynchronous read of message_pipe.
 * This is called from event loop when a read is possible on message_pipe.
 */
void read_message_pipe_fn(gpointer data, gint fd, GdkInputCondition condition)
{
    if (message_pipe[0] != fd) {
	if (debug & DEBUG_GENERAL)
	    gs_addmess("read_message_pipe_fn: called with wrong source\n");
	return;
    }

    if (condition & GDK_INPUT_EXCEPTION) {
	/* complain */
	if (debug & DEBUG_GENERAL)
	    gs_addmess("read_message_pipe_fn: exception\n");
	close_img_message();
    }
    else if (condition & GDK_INPUT_READ) {
	while (read_img_message()==0) {
	    if (message_count >= message_max)
		process_img_message();
	}
	while (message_count)
	    process_img_message();
    }
    else {
	if (debug & DEBUG_GENERAL)
	    gs_addmessf("read_message_pipe_fn: unknown condition %d\n", condition);
    }
}

/* start listening to messages from the GS thread */
int
init_img_message(void)
{
    int flags;
    if (pipe(message_pipe)) {
	gs_addmessf("Could not open pipe for messages, errno=%d\n", errno);
	return 1;
    }
    flags = fcntl(message_pipe[0], F_GETFL, 0);
    if (fcntl(message_pipe[0], F_SETFL, flags | O_NONBLOCK)) {
	gs_addmessf("Could not set message pipe to non-blocking, errno=%d\n", errno);
	close(message_pipe[0]);
	close(message_pipe[1]);
	return 1;
    }
    message_pipe_tag = gdk_input_add(message_pipe[0], 
	(GdkInputCondition)(GDK_INPUT_READ | GDK_INPUT_EXCEPTION),
	read_message_pipe_fn, 0);
    return 0;
}

/* Send a message from GS thread to the GUI thread.
 * Use a pipe to implement this.
 * We need this because the GS thread is not allowed
 * to use gtk, gdk or Xlib.
 */
void
post_img_message(int message, int param)
{
    int mess[2];
    mess[0] = message;
    mess[1] = param;
    write(message_pipe[1], &mess, sizeof(mess));
    /* no flush needed, because low level I/O doesn't buffer */
}


/* On "File | Exit" or window destroy */ 
void quit_gsview( GtkWidget *w,
                         gpointer   data )

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -