Mac OS X-style window cycling.
[wmaker-crm.git] / src / switchpanel.c
1 /*
2 * Window Maker window manager
3 *
4 * Copyright (c) 1997-2004 Alfredo K. Kojima
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
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 * USA.
20 */
21
22 #include "wconfig.h"
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/time.h>
27
28 #include "WindowMaker.h"
29 #include "screen.h"
30 #include "wcore.h"
31 #include "framewin.h"
32 #include "window.h"
33 #include "defaults.h"
34 #include "switchpanel.h"
35 #include "funcs.h"
36 #include "xinerama.h"
37 #include "window.h"
38
39 extern Atom _XA_WM_IGNORE_FOCUS_EVENTS;
40
41 #ifdef SHAPE
42 #include <X11/extensions/shape.h>
43
44 extern Bool wShapeSupported;
45 #endif
46
47 struct SwitchPanel {
48 WScreen *scr;
49 WMWindow *win;
50 WMFrame *iconBox;
51
52 WMArray *icons;
53 WMArray *images;
54 WMArray *windows;
55 RImage *bg;
56 int current;
57 int firstVisible;
58 int visibleCount;
59
60 WMLabel *label;
61
62 RImage *defIcon;
63
64 RImage *tileTmp;
65 RImage *tile;
66
67 WMFont *font;
68 WMColor *white;
69 };
70
71 extern WPreferences wPreferences;
72
73 #define BORDER_SPACE 10
74 #define ICON_SIZE 48
75 #define ICON_TILE_SIZE 64
76 #define LABEL_HEIGHT 25
77 #define SCREEN_BORDER_SPACING 2*20
78 #define SCROLL_STEPS (ICON_TILE_SIZE/2)
79
80 static int canReceiveFocus(WWindow * wwin)
81 {
82 if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
83 return 0;
84 if (!wwin->flags.mapped) {
85 if (!wwin->flags.shaded && !wwin->flags.miniaturized && !wwin->flags.hidden)
86 return 0;
87 else
88 return -1;
89 }
90 if (WFLAGP(wwin, no_focusable))
91 return 0;
92 return 1;
93 }
94
95 static void changeImage(WSwitchPanel * panel, int idecks, int selected)
96 {
97 WMFrame *icon = WMGetFromArray(panel->icons, idecks);
98 RImage *image = WMGetFromArray(panel->images, idecks);
99
100 if (!panel->bg && !panel->tile) {
101 if (!selected)
102 WMSetFrameRelief(icon, WRFlat);
103 }
104
105 if (image && icon) {
106 RImage *back;
107 int opaq = 255;
108 RImage *tile;
109 WMPoint pos;
110 Pixmap p;
111
112 if (canReceiveFocus(WMGetFromArray(panel->windows, idecks)) < 0)
113 opaq = 50;
114
115 pos = WMGetViewPosition(WMWidgetView(icon));
116 back = panel->tileTmp;
117 if (panel->bg)
118 RCopyArea(back, panel->bg,
119 BORDER_SPACE + pos.x - panel->firstVisible * ICON_TILE_SIZE,
120 BORDER_SPACE + pos.y, back->width, back->height, 0, 0);
121 else {
122 RColor color;
123 WMScreen *wscr = WMWidgetScreen(icon);
124 color.red = 255;
125 color.red = WMRedComponentOfColor(WMGrayColor(wscr)) >> 8;
126 color.green = WMGreenComponentOfColor(WMGrayColor(wscr)) >> 8;
127 color.blue = WMBlueComponentOfColor(WMGrayColor(wscr)) >> 8;
128 RFillImage(back, &color);
129 }
130 if (selected) {
131 tile = panel->tile;
132 RCombineArea(back, tile, 0, 0, tile->width, tile->height,
133 (back->width - tile->width) / 2, (back->height - tile->height) / 2);
134 }
135 RCombineAreaWithOpaqueness(back, image, 0, 0, image->width, image->height,
136 (back->width - image->width) / 2, (back->height - image->height) / 2,
137 opaq);
138
139 RConvertImage(panel->scr->rcontext, back, &p);
140 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(icon), p);
141 XClearWindow(dpy, WMWidgetXID(icon));
142 XFreePixmap(dpy, p);
143 }
144
145 if (!panel->bg && !panel->tile) {
146 if (selected)
147 WMSetFrameRelief(icon, WRSimple);
148 }
149 }
150
151 static RImage *scaleDownIfNeeded(RImage * image)
152 {
153 if (image && ((image->width - ICON_SIZE) > 2 || (image->height - ICON_SIZE) > 2)) {
154 RImage *nimage;
155 nimage = RScaleImage(image, ICON_SIZE, (image->height * ICON_SIZE / image->width));
156 RReleaseImage(image);
157 image = nimage;
158 }
159 return image;
160 }
161
162 static void addIconForWindow(WSwitchPanel * panel, WMWidget * parent, WWindow * wwin, int x, int y)
163 {
164 WMFrame *icon = WMCreateFrame(parent);
165 RImage *image = NULL;
166
167 WMSetFrameRelief(icon, WRFlat);
168 WMResizeWidget(icon, ICON_TILE_SIZE, ICON_TILE_SIZE);
169 WMMoveWidget(icon, x, y);
170
171 if (!WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
172 image = RRetainImage(wwin->net_icon_image);
173
174 // Make this use a caching thing. When there are many windows,
175 // it's very likely that most of them are instances of the same thing,
176 // so caching them should get performance acceptable in these cases.
177 if (!image)
178 image = wDefaultGetImage(panel->scr, wwin->wm_instance, wwin->wm_class);
179
180 if (!image && !panel->defIcon) {
181 char *file = wDefaultGetIconFile(panel->scr, NULL, NULL, False);
182 if (file) {
183 char *path = FindImage(wPreferences.icon_path, file);
184 if (path) {
185 image = RLoadImage(panel->scr->rcontext, path, 0);
186 wfree(path);
187 }
188 }
189 if (image)
190 panel->defIcon = scaleDownIfNeeded(image);
191 image = NULL;
192 }
193 if (!image && panel->defIcon)
194 image = RRetainImage(panel->defIcon);
195
196 image = scaleDownIfNeeded(image);
197
198 WMAddToArray(panel->images, image);
199 WMAddToArray(panel->icons, icon);
200 }
201
202 static void scrollIcons(WSwitchPanel * panel, int delta)
203 {
204 int nfirst = panel->firstVisible + delta;
205 int i;
206 int count = WMGetArrayItemCount(panel->windows);
207 // int nx, ox;
208 // struct timeval tv1, tv2;
209
210 if (count <= panel->visibleCount)
211 return;
212
213 if (nfirst < 0)
214 nfirst = 0;
215 else if (nfirst >= count - panel->visibleCount)
216 nfirst = count - panel->visibleCount;
217
218 if (nfirst == panel->firstVisible)
219 return;
220 /*
221 ox = -panel->firstVisible * ICON_TILE_SIZE;
222 nx = -nfirst * ICON_TILE_SIZE;
223 for (i= 0; i < SCROLL_STEPS; i++) {
224 unsigned int diff;
225 gettimeofday(&tv1, NULL);
226 WMMoveWidget(panel->iconBox, (nx*i + ox*(SCROLL_STEPS-i))/(SCROLL_STEPS-1), 0);
227 XSync(dpy, False);
228 gettimeofday(&tv2, NULL);
229 diff = (tv2.tv_sec-tv1.tv_sec)*10000+(tv2.tv_usec-tv1.tv_usec)/100;
230 if (diff < 200)
231 wusleep(300-diff);
232 }
233 */
234 WMMoveWidget(panel->iconBox, -nfirst * ICON_TILE_SIZE, 0);
235
236 panel->firstVisible = nfirst;
237
238 for (i = panel->firstVisible; i < panel->firstVisible + panel->visibleCount; i++) {
239 changeImage(panel, i, i == panel->current);
240 }
241 }
242
243 /*
244 * 0 1 2
245 * 3 4 5
246 * 6 7 8
247 */
248 static RImage *assemblePuzzleImage(RImage ** images, int width, int height)
249 {
250 RImage *img = RCreateImage(width, height, 1);
251 RImage *tmp;
252 int tw, th;
253 RColor color;
254 if (!img)
255 return NULL;
256
257 color.red = 0;
258 color.green = 0;
259 color.blue = 0;
260 color.alpha = 255;
261
262 RFillImage(img, &color);
263
264 tw = width - images[0]->width - images[2]->width;
265 th = height - images[0]->height - images[6]->height;
266
267 if (tw <= 0 || th <= 0) {
268 //XXX
269 return NULL;
270 }
271
272 /* top */
273 if (tw > 0) {
274 tmp = RSmoothScaleImage(images[1], tw, images[1]->height);
275 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, 0);
276 RReleaseImage(tmp);
277 }
278 /* bottom */
279 if (tw > 0) {
280 tmp = RSmoothScaleImage(images[7], tw, images[7]->height);
281 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[6]->width, height - images[6]->height);
282 RReleaseImage(tmp);
283 }
284 /* left */
285 if (th > 0) {
286 tmp = RSmoothScaleImage(images[3], images[3]->width, th);
287 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, 0, images[0]->height);
288 RReleaseImage(tmp);
289 }
290 /* right */
291 if (th > 0) {
292 tmp = RSmoothScaleImage(images[5], images[5]->width, th);
293 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, width - images[5]->width, images[2]->height);
294 RReleaseImage(tmp);
295 }
296 /* center */
297 if (tw > 0 && th > 0) {
298 tmp = RSmoothScaleImage(images[4], tw, th);
299 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, images[0]->height);
300 RReleaseImage(tmp);
301 }
302
303 /* corners */
304 RCopyArea(img, images[0], 0, 0, images[0]->width, images[0]->height, 0, 0);
305
306 RCopyArea(img, images[2], 0, 0, images[2]->width, images[2]->height, width - images[2]->width, 0);
307
308 RCopyArea(img, images[6], 0, 0, images[6]->width, images[6]->height, 0, height - images[6]->height);
309
310 RCopyArea(img, images[8], 0, 0, images[8]->width, images[8]->height,
311 width - images[8]->width, height - images[8]->height);
312
313 return img;
314 }
315
316 static RImage *createBackImage(WScreen * scr, int width, int height)
317 {
318 return assemblePuzzleImage(wPreferences.swbackImage, width, height);
319 }
320
321 static RImage *getTile(WSwitchPanel * panel)
322 {
323 RImage *stile;
324
325 if (!wPreferences.swtileImage)
326 return NULL;
327
328 stile = RScaleImage(wPreferences.swtileImage, ICON_TILE_SIZE, ICON_TILE_SIZE);
329 if (!stile)
330 return wPreferences.swtileImage;
331
332 return stile;
333 }
334
335 static void drawTitle(WSwitchPanel * panel, int idecks, char *title)
336 {
337 char *ntitle;
338 int width = WMWidgetWidth(panel->win);
339 int x;
340
341 if (title)
342 ntitle = ShrinkString(panel->font, title, width - 2 * BORDER_SPACE);
343 else
344 ntitle = NULL;
345
346 if (panel->bg) {
347 if (ntitle) {
348 if (strcmp(ntitle, title) != 0)
349 x = BORDER_SPACE;
350 else {
351 int w = WMWidthOfString(panel->font, ntitle, strlen(ntitle));
352
353 x = BORDER_SPACE + (idecks - panel->firstVisible) * ICON_TILE_SIZE +
354 ICON_TILE_SIZE / 2 - w / 2;
355 if (x < BORDER_SPACE)
356 x = BORDER_SPACE;
357 else if (x + w > width - BORDER_SPACE)
358 x = width - BORDER_SPACE - w;
359 }
360 }
361 XClearWindow(dpy, WMWidgetXID(panel->win));
362 if (ntitle)
363 WMDrawString(panel->scr->wmscreen,
364 WMWidgetXID(panel->win),
365 panel->white, panel->font,
366 x,
367 WMWidgetHeight(panel->win) - BORDER_SPACE - LABEL_HEIGHT +
368 WMFontHeight(panel->font) / 2, ntitle, strlen(ntitle));
369 } else {
370 if (ntitle)
371 WMSetLabelText(panel->label, ntitle);
372 }
373 if (ntitle)
374 free(ntitle);
375 }
376
377 static WMArray *makeWindowListArray(WScreen * scr, WWindow * curwin, int workspace, int include_unmapped, Bool class_only)
378 {
379 WMArray *windows = WMCreateArray(10);
380 int fl;
381 WWindow *wwin;
382
383 for (fl = 0; fl < 2; fl++) {
384 for (wwin = curwin; wwin; wwin = wwin->prev) {
385 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
386 (!WFLAGP(wwin, skip_window_list) || wwin->flags.internal_window) &&
387 (wwin->flags.mapped || include_unmapped)) {
388 if (class_only) {
389 if (!wwin->wm_class || !curwin->wm_class)
390 continue;
391 if (strcmp(wwin->wm_class, curwin->wm_class))
392 continue;
393 }
394 WMAddToArray(windows, wwin);
395 }
396 }
397 wwin = curwin;
398 /* start over from the beginning of the list */
399 while (wwin->next)
400 wwin = wwin->next;
401
402 for (wwin = curwin; wwin && wwin != curwin; wwin = wwin->prev) {
403 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
404 (!WFLAGP(wwin, skip_window_list) || wwin->flags.internal_window) &&
405 (wwin->flags.mapped || include_unmapped)) {
406 if (class_only) {
407 if (!wwin->wm_class || !curwin->wm_class)
408 continue;
409 if (strcmp(wwin->wm_class, curwin->wm_class))
410 continue;
411 }
412 WMAddToArray(windows, wwin);
413 }
414 }
415 }
416
417 return windows;
418 }
419
420 WSwitchPanel *wInitSwitchPanel(WScreen * scr, WWindow * curwin, int workspace, Bool class_only)
421 {
422 WWindow *wwin;
423 WSwitchPanel *panel = wmalloc(sizeof(WSwitchPanel));
424 WMFrame *viewport;
425 int i;
426 int width, height;
427 int iconsThatFitCount;
428 int count;
429 WMRect rect;
430 rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
431
432 memset(panel, 0, sizeof(WSwitchPanel));
433
434 panel->scr = scr;
435
436 panel->windows = makeWindowListArray(scr, curwin, workspace, wPreferences.swtileImage != 0, class_only);
437 count = WMGetArrayItemCount(panel->windows);
438
439 if (count == 0) {
440 WMFreeArray(panel->windows);
441 wfree(panel);
442 return NULL;
443 }
444
445 width = ICON_TILE_SIZE * count;
446 iconsThatFitCount = count;
447
448 if (width > rect.size.width) {
449 iconsThatFitCount = (rect.size.width - SCREEN_BORDER_SPACING) / ICON_TILE_SIZE;
450 width = iconsThatFitCount * ICON_TILE_SIZE;
451 }
452
453 panel->visibleCount = iconsThatFitCount;
454
455 if (!wPreferences.swtileImage)
456 return panel;
457
458 height = LABEL_HEIGHT + ICON_TILE_SIZE;
459
460 panel->tileTmp = RCreateImage(ICON_TILE_SIZE, ICON_TILE_SIZE, 1);
461 panel->tile = getTile(panel);
462 if (panel->tile && wPreferences.swbackImage[8]) {
463 panel->bg = createBackImage(scr, width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
464 }
465 if (!panel->tileTmp || !panel->tile) {
466 if (panel->bg)
467 RReleaseImage(panel->bg);
468 panel->bg = NULL;
469 if (panel->tile)
470 RReleaseImage(panel->tile);
471 panel->tile = NULL;
472 if (panel->tileTmp)
473 RReleaseImage(panel->tileTmp);
474 panel->tileTmp = NULL;
475 }
476
477 panel->white = WMWhiteColor(scr->wmscreen);
478 panel->font = WMBoldSystemFontOfSize(scr->wmscreen, 12);
479 panel->icons = WMCreateArray(count);
480 panel->images = WMCreateArray(count);
481
482 panel->win = WMCreateWindow(scr->wmscreen, "");
483
484 if (!panel->bg) {
485 WMFrame *frame = WMCreateFrame(panel->win);
486 WMColor *darkGray = WMDarkGrayColor(scr->wmscreen);
487 WMSetFrameRelief(frame, WRSimple);
488 WMSetViewExpandsToParent(WMWidgetView(frame), 0, 0, 0, 0);
489
490 panel->label = WMCreateLabel(panel->win);
491 WMResizeWidget(panel->label, width, LABEL_HEIGHT);
492 WMMoveWidget(panel->label, BORDER_SPACE, BORDER_SPACE + ICON_TILE_SIZE + 5);
493 WMSetLabelRelief(panel->label, WRSimple);
494 WMSetWidgetBackgroundColor(panel->label, darkGray);
495 WMSetLabelFont(panel->label, panel->font);
496 WMSetLabelTextColor(panel->label, panel->white);
497
498 WMReleaseColor(darkGray);
499 height += 5;
500 }
501
502 WMResizeWidget(panel->win, width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
503
504 viewport = WMCreateFrame(panel->win);
505 WMResizeWidget(viewport, width, ICON_TILE_SIZE);
506 WMMoveWidget(viewport, BORDER_SPACE, BORDER_SPACE);
507 WMSetFrameRelief(viewport, WRFlat);
508
509 panel->iconBox = WMCreateFrame(viewport);
510 WMMoveWidget(panel->iconBox, 0, 0);
511 WMResizeWidget(panel->iconBox, ICON_TILE_SIZE * count, ICON_TILE_SIZE);
512 WMSetFrameRelief(panel->iconBox, WRFlat);
513
514 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
515 addIconForWindow(panel, panel->iconBox, wwin, i * ICON_TILE_SIZE, 0);
516 }
517
518 WMMapSubwidgets(panel->win);
519 WMRealizeWidget(panel->win);
520
521 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
522 changeImage(panel, i, 0);
523 }
524
525 if (panel->bg) {
526 Pixmap pixmap, mask;
527
528 RConvertImageMask(scr->rcontext, panel->bg, &pixmap, &mask, 250);
529
530 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(panel->win), pixmap);
531
532 #ifdef SHAPE
533 if (mask && wShapeSupported)
534 XShapeCombineMask(dpy, WMWidgetXID(panel->win), ShapeBounding, 0, 0, mask, ShapeSet);
535 #endif
536
537 if (pixmap)
538 XFreePixmap(dpy, pixmap);
539 if (mask)
540 XFreePixmap(dpy, mask);
541 }
542
543 {
544 WMPoint center;
545 center = wGetPointToCenterRectInHead(scr, wGetHeadForPointerLocation(scr),
546 width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
547 WMMoveWidget(panel->win, center.x, center.y);
548 }
549
550 panel->current = WMGetFirstInArray(panel->windows, curwin);
551 if (panel->current >= 0)
552 changeImage(panel, panel->current, 1);
553
554 WMMapWidget(panel->win);
555
556 return panel;
557 }
558
559 void wSwitchPanelDestroy(WSwitchPanel * panel)
560 {
561 int i;
562 RImage *image;
563
564 if (panel->win) {
565 Window info_win = panel->scr->info_window;
566 XEvent ev;
567 ev.xclient.type = ClientMessage;
568 ev.xclient.message_type = _XA_WM_IGNORE_FOCUS_EVENTS;
569 ev.xclient.format = 32;
570 ev.xclient.data.l[0] = True;
571
572 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
573 WMUnmapWidget(panel->win);
574
575 ev.xclient.data.l[0] = False;
576 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
577 }
578
579 if (panel->images) {
580 WM_ITERATE_ARRAY(panel->images, image, i) {
581 if (image)
582 RReleaseImage(image);
583 }
584 WMFreeArray(panel->images);
585 }
586 if (panel->win)
587 WMDestroyWidget(panel->win);
588 if (panel->icons)
589 WMFreeArray(panel->icons);
590 WMFreeArray(panel->windows);
591 if (panel->defIcon)
592 RReleaseImage(panel->defIcon);
593 if (panel->tile)
594 RReleaseImage(panel->tile);
595 if (panel->tileTmp)
596 RReleaseImage(panel->tileTmp);
597 if (panel->bg)
598 RReleaseImage(panel->bg);
599 if (panel->font)
600 WMReleaseFont(panel->font);
601 if (panel->white)
602 WMReleaseColor(panel->white);
603 wfree(panel);
604 }
605
606 WWindow *wSwitchPanelSelectNext(WSwitchPanel * panel, int back)
607 {
608 WWindow *wwin;
609 int count = WMGetArrayItemCount(panel->windows);
610
611 if (count == 0)
612 return NULL;
613
614 if (panel->win)
615 changeImage(panel, panel->current, 0);
616
617 if (back)
618 panel->current--;
619 else
620 panel->current++;
621
622 wwin = WMGetFromArray(panel->windows, (count + panel->current) % count);
623
624 if (back) {
625 if (panel->current < 0)
626 scrollIcons(panel, count);
627 else if (panel->current < panel->firstVisible)
628 scrollIcons(panel, -1);
629 } else {
630 if (panel->current >= count)
631 scrollIcons(panel, -count);
632 else if (panel->current - panel->firstVisible >= panel->visibleCount)
633 scrollIcons(panel, 1);
634 }
635
636 panel->current = (count + panel->current) % count;
637
638 if (panel->win) {
639 drawTitle(panel, panel->current, wwin->frame->title);
640
641 changeImage(panel, panel->current, 1);
642 }
643
644 return wwin;
645 }
646
647 WWindow *wSwitchPanelSelectFirst(WSwitchPanel * panel, int back)
648 {
649 WWindow *wwin;
650 int count = WMGetArrayItemCount(panel->windows);
651
652 if (count == 0)
653 return NULL;
654
655 if (panel->win)
656 changeImage(panel, panel->current, 0);
657
658 if (back) {
659 panel->current = count - 1;
660 scrollIcons(panel, count);
661 } else {
662 panel->current = 0;
663 scrollIcons(panel, -count);
664 }
665
666 wwin = WMGetFromArray(panel->windows, panel->current);
667
668 if (panel->win) {
669 drawTitle(panel, panel->current, wwin->frame->title);
670
671 changeImage(panel, panel->current, 1);
672 }
673 return wwin;
674 }
675
676 WWindow *wSwitchPanelHandleEvent(WSwitchPanel * panel, XEvent * event)
677 {
678 WMFrame *icon;
679 int i;
680 int focus = -1;
681
682 if (!panel->win)
683 return NULL;
684
685 /* if (event->type == LeaveNotify) {
686 if (event->xcrossing.window == WMWidgetXID(panel->win))
687 focus= 0;
688 } else */ if (event->type == MotionNotify) {
689
690 WM_ITERATE_ARRAY(panel->icons, icon, i) {
691 if (WMWidgetXID(icon) == event->xmotion.window) {
692 focus = i;
693 break;
694 }
695 }
696 }
697 if (focus >= 0 && panel->current != focus) {
698 WWindow *wwin;
699
700 changeImage(panel, panel->current, 0);
701 changeImage(panel, focus, 1);
702 panel->current = focus;
703
704 wwin = WMGetFromArray(panel->windows, focus);
705
706 drawTitle(panel, panel->current, wwin->frame->title);
707
708 return wwin;
709 }
710
711 return NULL;
712 }
713
714 Window wSwitchPanelGetWindow(WSwitchPanel * swpanel)
715 {
716 if (!swpanel->win)
717 return None;
718 return WMWidgetXID(swpanel->win);
719 }