Hello, everybody The following patch add "open Tex source file" support for evince. It means that, if some person compiles a LaTeX source file by "latex --src-specials main.tex" and she starts evince by "evince --editor='gedit +%l %f' main.dvi", then when she double-clicked a characters in the page, "gedit" will open "main.tex" and jumps to the corresponding place.
This feature is also called "Inverse search", which is a feature of xdvi. See also http://xdvi.sourceforge.net/inverse-search.html This patch is upon the svn trunk revision 3613. Cheers, Homer Index: backend/dvi/dvi-document.c =================================================================== --- backend/dvi/dvi-document.c (revision 3613) +++ backend/dvi/dvi-document.c (working copy) @@ -253,6 +253,102 @@ return info; } +static void +dvi_open_tex_source (EvDocument *document, + int page, + double x, + double y, + const gchar *tex_editor) +{ + DviDocument *dvi_document = DVI_DOCUMENT (document); + DviContext *dvi = dvi_document->context; + DviTeXSrc *p, *found; + double dist, dist2; + GString *cmd_str; + gchar *cmd; + gint argc; + gchar **argv; + gboolean retval = FALSE; + GError *error = NULL; + const gchar *c; + gchar *path; + gchar *file; + + if (!dvi->src) + return; + + 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; + } + + cmd_str = g_string_new (""); + 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", found->line); + break; + case 'f': + if (found->source[0] == '/') + g_string_append (cmd_str, found->source); + else { + path = g_strdup (dvi_document->uri); + * (strrchr (path, '/')+1) = 0; + file = strrchr (found->source, '/'); + if (!file) + file = found->source; + else + file++; + g_string_append (cmd_str, path); + g_string_append (cmd_str, file); + g_free(path); + } + } + c++; + } + + 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 launching '%s': %s\n", tex_editor, error->message); + g_error_free (error); + } +} + static void dvi_document_document_iface_init (EvDocumentIface *iface) { @@ -262,6 +358,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: 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) @@ -133,6 +133,8 @@ gint *page, gint *x_offset, gint *y_offset); +static int find_page_contains (EvView *view, + EvPoint *p); static gboolean doc_point_to_view_point (EvView *view, int page, EvPoint *doc_point, @@ -309,6 +311,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, + EvPoint *p, + int page); G_DEFINE_TYPE (EvView, ev_view, GTK_TYPE_LAYOUT) @@ -2634,7 +2639,19 @@ 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) { + EvPoint p; + int page; + p.x = event->x + view->scroll_x; + p.y = event->y + view->scroll_y; + if ((page = find_page_contains(view, &p)) != -1) { + ev_view_open_tex_source(view, &p, page); + } + 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 +3752,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 +4695,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 +5247,56 @@ point->y < rectangle->y + rectangle->height; } +static int +find_page_contains (EvView *view, EvPoint *p) +{ + int n_pages; + int start_page, end_page; + int i; + GdkRectangle page_area; + GtkBorder border; + + n_pages = ev_page_cache_get_n_pages (view->page_cache); + if (view->continuous) { + start_page = 0; + end_page = n_pages; + } else if (view->dual_page) { + start_page = view->start_page; + end_page = view->end_page + 1; + } else { + start_page = view->current_page; + end_page = view->current_page + 1; + } + + for (i = start_page; i < end_page; i++) { + get_page_extents (view, i, &page_area, &border); + + if (page_area.x <= p->x && + page_area.y <= p->y && + p->x < page_area.x + page_area.width && + p->y < page_area.y + page_area.height) + return i; + } + + return -1; +} + +static void +ev_view_open_tex_source(EvView *view, EvPoint *p, int page) +{ + GdkRectangle page_area; + GtkBorder border; + + get_page_extents (view, page, &page_area, &border); + p->x = (p->x - page_area.x)/page_area.width; + p->y = (p->y - page_area.y)/page_area.height; + (EV_DOCUMENT_GET_IFACE (view->document))->open_tex_source(view->document, + page, + p->x, + p->y, + 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,11 @@ 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, + 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) _______________________________________________ Evince-list mailing list [email protected] http://mail.gnome.org/mailman/listinfo/evince-list
