1 /** Tab-style (those containing real documents) windows infrastructure.
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"
31 init_tab(struct terminal
*term
, void *data
, window_handler_T handler
)
33 struct window
*win
= mem_calloc(1, sizeof(*win
));
36 if (!win
) return NULL
;
38 win
->handler
= handler
;
41 win
->type
= WINDOW_TAB
;
44 /* Insert the new tab immediately above all existing tabs in
45 * the stack of windows. */
46 foreach_tab (pos
, term
->windows
) {
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
;
59 assert_window_stacking(term
);
64 /** If the topmost window is a tab, return 1; else, return 0. */
66 tabs_are_on_top(struct terminal
*term
)
68 if (list_empty(term
->windows
))
71 return ((struct window
*) term
->windows
.next
)->type
== WINDOW_TAB
;
74 /** Number of tabs at the terminal (in term->windows) */
76 number_of_tabs(struct terminal
*term
)
81 foreach_tab (win
, term
->windows
)
89 get_tab_number(struct window
*window
)
91 struct terminal
*term
= window
->term
;
96 foreachback_tab (win
, term
->windows
) {
107 /** Get tab of an according index */
109 get_tab_by_number(struct terminal
*term
, int num
)
111 struct window
*win
= NULL
;
113 foreachback_tab (win
, term
->windows
) {
118 /* Ensure that the return value actually points to a struct
120 assertm((LIST_OF(struct window
) *) win
!= &term
->windows
,
121 "tab number out of range");
122 if_assert_failed
return term
->windows
.next
;
127 /** Returns number of the tab at @a xpos, or -1 if none. */
129 get_tab_number_by_xpos(struct terminal
*term
, int xpos
)
132 struct window
*win
= NULL
;
134 foreachback_tab (win
, term
->windows
) {
135 if (xpos
>= win
->xpos
136 && xpos
< win
->xpos
+ win
->width
)
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. */
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
)) {
155 if (tab
< 0) tab
+= tabs_count
;
157 int_bounds(&tab
, 0, tabs_count
- 1);
160 if (tab
!= term
->current_tab
) {
161 term
->current_tab
= tab
;
162 set_screen_dirty(term
->screen
, 0, term
->height
);
163 redraw_terminal(term
);
168 switch_current_tab(struct session
*ses
, int direction
)
170 struct terminal
*term
= ses
->tab
->term
;
171 int tabs_count
= number_of_tabs(term
);
177 count
= eat_kbd_repeat_count(ses
);
178 if (count
) direction
*= count
;
180 switch_to_tab(term
, term
->current_tab
+ direction
, tabs_count
);
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
);
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) {
210 if (!get_opt_bool("ui.tabs.confirm_close", ses
)) {
211 really_close_tab(ses
);
215 msg_box(term
, NULL
, 0,
216 N_("Close tab"), ALIGN_CENTER
,
217 N_("Do you really want to close the current tab?"),
219 MSG_BOX_BUTTON(N_("~Yes"), really_close_tab
, B_ENTER
),
220 MSG_BOX_BUTTON(N_("~No"), NULL
, B_ESC
));
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
);
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)
241 delete_window(tab
->next
);
244 redraw_terminal(term
);
248 close_all_tabs_but_current(struct session
*ses
)
250 /* [gettext_accelerator_context(close_all_tabs_but_current)] */
252 if_assert_failed
return;
254 if (!get_opt_bool("ui.tabs.confirm_close", ses
)) {
255 really_close_tabs(ses
);
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?"),
263 MSG_BOX_BUTTON(N_("~Yes"), really_close_tabs
, B_ENTER
),
264 MSG_BOX_BUTTON(N_("~No"), NULL
, B_ESC
));
269 open_uri_in_new_tab(struct session
*ses
, struct uri
*uri
, int in_background
,
273 /* @based means whether the current @ses location will be preloaded
275 init_session(based
? ses
: NULL
, ses
->tab
->term
, uri
, in_background
);
279 delayed_open(void *data
)
281 struct delayed_open
*deo
= data
;
284 open_uri_in_new_tab(deo
->ses
, deo
->uri
, 0, 0);
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
;
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
);
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
);
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
)) {
325 if (new_pos
< 0) new_pos
= tabs
+ new_pos
;
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
);
336 tab
= get_tab_by_number(term
, 0);
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
);