Merge Irssi 0.8.16-rc1
[silc.git] / apps / irssi / src / fe-common / core / window-items.c
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
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
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
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
93 void window_item_remove(WI_ITEM_REC *item)
94 {
95         window_item_remove_signal(item, TRUE);
96 }
97
98 void window_item_destroy(WI_ITEM_REC *item)
99 {
100         window_item_remove(item);
101         item->destroy(item);
102 }
103
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
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. */
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
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
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
211 WI_ITEM_REC *window_item_find_window(WINDOW_REC *window,
212                                      void *server, const char *name)
213 {
214         CHANNEL_REC *channel;
215         GSList *tmp;
216
217         for (tmp = window->items; tmp != NULL; tmp = tmp->next) {
218                 WI_ITEM_REC *rec = tmp->data;
219
220                 if ((server == NULL || rec->server == server) &&
221                     g_strcasecmp(name, rec->visible_name) == 0)
222                         return rec;
223         }
224
225         /* try with channel name too, it's not necessarily
226            same as visible_name (!channels) */
227         channel = channel_find(server, name);
228         if (channel != NULL && window_item_window(channel) == window)
229                 return (WI_ITEM_REC *) channel;
230
231         return NULL;
232 }
233
234 /* Find wanted window item by name. `server' can be NULL. */
235 WI_ITEM_REC *window_item_find(void *server, const char *name)
236 {
237         WI_ITEM_REC *item;
238         GSList *tmp;
239
240         g_return_val_if_fail(name != NULL, NULL);
241
242         for (tmp = windows; tmp != NULL; tmp = tmp->next) {
243                 WINDOW_REC *rec = tmp->data;
244
245                 item = window_item_find_window(rec, server, name);
246                 if (item != NULL) return item;
247         }
248
249         return NULL;
250 }
251
252 static int window_bind_has_sticky(WINDOW_REC *window)
253 {
254         GSList *tmp;
255
256         for (tmp = window->bound_items; tmp != NULL; tmp = tmp->next) {
257                 WINDOW_BIND_REC *rec = tmp->data;
258
259                 if (rec->sticky)
260                         return TRUE;
261         }
262
263         return FALSE;
264 }
265
266 void window_item_create(WI_ITEM_REC *item, int automatic)
267 {
268         WINDOW_REC *window;
269         WINDOW_BIND_REC *bind;
270         GSList *tmp, *sorted;
271         int clear_waiting, reuse_unused_windows;
272
273         g_return_if_fail(item != NULL);
274
275         reuse_unused_windows = settings_get_bool("reuse_unused_windows");
276
277         clear_waiting = TRUE;
278         window = NULL;
279         sorted = windows_get_sorted();
280         for (tmp = sorted; tmp != NULL; tmp = tmp->next) {
281                 WINDOW_REC *rec = tmp->data;
282
283                 /* is item bound to this window? */
284                 if (item->server != NULL) {
285                         bind = window_bind_find(rec, item->server->tag,
286                                                 item->visible_name);
287                         if (bind != NULL) {
288                                 if (!bind->sticky)
289                                         window_bind_destroy(rec, bind);
290                                 window = rec;
291                                 clear_waiting = FALSE;
292                                 break;
293                         }
294                 }
295
296                 /* use this window IF:
297                      - reuse_unused_windows is ON
298                      - window has no existing items
299                      - window has no name
300                      - window has no sticky binds (/LAYOUT SAVEd)
301                      - we already haven't found "good enough" window,
302                        except if
303                          - this is the active window
304                          - old window had some temporary bounds and this
305                            one doesn't
306                      */
307                 if (reuse_unused_windows && rec->items == NULL &&
308                     rec->name == NULL && !window_bind_has_sticky(rec) &&
309                     (window == NULL || rec == active_win ||
310                      window->bound_items != NULL))
311                         window = rec;
312         }
313         g_slist_free(sorted);
314
315         if (window == NULL && !settings_get_bool("autocreate_windows")) {
316                 /* never create new windows automatically */
317                 window = active_win;
318         }
319
320         if (window == NULL) {
321                 /* create new window to use */
322                 if (settings_get_bool("autocreate_split_windows")) {
323                         signal_emit("gui window create override", 1,
324                                     GINT_TO_POINTER(0));
325                 }
326                 window = window_create(item, automatic);
327         } else {
328                 /* use existing window */
329                 window_item_add(window, item, automatic);
330         }
331
332         if (clear_waiting)
333                 window_bind_remove_unsticky(window);
334 }
335
336 static void signal_window_item_changed(WINDOW_REC *window, WI_ITEM_REC *item)
337 {
338         g_return_if_fail(window != NULL);
339
340         if (g_slist_length(window->items) > 1) {
341                 /* default to printing "talking with ...",
342                    you can override it it you wish */
343                 printformat(item->server, item->visible_name,
344                             MSGLEVEL_CLIENTNOTICE,
345                             TXT_TALKING_WITH, item->visible_name);
346         }
347 }
348
349 void window_items_init(void)
350 {
351         settings_add_bool("lookandfeel", "reuse_unused_windows", FALSE);
352         settings_add_bool("lookandfeel", "autocreate_windows", TRUE);
353         settings_add_bool("lookandfeel", "autocreate_split_windows", FALSE);
354         settings_add_bool("lookandfeel", "autofocus_new_items", TRUE);
355
356         signal_add_last("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
357 }
358
359 void window_items_deinit(void)
360 {
361         signal_remove("window item changed", (SIGNAL_FUNC) signal_window_item_changed);
362 }