configure: rewrote support for option '--disable-locale'
[wmaker-crm.git] / src / switchpanel.c
blobb3832f16ecf52efe0a4883e78dba1778d742fb4e
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
72 #define ICON_SELECTED (1<<1)
73 #define ICON_DIM (1<<2)
75 static int canReceiveFocus(WWindow *wwin)
77 if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
78 return 0;
80 if (wPreferences.cycle_active_head_only &&
81 wGetHeadForWindow(wwin) != wGetHeadForPointerLocation(wwin->screen_ptr))
82 return 0;
84 if (WFLAGP(wwin, no_focusable))
85 return 0;
87 if (!wwin->flags.mapped) {
88 if (!wwin->flags.shaded && !wwin->flags.miniaturized && !wwin->flags.hidden)
89 return 0;
90 else
91 return -1;
94 return 1;
97 static Bool sameWindowClass(WWindow *wwin, WWindow *curwin)
99 if (!wwin->wm_class || !curwin->wm_class)
100 return False;
101 if (strcmp(wwin->wm_class, curwin->wm_class))
102 return False;
104 return True;
107 static void changeImage(WSwitchPanel *panel, int idecks, int selected, Bool dim, Bool force)
109 WMFrame *icon = NULL;
110 RImage *image = NULL;
111 int flags;
112 int desired = 0;
114 /* This whole function is a no-op if we aren't drawing the panel */
115 if (!wPreferences.swtileImage)
116 return;
118 icon = WMGetFromArray(panel->icons, idecks);
119 image = WMGetFromArray(panel->images, idecks);
120 flags = (int) (uintptr_t) WMGetFromArray(panel->flags, idecks);
122 if (selected)
123 desired |= ICON_SELECTED;
124 if (dim)
125 desired |= ICON_DIM;
127 if (flags == desired && !force)
128 return;
130 WMReplaceInArray(panel->flags, idecks, (void *) (uintptr_t) desired);
132 if (!panel->bg && !panel->tile && !selected)
133 WMSetFrameRelief(icon, WRFlat);
135 if (image && icon) {
136 RImage *back;
137 int opaq = (dim) ? 75 : 255;
138 RImage *tile;
139 WMPoint pos;
140 Pixmap p;
142 if (canReceiveFocus(WMGetFromArray(panel->windows, idecks)) < 0)
143 opaq = 50;
145 pos = WMGetViewPosition(WMWidgetView(icon));
146 back = panel->tileTmp;
147 if (panel->bg) {
148 RCopyArea(back, panel->bg,
149 BORDER_SPACE + pos.x - panel->firstVisible * ICON_TILE_SIZE,
150 BORDER_SPACE + pos.y, back->width, back->height, 0, 0);
151 } else {
152 RColor color;
153 WMScreen *wscr = WMWidgetScreen(icon);
154 color.red = 255;
155 color.red = WMRedComponentOfColor(WMGrayColor(wscr)) >> 8;
156 color.green = WMGreenComponentOfColor(WMGrayColor(wscr)) >> 8;
157 color.blue = WMBlueComponentOfColor(WMGrayColor(wscr)) >> 8;
158 RFillImage(back, &color);
161 if (selected) {
162 tile = panel->tile;
163 RCombineArea(back, tile, 0, 0, tile->width, tile->height,
164 (back->width - tile->width) / 2, (back->height - tile->height) / 2);
167 RCombineAreaWithOpaqueness(back, image, 0, 0, image->width, image->height,
168 (back->width - image->width) / 2, (back->height - image->height) / 2,
169 opaq);
171 RConvertImage(panel->scr->rcontext, back, &p);
172 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(icon), p);
173 XClearWindow(dpy, WMWidgetXID(icon));
174 XFreePixmap(dpy, p);
177 if (!panel->bg && !panel->tile && selected)
178 WMSetFrameRelief(icon, WRSimple);
181 static void addIconForWindow(WSwitchPanel *panel, WMWidget *parent, WWindow *wwin, int x, int y)
183 WMFrame *icon = WMCreateFrame(parent);
184 RImage *image = NULL;
186 WMSetFrameRelief(icon, WRFlat);
187 WMResizeWidget(icon, ICON_TILE_SIZE, ICON_TILE_SIZE);
188 WMMoveWidget(icon, x, y);
190 if (!WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
191 image = RRetainImage(wwin->net_icon_image);
193 /* get_icon_image() includes the default icon image */
194 if (!image)
195 image = get_icon_image(panel->scr, wwin->wm_instance, wwin->wm_class, ICON_TILE_SIZE);
197 /* We must resize the icon size (~64) to the switch panel icon size (~48) */
198 image = wIconValidateIconSize(image, ICON_SIZE);
200 WMAddToArray(panel->images, image);
201 WMAddToArray(panel->icons, icon);
204 static void scrollIcons(WSwitchPanel *panel, int delta)
206 int nfirst = panel->firstVisible + delta;
207 int i;
208 int count = WMGetArrayItemCount(panel->windows);
209 Bool dim;
211 if (count <= panel->visibleCount)
212 return;
214 if (nfirst < 0)
215 nfirst = 0;
216 else if (nfirst >= count - panel->visibleCount)
217 nfirst = count - panel->visibleCount;
219 if (nfirst == panel->firstVisible)
220 return;
222 WMMoveWidget(panel->iconBox, -nfirst * ICON_TILE_SIZE, 0);
224 panel->firstVisible = nfirst;
226 for (i = panel->firstVisible; i < panel->firstVisible + panel->visibleCount; i++) {
227 if (i == panel->current)
228 continue;
229 dim = ((int) (uintptr_t) WMGetFromArray(panel->flags, i) & ICON_DIM);
230 changeImage(panel, i, 0, dim, True);
235 * 0 1 2
236 * 3 4 5
237 * 6 7 8
239 static RImage *assemblePuzzleImage(RImage **images, int width, int height)
241 RImage *img;
242 RImage *tmp;
243 int tw, th;
244 RColor color;
246 tw = width - images[0]->width - images[2]->width;
247 th = height - images[0]->height - images[6]->height;
249 if (tw <= 0 || th <= 0)
250 return NULL;
252 img = RCreateImage(width, height, 1);
253 if (!img)
254 return NULL;
256 color.red = 0;
257 color.green = 0;
258 color.blue = 0;
259 color.alpha = 255;
260 RFillImage(img, &color);
262 /* top */
263 tmp = RSmoothScaleImage(images[1], tw, images[1]->height);
264 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, 0);
265 RReleaseImage(tmp);
267 /* bottom */
268 tmp = RSmoothScaleImage(images[7], tw, images[7]->height);
269 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[6]->width, height - images[6]->height);
270 RReleaseImage(tmp);
272 /* left */
273 tmp = RSmoothScaleImage(images[3], images[3]->width, th);
274 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, 0, images[0]->height);
275 RReleaseImage(tmp);
277 /* right */
278 tmp = RSmoothScaleImage(images[5], images[5]->width, th);
279 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, width - images[5]->width, images[2]->height);
280 RReleaseImage(tmp);
282 /* center */
283 tmp = RSmoothScaleImage(images[4], tw, th);
284 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, images[0]->height);
285 RReleaseImage(tmp);
287 /* corners */
288 RCopyArea(img, images[0], 0, 0, images[0]->width, images[0]->height, 0, 0);
289 RCopyArea(img, images[2], 0, 0, images[2]->width, images[2]->height, width - images[2]->width, 0);
290 RCopyArea(img, images[6], 0, 0, images[6]->width, images[6]->height, 0, height - images[6]->height);
291 RCopyArea(img, images[8], 0, 0, images[8]->width, images[8]->height,
292 width - images[8]->width, height - images[8]->height);
294 return img;
297 static RImage *createBackImage(int width, int height)
299 return assemblePuzzleImage(wPreferences.swbackImage, width, height);
302 static RImage *getTile(void)
304 RImage *stile;
306 if (!wPreferences.swtileImage)
307 return NULL;
309 stile = RScaleImage(wPreferences.swtileImage, ICON_TILE_SIZE, ICON_TILE_SIZE);
310 if (!stile)
311 return wPreferences.swtileImage;
313 return stile;
316 static void drawTitle(WSwitchPanel *panel, int idecks, const char *title)
318 char *ntitle;
319 int width = WMWidgetWidth(panel->win);
320 int x;
322 if (title)
323 ntitle = ShrinkString(panel->font, title, width - 2 * BORDER_SPACE);
324 else
325 ntitle = NULL;
327 if (panel->bg) {
328 if (ntitle) {
329 if (strcmp(ntitle, title) != 0) {
330 x = BORDER_SPACE;
331 } else {
332 int w = WMWidthOfString(panel->font, ntitle, strlen(ntitle));
334 x = BORDER_SPACE + (idecks - panel->firstVisible) * ICON_TILE_SIZE +
335 ICON_TILE_SIZE / 2 - w / 2;
336 if (x < BORDER_SPACE)
337 x = BORDER_SPACE;
338 else if (x + w > width - BORDER_SPACE)
339 x = width - BORDER_SPACE - w;
343 XClearWindow(dpy, WMWidgetXID(panel->win));
344 if (ntitle)
345 WMDrawString(panel->scr->wmscreen,
346 WMWidgetXID(panel->win),
347 panel->white, panel->font,
349 WMWidgetHeight(panel->win) - BORDER_SPACE - LABEL_HEIGHT +
350 WMFontHeight(panel->font) / 2, ntitle, strlen(ntitle));
351 } else {
352 if (ntitle)
353 WMSetLabelText(panel->label, ntitle);
356 if (ntitle)
357 free(ntitle);
360 static WMArray *makeWindowListArray(WScreen *scr, int include_unmapped, Bool class_only)
362 WMArray *windows = WMCreateArray(10);
363 WWindow *wwin = scr->focused_window;
365 while (wwin) {
366 if ((canReceiveFocus(wwin) != 0) &&
367 (wwin->flags.mapped || wwin->flags.shaded || include_unmapped)) {
368 if (class_only)
369 if (!sameWindowClass(scr->focused_window, wwin))
370 continue;
371 if (!WFLAGP(wwin, skip_switchpanel))
372 WMAddToArray(windows, wwin);
374 wwin = wwin->prev;
376 return windows;
379 static WMArray *makeWindowFlagsArray(int count)
381 WMArray *flags = WMCreateArray(count);
382 int i;
384 for (i = 0; i < count; i++)
385 WMAddToArray(flags, (void *) 0);
387 return flags;
390 WSwitchPanel *wInitSwitchPanel(WScreen *scr, WWindow *curwin, Bool class_only)
392 WWindow *wwin;
393 WSwitchPanel *panel = wmalloc(sizeof(WSwitchPanel));
394 WMFrame *viewport;
395 int i, width, height, iconsThatFitCount, count;
396 WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
398 panel->scr = scr;
399 panel->windows = makeWindowListArray(scr, wPreferences.swtileImage != NULL, class_only);
400 count = WMGetArrayItemCount(panel->windows);
401 if (count)
402 panel->flags = makeWindowFlagsArray(count);
404 if (count == 0) {
405 WMFreeArray(panel->windows);
406 wfree(panel);
407 return NULL;
410 width = ICON_TILE_SIZE * count;
411 iconsThatFitCount = count;
413 if (width > rect.size.width) {
414 iconsThatFitCount = (rect.size.width - SCREEN_BORDER_SPACING) / ICON_TILE_SIZE;
415 width = iconsThatFitCount * ICON_TILE_SIZE;
418 panel->visibleCount = iconsThatFitCount;
420 if (!wPreferences.swtileImage)
421 return panel;
423 height = LABEL_HEIGHT + ICON_TILE_SIZE;
425 panel->tileTmp = RCreateImage(ICON_TILE_SIZE, ICON_TILE_SIZE, 1);
426 panel->tile = getTile();
427 if (panel->tile && wPreferences.swbackImage[8])
428 panel->bg = createBackImage(width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
430 if (!panel->tileTmp || !panel->tile) {
431 if (panel->bg)
432 RReleaseImage(panel->bg);
433 panel->bg = NULL;
434 if (panel->tile)
435 RReleaseImage(panel->tile);
436 panel->tile = NULL;
437 if (panel->tileTmp)
438 RReleaseImage(panel->tileTmp);
439 panel->tileTmp = NULL;
442 panel->white = WMWhiteColor(scr->wmscreen);
443 panel->font = WMBoldSystemFontOfSize(scr->wmscreen, 12);
444 panel->icons = WMCreateArray(count);
445 panel->images = WMCreateArray(count);
447 panel->win = WMCreateWindow(scr->wmscreen, "");
449 if (!panel->bg) {
450 WMFrame *frame = WMCreateFrame(panel->win);
451 WMColor *darkGray = WMDarkGrayColor(scr->wmscreen);
452 WMSetFrameRelief(frame, WRSimple);
453 WMSetViewExpandsToParent(WMWidgetView(frame), 0, 0, 0, 0);
455 panel->label = WMCreateLabel(panel->win);
456 WMResizeWidget(panel->label, width, LABEL_HEIGHT);
457 WMMoveWidget(panel->label, BORDER_SPACE, BORDER_SPACE + ICON_TILE_SIZE + 5);
458 WMSetLabelRelief(panel->label, WRSimple);
459 WMSetWidgetBackgroundColor(panel->label, darkGray);
460 WMSetLabelFont(panel->label, panel->font);
461 WMSetLabelTextColor(panel->label, panel->white);
463 WMReleaseColor(darkGray);
464 height += 5;
467 WMResizeWidget(panel->win, width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
469 viewport = WMCreateFrame(panel->win);
470 WMResizeWidget(viewport, width, ICON_TILE_SIZE);
471 WMMoveWidget(viewport, BORDER_SPACE, BORDER_SPACE);
472 WMSetFrameRelief(viewport, WRFlat);
474 panel->iconBox = WMCreateFrame(viewport);
475 WMMoveWidget(panel->iconBox, 0, 0);
476 WMResizeWidget(panel->iconBox, ICON_TILE_SIZE * count, ICON_TILE_SIZE);
477 WMSetFrameRelief(panel->iconBox, WRFlat);
479 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
480 addIconForWindow(panel, panel->iconBox, wwin, i * ICON_TILE_SIZE, 0);
483 WMMapSubwidgets(panel->win);
484 WMRealizeWidget(panel->win);
486 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
487 changeImage(panel, i, 0, False, True);
490 if (panel->bg) {
491 Pixmap pixmap, mask;
493 RConvertImageMask(scr->rcontext, panel->bg, &pixmap, &mask, 250);
495 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(panel->win), pixmap);
497 #ifdef USE_XSHAPE
498 if (mask && w_global.xext.shape.supported)
499 XShapeCombineMask(dpy, WMWidgetXID(panel->win), ShapeBounding, 0, 0, mask, ShapeSet);
500 #endif
501 if (pixmap)
502 XFreePixmap(dpy, pixmap);
504 if (mask)
505 XFreePixmap(dpy, mask);
509 WMPoint center;
510 center = wGetPointToCenterRectInHead(scr, wGetHeadForPointerLocation(scr),
511 width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
512 WMMoveWidget(panel->win, center.x, center.y);
515 panel->current = WMGetFirstInArray(panel->windows, curwin);
516 if (panel->current >= 0)
517 changeImage(panel, panel->current, 1, False, False);
519 WMMapWidget(panel->win);
521 return panel;
524 void wSwitchPanelDestroy(WSwitchPanel *panel)
526 int i;
527 RImage *image;
529 if (panel->win) {
530 Window info_win = panel->scr->info_window;
531 XEvent ev;
532 ev.xclient.type = ClientMessage;
533 ev.xclient.message_type = w_global.atom.wm.ignore_focus_events;
534 ev.xclient.format = 32;
535 ev.xclient.data.l[0] = True;
537 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
538 WMUnmapWidget(panel->win);
540 ev.xclient.data.l[0] = False;
541 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
544 if (panel->images) {
545 WM_ITERATE_ARRAY(panel->images, image, i) {
546 if (image)
547 RReleaseImage(image);
549 WMFreeArray(panel->images);
552 if (panel->win)
553 WMDestroyWidget(panel->win);
555 if (panel->icons)
556 WMFreeArray(panel->icons);
558 if (panel->flags)
559 WMFreeArray(panel->flags);
561 WMFreeArray(panel->windows);
563 if (panel->tile)
564 RReleaseImage(panel->tile);
566 if (panel->tileTmp)
567 RReleaseImage(panel->tileTmp);
569 if (panel->bg)
570 RReleaseImage(panel->bg);
572 if (panel->font)
573 WMReleaseFont(panel->font);
575 if (panel->white)
576 WMReleaseColor(panel->white);
578 wfree(panel);
581 WWindow *wSwitchPanelSelectNext(WSwitchPanel *panel, int back, int ignore_minimized, Bool class_only)
583 WWindow *wwin, *curwin, *tmpwin;
584 int count = WMGetArrayItemCount(panel->windows);
585 int orig = panel->current;
586 int i;
587 Bool dim = False;
589 if (count == 0)
590 return NULL;
592 if (!wPreferences.cycle_ignore_minimized)
593 ignore_minimized = False;
595 if (ignore_minimized && canReceiveFocus(WMGetFromArray(panel->windows, (count + panel->current) % count)) < 0)
596 ignore_minimized = False;
598 curwin = WMGetFromArray(panel->windows, orig);
599 do {
600 do {
601 if (back)
602 panel->current--;
603 else
604 panel->current++;
606 panel->current = (count + panel->current) % count;
607 wwin = WMGetFromArray(panel->windows, panel->current);
609 if (!class_only)
610 break;
611 if (panel->current == orig)
612 break;
613 } while (!sameWindowClass(wwin, curwin));
614 } while (ignore_minimized && panel->current != orig && canReceiveFocus(wwin) < 0);
616 WM_ITERATE_ARRAY(panel->windows, tmpwin, i) {
617 if (i == panel->current)
618 continue;
619 if (!class_only || sameWindowClass(tmpwin, curwin))
620 changeImage(panel, i, 0, False, False);
621 else {
622 if (i == orig)
623 dim = True;
624 changeImage(panel, i, 0, True, False);
629 if (panel->current < panel->firstVisible)
630 scrollIcons(panel, panel->current - panel->firstVisible);
631 else if (panel->current - panel->firstVisible >= panel->visibleCount)
632 scrollIcons(panel, panel->current - panel->firstVisible - panel->visibleCount + 1);
634 if (panel->win) {
635 drawTitle(panel, panel->current, wwin->frame->title);
636 if (panel->current != orig)
637 changeImage(panel, orig, 0, dim, False);
638 changeImage(panel, panel->current, 1, False, False);
641 return wwin;
644 WWindow *wSwitchPanelSelectFirst(WSwitchPanel *panel, int back)
646 WWindow *wwin;
647 int count = WMGetArrayItemCount(panel->windows);
648 char *title;
649 int i;
651 if (count == 0)
652 return NULL;
654 if (back) {
655 panel->current = count - 1;
656 scrollIcons(panel, count);
657 } else {
658 panel->current = 0;
659 scrollIcons(panel, -count);
662 wwin = WMGetFromArray(panel->windows, panel->current);
663 title = wwin->frame->title;
665 if (panel->win) {
666 for (WMArrayFirst(panel->windows, &(i)); (i) != WANotFound; WMArrayNext(panel->windows, &(i)))
667 changeImage(panel, i, i == panel->current, False, False);
669 drawTitle(panel, panel->current, title);
672 return wwin;
675 WWindow *wSwitchPanelHandleEvent(WSwitchPanel *panel, XEvent *event)
677 WMFrame *icon;
678 int i;
679 int focus = -1;
681 if (!panel->win)
682 return NULL;
684 if (event->type == MotionNotify) {
685 WM_ITERATE_ARRAY(panel->icons, icon, i) {
686 if (WMWidgetXID(icon) == event->xmotion.window) {
687 focus = i;
688 break;
693 if (focus >= 0 && panel->current != focus) {
694 WWindow *wwin;
696 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
697 changeImage(panel, i, i == focus, False, False);
699 panel->current = focus;
701 wwin = WMGetFromArray(panel->windows, focus);
703 drawTitle(panel, panel->current, wwin->frame->title);
705 return wwin;
708 return NULL;
711 Window wSwitchPanelGetWindow(WSwitchPanel *swpanel)
713 if (!swpanel->win)
714 return None;
716 return WMWidgetXID(swpanel->win);