1 /*
2  window-items.c : irssi
3 
4     Copyright (C) 2000 Timo Sirainen
5 
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10 
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15 
16     You should have received a copy of the GNU General Public License along
17     with this program; if not, write to the Free Software Foundation, Inc.,
18     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 */
20 
21 #include "module.h"
22 #include "module-formats.h"
23 #include "modules.h"
24 #include "signals.h"
25 #include "servers.h"
26 #include "channels.h"
27 #include "settings.h"
28 
29 #include "levels.h"
30 
31 #include "fe-windows.h"
32 #include "window-items.h"
33 #include "printtext.h"
34 
window_item_add_signal(WINDOW_REC * window,WI_ITEM_REC * item,int automatic,int send_signal)35 static void window_item_add_signal(WINDOW_REC *window, WI_ITEM_REC *item, int automatic, int send_signal)
36 {
37 	g_return_if_fail(window != NULL);
38 	g_return_if_fail(item != NULL);
39 	g_return_if_fail(item->window == NULL);
40 
41         item->window = window;
42 
43 	if (window->items == NULL) {
44 		window->active = item;
45 		window->active_server = item->server;
46 	}
47 
48 	if (!automatic || settings_get_bool("window_auto_change")) {
49 		if (automatic)
50 			signal_emit("window changed automatic", 1, window);
51 		window_set_active(window);
52 	}
53 
54 	window->items = g_slist_append(window->items, item);
55 	if (send_signal)
56 		signal_emit("window item new", 2, window, item);
57 
58 	if (g_slist_length(window->items) == 1 ||
59 	    (!automatic && settings_get_bool("autofocus_new_items"))) {
60                 window->active = NULL;
61 		window_item_set_active(window, item);
62 	}
63 }
64 
window_item_add(WINDOW_REC * window,WI_ITEM_REC * item,int automatic)65 void window_item_add(WINDOW_REC *window, WI_ITEM_REC *item, int automatic)
66 {
67 	window_item_add_signal(window, item, automatic, TRUE);
68 }
69 
window_item_remove_signal(WI_ITEM_REC * item,int emit_signal)70 static void window_item_remove_signal(WI_ITEM_REC *item, int emit_signal)
71 {
72 	WINDOW_REC *window;
73 
74 	g_return_if_fail(item != NULL);
75 
76 	window = window_item_window(item);
77 
78 	if (window == NULL)
79 		return;
80 
81         item->window = NULL;
82 	window->items = g_slist_remove(window->items, item);
83 
84 	if (window->active == item) {
85 		window_item_set_active(window, window->items == NULL ? NULL :
86 				       window->items->data);
87 	}
88 
89 	if (emit_signal)
90 		signal_emit("window item remove", 2, window, item);
91 }
92 
window_item_remove(WI_ITEM_REC * item)93 void window_item_remove(WI_ITEM_REC *item)
94 {
95 	window_item_remove_signal(item, TRUE);
96 }
97 
window_item_destroy(WI_ITEM_REC * item)98 void window_item_destroy(WI_ITEM_REC *item)
99 {
100         window_item_remove(item);
101         item->destroy(item);
102 }
103 
window_item_change_server(WI_ITEM_REC * item,void * server)104 void window_item_change_server(WI_ITEM_REC *item, void *server)
105 {
106 	WINDOW_REC *window;
107 
108 	g_return_if_fail(item != NULL);
109 
110 	window = window_item_window(item);
111 	item->server = server;
112 
113         signal_emit("window item server changed", 2, window, item);
114 	if (window->active == item) window_change_server(window, item->server);
115 }
116 
window_item_set_active(WINDOW_REC * window,WI_ITEM_REC * item)117 void window_item_set_active(WINDOW_REC *window, WI_ITEM_REC *item)
118 {
119 		WINDOW_REC *old_window;
120 
121         g_return_if_fail(window != NULL);
122 
123         if (item != NULL) {
124             old_window = window_item_window(item);
125         	if (old_window != window) {
126                 /* move item to different window */
127                 window_item_remove_signal(item, FALSE);
128                 window_item_add_signal(window, item, FALSE, FALSE);
129                 signal_emit("window item moved", 3, window, item, old_window);
130         	}
131         }
132 
133 	if (window->active != item) {
134 		window->active = item;
135 		if (item != NULL && window->active_server != item->server)
136 			window_change_server(window, item->server);
137 		signal_emit("window item changed", 2, window, item);
138 	}
139 }
140 
141 /* Return TRUE if `item' is the active window item in the window.
142    `item' can be NULL. */
window_item_is_active(WI_ITEM_REC * item)143 int window_item_is_active(WI_ITEM_REC *item)
144 {
145 	WINDOW_REC *window;
146 
147 	if (item == NULL)
148 		return FALSE;
149 
150 	window = window_item_window(item);
151 	if (window == NULL)
152 		return FALSE;
153 
154 	return window->active == item;
155 }
156 
window_item_prev(WINDOW_REC * window)157 void window_item_prev(WINDOW_REC *window)
158 {
159 	WI_ITEM_REC *last;
160 	GSList *tmp;
161 
162 	g_return_if_fail(window != NULL);
163 
164 	last = NULL;
165 	for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
166 		WI_ITEM_REC *rec = tmp->data;
167 
168 		if (rec != window->active)
169 			last = rec;
170 		else {
171 			/* current channel. did we find anything?
172 			   if not, go to the last channel */
173 			if (last != NULL) break;
174 		}
175 	}
176 
177 	if (last != NULL)
178                 window_item_set_active(window, last);
179 }
180 
window_item_next(WINDOW_REC * window)181 void window_item_next(WINDOW_REC *window)
182 {
183 	WI_ITEM_REC *next;
184 	GSList *tmp;
185 	int gone;
186 
187 	g_return_if_fail(window != NULL);
188 
189 	next = NULL; gone = FALSE;
190 	for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
191 		WI_ITEM_REC *rec = tmp->data;
192 
193 		if (rec == window->active)
194 			gone = TRUE;
195 		else {
196 			if (gone) {
197 				/* found the next channel */
198 				next = rec;
199 				break;
200 			}
201 
202 			if (next == NULL)
203 				next = rec; /* fallback to first channel */
204 		}
205 	}
206 
207 	if (next != NULL)
208                 window_item_set_active(window, next);
209 }
210 
window_item_find_window(WINDOW_REC * window,void * server,const char * name)211 WI_ITEM_REC *window_item_find_window(WINDOW_REC *window,
212                                      void *server, const char *name)
213 {
214 	GSList *tmp;
215 
216 	for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
217 		WI_ITEM_REC *rec = tmp->data;
218 
219 		if ((server == NULL || rec->server == server) &&
220 		    (g_ascii_strcasecmp(name, rec->visible_name) == 0
221 		     || (rec->name && g_ascii_strcasecmp(name, rec->name) == 0)))
222 			return rec;
223 	}
224 	return NULL;
225 }
226 
227 /* Find wanted window item by name. `server' can be NULL. */
window_item_find(void * server,const char * name)228 WI_ITEM_REC *window_item_find(void *server, const char *name)
229 {
230 	WI_ITEM_REC *item;
231 	GSList *tmp;
232 
233 	g_return_val_if_fail(name != NULL, NULL);
234 
235 	for (tmp = windows; tmp != NULL; tmp = tmp->next) {
236 		WINDOW_REC *rec = tmp->data;
237 
238 		item = window_item_find_window(rec, server, name);
239 		if (item != NULL) return item;
240 	}
241 
242 	return NULL;
243 }
244 
window_bind_has_sticky(WINDOW_REC * window)245 static int window_bind_has_sticky(WINDOW_REC *window)
246 {
247 	GSList *tmp;
248 
249 	for (tmp = window->bound_items; tmp != NULL; tmp = tmp->next) {
250 		WINDOW_BIND_REC *rec = tmp->data;
251 
252 		if (rec->sticky)
253                         return TRUE;
254 	}
255 
256         return FALSE;
257 }
258 
window_item_create(WI_ITEM_REC * item,int automatic)259 void window_item_create(WI_ITEM_REC *item, int automatic)
260 {
261 	WINDOW_REC *window;
262         WINDOW_BIND_REC *bind;
263 	GSList *tmp, *sorted;
264 	int clear_waiting, reuse_unused_windows;
265 
266 	g_return_if_fail(item != NULL);
267 
268 	reuse_unused_windows = settings_get_bool("reuse_unused_windows");
269 
270 	clear_waiting = TRUE;
271 	window = NULL;
272         sorted = windows_get_sorted();
273 	for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
274 		WINDOW_REC *rec = tmp->data;
275 
276                 /* is item bound to this window? */
277 		if (item->server != NULL) {
278 			bind = window_bind_find(rec, item->server->tag,
279 						item->visible_name);
280 			if (bind != NULL) {
281                                 if (!bind->sticky)
282 					window_bind_destroy(rec, bind);
283 				window = rec;
284 				clear_waiting = FALSE;
285 				break;
286 			}
287 		}
288 
289 		/* use this window IF:
290 		     - reuse_unused_windows is ON
291 		     - window has no existing items
292 		     - window has no name
293 		     - window has no sticky binds (/LAYOUT SAVEd)
294 		     - we already haven't found "good enough" window,
295 		       except if
296                          - this is the active window
297                          - old window had some temporary bounds and this
298 			   one doesn't
299 		     */
300 		if (reuse_unused_windows && rec->items == NULL &&
301 		    rec->name == NULL && !window_bind_has_sticky(rec) &&
302 		    (window == NULL || rec == active_win ||
303 		     window->bound_items != NULL))
304 			window = rec;
305 	}
306         g_slist_free(sorted);
307 
308         if (window == NULL && !settings_get_bool("autocreate_windows")) {
309                 /* never create new windows automatically */
310                 window = active_win;
311         }
312 
313 	if (window == NULL) {
314 		/* create new window to use */
315 		if (settings_get_bool("autocreate_split_windows")) {
316 			signal_emit("gui window create override", 1,
317 				    GINT_TO_POINTER(MAIN_WINDOW_TYPE_SPLIT));
318 		}
319 		window = window_create(item, automatic);
320 	} else {
321 		/* use existing window */
322 		window_item_add(window, item, automatic);
323 	}
324 
325 	if (clear_waiting)
326                 window_bind_remove_unsticky(window);
327 }
328 
signal_window_item_changed(WINDOW_REC * window,WI_ITEM_REC * item)329 static void signal_window_item_changed(WINDOW_REC *window, WI_ITEM_REC *item)
330 {
331 	g_return_if_fail(window != NULL);
332 
333 	if (g_slist_length(window->items) > 1) {
334 		/* default to printing "talking with ...",
335 		   you can override it it you wish */
336 		printformat(item->server, item->visible_name,
337 			    MSGLEVEL_CLIENTNOTICE,
338 			    TXT_TALKING_WITH, item->visible_name);
339 	}
340 }
341 
window_items_init(void)342 void window_items_init(void)
343 {
344 	settings_add_bool("lookandfeel", "reuse_unused_windows", FALSE);
345 	settings_add_bool("lookandfeel", "autocreate_windows", TRUE);
346 	settings_add_bool("lookandfeel", "autocreate_split_windows", FALSE);
347 	settings_add_bool("lookandfeel", "autofocus_new_items", TRUE);
348 
349 	signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
350 }
351 
window_items_deinit(void)352 void window_items_deinit(void)
353 {
354 	signal_remove("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
355 }
356