Re: [PATCH] Add "open Tex source file" support for evince
- From: Dongsheng Xing <homer xing yahoo com>
- To: evince-list gnome org
- Subject: Re: [PATCH] Add "open Tex source file" support for evince
- Date: Mon, 11 May 2009 07:53:15 -0700 (PDT)
Hello, everyone,
I updated the attached patch, with the following improvements:
* Support both SyncTeX and "DVI src specials" now.
* Remove find_page_contains(), thus reducing 20+ lines of codes.
* Break the big function dvi_open_tex_source() into three small functions.
How to use:
Suppose you have a TeX source file "main.tex"
1. Using DVI src specials:
latex --src-specials main.tex
evince --editor="gedit +%l %f" main.dvi
2. Using SyncTex & DVI:
latex -synctex=1 main.tex
shell/evince --editor="synctex edit -o %p:%x:%y:%o -x 'gedit +%%{line} %%{input}'" main.dvi
3. Using SyncTex & PDF:
pdflatex -synctex=1 main.tex
shell/evince --editor="synctex edit -o %p:%x:%y:%o -x 'gedit +%%{line} %%{input}'" main.pdf
Where
%l means the corresponding line,
%f the corresponding source,
%p the page number
%x the x-coordinate of the mouse click
%y the y-coordinate of the mouse click
%o the uri passed to evince, such as "main.dvi", "main.pdf"
Evince will replace the "%x" stuffs with the correct informations, in build_cmd_str().
Cheers,
Homer
Index: backend/dvi/dvi-document.c
===================================================================
--- backend/dvi/dvi-document.c (revision 3613)
+++ backend/dvi/dvi-document.c (working copy)
@@ -253,7 +253,152 @@
return info;
}
+static DviTeXSrc*
+find_nearest_tex_src (DviContext *dvi, int page, double y)
+{
+ DviTeXSrc *p, *found;
+ double dist, dist2;
+
+ if (!dvi->src)
+ return NULL;
+ p = dvi->src;
+ found = dvi->src;
+ while (p) {
+ if (p->page < page)
+ found = p;
+ else if (p->page == page && p->y < y)
+ found = p;
+ else
+ break;
+ p = p->next;
+ }
+ dist = y - found->y;
+ p = found->next;
+ if (p) {
+ dist2 = p->y + (p->page - page - y);
+ if (dist2 < dist)
+ found = p;
+ }
+ return found;
+}
+
static void
+build_cmd_str (const gchar *tex_editor,
+ GString *cmd_str,
+ int page,
+ int docx,
+ int docy,
+ int line,
+ char *source,
+ const gchar *uri)
+{
+ const gchar *c;
+ gchar *path;
+ gchar *file;
+
+ c = tex_editor;
+ while (*c) {
+ if (*c != '%')
+ g_string_append_c (cmd_str, *c);
+ else
+ switch (*(++c)) {
+ case '%':
+ g_string_append_c (cmd_str, '%');
+ break;
+ case 'l':
+ g_string_append_printf (cmd_str, "%d", line);
+ break;
+ case 'f':
+ if (source[0] == '/')
+ g_string_append (cmd_str, source);
+ else {
+ path = g_strdup (uri);
+ * (strrchr (path, '/')+1) = 0;
+ file = strrchr (source, '/');
+ if (!file)
+ file = source;
+ else
+ file++;
+ g_string_append (cmd_str, path);
+ g_string_append (cmd_str, file);
+ g_free(path);
+ }
+ break;
+ case 'p':
+ g_string_append_printf (cmd_str, "%d", page+1);
+ break;
+ case 'x':
+ g_string_append_printf (cmd_str, "%d", docx);
+ break;
+ case 'y':
+ g_string_append_printf (cmd_str, "%d", docy);
+ break;
+ case 'o':
+ g_string_append_printf (cmd_str, "%s", uri + 7);
+ break;
+ }
+ c++;
+ }
+}
+
+static void
+spawn_tex_editor (GString *cmd_str)
+{
+ gchar *cmd;
+ gint argc;
+ gchar **argv;
+ gboolean retval = FALSE;
+ GError *error = NULL;
+
+ cmd = g_string_free (cmd_str, FALSE);
+ g_shell_parse_argv (cmd, &argc, &argv, &error);
+ g_free (cmd);
+
+ if (!error) {
+ retval = gdk_spawn_on_screen (gdk_screen_get_default (),
+ NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL, NULL,
+ &error);
+ g_strfreev (argv);
+ }
+
+ if (error) {
+ g_warning ("Error spawning '%s': %s\n", cmd, error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+dvi_open_tex_source (EvDocument *document,
+ int page,
+ double x,
+ double y,
+ int docx,
+ int docy,
+ const gchar *tex_editor)
+{
+ DviDocument *dvi_document = DVI_DOCUMENT (document);
+ DviContext *dvi = dvi_document->context;
+ GString *cmd_str = 0;
+ DviTeXSrc *found;
+ int line = 0;
+ char *source = 0;
+
+ if (strncmp (tex_editor, "synctex", 7)) { /* DVI TeX specials */
+ found = find_nearest_tex_src (dvi, page, y);
+ if (!found)
+ return;
+ line = found->line;
+ source = found->source;
+ } else if (strncmp (dvi_document->uri, "file://", 7)) /* SyncTeX */
+ return;
+ cmd_str = g_string_new(NULL);
+ build_cmd_str (tex_editor, cmd_str, page, docx, docy, line, source, dvi_document->uri);
+ spawn_tex_editor (cmd_str);
+}
+
+static void
dvi_document_document_iface_init (EvDocumentIface *iface)
{
iface->load = dvi_document_load;
@@ -262,6 +407,7 @@
iface->get_page_size = dvi_document_get_page_size;
iface->render = dvi_document_render;
iface->get_info = dvi_document_get_info;
+ iface->open_tex_source = dvi_open_tex_source;
}
static void
Index: backend/dvi/mdvi-lib/dviread.c
===================================================================
--- backend/dvi/mdvi-lib/dviread.c (revision 3613)
+++ backend/dvi/mdvi-lib/dviread.c (working copy)
@@ -911,6 +911,19 @@
void mdvi_destroy_context(DviContext *dvi)
{
+ if(dvi->lasth) {
+ free(dvi->lasth);
+ free(dvi->lastv);
+ }
+ if(dvi->src) {
+ DviTeXSrc *p = dvi->src, *next;
+ do{
+ next = p->next;
+ free(p->source);
+ free(p);
+ p = next;
+ }while(p);
+ }
if(dvi->device.dev_destroy)
dvi->device.dev_destroy(dvi->device.device_data);
/* release all fonts */
Index: backend/dvi/mdvi-lib/mdvi.h
===================================================================
--- backend/dvi/mdvi-lib/mdvi.h (revision 3613)
+++ backend/dvi/mdvi-lib/mdvi.h (working copy)
@@ -38,6 +38,7 @@
typedef struct _DviPageSpec *DviPageSpec;
typedef struct _DviParams DviParams;
typedef struct _DviBuffer DviBuffer;
+typedef struct _DviTeXSrc DviTeXSrc;
typedef struct _DviContext DviContext;
typedef struct _DviRange DviRange;
typedef struct _DviColorPair DviColorPair;
@@ -357,7 +358,19 @@
Ulong bg;
};
+struct _DviTeXSrc {
+ char *source; /* TeX source */
+ int line; /* line number in TeX source */
+ int page; /* page number in DVI */
+ double x; /* horizontal position, 0~1 */
+ double y; /* vertical position, 0~1 */
+ struct _DviTeXSrc *next;
+};
+
struct _DviContext {
+ int *lastv; /* last TeX src v position in a certain page */
+ int *lasth; /* last TeX src h position in a certain page */
+ struct _DviTeXSrc *src; /* linked list */
char *filename; /* name of the DVI file */
FILE *in; /* from here we read */
char *fileid; /* from preamble */
Index: backend/dvi/mdvi-lib/special.c
===================================================================
--- backend/dvi/mdvi-lib/special.c (revision 3613)
+++ backend/dvi/mdvi-lib/special.c (working copy)
@@ -46,6 +46,7 @@
void x __PROTO((DviContext *, const char *, const char *))
static SPECIAL(sp_layer);
+static SPECIAL(sp_tex_src_special);
extern SPECIAL(epsf_special);
extern SPECIAL(do_color_special);
@@ -56,6 +57,7 @@
DviSpecialHandler handler;
} builtins[] = {
{"Layers", "layer", NULL, sp_layer},
+ {"TexSource", "src", NULL, sp_tex_src_special},
{"EPSF", "psfile", NULL, epsf_special}
};
#define NSPECIALS (sizeof(builtins) / sizeof(builtins[0]))
@@ -247,3 +249,80 @@
DEBUG((DBG_SPECIAL, "Layer level: %d\n", dvi->curr_layer));
}
+static int compare(DviTeXSrc *a, DviTeXSrc *b)
+{
+ if (a->page < b->page)
+ return -1;
+ if (a->page > b->page)
+ return 1;
+ if (a->y < b->y)
+ return -1;
+ if (a->y > b->y)
+ return 1;
+ if (a->x < b->x)
+ return -1;
+ if (a->x > b->x)
+ return 1;
+ return 0;
+}
+
+static void insert(DviContext *dvi, char *source, int line, int page, double x, double y)
+{
+ DviTeXSrc *a = (DviTeXSrc *)malloc(sizeof(DviTeXSrc)), *p;
+ a->source = source;
+ a->line = line;
+ a->page = page;
+ a->x = x;
+ a->y = y;
+ a->next = 0;
+ if (dvi->src == 0){
+ dvi->src = a;
+ } else if (compare(a, dvi->src) < 0) {
+ a->next = dvi->src;
+ dvi->src = a;
+ } else {
+ p = dvi->src;
+ while (p->next) {
+ if (compare(a, p->next) < 0)
+ break;
+ p = p->next;
+ }
+ a->next = p->next;
+ p->next = a;
+ }
+}
+
+static int caninsert(DviContext *dvi, int page, int v, int h)
+{
+ if(!dvi->lastv){
+ dvi->lastv = (int *)malloc(sizeof(int) * dvi->npages);
+ memset(dvi->lastv, 0, sizeof(int) * dvi->npages);
+ dvi->lasth = (int *)malloc(sizeof(int) * dvi->npages);
+ memset(dvi->lasth, 0, sizeof(int) * dvi->npages);
+ }
+ if(v > dvi->lastv[dvi->currpage])
+ return 1;
+ if(v < dvi->lastv[dvi->currpage])
+ return 0;
+ if(h > dvi->lasth[dvi->currpage])
+ return 1;
+ return 0;
+}
+
+void sp_tex_src_special(DviContext *dvi, const char *prefix, const char *arg)
+{
+ if(caninsert(dvi, dvi->currpage, dvi->pos.v, dvi->pos.h)){
+ double x = (double)(dvi->pos.h) / dvi->dvi_page_w;
+ double y = (double)(dvi->pos.v) / dvi->dvi_page_h;
+ int line = atoi(arg);
+ const char *source = arg;
+ while ('0' <= *source && *source <= '9')
+ source++;
+ source++;
+
+ insert(dvi, strdup (source), line, dvi->currpage, x, y);
+
+ dvi->lastv[dvi->currpage] = dvi->pos.v;
+ dvi->lasth[dvi->currpage] = dvi->pos.h;
+ }
+}
Index: backend/pdf/ev-poppler.cc
===================================================================
--- backend/pdf/ev-poppler.cc (revision 3613)
+++ backend/pdf/ev-poppler.cc (working copy)
@@ -102,6 +102,7 @@
PdfPrintContext *print_ctx;
GList *layers;
+ char *uri;
};
static void pdf_document_document_iface_init (EvDocumentIface *iface);
@@ -121,7 +122,15 @@
gint *width,
gint *height);
static int pdf_document_get_n_pages (EvDocument *document);
+static void pdf_open_tex_source (EvDocument *document,
+ int page,
+ double x,
+ double y,
+ int docx,
+ int docy,
+ const gchar* tex_editor);
+
static EvLinkDest *ev_link_dest_from_dest (PdfDocument *pdf_document,
PopplerDest *dest);
static EvLink *ev_link_from_action (PdfDocument *pdf_document,
@@ -292,6 +301,7 @@
return FALSE;
}
+ pdf_document->uri = strdup (uri);
return TRUE;
}
@@ -725,6 +735,7 @@
iface->get_attachments = pdf_document_get_attachments;
iface->render = pdf_document_render;
iface->get_info = pdf_document_get_info;
+ iface->open_tex_source = pdf_open_tex_source;
};
static void
@@ -1954,6 +1965,91 @@
iface->get_effect = pdf_document_get_effect;
}
+static void
+build_cmd_str (const gchar *tex_editor,
+ GString *cmd_str,
+ int page,
+ int docx,
+ int docy,
+ const gchar *uri)
+{
+ const gchar *c;
+ gchar *path;
+ gchar *file;
+
+ c = tex_editor;
+ while (*c) {
+ if (*c != '%')
+ g_string_append_c (cmd_str, *c);
+ else
+ switch (*(++c)) {
+ case '%':
+ g_string_append_c (cmd_str, '%');
+ break;
+ case 'p':
+ g_string_append_printf (cmd_str, "%d", page+1);
+ break;
+ case 'x':
+ g_string_append_printf (cmd_str, "%d", docx);
+ break;
+ case 'y':
+ g_string_append_printf (cmd_str, "%d", docy);
+ break;
+ case 'o':
+ g_string_append_printf (cmd_str, "%s", uri + 7);
+ break;
+ }
+ c++;
+ }
+}
+
+static void
+spawn_tex_editor (GString *cmd_str)
+{
+ gchar *cmd;
+ gint argc;
+ gchar **argv;
+ gboolean retval = FALSE;
+ GError *error = NULL;
+
+ cmd = g_string_free (cmd_str, FALSE);
+ g_shell_parse_argv (cmd, &argc, &argv, &error);
+ g_free (cmd);
+
+ if (!error) {
+ retval = gdk_spawn_on_screen (gdk_screen_get_default (),
+ NULL, argv, NULL,
+ G_SPAWN_SEARCH_PATH,
+ NULL, NULL, NULL,
+ &error);
+ g_strfreev (argv);
+ }
+
+ if (error) {
+ g_warning ("Error spawning '%s': %s\n", cmd, error->message);
+ g_error_free (error);
+ }
+}
+
+static void
+pdf_open_tex_source (EvDocument *document,
+ int page,
+ double x,
+ double y,
+ int docx,
+ int docy,
+ const gchar *tex_editor)
+{
+ PdfDocument *pdf_document = PDF_DOCUMENT (document);
+ GString *cmd_str = 0;
+
+ if (strncmp (pdf_document->uri, "file://", 7)) /* SyncTeX */
+ return;
+ cmd_str = g_string_new(NULL);
+ build_cmd_str (tex_editor, cmd_str, page, docx, docy, pdf_document->uri);
+ spawn_tex_editor (cmd_str);
+}
+
/* Forms */
static void
pdf_document_get_crop_box (EvDocument *document,
Index: libview/ev-view-private.h
===================================================================
--- libview/ev-view-private.h (revision 3613)
+++ libview/ev-view-private.h (working copy)
@@ -178,6 +178,8 @@
GtkWidget *goto_entry;
EvTransitionAnimation *animation;
+
+ gchar* tex_editor;
};
struct _EvViewClass {
Index: libview/ev-view.c
===================================================================
--- libview/ev-view.c (revision 3613)
+++ libview/ev-view.c (working copy)
@@ -309,6 +309,9 @@
static void ev_view_presentation_transition_start (EvView *ev_view);
static void ev_view_presentation_transition_stop (EvView *ev_view);
+static void ev_view_open_tex_source (EvView *view,
+ gint x,
+ gint y);
G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_LAYOUT)
@@ -2634,7 +2637,12 @@
EvImage *image;
EvFormField *field;
- if (EV_IS_SELECTION (view->document) && view->selection_info.selections) {
+ /* opens the corresponding TeX source */
+ if (event->type == GDK_2BUTTON_PRESS && ev_view_get_tex_editor(view) &&
+ (EV_DOCUMENT_GET_IFACE (view->document))->open_tex_source) {
+ ev_view_open_tex_source (view, event->x + view->scroll_x, event->y + view->scroll_y);
+ return TRUE; /* Stop subsequence processing. */
+ }else if (EV_IS_SELECTION (view->document) && view->selection_info.selections) {
if (event->type == GDK_3BUTTON_PRESS) {
start_selection_for_event (view, event);
} else if (location_in_selected_text (view,
@@ -3735,6 +3743,11 @@
{
EvView *view = EV_VIEW (object);
+ if (view->tex_editor) {
+ g_free (view->tex_editor);
+ view->tex_editor = NULL;
+ }
+
if (view->document) {
g_object_unref (view->document);
view->document = NULL;
@@ -4673,6 +4686,22 @@
return view->sizing_mode;
}
+void
+ev_view_set_tex_editor (EvView *view, const gchar *tex_editor)
+{
+ g_return_if_fail (EV_IS_VIEW (view));
+
+ view->tex_editor = g_strdup(tex_editor);
+}
+
+const gchar*
+ev_view_get_tex_editor (EvView *view)
+{
+ g_return_val_if_fail (EV_IS_VIEW (view), 0);
+
+ return view->tex_editor;
+}
+
gboolean
ev_view_can_zoom_in (EvView *view)
{
@@ -5209,6 +5238,29 @@
point->y < rectangle->y + rectangle->height;
}
+static void
+ev_view_open_tex_source (EvView *view, gint x, gint y)
+{
+ int page;
+ gint ox=0, oy=0;
+ gint docx=0, docy=0;
+ GdkRectangle page_area;
+ GtkBorder border;
+
+ find_page_at_location (view, x, y, &page, &ox, &oy);
+ if (page == -1)
+ return;
+ get_doc_point_from_offset (view, page, ox, oy, &docx, &docy);
+ get_page_extents (view, page, &page_area, &border);
+ (EV_DOCUMENT_GET_IFACE (view->document))->open_tex_source (view->document,
+ page,
+ (double)(x - page_area.x) / page_area.width,
+ (double)(y - page_area.y) / page_area.height,
+ docx,
+ docy,
+ view->tex_editor);
+}
+
static GList *
compute_new_selection_text (EvView *view,
EvSelectionStyle style,
Index: libview/ev-view.h
===================================================================
--- libview/ev-view.h (revision 3613)
+++ libview/ev-view.h (working copy)
@@ -84,8 +84,10 @@
void ev_view_set_sizing_mode (EvView *view,
EvSizingMode mode);
EvSizingMode ev_view_get_sizing_mode (EvView *view);
+void ev_view_set_tex_editor (EvView *view,
+ const gchar *tex_editor);
+const gchar* ev_view_get_tex_editor (EvView *view);
-
/* Page size */
gboolean ev_view_can_zoom_in (EvView *view);
void ev_view_zoom_in (EvView *view);
Index: libdocument/ev-document.h
===================================================================
--- libdocument/ev-document.h (revision 3613)
+++ libdocument/ev-document.h (working copy)
@@ -96,6 +96,13 @@
cairo_surface_t * (* render) (EvDocument *document,
EvRenderContext *rc);
EvDocumentInfo * (* get_info) (EvDocument *document);
+ void (* open_tex_source) (EvDocument *document,
+ int page,
+ double x,
+ double y,
+ int docx,
+ int docy,
+ const gchar* tex_editor);
};
GType ev_document_get_type (void) G_GNUC_CONST;
Index: shell/ev-application.c
===================================================================
--- shell/ev-application.c (revision 3613)
+++ shell/ev-application.c (working copy)
@@ -385,6 +385,18 @@
return value ? g_value_get_string (value) : NULL;
}
+static const gchar *
+get_tex_editor_from_args (GHashTable *args)
+{
+ GValue *value = NULL;
+
+ g_assert (args != NULL);
+
+ value = g_hash_table_lookup (args, "tex_editor");
+
+ return value ? g_value_get_string (value) : NULL;
+}
+
/**
* ev_application_open_window:
* @application: The instance of the application.
@@ -539,7 +551,7 @@
ev_stock_icons_add_icons_path_for_screen (screen);
gtk_window_set_screen (GTK_WINDOW (new_window), screen);
}
-
+
/* We need to load uri before showing the window, so
we can restore window size without flickering */
ev_window_open_uri (new_window, uri, dest, mode, search_string);
@@ -581,14 +593,19 @@
EvWindowRunMode mode = EV_WINDOW_MODE_NORMAL;
const gchar *search_string = NULL;
GdkScreen *screen = NULL;
+ const gchar *tex_editor = NULL;
if (args) {
screen = get_screen_from_args (args);
dest = get_destination_from_args (args);
mode = get_window_run_mode_from_args (args);
search_string = get_find_string_from_args (args);
+ tex_editor = get_tex_editor_from_args (args);
}
-
+
+ ev_metadata_manager_init();
+ ev_metadata_manager_set_string(uri, "tex_editor", tex_editor);
+
ev_application_open_uri_at_dest (application, uri, screen,
dest, mode, search_string,
timestamp);
Index: shell/ev-window.c
===================================================================
--- shell/ev-window.c (revision 3613)
+++ shell/ev-window.c (working copy)
@@ -1051,7 +1051,14 @@
GValue presentation = { 0, };
GValue fullscreen = { 0, };
GValue rotation = { 0, };
+ GValue tex_editor = { 0, };
+ /* TeX editor */
+ if (ev_metadata_manager_get (uri, "tex_editor", &tex_editor, FALSE)) {
+ ev_view_set_tex_editor (view, g_value_get_string(&tex_editor));
+ g_value_unset (&tex_editor);
+ }
+
/* Sizing mode */
if (ev_metadata_manager_get (uri, "sizing_mode", &sizing_mode, FALSE)) {
enum_value = g_enum_get_value_by_nick
@@ -1780,7 +1787,6 @@
const gchar *search_string)
{
GFile *source_file;
-
ev_window->priv->in_reload = FALSE;
if (ev_window->priv->uri &&
Index: shell/main.c
===================================================================
--- shell/main.c (revision 3613)
+++ shell/main.c (working copy)
@@ -49,6 +49,7 @@
static gboolean presentation_mode = FALSE;
static gboolean unlink_temp_file = FALSE;
static gchar *print_settings;
+static gchar *tex_editor;
static const char **file_arguments = NULL;
static gboolean
@@ -73,6 +74,7 @@
{ "unlink-tempfile", 'u', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &unlink_temp_file, NULL, NULL },
{ "print-settings", 't', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_FILENAME, &print_settings, NULL, NULL },
{ "version", 0, G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, option_version_cb, NULL, NULL },
+ { "editor", 'e', 0, G_OPTION_ARG_STRING, &tex_editor, N_("A double-click in the evince window opens the corresponding TeX source"), N_("STRING")},
{ G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &file_arguments, NULL, N_("[FILE...]") },
{ NULL }
};
@@ -210,6 +212,17 @@
ev_page_label = NULL;
}
+ if (tex_editor) {
+ value = g_new0 (GValue, 1);
+ g_value_init (value, G_TYPE_STRING);
+ g_value_set_string (value, tex_editor);
+
+ g_hash_table_insert (args, g_strdup ("tex_editor"), value);
+
+ g_free (tex_editor);
+ tex_editor = NULL;
+ }
+
if (fullscreen_mode)
mode = EV_WINDOW_MODE_FULLSCREEN;
else if (presentation_mode)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]