Change to the linux kernel coding style
[wmaker-crm.git] / src / switchmenu.c
1 /*
2  *  Window Maker window manager
3  *
4  *  Copyright (c) 1997      Shige Abe
5  *  Copyright (c) 1997-2003 Alfredo K. Kojima
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20  *  USA.
21  */
22
23 #include "wconfig.h"
24
25 #ifndef LITE
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <stdint.h>
31
32 #include <X11/Xlib.h>
33 #include <X11/Xutil.h>
34
35 #include "WindowMaker.h"
36 #include "window.h"
37 #include "actions.h"
38 #include "client.h"
39 #include "funcs.h"
40 #include "stacking.h"
41 #include "workspace.h"
42 #include "framewin.h"
43
44 /********* Global Variables *******/
45 extern WPreferences wPreferences;
46 extern Time LastTimestamp;
47
48 static int initialized = 0;
49
50 static void observer(void *self, WMNotification * notif);
51 static void wsobserver(void *self, WMNotification * notif);
52
53 /*
54  * FocusWindow
55  *
56  *  - Needs to check if already in the right workspace before
57  *    calling wChangeWorkspace?
58  *
59  *  Order:
60  *    Switch to correct workspace
61  *    Unshade if shaded
62  *    If iconified then deiconify else focus/raise.
63  */
64 static void focusWindow(WMenu * menu, WMenuEntry * entry)
65 {
66         WWindow *wwin;
67         WScreen *scr;
68         int x, y, move = 0;
69
70         wwin = (WWindow *) entry->clientdata;
71         scr = wwin->screen_ptr;
72
73         wMakeWindowVisible(wwin);
74
75         x = wwin->frame_x;
76         y = wwin->frame_y;
77
78         /* bring window back to visible area */
79         move = wScreenBringInside(scr, &x, &y, wwin->frame->core->width, wwin->frame->core->height);
80
81         if (move) {
82                 wWindowConfigure(wwin, x, y, wwin->client.width, wwin->client.height);
83         }
84 }
85
86 void InitializeSwitchMenu()
87 {
88         if (!initialized) {
89                 initialized = 1;
90
91                 WMAddNotificationObserver(observer, NULL, WMNManaged, NULL);
92                 WMAddNotificationObserver(observer, NULL, WMNUnmanaged, NULL);
93                 WMAddNotificationObserver(observer, NULL, WMNChangedWorkspace, NULL);
94                 WMAddNotificationObserver(observer, NULL, WMNChangedState, NULL);
95                 WMAddNotificationObserver(observer, NULL, WMNChangedFocus, NULL);
96                 WMAddNotificationObserver(observer, NULL, WMNChangedStacking, NULL);
97                 WMAddNotificationObserver(observer, NULL, WMNChangedName, NULL);
98
99                 WMAddNotificationObserver(wsobserver, NULL, WMNWorkspaceChanged, NULL);
100                 WMAddNotificationObserver(wsobserver, NULL, WMNWorkspaceNameChanged, NULL);
101         }
102 }
103
104 /*
105  *
106  * Open switch menu
107  *
108  */
109 void OpenSwitchMenu(WScreen * scr, int x, int y, int keyboard)
110 {
111         WMenu *switchmenu = scr->switch_menu;
112         WWindow *wwin;
113
114         if (switchmenu) {
115                 if (switchmenu->flags.mapped) {
116                         if (!switchmenu->flags.buttoned) {
117                                 wMenuUnmap(switchmenu);
118                         } else {
119                                 wRaiseFrame(switchmenu->frame->core);
120
121                                 if (keyboard)
122                                         wMenuMapAt(switchmenu, 0, 0, True);
123                                 else
124                                         wMenuMapCopyAt(switchmenu, x - switchmenu->frame->core->width / 2, y);
125                         }
126                 } else {
127                         if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
128                                 y = y - switchmenu->frame->core->height / 2;
129                         }
130                         wMenuMapAt(switchmenu, x - switchmenu->frame->core->width / 2, y, keyboard);
131                 }
132                 return;
133         }
134         switchmenu = wMenuCreate(scr, _("Windows"), True);
135         scr->switch_menu = switchmenu;
136
137         wwin = scr->focused_window;
138         while (wwin) {
139                 UpdateSwitchMenu(scr, wwin, ACTION_ADD);
140
141                 wwin = wwin->prev;
142         }
143
144         if (switchmenu) {
145                 int newx, newy;
146
147                 if (!switchmenu->flags.realized)
148                         wMenuRealize(switchmenu);
149
150                 if (keyboard && x == 0 && y == 0) {
151                         newx = newy = 0;
152                 } else if (keyboard && x == scr->scr_width / 2 && y == scr->scr_height / 2) {
153                         newx = x - switchmenu->frame->core->width / 2;
154                         newy = y - switchmenu->frame->core->height / 2;
155                 } else {
156                         newx = x - switchmenu->frame->core->width / 2;
157                         newy = y;
158                 }
159                 wMenuMapAt(switchmenu, newx, newy, keyboard);
160         }
161 }
162
163 static int menuIndexForWindow(WMenu * menu, WWindow * wwin, int old_pos)
164 {
165         int idx;
166
167         if (menu->entry_no <= old_pos)
168                 return -1;
169
170 #define WS(i)  ((WWindow*)menu->entries[i]->clientdata)->frame->workspace
171         if (old_pos >= 0) {
172                 if (WS(old_pos) >= wwin->frame->workspace
173                     && (old_pos == 0 || WS(old_pos - 1) <= wwin->frame->workspace)) {
174                         return old_pos;
175                 }
176         }
177 #undef WS
178
179         for (idx = 0; idx < menu->entry_no; idx++) {
180                 WWindow *tw = (WWindow *) menu->entries[idx]->clientdata;
181
182                 if (!IS_OMNIPRESENT(tw)
183                     && tw->frame->workspace > wwin->frame->workspace) {
184                         break;
185                 }
186         }
187
188         return idx;
189 }
190
191 /*
192  * Update switch menu
193  */
194 void UpdateSwitchMenu(WScreen * scr, WWindow * wwin, int action)
195 {
196         WMenu *switchmenu = scr->switch_menu;
197         WMenuEntry *entry;
198         char title[MAX_MENU_TEXT_LENGTH + 6];
199         int len = sizeof(title);
200         int i;
201         int checkVisibility = 0;
202
203         if (!wwin->screen_ptr->switch_menu)
204                 return;
205         /*
206          *  This menu is updated under the following conditions:
207          *
208          *    1.  When a window is created.
209          *    2.  When a window is destroyed.
210          *
211          *    3.  When a window changes it's title.
212          *    4.  When a window changes its workspace.
213          */
214         if (action == ACTION_ADD) {
215                 char *t;
216                 int idx;
217
218                 if (wwin->flags.internal_window || WFLAGP(wwin, skip_window_list) || IS_GNUSTEP_MENU(wwin)) {
219                         return;
220                 }
221
222                 if (wwin->frame->title)
223                         snprintf(title, len, "%s", wwin->frame->title);
224                 else
225                         snprintf(title, len, "%s", DEF_WINDOW_TITLE);
226                 t = ShrinkString(scr->menu_entry_font, title, MAX_WINDOWLIST_WIDTH);
227
228                 if (IS_OMNIPRESENT(wwin))
229                         idx = -1;
230                 else {
231                         idx = menuIndexForWindow(switchmenu, wwin, -1);
232                 }
233
234                 entry = wMenuInsertCallback(switchmenu, idx, t, focusWindow, wwin);
235                 wfree(t);
236
237                 entry->flags.indicator = 1;
238                 entry->rtext = wmalloc(MAX_WORKSPACENAME_WIDTH + 8);
239                 if (IS_OMNIPRESENT(wwin))
240                         snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH, "[*]");
241                 else
242                         snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH, "[%s]",
243                                  scr->workspaces[wwin->frame->workspace]->name);
244
245                 if (wwin->flags.hidden) {
246                         entry->flags.indicator_type = MI_HIDDEN;
247                         entry->flags.indicator_on = 1;
248                 } else if (wwin->flags.miniaturized) {
249                         entry->flags.indicator_type = MI_MINIWINDOW;
250                         entry->flags.indicator_on = 1;
251                 } else if (wwin->flags.focused) {
252                         entry->flags.indicator_type = MI_DIAMOND;
253                         entry->flags.indicator_on = 1;
254                 } else if (wwin->flags.shaded) {
255                         entry->flags.indicator_type = MI_SHADED;
256                         entry->flags.indicator_on = 1;
257                 }
258
259                 wMenuRealize(switchmenu);
260                 checkVisibility = 1;
261         } else {
262                 char *t;
263                 for (i = 0; i < switchmenu->entry_no; i++) {
264                         entry = switchmenu->entries[i];
265                         /* this is the entry that was changed */
266                         if (entry->clientdata == wwin) {
267                                 switch (action) {
268                                 case ACTION_REMOVE:
269                                         wMenuRemoveItem(switchmenu, i);
270                                         wMenuRealize(switchmenu);
271                                         checkVisibility = 1;
272                                         break;
273
274                                 case ACTION_CHANGE:
275                                         if (entry->text)
276                                                 wfree(entry->text);
277
278                                         if (wwin->frame->title)
279                                                 snprintf(title, MAX_MENU_TEXT_LENGTH, "%s", wwin->frame->title);
280                                         else
281                                                 snprintf(title, MAX_MENU_TEXT_LENGTH, "%s", DEF_WINDOW_TITLE);
282
283                                         t = ShrinkString(scr->menu_entry_font, title, MAX_WINDOWLIST_WIDTH);
284                                         entry->text = t;
285
286                                         wMenuRealize(switchmenu);
287                                         checkVisibility = 1;
288                                         break;
289
290                                 case ACTION_CHANGE_WORKSPACE:
291                                         if (entry->rtext) {
292                                                 int idx = -1;
293                                                 char *t, *rt;
294                                                 int it, ion;
295
296                                                 if (IS_OMNIPRESENT(wwin)) {
297                                                         snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH, "[*]");
298                                                 } else {
299                                                         snprintf(entry->rtext, MAX_WORKSPACENAME_WIDTH,
300                                                                  "[%s]",
301                                                                  scr->workspaces[wwin->frame->workspace]->name);
302                                                 }
303
304                                                 rt = entry->rtext;
305                                                 entry->rtext = NULL;
306                                                 t = entry->text;
307                                                 entry->text = NULL;
308
309                                                 it = entry->flags.indicator_type;
310                                                 ion = entry->flags.indicator_on;
311
312                                                 if (!IS_OMNIPRESENT(wwin) && idx < 0) {
313                                                         idx = menuIndexForWindow(switchmenu, wwin, i);
314                                                 }
315
316                                                 wMenuRemoveItem(switchmenu, i);
317
318                                                 entry = wMenuInsertCallback(switchmenu, idx, t, focusWindow, wwin);
319                                                 wfree(t);
320                                                 entry->rtext = rt;
321                                                 entry->flags.indicator = 1;
322                                                 entry->flags.indicator_type = it;
323                                                 entry->flags.indicator_on = ion;
324                                         }
325                                         wMenuRealize(switchmenu);
326                                         checkVisibility = 1;
327                                         break;
328
329                                 case ACTION_CHANGE_STATE:
330                                         if (wwin->flags.hidden) {
331                                                 entry->flags.indicator_type = MI_HIDDEN;
332                                                 entry->flags.indicator_on = 1;
333                                         } else if (wwin->flags.miniaturized) {
334                                                 entry->flags.indicator_type = MI_MINIWINDOW;
335                                                 entry->flags.indicator_on = 1;
336                                         } else if (wwin->flags.shaded && !wwin->flags.focused) {
337                                                 entry->flags.indicator_type = MI_SHADED;
338                                                 entry->flags.indicator_on = 1;
339                                         } else {
340                                                 entry->flags.indicator_on = wwin->flags.focused;
341                                                 entry->flags.indicator_type = MI_DIAMOND;
342                                         }
343                                         break;
344                                 }
345                                 break;
346                         }
347                 }
348         }
349         if (checkVisibility) {
350                 int tmp;
351
352                 tmp = switchmenu->frame->top_width + 5;
353                 /* if menu got unreachable, bring it to a visible place */
354                 if (switchmenu->frame_x < tmp - (int)switchmenu->frame->core->width) {
355                         wMenuMove(switchmenu, tmp - (int)switchmenu->frame->core->width,
356                                   switchmenu->frame_y, False);
357                 }
358         }
359         wMenuPaint(switchmenu);
360 }
361
362 void UpdateSwitchMenuWorkspace(WScreen * scr, int workspace)
363 {
364         WMenu *menu = scr->switch_menu;
365         int i;
366         WWindow *wwin;
367
368         if (!menu)
369                 return;
370
371         for (i = 0; i < menu->entry_no; i++) {
372                 wwin = (WWindow *) menu->entries[i]->clientdata;
373
374                 if (wwin->frame->workspace == workspace && !IS_OMNIPRESENT(wwin)) {
375                         if (IS_OMNIPRESENT(wwin))
376                                 snprintf(menu->entries[i]->rtext, MAX_WORKSPACENAME_WIDTH, "[*]");
377                         else
378                                 snprintf(menu->entries[i]->rtext, MAX_WORKSPACENAME_WIDTH, "[%s]",
379                                          scr->workspaces[wwin->frame->workspace]->name);
380                         menu->flags.realized = 0;
381                 }
382         }
383         if (!menu->flags.realized)
384                 wMenuRealize(menu);
385 }
386
387 static void observer(void *self, WMNotification * notif)
388 {
389         WWindow *wwin = (WWindow *) WMGetNotificationObject(notif);
390         const char *name = WMGetNotificationName(notif);
391         void *data = WMGetNotificationClientData(notif);
392
393         if (!wwin)
394                 return;
395
396         if (strcmp(name, WMNManaged) == 0)
397                 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_ADD);
398         else if (strcmp(name, WMNUnmanaged) == 0)
399                 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_REMOVE);
400         else if (strcmp(name, WMNChangedWorkspace) == 0)
401                 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_WORKSPACE);
402         else if (strcmp(name, WMNChangedFocus) == 0)
403                 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_STATE);
404         else if (strcmp(name, WMNChangedName) == 0)
405                 UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE);
406         else if (strcmp(name, WMNChangedState) == 0) {
407                 if (strcmp((char *)data, "omnipresent") == 0) {
408                         UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_WORKSPACE);
409                 } else {
410                         UpdateSwitchMenu(wwin->screen_ptr, wwin, ACTION_CHANGE_STATE);
411                 }
412         }
413 }
414
415 static void wsobserver(void *self, WMNotification * notif)
416 {
417         WScreen *scr = (WScreen *) WMGetNotificationObject(notif);
418         const char *name = WMGetNotificationName(notif);
419         void *data = WMGetNotificationClientData(notif);
420
421         if (strcmp(name, WMNWorkspaceNameChanged) == 0) {
422                 UpdateSwitchMenuWorkspace(scr, (int)(uintptr_t) data);
423         } else if (strcmp(name, WMNWorkspaceChanged) == 0) {
424
425         }
426 }
427
428 #endif                          /* !LITE */