[wip] draw_images(): w, h, spos -> box screen_img; img_box -> pixel_box
[elinks/images.git] / src / terminal / tab.c
blob85f60ddb662db711e9e6f6582f046b168f0f698f
1 /** Tab-style (those containing real documents) windows infrastructure.
2 * @file */
4 #ifdef HAVE_CONFIG_H
5 #include "config.h"
6 #endif
8 #include "elinks.h"
10 #include "bfu/dialog.h"
11 #include "config/options.h"
12 #include "dialogs/menu.h"
13 #include "document/document.h"
14 #include "document/view.h"
15 #include "intl/gettext/libintl.h"
16 #include "main/select.h"
17 #include "protocol/uri.h"
18 #include "session/session.h"
19 #include "terminal/screen.h"
20 #include "terminal/tab.h"
21 #include "terminal/terminal.h"
22 #include "terminal/window.h"
23 #include "util/error.h"
24 #include "util/memory.h"
25 #include "util/lists.h"
26 #include "viewer/text/link.h"
27 #include "viewer/text/view.h"
30 struct window *
31 init_tab(struct terminal *term, void *data, window_handler_T handler)
33 struct window *win = mem_calloc(1, sizeof(*win));
34 struct window *pos;
36 if (!win) return NULL;
38 win->handler = handler;
39 win->term = term;
40 win->data = data;
41 win->type = WINDOW_TAB;
42 win->resize = 1;
44 /* Insert the new tab immediately above all existing tabs in
45 * the stack of windows. */
46 foreach_tab (pos, term->windows) {
47 pos = pos->prev;
48 goto found_pos;
50 /* This is a new terminal and there are no tabs yet. If there
51 * were a main menu already, then we'd have to place the tab
52 * above it if it were inactive, or below if it were active. */
53 assert(term->main_menu == NULL);
54 pos = (struct window *) term->windows.prev;
56 found_pos:
57 add_at_pos(pos, win);
59 assert_window_stacking(term);
61 return win;
64 /** If the topmost window is a tab, return 1; else, return 0. */
65 NONSTATIC_INLINE int
66 tabs_are_on_top(struct terminal *term)
68 if (list_empty(term->windows))
69 return 0;
71 return ((struct window *) term->windows.next)->type == WINDOW_TAB;
74 /** Number of tabs at the terminal (in term->windows) */
75 NONSTATIC_INLINE int
76 number_of_tabs(struct terminal *term)
78 int result = 0;
79 struct window *win;
81 foreach_tab (win, term->windows)
82 result++;
84 return result;
87 /** Number of tab */
88 int
89 get_tab_number(struct window *window)
91 struct terminal *term = window->term;
92 struct window *win;
93 int current = 0;
94 int num = 0;
96 foreachback_tab (win, term->windows) {
97 if (win == window) {
98 num = current;
99 break;
101 current++;
104 return num;
107 /** Get tab of an according index */
108 struct window *
109 get_tab_by_number(struct terminal *term, int num)
111 struct window *win = NULL;
113 foreachback_tab (win, term->windows) {
114 if (!num) break;
115 num--;
118 /* Ensure that the return value actually points to a struct
119 * window. */
120 assertm((LIST_OF(struct window) *) win != &term->windows,
121 "tab number out of range");
122 if_assert_failed return term->windows.next;
124 return win;
127 /** Returns number of the tab at @a xpos, or -1 if none. */
129 get_tab_number_by_xpos(struct terminal *term, int xpos)
131 int num = 0;
132 struct window *win = NULL;
134 foreachback_tab (win, term->windows) {
135 if (xpos >= win->xpos
136 && xpos < win->xpos + win->width)
137 return num;
138 num++;
141 return -1;
144 /*! If @a tabs_count > 0, then it is taken as the result of a recent
145 * call to number_of_tabs() so it just uses this value. */
146 void
147 switch_to_tab(struct terminal *term, int tab, int tabs_count)
149 if (tabs_count < 0) tabs_count = number_of_tabs(term);
151 if (tabs_count > 1) {
152 if (get_opt_bool("ui.tabs.wraparound",
153 get_current_tab(term)->data)) {
154 tab %= tabs_count;
155 if (tab < 0) tab += tabs_count;
156 } else
157 int_bounds(&tab, 0, tabs_count - 1);
158 } else tab = 0;
160 if (tab != term->current_tab) {
161 term->current_tab = tab;
162 set_screen_dirty(term->screen, 0, term->height);
163 redraw_terminal(term);
167 void
168 switch_current_tab(struct session *ses, int direction)
170 struct terminal *term = ses->tab->term;
171 int tabs_count = number_of_tabs(term);
172 int count;
174 if (tabs_count < 2)
175 return;
177 count = eat_kbd_repeat_count(ses);
178 if (count) direction *= count;
180 switch_to_tab(term, term->current_tab + direction, tabs_count);
183 static void
184 really_close_tab(void *ses_)
186 struct session *ses = ses_;
187 struct terminal *term = ses->tab->term;
188 struct window *current_tab = get_current_tab(term);
190 if (ses->tab == current_tab) {
191 int tabs_count = number_of_tabs(term);
193 switch_to_tab(term, term->current_tab - 1, tabs_count - 1);
196 delete_window(ses->tab);
199 void
200 close_tab(struct terminal *term, struct session *ses)
202 /* [gettext_accelerator_context(close_tab)] */
203 int tabs_count = number_of_tabs(term);
205 if (tabs_count < 2) {
206 query_exit(ses);
207 return;
210 if (!get_opt_bool("ui.tabs.confirm_close", ses)) {
211 really_close_tab(ses);
212 return;
215 msg_box(term, NULL, 0,
216 N_("Close tab"), ALIGN_CENTER,
217 N_("Do you really want to close the current tab?"),
218 ses, 2,
219 MSG_BOX_BUTTON(N_("~Yes"), really_close_tab, B_ENTER),
220 MSG_BOX_BUTTON(N_("~No"), NULL, B_ESC));
223 static void
224 really_close_tabs(void *ses_)
226 struct session *ses = ses_;
227 struct terminal *term = ses->tab->term;
228 struct window *current_tab = get_current_tab(term);
229 struct window *tab;
231 foreach_tab (tab, term->windows) {
232 if (tab == current_tab) continue;
234 /* Update the current tab counter so assertions in the
235 * delete_window() call-chain will hold, namely the one in
236 * get_tab_by_number(). */
237 if (term->current_tab > 0)
238 term->current_tab--;
240 tab = tab->prev;
241 delete_window(tab->next);
244 redraw_terminal(term);
247 void
248 close_all_tabs_but_current(struct session *ses)
250 /* [gettext_accelerator_context(close_all_tabs_but_current)] */
251 assert(ses);
252 if_assert_failed return;
254 if (!get_opt_bool("ui.tabs.confirm_close", ses)) {
255 really_close_tabs(ses);
256 return;
259 msg_box(ses->tab->term, NULL, 0,
260 N_("Close tab"), ALIGN_CENTER,
261 N_("Do you really want to close all except the current tab?"),
262 ses, 2,
263 MSG_BOX_BUTTON(N_("~Yes"), really_close_tabs, B_ENTER),
264 MSG_BOX_BUTTON(N_("~No"), NULL, B_ESC));
268 void
269 open_uri_in_new_tab(struct session *ses, struct uri *uri, int in_background,
270 int based)
272 assert(ses);
273 /* @based means whether the current @ses location will be preloaded
274 * in the tab. */
275 init_session(based ? ses : NULL, ses->tab->term, uri, in_background);
278 void
279 delayed_open(void *data)
281 struct delayed_open *deo = data;
283 assert(deo);
284 open_uri_in_new_tab(deo->ses, deo->uri, 0, 0);
285 done_uri(deo->uri);
286 mem_free(deo);
289 void
290 open_current_link_in_new_tab(struct session *ses, int in_background)
292 struct document_view *doc_view = current_frame(ses);
293 struct uri *uri = NULL;
294 struct link *link;
296 if (doc_view) assert(doc_view->vs && doc_view->document);
297 if_assert_failed return;
299 link = get_current_link(doc_view);
300 if (link) uri = get_link_uri(ses, doc_view, link);
302 open_uri_in_new_tab(ses, uri, in_background, 1);
303 if (uri) done_uri(uri);
306 void
307 move_current_tab(struct session *ses, int direction)
309 struct terminal *term = ses->tab->term;
310 int tabs = number_of_tabs(term);
311 struct window *current_tab = get_current_tab(term);
312 struct window *tab;
313 int new_pos;
314 int count;
316 assert(ses && direction);
318 count = eat_kbd_repeat_count(ses);
319 if (count) direction *= count;
321 new_pos = term->current_tab + direction;
323 if (get_opt_bool("ui.tabs.wraparound", ses)) {
324 new_pos %= tabs;
325 if (new_pos < 0) new_pos = tabs + new_pos;
326 } else {
327 int_bounds(&new_pos, 0, tabs - 1);
329 assert(0 <= new_pos && new_pos < tabs);
331 /* This protects against tabs==1 and optimizes an unusual case. */
332 if (new_pos == term->current_tab) return;
334 del_from_list(current_tab);
335 if (new_pos == 0) {
336 tab = get_tab_by_number(term, 0);
337 } else {
338 tab = get_tab_by_number(term, new_pos-1)->prev;
340 add_at_pos(tab, current_tab);
342 switch_to_tab(term, new_pos, tabs);