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

3 4 5 6
#include <girara/session.h>
#include <girara/settings.h>
#include <girara/datastructures.h>
#include <girara/shortcuts.h>
7
#include <girara/utils.h>
Moritz Lipp's avatar
Moritz Lipp committed
8
#include <gtk/gtk.h>
9
#include <glib/gi18n.h>
Moritz Lipp's avatar
Moritz Lipp committed
10 11

#include "callbacks.h"
12
#include "shortcuts.h"
13
#include "document.h"
Moritz Lipp's avatar
Moritz Lipp committed
14
#include "zathura.h"
15
#include "render.h"
16
#include "utils.h"
Moritz Lipp's avatar
Moritz Lipp committed
17
#include "page.h"
Moritz Lipp's avatar
Moritz Lipp committed
18
#include "print.h"
19
#include "page-widget.h"
20
#include "adjustment.h"
21
#include <math.h>
22

23 24 25 26 27 28 29
/* Helper function; see sc_display_link and sc_follow. */
static bool
draw_links(zathura_t* zathura)
{
  /* set pages to draw links */
  bool show_links = false;
  unsigned int page_offset = 0;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
30
  const unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
31 32 33 34 35 36 37
  for (unsigned int page_id = 0; page_id < number_of_pages; page_id++) {
    zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
    if (page == NULL) {
      continue;
    }

    GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
38 39
    GObject* obj_page_widget = G_OBJECT(page_widget);
    g_object_set(obj_page_widget, "draw-search-results", FALSE, NULL);
40
    if (zathura_page_get_visibility(page) == true) {
41
      g_object_set(obj_page_widget, "draw-links", TRUE, NULL);
42 43

      int number_of_links = 0;
44
      g_object_get(obj_page_widget, "number-of-links", &number_of_links, NULL);
45 46 47
      if (number_of_links != 0) {
        show_links = true;
      }
48
      g_object_set(obj_page_widget, "offset-links", page_offset, NULL);
49 50
      page_offset += number_of_links;
    } else {
51
      g_object_set(obj_page_widget, "draw-links", FALSE, NULL);
52 53 54 55 56
    }
  }
  return show_links;
}

Moritz Lipp's avatar
Moritz Lipp committed
57
bool
58
sc_abort(girara_session_t* session, girara_argument_t* UNUSED(argument),
Moritz Lipp's avatar
Moritz Lipp committed
59
         girara_event_t* UNUSED(event), unsigned int UNUSED(t))
60
{
Moritz Lipp's avatar
Moritz Lipp committed
61
  g_return_val_if_fail(session != NULL, false);
62 63 64
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;

65 66 67
  bool clear_search = true;
  girara_setting_get(session, "abort-clear-search", &clear_search);

Moritz Lipp's avatar
Moritz Lipp committed
68
  if (zathura->document != NULL) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
69
    const unsigned int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
70 71
    for (unsigned int page_id = 0; page_id < number_of_pages; ++page_id) {
      zathura_page_t* page = zathura_document_get_page(zathura->document, page_id);
Moritz Lipp's avatar
Moritz Lipp committed
72 73 74 75
      if (page == NULL) {
        continue;
      }

76
      GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
77 78
      GObject* obj_page_widget = G_OBJECT(page_widget);
      g_object_set(obj_page_widget, "draw-links", FALSE, NULL);
79
      if (clear_search == true) {
80
        g_object_set(obj_page_widget, "draw-search-results", FALSE, NULL);
81
      }
82 83
    }
  }
Moritz Lipp's avatar
Moritz Lipp committed
84

85 86 87 88
  /* Setting the mode back here has not worked for ages. We need another way to
   * do this. Let's disable this for now.
   */
  /* girara_mode_set(session, session->modes.normal); */
89
  girara_sc_abort(session, NULL, NULL, 0);
Moritz Lipp's avatar
Moritz Lipp committed
90

Moritz Lipp's avatar
Moritz Lipp committed
91
  return false;
92 93
}

94 95
bool
sc_adjust_window(girara_session_t* session, girara_argument_t* argument,
Moritz Lipp's avatar
Moritz Lipp committed
96
                 girara_event_t* UNUSED(event), unsigned int UNUSED(t))
97
{
98 99 100 101
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
102

103 104 105 106 107 108 109 110 111
  if (argument->n < ZATHURA_ADJUST_NONE || argument->n >= ZATHURA_ADJUST_MODE_NUMBER) {
    girara_error("Invalid adjust mode: %d", argument->n);
    girara_notify(session, GIRARA_ERROR, _("Invalid adjust mode: %d"), argument->n);
  } else {
    girara_debug("Setting adjust mode to: %d", argument->n);

    zathura_document_set_adjust_mode(zathura->document, argument->n);
    adjust_view(zathura);
  }
Moritz Lipp's avatar
Moritz Lipp committed
112

113
  return false;
114 115
}

Moritz Lipp's avatar
Moritz Lipp committed
116
bool
Moritz Lipp's avatar
Moritz Lipp committed
117
sc_change_mode(girara_session_t* session, girara_argument_t* argument,
Moritz Lipp's avatar
Moritz Lipp committed
118
               girara_event_t* UNUSED(event), unsigned int UNUSED(t))
119
{
Moritz Lipp's avatar
Moritz Lipp committed
120 121 122 123
  g_return_val_if_fail(session != NULL, false);

  girara_mode_set(session, argument->n);

Moritz Lipp's avatar
Moritz Lipp committed
124
  return false;
125 126
}

127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
bool
sc_display_link(girara_session_t* session, girara_argument_t* UNUSED(argument),
                girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;

  if (zathura->document == NULL || zathura->ui.session == NULL) {
    return false;
  }

  bool show_links = draw_links(zathura);

  /* ask for input */
  if (show_links) {
143
    zathura_document_set_adjust_mode(zathura->document, ZATHURA_ADJUST_INPUTBAR);
144
    girara_dialog(zathura->ui.session, "Display link:", FALSE, NULL,
145
        cb_sc_display_link,
146 147 148 149 150 151
        zathura->ui.session);
  }

  return false;
}

152 153 154 155 156 157 158 159 160
bool
sc_focus_inputbar(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int UNUSED(t))
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->gtk.inputbar_entry != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);

161 162
  zathura_document_set_adjust_mode(zathura->document, ZATHURA_ADJUST_INPUTBAR);

163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
  if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.inputbar)) == false) {
    gtk_widget_show(GTK_WIDGET(session->gtk.inputbar));
  }

  if (gtk_widget_get_visible(GTK_WIDGET(session->gtk.notification_area)) == true) {
    gtk_widget_hide(GTK_WIDGET(session->gtk.notification_area));
  }

  gtk_widget_grab_focus(GTK_WIDGET(session->gtk.inputbar_entry));

  if (argument->data != NULL) {
    gtk_entry_set_text(session->gtk.inputbar_entry, (char*) argument->data);

    /* append filepath */
    if (argument->n == APPEND_FILEPATH && zathura->document != NULL) {
178 179 180 181
      const char* file_path = zathura_document_get_path(zathura->document);
      if (file_path == NULL) {
        return false;
      }
182

183 184 185 186 187
      char* path = g_path_get_dirname(file_path);
      char* escaped = girara_escape_string(path);
      char* tmp  = g_strdup_printf("%s%s/", (char*) argument->data, (g_strcmp0(path, "/") == 0) ? "" : escaped);
      g_free(path);
      g_free(escaped);
188 189 190 191 192

      gtk_entry_set_text(session->gtk.inputbar_entry, tmp);
      g_free(tmp);
    }

193 194
    GdkAtom* selection = get_selection(zathura);

195
    /* we save the X clipboard that will be clear by "grab_focus" */
196
    gchar* x_clipboard_text = NULL;
197 198 199 200

    if (selection != NULL) {
      x_clipboard_text = gtk_clipboard_wait_for_text(gtk_clipboard_get(*selection));
    }
201 202 203

    gtk_editable_set_position(GTK_EDITABLE(session->gtk.inputbar_entry), -1);

204
    if (x_clipboard_text != NULL && selection != NULL) {
205
      /* we reset the X clipboard with saved text */
206
      gtk_clipboard_set_text(gtk_clipboard_get(*selection), x_clipboard_text, -1);
207 208
      g_free(x_clipboard_text);
    }
209 210

    g_free(selection);
211 212 213 214 215
  }

  return true;
}

Moritz Lipp's avatar
Moritz Lipp committed
216
bool
217
sc_follow(girara_session_t* session, girara_argument_t* UNUSED(argument),
Moritz Lipp's avatar
Moritz Lipp committed
218
          girara_event_t* UNUSED(event), unsigned int UNUSED(t))
219
{
Moritz Lipp's avatar
Moritz Lipp committed
220
  g_return_val_if_fail(session != NULL, false);
221 222 223
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;

Moritz Lipp's avatar
Moritz Lipp committed
224
  if (zathura->document == NULL || zathura->ui.session == NULL) {
225 226 227
    return false;
  }

228
  bool show_links = draw_links(zathura);
229

Moritz Lipp's avatar
Moritz Lipp committed
230
  /* ask for input */
Moritz Lipp's avatar
Moritz Lipp committed
231
  if (show_links == true) {
232
    zathura_document_set_adjust_mode(zathura->document, ZATHURA_ADJUST_INPUTBAR);
233
    girara_dialog(zathura->ui.session, "Follow link:", FALSE, NULL, cb_sc_follow, zathura->ui.session);
Moritz Lipp's avatar
Moritz Lipp committed
234
  }
Moritz Lipp's avatar
Moritz Lipp committed
235

Moritz Lipp's avatar
Moritz Lipp committed
236 237
  return false;
}
238

Moritz Lipp's avatar
Moritz Lipp committed
239
bool
Moritz Lipp's avatar
Moritz Lipp committed
240
sc_goto(girara_session_t* session, girara_argument_t* argument, girara_event_t* UNUSED(event), unsigned int t)
Moritz Lipp's avatar
Moritz Lipp committed
241
{
Moritz Lipp's avatar
Moritz Lipp committed
242 243 244 245 246
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);
247

248
  zathura_jumplist_add(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
249
  if (t != 0) {
250
    /* add offset */
Moritz Lipp's avatar
Moritz Lipp committed
251
    t += zathura_document_get_page_offset(zathura->document);
252

Moritz Lipp's avatar
Moritz Lipp committed
253
    page_set(zathura, t - 1);
Moritz Lipp's avatar
Moritz Lipp committed
254
  } else if (argument->n == TOP) {
Moritz Lipp's avatar
Moritz Lipp committed
255
    page_set(zathura, 0);
Moritz Lipp's avatar
Moritz Lipp committed
256
  } else if (argument->n == BOTTOM) {
257
    page_set(zathura, zathura_document_get_number_of_pages(zathura->document) - 1);
258 259
  }

260 261
  zathura_jumplist_add(zathura);

Moritz Lipp's avatar
Moritz Lipp committed
262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277
  return false;
}

bool
sc_mouse_scroll(girara_session_t* session, girara_argument_t* argument, girara_event_t* event, unsigned int t)
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(event != NULL, false);

  if (zathura->document == NULL) {
    return false;
  }

Moritz Lipp's avatar
Moritz Lipp committed
278 279 280 281
  GtkAdjustment* x_adj = NULL;
  GtkAdjustment* y_adj = NULL;

  switch (event->type) {
Moritz Lipp's avatar
Moritz Lipp committed
282
      /* scroll */
Moritz Lipp's avatar
Moritz Lipp committed
283 284 285 286
    case GIRARA_EVENT_SCROLL_UP:
    case GIRARA_EVENT_SCROLL_DOWN:
    case GIRARA_EVENT_SCROLL_LEFT:
    case GIRARA_EVENT_SCROLL_RIGHT:
287
    case GIRARA_EVENT_SCROLL_BIDIRECTIONAL:
288
      return sc_scroll(session, argument, event, t);
Moritz Lipp's avatar
Moritz Lipp committed
289

Moritz Lipp's avatar
Moritz Lipp committed
290
      /* drag */
Moritz Lipp's avatar
Moritz Lipp committed
291
    case GIRARA_EVENT_BUTTON_PRESS:
292 293
      zathura->shortcut.mouse.x = event->x;
      zathura->shortcut.mouse.y = event->y;
Moritz Lipp's avatar
Moritz Lipp committed
294 295
      break;
    case GIRARA_EVENT_BUTTON_RELEASE:
296 297
      zathura->shortcut.mouse.x = 0;
      zathura->shortcut.mouse.y = 0;
Moritz Lipp's avatar
Moritz Lipp committed
298 299 300
      break;
    case GIRARA_EVENT_MOTION_NOTIFY:
      x_adj = gtk_scrolled_window_get_hadjustment(GTK_SCROLLED_WINDOW(session->gtk.view));
301
      y_adj = gtk_scrolled_window_get_vadjustment(GTK_SCROLLED_WINDOW(session->gtk.view));
Moritz Lipp's avatar
Moritz Lipp committed
302 303 304 305

      if (x_adj == NULL || y_adj == NULL) {
        return false;
      }
Moritz Lipp's avatar
Moritz Lipp committed
306

307
      zathura_adjustment_set_value(x_adj,
308
          gtk_adjustment_get_value(x_adj) - (event->x - zathura->shortcut.mouse.x));
309
      zathura_adjustment_set_value(y_adj,
310
          gtk_adjustment_get_value(y_adj) - (event->y - zathura->shortcut.mouse.y));
Moritz Lipp's avatar
Moritz Lipp committed
311 312
      break;

Moritz Lipp's avatar
Moritz Lipp committed
313
      /* unhandled events */
Moritz Lipp's avatar
Moritz Lipp committed
314 315
    default:
      break;
Moritz Lipp's avatar
Moritz Lipp committed
316 317
  }

Moritz Lipp's avatar
Moritz Lipp committed
318
  return false;
319 320
}

Moritz Lipp's avatar
Moritz Lipp committed
321 322 323 324 325 326 327 328 329 330 331 332 333 334
bool
sc_mouse_zoom(girara_session_t* session, girara_argument_t* argument, girara_event_t* event, unsigned int t)
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(event != NULL, false);

  if (zathura->document == NULL) {
    return false;
  }

  /* scroll event */
335 336 337 338 339 340 341
  switch (event->type) {
    case GIRARA_EVENT_SCROLL_UP:
      argument->n = ZOOM_IN;
      break;
    case GIRARA_EVENT_SCROLL_DOWN:
      argument->n = ZOOM_OUT;
      break;
342 343 344
    case GIRARA_EVENT_SCROLL_BIDIRECTIONAL:
      argument->n = ZOOM_SMOOTH;
      break;
345 346
    default:
      return false;
Moritz Lipp's avatar
Moritz Lipp committed
347 348
  }

349
  return sc_zoom(session, argument, event, t);
350 351
}

Moritz Lipp's avatar
Moritz Lipp committed
352
bool
Moritz Lipp's avatar
Moritz Lipp committed
353
sc_navigate(girara_session_t* session, girara_argument_t* argument,
Moritz Lipp's avatar
Moritz Lipp committed
354
            girara_event_t* UNUSED(event), unsigned int t)
355
{
Moritz Lipp's avatar
Moritz Lipp committed
356 357 358 359 360
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
361

362 363
  int number_of_pages = zathura_document_get_number_of_pages(zathura->document);
  int new_page        = zathura_document_get_current_page_number(zathura->document);
Moritz Lipp's avatar
Moritz Lipp committed
364

365 366 367
  bool scroll_wrap = false;
  girara_setting_get(session, "scroll-wrap", &scroll_wrap);

368 369 370 371 372 373 374 375
  bool columns_per_row_offset = false;
  girara_setting_get(session, "advance-pages-per-row", &columns_per_row_offset);

  int offset = 1;
  if (columns_per_row_offset == true) {
    girara_setting_get(session, "pages-per-row", &offset);
  }

Moritz Lipp's avatar
Moritz Lipp committed
376
  t = (t == 0) ? (unsigned int) offset : t;
Moritz Lipp's avatar
Moritz Lipp committed
377
  if (argument->n == NEXT) {
Benoît Knecht's avatar
Benoît Knecht committed
378
    if (scroll_wrap == false) {
379 380 381 382
      new_page = new_page + t;
    } else {
      new_page = (new_page + t) % number_of_pages;
    }
Moritz Lipp's avatar
Moritz Lipp committed
383
  } else if (argument->n == PREVIOUS) {
Benoît Knecht's avatar
Benoît Knecht committed
384
    if (scroll_wrap == false) {
385 386 387 388 389 390
      new_page = new_page - t;
    } else {
      new_page = (new_page + number_of_pages - t) % number_of_pages;
    }
  }

391 392 393 394 395 396
  if (!scroll_wrap) {
    if (new_page <= 0) {
      new_page = 0;
    } else if (new_page >= number_of_pages) {
      new_page = number_of_pages - 1;
    }
Moritz Lipp's avatar
Moritz Lipp committed
397 398
  }

399
  page_set(zathura, new_page);
Moritz Lipp's avatar
Moritz Lipp committed
400

Moritz Lipp's avatar
Moritz Lipp committed
401
  return false;
402 403
}

Moritz Lipp's avatar
Moritz Lipp committed
404 405
bool
sc_print(girara_session_t* session, girara_argument_t* UNUSED(argument),
Moritz Lipp's avatar
Moritz Lipp committed
406
         girara_event_t* UNUSED(event), unsigned int UNUSED(t))
Moritz Lipp's avatar
Moritz Lipp committed
407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;

  if (zathura->document == NULL) {
    girara_notify(session, GIRARA_ERROR, _("No document opened."));
    return false;
  }

  print(zathura);

  return true;
}

Moritz Lipp's avatar
Moritz Lipp committed
422
bool
423
sc_recolor(girara_session_t* session, girara_argument_t* UNUSED(argument),
Moritz Lipp's avatar
Moritz Lipp committed
424
           girara_event_t* UNUSED(event), unsigned int UNUSED(t))
425
{
Moritz Lipp's avatar
Moritz Lipp committed
426
  g_return_val_if_fail(session != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
427

Moritz Lipp's avatar
Moritz Lipp committed
428 429 430 431
  bool value = false;
  girara_setting_get(session, "recolor", &value);
  value = !value;
  girara_setting_set(session, "recolor", &value);
Moritz Lipp's avatar
Moritz Lipp committed
432

Moritz Lipp's avatar
Moritz Lipp committed
433
  return false;
434 435
}

Moritz Lipp's avatar
Moritz Lipp committed
436
bool
437
sc_reload(girara_session_t* session, girara_argument_t* UNUSED(argument),
Moritz Lipp's avatar
Moritz Lipp committed
438
          girara_event_t* UNUSED(event), unsigned int UNUSED(t))
439
{
Moritz Lipp's avatar
Moritz Lipp committed
440
  g_return_val_if_fail(session != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
441 442 443
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;

444
  if (zathura->file_monitor.monitor == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
445 446 447 448
    return false;
  }

  /* close current document */
Moritz Lipp's avatar
Moritz Lipp committed
449
  document_close(zathura, true);
Moritz Lipp's avatar
Moritz Lipp committed
450 451

  /* reopen document */
452 453 454
  document_open(
    zathura, zathura_filemonitor_get_filepath(zathura->file_monitor.monitor),
    NULL, zathura->file_monitor.password, ZATHURA_PAGE_NUMBER_UNSPECIFIED);
Moritz Lipp's avatar
Moritz Lipp committed
455

Moritz Lipp's avatar
Moritz Lipp committed
456
  return false;
457 458
}

Moritz Lipp's avatar
Moritz Lipp committed
459
bool
460
sc_rotate(girara_session_t* session, girara_argument_t* argument,
Moritz Lipp's avatar
Moritz Lipp committed
461
          girara_event_t* UNUSED(event), unsigned int t)
462
{
Moritz Lipp's avatar
Moritz Lipp committed
463 464 465 466
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(zathura->document != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
467

Sebastian Ramacher's avatar
Sebastian Ramacher committed
468
  const unsigned int page_number = zathura_document_get_current_page_number(zathura->document);
469

470 471 472 473 474
  int angle = 90;
  if (argument != NULL && argument->n == ROTATE_CCW) {
    angle = 270;
  }

Moritz Lipp's avatar
Moritz Lipp committed
475
  /* update rotate value */
Moritz Lipp's avatar
Moritz Lipp committed
476
  t = (t == 0) ? 1 : t;
477
  unsigned int rotation = zathura_document_get_rotation(zathura->document);
Moritz Lipp's avatar
Moritz Lipp committed
478
  zathura_document_set_rotation(zathura->document, (rotation + angle * t) % 360);
Moritz Lipp's avatar
Moritz Lipp committed
479

480 481 482 483
  /* update scale */
  girara_argument_t new_argument = { zathura_document_get_adjust_mode(zathura->document), NULL };
  sc_adjust_window(zathura->ui.session, &new_argument, NULL, 0);

Moritz Lipp's avatar
Moritz Lipp committed
484
  /* render all pages again */
485
  render_all(zathura);
Moritz Lipp's avatar
Moritz Lipp committed
486

487
  page_set(zathura, page_number);
488

Moritz Lipp's avatar
Moritz Lipp committed
489
  return false;
490 491
}

Moritz Lipp's avatar
Moritz Lipp committed
492
bool
Moritz Lipp's avatar
Moritz Lipp committed
493
sc_scroll(girara_session_t* session, girara_argument_t* argument,
Sebastian Ramacher's avatar
Sebastian Ramacher committed
494
          girara_event_t* event, unsigned int t)
495
{
Moritz Lipp's avatar
Moritz Lipp committed
496 497 498 499
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
500 501 502
  if (zathura->document == NULL) {
    return false;
  }
Moritz Lipp's avatar
Moritz Lipp committed
503

504 505 506 507 508 509 510 511 512
  /* if TOP or BOTTOM, go there and we are done */
  if (argument->n == TOP) {
    position_set(zathura, -1, 0);
    return false;
  } else if (argument->n == BOTTOM) {
    position_set(zathura, -1, 1.0);
    return false;
  }

513 514 515 516 517 518 519
  /* Retrieve current page and position */
  const unsigned int page_id = zathura_document_get_current_page_number(zathura->document);
  double pos_x = zathura_document_get_position_x(zathura->document);
  double pos_y = zathura_document_get_position_y(zathura->document);

  /* If PAGE_TOP or PAGE_BOTTOM, go there and we are done */
  if (argument->n == PAGE_TOP) {
520 521 522
    double dontcare = 0.5;
    page_number_to_position(zathura->document, page_id, dontcare, 0.0, &dontcare, &pos_y);
    position_set(zathura, pos_x, pos_y);
523 524
    return false;
  } else if (argument->n == PAGE_BOTTOM) {
525 526 527
    double dontcare = 0.5;
    page_number_to_position(zathura->document, page_id, dontcare, 1.0, &dontcare, &pos_y);
    position_set(zathura, pos_x, pos_y);
528 529 530
    return false;
  }

Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
531
  if (t == 0) {
532 533 534
    t = 1;
  }

Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
535 536
  unsigned int view_width  = 0;
  unsigned int view_height = 0;
537
  zathura_document_get_viewport_size(zathura->document, &view_height, &view_width);
Moritz Lipp's avatar
Moritz Lipp committed
538

Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
539 540
  unsigned int doc_width  = 0;
  unsigned int doc_height = 0;
541
  zathura_document_get_document_size(zathura->document, &doc_height, &doc_width);
542

543 544
  float scroll_step = 40;
  girara_setting_get(session, "scroll-step", &scroll_step);
Julian Orth's avatar
Julian Orth committed
545 546 547 548 549
  float scroll_hstep = -1;
  girara_setting_get(session, "scroll-hstep", &scroll_hstep);
  if (scroll_hstep < 0) {
    scroll_hstep = scroll_step;
  }
550
  float scroll_full_overlap = 0.0;
551
  girara_setting_get(session, "scroll-full-overlap", &scroll_full_overlap);
552 553
  bool scroll_page_aware = false;
  girara_setting_get(session, "scroll-page-aware", &scroll_page_aware);
554

555 556 557
  bool scroll_wrap = false;
  girara_setting_get(session, "scroll-wrap", &scroll_wrap);

558
  /* compute the direction of scrolling */
559 560 561
  double direction = 1.0;
  if ((argument->n == LEFT) || (argument->n == FULL_LEFT) || (argument->n == HALF_LEFT) ||
      (argument->n == UP) || (argument->n == FULL_UP) || (argument->n == HALF_UP)) {
562 563 564
    direction = -1.0;
  }

565 566
  const double vstep = (double)view_height / (double)doc_height;
  const double hstep = (double)view_width / (double)doc_width;
567 568

  /* compute new position */
569
  switch (argument->n) {
Moritz Lipp's avatar
Moritz Lipp committed
570 571
    case FULL_UP:
    case FULL_DOWN:
572 573 574 575
      pos_y += direction * (1.0 - scroll_full_overlap) * vstep;
      break;

    case FULL_LEFT:
576
    case FULL_RIGHT:
577
      pos_x += direction * (1.0 - scroll_full_overlap) * hstep;
Moritz Lipp's avatar
Moritz Lipp committed
578
      break;
579

Moritz Lipp's avatar
Moritz Lipp committed
580 581
    case HALF_UP:
    case HALF_DOWN:
582
      pos_y += direction * 0.5 * vstep;
Moritz Lipp's avatar
Moritz Lipp committed
583
      break;
584 585 586 587

    case HALF_LEFT:
    case HALF_RIGHT:
      pos_x += direction * 0.5 * hstep;
Julian Orth's avatar
Julian Orth committed
588
      break;
589

Moritz Lipp's avatar
Moritz Lipp committed
590 591
    case UP:
    case DOWN:
592
      pos_y += direction * t * scroll_step / (double)doc_height;
Moritz Lipp's avatar
Moritz Lipp committed
593
      break;
594 595 596 597

    case LEFT:
    case RIGHT:
      pos_x += direction * t * scroll_hstep / (double)doc_width;
Moritz Lipp's avatar
Moritz Lipp committed
598
      break;
599 600

    case BIDIRECTIONAL: {
601 602
      pos_x += event->x * t * scroll_hstep / (double)doc_width;
      pos_y += event->y * t * scroll_step / (double)doc_height;
603 604
      break;
    }
Moritz Lipp's avatar
Moritz Lipp committed
605 606
  }

607
  /* handle boundaries */
608 609
  const double end_x = 0.5 * (double)view_width / (double)doc_width;
  const double end_y = 0.5 * (double)view_height / (double)doc_height;
610

611 612
  const double new_x = scroll_wrap ? 1.0 - end_x : end_x;
  const double new_y = scroll_wrap ? 1.0 - end_y : end_y;
613

614 615 616 617 618
  if (pos_x < end_x) {
    pos_x = new_x;
  } else if (pos_x > 1.0 - end_x) {
    pos_x = 1 - new_x;
  }
619

620 621 622 623 624
  if (pos_y < end_y) {
    pos_y = new_y;
  } else if (pos_y > 1.0 - end_y) {
    pos_y = 1 - new_y;
  }
Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
625

626
  /* snap to the border if we change page */
627
  const unsigned int new_page_id = position_to_page_number(zathura->document, pos_x, pos_y);
628
  if (scroll_page_aware == true && page_id != new_page_id) {
629
    double dummy = 0.0;
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649
    switch(argument->n) {
      case FULL_LEFT:
      case HALF_LEFT:
        page_number_to_position(zathura->document, new_page_id, 1.0, 0.0, &pos_x, &dummy);
        break;

      case FULL_RIGHT:
      case HALF_RIGHT:
        page_number_to_position(zathura->document, new_page_id, 0.0, 0.0, &pos_x, &dummy);
        break;

      case FULL_UP:
      case HALF_UP:
        page_number_to_position(zathura->document, new_page_id, 0.0, 1.0, &dummy, &pos_y);
        break;

      case FULL_DOWN:
      case HALF_DOWN:
        page_number_to_position(zathura->document, new_page_id, 0.0, 0.0, &dummy, &pos_y);
        break;
Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
650
    }
651 652
  }

653
  position_set(zathura, pos_x, pos_y);
Moritz Lipp's avatar
Moritz Lipp committed
654
  return false;
655 656
}

657 658

bool
659 660
sc_jumplist(girara_session_t* session, girara_argument_t* argument,
            girara_event_t* UNUSED(event), unsigned int UNUSED(t))
661 662 663 664 665 666
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);
667 668 669

  /* if no jumps in the jumplist */
  if (zathura->jumplist.size == 0) {
Moritz Lipp's avatar
Moritz Lipp committed
670
    return true;
671
  }
672

673 674 675
  double x = zathura_document_get_position_x(zathura->document);
  double y = zathura_document_get_position_y(zathura->document);

676
  zathura_jump_t* jump = NULL;
677 678 679
  zathura_jump_t* prev_jump = zathura_jumplist_current(zathura);
  bool go_to_current = false;

Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
680
  if (zathura_jumplist_has_next(zathura) == false || zathura_jumplist_has_previous(zathura) == false) {
681 682 683 684 685 686 687
    if (x == prev_jump->x && y == prev_jump->y) {
      go_to_current = false;
    } else {
      go_to_current = true;
    }
  }

688 689
  switch(argument->n) {
    case FORWARD:
690 691 692 693 694 695
      if (go_to_current == true && zathura_jumplist_has_previous(zathura) == false) {
        jump = zathura_jumplist_current(zathura);
      } else {
        zathura_jumplist_forward(zathura);
        jump = zathura_jumplist_current(zathura);
      }
696 697
      break;
    case BACKWARD:
698 699 700 701 702 703
      if (go_to_current == true && zathura_jumplist_has_next(zathura) == false) {
        jump = zathura_jumplist_current(zathura);
      } else {
        zathura_jumplist_backward(zathura);
        jump = zathura_jumplist_current(zathura);
      }
704 705 706
      break;
  }

707
  if (jump == prev_jump) {
Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
708 709
    if ((zathura_jumplist_has_previous(zathura) == false && argument->n == BACKWARD) ||
        (zathura_jumplist_has_next(zathura) == false && argument->n == FORWARD)) {
710 711 712 713
      jump = NULL;
    }
  }

714
  if (jump != NULL) {
715 716 717
    page_set(zathura, jump->page);
    position_set(zathura, jump->x, jump->y);
  }
718 719 720 721

  return false;
}

722 723 724 725 726 727 728 729 730 731 732

bool
sc_bisect(girara_session_t* session, girara_argument_t* argument,
          girara_event_t* UNUSED(event), unsigned int t)
{
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);

733 734
  const unsigned int num_pages = zathura_document_get_number_of_pages(zathura->document);
  const unsigned int cur_page = zathura_document_get_current_page_number(zathura->document);
735 736 737

  /* process arguments */
  int direction;
738 739 740 741 742 743 744 745 746 747
  if (t > 0 && t <= num_pages) {
    /* bisect between cur_page and t */
    t -= 1;
    if (t == cur_page) {
      /* nothing to do */
      return false;
    }
    else if (t > cur_page) {
      zathura->bisect.start = cur_page;
      zathura->bisect.end = t;
748 749
      direction = BACKWARD;
    } else {
750 751
      zathura->bisect.start = t;
      zathura->bisect.end = cur_page;
752 753 754 755 756
      direction = FORWARD;
    }
  } else if (argument != NULL)  {
    direction = argument->n;

Abdó Roig-Maranges's avatar
Abdó Roig-Maranges committed
757
    /* setup initial bisect range */
758 759 760 761 762 763
    zathura_jump_t* jump = zathura_jumplist_current(zathura);
    if (jump == NULL) {
      girara_debug("bisecting between first and last page because there are no jumps");
      zathura->bisect.start = 0;
      zathura->bisect.end = num_pages - 1;

764
    } else if (jump->page != cur_page || jump->page != zathura->bisect.last_jump) {
Abdó Roig-Maranges's avatar
Abdó Roig-Maranges committed
765 766 767
      girara_debug("last jump doesn't match up, starting new bisecting");
      zathura->bisect.start = 0;
      zathura->bisect.end = num_pages - 1;
768

Abdó Roig-Maranges's avatar
Abdó Roig-Maranges committed
769 770 771 772 773 774 775 776 777 778 779 780 781
      unsigned int prev_page;
      if (direction == FORWARD) {
        prev_page = num_pages - 1;
      } else {
        prev_page = 0;
      }

      /* check if we have previous jumps */
      if (zathura_jumplist_has_previous(zathura) == true) {
        zathura_jumplist_backward(zathura);
        jump = zathura_jumplist_current(zathura);
        if (jump != NULL) {
          prev_page = jump->page;
782
        }
Abdó Roig-Maranges's avatar
Abdó Roig-Maranges committed
783
        zathura_jumplist_forward(zathura);
784
      }
Abdó Roig-Maranges's avatar
Abdó Roig-Maranges committed
785 786 787 788

      zathura->bisect.start = MIN(prev_page, cur_page);
      zathura->bisect.end = MAX(prev_page, cur_page);
      zathura->bisect.last_jump = cur_page;
789
    }
790 791 792 793 794 795 796 797
  } else {
    return false;
  }

  girara_debug("bisecting between %d and %d, at %d", zathura->bisect.start, zathura->bisect.end, cur_page);
  if (zathura->bisect.start == zathura->bisect.end) {
    /* nothing to do */
    return false;
798 799
  }

800 801 802 803
  unsigned int next_page = cur_page;
  unsigned int next_start = zathura->bisect.start;
  unsigned int next_end = zathura->bisect.end;

Abdó Roig-Maranges's avatar
Abdó Roig-Maranges committed
804 805 806
  /* here we have next_start <= next_page <= next_end */

  /* bisect step */
807 808
  switch(direction) {
    case FORWARD:
809 810 811 812
      if (cur_page != zathura->bisect.end) {
        next_page = (cur_page + zathura->bisect.end) / 2;
        if (next_page == cur_page) {
          ++next_page;
813
        }
814
        next_start = cur_page;
815 816 817 818
      }
      break;

    case BACKWARD:
819 820 821 822
      if (cur_page != zathura->bisect.start) {
        next_page = (cur_page + zathura->bisect.start) / 2;
        if (next_page == cur_page) {
          --next_page;
823
        }
824
        next_end = cur_page;
825 826 827
      }
      break;
  }
828

829 830 831 832 833 834 835 836 837 838
  if (next_page == cur_page) {
    /* nothing to do */
    return false;
  }

  girara_debug("bisecting between %d and %d, jumping to %d", zathura->bisect.start, zathura->bisect.end, next_page);
  zathura->bisect.last_jump = next_page;
  zathura->bisect.start = next_start;
  zathura->bisect.end = next_end;

Abdó Roig-Maranges's avatar
Abdó Roig-Maranges committed
839
  zathura_jumplist_add(zathura);
840 841 842
  page_set(zathura, next_page);
  zathura_jumplist_add(zathura);

843 844 845 846
  return false;
}


Moritz Lipp's avatar
Moritz Lipp committed
847
bool
Moritz Lipp's avatar
Moritz Lipp committed
848
sc_search(girara_session_t* session, girara_argument_t* argument,
Moritz Lipp's avatar
Moritz Lipp committed
849
          girara_event_t* UNUSED(event), unsigned int UNUSED(t))
850
{
Moritz Lipp's avatar
Moritz Lipp committed
851 852 853 854 855
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);
Moritz Lipp's avatar
Moritz Lipp committed
856

857 858
  const unsigned int num_pages = zathura_document_get_number_of_pages(zathura->document);
  const unsigned int cur_page  = zathura_document_get_current_page_number(zathura->document);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
859 860 861
  GtkWidget* cur_page_widget   = zathura_page_get_widget(zathura, zathura_document_get_page(zathura->document, cur_page));
  bool nohlsearch              = false;
  bool first_time_after_abort  = false;
862 863 864

  girara_setting_get(session, "nohlsearch", &nohlsearch);
  if (nohlsearch == false) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
865
    gboolean draw = FALSE;
866
    g_object_get(G_OBJECT(cur_page_widget), "draw-search-results", &draw, NULL);
867 868 869 870 871 872 873

    if (draw == false) {
      first_time_after_abort = true;
    }

    document_draw_search_results(zathura, true);
  }
874

875
  int diff = argument->n == FORWARD ? 1 : -1;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
876
  if (zathura->global.search_direction == BACKWARD) {
877
    diff = -diff;
Sebastian Ramacher's avatar
Sebastian Ramacher committed
878
  }
879

Sebastian Ramacher's avatar
Sebastian Ramacher committed
880 881 882
  zathura_page_t* target_page = NULL;
  int target_idx = 0;

883
  for (unsigned int page_id = 0; page_id < num_pages; ++page_id) {
884
    int tmp = cur_page + diff * page_id;
885
    zathura_page_t* page = zathura_document_get_page(zathura->document, (tmp + num_pages) % num_pages);
886 887 888 889
    if (page == NULL) {
      continue;
    }

890
    GtkWidget* page_widget = zathura_page_get_widget(zathura, page);
Moritz Lipp's avatar
Moritz Lipp committed
891

892
    int num_search_results = 0, current = -1;
893
    g_object_get(G_OBJECT(page_widget), "search-current", &current, "search-length", &num_search_results, NULL);
894 895 896 897
    if (num_search_results == 0 || current == -1) {
      continue;
    }

Marwan Tanager's avatar
Marwan Tanager committed
898
    if (first_time_after_abort == true || (tmp + num_pages) % num_pages != cur_page) {
899 900 901 902 903
      target_page = page;
      target_idx = diff == 1 ? 0 : num_search_results - 1;
      break;
    }

904 905
    if (diff == 1 && current < num_search_results - 1) {
      /* the next result is on the same page */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
906 907
      target_page = page;
      target_idx = current + 1;
908
    } else if (diff == -1 && current > 0) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
909 910
      target_page = page;
      target_idx = current - 1;
911 912
    } else {
      /* the next result is on a different page */
913
      g_object_set(G_OBJECT(page_widget), "search-current", -1, NULL);
914 915 916

      for (int npage_id = 1; page_id < num_pages; ++npage_id) {
        int ntmp = cur_page + diff * (page_id + npage_id);
917
        zathura_page_t* npage = zathura_document_get_page(zathura->document, (ntmp + 2*num_pages) % num_pages);
918
        GtkWidget* npage_page_widget = zathura_page_get_widget(zathura, npage);
919
        g_object_get(G_OBJECT(npage_page_widget), "search-length", &num_search_results, NULL);
920
        if (num_search_results != 0) {
Sebastian Ramacher's avatar
Sebastian Ramacher committed
921 922
          target_page = npage;
          target_idx = diff == 1 ? 0 : num_search_results - 1;
923 924 925 926
          break;
        }
      }
    }
927

928 929
    break;
  }
Moritz Lipp's avatar
Moritz Lipp committed
930

Sebastian Ramacher's avatar
Sebastian Ramacher committed
931 932
  if (target_page != NULL) {
    girara_list_t* results = NULL;
933
    GtkWidget* page_widget = zathura_page_get_widget(zathura, target_page);
934 935 936
    GObject* obj_page_widget = G_OBJECT(page_widget);
    g_object_set(obj_page_widget, "search-current", target_idx, NULL);
    g_object_get(obj_page_widget, "search-results", &results, NULL);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
937

938
    /* Need to adjust rectangle to page scale and orientation */
Sebastian Ramacher's avatar
Sebastian Ramacher committed
939 940
    zathura_rectangle_t* rect = girara_list_nth(results, target_idx);
    zathura_rectangle_t rectangle = recalc_rectangle(target_page, *rect);
Julian Orth's avatar
Julian Orth committed
941 942 943

    bool search_hadjust = true;
    girara_setting_get(session, "search-hadjust", &search_hadjust);
944 945

    /* compute the position of the center of the page */
Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
946 947
    double pos_x = 0;
    double pos_y = 0;
948 949 950 951 952
    page_number_to_position(zathura->document, zathura_page_get_index(target_page),
                            0.5, 0.5, &pos_x, &pos_y);

    /* correction to center the current result                          */
    /* NOTE: rectangle is in viewport units, already scaled and rotated */
Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
953 954
    unsigned int cell_height = 0;
    unsigned int cell_width = 0;
955 956
    zathura_document_get_cell_size(zathura->document, &cell_height, &cell_width);

Sebastian Ramacher's avatar
CS  
Sebastian Ramacher committed
957 958
    unsigned int doc_height = 0;
    unsigned int doc_width = 0;
959 960
    zathura_document_get_document_size(zathura->document, &doc_height, &doc_width);

961 962 963 964
    /* compute the center of the rectangle, which will be aligned to the center
       of the viewport */
    double center_x = (rectangle.x1 + rectangle.x2) / 2;
    double center_y = (rectangle.y1 + rectangle.y2) / 2;
965

966
    pos_y += (center_y - (double)cell_height/2) / (double)doc_height;
Julian Orth's avatar
Julian Orth committed
967
    if (search_hadjust == true) {
968
      pos_x += (center_x - (double)cell_width/2) / (double)doc_width;
Julian Orth's avatar
Julian Orth committed
969
    }
970

971 972 973
    /* move to position */
    zathura_jumplist_add(zathura);
    position_set(zathura, pos_x, pos_y);
974
    zathura_jumplist_add(zathura);
Sebastian Ramacher's avatar
Sebastian Ramacher committed
975 976
  }

Moritz Lipp's avatar
Moritz Lipp committed
977
  return false;
978 979
}

Moritz Lipp's avatar
Moritz Lipp committed
980
bool
981
sc_navigate_index(girara_session_t* session, girara_argument_t* argument,
Moritz Lipp's avatar
Moritz Lipp committed
982
                  girara_event_t* UNUSED(event), unsigned int UNUSED(t))
983
{
Moritz Lipp's avatar
Moritz Lipp committed
984 985 986 987 988 989
  g_return_val_if_fail(session != NULL, false);
  g_return_val_if_fail(session->global.data != NULL, false);
  zathura_t* zathura = session->global.data;
  g_return_val_if_fail(argument != NULL, false);
  g_return_val_if_fail(zathura->document != NULL, false);

Moritz Lipp's avatar
Moritz Lipp committed
990 991 992 993 994 995 996 997
  if(zathura->ui.index == NULL) {
    return false;
  }

  GtkTreeView *tree_view = gtk_container_get_children(GTK_CONTAINER(zathura->ui.index))->data;
  GtkTreePath *path;

  gtk_tree_view_get_cursor(tree_view, &path, NULL);
Moritz Lipp's avatar
Moritz Lipp committed
998
  if (path == NULL) {
Moritz Lipp's avatar
Moritz Lipp committed
999 1000 1001 1002 1003 1004 1005 1006 1007 1008
    return false;
  }

  GtkTreeModel *model = gtk_tree_view_get_model(tree_view);
  GtkTreeIter   iter;
  GtkTreeIter   child_iter;

  gboolean is_valid_path = TRUE;

  switch(argument->n) {
1009
    case TOP:
Sebastian Ramacher's avatar
Sebastian Ramacher committed
1010
      /* go to the first node */
1011 1012 1013 1014
      gtk_tree_path_free(path);
      path = gtk_tree_path_new_first();
      break;
    case BOTTOM:
Sebastian Ramacher's avatar
Sebastian Ramacher committed
1015
      /* go to the last visiible node */
1016 1017 1018 1019 1020 1021 1022 1023
      gtk_tree_path_free(path);
      path = gtk_tree_path_new_from_indices(gtk_tree_model_iter_n_children(model, NULL) - 1, -1);
      gtk_tree_model_get_iter(model, &iter, path);
      while (gtk_tree_model_iter_has_child(model, &iter) == TRUE &&
          gtk_tree_view_row_expanded(tree_view, path) == TRUE) {
          gtk_tree_path_append_index(path, gtk_tree_model_iter_n_children(model, &iter) - 1);
      }
      break;
Moritz Lipp's avatar
Moritz Lipp committed
1024
    case UP:
1025
      if (gtk_tree_path_prev(path) == FALSE) {
1026 1027 1028
        /* For some reason gtk_tree_path_up returns TRUE although we're not
         * moving anywhere. */
        is_valid_path = gtk_tree_path_up(path) && (gtk_tree_path_get_depth(path) > 0);
Moritz Lipp's avatar
Moritz Lipp committed
1029
      } else { /* row above */
1030
        while (gtk_tree_view_row_expanded(tree_view, path)) {
Moritz Lipp's avatar
Moritz Lipp committed
1031 1032 1033
          gtk_tree_model_get_iter(model, &iter, path);
          /* select last child */
          gtk_tree_model_iter_nth_child(model, &child_iter, &iter,
Moritz Lipp's avatar
Moritz Lipp committed
1034
                                        gtk_tree_model_iter_n_children(model, &iter)-1);
Moritz Lipp's avatar
Moritz Lipp committed
1035 1036 1037 1038 1039 1040
          gtk_tree_path_free(path);
          path = gtk_tree_model_get_path(model, &child_iter);
        }
      }
      break;
    case COLLAPSE:
1041
      if (gtk_tree_view_collapse_row(tree_view, path) == FALSE
Moritz Lipp's avatar