wmaker: Replaced local declaration of system function by proper header call
[wmaker-crm.git] / src / switchpanel.c
blobf305d4137b47cd6588597735f4cf2472b52fc1b0
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"
38 extern Atom _XA_WM_IGNORE_FOCUS_EVENTS;
40 #ifdef SHAPE
41 #include <X11/extensions/shape.h>
43 extern Bool wShapeSupported;
44 #endif
46 struct SwitchPanel {
47 WScreen *scr;
48 WMWindow *win;
49 WMFrame *iconBox;
51 WMArray *icons;
52 WMArray *images;
53 WMArray *windows;
54 WMArray *flags;
55 RImage *bg;
56 int current;
57 int firstVisible;
58 int visibleCount;
60 WMLabel *label;
62 RImage *tileTmp;
63 RImage *tile;
65 WMFont *font;
66 WMColor *white;
69 #define BORDER_SPACE 10
70 #define ICON_SIZE 48
71 #define ICON_TILE_SIZE 64
72 #define LABEL_HEIGHT 25
73 #define SCREEN_BORDER_SPACING 2*20
74 #define SCROLL_STEPS (ICON_TILE_SIZE/2)
76 #define ICON_SELECTED (1<<1)
77 #define ICON_DIM (1<<2)
79 static int canReceiveFocus(WWindow *wwin)
81 if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
82 return 0;
84 if (wPreferences.cycle_active_head_only &&
85 wGetHeadForWindow(wwin) != wGetHeadForPointerLocation(wwin->screen_ptr))
86 return 0;
88 if (WFLAGP(wwin, no_focusable))
89 return 0;
91 if (!wwin->flags.mapped) {
92 if (!wwin->flags.shaded && !wwin->flags.miniaturized && !wwin->flags.hidden)
93 return 0;
94 else
95 return -1;
98 return 1;
101 static Bool sameWindowClass(WWindow *wwin, WWindow *curwin)
103 if (!wwin->wm_class || !curwin->wm_class)
104 return False;
105 if (strcmp(wwin->wm_class, curwin->wm_class))
106 return False;
108 return True;
111 static void changeImage(WSwitchPanel *panel, int idecks, int selected, Bool dim, Bool force)
113 WMFrame *icon = NULL;
114 RImage *image = NULL;
115 char flags = 0;
116 char desired = 0;
118 /* This whole function is a no-op if we aren't drawing the panel */
119 if (!wPreferences.swtileImage)
120 return;
122 icon = WMGetFromArray(panel->icons, idecks);
123 image = WMGetFromArray(panel->images, idecks);
124 flags = (char) (uintptr_t) WMGetFromArray(panel->flags, idecks);
126 if (selected)
127 desired |= ICON_SELECTED;
128 if (dim)
129 desired |= ICON_DIM;
131 if (flags == desired && !force)
132 return;
134 WMReplaceInArray(panel->flags, idecks, (void *) (uintptr_t) desired);
136 if (!panel->bg && !panel->tile && !selected)
137 WMSetFrameRelief(icon, WRFlat);
139 if (image && icon) {
140 RImage *back;
141 int opaq = (dim) ? 75 : 255;
142 RImage *tile;
143 WMPoint pos;
144 Pixmap p;
146 if (canReceiveFocus(WMGetFromArray(panel->windows, idecks)) < 0)
147 opaq = 50;
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);
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);
171 RCombineAreaWithOpaqueness(back, image, 0, 0, image->width, image->height,
172 (back->width - image->width) / 2, (back->height - image->height) / 2,
173 opaq);
175 RConvertImage(panel->scr->rcontext, back, &p);
176 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(icon), p);
177 XClearWindow(dpy, WMWidgetXID(icon));
178 XFreePixmap(dpy, p);
181 if (!panel->bg && !panel->tile && selected)
182 WMSetFrameRelief(icon, WRSimple);
185 static void addIconForWindow(WSwitchPanel *panel, WMWidget *parent, WWindow *wwin, int x, int y)
187 WMFrame *icon = WMCreateFrame(parent);
188 RImage *image = NULL;
190 WMSetFrameRelief(icon, WRFlat);
191 WMResizeWidget(icon, ICON_TILE_SIZE, ICON_TILE_SIZE);
192 WMMoveWidget(icon, x, y);
194 if (!WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
195 image = RRetainImage(wwin->net_icon_image);
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);
201 /* We must resize the icon size (~64) to the switchpanel icon size (~48) */
202 image = wIconValidateIconSize(image, ICON_SIZE);
204 WMAddToArray(panel->images, image);
205 WMAddToArray(panel->icons, icon);
208 static void scrollIcons(WSwitchPanel *panel, int delta)
210 int nfirst = panel->firstVisible + delta;
211 int i;
212 int count = WMGetArrayItemCount(panel->windows);
213 Bool dim;
215 if (count <= panel->visibleCount)
216 return;
218 if (nfirst < 0)
219 nfirst = 0;
220 else if (nfirst >= count - panel->visibleCount)
221 nfirst = count - panel->visibleCount;
223 if (nfirst == panel->firstVisible)
224 return;
226 WMMoveWidget(panel->iconBox, -nfirst * ICON_TILE_SIZE, 0);
228 panel->firstVisible = nfirst;
230 for (i = panel->firstVisible; i < panel->firstVisible + panel->visibleCount; i++) {
231 if (i == panel->current)
232 continue;
233 dim = ((char) (uintptr_t) WMGetFromArray(panel->flags, i) & ICON_DIM);
234 changeImage(panel, i, 0, dim, True);
239 * 0 1 2
240 * 3 4 5
241 * 6 7 8
243 static RImage *assemblePuzzleImage(RImage **images, int width, int height)
245 RImage *img = RCreateImage(width, height, 1);
246 RImage *tmp;
247 int tw, th;
248 RColor color;
249 if (!img)
250 return NULL;
252 color.red = 0;
253 color.green = 0;
254 color.blue = 0;
255 color.alpha = 255;
257 RFillImage(img, &color);
259 tw = width - images[0]->width - images[2]->width;
260 th = height - images[0]->height - images[6]->height;
262 if (tw <= 0 || th <= 0)
263 return NULL;
265 /* top */
266 if (tw > 0) {
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);
271 /* bottom */
272 if (tw > 0) {
273 tmp = RSmoothScaleImage(images[7], tw, images[7]->height);
274 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[6]->width, height - images[6]->height);
275 RReleaseImage(tmp);
277 /* left */
278 if (th > 0) {
279 tmp = RSmoothScaleImage(images[3], images[3]->width, th);
280 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, 0, images[0]->height);
281 RReleaseImage(tmp);
283 /* right */
284 if (th > 0) {
285 tmp = RSmoothScaleImage(images[5], images[5]->width, th);
286 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, width - images[5]->width, images[2]->height);
287 RReleaseImage(tmp);
289 /* center */
290 if (tw > 0 && th > 0) {
291 tmp = RSmoothScaleImage(images[4], tw, th);
292 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, images[0]->height);
293 RReleaseImage(tmp);
296 /* corners */
297 RCopyArea(img, images[0], 0, 0, images[0]->width, images[0]->height, 0, 0);
298 RCopyArea(img, images[2], 0, 0, images[2]->width, images[2]->height, width - images[2]->width, 0);
299 RCopyArea(img, images[6], 0, 0, images[6]->width, images[6]->height, 0, height - images[6]->height);
300 RCopyArea(img, images[8], 0, 0, images[8]->width, images[8]->height,
301 width - images[8]->width, height - images[8]->height);
303 return img;
306 static RImage *createBackImage(int width, int height)
308 return assemblePuzzleImage(wPreferences.swbackImage, width, height);
311 static RImage *getTile(void)
313 RImage *stile;
315 if (!wPreferences.swtileImage)
316 return NULL;
318 stile = RScaleImage(wPreferences.swtileImage, ICON_TILE_SIZE, ICON_TILE_SIZE);
319 if (!stile)
320 return wPreferences.swtileImage;
322 return stile;
325 static void drawTitle(WSwitchPanel *panel, int idecks, const char *title)
327 char *ntitle;
328 int width = WMWidgetWidth(panel->win);
329 int x;
331 if (title)
332 ntitle = ShrinkString(panel->font, title, width - 2 * BORDER_SPACE);
333 else
334 ntitle = NULL;
336 if (panel->bg) {
337 if (ntitle) {
338 if (strcmp(ntitle, title) != 0) {
339 x = BORDER_SPACE;
340 } else {
341 int w = WMWidthOfString(panel->font, ntitle, strlen(ntitle));
343 x = BORDER_SPACE + (idecks - panel->firstVisible) * ICON_TILE_SIZE +
344 ICON_TILE_SIZE / 2 - w / 2;
345 if (x < BORDER_SPACE)
346 x = BORDER_SPACE;
347 else if (x + w > width - BORDER_SPACE)
348 x = width - BORDER_SPACE - w;
352 XClearWindow(dpy, WMWidgetXID(panel->win));
353 if (ntitle)
354 WMDrawString(panel->scr->wmscreen,
355 WMWidgetXID(panel->win),
356 panel->white, panel->font,
358 WMWidgetHeight(panel->win) - BORDER_SPACE - LABEL_HEIGHT +
359 WMFontHeight(panel->font) / 2, ntitle, strlen(ntitle));
360 } else {
361 if (ntitle)
362 WMSetLabelText(panel->label, ntitle);
365 if (ntitle)
366 free(ntitle);
369 static WMArray *makeWindowListArray(WWindow *curwin, int include_unmapped, Bool class_only)
371 WMArray *windows = WMCreateArray(10);
372 int fl;
373 WWindow *wwin;
375 for (fl = 0; fl < 2; fl++) {
376 for (wwin = curwin; wwin; wwin = wwin->prev) {
377 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
378 (wwin->flags.mapped || include_unmapped)) {
379 if (class_only)
380 if (!sameWindowClass(wwin, curwin))
381 continue;
383 if (!WFLAGP(wwin, skip_switchpanel))
384 WMAddToArray(windows, wwin);
387 wwin = curwin;
388 /* start over from the beginning of the list */
389 while (wwin->next)
390 wwin = wwin->next;
392 for (wwin = curwin; wwin && wwin != curwin; wwin = wwin->prev) {
393 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
394 (wwin->flags.mapped || include_unmapped)) {
395 if (class_only)
396 if (!sameWindowClass(wwin, curwin))
397 continue;
398 if (!WFLAGP(wwin, skip_switchpanel))
399 WMAddToArray(windows, wwin);
404 return windows;
407 static WMArray *makeWindowFlagsArray(int count)
409 WMArray *flags = WMCreateArray(1);
410 int i;
412 for (i = 0; i < count; i++)
413 WMAddToArray(flags, (char) 0);
415 return flags;
418 WSwitchPanel *wInitSwitchPanel(WScreen *scr, WWindow *curwin, Bool class_only)
420 WWindow *wwin;
421 WSwitchPanel *panel = wmalloc(sizeof(WSwitchPanel));
422 WMFrame *viewport;
423 int i, width, height, iconsThatFitCount, count;
424 WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
426 panel->scr = scr;
427 panel->windows = makeWindowListArray(curwin, wPreferences.swtileImage != NULL, class_only);
428 count = WMGetArrayItemCount(panel->windows);
429 if (count)
430 panel->flags = makeWindowFlagsArray(count);
432 if (count == 0) {
433 WMFreeArray(panel->windows);
434 wfree(panel);
435 return NULL;
438 width = ICON_TILE_SIZE * count;
439 iconsThatFitCount = count;
441 if (width > rect.size.width) {
442 iconsThatFitCount = (rect.size.width - SCREEN_BORDER_SPACING) / ICON_TILE_SIZE;
443 width = iconsThatFitCount * ICON_TILE_SIZE;
446 panel->visibleCount = iconsThatFitCount;
448 if (!wPreferences.swtileImage)
449 return panel;
451 height = LABEL_HEIGHT + ICON_TILE_SIZE;
453 panel->tileTmp = RCreateImage(ICON_TILE_SIZE, ICON_TILE_SIZE, 1);
454 panel->tile = getTile();
455 if (panel->tile && wPreferences.swbackImage[8])
456 panel->bg = createBackImage(width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
458 if (!panel->tileTmp || !panel->tile) {
459 if (panel->bg)
460 RReleaseImage(panel->bg);
461 panel->bg = NULL;
462 if (panel->tile)
463 RReleaseImage(panel->tile);
464 panel->tile = NULL;
465 if (panel->tileTmp)
466 RReleaseImage(panel->tileTmp);
467 panel->tileTmp = NULL;
470 panel->white = WMWhiteColor(scr->wmscreen);
471 panel->font = WMBoldSystemFontOfSize(scr->wmscreen, 12);
472 panel->icons = WMCreateArray(count);
473 panel->images = WMCreateArray(count);
475 panel->win = WMCreateWindow(scr->wmscreen, "");
477 if (!panel->bg) {
478 WMFrame *frame = WMCreateFrame(panel->win);
479 WMColor *darkGray = WMDarkGrayColor(scr->wmscreen);
480 WMSetFrameRelief(frame, WRSimple);
481 WMSetViewExpandsToParent(WMWidgetView(frame), 0, 0, 0, 0);
483 panel->label = WMCreateLabel(panel->win);
484 WMResizeWidget(panel->label, width, LABEL_HEIGHT);
485 WMMoveWidget(panel->label, BORDER_SPACE, BORDER_SPACE + ICON_TILE_SIZE + 5);
486 WMSetLabelRelief(panel->label, WRSimple);
487 WMSetWidgetBackgroundColor(panel->label, darkGray);
488 WMSetLabelFont(panel->label, panel->font);
489 WMSetLabelTextColor(panel->label, panel->white);
491 WMReleaseColor(darkGray);
492 height += 5;
495 WMResizeWidget(panel->win, width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
497 viewport = WMCreateFrame(panel->win);
498 WMResizeWidget(viewport, width, ICON_TILE_SIZE);
499 WMMoveWidget(viewport, BORDER_SPACE, BORDER_SPACE);
500 WMSetFrameRelief(viewport, WRFlat);
502 panel->iconBox = WMCreateFrame(viewport);
503 WMMoveWidget(panel->iconBox, 0, 0);
504 WMResizeWidget(panel->iconBox, ICON_TILE_SIZE * count, ICON_TILE_SIZE);
505 WMSetFrameRelief(panel->iconBox, WRFlat);
507 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
508 addIconForWindow(panel, panel->iconBox, wwin, i * ICON_TILE_SIZE, 0);
511 WMMapSubwidgets(panel->win);
512 WMRealizeWidget(panel->win);
514 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
515 changeImage(panel, i, 0, False, True);
518 if (panel->bg) {
519 Pixmap pixmap, mask;
521 RConvertImageMask(scr->rcontext, panel->bg, &pixmap, &mask, 250);
523 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(panel->win), pixmap);
525 #ifdef SHAPE
526 if (mask && wShapeSupported)
527 XShapeCombineMask(dpy, WMWidgetXID(panel->win), ShapeBounding, 0, 0, mask, ShapeSet);
528 #endif
529 if (pixmap)
530 XFreePixmap(dpy, pixmap);
532 if (mask)
533 XFreePixmap(dpy, mask);
537 WMPoint center;
538 center = wGetPointToCenterRectInHead(scr, wGetHeadForPointerLocation(scr),
539 width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
540 WMMoveWidget(panel->win, center.x, center.y);
543 panel->current = WMGetFirstInArray(panel->windows, curwin);
544 if (panel->current >= 0)
545 changeImage(panel, panel->current, 1, False, False);
547 WMMapWidget(panel->win);
549 return panel;
552 void wSwitchPanelDestroy(WSwitchPanel *panel)
554 int i;
555 RImage *image;
557 if (panel->win) {
558 Window info_win = panel->scr->info_window;
559 XEvent ev;
560 ev.xclient.type = ClientMessage;
561 ev.xclient.message_type = _XA_WM_IGNORE_FOCUS_EVENTS;
562 ev.xclient.format = 32;
563 ev.xclient.data.l[0] = True;
565 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
566 WMUnmapWidget(panel->win);
568 ev.xclient.data.l[0] = False;
569 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
572 if (panel->images) {
573 WM_ITERATE_ARRAY(panel->images, image, i) {
574 if (image)
575 RReleaseImage(image);
577 WMFreeArray(panel->images);
580 if (panel->win)
581 WMDestroyWidget(panel->win);
583 if (panel->icons)
584 WMFreeArray(panel->icons);
586 if (panel->flags)
587 WMFreeArray(panel->flags);
589 WMFreeArray(panel->windows);
591 if (panel->tile)
592 RReleaseImage(panel->tile);
594 if (panel->tileTmp)
595 RReleaseImage(panel->tileTmp);
597 if (panel->bg)
598 RReleaseImage(panel->bg);
600 if (panel->font)
601 WMReleaseFont(panel->font);
603 if (panel->white)
604 WMReleaseColor(panel->white);
606 wfree(panel);
609 WWindow *wSwitchPanelSelectNext(WSwitchPanel *panel, int back, int ignore_minimized, Bool class_only)
611 WWindow *wwin, *curwin, *tmpwin;
612 int count = WMGetArrayItemCount(panel->windows);
613 int orig = panel->current;
614 int i;
615 Bool dim = False;
617 if (count == 0)
618 return NULL;
620 if (!wPreferences.cycle_ignore_minimized)
621 ignore_minimized = False;
623 if (ignore_minimized && canReceiveFocus(WMGetFromArray(panel->windows, (count + panel->current) % count)) < 0)
624 ignore_minimized = False;
626 curwin = WMGetFromArray(panel->windows, orig);
627 do {
628 do {
629 if (back)
630 panel->current--;
631 else
632 panel->current++;
634 panel->current= (count + panel->current) % count;
635 wwin = WMGetFromArray(panel->windows, panel->current);
637 if (!class_only)
638 break;
639 if (panel->current == orig)
640 break;
641 } while (!sameWindowClass(wwin, curwin));
642 } while (ignore_minimized && panel->current != orig && canReceiveFocus(wwin) < 0);
644 WM_ITERATE_ARRAY(panel->windows, tmpwin, i) {
645 if (i == panel->current)
646 continue;
647 if (!class_only || sameWindowClass(tmpwin, curwin))
648 changeImage(panel, i, 0, False, False);
649 else {
650 if (i == orig)
651 dim = True;
652 changeImage(panel, i, 0, True, False);
657 if (panel->current < panel->firstVisible)
658 scrollIcons(panel, panel->current - panel->firstVisible);
659 else if (panel->current - panel->firstVisible >= panel->visibleCount)
660 scrollIcons(panel, panel->current - panel->firstVisible - panel->visibleCount + 1);
662 if (panel->win) {
663 drawTitle(panel, panel->current, wwin->frame->title);
664 if (panel->current != orig)
665 changeImage(panel, orig, 0, dim, False);
666 changeImage(panel, panel->current, 1, False, False);
669 return wwin;
672 WWindow *wSwitchPanelSelectFirst(WSwitchPanel *panel, int back)
674 WWindow *wwin;
675 int count = WMGetArrayItemCount(panel->windows);
676 char *title;
677 int i;
679 if (count == 0)
680 return NULL;
682 if (back) {
683 panel->current = count - 1;
684 scrollIcons(panel, count);
685 } else {
686 panel->current = 0;
687 scrollIcons(panel, -count);
690 wwin = WMGetFromArray(panel->windows, panel->current);
691 title = wwin->frame->title;
693 if (panel->win) {
694 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
695 changeImage(panel, i, i == panel->current, False, False);
697 drawTitle(panel, panel->current, title);
700 return wwin;
703 WWindow *wSwitchPanelHandleEvent(WSwitchPanel *panel, XEvent *event)
705 WMFrame *icon;
706 int i;
707 int focus = -1;
709 if (!panel->win)
710 return NULL;
712 if (event->type == MotionNotify) {
713 WM_ITERATE_ARRAY(panel->icons, icon, i) {
714 if (WMWidgetXID(icon) == event->xmotion.window) {
715 focus = i;
716 break;
721 if (focus >= 0 && panel->current != focus) {
722 WWindow *wwin;
724 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
725 changeImage(panel, i, i == focus, False, False);
727 panel->current = focus;
729 wwin = WMGetFromArray(panel->windows, focus);
731 drawTitle(panel, panel->current, wwin->frame->title);
733 return wwin;
736 return NULL;
739 Window wSwitchPanelGetWindow(WSwitchPanel *swpanel)
741 if (!swpanel->win)
742 return None;
744 return WMWidgetXID(swpanel->win);