wrlib: add explicit type definition in API to allow compiler Type Checks (3/3)
[wmaker-crm.git] / src / switchpanel.c
blob041e1675bb3f2ee08bbe423abc92f4ad0d83f12b
1 /*
2 * Window Maker window manager
4 * Copyright (c) 1997-2004 Alfredo K. Kojima
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.
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.
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.
21 #include "wconfig.h"
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/time.h>
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"
39 #ifdef USE_XSHAPE
40 #include <X11/extensions/shape.h>
41 #endif
43 struct SwitchPanel {
44 WScreen *scr;
45 WMWindow *win;
46 WMFrame *iconBox;
48 WMArray *icons;
49 WMArray *images;
50 WMArray *windows;
51 WMArray *flags;
52 RImage *bg;
53 int current;
54 int firstVisible;
55 int visibleCount;
57 WMLabel *label;
59 RImage *tileTmp;
60 RImage *tile;
62 WMFont *font;
63 WMColor *white;
66 #define BORDER_SPACE 10
67 #define ICON_SIZE 48
68 #define ICON_TILE_SIZE 64
69 #define LABEL_HEIGHT 25
70 #define SCREEN_BORDER_SPACING 2*20
71 #define SCROLL_STEPS (ICON_TILE_SIZE/2)
73 #define ICON_SELECTED (1<<1)
74 #define ICON_DIM (1<<2)
76 static int canReceiveFocus(WWindow *wwin)
78 if (wwin->frame->workspace != w_global.workspace.current)
79 return 0;
81 if (wPreferences.cycle_active_head_only &&
82 wGetHeadForWindow(wwin) != wGetHeadForPointerLocation(wwin->screen_ptr))
83 return 0;
85 if (WFLAGP(wwin, no_focusable))
86 return 0;
88 if (!wwin->flags.mapped) {
89 if (!wwin->flags.shaded && !wwin->flags.miniaturized && !wwin->flags.hidden)
90 return 0;
91 else
92 return -1;
95 return 1;
98 static Bool sameWindowClass(WWindow *wwin, WWindow *curwin)
100 if (!wwin->wm_class || !curwin->wm_class)
101 return False;
102 if (strcmp(wwin->wm_class, curwin->wm_class))
103 return False;
105 return True;
108 static void changeImage(WSwitchPanel *panel, int idecks, int selected, Bool dim, Bool force)
110 WMFrame *icon = NULL;
111 RImage *image = NULL;
112 int flags;
113 int desired = 0;
115 /* This whole function is a no-op if we aren't drawing the panel */
116 if (!wPreferences.swtileImage)
117 return;
119 icon = WMGetFromArray(panel->icons, idecks);
120 image = WMGetFromArray(panel->images, idecks);
121 flags = (int) (uintptr_t) WMGetFromArray(panel->flags, idecks);
123 if (selected)
124 desired |= ICON_SELECTED;
125 if (dim)
126 desired |= ICON_DIM;
128 if (flags == desired && !force)
129 return;
131 WMReplaceInArray(panel->flags, idecks, (void *) (uintptr_t) desired);
133 if (!panel->bg && !panel->tile && !selected)
134 WMSetFrameRelief(icon, WRFlat);
136 if (image && icon) {
137 RImage *back;
138 int opaq = (dim) ? 75 : 255;
139 RImage *tile;
140 WMPoint pos;
141 Pixmap p;
143 if (canReceiveFocus(WMGetFromArray(panel->windows, idecks)) < 0)
144 opaq = 50;
146 pos = WMGetViewPosition(WMWidgetView(icon));
147 back = panel->tileTmp;
148 if (panel->bg) {
149 RCopyArea(back, panel->bg,
150 BORDER_SPACE + pos.x - panel->firstVisible * ICON_TILE_SIZE,
151 BORDER_SPACE + pos.y, back->width, back->height, 0, 0);
152 } else {
153 RColor color;
154 WMScreen *wscr = WMWidgetScreen(icon);
155 color.red = 255;
156 color.red = WMRedComponentOfColor(WMGrayColor(wscr)) >> 8;
157 color.green = WMGreenComponentOfColor(WMGrayColor(wscr)) >> 8;
158 color.blue = WMBlueComponentOfColor(WMGrayColor(wscr)) >> 8;
159 RFillImage(back, &color);
162 if (selected) {
163 tile = panel->tile;
164 RCombineArea(back, tile, 0, 0, tile->width, tile->height,
165 (back->width - tile->width) / 2, (back->height - tile->height) / 2);
168 RCombineAreaWithOpaqueness(back, image, 0, 0, image->width, image->height,
169 (back->width - image->width) / 2, (back->height - image->height) / 2,
170 opaq);
172 RConvertImage(panel->scr->rcontext, back, &p);
173 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(icon), p);
174 XClearWindow(dpy, WMWidgetXID(icon));
175 XFreePixmap(dpy, p);
178 if (!panel->bg && !panel->tile && selected)
179 WMSetFrameRelief(icon, WRSimple);
182 static void addIconForWindow(WSwitchPanel *panel, WMWidget *parent, WWindow *wwin, int x, int y)
184 WMFrame *icon = WMCreateFrame(parent);
185 RImage *image = NULL;
187 WMSetFrameRelief(icon, WRFlat);
188 WMResizeWidget(icon, ICON_TILE_SIZE, ICON_TILE_SIZE);
189 WMMoveWidget(icon, x, y);
191 if (!WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
192 image = RRetainImage(wwin->net_icon_image);
194 /* get_icon_image() includes the default icon image */
195 if (!image)
196 image = get_icon_image(panel->scr, wwin->wm_instance, wwin->wm_class, ICON_TILE_SIZE);
198 /* We must resize the icon size (~64) to the switchpanel icon size (~48) */
199 image = wIconValidateIconSize(image, ICON_SIZE);
201 WMAddToArray(panel->images, image);
202 WMAddToArray(panel->icons, icon);
205 static void scrollIcons(WSwitchPanel *panel, int delta)
207 int nfirst = panel->firstVisible + delta;
208 int i;
209 int count = WMGetArrayItemCount(panel->windows);
210 Bool dim;
212 if (count <= panel->visibleCount)
213 return;
215 if (nfirst < 0)
216 nfirst = 0;
217 else if (nfirst >= count - panel->visibleCount)
218 nfirst = count - panel->visibleCount;
220 if (nfirst == panel->firstVisible)
221 return;
223 WMMoveWidget(panel->iconBox, -nfirst * ICON_TILE_SIZE, 0);
225 panel->firstVisible = nfirst;
227 for (i = panel->firstVisible; i < panel->firstVisible + panel->visibleCount; i++) {
228 if (i == panel->current)
229 continue;
230 dim = ((int) (uintptr_t) WMGetFromArray(panel->flags, i) & ICON_DIM);
231 changeImage(panel, i, 0, dim, True);
236 * 0 1 2
237 * 3 4 5
238 * 6 7 8
240 static RImage *assemblePuzzleImage(RImage **images, int width, int height)
242 RImage *img;
243 RImage *tmp;
244 int tw, th;
245 RColor color;
247 tw = width - images[0]->width - images[2]->width;
248 th = height - images[0]->height - images[6]->height;
250 if (tw <= 0 || th <= 0)
251 return NULL;
253 img = RCreateImage(width, height, 1);
254 if (!img)
255 return NULL;
257 color.red = 0;
258 color.green = 0;
259 color.blue = 0;
260 color.alpha = 255;
261 RFillImage(img, &color);
263 /* top */
264 if (tw > 0) {
265 tmp = RSmoothScaleImage(images[1], tw, images[1]->height);
266 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, 0);
267 RReleaseImage(tmp);
269 /* bottom */
270 if (tw > 0) {
271 tmp = RSmoothScaleImage(images[7], tw, images[7]->height);
272 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[6]->width, height - images[6]->height);
273 RReleaseImage(tmp);
275 /* left */
276 if (th > 0) {
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);
281 /* right */
282 if (th > 0) {
283 tmp = RSmoothScaleImage(images[5], images[5]->width, th);
284 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, width - images[5]->width, images[2]->height);
285 RReleaseImage(tmp);
287 /* center */
288 if (tw > 0 && th > 0) {
289 tmp = RSmoothScaleImage(images[4], tw, th);
290 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, images[0]->height);
291 RReleaseImage(tmp);
294 /* corners */
295 RCopyArea(img, images[0], 0, 0, images[0]->width, images[0]->height, 0, 0);
296 RCopyArea(img, images[2], 0, 0, images[2]->width, images[2]->height, width - images[2]->width, 0);
297 RCopyArea(img, images[6], 0, 0, images[6]->width, images[6]->height, 0, height - images[6]->height);
298 RCopyArea(img, images[8], 0, 0, images[8]->width, images[8]->height,
299 width - images[8]->width, height - images[8]->height);
301 return img;
304 static RImage *createBackImage(int width, int height)
306 return assemblePuzzleImage(wPreferences.swbackImage, width, height);
309 static RImage *getTile(void)
311 RImage *stile;
313 if (!wPreferences.swtileImage)
314 return NULL;
316 stile = RScaleImage(wPreferences.swtileImage, ICON_TILE_SIZE, ICON_TILE_SIZE);
317 if (!stile)
318 return wPreferences.swtileImage;
320 return stile;
323 static void drawTitle(WSwitchPanel *panel, int idecks, const char *title)
325 char *ntitle;
326 int width = WMWidgetWidth(panel->win);
327 int x;
329 if (title)
330 ntitle = ShrinkString(panel->font, title, width - 2 * BORDER_SPACE);
331 else
332 ntitle = NULL;
334 if (panel->bg) {
335 if (ntitle) {
336 if (strcmp(ntitle, title) != 0) {
337 x = BORDER_SPACE;
338 } else {
339 int w = WMWidthOfString(panel->font, ntitle, strlen(ntitle));
341 x = BORDER_SPACE + (idecks - panel->firstVisible) * ICON_TILE_SIZE +
342 ICON_TILE_SIZE / 2 - w / 2;
343 if (x < BORDER_SPACE)
344 x = BORDER_SPACE;
345 else if (x + w > width - BORDER_SPACE)
346 x = width - BORDER_SPACE - w;
350 XClearWindow(dpy, WMWidgetXID(panel->win));
351 if (ntitle)
352 WMDrawString(panel->scr->wmscreen,
353 WMWidgetXID(panel->win),
354 panel->white, panel->font,
356 WMWidgetHeight(panel->win) - BORDER_SPACE - LABEL_HEIGHT +
357 WMFontHeight(panel->font) / 2, ntitle, strlen(ntitle));
358 } else {
359 if (ntitle)
360 WMSetLabelText(panel->label, ntitle);
363 if (ntitle)
364 free(ntitle);
367 static WMArray *makeWindowListArray(WWindow *curwin, int include_unmapped, Bool class_only)
369 WMArray *windows = WMCreateArray(10);
370 int fl;
371 WWindow *wwin;
373 for (fl = 0; fl < 2; fl++) {
374 for (wwin = curwin; wwin; wwin = wwin->prev) {
375 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
376 (wwin->flags.mapped || wwin->flags.shaded || include_unmapped)) {
377 if (class_only)
378 if (!sameWindowClass(wwin, curwin))
379 continue;
381 if (!WFLAGP(wwin, skip_switchpanel))
382 WMAddToArray(windows, wwin);
385 wwin = curwin;
386 /* start over from the beginning of the list */
387 while (wwin->next)
388 wwin = wwin->next;
390 for (wwin = curwin; wwin && wwin != curwin; wwin = wwin->prev) {
391 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
392 (wwin->flags.mapped || wwin->flags.shaded || include_unmapped)) {
393 if (class_only)
394 if (!sameWindowClass(wwin, curwin))
395 continue;
396 if (!WFLAGP(wwin, skip_switchpanel))
397 WMAddToArray(windows, wwin);
402 return windows;
405 static WMArray *makeWindowFlagsArray(int count)
407 WMArray *flags = WMCreateArray(count);
408 int i;
410 for (i = 0; i < count; i++)
411 WMAddToArray(flags, (void *) 0);
413 return flags;
416 WSwitchPanel *wInitSwitchPanel(WScreen *scr, WWindow *curwin, Bool class_only)
418 WWindow *wwin;
419 WSwitchPanel *panel = wmalloc(sizeof(WSwitchPanel));
420 WMFrame *viewport;
421 int i, width, height, iconsThatFitCount, count;
422 WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
424 panel->scr = scr;
425 panel->windows = makeWindowListArray(curwin, wPreferences.swtileImage != NULL, class_only);
426 count = WMGetArrayItemCount(panel->windows);
427 if (count)
428 panel->flags = makeWindowFlagsArray(count);
430 if (count == 0) {
431 WMFreeArray(panel->windows);
432 wfree(panel);
433 return NULL;
436 width = ICON_TILE_SIZE * count;
437 iconsThatFitCount = count;
439 if (width > rect.size.width) {
440 iconsThatFitCount = (rect.size.width - SCREEN_BORDER_SPACING) / ICON_TILE_SIZE;
441 width = iconsThatFitCount * ICON_TILE_SIZE;
444 panel->visibleCount = iconsThatFitCount;
446 if (!wPreferences.swtileImage)
447 return panel;
449 height = LABEL_HEIGHT + ICON_TILE_SIZE;
451 panel->tileTmp = RCreateImage(ICON_TILE_SIZE, ICON_TILE_SIZE, 1);
452 panel->tile = getTile();
453 if (panel->tile && wPreferences.swbackImage[8])
454 panel->bg = createBackImage(width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
456 if (!panel->tileTmp || !panel->tile) {
457 if (panel->bg)
458 RReleaseImage(panel->bg);
459 panel->bg = NULL;
460 if (panel->tile)
461 RReleaseImage(panel->tile);
462 panel->tile = NULL;
463 if (panel->tileTmp)
464 RReleaseImage(panel->tileTmp);
465 panel->tileTmp = NULL;
468 panel->white = WMWhiteColor(scr->wmscreen);
469 panel->font = WMBoldSystemFontOfSize(scr->wmscreen, 12);
470 panel->icons = WMCreateArray(count);
471 panel->images = WMCreateArray(count);
473 panel->win = WMCreateWindow(scr->wmscreen, "");
475 if (!panel->bg) {
476 WMFrame *frame = WMCreateFrame(panel->win);
477 WMColor *darkGray = WMDarkGrayColor(scr->wmscreen);
478 WMSetFrameRelief(frame, WRSimple);
479 WMSetViewExpandsToParent(WMWidgetView(frame), 0, 0, 0, 0);
481 panel->label = WMCreateLabel(panel->win);
482 WMResizeWidget(panel->label, width, LABEL_HEIGHT);
483 WMMoveWidget(panel->label, BORDER_SPACE, BORDER_SPACE + ICON_TILE_SIZE + 5);
484 WMSetLabelRelief(panel->label, WRSimple);
485 WMSetWidgetBackgroundColor(panel->label, darkGray);
486 WMSetLabelFont(panel->label, panel->font);
487 WMSetLabelTextColor(panel->label, panel->white);
489 WMReleaseColor(darkGray);
490 height += 5;
493 WMResizeWidget(panel->win, width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
495 viewport = WMCreateFrame(panel->win);
496 WMResizeWidget(viewport, width, ICON_TILE_SIZE);
497 WMMoveWidget(viewport, BORDER_SPACE, BORDER_SPACE);
498 WMSetFrameRelief(viewport, WRFlat);
500 panel->iconBox = WMCreateFrame(viewport);
501 WMMoveWidget(panel->iconBox, 0, 0);
502 WMResizeWidget(panel->iconBox, ICON_TILE_SIZE * count, ICON_TILE_SIZE);
503 WMSetFrameRelief(panel->iconBox, WRFlat);
505 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
506 addIconForWindow(panel, panel->iconBox, wwin, i * ICON_TILE_SIZE, 0);
509 WMMapSubwidgets(panel->win);
510 WMRealizeWidget(panel->win);
512 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
513 changeImage(panel, i, 0, False, True);
516 if (panel->bg) {
517 Pixmap pixmap, mask;
519 RConvertImageMask(scr->rcontext, panel->bg, &pixmap, &mask, 250);
521 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(panel->win), pixmap);
523 #ifdef USE_XSHAPE
524 if (mask && w_global.xext.shape.supported)
525 XShapeCombineMask(dpy, WMWidgetXID(panel->win), ShapeBounding, 0, 0, mask, ShapeSet);
526 #endif
527 if (pixmap)
528 XFreePixmap(dpy, pixmap);
530 if (mask)
531 XFreePixmap(dpy, mask);
535 WMPoint center;
536 center = wGetPointToCenterRectInHead(scr, wGetHeadForPointerLocation(scr),
537 width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
538 WMMoveWidget(panel->win, center.x, center.y);
541 panel->current = WMGetFirstInArray(panel->windows, curwin);
542 if (panel->current >= 0)
543 changeImage(panel, panel->current, 1, False, False);
545 WMMapWidget(panel->win);
547 return panel;
550 void wSwitchPanelDestroy(WSwitchPanel *panel)
552 int i;
553 RImage *image;
555 if (panel->win) {
556 Window info_win = panel->scr->info_window;
557 XEvent ev;
558 ev.xclient.type = ClientMessage;
559 ev.xclient.message_type = w_global.atom.wm.ignore_focus_events;
560 ev.xclient.format = 32;
561 ev.xclient.data.l[0] = True;
563 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
564 WMUnmapWidget(panel->win);
566 ev.xclient.data.l[0] = False;
567 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
570 if (panel->images) {
571 WM_ITERATE_ARRAY(panel->images, image, i) {
572 if (image)
573 RReleaseImage(image);
575 WMFreeArray(panel->images);
578 if (panel->win)
579 WMDestroyWidget(panel->win);
581 if (panel->icons)
582 WMFreeArray(panel->icons);
584 if (panel->flags)
585 WMFreeArray(panel->flags);
587 WMFreeArray(panel->windows);
589 if (panel->tile)
590 RReleaseImage(panel->tile);
592 if (panel->tileTmp)
593 RReleaseImage(panel->tileTmp);
595 if (panel->bg)
596 RReleaseImage(panel->bg);
598 if (panel->font)
599 WMReleaseFont(panel->font);
601 if (panel->white)
602 WMReleaseColor(panel->white);
604 wfree(panel);
607 WWindow *wSwitchPanelSelectNext(WSwitchPanel *panel, int back, int ignore_minimized, Bool class_only)
609 WWindow *wwin, *curwin, *tmpwin;
610 int count = WMGetArrayItemCount(panel->windows);
611 int orig = panel->current;
612 int i;
613 Bool dim = False;
615 if (count == 0)
616 return NULL;
618 if (!wPreferences.cycle_ignore_minimized)
619 ignore_minimized = False;
621 if (ignore_minimized && canReceiveFocus(WMGetFromArray(panel->windows, (count + panel->current) % count)) < 0)
622 ignore_minimized = False;
624 curwin = WMGetFromArray(panel->windows, orig);
625 do {
626 do {
627 if (back)
628 panel->current--;
629 else
630 panel->current++;
632 panel->current= (count + panel->current) % count;
633 wwin = WMGetFromArray(panel->windows, panel->current);
635 if (!class_only)
636 break;
637 if (panel->current == orig)
638 break;
639 } while (!sameWindowClass(wwin, curwin));
640 } while (ignore_minimized && panel->current != orig && canReceiveFocus(wwin) < 0);
642 WM_ITERATE_ARRAY(panel->windows, tmpwin, i) {
643 if (i == panel->current)
644 continue;
645 if (!class_only || sameWindowClass(tmpwin, curwin))
646 changeImage(panel, i, 0, False, False);
647 else {
648 if (i == orig)
649 dim = True;
650 changeImage(panel, i, 0, True, False);
655 if (panel->current < panel->firstVisible)
656 scrollIcons(panel, panel->current - panel->firstVisible);
657 else if (panel->current - panel->firstVisible >= panel->visibleCount)
658 scrollIcons(panel, panel->current - panel->firstVisible - panel->visibleCount + 1);
660 if (panel->win) {
661 drawTitle(panel, panel->current, wwin->frame->title);
662 if (panel->current != orig)
663 changeImage(panel, orig, 0, dim, False);
664 changeImage(panel, panel->current, 1, False, False);
667 return wwin;
670 WWindow *wSwitchPanelSelectFirst(WSwitchPanel *panel, int back)
672 WWindow *wwin;
673 int count = WMGetArrayItemCount(panel->windows);
674 char *title;
675 int i;
677 if (count == 0)
678 return NULL;
680 if (back) {
681 panel->current = count - 1;
682 scrollIcons(panel, count);
683 } else {
684 panel->current = 0;
685 scrollIcons(panel, -count);
688 wwin = WMGetFromArray(panel->windows, panel->current);
689 title = wwin->frame->title;
691 if (panel->win) {
692 for (WMArrayFirst(panel->windows, &(i)); (i) != WANotFound; WMArrayNext(panel->windows, &(i)))
693 changeImage(panel, i, i == panel->current, False, False);
695 drawTitle(panel, panel->current, title);
698 return wwin;
701 WWindow *wSwitchPanelHandleEvent(WSwitchPanel *panel, XEvent *event)
703 WMFrame *icon;
704 int i;
705 int focus = -1;
707 if (!panel->win)
708 return NULL;
710 if (event->type == MotionNotify) {
711 WM_ITERATE_ARRAY(panel->icons, icon, i) {
712 if (WMWidgetXID(icon) == event->xmotion.window) {
713 focus = i;
714 break;
719 if (focus >= 0 && panel->current != focus) {
720 WWindow *wwin;
722 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
723 changeImage(panel, i, i == focus, False, False);
725 panel->current = focus;
727 wwin = WMGetFromArray(panel->windows, focus);
729 drawTitle(panel, panel->current, wwin->frame->title);
731 return wwin;
734 return NULL;
737 Window wSwitchPanelGetWindow(WSwitchPanel *swpanel)
739 if (!swpanel->win)
740 return None;
742 return WMWidgetXID(swpanel->win);