WPrefs: use W_KeycodeToKeysym instead of XkbKeycodeToKeysym
[wmaker-crm.git] / src / switchpanel.c
blob4b333c36480b64086bee3427e45b758ec1dc9d67
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;
64 WMColor *gray;
67 /* these values will be updated whenever the switch panel
68 * is created to to match the size defined in switch_panel_icon_size
69 * and the selected font size */
70 static short int icon_size;
71 static short int border_space;
72 static short int icon_tile_size;
73 static short int label_height;
75 #define SCREEN_BORDER_SPACING 2*20
77 #define ICON_SELECTED (1<<1)
78 #define ICON_DIM (1<<2)
80 static int canReceiveFocus(WWindow *wwin)
82 if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
83 return 0;
85 if (wPreferences.cycle_active_head_only &&
86 wGetHeadForWindow(wwin) != wGetHeadForPointerLocation(wwin->screen_ptr))
87 return 0;
89 if (WFLAGP(wwin, no_focusable))
90 return 0;
92 if (!wwin->flags.mapped) {
93 if (!wwin->flags.shaded && !wwin->flags.miniaturized && !wwin->flags.hidden)
94 return 0;
95 else
96 return -1;
99 return 1;
102 static Bool sameWindowClass(WWindow *wwin, WWindow *curwin)
104 if (!wwin->wm_class || !curwin->wm_class)
105 return False;
106 if (strcmp(wwin->wm_class, curwin->wm_class))
107 return False;
109 return True;
112 static void changeImage(WSwitchPanel *panel, int idecks, int selected, Bool dim, Bool force)
114 WMFrame *icon = NULL;
115 RImage *image = NULL;
116 int flags;
117 int desired = 0;
119 /* This whole function is a no-op if we aren't drawing the panel */
120 if (!wPreferences.swtileImage)
121 return;
123 icon = WMGetFromArray(panel->icons, idecks);
124 image = WMGetFromArray(panel->images, idecks);
125 flags = (int) (uintptr_t) WMGetFromArray(panel->flags, idecks);
127 if (selected)
128 desired |= ICON_SELECTED;
129 if (dim)
130 desired |= ICON_DIM;
132 if (flags == desired && !force)
133 return;
135 WMReplaceInArray(panel->flags, idecks, (void *) (uintptr_t) desired);
137 if (!panel->bg && !panel->tile && !selected)
138 WMSetFrameRelief(icon, WRFlat);
140 if (image && icon) {
141 RImage *back;
142 int opaq = (dim) ? 75 : 255;
143 RImage *tile;
144 WMPoint pos;
145 Pixmap p;
147 if (canReceiveFocus(WMGetFromArray(panel->windows, idecks)) < 0)
148 opaq = 50;
150 pos = WMGetViewPosition(WMWidgetView(icon));
151 back = panel->tileTmp;
152 if (panel->bg) {
153 RCopyArea(back, panel->bg,
154 border_space + pos.x - panel->firstVisible * icon_tile_size,
155 border_space + pos.y, back->width, back->height, 0, 0);
156 } else {
157 RColor color;
158 color.red = WMRedComponentOfColor(panel->gray) >> 8;
159 color.green = WMGreenComponentOfColor(panel->gray) >> 8;
160 color.blue = WMBlueComponentOfColor(panel->gray) >> 8;
161 RFillImage(back, &color);
164 if (selected) {
165 tile = panel->tile;
166 RCombineArea(back, tile, 0, 0, tile->width, tile->height,
167 (back->width - tile->width) / 2, (back->height - tile->height) / 2);
170 RCombineAreaWithOpaqueness(back, image, 0, 0, image->width, image->height,
171 (back->width - image->width) / 2, (back->height - image->height) / 2,
172 opaq);
174 RConvertImage(panel->scr->rcontext, back, &p);
175 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(icon), p);
176 XClearWindow(dpy, WMWidgetXID(icon));
177 XFreePixmap(dpy, p);
180 if (!panel->bg && !panel->tile && selected)
181 WMSetFrameRelief(icon, WRSimple);
184 static void addIconForWindow(WSwitchPanel *panel, WMWidget *parent, WWindow *wwin, int x, int y)
186 WMFrame *icon = WMCreateFrame(parent);
187 RImage *image = NULL;
189 WMSetFrameRelief(icon, WRFlat);
190 WMResizeWidget(icon, icon_tile_size, icon_tile_size);
191 WMMoveWidget(icon, x, y);
193 if (!WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
194 image = RRetainImage(wwin->net_icon_image);
196 /* get_icon_image() includes the default icon image */
197 if (!image)
198 image = get_icon_image(panel->scr, wwin->wm_instance, wwin->wm_class, icon_tile_size);
200 /* We must resize the icon size (~64) to the switch panel icon size (~48) */
201 image = wIconValidateIconSize(image, icon_size);
203 WMAddToArray(panel->images, image);
204 WMAddToArray(panel->icons, icon);
207 static void scrollIcons(WSwitchPanel *panel, int delta)
209 int nfirst = panel->firstVisible + delta;
210 int i;
211 int count = WMGetArrayItemCount(panel->windows);
212 Bool dim;
214 if (count <= panel->visibleCount)
215 return;
217 if (nfirst < 0)
218 nfirst = 0;
219 else if (nfirst >= count - panel->visibleCount)
220 nfirst = count - panel->visibleCount;
222 if (nfirst == panel->firstVisible)
223 return;
225 WMMoveWidget(panel->iconBox, -nfirst * icon_tile_size, 0);
227 panel->firstVisible = nfirst;
229 for (i = panel->firstVisible; i < panel->firstVisible + panel->visibleCount; i++) {
230 if (i == panel->current)
231 continue;
232 dim = ((int) (uintptr_t) WMGetFromArray(panel->flags, i) & ICON_DIM);
233 changeImage(panel, i, 0, dim, True);
238 * 0 1 2
239 * 3 4 5
240 * 6 7 8
242 static RImage *assemblePuzzleImage(RImage **images, int width, int height)
244 RImage *img;
245 RImage *tmp;
246 int tw, th;
247 RColor color;
249 tw = width - images[0]->width - images[2]->width;
250 th = height - images[0]->height - images[6]->height;
252 if (tw <= 0 || th <= 0)
253 return NULL;
255 img = RCreateImage(width, height, 1);
256 if (!img)
257 return NULL;
259 color.red = 0;
260 color.green = 0;
261 color.blue = 0;
262 color.alpha = 255;
263 RFillImage(img, &color);
265 /* top */
266 tmp = RSmoothScaleImage(images[1], tw, images[1]->height);
267 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, 0);
268 RReleaseImage(tmp);
270 /* bottom */
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 tmp = RSmoothScaleImage(images[3], images[3]->width, th);
277 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, 0, images[0]->height);
278 RReleaseImage(tmp);
280 /* right */
281 tmp = RSmoothScaleImage(images[5], images[5]->width, th);
282 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, width - images[5]->width, images[2]->height);
283 RReleaseImage(tmp);
285 /* center */
286 tmp = RSmoothScaleImage(images[4], tw, th);
287 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, images[0]->height);
288 RReleaseImage(tmp);
290 /* corners */
291 RCopyArea(img, images[0], 0, 0, images[0]->width, images[0]->height, 0, 0);
292 RCopyArea(img, images[2], 0, 0, images[2]->width, images[2]->height, width - images[2]->width, 0);
293 RCopyArea(img, images[6], 0, 0, images[6]->width, images[6]->height, 0, height - images[6]->height);
294 RCopyArea(img, images[8], 0, 0, images[8]->width, images[8]->height,
295 width - images[8]->width, height - images[8]->height);
297 return img;
300 static RImage *createBackImage(int width, int height)
302 return assemblePuzzleImage(wPreferences.swbackImage, width, height);
305 static RImage *getTile(void)
307 RImage *stile;
309 if (!wPreferences.swtileImage)
310 return NULL;
312 stile = RScaleImage(wPreferences.swtileImage, icon_tile_size, icon_tile_size);
313 if (!stile)
314 return wPreferences.swtileImage;
316 return stile;
319 static void drawTitle(WSwitchPanel *panel, int idecks, const char *title)
321 char *ntitle;
322 int width = WMWidgetWidth(panel->win);
323 int x;
325 if (title)
326 ntitle = ShrinkString(panel->font, title, width - 2 * border_space);
327 else
328 ntitle = NULL;
330 if (panel->bg) {
331 if (ntitle) {
332 if (strcmp(ntitle, title) != 0) {
333 x = border_space;
334 } else {
335 int w = WMWidthOfString(panel->font, ntitle, strlen(ntitle));
337 x = border_space + (idecks - panel->firstVisible) * icon_tile_size+
338 icon_tile_size/ 2 - w / 2;
339 if (x < border_space)
340 x = border_space;
341 else if (x + w > width - border_space)
342 x = width - border_space - w;
346 XClearWindow(dpy, WMWidgetXID(panel->win));
347 if (ntitle)
348 WMDrawString(panel->scr->wmscreen,
349 WMWidgetXID(panel->win),
350 panel->white, panel->font,
352 WMWidgetHeight(panel->win) - border_space - label_height +
353 WMFontHeight(panel->font) / 2, ntitle, strlen(ntitle));
354 } else {
355 if (ntitle)
356 WMSetLabelText(panel->label, ntitle);
359 if (ntitle)
360 free(ntitle);
363 static WMArray *makeWindowListArray(WScreen *scr, int include_unmapped, Bool class_only)
365 WMArray *windows = WMCreateArray(10);
366 WWindow *wwin = scr->focused_window;
368 while (wwin) {
369 if ((canReceiveFocus(wwin) != 0) &&
370 (wwin->flags.mapped || wwin->flags.shaded || include_unmapped)) {
371 if (class_only)
372 if (!sameWindowClass(scr->focused_window, wwin)) {
373 wwin = wwin->prev;
374 continue;
376 if (!WFLAGP(wwin, skip_switchpanel))
377 WMAddToArray(windows, wwin);
379 wwin = wwin->prev;
381 return windows;
384 static WMArray *makeWindowFlagsArray(int count)
386 WMArray *flags = WMCreateArray(count);
387 int i;
389 for (i = 0; i < count; i++)
390 WMAddToArray(flags, (void *) 0);
392 return flags;
395 WSwitchPanel *wInitSwitchPanel(WScreen *scr, WWindow *curwin, Bool class_only)
397 int wmScaleWidth, wmScaleHeight;
398 WMGetScaleBaseFromSystemFont(scr->wmscreen, &wmScaleWidth, &wmScaleHeight);
400 icon_size = wPreferences.switch_panel_icon_size;
401 icon_tile_size = (short int)(((float)icon_size * 1.2F) + 0.5F);
402 border_space = WMScaleY(10);
403 label_height = WMScaleY(25);
405 WWindow *wwin;
406 WSwitchPanel *panel = wmalloc(sizeof(WSwitchPanel));
407 WMFrame *viewport;
408 int i, width, height, iconsThatFitCount, count;
409 WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
411 panel->scr = scr;
412 panel->windows = makeWindowListArray(scr, wPreferences.swtileImage != NULL, class_only);
413 count = WMGetArrayItemCount(panel->windows);
414 if (count)
415 panel->flags = makeWindowFlagsArray(count);
417 if (count == 0) {
418 WMFreeArray(panel->windows);
419 wfree(panel);
420 return NULL;
423 width = icon_tile_size* count;
424 iconsThatFitCount = count;
426 if (width > (int)rect.size.width) {
427 iconsThatFitCount = (rect.size.width - SCREEN_BORDER_SPACING) / icon_tile_size;
428 width = iconsThatFitCount * icon_tile_size;
431 panel->visibleCount = iconsThatFitCount;
433 if (!wPreferences.swtileImage)
434 return panel;
436 height = label_height + icon_tile_size;
438 panel->tileTmp = RCreateImage(icon_tile_size, icon_tile_size, 1);
439 panel->tile = getTile();
440 if (panel->tile && wPreferences.swbackImage[8])
441 panel->bg = createBackImage(width + 2 * border_space, height + 2 * border_space);
443 if (!panel->tileTmp || !panel->tile) {
444 if (panel->bg)
445 RReleaseImage(panel->bg);
446 panel->bg = NULL;
447 if (panel->tile)
448 RReleaseImage(panel->tile);
449 panel->tile = NULL;
450 if (panel->tileTmp)
451 RReleaseImage(panel->tileTmp);
452 panel->tileTmp = NULL;
455 panel->white = WMWhiteColor(scr->wmscreen);
456 panel->gray = WMGrayColor(scr->wmscreen);
457 panel->font = WMBoldSystemFontOfSize(scr->wmscreen, WMScaleY(12));
458 panel->icons = WMCreateArray(count);
459 panel->images = WMCreateArray(count);
461 panel->win = WMCreateWindow(scr->wmscreen, "");
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);
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);
477 WMReleaseColor(darkGray);
478 height += 5;
481 WMResizeWidget(panel->win, width + 2 * border_space, height + 2 * border_space);
483 viewport = WMCreateFrame(panel->win);
484 WMResizeWidget(viewport, width, icon_tile_size);
485 WMMoveWidget(viewport, border_space, border_space);
486 WMSetFrameRelief(viewport, WRFlat);
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);
493 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
494 addIconForWindow(panel, panel->iconBox, wwin, i * icon_tile_size, 0);
497 WMMapSubwidgets(panel->win);
498 WMRealizeWidget(panel->win);
500 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
501 changeImage(panel, i, 0, False, True);
504 if (panel->bg) {
505 Pixmap pixmap, mask;
507 RConvertImageMask(scr->rcontext, panel->bg, &pixmap, &mask, 250);
509 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(panel->win), pixmap);
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);
518 if (mask)
519 XFreePixmap(dpy, mask);
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);
529 panel->current = WMGetFirstInArray(panel->windows, curwin);
530 if (panel->current >= 0)
531 changeImage(panel, panel->current, 1, False, False);
533 WMMapWidget(panel->win);
535 return panel;
538 void wSwitchPanelDestroy(WSwitchPanel *panel)
540 int i;
541 RImage *image;
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;
551 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
552 WMUnmapWidget(panel->win);
554 ev.xclient.data.l[0] = False;
555 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
558 if (panel->images) {
559 WM_ITERATE_ARRAY(panel->images, image, i) {
560 if (image)
561 RReleaseImage(image);
563 WMFreeArray(panel->images);
566 if (panel->win)
567 WMDestroyWidget(panel->win);
569 if (panel->icons)
570 WMFreeArray(panel->icons);
572 if (panel->flags)
573 WMFreeArray(panel->flags);
575 WMFreeArray(panel->windows);
577 if (panel->tile)
578 RReleaseImage(panel->tile);
580 if (panel->tileTmp)
581 RReleaseImage(panel->tileTmp);
583 if (panel->bg)
584 RReleaseImage(panel->bg);
586 if (panel->font)
587 WMReleaseFont(panel->font);
589 if (panel->white)
590 WMReleaseColor(panel->white);
592 if (panel->gray)
593 WMReleaseColor(panel->gray);
595 wfree(panel);
598 WWindow *wSwitchPanelSelectNext(WSwitchPanel *panel, int back, int ignore_minimized, Bool class_only)
600 WWindow *wwin, *curwin, *tmpwin;
601 int count = WMGetArrayItemCount(panel->windows);
602 int orig = panel->current;
603 int i;
604 Bool dim = False;
606 if (count == 0)
607 return NULL;
609 if (!wPreferences.cycle_ignore_minimized)
610 ignore_minimized = False;
612 if (ignore_minimized && canReceiveFocus(WMGetFromArray(panel->windows, (count + panel->current) % count)) < 0)
613 ignore_minimized = False;
615 curwin = WMGetFromArray(panel->windows, orig);
616 do {
617 do {
618 if (back)
619 panel->current--;
620 else
621 panel->current++;
623 panel->current = (count + panel->current) % count;
624 wwin = WMGetFromArray(panel->windows, panel->current);
626 if (!class_only)
627 break;
628 if (panel->current == orig)
629 break;
630 } while (!sameWindowClass(wwin, curwin));
631 } while (ignore_minimized && panel->current != orig && canReceiveFocus(wwin) < 0);
633 WM_ITERATE_ARRAY(panel->windows, tmpwin, i) {
634 if (i == panel->current)
635 continue;
636 if (!class_only || sameWindowClass(tmpwin, curwin))
637 changeImage(panel, i, 0, False, False);
638 else {
639 if (i == orig)
640 dim = True;
641 changeImage(panel, i, 0, True, False);
646 if (panel->current < panel->firstVisible)
647 scrollIcons(panel, panel->current - panel->firstVisible);
648 else if (panel->current - panel->firstVisible >= panel->visibleCount)
649 scrollIcons(panel, panel->current - panel->firstVisible - panel->visibleCount + 1);
651 if (panel->win) {
652 drawTitle(panel, panel->current, wwin->frame->title);
653 if (panel->current != orig)
654 changeImage(panel, orig, 0, dim, False);
655 changeImage(panel, panel->current, 1, False, False);
658 return wwin;
661 WWindow *wSwitchPanelSelectFirst(WSwitchPanel *panel, int back)
663 WWindow *wwin;
664 int count = WMGetArrayItemCount(panel->windows);
665 char *title;
666 int i;
668 if (count == 0)
669 return NULL;
671 if (back) {
672 panel->current = count - 1;
673 scrollIcons(panel, count);
674 } else {
675 panel->current = 0;
676 scrollIcons(panel, -count);
679 wwin = WMGetFromArray(panel->windows, panel->current);
680 title = wwin->frame->title;
682 if (panel->win) {
683 for (WMArrayFirst(panel->windows, &(i)); (i) != WANotFound; WMArrayNext(panel->windows, &(i)))
684 changeImage(panel, i, i == panel->current, False, False);
686 drawTitle(panel, panel->current, title);
689 return wwin;
692 WWindow *wSwitchPanelHandleEvent(WSwitchPanel *panel, XEvent *event)
694 WMFrame *icon;
695 int i;
696 int focus = -1;
698 if (!panel->win)
699 return NULL;
701 if (event->type == MotionNotify) {
702 WM_ITERATE_ARRAY(panel->icons, icon, i) {
703 if (WMWidgetXID(icon) == event->xmotion.window) {
704 focus = i;
705 break;
710 if (focus >= 0 && panel->current != focus) {
711 WWindow *wwin;
713 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
714 changeImage(panel, i, i == focus, False, False);
716 panel->current = focus;
718 wwin = WMGetFromArray(panel->windows, focus);
720 drawTitle(panel, panel->current, wwin->frame->title);
722 return wwin;
725 return NULL;
728 Window wSwitchPanelGetWindow(WSwitchPanel *swpanel)
730 if (!swpanel->win)
731 return None;
733 return WMWidgetXID(swpanel->win);