synctex.c 5.13 KB
Newer Older
1 2
/* See LICENSE file for license and copyright information */

3
#include <glib.h>
4
#include <girara/utils.h>
Moritz Lipp's avatar
Moritz Lipp committed
5 6

#ifdef WITH_SYSTEM_SYNCTEX
7
#include <synctex/synctex_parser.h>
Moritz Lipp's avatar
Moritz Lipp committed
8 9 10
#else
#include "synctex/synctex_parser.h"
#endif
11

12 13 14 15
#include "synctex.h"
#include "zathura.h"
#include "page.h"
#include "document.h"
16 17
#include "utils.h"

18 19 20
bool
synctex_get_input_line_column(const char* filename, unsigned int page, int x, int y,
    char** input_file, unsigned int* line, unsigned int* column)
21
{
Moritz Lipp's avatar
Moritz Lipp committed
22
  if (filename == NULL) {
23
    return false;
Moritz Lipp's avatar
Moritz Lipp committed
24 25
  }

26 27 28
  synctex_scanner_t scanner = synctex_scanner_new_with_output_file(filename, NULL, 1);
  if (scanner == NULL) {
    girara_debug("Failed to create synctex scanner.");
29
    return false;
30 31 32 33 34 35
  }

  synctex_scanner_t temp = synctex_scanner_parse(scanner);
  if (temp == NULL) {
    girara_debug("Failed to parse synctex file.");
    synctex_scanner_free(scanner);
36
    return false;
37 38
  }

39 40 41 42
  bool ret = false;

  if (synctex_edit_query(scanner, page + 1u, x, y) > 0) {
    /* Assume that a backward search returns at most one result. */
43 44
    synctex_node_t node = synctex_next_result(scanner);
    if (node != NULL) {
45 46 47 48 49 50 51 52
      if (input_file != NULL) {
        *input_file = g_strdup(synctex_scanner_get_name(scanner, synctex_node_tag(node)));
      }
      if (line != NULL) {
        *line = synctex_node_line(node);
      }
      if (column != NULL) {
        *column = synctex_node_column(node);
53 54
      }

55
      ret = true;
56
    }
57 58
  }

59
  synctex_scanner_free(scanner);
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113

  return ret;
}

void
synctex_edit(const char* editor, zathura_page_t* page, int x, int y)
{
  if (editor == NULL || page == NULL) {
    return;
  }

  zathura_document_t* document = zathura_page_get_document(page);
  if (document == NULL) {
    return;
  }

  const char* filename = zathura_document_get_path(document);
  if (filename == NULL) {
    return;
  }

  unsigned int line = 0;
  unsigned int column = 0;
  char* input_file = NULL;

  if (synctex_get_input_line_column(filename, zathura_page_get_index(page), x, y,
        &input_file, &line, &column) == true) {
    char* linestr = g_strdup_printf("%d", line);
    char* columnstr = g_strdup_printf("%d", column);

    gchar** argv = NULL;
    gint    argc = 0;
    if (g_shell_parse_argv(editor, &argc, &argv, NULL) == TRUE) {
      for (gint i = 0; i != argc; ++i) {
        char* temp = girara_replace_substring(argv[i], "%{line}", linestr);
        g_free(argv[i]);
        argv[i] = temp;
        temp = girara_replace_substring(argv[i], "%{column}", columnstr);
        g_free(argv[i]);
        argv[i] = temp;
        temp = girara_replace_substring(argv[i], "%{input}", input_file);
        g_free(argv[i]);
        argv[i] = temp;
      }

      g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
      g_strfreev(argv);
    }

    g_free(linestr);
    g_free(columnstr);

    g_free(input_file);
  }
114
}
115

116
girara_list_t*
117 118
synctex_rectangles_from_position(const char* filename, const char* input_file,
                                 int line, int column, unsigned int* page,
119
                                 girara_list_t** secondary_rects)
120
{
121
  if (filename == NULL || input_file == NULL || page == NULL) {
122
    return NULL;
123 124
  }

125 126 127 128
  /* We use indexes starting at 0 but SyncTeX uses 1 */
  ++line;
  ++column;

129 130 131
  synctex_scanner_t scanner = synctex_scanner_new_with_output_file(filename, NULL, 1);
  if (scanner == NULL) {
    girara_debug("Failed to create synctex scanner.");
132 133 134
    return NULL;
  }

135 136 137 138 139
  synctex_scanner_t temp = synctex_scanner_parse(scanner);
  if (temp == NULL) {
    girara_debug("Failed to parse synctex file.");
    synctex_scanner_free(scanner);
    return NULL;
140 141
  }

142
  girara_list_t* hitlist     = girara_list_new2(g_free);
143
  girara_list_t* other_rects = girara_list_new2(g_free);
144

145 146 147
  if (synctex_display_query(scanner, input_file, line, column) > 0) {
    synctex_node_t node        = NULL;
    bool got_page              = false;
148

149 150 151 152 153 154
    while ((node = synctex_next_result (scanner)) != NULL) {
      const unsigned int current_page = synctex_node_page(node) - 1;
      if (got_page == false) {
        got_page = true;
        *page = current_page;
      }
155

156 157 158 159 160
      zathura_rectangle_t rect = { 0, 0, 0, 0 };
      rect.x1 = synctex_node_box_visible_h(node);
      rect.y1 = synctex_node_box_visible_v(node) - synctex_node_box_visible_height(node);
      rect.x2 = rect.x1 + synctex_node_box_visible_width(node);
      rect.y2 = synctex_node_box_visible_depth(node) + synctex_node_box_visible_height (node) + rect.y1;
161

162 163 164 165
      if (*page == current_page) {
        zathura_rectangle_t* real_rect = g_try_malloc(sizeof(zathura_rectangle_t));
        if (real_rect == NULL) {
          continue;
166 167
        }

168
        *real_rect = rect;
169
        girara_list_append(hitlist, real_rect);
170 171 172 173 174 175
      } else {
        synctex_page_rect_t* page_rect = g_try_malloc(sizeof(synctex_page_rect_t));
        if (page_rect == NULL) {
          continue;
        }

176
        page_rect->page = current_page;
177 178
        page_rect->rect = rect;

179 180
        girara_list_append(other_rects, page_rect);
      }
181
    }
182 183
  }

184
  synctex_scanner_free(scanner);
185

186 187 188 189 190 191
  if (secondary_rects != NULL) {
    *secondary_rects = other_rects;
  } else {
    girara_list_free(other_rects);
  }

192
  return hitlist;
193
}
194