diff --git a/src/config/actions-main.inc b/src/config/actions-main.inc index 4fcf597b..71b11719 100644 --- a/src/config/actions-main.inc +++ b/src/config/actions-main.inc @@ -60,11 +60,15 @@ ACTION_(MAIN, "move-cursor-up", MOVE_CURSOR_UP, N__("Move cursor up"), ACTION_RE ACTION_(MAIN, "move-document-end", MOVE_DOCUMENT_END, N__("Move to the end of the document"), ACTION_REQUIRE_VIEW_STATE), ACTION_(MAIN, "move-document-start", MOVE_DOCUMENT_START, N__("Move to the start of the document"), ACTION_REQUIRE_VIEW_STATE), ACTION_(MAIN, "move-link-down", MOVE_LINK_DOWN, N__("Move one link down"), ACTION_REQUIRE_VIEW_STATE), +ACTION_(MAIN, "move-link-down-line", MOVE_LINK_DOWN_LINE, N__("Move to the next line with a link"), ACTION_REQUIRE_VIEW_STATE), ACTION_(MAIN, "move-link-left", MOVE_LINK_LEFT, N__("Move one link left"), ACTION_REQUIRE_VIEW_STATE), +ACTION_(MAIN, "move-link-left-line", MOVE_LINK_LEFT_LINE, N__("Move one link left or to the previous link"), ACTION_REQUIRE_VIEW_STATE), ACTION_(MAIN, "move-link-next", MOVE_LINK_NEXT, N__("Move to the next link"), ACTION_REQUIRE_VIEW_STATE), ACTION_(MAIN, "move-link-prev", MOVE_LINK_PREV, N__("Move to the previous link"), ACTION_REQUIRE_VIEW_STATE), ACTION_(MAIN, "move-link-right", MOVE_LINK_RIGHT, N__("Move one link right"), ACTION_REQUIRE_VIEW_STATE), +ACTION_(MAIN, "move-link-right-line", MOVE_LINK_RIGHT_LINE, N__("Move one link right or to the next link"), ACTION_REQUIRE_VIEW_STATE), ACTION_(MAIN, "move-link-up", MOVE_LINK_UP, N__("Move one link up"), ACTION_REQUIRE_VIEW_STATE), +ACTION_(MAIN, "move-link-up-line", MOVE_LINK_UP_LINE, N__("Move to the previous line with a link"), ACTION_REQUIRE_VIEW_STATE), ACTION_(MAIN, "move-page-down", MOVE_PAGE_DOWN, N__("Move downwards by a page"), ACTION_REQUIRE_VIEW_STATE), ACTION_(MAIN, "move-page-up", MOVE_PAGE_UP, N__("Move upwards by a page"), ACTION_REQUIRE_VIEW_STATE), ACTION_(MAIN, "open-link-in-new-tab", OPEN_LINK_IN_NEW_TAB, N__("Open the current link in a new tab"), ACTION_REQUIRE_VIEW_STATE | ACTION_JUMP_TO_LINK | ACTION_REQUIRE_LINK), diff --git a/src/viewer/action.c b/src/viewer/action.c index 6396b39f..093a2d17 100644 --- a/src/viewer/action.c +++ b/src/viewer/action.c @@ -364,10 +364,18 @@ do_action(struct session *ses, enum main_action action_id, int verbose) status = move_link_down(ses, doc_view); break; + case ACT_MAIN_MOVE_LINK_DOWN_LINE: + status = move_link_down_line(ses, doc_view); + break; + case ACT_MAIN_MOVE_LINK_LEFT: status = move_link_left(ses, doc_view); break; + case ACT_MAIN_MOVE_LINK_LEFT_LINE: + status = move_link_prev_line(ses, doc_view); + break; + case ACT_MAIN_MOVE_LINK_NEXT: status = move_link_next(ses, doc_view); break; @@ -380,10 +388,18 @@ do_action(struct session *ses, enum main_action action_id, int verbose) status = move_link_right(ses, doc_view); break; + case ACT_MAIN_MOVE_LINK_RIGHT_LINE: + status = move_link_next_line(ses, doc_view); + break; + case ACT_MAIN_MOVE_LINK_UP: status = move_link_up(ses, doc_view); break; + case ACT_MAIN_MOVE_LINK_UP_LINE: + status = move_link_up_line(ses, doc_view); + break; + case ACT_MAIN_MOVE_PAGE_DOWN: status = move_page_down(ses, doc_view); break; diff --git a/src/viewer/text/link.c b/src/viewer/text/link.c index 68551181..939132d7 100644 --- a/src/viewer/text/link.c +++ b/src/viewer/text/link.c @@ -543,7 +543,7 @@ next_link_in_view_y(struct document_view *doc_view, int current, int direction) /** Get the bounding columns of @a link at line @a y (or all lines if * @a y == -1). */ -static void +void get_link_x_bounds(struct link *link, int y, int *min_x, int *max_x) { int point; diff --git a/src/viewer/text/link.h b/src/viewer/text/link.h index a27c2d26..07502fe9 100644 --- a/src/viewer/text/link.h +++ b/src/viewer/text/link.h @@ -43,11 +43,14 @@ int current_link_is_visible(struct document_view *doc_view); int next_link_in_view(struct document_view *doc_view, int current, int direction); int next_link_in_view_y(struct document_view *doc_view, int current, int direction); int next_link_in_dir(struct document_view *doc_view, int dir_x, int dir_y); +int next_link_vertical(struct session *ses, struct document_view *doc_view, int dir_y); void jump_to_link_number(struct session *ses, struct document_view *doc_view, int); struct link *goto_current_link(struct session *ses, struct document_view *, int); void goto_link_number(struct session *ses, unsigned char *num); +void get_link_x_bounds(struct link *link, int y, int *min_x, int *max_x); + /* Bruteforce compilation fixes */ enum frame_event_status enter(struct session *ses, struct document_view *doc_view, int do_reload); diff --git a/src/viewer/text/view.c b/src/viewer/text/view.c index 65020787..4632fa40 100644 --- a/src/viewer/text/view.c +++ b/src/viewer/text/view.c @@ -61,6 +61,7 @@ #include "viewer/text/vs.h" +static enum frame_event_status move_cursor_rel(struct session *ses, struct document_view *view, int rx, int ry); void detach_formatted(struct document_view *doc_view) @@ -163,6 +164,91 @@ move_page_up(struct session *ses, struct document_view *doc_view) return doc_view->vs->y == oldy ? FRAME_EVENT_OK : FRAME_EVENT_REFRESH; } +enum frame_event_status +move_link_prev_line(struct session *ses, struct document_view *doc_view) +{ + struct view_state *vs; + struct document *document; + struct link *link, *last = NULL; + int y1, y, min_x, max_x, x1; + + assert(ses && doc_view && doc_view->vs && doc_view->document); + if_assert_failed return FRAME_EVENT_OK; + + vs = doc_view->vs; + document = doc_view->document; + if (!document->lines1) return FRAME_EVENT_OK; + + y = y1 = vs->y + ses->tab->y - ses->status.show_title_bar + - (ses->status.show_tabs_bar && ses->status.show_tabs_bar_at_top); + x1 = vs->x + ses->tab->x; + + link = get_current_link(doc_view); + if (link) { + get_link_x_bounds(link, y1, &min_x, &max_x); + } else { + min_x = max_x = x1; + int_upper_bound(&y, document->height - 1); + } + + for (; y >= 0; y--, min_x = INT_MAX) { + link = document->lines1[y]; + if (!link) continue; + for (; link <= document->lines2[y]; link++) { + if (link->points[0].y != y) continue; + if (link->points[0].x >= min_x) continue; + if (!last) last = link; + else if (link->points[0].x > last->points[0].x) last = link; + } + if (last) + return move_cursor_rel(ses, doc_view, last->points[0].x - x1, last->points[0].y - y1); + } + return FRAME_EVENT_OK; +} + + +enum frame_event_status +move_link_next_line(struct session *ses, struct document_view *doc_view) +{ + struct view_state *vs; + struct document *document; + struct link *link, *last = NULL; + int y1, y, min_x, max_x, x1; + + assert(ses && doc_view && doc_view->vs && doc_view->document); + if_assert_failed return FRAME_EVENT_OK; + + vs = doc_view->vs; + document = doc_view->document; + if (!document->lines1) return FRAME_EVENT_OK; + + y = y1 = vs->y + ses->tab->y - ses->status.show_title_bar + - (ses->status.show_tabs_bar && ses->status.show_tabs_bar_at_top); + x1 = vs->x + ses->tab->x; + + link = get_current_link(doc_view); + if (link) { + get_link_x_bounds(link, y1, &min_x, &max_x); + } else { + min_x = max_x = x1; + int_upper_bound(&y, document->height - 1); + } + + for (; y < document->height; y++, min_x = -1) { + link = document->lines1[y]; + if (!link) continue; + for (; link <= document->lines2[y]; link++) { + if (link->points[0].y != y) continue; + if (link->points[0].x <= min_x) continue; + if (!last) last = link; + else if (link->points[0].x < last->points[0].x) last = link; + } + if (last) + return move_cursor_rel(ses, doc_view, last->points[0].x - x1, last->points[0].y - y1); + } + return FRAME_EVENT_OK; +} + enum frame_event_status move_link(struct session *ses, struct document_view *doc_view, int direction, int wraparound_bound, int wraparound_link) @@ -555,7 +641,7 @@ move_cursor(struct session *ses, struct document_view *doc_view, int x, int y) return status; } -enum frame_event_status +static enum frame_event_status move_cursor_rel(struct session *ses, struct document_view *view, int rx, int ry) { @@ -593,6 +679,38 @@ move_cursor_down(struct session *ses, struct document_view *view) return move_cursor_rel(ses, view, 0, 1); } +enum frame_event_status +move_link_vertical(struct session *ses, struct document_view *doc_view, int dir_y) +{ + struct document *document; + struct view_state *vs; + int y, y1; + + assert(ses && doc_view && doc_view->vs && doc_view->document); + if_assert_failed return FRAME_EVENT_OK; + vs = doc_view->vs; + document = doc_view->document; + if (!document->lines1) return FRAME_EVENT_OK; + + y1 = vs->y + ses->tab->y - ses->status.show_status_bar + - (ses->status.show_tabs_bar && ses->status.show_tabs_bar_at_top); + y = y1 + dir_y; + if (dir_y < 0) + int_upper_bound(&y, document->height - 1); + else + int_lower_bound(&y, 0); + for (; dir_y > 0 ? y < document->height : y >= 0; y += dir_y) { + struct link *link = document->lines1[y]; + + if (!link) continue; + for (; link <= document->lines2[y]; link++) { + if (link->points[0].y == y) { + return move_cursor_rel(ses, doc_view, 0, y - y1); + } + } + } + return FRAME_EVENT_OK; +} enum frame_event_status copy_current_link_to_clipboard(struct session *ses, diff --git a/src/viewer/text/view.h b/src/viewer/text/view.h index e87fae64..ad9049bd 100644 --- a/src/viewer/text/view.h +++ b/src/viewer/text/view.h @@ -30,6 +30,13 @@ enum frame_event_status move_link_dir(struct session *ses, struct document_view #define move_link_left(ses, doc_view) move_link_dir(ses, doc_view, -1, 0) #define move_link_right(ses, doc_view) move_link_dir(ses, doc_view, 1, 0) +enum frame_event_status move_link_prev_line(struct session *ses, struct document_view *doc_view); +enum frame_event_status move_link_next_line(struct session *ses, struct document_view *doc_view); +enum frame_event_status move_link_vertical(struct session *ses, struct document_view *doc_view, int dir_y); + +#define move_link_up_line(ses, doc_view) move_link_vertical(ses, doc_view, -1) +#define move_link_down_line(ses, doc_view) move_link_vertical(ses, doc_view, 1) + enum frame_event_status scroll_up(struct session *ses, struct document_view *doc_view); enum frame_event_status scroll_down(struct session *ses, struct document_view *doc_view); enum frame_event_status scroll_left(struct session *ses, struct document_view *doc_view);