1 /* Tab-style (those containing real documents) windows infrastructure. */
2 
3 #ifdef HAVE_CONFIG_H
4 #include "config.h"
5 #endif
6 
7 #include "elinks.h"
8 
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"
27 
28 
29 struct window *
init_tab(struct terminal * term,void * data,window_handler_T handler)30 init_tab(struct terminal *term, void *data, window_handler_T handler)
31 {
32 	struct window *win = mem_calloc(1, sizeof(*win));
33 
34 	if (!win) return NULL;
35 
36 	win->handler = handler;
37 	win->term = term;
38 	win->data = data;
39 	win->type = WINDOW_TAB;
40 	win->resize = 1;
41 
42 	add_to_list(term->windows, win);
43 
44 	return win;
45 }
46 
47 /* Number of tabs at the terminal (in term->windows) */
48 inline int
number_of_tabs(struct terminal * term)49 number_of_tabs(struct terminal *term)
50 {
51 	int result = 0;
52 	struct window *win;
53 
54 	foreach_tab (win, term->windows)
55 		result++;
56 
57 	return result;
58 }
59 
60 /* Number of tab */
61 int
get_tab_number(struct window * window)62 get_tab_number(struct window *window)
63 {
64 	struct terminal *term = window->term;
65 	struct window *win;
66 	int current = 0;
67 	int num = 0;
68 
69 	foreachback_tab (win, term->windows) {
70 		if (win == window) {
71 			num = current;
72 			break;
73 		}
74 		current++;
75 	}
76 
77 	return num;
78 }
79 
80 /* Get tab of an according index */
81 struct window *
get_tab_by_number(struct terminal * term,int num)82 get_tab_by_number(struct terminal *term, int num)
83 {
84 	struct window *win = NULL;
85 
86 	foreachback_tab (win, term->windows) {
87 		if (!num) break;
88 		num--;
89 	}
90 
91 	return win;
92 }
93 
94 /* Returns number of the tab at @xpos, or -1 if none. */
95 int
get_tab_number_by_xpos(struct terminal * term,int xpos)96 get_tab_number_by_xpos(struct terminal *term, int xpos)
97 {
98 	int num = 0;
99 	struct window *win = NULL;
100 
101 	foreachback_tab (win, term->windows) {
102 		if (xpos >= win->xpos
103 		    && xpos < win->xpos + win->width)
104 			return num;
105 		num++;
106 	}
107 
108 	return -1;
109 }
110 
111 /* if @tabs > 0, then it is taken as the result of a recent
112  * call to number_of_tabs() so it just uses this value. */
113 void
switch_to_tab(struct terminal * term,int tab,int tabs)114 switch_to_tab(struct terminal *term, int tab, int tabs)
115 {
116 	if (tabs < 0) tabs = number_of_tabs(term);
117 
118 	if (tabs > 1) {
119 		if (tab >= tabs) {
120 			if (get_opt_bool("ui.tabs.wraparound"))
121 				tab = 0;
122 			else
123 				tab = tabs - 1;
124 		}
125 
126 		if (tab < 0) {
127 			if (get_opt_bool("ui.tabs.wraparound"))
128 				tab = tabs - 1;
129 			else
130 				tab = 0;
131 		}
132 	} else tab = 0;
133 
134 	if (tab != term->current_tab) {
135 		term->current_tab = tab;
136 		set_screen_dirty(term->screen, 0, term->height);
137 		redraw_terminal(term);
138 	}
139 }
140 
141 void
switch_current_tab(struct session * ses,int direction)142 switch_current_tab(struct session *ses, int direction)
143 {
144 	struct terminal *term = ses->tab->term;
145 	int num_tabs = number_of_tabs(term);
146 	int count;
147 
148 	if (num_tabs < 2)
149 		return;
150 
151 	count = eat_kbd_repeat_count(ses);
152 	if (count) direction *= count;
153 
154 	switch_to_tab(term, term->current_tab + direction, num_tabs);
155 }
156 
157 static void
really_close_tab(struct session * ses)158 really_close_tab(struct session *ses)
159 {
160 	struct terminal *term = ses->tab->term;
161 	struct window *current_tab = get_current_tab(term);
162 
163 	if (ses->tab == current_tab) {
164 		int num_tabs = number_of_tabs(term);
165 
166 		switch_to_tab(term, term->current_tab - 1, num_tabs - 1);
167 	}
168 
169 	delete_window(ses->tab);
170 }
171 
172 void
close_tab(struct terminal * term,struct session * ses)173 close_tab(struct terminal *term, struct session *ses)
174 {
175 	int num_tabs = number_of_tabs(term);
176 
177 	if (num_tabs < 2) {
178 		query_exit(ses);
179 		return;
180 	}
181 
182 	if (!get_opt_bool("ui.tabs.confirm_close")) {
183 		really_close_tab(ses);
184 		return;
185 	}
186 
187 	msg_box(term, NULL, 0,
188 		N_("Close tab"), ALIGN_CENTER,
189 		N_("Do you really want to close the current tab?"),
190 		ses, 2,
191 		N_("~Yes"), (void (*)(void *)) really_close_tab, B_ENTER,
192 		N_("~No"), NULL, B_ESC);
193 }
194 
195 static void
really_close_tabs(struct session * ses)196 really_close_tabs(struct session *ses)
197 {
198 	struct terminal *term = ses->tab->term;
199 	struct window *current = get_current_tab(term);
200 	struct window *tab;
201 
202 	foreach_tab (tab, term->windows) {
203 		if (tab == current) continue;
204 		tab = tab->prev;
205 		delete_window(tab->next);
206 	}
207 
208 	term->current_tab = 0;
209 	redraw_terminal(term);
210 }
211 
212 void
close_all_tabs_but_current(struct session * ses)213 close_all_tabs_but_current(struct session *ses)
214 {
215 	assert(ses);
216 	if_assert_failed return;
217 
218 	if (!get_opt_bool("ui.tabs.confirm_close")) {
219 		really_close_tabs(ses);
220 		return;
221 	}
222 
223 	msg_box(ses->tab->term, NULL, 0,
224 		N_("Close tab"), ALIGN_CENTER,
225 		N_("Do you really want to close all except the current tab?"),
226 		ses, 2,
227 		N_("~Yes"), (void (*)(void *)) really_close_tabs, B_ENTER,
228 		N_("~No"), NULL, B_ESC);
229 }
230 
231 
232 void
open_uri_in_new_tab(struct session * ses,struct uri * uri,int in_background,int based)233 open_uri_in_new_tab(struct session *ses, struct uri *uri, int in_background,
234                     int based)
235 {
236 	assert(ses);
237 	/* @based means whether the current @ses location will be preloaded
238 	 * in the tab. */
239 	init_session(based ? ses : NULL, ses->tab->term, uri, in_background);
240 }
241 
242 void
open_current_link_in_new_tab(struct session * ses,int in_background)243 open_current_link_in_new_tab(struct session *ses, int in_background)
244 {
245 	struct document_view *doc_view = current_frame(ses);
246 	struct uri *uri = NULL;
247 	struct link *link;
248 
249 	if (doc_view) assert(doc_view->vs && doc_view->document);
250 	if_assert_failed return;
251 
252 	link = get_current_link(doc_view);
253 	if (link) uri = get_link_uri(ses, doc_view, link);
254 
255 	open_uri_in_new_tab(ses, uri, in_background, 1);
256 	if (uri) done_uri(uri);
257 }
258 
259 void
move_current_tab(struct session * ses,int direction)260 move_current_tab(struct session *ses, int direction)
261 {
262 	struct terminal *term = ses->tab->term;
263 	int tabs = number_of_tabs(term);
264 	struct window *current_tab = get_current_tab(term);
265 	struct window *tab;
266 	int new_pos;
267 	int count;
268 
269 	assert(ses && direction);
270 
271 	count = eat_kbd_repeat_count(ses);
272 	if (count) direction *= count;
273 
274 	new_pos = term->current_tab + direction;
275 
276 	while (new_pos < 1 || new_pos > tabs)
277 		new_pos += new_pos < 1 ? tabs : -tabs;
278 
279 	assert(0 < new_pos && new_pos <= tabs);
280 
281 	if (new_pos == term->current_tab) return;
282 
283 	tab = get_tab_by_number(term, new_pos);
284 
285 	del_from_list(current_tab);
286 
287 	if (new_pos < term->current_tab) {
288 		add_at_pos(tab, current_tab);
289 	} else {
290 		add_to_list_end(*tab, current_tab);
291 	}
292 
293 	switch_to_tab(term, new_pos, tabs);
294 }
295