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