page-widget.c 21.5 KB
Newer Older
1 2 3 4
/* See LICENSE file for license and copyright information */

#include <girara/utils.h>
#include <girara/settings.h>
5
#include <girara/datastructures.h>
6 7 8
#include <girara/session.h>
#include <string.h>
#include <glib/gi18n.h>
9

10
#include "page-widget.h"
11 12 13 14
#include "render.h"
#include "utils.h"
#include "shortcuts.h"

15
G_DEFINE_TYPE(ZathuraPage, zathura_page_widget, GTK_TYPE_DRAWING_AREA)
16

17
typedef struct zathura_page_widget_private_s {
18 19
  zathura_page_t* page;
  zathura_t* zathura;
20
  cairo_surface_t* surface; /**< Cairo surface */
21
  GStaticMutex lock; /**< Lock */
Moritz Lipp's avatar
Moritz Lipp committed
22 23 24
  girara_list_t* links; /**< List of links on the page */
  bool links_got; /**< True if we already tried to retrieve the list of links */
  bool draw_links; /**< True if links should be drawn */
Moritz Lipp's avatar
Moritz Lipp committed
25 26
  unsigned int link_offset; /**< Offset to the links */
  unsigned int number_of_links; /**< Offset to the links */
27 28 29 30 31 32 33
  girara_list_t* search_results; /**< A list if there are search results that should be drawn */
  int search_current; /**< The index of the current search result */
  zathura_rectangle_t selection; /**< Region selected with the mouse */
  struct {
    int x;
    int y;
  } selection_basepoint;
34
} zathura_page_widget_private_t;
35

36 37
#define ZATHURA_PAGE_GET_PRIVATE(obj) \
  (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ZATHURA_TYPE_PAGE, zathura_page_widget_private_t))
38

39 40
static gboolean zathura_page_widget_draw(GtkWidget* widget, cairo_t* cairo);
#if GTK_MAJOR_VERSION == 2
41
static gboolean zathura_page_widget_expose(GtkWidget* widget, GdkEventExpose* event);
42
#endif
43 44 45 46 47 48
static void zathura_page_widget_finalize(GObject* object);
static void zathura_page_widget_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec);
static void zathura_page_widget_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec);
static void zathura_page_widget_size_allocate(GtkWidget* widget, GdkRectangle* allocation);
static void redraw_rect(ZathuraPage* widget, zathura_rectangle_t* rectangle);
static void redraw_all_rects(ZathuraPage* widget, girara_list_t* rectangles);
49
static gboolean cb_zathura_page_widget_button_press_event(GtkWidget* widget, GdkEventButton* button);
50 51
static gboolean cb_zathura_page_widget_button_release_event(GtkWidget* widget, GdkEventButton* button);
static gboolean cb_zathura_page_widget_motion_notify(GtkWidget* widget, GdkEventMotion* event);
52 53 54 55

enum properties_e
{
  PROP_0,
Moritz Lipp's avatar
Moritz Lipp committed
56 57
  PROP_PAGE,
  PROP_DRAW_LINKS,
Moritz Lipp's avatar
Moritz Lipp committed
58 59
  PROP_LINKS_OFFSET,
  PROP_LINKS_NUMBER,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
60
  PROP_SEARCH_RESULT,
61 62 63
  PROP_SEARCH_RESULTS,
  PROP_SEARCH_RESULTS_LENGTH,
  PROP_SEARCH_RESULTS_CURRENT
64 65 66
};

static void
67
zathura_page_widget_class_init(ZathuraPageClass* class)
68 69
{
  /* add private members */
70
  g_type_class_add_private(class, sizeof(zathura_page_widget_private_t));
71 72 73

  /* overwrite methods */
  GtkWidgetClass* widget_class = GTK_WIDGET_CLASS(class);
74
#if GTK_MAJOR_VERSION == 3
75
  widget_class->draw                 = zathura_page_widget_draw;
76
#else
77
  widget_class->expose_event         = zathura_page_widget_expose;
78
#endif
79 80
  widget_class->size_allocate        = zathura_page_widget_size_allocate;
  widget_class->button_press_event   = cb_zathura_page_widget_button_press_event;
81
  widget_class->button_release_event = cb_zathura_page_widget_button_release_event;
82
  widget_class->motion_notify_event  = cb_zathura_page_widget_motion_notify;
83 84

  GObjectClass* object_class = G_OBJECT_CLASS(class);
85
  object_class->finalize     = zathura_page_widget_finalize;
86 87
  object_class->set_property = zathura_page_widget_set_property;
  object_class->get_property = zathura_page_widget_get_property;
88 89 90 91

  /* add properties */
  g_object_class_install_property(object_class, PROP_PAGE,
      g_param_spec_pointer("page", "page", "the page to draw", G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
Moritz Lipp's avatar
Moritz Lipp committed
92 93
  g_object_class_install_property(object_class, PROP_DRAW_LINKS,
      g_param_spec_boolean("draw-links", "draw-links", "Set to true if links should be drawn", FALSE, G_PARAM_WRITABLE));
Moritz Lipp's avatar
Moritz Lipp committed
94 95 96 97
  g_object_class_install_property(object_class, PROP_LINKS_OFFSET,
      g_param_spec_int("offset-links", "offset-links", "Offset for the link numbers", 0, INT_MAX, 0, G_PARAM_WRITABLE));
  g_object_class_install_property(object_class, PROP_LINKS_NUMBER,
      g_param_spec_int("number-of-links", "number-of-links", "Number of links", 0, INT_MAX, 0, G_PARAM_READABLE));
98
  g_object_class_install_property(object_class, PROP_SEARCH_RESULTS,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
99
      g_param_spec_pointer("search-results", "search-results", "Set to the list of search results", G_PARAM_WRITABLE | G_PARAM_READABLE));
100 101 102 103
  g_object_class_install_property(object_class, PROP_SEARCH_RESULTS_CURRENT,
      g_param_spec_int("search-current", "search-current", "The current search result", -1, INT_MAX, 0, G_PARAM_WRITABLE | G_PARAM_READABLE));
  g_object_class_install_property(object_class, PROP_SEARCH_RESULTS_LENGTH,
      g_param_spec_int("search-length", "search-length", "The number of search results", -1, INT_MAX, 0, G_PARAM_READABLE));
104 105 106
}

static void
107
zathura_page_widget_init(ZathuraPage* widget)
108
{
109
  zathura_page_widget_private_t* priv = ZATHURA_PAGE_GET_PRIVATE(widget);
Moritz Lipp's avatar
Moritz Lipp committed
110 111 112 113 114
  priv->page           = NULL;
  priv->surface        = NULL;
  priv->links          = NULL;
  priv->links_got      = false;
  priv->link_offset    = 0;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
115 116
  priv->search_results = NULL;
  priv->search_current = INT_MAX;
Moritz Lipp's avatar
Moritz Lipp committed
117
  priv->selection.x1   = -1;
118 119
  priv->selection_basepoint.x = -1;
  priv->selection_basepoint.y = -1;
120 121 122 123
  g_static_mutex_init(&(priv->lock));

  /* we want mouse events */
  gtk_widget_add_events(GTK_WIDGET(widget),
124
    GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_POINTER_MOTION_MASK);
125 126 127
}

GtkWidget*
128
zathura_page_widget_new(zathura_page_t* page)
129 130 131
{
  g_return_val_if_fail(page != NULL, NULL);

132
  return g_object_new(ZATHURA_TYPE_PAGE, "page", page, NULL);
133 134 135
}

static void
136
zathura_page_widget_finalize(GObject* object)
137
{
138 139
  ZathuraPage* widget = ZATHURA_PAGE(object);
  zathura_page_widget_private_t* priv = ZATHURA_PAGE_GET_PRIVATE(widget);
140

Sebastian Ramacher's avatar
Sebastian Ramacher committed
141
  if (priv->surface != NULL) {
142 143
    cairo_surface_destroy(priv->surface);
  }
144 145 146 147 148 149 150 151 152

  if (priv->search_results != NULL) {
    girara_list_free(priv->search_results);
  }

  if (priv->links != NULL) {
    girara_list_free(priv->links);
  }

153 154
  g_static_mutex_free(&(priv->lock));

155
  G_OBJECT_CLASS(zathura_page_widget_parent_class)->finalize(object);
156 157 158
}

static void
159
zathura_page_widget_set_property(GObject* object, guint prop_id, const GValue* value, GParamSpec* pspec)
160
{
161 162
  ZathuraPage* pageview = ZATHURA_PAGE(object);
  zathura_page_widget_private_t* priv = ZATHURA_PAGE_GET_PRIVATE(pageview);
163 164 165

  switch (prop_id) {
    case PROP_PAGE:
Moritz Lipp's avatar
Moritz Lipp committed
166
      priv->page    = g_value_get_pointer(value);
167 168
      priv->zathura = priv->page->document->zathura;
      break;
Moritz Lipp's avatar
Moritz Lipp committed
169 170 171 172
    case PROP_DRAW_LINKS:
      priv->draw_links = g_value_get_boolean(value);
      /* get links */
      if (priv->draw_links == true && priv->links_got == false) {
Moritz Lipp's avatar
Moritz Lipp committed
173 174
        priv->links           = zathura_page_links_get(priv->page, NULL);
        priv->links_got       = true;
175
        priv->number_of_links = (priv->links == NULL) ? 0 : girara_list_size(priv->links);
Moritz Lipp's avatar
Moritz Lipp committed
176 177 178 179 180 181 182 183 184
      }

      if (priv->links_got == true && priv->links != NULL) {
        GIRARA_LIST_FOREACH(priv->links, zathura_link_t*, iter, link)
          zathura_rectangle_t rectangle = recalc_rectangle(priv->page, link->position);
          redraw_rect(pageview, &rectangle);
        GIRARA_LIST_FOREACH_END(priv->links, zathura_link_t*, iter, link);
      }
      break;
Moritz Lipp's avatar
Moritz Lipp committed
185 186 187
    case PROP_LINKS_OFFSET:
      priv->link_offset = g_value_get_int(value);
      break;
188 189 190 191 192 193 194 195 196 197
    case PROP_SEARCH_RESULTS:
      if (priv->search_results != NULL) {
        redraw_all_rects(pageview, priv->search_results);
        girara_list_free(priv->search_results);
      }
      priv->search_results = g_value_get_pointer(value);
      if (priv->search_results != NULL) {
        priv->draw_links = false;
        redraw_all_rects(pageview, priv->search_results);
      }
198
      priv->search_current = -1;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
199
      break;
200 201
    case PROP_SEARCH_RESULTS_CURRENT: {
      g_return_if_fail(priv->search_results != NULL);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
202
      if (priv->search_current >= 0 && priv->search_current < (signed) girara_list_size(priv->search_results)) {
203 204 205 206 207
        zathura_rectangle_t* rect = girara_list_nth(priv->search_results, priv->search_current);
        zathura_rectangle_t rectangle = recalc_rectangle(priv->page, *rect);
        redraw_rect(pageview, &rectangle);
      }
      int val = g_value_get_int(value);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
208
      if (val < 0) {
209 210 211 212 213 214 215 216 217
        priv->search_current = girara_list_size(priv->search_results);
      } else {
        priv->search_current = val;
        zathura_rectangle_t* rect = girara_list_nth(priv->search_results, priv->search_current);
        zathura_rectangle_t rectangle = recalc_rectangle(priv->page, *rect);
        redraw_rect(pageview, &rectangle);
      }
      break;
    }
Moritz Lipp's avatar
Moritz Lipp committed
218 219 220 221 222 223
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
  }
}

static void
224
zathura_page_widget_get_property(GObject* object, guint prop_id, GValue* value, GParamSpec* pspec)
Moritz Lipp's avatar
Moritz Lipp committed
225
{
226 227
  ZathuraPage* pageview = ZATHURA_PAGE(object);
  zathura_page_widget_private_t* priv = ZATHURA_PAGE_GET_PRIVATE(pageview);
Moritz Lipp's avatar
Moritz Lipp committed
228 229 230 231

  switch (prop_id) {
    case PROP_LINKS_NUMBER:
      g_value_set_int(value, priv->number_of_links);
232
      break;
233 234 235 236 237 238
    case PROP_SEARCH_RESULTS_LENGTH:
      g_value_set_int(value, priv->search_results == NULL ? 0 : girara_list_size(priv->search_results));
      break;
    case PROP_SEARCH_RESULTS_CURRENT:
      g_value_set_int(value, priv->search_results == NULL ? -1 : priv->search_current);
      break;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
239 240 241
    case PROP_SEARCH_RESULTS:
      g_value_set_pointer(value, priv->search_results);
      break;
242 243 244 245 246
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
  }
}

247
#if GTK_MAJOR_VERSION == 2
248
static gboolean
249
zathura_page_widget_expose(GtkWidget* widget, GdkEventExpose* event)
250
{
251
  cairo_t* cairo = gdk_cairo_create(gtk_widget_get_window(widget));
252 253 254 255 256 257 258 259 260
  if (cairo == NULL) {
    girara_error("Could not retreive cairo object");
    return FALSE;
  }

  /* set clip region */
  cairo_rectangle(cairo, event->area.x, event->area.y, event->area.width, event->area.height);
  cairo_clip(cairo);

261 262 263 264 265 266 267 268 269
  const gboolean ret = zathura_page_widget_draw(widget, cairo);
  cairo_destroy(cairo);
  return ret;
}
#endif

static gboolean
zathura_page_widget_draw(GtkWidget* widget, cairo_t* cairo)
{
270
  zathura_page_widget_private_t* priv = ZATHURA_PAGE_GET_PRIVATE(widget);
271
  g_static_mutex_lock(&(priv->lock));
272 273

#if GTK_MAJOR_VERSION == 2
274 275 276 277
  GtkAllocation allocation;
  gtk_widget_get_allocation(widget, &allocation);
  const unsigned int page_height = allocation.height;
  const unsigned int page_width  = allocation.width;
278 279 280 281 282
#else
  const unsigned int page_height = gtk_widget_get_allocated_height(widget);
  const unsigned int page_width  = gtk_widget_get_allocated_width(widget);
#endif

283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
  if (priv->surface != NULL) {
    cairo_save(cairo);

    switch (priv->page->document->rotate) {
      case 90:
        cairo_translate(cairo, page_width, 0);
        break;
      case 180:
        cairo_translate(cairo, page_width, page_height);
        break;
      case 270:
        cairo_translate(cairo, 0, page_height);
        break;
    }

    if (priv->page->document->rotate != 0) {
      cairo_rotate(cairo, priv->page->document->rotate * G_PI / 180.0);
    }

    cairo_set_source_surface(cairo, priv->surface, 0, 0);
    cairo_paint(cairo);
    cairo_restore(cairo);
305 306 307 308 309 310 311 312 313 314 315

    /* draw rectangles */
    char* font = NULL;
    girara_setting_get(priv->zathura->ui.session, "font", &font);

    float transparency = 0.5;
    girara_setting_get(priv->zathura->ui.session, "highlight-transparency", &transparency);

    if (font != NULL) {
      cairo_select_font_face(cairo, font, CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
    }
316
    g_free(font);
317

Moritz Lipp's avatar
Moritz Lipp committed
318
    /* draw links */
319
    if (priv->draw_links == true && priv->number_of_links != 0) {
Moritz Lipp's avatar
Moritz Lipp committed
320
      unsigned int link_counter = 0;
Moritz Lipp's avatar
Moritz Lipp committed
321 322 323 324 325 326 327 328 329
      GIRARA_LIST_FOREACH(priv->links, zathura_link_t*, iter, link)
        zathura_rectangle_t rectangle = recalc_rectangle(priv->page, link->position);

        /* draw position */
        GdkColor color = priv->zathura->ui.colors.highlight_color;
        cairo_set_source_rgba(cairo, color.red, color.green, color.blue, transparency);
        cairo_rectangle(cairo, rectangle.x1, rectangle.y1,
            (rectangle.x2 - rectangle.x1), (rectangle.y2 - rectangle.y1));
        cairo_fill(cairo);
330 331 332 333

        /* draw text */
        cairo_set_source_rgba(cairo, 0, 0, 0, 1);
        cairo_set_font_size(cairo, 10);
334
        cairo_move_to(cairo, rectangle.x1 + 1, rectangle.y2 - 1);
335 336 337
        char* link_number = g_strdup_printf("%i", priv->link_offset + ++link_counter);
        cairo_show_text(cairo, link_number);
        g_free(link_number);
Moritz Lipp's avatar
Moritz Lipp committed
338 339
      GIRARA_LIST_FOREACH_END(priv->links, zathura_link_t*, iter, link);
    }
340 341 342

    /* draw search results */
    if (priv->search_results != NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
343
      int idx = 0;
344 345 346 347
      GIRARA_LIST_FOREACH(priv->search_results, zathura_rectangle_t*, iter, rect)
        zathura_rectangle_t rectangle = recalc_rectangle(priv->page, *rect);

        /* draw position */
348
        if (idx == priv->search_current) {
349 350
          GdkColor color = priv->zathura->ui.colors.highlight_color_active;
          cairo_set_source_rgba(cairo, color.red, color.green, color.blue, transparency);
351
        } else {
352
          GdkColor color = priv->zathura->ui.colors.highlight_color;
353 354
          cairo_set_source_rgba(cairo, color.red, color.green, color.blue, transparency);
        }
355 356 357
        cairo_rectangle(cairo, rectangle.x1, rectangle.y1,
            (rectangle.x2 - rectangle.x1), (rectangle.y2 - rectangle.y1));
        cairo_fill(cairo);
358
        ++idx;
359 360
      GIRARA_LIST_FOREACH_END(priv->search_results, zathura_rectangle_t*, iter, rect);
    }
361 362 363 364 365 366 367 368
    /* draw selection */
    if (priv->selection.y2 != -1 && priv->selection.x2 != -1) {
        GdkColor color = priv->zathura->ui.colors.highlight_color;
        cairo_set_source_rgba(cairo, color.red, color.green, color.blue, transparency);
        cairo_rectangle(cairo, priv->selection.x1, priv->selection.y1,
            (priv->selection.x2 - priv->selection.x1), (priv->selection.y2 - priv->selection.y1));
        cairo_fill(cairo);
    }
369 370 371
  } else {
    /* set background color */
    cairo_set_source_rgb(cairo, 255, 255, 255);
372
    cairo_rectangle(cairo, 0, 0, page_width, page_height);
373 374 375 376 377 378 379 380 381 382 383 384 385
    cairo_fill(cairo);

    bool render_loading = true;
    girara_setting_get(priv->zathura->ui.session, "render-loading", &render_loading);

    /* write text */
    if (render_loading == true) {
      cairo_set_source_rgb(cairo, 0, 0, 0);
      const char* text = "Loading...";
      cairo_select_font_face(cairo, "Sans", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
      cairo_set_font_size(cairo, 16.0);
      cairo_text_extents_t extents;
      cairo_text_extents(cairo, text, &extents);
386 387
      double x = page_width * 0.5 - (extents.width * 0.5 + extents.x_bearing);
      double y = page_height * 0.5 - (extents.height * 0.5 + extents.y_bearing);
388 389 390 391 392 393 394 395 396 397 398 399
      cairo_move_to(cairo, x, y);
      cairo_show_text(cairo, text);
    }

    /* render real page */
    render_page(priv->zathura->sync.render_thread, priv->page);
  }
  g_static_mutex_unlock(&(priv->lock));
  return FALSE;
}

static void
400
zathura_page_widget_redraw_canvas(ZathuraPage* pageview)
401 402 403 404 405 406
{
  GtkWidget* widget = GTK_WIDGET(pageview);
  gtk_widget_queue_draw(widget);
}

void
407
zathura_page_widget_update_surface(ZathuraPage* widget, cairo_surface_t* surface)
408
{
409
  zathura_page_widget_private_t* priv = ZATHURA_PAGE_GET_PRIVATE(widget);
410
  g_static_mutex_lock(&(priv->lock));
Moritz Lipp's avatar
CS  
Moritz Lipp committed
411
  if (priv->surface != NULL) {
412 413 414 415 416
    cairo_surface_destroy(priv->surface);
  }
  priv->surface = surface;
  g_static_mutex_unlock(&(priv->lock));
  /* force a redraw here */
417
  zathura_page_widget_redraw_canvas(widget);
418 419 420
}

static void
421
zathura_page_widget_size_allocate(GtkWidget* widget, GdkRectangle* allocation)
422
{
423 424
  GTK_WIDGET_CLASS(zathura_page_widget_parent_class)->size_allocate(widget, allocation);
  zathura_page_widget_update_surface(ZATHURA_PAGE(widget), NULL);
425
}
426 427

static void
428
redraw_rect(ZathuraPage* widget, zathura_rectangle_t* rectangle)
429 430 431
{
   /* cause the rect to be drawn */
  GdkRectangle grect;
Moritz Lipp's avatar
Moritz Lipp committed
432
  grect.x = rectangle->x1;
433
  grect.y = rectangle->y1;
Moritz Lipp's avatar
Moritz Lipp committed
434
  grect.width  = rectangle->x2 - rectangle->x1;
435
  grect.height = rectangle->y2 - rectangle->y1;
436
#if (GTK_MAJOR_VERSION == 3)
437
  gtk_widget_queue_draw_area(GTK_WIDGET(widget), grect.x, grect.y, grect.width, grect.height);
438
#else
439
  gdk_window_invalidate_rect(gtk_widget_get_window(GTK_WIDGET(widget)), &grect, TRUE);
440
#endif
441 442
}

443
static void
444
redraw_all_rects(ZathuraPage* widget, girara_list_t* rectangles)
445
{
446
  zathura_page_widget_private_t* priv = ZATHURA_PAGE_GET_PRIVATE(widget);
447 448 449 450 451 452 453

  GIRARA_LIST_FOREACH(rectangles, zathura_rectangle_t*, iter, rect)
    zathura_rectangle_t rectangle = recalc_rectangle(priv->page, *rect);
    redraw_rect(widget, &rectangle);
  GIRARA_LIST_FOREACH_END(rectangles, zathura_recantgle_t*, iter, rect);
}

Moritz Lipp's avatar
Moritz Lipp committed
454
zathura_link_t*
455
zathura_page_widget_link_get(ZathuraPage* widget, unsigned int index)
456
{
Moritz Lipp's avatar
Moritz Lipp committed
457
  g_return_val_if_fail(widget != NULL, NULL);
458
  zathura_page_widget_private_t* priv = ZATHURA_PAGE_GET_PRIVATE(widget);
Moritz Lipp's avatar
Moritz Lipp committed
459
  g_return_val_if_fail(priv != NULL, NULL);
460

Moritz Lipp's avatar
Moritz Lipp committed
461 462 463 464 465 466
  if (priv->links != NULL && index >= priv->link_offset &&
      girara_list_size(priv->links) >= index - priv->link_offset) {
    return girara_list_nth(priv->links, index - priv->link_offset);
  } else {
    return NULL;
  }
467
}
Moritz Lipp's avatar
Moritz Lipp committed
468

469 470
static gboolean
cb_zathura_page_widget_button_press_event(GtkWidget* widget, GdkEventButton* button)
Moritz Lipp's avatar
Moritz Lipp committed
471 472 473
{
  g_return_val_if_fail(widget != NULL, false);
  g_return_val_if_fail(button != NULL, false);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
474 475 476
  if (button->button != 1) {
    return false;
  }
Moritz Lipp's avatar
Moritz Lipp committed
477 478 479 480

  zathura_page_widget_private_t* priv = ZATHURA_PAGE_GET_PRIVATE(widget);

  if (button->type == GDK_BUTTON_PRESS) {
481
    /* start the selection */
482 483
    priv->selection_basepoint.x = button->x;
    priv->selection_basepoint.y = button->y;
484 485
    priv->selection.x1 = button->x;
    priv->selection.y1 = button->y;
486 487
    priv->selection.x2 = button->x;
    priv->selection.y2 = button->y;
488 489
  } else if (button->type == GDK_2BUTTON_PRESS || button->type == GDK_3BUTTON_PRESS) {
    /* abort the selection */
490 491
    priv->selection_basepoint.x = -1;
    priv->selection_basepoint.y = -1;
492 493 494 495 496 497 498 499 500 501 502 503 504
    priv->selection.x1 = -1;
    priv->selection.y1 = -1;
    priv->selection.x2 = -1;
    priv->selection.y2 = -1;
  }
  return false;
}

static gboolean
cb_zathura_page_widget_button_release_event(GtkWidget* widget, GdkEventButton* button)
{
  g_return_val_if_fail(widget != NULL, false);
  g_return_val_if_fail(button != NULL, false);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
505
  if (button->type != GDK_BUTTON_RELEASE || button->button != 1) {
506 507 508
    return false;
  }

Sebastian Ramacher's avatar
Sebastian Ramacher committed
509 510
  zathura_page_widget_private_t* priv = ZATHURA_PAGE_GET_PRIVATE(widget);

511 512
  if (priv->selection.y2 == -1 && priv->selection.x2 == -1 ) {
    /* simple single click */
Moritz Lipp's avatar
Moritz Lipp committed
513 514
    /* get links */
    if (priv->links_got == false) {
515
      priv->links = zathura_page_links_get(priv->page, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
516 517 518 519 520 521
      priv->links_got = true;
      priv->number_of_links = (priv->links == NULL) ? 0 : girara_list_size(priv->links);
    }

    if (priv->links != NULL && priv->number_of_links > 0) {
      GIRARA_LIST_FOREACH(priv->links, zathura_link_t*, iter, link)
522 523 524
        zathura_rectangle_t rect = recalc_rectangle(priv->page, link->position);
        if (rect.x1 <= button->x && rect.x2 >= button->x
            && rect.y1 <= button->y && rect.y2 >= button->y) {
Moritz Lipp's avatar
Moritz Lipp committed
525 526
          switch (link->type) {
            case ZATHURA_LINK_TO_PAGE:
527
              page_set_delayed(priv->page->document->zathura, link->target.page_number);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
528
              return false;
Moritz Lipp's avatar
Moritz Lipp committed
529 530
            case ZATHURA_LINK_EXTERNAL:
              girara_xdg_open(link->target.value);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
531
              return false;
Moritz Lipp's avatar
Moritz Lipp committed
532 533 534 535
          }
        }
      GIRARA_LIST_FOREACH_END(priv->links, zathura_link_t*, iter, link);
    }
536 537 538
  } else {
    redraw_rect(ZATHURA_PAGE(widget), &priv->selection);
    zathura_rectangle_t tmp = priv->selection;
Moritz Lipp's avatar
Moritz Lipp committed
539

540 541 542 543 544 545 546
    tmp.x1 /= priv->page->document->scale;
    tmp.x2 /= priv->page->document->scale;
    tmp.y1 /= priv->page->document->scale;
    tmp.y2 /= priv->page->document->scale;

    char* text = zathura_page_get_text(priv->page, tmp, NULL);
    if (text != NULL) {
547 548 549 550 551 552 553 554 555 556 557 558 559
      if (strlen(text) > 0) {
        /* copy to clipboard */
        gtk_clipboard_set_text(gtk_clipboard_get(GDK_SELECTION_PRIMARY), text, -1);


        if (priv->page != NULL && priv->page->document != NULL && priv->page->document->zathura != NULL) {
          zathura_t* zathura = priv->page->document->zathura;
          char* stripped_text = g_strdelimit(g_strdup(text), "\n\t\r\n", ' ');
          girara_notify(zathura->ui.session, GIRARA_INFO, _("Copied selected text to clipbard: %s"), stripped_text);
          g_free(stripped_text);
        }
      }

560 561 562
      g_free(text);
    }
  }
Moritz Lipp's avatar
Moritz Lipp committed
563

564 565
  priv->selection_basepoint.x = -1;
  priv->selection_basepoint.y = -1;
566 567 568 569
  priv->selection.x1 = -1;
  priv->selection.y1 = -1;
  priv->selection.x2 = -1;
  priv->selection.y2 = -1;
570

571 572 573 574 575 576 577 578 579 580 581 582 583 584
  return false;
}

static gboolean
cb_zathura_page_widget_motion_notify(GtkWidget* widget, GdkEventMotion* event)
{
  g_return_val_if_fail(widget != NULL, false);
  g_return_val_if_fail(event != NULL, false);
  if ((event->state & GDK_BUTTON1_MASK) == 0) {
    return false;
  }

  zathura_page_widget_private_t* priv = ZATHURA_PAGE_GET_PRIVATE(widget);
  zathura_rectangle_t tmp = priv->selection;
585
  if (event->x < priv->selection_basepoint.x) {
586
    tmp.x1 = event->x;
587
    tmp.x2 = priv->selection_basepoint.x;
588 589
  } else {
    tmp.x2 = event->x;
590
    tmp.x1 = priv->selection_basepoint.x;
591
  }
592
  if (event->y < priv->selection_basepoint.y) {
593
    tmp.y1 = event->y;
594
    tmp.y2 = priv->selection_basepoint.y;
595
  } else {
596
    tmp.y1 = priv->selection_basepoint.y;
597
    tmp.y2 = event->y;
Moritz Lipp's avatar
Moritz Lipp committed
598 599
  }

600 601 602 603
  redraw_rect(ZATHURA_PAGE(widget), &priv->selection);
  redraw_rect(ZATHURA_PAGE(widget), &tmp);
  priv->selection = tmp;

Moritz Lipp's avatar
Moritz Lipp committed
604 605
  return false;
}