BZ_bzRead2 is based on BZ_bzRead from the bzlib library.
[elinks.git] / src / terminal / tab.c
blob24ac7579fb10d570706a19449d7101ebd48f74e3
1 /* Tab-style (those containing real documents) windows infrastructure. */
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
7 #include "elinks.h"
9 #include "bfu/dialog.h"
10 #include "config/options.h"
11 #include "dialogs/menu.h"
12 #include "document/document.h"
13 #include "document/view.h"
14 #include "intl/gettext/libintl.h"
15 #include "main/select.h"
16 #include "protocol/uri.h"
17 #include "session/session.h"
18 #include "terminal/screen.h"
19 #include "terminal/tab.h"
20 #include "terminal/terminal.h"
21 #include "terminal/window.h"
22 #include "util/error.h"
23 #include "util/memory.h"
24 #include "util/lists.h"
25 #include "viewer/text/link.h"
26 #include "viewer/text/view.h"
29 struct window *
30 init_tab(struct terminal *term, void *data, window_handler_T handler)
32 struct window *win = mem_calloc(1, sizeof(*win));
33 struct window *pos;
35 if (!win) return NULL;
37 win->handler = handler;
38 win->term = term;
39 win->data = data;
40 win->type = WINDOW_TAB;
41 win->resize = 1;
43 /* Insert the new tab immediately above all existing tabs in
44 * the stack of windows. */
45 foreach_tab (pos, term->windows) {
46 pos = pos->prev;
47 goto found_pos;
49 /* This is a new terminal and there are no tabs yet. If there
50 * were a main menu already, then we'd have to place the tab
51 * above it if it were inactive, or below if it were active. */
52 assert(term->main_menu == NULL);
53 pos = (struct window *) term->windows.prev;
55 found_pos:
56 add_at_pos(pos, win);
58 assert_window_stacking(term);
60 return win;
63 /* Number of tabs at the terminal (in term->windows) */
64 inline int
65 number_of_tabs(struct terminal *term)
67 int result = 0;
68 struct window *win;
70 foreach_tab (win, term->windows)
71 result++;
73 return result;
76 /* Number of tab */
77 int
78 get_tab_number(struct window *window)
80 struct terminal *term = window->term;
81 struct window *win;
82 int current = 0;
83 int num = 0;
85 foreachback_tab (win, term->windows) {
86 if (win == window) {
87 num = current;
88 break;
90 current++;
93 return num;
96 /* Get tab of an according index */
97 struct window *
98 get_tab_by_number(struct terminal *term, int num)
100 struct window *win = NULL;
102 foreachback_tab (win, term->windows) {
103 if (!num) break;
104 num--;
107 /* Ensure that the return value actually points to a struct
108 * window. */
109 assertm((struct list_head *) win != &term->windows,
110 "tab number out of range");
111 if_assert_failed return term->windows.next;
113 return win;
116 /* Returns number of the tab at @xpos, or -1 if none. */
118 get_tab_number_by_xpos(struct terminal *term, int xpos)
120 int num = 0;
121 struct window *win = NULL;
123 foreachback_tab (win, term->windows) {
124 if (xpos >= win->xpos
125 && xpos < win->xpos + win->width)
126 return num;
127 num++;
130 return -1;
133 /* if @tabs_count > 0, then it is taken as the result of a recent
134 * call to number_of_tabs() so it just uses this value. */
135 void
136 switch_to_tab(struct terminal *term, int tab, int tabs_count)
138 if (tabs_count < 0) tabs_count = number_of_tabs(term);
140 if (tabs_count > 1) {
141 if (get_opt_bool("ui.tabs.wraparound")) {
142 tab %= tabs_count;
143 if (tab < 0) tab += tabs_count;
144 } else
145 int_bounds(&tab, 0, tabs_count - 1);
146 } else tab = 0;
148 if (tab != term->current_tab) {
149 term->current_tab = tab;
150 set_screen_dirty(term->screen, 0, term->height);
151 redraw_terminal(term);
155 void
156 switch_current_tab(struct session *ses, int direction)
158 struct terminal *term = ses->tab->term;
159 int tabs_count = number_of_tabs(term);
160 int count;
162 if (tabs_count < 2)
163 return;
165 count = eat_kbd_repeat_count(ses);
166 if (count) direction *= count;
168 switch_to_tab(term, term->current_tab + direction, tabs_count);
171 static void
172 really_close_tab(struct session *ses)
174 struct terminal *term = ses->tab->term;
175 struct window *current_tab = get_current_tab(term);
177 if (ses->tab == current_tab) {
178 int tabs_count = number_of_tabs(term);
180 switch_to_tab(term, term->current_tab - 1, tabs_count - 1);
183 delete_window(ses->tab);
186 void
187 close_tab(struct terminal *term, struct session *ses)
189 /* [gettext_accelerator_context(close_tab)] */
190 int tabs_count = number_of_tabs(term);
192 if (tabs_count < 2) {
193 query_exit(ses);
194 return;
197 if (!get_opt_bool("ui.tabs.confirm_close")) {
198 really_close_tab(ses);
199 return;
202 msg_box(term, NULL, 0,
203 N_("Close tab"), ALIGN_CENTER,
204 N_("Do you really want to close the current tab?"),
205 ses, 2,
206 N_("~Yes"), (void (*)(void *)) really_close_tab, B_ENTER,
207 N_("~No"), NULL, B_ESC);
210 static void
211 really_close_tabs(struct session *ses)
213 struct terminal *term = ses->tab->term;
214 struct window *current_tab = get_current_tab(term);
215 struct window *tab;
217 foreach_tab (tab, term->windows) {
218 if (tab == current_tab) continue;
220 /* Update the current tab counter so assertions in the
221 * delete_window() call-chain will hold, namely the one in
222 * get_tab_by_number(). */
223 if (term->current_tab > 0)
224 term->current_tab--;
226 tab = tab->prev;
227 delete_window(tab->next);
230 redraw_terminal(term);
233 void
234 close_all_tabs_but_current(struct session *ses)
236 /* [gettext_accelerator_context(close_all_tabs_but_current)] */
237 assert(ses);
238 if_assert_failed return;
240 if (!get_opt_bool("ui.tabs.confirm_close")) {
241 really_close_tabs(ses);
242 return;
245 msg_box(ses->tab->term, NULL, 0,
246 N_("Close tab"), ALIGN_CENTER,
247 N_("Do you really want to close all except the current tab?"),
248 ses, 2,
249 N_("~Yes"), (void (*)(void *)) really_close_tabs, B_ENTER,
250 N_("~No"), NULL, B_ESC);
254 void
255 open_uri_in_new_tab(struct session *ses, struct uri *uri, int in_background,
256 int based)
258 assert(ses);
259 /* @based means whether the current @ses location will be preloaded
260 * in the tab. */
261 init_session(based ? ses : NULL, ses->tab->term, uri, in_background);
264 void
265 delayed_open(void *data)
267 struct delayed_open *deo = data;
269 assert(deo);
270 open_uri_in_new_tab(deo->ses, deo->uri, 0, 0);
271 done_uri(deo->uri);
272 mem_free(deo);
275 void
276 open_current_link_in_new_tab(struct session *ses, int in_background)
278 struct document_view *doc_view = current_frame(ses);
279 struct uri *uri = NULL;
280 struct link *link;
282 if (doc_view) assert(doc_view->vs && doc_view->document);
283 if_assert_failed return;
285 link = get_current_link(doc_view);
286 if (link) uri = get_link_uri(ses, doc_view, link);
288 open_uri_in_new_tab(ses, uri, in_background, 1);
289 if (uri) done_uri(uri);
292 void
293 move_current_tab(struct session *ses, int direction)
295 struct terminal *term = ses->tab->term;
296 int tabs = number_of_tabs(term);
297 struct window *current_tab = get_current_tab(term);
298 struct window *tab;
299 int new_pos;
300 int count;
302 assert(ses && direction);
304 count = eat_kbd_repeat_count(ses);
305 if (count) direction *= count;
307 new_pos = term->current_tab + direction;
309 if (get_opt_bool("ui.tabs.wraparound")) {
310 new_pos %= tabs;
311 if (new_pos < 0) new_pos = tabs + new_pos;
312 } else {
313 int_bounds(&new_pos, 0, tabs - 1);
315 assert(0 <= new_pos && new_pos < tabs);
317 /* This protects against tabs==1 and optimizes an unusual case. */
318 if (new_pos == term->current_tab) return;
320 del_from_list(current_tab);
321 if (new_pos == 0) {
322 tab = get_tab_by_number(term, 0);
323 } else {
324 tab = get_tab_by_number(term, new_pos-1)->prev;
326 add_at_pos(tab, current_tab);
328 switch_to_tab(term, new_pos, tabs);