1 /* Tab-style (those containing real documents) windows infrastructure. */
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"
30 init_tab(struct terminal
*term
, void *data
, window_handler_T handler
)
32 struct window
*win
= mem_calloc(1, sizeof(*win
));
35 if (!win
) return NULL
;
37 win
->handler
= handler
;
40 win
->type
= WINDOW_TAB
;
43 /* Insert the new tab immediately above all existing tabs in
44 * the stack of windows. */
45 foreach_tab (pos
, term
->windows
) {
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
;
58 assert_window_stacking(term
);
63 /* Number of tabs at the terminal (in term->windows) */
65 number_of_tabs(struct terminal
*term
)
70 foreach_tab (win
, term
->windows
)
78 get_tab_number(struct window
*window
)
80 struct terminal
*term
= window
->term
;
85 foreachback_tab (win
, term
->windows
) {
96 /* Get tab of an according index */
98 get_tab_by_number(struct terminal
*term
, int num
)
100 struct window
*win
= NULL
;
102 foreachback_tab (win
, term
->windows
) {
107 /* Ensure that the return value actually points to a struct
109 assertm((struct list_head
*) win
!= &term
->windows
,
110 "tab number out of range");
111 if_assert_failed
return term
->windows
.next
;
116 /* Returns number of the tab at @xpos, or -1 if none. */
118 get_tab_number_by_xpos(struct terminal
*term
, int xpos
)
121 struct window
*win
= NULL
;
123 foreachback_tab (win
, term
->windows
) {
124 if (xpos
>= win
->xpos
125 && xpos
< win
->xpos
+ win
->width
)
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. */
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")) {
143 if (tab
< 0) tab
+= tabs_count
;
145 int_bounds(&tab
, 0, tabs_count
- 1);
148 if (tab
!= term
->current_tab
) {
149 term
->current_tab
= tab
;
150 set_screen_dirty(term
->screen
, 0, term
->height
);
151 redraw_terminal(term
);
156 switch_current_tab(struct session
*ses
, int direction
)
158 struct terminal
*term
= ses
->tab
->term
;
159 int tabs_count
= number_of_tabs(term
);
165 count
= eat_kbd_repeat_count(ses
);
166 if (count
) direction
*= count
;
168 switch_to_tab(term
, term
->current_tab
+ direction
, tabs_count
);
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
);
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) {
197 if (!get_opt_bool("ui.tabs.confirm_close")) {
198 really_close_tab(ses
);
202 msg_box(term
, NULL
, 0,
203 N_("Close tab"), ALIGN_CENTER
,
204 N_("Do you really want to close the current tab?"),
206 N_("~Yes"), (void (*)(void *)) really_close_tab
, B_ENTER
,
207 N_("~No"), NULL
, B_ESC
);
211 really_close_tabs(struct session
*ses
)
213 struct terminal
*term
= ses
->tab
->term
;
214 struct window
*current_tab
= get_current_tab(term
);
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)
227 delete_window(tab
->next
);
230 redraw_terminal(term
);
234 close_all_tabs_but_current(struct session
*ses
)
236 /* [gettext_accelerator_context(close_all_tabs_but_current)] */
238 if_assert_failed
return;
240 if (!get_opt_bool("ui.tabs.confirm_close")) {
241 really_close_tabs(ses
);
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?"),
249 N_("~Yes"), (void (*)(void *)) really_close_tabs
, B_ENTER
,
250 N_("~No"), NULL
, B_ESC
);
255 open_uri_in_new_tab(struct session
*ses
, struct uri
*uri
, int in_background
,
259 /* @based means whether the current @ses location will be preloaded
261 init_session(based
? ses
: NULL
, ses
->tab
->term
, uri
, in_background
);
265 delayed_open(void *data
)
267 struct delayed_open
*deo
= data
;
270 open_uri_in_new_tab(deo
->ses
, deo
->uri
, 0, 0);
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
;
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
);
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
);
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")) {
311 if (new_pos
< 0) new_pos
= tabs
+ new_pos
;
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
);
322 tab
= get_tab_by_number(term
, 0);
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
);