Added some comments and spaces in Git's ignore file
[wmaker-crm.git] / src / switchpanel.c
blob7e3f2f3cfce611a51a615d8d57d21fbe969ba349
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 <stdlib.h>
24 #include <string.h>
25 #include <sys/time.h>
27 #include "WindowMaker.h"
28 #include "screen.h"
29 #include "framewin.h"
30 #include "window.h"
31 #include "defaults.h"
32 #include "switchpanel.h"
33 #include "funcs.h"
34 #include "xinerama.h"
36 extern Atom _XA_WM_IGNORE_FOCUS_EVENTS;
38 #ifdef SHAPE
39 #include <X11/extensions/shape.h>
41 extern Bool wShapeSupported;
42 #endif
44 struct SwitchPanel {
45 WScreen *scr;
46 WMWindow *win;
47 WMFrame *iconBox;
49 WMArray *icons;
50 WMArray *images;
51 WMArray *windows;
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 extern WPreferences wPreferences;
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 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 void changeImage(WSwitchPanel *panel, int idecks, int selected)
99 WMFrame *icon = WMGetFromArray(panel->icons, idecks);
100 RImage *image = WMGetFromArray(panel->images, idecks);
102 if (!panel->bg && !panel->tile && !selected)
103 WMSetFrameRelief(icon, WRFlat);
105 if (image && icon) {
106 RImage *back;
107 int opaq = 255;
108 RImage *tile;
109 WMPoint pos;
110 Pixmap p;
112 if (canReceiveFocus(WMGetFromArray(panel->windows, idecks)) < 0)
113 opaq = 50;
115 pos = WMGetViewPosition(WMWidgetView(icon));
116 back = panel->tileTmp;
117 if (panel->bg) {
118 RCopyArea(back, panel->bg,
119 BORDER_SPACE + pos.x - panel->firstVisible * ICON_TILE_SIZE,
120 BORDER_SPACE + pos.y, back->width, back->height, 0, 0);
121 } else {
122 RColor color;
123 WMScreen *wscr = WMWidgetScreen(icon);
124 color.red = 255;
125 color.red = WMRedComponentOfColor(WMGrayColor(wscr)) >> 8;
126 color.green = WMGreenComponentOfColor(WMGrayColor(wscr)) >> 8;
127 color.blue = WMBlueComponentOfColor(WMGrayColor(wscr)) >> 8;
128 RFillImage(back, &color);
131 if (selected) {
132 tile = panel->tile;
133 RCombineArea(back, tile, 0, 0, tile->width, tile->height,
134 (back->width - tile->width) / 2, (back->height - tile->height) / 2);
137 RCombineAreaWithOpaqueness(back, image, 0, 0, image->width, image->height,
138 (back->width - image->width) / 2, (back->height - image->height) / 2,
139 opaq);
141 RConvertImage(panel->scr->rcontext, back, &p);
142 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(icon), p);
143 XClearWindow(dpy, WMWidgetXID(icon));
144 XFreePixmap(dpy, p);
147 if (!panel->bg && !panel->tile && selected)
148 WMSetFrameRelief(icon, WRSimple);
151 static RImage *scaleDownIfNeeded(RImage *image)
153 if (image && ((image->width - ICON_SIZE) > 2 || (image->height - ICON_SIZE) > 2)) {
154 RImage *nimage;
155 nimage = RScaleImage(image, ICON_SIZE, (image->height * ICON_SIZE / image->width));
156 RReleaseImage(image);
157 image = nimage;
160 return image;
163 static void addIconForWindow(WSwitchPanel *panel, WMWidget *parent, WWindow *wwin, int x, int y)
165 WMFrame *icon = WMCreateFrame(parent);
166 RImage *image = NULL;
168 WMSetFrameRelief(icon, WRFlat);
169 WMResizeWidget(icon, ICON_TILE_SIZE, ICON_TILE_SIZE);
170 WMMoveWidget(icon, x, y);
172 if (!WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
173 image = RRetainImage(wwin->net_icon_image);
175 /* wDefaultGetImage() includes the default icon image */
176 if (!image)
177 image = wDefaultGetImage(panel->scr, wwin->wm_instance, wwin->wm_class, ICON_TILE_SIZE);
179 image = scaleDownIfNeeded(image);
181 WMAddToArray(panel->images, image);
182 WMAddToArray(panel->icons, icon);
185 static void scrollIcons(WSwitchPanel *panel, int delta)
187 int nfirst = panel->firstVisible + delta;
188 int i;
189 int count = WMGetArrayItemCount(panel->windows);
191 if (count <= panel->visibleCount)
192 return;
194 if (nfirst < 0)
195 nfirst = 0;
196 else if (nfirst >= count - panel->visibleCount)
197 nfirst = count - panel->visibleCount;
199 if (nfirst == panel->firstVisible)
200 return;
202 WMMoveWidget(panel->iconBox, -nfirst * ICON_TILE_SIZE, 0);
204 panel->firstVisible = nfirst;
206 for (i = panel->firstVisible; i < panel->firstVisible + panel->visibleCount; i++)
207 changeImage(panel, i, i == panel->current);
211 * 0 1 2
212 * 3 4 5
213 * 6 7 8
215 static RImage *assemblePuzzleImage(RImage **images, int width, int height)
217 RImage *img = RCreateImage(width, height, 1);
218 RImage *tmp;
219 int tw, th;
220 RColor color;
221 if (!img)
222 return NULL;
224 color.red = 0;
225 color.green = 0;
226 color.blue = 0;
227 color.alpha = 255;
229 RFillImage(img, &color);
231 tw = width - images[0]->width - images[2]->width;
232 th = height - images[0]->height - images[6]->height;
234 if (tw <= 0 || th <= 0)
235 return NULL;
237 /* top */
238 if (tw > 0) {
239 tmp = RSmoothScaleImage(images[1], tw, images[1]->height);
240 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, 0);
241 RReleaseImage(tmp);
243 /* bottom */
244 if (tw > 0) {
245 tmp = RSmoothScaleImage(images[7], tw, images[7]->height);
246 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[6]->width, height - images[6]->height);
247 RReleaseImage(tmp);
249 /* left */
250 if (th > 0) {
251 tmp = RSmoothScaleImage(images[3], images[3]->width, th);
252 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, 0, images[0]->height);
253 RReleaseImage(tmp);
255 /* right */
256 if (th > 0) {
257 tmp = RSmoothScaleImage(images[5], images[5]->width, th);
258 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, width - images[5]->width, images[2]->height);
259 RReleaseImage(tmp);
261 /* center */
262 if (tw > 0 && th > 0) {
263 tmp = RSmoothScaleImage(images[4], tw, th);
264 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, images[0]->height);
265 RReleaseImage(tmp);
268 /* corners */
269 RCopyArea(img, images[0], 0, 0, images[0]->width, images[0]->height, 0, 0);
270 RCopyArea(img, images[2], 0, 0, images[2]->width, images[2]->height, width - images[2]->width, 0);
271 RCopyArea(img, images[6], 0, 0, images[6]->width, images[6]->height, 0, height - images[6]->height);
272 RCopyArea(img, images[8], 0, 0, images[8]->width, images[8]->height,
273 width - images[8]->width, height - images[8]->height);
275 return img;
278 static RImage *createBackImage(int width, int height)
280 return assemblePuzzleImage(wPreferences.swbackImage, width, height);
283 static RImage *getTile(void)
285 RImage *stile;
287 if (!wPreferences.swtileImage)
288 return NULL;
290 stile = RScaleImage(wPreferences.swtileImage, ICON_TILE_SIZE, ICON_TILE_SIZE);
291 if (!stile)
292 return wPreferences.swtileImage;
294 return stile;
297 static void drawTitle(WSwitchPanel *panel, int idecks, char *title)
299 char *ntitle;
300 int width = WMWidgetWidth(panel->win);
301 int x;
303 if (title)
304 ntitle = ShrinkString(panel->font, title, width - 2 * BORDER_SPACE);
305 else
306 ntitle = NULL;
308 if (panel->bg) {
309 if (ntitle) {
310 if (strcmp(ntitle, title) != 0) {
311 x = BORDER_SPACE;
312 } else {
313 int w = WMWidthOfString(panel->font, ntitle, strlen(ntitle));
315 x = BORDER_SPACE + (idecks - panel->firstVisible) * ICON_TILE_SIZE +
316 ICON_TILE_SIZE / 2 - w / 2;
317 if (x < BORDER_SPACE)
318 x = BORDER_SPACE;
319 else if (x + w > width - BORDER_SPACE)
320 x = width - BORDER_SPACE - w;
324 XClearWindow(dpy, WMWidgetXID(panel->win));
325 if (ntitle)
326 WMDrawString(panel->scr->wmscreen,
327 WMWidgetXID(panel->win),
328 panel->white, panel->font,
330 WMWidgetHeight(panel->win) - BORDER_SPACE - LABEL_HEIGHT +
331 WMFontHeight(panel->font) / 2, ntitle, strlen(ntitle));
332 } else {
333 if (ntitle)
334 WMSetLabelText(panel->label, ntitle);
337 if (ntitle)
338 free(ntitle);
341 static WMArray *makeWindowListArray(WWindow *curwin, int include_unmapped, Bool class_only)
343 WMArray *windows = WMCreateArray(10);
344 int fl;
345 WWindow *wwin;
347 for (fl = 0; fl < 2; fl++) {
348 for (wwin = curwin; wwin; wwin = wwin->prev) {
349 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
350 (wwin->flags.mapped || include_unmapped)) {
351 if (class_only) {
352 if (!wwin->wm_class || !curwin->wm_class)
353 continue;
354 if (strcmp(wwin->wm_class, curwin->wm_class))
355 continue;
358 if (!WFLAGP(wwin, skip_switchpanel))
359 WMAddToArray(windows, wwin);
362 wwin = curwin;
363 /* start over from the beginning of the list */
364 while (wwin->next)
365 wwin = wwin->next;
367 for (wwin = curwin; wwin && wwin != curwin; wwin = wwin->prev) {
368 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
369 (wwin->flags.mapped || include_unmapped)) {
370 if (class_only) {
371 if (!wwin->wm_class || !curwin->wm_class)
372 continue;
373 if (strcmp(wwin->wm_class, curwin->wm_class))
374 continue;
377 if (!WFLAGP(wwin, skip_switchpanel))
378 WMAddToArray(windows, wwin);
383 return windows;
386 WSwitchPanel *wInitSwitchPanel(WScreen *scr, WWindow *curwin, Bool class_only)
388 WWindow *wwin;
389 WSwitchPanel *panel = wmalloc(sizeof(WSwitchPanel));
390 WMFrame *viewport;
391 int i, width, height, iconsThatFitCount, count;
392 WMRect rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
394 panel->scr = scr;
395 panel->windows = makeWindowListArray(curwin, wPreferences.swtileImage != NULL, class_only);
396 count = WMGetArrayItemCount(panel->windows);
398 if (count == 0) {
399 WMFreeArray(panel->windows);
400 wfree(panel);
401 return NULL;
404 width = ICON_TILE_SIZE * count;
405 iconsThatFitCount = count;
407 if (width > rect.size.width) {
408 iconsThatFitCount = (rect.size.width - SCREEN_BORDER_SPACING) / ICON_TILE_SIZE;
409 width = iconsThatFitCount * ICON_TILE_SIZE;
412 panel->visibleCount = iconsThatFitCount;
414 if (!wPreferences.swtileImage)
415 return panel;
417 height = LABEL_HEIGHT + ICON_TILE_SIZE;
419 panel->tileTmp = RCreateImage(ICON_TILE_SIZE, ICON_TILE_SIZE, 1);
420 panel->tile = getTile();
421 if (panel->tile && wPreferences.swbackImage[8])
422 panel->bg = createBackImage(width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
424 if (!panel->tileTmp || !panel->tile) {
425 if (panel->bg)
426 RReleaseImage(panel->bg);
427 panel->bg = NULL;
428 if (panel->tile)
429 RReleaseImage(panel->tile);
430 panel->tile = NULL;
431 if (panel->tileTmp)
432 RReleaseImage(panel->tileTmp);
433 panel->tileTmp = NULL;
436 panel->white = WMWhiteColor(scr->wmscreen);
437 panel->font = WMBoldSystemFontOfSize(scr->wmscreen, 12);
438 panel->icons = WMCreateArray(count);
439 panel->images = WMCreateArray(count);
441 panel->win = WMCreateWindow(scr->wmscreen, "");
443 if (!panel->bg) {
444 WMFrame *frame = WMCreateFrame(panel->win);
445 WMColor *darkGray = WMDarkGrayColor(scr->wmscreen);
446 WMSetFrameRelief(frame, WRSimple);
447 WMSetViewExpandsToParent(WMWidgetView(frame), 0, 0, 0, 0);
449 panel->label = WMCreateLabel(panel->win);
450 WMResizeWidget(panel->label, width, LABEL_HEIGHT);
451 WMMoveWidget(panel->label, BORDER_SPACE, BORDER_SPACE + ICON_TILE_SIZE + 5);
452 WMSetLabelRelief(panel->label, WRSimple);
453 WMSetWidgetBackgroundColor(panel->label, darkGray);
454 WMSetLabelFont(panel->label, panel->font);
455 WMSetLabelTextColor(panel->label, panel->white);
457 WMReleaseColor(darkGray);
458 height += 5;
461 WMResizeWidget(panel->win, width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
463 viewport = WMCreateFrame(panel->win);
464 WMResizeWidget(viewport, width, ICON_TILE_SIZE);
465 WMMoveWidget(viewport, BORDER_SPACE, BORDER_SPACE);
466 WMSetFrameRelief(viewport, WRFlat);
468 panel->iconBox = WMCreateFrame(viewport);
469 WMMoveWidget(panel->iconBox, 0, 0);
470 WMResizeWidget(panel->iconBox, ICON_TILE_SIZE * count, ICON_TILE_SIZE);
471 WMSetFrameRelief(panel->iconBox, WRFlat);
473 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
474 addIconForWindow(panel, panel->iconBox, wwin, i * ICON_TILE_SIZE, 0);
477 WMMapSubwidgets(panel->win);
478 WMRealizeWidget(panel->win);
480 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
481 changeImage(panel, i, 0);
484 if (panel->bg) {
485 Pixmap pixmap, mask;
487 RConvertImageMask(scr->rcontext, panel->bg, &pixmap, &mask, 250);
489 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(panel->win), pixmap);
491 #ifdef SHAPE
492 if (mask && wShapeSupported)
493 XShapeCombineMask(dpy, WMWidgetXID(panel->win), ShapeBounding, 0, 0, mask, ShapeSet);
494 #endif
495 if (pixmap)
496 XFreePixmap(dpy, pixmap);
498 if (mask)
499 XFreePixmap(dpy, mask);
503 WMPoint center;
504 center = wGetPointToCenterRectInHead(scr, wGetHeadForPointerLocation(scr),
505 width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
506 WMMoveWidget(panel->win, center.x, center.y);
509 panel->current = WMGetFirstInArray(panel->windows, curwin);
510 if (panel->current >= 0)
511 changeImage(panel, panel->current, 1);
513 WMMapWidget(panel->win);
515 return panel;
518 void wSwitchPanelDestroy(WSwitchPanel *panel)
520 int i;
521 RImage *image;
523 if (panel->win) {
524 Window info_win = panel->scr->info_window;
525 XEvent ev;
526 ev.xclient.type = ClientMessage;
527 ev.xclient.message_type = _XA_WM_IGNORE_FOCUS_EVENTS;
528 ev.xclient.format = 32;
529 ev.xclient.data.l[0] = True;
531 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
532 WMUnmapWidget(panel->win);
534 ev.xclient.data.l[0] = False;
535 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
538 if (panel->images) {
539 WM_ITERATE_ARRAY(panel->images, image, i) {
540 if (image)
541 RReleaseImage(image);
543 WMFreeArray(panel->images);
546 if (panel->win)
547 WMDestroyWidget(panel->win);
549 if (panel->icons)
550 WMFreeArray(panel->icons);
552 WMFreeArray(panel->windows);
554 if (panel->tile)
555 RReleaseImage(panel->tile);
557 if (panel->tileTmp)
558 RReleaseImage(panel->tileTmp);
560 if (panel->bg)
561 RReleaseImage(panel->bg);
563 if (panel->font)
564 WMReleaseFont(panel->font);
566 if (panel->white)
567 WMReleaseColor(panel->white);
569 wfree(panel);
572 WWindow *wSwitchPanelSelectNext(WSwitchPanel *panel, int back)
574 WWindow *wwin;
575 int count = WMGetArrayItemCount(panel->windows);
577 if (count == 0)
578 return NULL;
580 if (panel->win)
581 changeImage(panel, panel->current, 0);
583 if (back)
584 panel->current--;
585 else
586 panel->current++;
588 wwin = WMGetFromArray(panel->windows, (count + panel->current) % count);
590 if (back) {
591 if (panel->current < 0)
592 scrollIcons(panel, count);
593 else if (panel->current < panel->firstVisible)
594 scrollIcons(panel, -1);
595 } else {
596 if (panel->current >= count)
597 scrollIcons(panel, -count);
598 else if (panel->current - panel->firstVisible >= panel->visibleCount)
599 scrollIcons(panel, 1);
602 panel->current = (count + panel->current) % count;
604 if (panel->win) {
605 drawTitle(panel, panel->current, wwin->frame->title);
607 changeImage(panel, panel->current, 1);
610 return wwin;
613 WWindow *wSwitchPanelSelectFirst(WSwitchPanel *panel, int back)
615 WWindow *wwin;
616 int count = WMGetArrayItemCount(panel->windows);
618 if (count == 0)
619 return NULL;
621 if (panel->win)
622 changeImage(panel, panel->current, 0);
624 if (back) {
625 panel->current = count - 1;
626 scrollIcons(panel, count);
627 } else {
628 panel->current = 0;
629 scrollIcons(panel, -count);
632 wwin = WMGetFromArray(panel->windows, panel->current);
634 if (panel->win) {
635 drawTitle(panel, panel->current, wwin->frame->title);
636 changeImage(panel, panel->current, 1);
639 return wwin;
642 WWindow *wSwitchPanelHandleEvent(WSwitchPanel *panel, XEvent *event)
644 WMFrame *icon;
645 int i;
646 int focus = -1;
648 if (!panel->win)
649 return NULL;
651 if (event->type == MotionNotify) {
652 WM_ITERATE_ARRAY(panel->icons, icon, i) {
653 if (WMWidgetXID(icon) == event->xmotion.window) {
654 focus = i;
655 break;
660 if (focus >= 0 && panel->current != focus) {
661 WWindow *wwin;
663 changeImage(panel, panel->current, 0);
664 changeImage(panel, focus, 1);
665 panel->current = focus;
667 wwin = WMGetFromArray(panel->windows, focus);
669 drawTitle(panel, panel->current, wwin->frame->title);
671 return wwin;
674 return NULL;
677 Window wSwitchPanelGetWindow(WSwitchPanel *swpanel)
679 if (!swpanel->win)
680 return None;
682 return WMWidgetXID(swpanel->win);