wmaker: Removed variable for X Context that is not used
[wmaker-crm.git] / src / switchpanel.c
blob424755d1e3bd1d07e34e337dcbf8a9052cb05f3f
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 SHAPE
40 #include <X11/extensions/shape.h>
42 extern Bool wShapeSupported;
43 #endif
45 struct SwitchPanel {
46 WScreen *scr;
47 WMWindow *win;
48 WMFrame *iconBox;
50 WMArray *icons;
51 WMArray *images;
52 WMArray *windows;
53 WMArray *flags;
54 RImage *bg;
55 int current;
56 int firstVisible;
57 int visibleCount;
59 WMLabel *label;
61 RImage *tileTmp;
62 RImage *tile;
64 WMFont *font;
65 WMColor *white;
68 #define BORDER_SPACE 10
69 #define ICON_SIZE 48
70 #define ICON_TILE_SIZE 64
71 #define LABEL_HEIGHT 25
72 #define SCREEN_BORDER_SPACING 2*20
73 #define SCROLL_STEPS (ICON_TILE_SIZE/2)
75 #define ICON_SELECTED (1<<1)
76 #define ICON_DIM (1<<2)
78 static int canReceiveFocus(WWindow *wwin)
80 if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
81 return 0;
83 if (wPreferences.cycle_active_head_only &&
84 wGetHeadForWindow(wwin) != wGetHeadForPointerLocation(wwin->screen_ptr))
85 return 0;
87 if (WFLAGP(wwin, no_focusable))
88 return 0;
90 if (!wwin->flags.mapped) {
91 if (!wwin->flags.shaded && !wwin->flags.miniaturized && !wwin->flags.hidden)
92 return 0;
93 else
94 return -1;
97 return 1;
100 static Bool sameWindowClass(WWindow *wwin, WWindow *curwin)
102 if (!wwin->wm_class || !curwin->wm_class)
103 return False;
104 if (strcmp(wwin->wm_class, curwin->wm_class))
105 return False;
107 return True;
110 static void changeImage(WSwitchPanel *panel, int idecks, int selected, Bool dim, Bool force)
112 WMFrame *icon = NULL;
113 RImage *image = NULL;
114 char flags = 0;
115 char desired = 0;
117 /* This whole function is a no-op if we aren't drawing the panel */
118 if (!wPreferences.swtileImage)
119 return;
121 icon = WMGetFromArray(panel->icons, idecks);
122 image = WMGetFromArray(panel->images, idecks);
123 flags = (char) (uintptr_t) WMGetFromArray(panel->flags, idecks);
125 if (selected)
126 desired |= ICON_SELECTED;
127 if (dim)
128 desired |= ICON_DIM;
130 if (flags == desired && !force)
131 return;
133 WMReplaceInArray(panel->flags, idecks, (void *) (uintptr_t) desired);
135 if (!panel->bg && !panel->tile && !selected)
136 WMSetFrameRelief(icon, WRFlat);
138 if (image && icon) {
139 RImage *back;
140 int opaq = (dim) ? 75 : 255;
141 RImage *tile;
142 WMPoint pos;
143 Pixmap p;
145 if (canReceiveFocus(WMGetFromArray(panel->windows, idecks)) < 0)
146 opaq = 50;
148 pos = WMGetViewPosition(WMWidgetView(icon));
149 back = panel->tileTmp;
150 if (panel->bg) {
151 RCopyArea(back, panel->bg,
152 BORDER_SPACE + pos.x - panel->firstVisible * ICON_TILE_SIZE,
153 BORDER_SPACE + pos.y, back->width, back->height, 0, 0);
154 } else {
155 RColor color;
156 WMScreen *wscr = WMWidgetScreen(icon);
157 color.red = 255;
158 color.red = WMRedComponentOfColor(WMGrayColor(wscr)) >> 8;
159 color.green = WMGreenComponentOfColor(WMGrayColor(wscr)) >> 8;
160 color.blue = WMBlueComponentOfColor(WMGrayColor(wscr)) >> 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 switchpanel 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 = ((char) (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 = RCreateImage(width, height, 1);
245 RImage *tmp;
246 int tw, th;
247 RColor color;
248 if (!img)
249 return NULL;
251 color.red = 0;
252 color.green = 0;
253 color.blue = 0;
254 color.alpha = 255;
256 RFillImage(img, &color);
258 tw = width - images[0]->width - images[2]->width;
259 th = height - images[0]->height - images[6]->height;
261 if (tw <= 0 || th <= 0)
262 return NULL;
264 /* top */
265 if (tw > 0) {
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 if (tw > 0) {
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);
276 /* left */
277 if (th > 0) {
278 tmp = RSmoothScaleImage(images[3], images[3]->width, th);
279 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, 0, images[0]->height);
280 RReleaseImage(tmp);
282 /* right */
283 if (th > 0) {
284 tmp = RSmoothScaleImage(images[5], images[5]->width, th);
285 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, width - images[5]->width, images[2]->height);
286 RReleaseImage(tmp);
288 /* center */
289 if (tw > 0 && th > 0) {
290 tmp = RSmoothScaleImage(images[4], tw, th);
291 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, images[0]->height);
292 RReleaseImage(tmp);
295 /* corners */
296 RCopyArea(img, images[0], 0, 0, images[0]->width, images[0]->height, 0, 0);
297 RCopyArea(img, images[2], 0, 0, images[2]->width, images[2]->height, width - images[2]->width, 0);
298 RCopyArea(img, images[6], 0, 0, images[6]->width, images[6]->height, 0, height - images[6]->height);
299 RCopyArea(img, images[8], 0, 0, images[8]->width, images[8]->height,
300 width - images[8]->width, height - images[8]->height);
302 return img;
305 static RImage *createBackImage(int width, int height)
307 return assemblePuzzleImage(wPreferences.swbackImage, width, height);
310 static RImage *getTile(void)
312 RImage *stile;
314 if (!wPreferences.swtileImage)
315 return NULL;
317 stile = RScaleImage(wPreferences.swtileImage, ICON_TILE_SIZE, ICON_TILE_SIZE);
318 if (!stile)
319 return wPreferences.swtileImage;
321 return stile;
324 static void drawTitle(WSwitchPanel *panel, int idecks, const char *title)
326 char *ntitle;
327 int width = WMWidgetWidth(panel->win);
328 int x;
330 if (title)
331 ntitle = ShrinkString(panel->font, title, width - 2 * BORDER_SPACE);
332 else
333 ntitle = NULL;
335 if (panel->bg) {
336 if (ntitle) {
337 if (strcmp(ntitle, title) != 0) {
338 x = BORDER_SPACE;
339 } else {
340 int w = WMWidthOfString(panel->font, ntitle, strlen(ntitle));
342 x = BORDER_SPACE + (idecks - panel->firstVisible) * ICON_TILE_SIZE +
343 ICON_TILE_SIZE / 2 - w / 2;
344 if (x < BORDER_SPACE)
345 x = BORDER_SPACE;
346 else if (x + w > width - BORDER_SPACE)
347 x = width - BORDER_SPACE - w;
351 XClearWindow(dpy, WMWidgetXID(panel->win));
352 if (ntitle)
353 WMDrawString(panel->scr->wmscreen,
354 WMWidgetXID(panel->win),
355 panel->white, panel->font,
357 WMWidgetHeight(panel->win) - BORDER_SPACE - LABEL_HEIGHT +
358 WMFontHeight(panel->font) / 2, ntitle, strlen(ntitle));
359 } else {
360 if (ntitle)
361 WMSetLabelText(panel->label, ntitle);
364 if (ntitle)
365 free(ntitle);
368 static WMArray *makeWindowListArray(WWindow *curwin, int include_unmapped, Bool class_only)
370 WMArray *windows = WMCreateArray(10);
371 int fl;
372 WWindow *wwin;
374 for (fl = 0; fl < 2; fl++) {
375 for (wwin = curwin; wwin; wwin = wwin->prev) {
376 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
377 (wwin->flags.mapped || include_unmapped)) {
378 if (class_only)
379 if (!sameWindowClass(wwin, curwin))
380 continue;
382 if (!WFLAGP(wwin, skip_switchpanel))
383 WMAddToArray(windows, wwin);
386 wwin = curwin;
387 /* start over from the beginning of the list */
388 while (wwin->next)
389 wwin = wwin->next;
391 for (wwin = curwin; wwin && wwin != curwin; wwin = wwin->prev) {
392 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
393 (wwin->flags.mapped || include_unmapped)) {
394 if (class_only)
395 if (!sameWindowClass(wwin, curwin))
396 continue;
397 if (!WFLAGP(wwin, skip_switchpanel))
398 WMAddToArray(windows, wwin);
403 return windows;
406 static WMArray *makeWindowFlagsArray(int count)
408 WMArray *flags = WMCreateArray(1);
409 int i;
411 for (i = 0; i < count; i++)
412 WMAddToArray(flags, (char) 0);
414 return flags;
417 WSwitchPanel *wInitSwitchPanel(WScreen *scr, WWindow *curwin, Bool class_only)
419 WWindow *wwin;
420 WSwitchPanel *panel = wmalloc(sizeof(WSwitchPanel));
421 WMFrame *viewport;
422 int i, width, height, iconsThatFitCount, count;
423 WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
425 panel->scr = scr;
426 panel->windows = makeWindowListArray(curwin, wPreferences.swtileImage != NULL, class_only);
427 count = WMGetArrayItemCount(panel->windows);
428 if (count)
429 panel->flags = makeWindowFlagsArray(count);
431 if (count == 0) {
432 WMFreeArray(panel->windows);
433 wfree(panel);
434 return NULL;
437 width = ICON_TILE_SIZE * count;
438 iconsThatFitCount = count;
440 if (width > rect.size.width) {
441 iconsThatFitCount = (rect.size.width - SCREEN_BORDER_SPACING) / ICON_TILE_SIZE;
442 width = iconsThatFitCount * ICON_TILE_SIZE;
445 panel->visibleCount = iconsThatFitCount;
447 if (!wPreferences.swtileImage)
448 return panel;
450 height = LABEL_HEIGHT + ICON_TILE_SIZE;
452 panel->tileTmp = RCreateImage(ICON_TILE_SIZE, ICON_TILE_SIZE, 1);
453 panel->tile = getTile();
454 if (panel->tile && wPreferences.swbackImage[8])
455 panel->bg = createBackImage(width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
457 if (!panel->tileTmp || !panel->tile) {
458 if (panel->bg)
459 RReleaseImage(panel->bg);
460 panel->bg = NULL;
461 if (panel->tile)
462 RReleaseImage(panel->tile);
463 panel->tile = NULL;
464 if (panel->tileTmp)
465 RReleaseImage(panel->tileTmp);
466 panel->tileTmp = NULL;
469 panel->white = WMWhiteColor(scr->wmscreen);
470 panel->font = WMBoldSystemFontOfSize(scr->wmscreen, 12);
471 panel->icons = WMCreateArray(count);
472 panel->images = WMCreateArray(count);
474 panel->win = WMCreateWindow(scr->wmscreen, "");
476 if (!panel->bg) {
477 WMFrame *frame = WMCreateFrame(panel->win);
478 WMColor *darkGray = WMDarkGrayColor(scr->wmscreen);
479 WMSetFrameRelief(frame, WRSimple);
480 WMSetViewExpandsToParent(WMWidgetView(frame), 0, 0, 0, 0);
482 panel->label = WMCreateLabel(panel->win);
483 WMResizeWidget(panel->label, width, LABEL_HEIGHT);
484 WMMoveWidget(panel->label, BORDER_SPACE, BORDER_SPACE + ICON_TILE_SIZE + 5);
485 WMSetLabelRelief(panel->label, WRSimple);
486 WMSetWidgetBackgroundColor(panel->label, darkGray);
487 WMSetLabelFont(panel->label, panel->font);
488 WMSetLabelTextColor(panel->label, panel->white);
490 WMReleaseColor(darkGray);
491 height += 5;
494 WMResizeWidget(panel->win, width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
496 viewport = WMCreateFrame(panel->win);
497 WMResizeWidget(viewport, width, ICON_TILE_SIZE);
498 WMMoveWidget(viewport, BORDER_SPACE, BORDER_SPACE);
499 WMSetFrameRelief(viewport, WRFlat);
501 panel->iconBox = WMCreateFrame(viewport);
502 WMMoveWidget(panel->iconBox, 0, 0);
503 WMResizeWidget(panel->iconBox, ICON_TILE_SIZE * count, ICON_TILE_SIZE);
504 WMSetFrameRelief(panel->iconBox, WRFlat);
506 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
507 addIconForWindow(panel, panel->iconBox, wwin, i * ICON_TILE_SIZE, 0);
510 WMMapSubwidgets(panel->win);
511 WMRealizeWidget(panel->win);
513 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
514 changeImage(panel, i, 0, False, True);
517 if (panel->bg) {
518 Pixmap pixmap, mask;
520 RConvertImageMask(scr->rcontext, panel->bg, &pixmap, &mask, 250);
522 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(panel->win), pixmap);
524 #ifdef SHAPE
525 if (mask && wShapeSupported)
526 XShapeCombineMask(dpy, WMWidgetXID(panel->win), ShapeBounding, 0, 0, mask, ShapeSet);
527 #endif
528 if (pixmap)
529 XFreePixmap(dpy, pixmap);
531 if (mask)
532 XFreePixmap(dpy, mask);
536 WMPoint center;
537 center = wGetPointToCenterRectInHead(scr, wGetHeadForPointerLocation(scr),
538 width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
539 WMMoveWidget(panel->win, center.x, center.y);
542 panel->current = WMGetFirstInArray(panel->windows, curwin);
543 if (panel->current >= 0)
544 changeImage(panel, panel->current, 1, False, False);
546 WMMapWidget(panel->win);
548 return panel;
551 void wSwitchPanelDestroy(WSwitchPanel *panel)
553 int i;
554 RImage *image;
556 if (panel->win) {
557 Window info_win = panel->scr->info_window;
558 XEvent ev;
559 ev.xclient.type = ClientMessage;
560 ev.xclient.message_type = w_global.atom.wm.ignore_focus_events;
561 ev.xclient.format = 32;
562 ev.xclient.data.l[0] = True;
564 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
565 WMUnmapWidget(panel->win);
567 ev.xclient.data.l[0] = False;
568 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
571 if (panel->images) {
572 WM_ITERATE_ARRAY(panel->images, image, i) {
573 if (image)
574 RReleaseImage(image);
576 WMFreeArray(panel->images);
579 if (panel->win)
580 WMDestroyWidget(panel->win);
582 if (panel->icons)
583 WMFreeArray(panel->icons);
585 if (panel->flags)
586 WMFreeArray(panel->flags);
588 WMFreeArray(panel->windows);
590 if (panel->tile)
591 RReleaseImage(panel->tile);
593 if (panel->tileTmp)
594 RReleaseImage(panel->tileTmp);
596 if (panel->bg)
597 RReleaseImage(panel->bg);
599 if (panel->font)
600 WMReleaseFont(panel->font);
602 if (panel->white)
603 WMReleaseColor(panel->white);
605 wfree(panel);
608 WWindow *wSwitchPanelSelectNext(WSwitchPanel *panel, int back, int ignore_minimized, Bool class_only)
610 WWindow *wwin, *curwin, *tmpwin;
611 int count = WMGetArrayItemCount(panel->windows);
612 int orig = panel->current;
613 int i;
614 Bool dim = False;
616 if (count == 0)
617 return NULL;
619 if (!wPreferences.cycle_ignore_minimized)
620 ignore_minimized = False;
622 if (ignore_minimized && canReceiveFocus(WMGetFromArray(panel->windows, (count + panel->current) % count)) < 0)
623 ignore_minimized = False;
625 curwin = WMGetFromArray(panel->windows, orig);
626 do {
627 do {
628 if (back)
629 panel->current--;
630 else
631 panel->current++;
633 panel->current= (count + panel->current) % count;
634 wwin = WMGetFromArray(panel->windows, panel->current);
636 if (!class_only)
637 break;
638 if (panel->current == orig)
639 break;
640 } while (!sameWindowClass(wwin, curwin));
641 } while (ignore_minimized && panel->current != orig && canReceiveFocus(wwin) < 0);
643 WM_ITERATE_ARRAY(panel->windows, tmpwin, i) {
644 if (i == panel->current)
645 continue;
646 if (!class_only || sameWindowClass(tmpwin, curwin))
647 changeImage(panel, i, 0, False, False);
648 else {
649 if (i == orig)
650 dim = True;
651 changeImage(panel, i, 0, True, False);
656 if (panel->current < panel->firstVisible)
657 scrollIcons(panel, panel->current - panel->firstVisible);
658 else if (panel->current - panel->firstVisible >= panel->visibleCount)
659 scrollIcons(panel, panel->current - panel->firstVisible - panel->visibleCount + 1);
661 if (panel->win) {
662 drawTitle(panel, panel->current, wwin->frame->title);
663 if (panel->current != orig)
664 changeImage(panel, orig, 0, dim, False);
665 changeImage(panel, panel->current, 1, False, False);
668 return wwin;
671 WWindow *wSwitchPanelSelectFirst(WSwitchPanel *panel, int back)
673 WWindow *wwin;
674 int count = WMGetArrayItemCount(panel->windows);
675 char *title;
676 int i;
678 if (count == 0)
679 return NULL;
681 if (back) {
682 panel->current = count - 1;
683 scrollIcons(panel, count);
684 } else {
685 panel->current = 0;
686 scrollIcons(panel, -count);
689 wwin = WMGetFromArray(panel->windows, panel->current);
690 title = wwin->frame->title;
692 if (panel->win) {
693 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
694 changeImage(panel, i, i == panel->current, False, False);
696 drawTitle(panel, panel->current, title);
699 return wwin;
702 WWindow *wSwitchPanelHandleEvent(WSwitchPanel *panel, XEvent *event)
704 WMFrame *icon;
705 int i;
706 int focus = -1;
708 if (!panel->win)
709 return NULL;
711 if (event->type == MotionNotify) {
712 WM_ITERATE_ARRAY(panel->icons, icon, i) {
713 if (WMWidgetXID(icon) == event->xmotion.window) {
714 focus = i;
715 break;
720 if (focus >= 0 && panel->current != focus) {
721 WWindow *wwin;
723 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
724 changeImage(panel, i, i == focus, False, False);
726 panel->current = focus;
728 wwin = WMGetFromArray(panel->windows, focus);
730 drawTitle(panel, panel->current, wwin->frame->title);
732 return wwin;
735 return NULL;
738 Window wSwitchPanelGetWindow(WSwitchPanel *swpanel)
740 if (!swpanel->win)
741 return None;
743 return WMWidgetXID(swpanel->win);