Kill redundant function
[wmaker-crm.git] / src / switchpanel.c
blob39797289b4fef923d081d1e9d1e10c657a48a379
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
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
19 * USA.
22 #include "wconfig.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 "window.h"
32 #include "defaults.h"
33 #include "switchpanel.h"
34 #include "funcs.h"
35 #include "xinerama.h"
36 #include "window.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 RImage *bg;
55 int current;
56 int firstVisible;
57 int visibleCount;
59 WMLabel *label;
61 RImage *defIcon;
63 RImage *tileTmp;
64 RImage *tile;
66 WMFont *font;
67 WMColor *white;
70 extern WPreferences wPreferences;
72 #define BORDER_SPACE 10
73 #define ICON_SIZE 48
74 #define ICON_TILE_SIZE 64
75 #define LABEL_HEIGHT 25
76 #define SCREEN_BORDER_SPACING 2*20
77 #define SCROLL_STEPS (ICON_TILE_SIZE/2)
79 static int canReceiveFocus(WWindow * wwin)
81 if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
82 return 0;
83 if (!wwin->flags.mapped) {
84 if (!wwin->flags.shaded && !wwin->flags.miniaturized && !wwin->flags.hidden)
85 return 0;
86 else
87 return -1;
89 if (WFLAGP(wwin, no_focusable))
90 return 0;
91 return 1;
94 static void changeImage(WSwitchPanel * panel, int idecks, int selected)
96 WMFrame *icon = WMGetFromArray(panel->icons, idecks);
97 RImage *image = WMGetFromArray(panel->images, idecks);
99 if (!panel->bg && !panel->tile) {
100 if (!selected)
101 WMSetFrameRelief(icon, WRFlat);
104 if (image && icon) {
105 RImage *back;
106 int opaq = 255;
107 RImage *tile;
108 WMPoint pos;
109 Pixmap p;
111 if (canReceiveFocus(WMGetFromArray(panel->windows, idecks)) < 0)
112 opaq = 50;
114 pos = WMGetViewPosition(WMWidgetView(icon));
115 back = panel->tileTmp;
116 if (panel->bg)
117 RCopyArea(back, panel->bg,
118 BORDER_SPACE + pos.x - panel->firstVisible * ICON_TILE_SIZE,
119 BORDER_SPACE + pos.y, back->width, back->height, 0, 0);
120 else {
121 RColor color;
122 WMScreen *wscr = WMWidgetScreen(icon);
123 color.red = 255;
124 color.red = WMRedComponentOfColor(WMGrayColor(wscr)) >> 8;
125 color.green = WMGreenComponentOfColor(WMGrayColor(wscr)) >> 8;
126 color.blue = WMBlueComponentOfColor(WMGrayColor(wscr)) >> 8;
127 RFillImage(back, &color);
129 if (selected) {
130 tile = panel->tile;
131 RCombineArea(back, tile, 0, 0, tile->width, tile->height,
132 (back->width - tile->width) / 2, (back->height - tile->height) / 2);
134 RCombineAreaWithOpaqueness(back, image, 0, 0, image->width, image->height,
135 (back->width - image->width) / 2, (back->height - image->height) / 2,
136 opaq);
138 RConvertImage(panel->scr->rcontext, back, &p);
139 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(icon), p);
140 XClearWindow(dpy, WMWidgetXID(icon));
141 XFreePixmap(dpy, p);
144 if (!panel->bg && !panel->tile) {
145 if (selected)
146 WMSetFrameRelief(icon, WRSimple);
150 static RImage *scaleDownIfNeeded(RImage * image)
152 if (image && ((image->width - ICON_SIZE) > 2 || (image->height - ICON_SIZE) > 2)) {
153 RImage *nimage;
154 nimage = RScaleImage(image, ICON_SIZE, (image->height * ICON_SIZE / image->width));
155 RReleaseImage(image);
156 image = nimage;
158 return image;
161 static void addIconForWindow(WSwitchPanel * panel, WMWidget * parent, WWindow * wwin, int x, int y)
163 WMFrame *icon = WMCreateFrame(parent);
164 RImage *image = NULL;
166 WMSetFrameRelief(icon, WRFlat);
167 WMResizeWidget(icon, ICON_TILE_SIZE, ICON_TILE_SIZE);
168 WMMoveWidget(icon, x, y);
170 if (!WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
171 image = RRetainImage(wwin->net_icon_image);
173 // Make this use a caching thing. When there are many windows,
174 // it's very likely that most of them are instances of the same thing,
175 // so caching them should get performance acceptable in these cases.
176 if (!image)
177 image = wDefaultGetImage(panel->scr, wwin->wm_instance, wwin->wm_class);
179 if (!image && !panel->defIcon) {
180 char *file = wDefaultGetIconFile(panel->scr, NULL, NULL, False);
181 if (file) {
182 char *path = FindImage(wPreferences.icon_path, file);
183 if (path) {
184 image = RLoadImage(panel->scr->rcontext, path, 0);
185 wfree(path);
188 if (image)
189 panel->defIcon = scaleDownIfNeeded(image);
190 image = NULL;
192 if (!image && panel->defIcon)
193 image = RRetainImage(panel->defIcon);
195 image = scaleDownIfNeeded(image);
197 WMAddToArray(panel->images, image);
198 WMAddToArray(panel->icons, icon);
201 static void scrollIcons(WSwitchPanel * panel, int delta)
203 int nfirst = panel->firstVisible + delta;
204 int i;
205 int count = WMGetArrayItemCount(panel->windows);
206 // int nx, ox;
207 // struct timeval tv1, tv2;
209 if (count <= panel->visibleCount)
210 return;
212 if (nfirst < 0)
213 nfirst = 0;
214 else if (nfirst >= count - panel->visibleCount)
215 nfirst = count - panel->visibleCount;
217 if (nfirst == panel->firstVisible)
218 return;
220 ox = -panel->firstVisible * ICON_TILE_SIZE;
221 nx = -nfirst * ICON_TILE_SIZE;
222 for (i= 0; i < SCROLL_STEPS; i++) {
223 unsigned int diff;
224 gettimeofday(&tv1, NULL);
225 WMMoveWidget(panel->iconBox, (nx*i + ox*(SCROLL_STEPS-i))/(SCROLL_STEPS-1), 0);
226 XSync(dpy, False);
227 gettimeofday(&tv2, NULL);
228 diff = (tv2.tv_sec-tv1.tv_sec)*10000+(tv2.tv_usec-tv1.tv_usec)/100;
229 if (diff < 200)
230 wusleep(300-diff);
233 WMMoveWidget(panel->iconBox, -nfirst * ICON_TILE_SIZE, 0);
235 panel->firstVisible = nfirst;
237 for (i = panel->firstVisible; i < panel->firstVisible + panel->visibleCount; i++) {
238 changeImage(panel, i, i == panel->current);
243 * 0 1 2
244 * 3 4 5
245 * 6 7 8
247 static RImage *assemblePuzzleImage(RImage ** images, int width, int height)
249 RImage *img = RCreateImage(width, height, 1);
250 RImage *tmp;
251 int tw, th;
252 RColor color;
253 if (!img)
254 return NULL;
256 color.red = 0;
257 color.green = 0;
258 color.blue = 0;
259 color.alpha = 255;
261 RFillImage(img, &color);
263 tw = width - images[0]->width - images[2]->width;
264 th = height - images[0]->height - images[6]->height;
266 if (tw <= 0 || th <= 0) {
267 //XXX
268 return NULL;
271 /* top */
272 if (tw > 0) {
273 tmp = RSmoothScaleImage(images[1], tw, images[1]->height);
274 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, 0);
275 RReleaseImage(tmp);
277 /* bottom */
278 if (tw > 0) {
279 tmp = RSmoothScaleImage(images[7], tw, images[7]->height);
280 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[6]->width, height - images[6]->height);
281 RReleaseImage(tmp);
283 /* left */
284 if (th > 0) {
285 tmp = RSmoothScaleImage(images[3], images[3]->width, th);
286 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, 0, images[0]->height);
287 RReleaseImage(tmp);
289 /* right */
290 if (th > 0) {
291 tmp = RSmoothScaleImage(images[5], images[5]->width, th);
292 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, width - images[5]->width, images[2]->height);
293 RReleaseImage(tmp);
295 /* center */
296 if (tw > 0 && th > 0) {
297 tmp = RSmoothScaleImage(images[4], tw, th);
298 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, images[0]->height);
299 RReleaseImage(tmp);
302 /* corners */
303 RCopyArea(img, images[0], 0, 0, images[0]->width, images[0]->height, 0, 0);
305 RCopyArea(img, images[2], 0, 0, images[2]->width, images[2]->height, width - images[2]->width, 0);
307 RCopyArea(img, images[6], 0, 0, images[6]->width, images[6]->height, 0, height - images[6]->height);
309 RCopyArea(img, images[8], 0, 0, images[8]->width, images[8]->height,
310 width - images[8]->width, height - images[8]->height);
312 return img;
315 static RImage *createBackImage(int width, int height)
317 return assemblePuzzleImage(wPreferences.swbackImage, width, height);
320 static RImage *getTile(void)
322 RImage *stile;
324 if (!wPreferences.swtileImage)
325 return NULL;
327 stile = RScaleImage(wPreferences.swtileImage, ICON_TILE_SIZE, ICON_TILE_SIZE);
328 if (!stile)
329 return wPreferences.swtileImage;
331 return stile;
334 static void drawTitle(WSwitchPanel * panel, int idecks, char *title)
336 char *ntitle;
337 int width = WMWidgetWidth(panel->win);
338 int x;
340 if (title)
341 ntitle = ShrinkString(panel->font, title, width - 2 * BORDER_SPACE);
342 else
343 ntitle = NULL;
345 if (panel->bg) {
346 if (ntitle) {
347 if (strcmp(ntitle, title) != 0)
348 x = BORDER_SPACE;
349 else {
350 int w = WMWidthOfString(panel->font, ntitle, strlen(ntitle));
352 x = BORDER_SPACE + (idecks - panel->firstVisible) * ICON_TILE_SIZE +
353 ICON_TILE_SIZE / 2 - w / 2;
354 if (x < BORDER_SPACE)
355 x = BORDER_SPACE;
356 else if (x + w > width - BORDER_SPACE)
357 x = width - BORDER_SPACE - w;
360 XClearWindow(dpy, WMWidgetXID(panel->win));
361 if (ntitle)
362 WMDrawString(panel->scr->wmscreen,
363 WMWidgetXID(panel->win),
364 panel->white, panel->font,
366 WMWidgetHeight(panel->win) - BORDER_SPACE - LABEL_HEIGHT +
367 WMFontHeight(panel->font) / 2, ntitle, strlen(ntitle));
368 } else {
369 if (ntitle)
370 WMSetLabelText(panel->label, ntitle);
372 if (ntitle)
373 free(ntitle);
376 static WMArray *makeWindowListArray(WWindow *curwin, int include_unmapped, Bool class_only)
378 WMArray *windows = WMCreateArray(10);
379 int fl;
380 WWindow *wwin;
382 for (fl = 0; fl < 2; fl++) {
383 for (wwin = curwin; wwin; wwin = wwin->prev) {
384 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
385 (!WFLAGP(wwin, skip_window_list) || wwin->flags.internal_window) &&
386 (wwin->flags.mapped || include_unmapped)) {
387 if (class_only) {
388 if (!wwin->wm_class || !curwin->wm_class)
389 continue;
390 if (strcmp(wwin->wm_class, curwin->wm_class))
391 continue;
393 WMAddToArray(windows, wwin);
396 wwin = curwin;
397 /* start over from the beginning of the list */
398 while (wwin->next)
399 wwin = wwin->next;
401 for (wwin = curwin; wwin && wwin != curwin; wwin = wwin->prev) {
402 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
403 (!WFLAGP(wwin, skip_window_list) || wwin->flags.internal_window) &&
404 (wwin->flags.mapped || include_unmapped)) {
405 if (class_only) {
406 if (!wwin->wm_class || !curwin->wm_class)
407 continue;
408 if (strcmp(wwin->wm_class, curwin->wm_class))
409 continue;
411 WMAddToArray(windows, wwin);
416 return windows;
419 WSwitchPanel *wInitSwitchPanel(WScreen * scr, WWindow * curwin, Bool class_only)
421 WWindow *wwin;
422 WSwitchPanel *panel = wmalloc(sizeof(WSwitchPanel));
423 WMFrame *viewport;
424 int i;
425 int width, height;
426 int iconsThatFitCount;
427 int count;
428 WMRect rect;
429 rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
431 memset(panel, 0, sizeof(WSwitchPanel));
433 panel->scr = scr;
435 panel->windows = makeWindowListArray(curwin, wPreferences.swtileImage != 0, class_only);
436 count = WMGetArrayItemCount(panel->windows);
438 if (count == 0) {
439 WMFreeArray(panel->windows);
440 wfree(panel);
441 return NULL;
444 width = ICON_TILE_SIZE * count;
445 iconsThatFitCount = count;
447 if (width > rect.size.width) {
448 iconsThatFitCount = (rect.size.width - SCREEN_BORDER_SPACING) / ICON_TILE_SIZE;
449 width = iconsThatFitCount * ICON_TILE_SIZE;
452 panel->visibleCount = iconsThatFitCount;
454 if (!wPreferences.swtileImage)
455 return panel;
457 height = LABEL_HEIGHT + ICON_TILE_SIZE;
459 panel->tileTmp = RCreateImage(ICON_TILE_SIZE, ICON_TILE_SIZE, 1);
460 panel->tile = getTile();
461 if (panel->tile && wPreferences.swbackImage[8]) {
462 panel->bg = createBackImage(width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
464 if (!panel->tileTmp || !panel->tile) {
465 if (panel->bg)
466 RReleaseImage(panel->bg);
467 panel->bg = NULL;
468 if (panel->tile)
469 RReleaseImage(panel->tile);
470 panel->tile = NULL;
471 if (panel->tileTmp)
472 RReleaseImage(panel->tileTmp);
473 panel->tileTmp = NULL;
476 panel->white = WMWhiteColor(scr->wmscreen);
477 panel->font = WMBoldSystemFontOfSize(scr->wmscreen, 12);
478 panel->icons = WMCreateArray(count);
479 panel->images = WMCreateArray(count);
481 panel->win = WMCreateWindow(scr->wmscreen, "");
483 if (!panel->bg) {
484 WMFrame *frame = WMCreateFrame(panel->win);
485 WMColor *darkGray = WMDarkGrayColor(scr->wmscreen);
486 WMSetFrameRelief(frame, WRSimple);
487 WMSetViewExpandsToParent(WMWidgetView(frame), 0, 0, 0, 0);
489 panel->label = WMCreateLabel(panel->win);
490 WMResizeWidget(panel->label, width, LABEL_HEIGHT);
491 WMMoveWidget(panel->label, BORDER_SPACE, BORDER_SPACE + ICON_TILE_SIZE + 5);
492 WMSetLabelRelief(panel->label, WRSimple);
493 WMSetWidgetBackgroundColor(panel->label, darkGray);
494 WMSetLabelFont(panel->label, panel->font);
495 WMSetLabelTextColor(panel->label, panel->white);
497 WMReleaseColor(darkGray);
498 height += 5;
501 WMResizeWidget(panel->win, width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
503 viewport = WMCreateFrame(panel->win);
504 WMResizeWidget(viewport, width, ICON_TILE_SIZE);
505 WMMoveWidget(viewport, BORDER_SPACE, BORDER_SPACE);
506 WMSetFrameRelief(viewport, WRFlat);
508 panel->iconBox = WMCreateFrame(viewport);
509 WMMoveWidget(panel->iconBox, 0, 0);
510 WMResizeWidget(panel->iconBox, ICON_TILE_SIZE * count, ICON_TILE_SIZE);
511 WMSetFrameRelief(panel->iconBox, WRFlat);
513 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
514 addIconForWindow(panel, panel->iconBox, wwin, i * ICON_TILE_SIZE, 0);
517 WMMapSubwidgets(panel->win);
518 WMRealizeWidget(panel->win);
520 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
521 changeImage(panel, i, 0);
524 if (panel->bg) {
525 Pixmap pixmap, mask;
527 RConvertImageMask(scr->rcontext, panel->bg, &pixmap, &mask, 250);
529 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(panel->win), pixmap);
531 #ifdef SHAPE
532 if (mask && wShapeSupported)
533 XShapeCombineMask(dpy, WMWidgetXID(panel->win), ShapeBounding, 0, 0, mask, ShapeSet);
534 #endif
536 if (pixmap)
537 XFreePixmap(dpy, pixmap);
538 if (mask)
539 XFreePixmap(dpy, mask);
543 WMPoint center;
544 center = wGetPointToCenterRectInHead(scr, wGetHeadForPointerLocation(scr),
545 width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
546 WMMoveWidget(panel->win, center.x, center.y);
549 panel->current = WMGetFirstInArray(panel->windows, curwin);
550 if (panel->current >= 0)
551 changeImage(panel, panel->current, 1);
553 WMMapWidget(panel->win);
555 return panel;
558 void wSwitchPanelDestroy(WSwitchPanel * panel)
560 int i;
561 RImage *image;
563 if (panel->win) {
564 Window info_win = panel->scr->info_window;
565 XEvent ev;
566 ev.xclient.type = ClientMessage;
567 ev.xclient.message_type = _XA_WM_IGNORE_FOCUS_EVENTS;
568 ev.xclient.format = 32;
569 ev.xclient.data.l[0] = True;
571 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
572 WMUnmapWidget(panel->win);
574 ev.xclient.data.l[0] = False;
575 XSendEvent(dpy, info_win, True, EnterWindowMask, &ev);
578 if (panel->images) {
579 WM_ITERATE_ARRAY(panel->images, image, i) {
580 if (image)
581 RReleaseImage(image);
583 WMFreeArray(panel->images);
585 if (panel->win)
586 WMDestroyWidget(panel->win);
587 if (panel->icons)
588 WMFreeArray(panel->icons);
589 WMFreeArray(panel->windows);
590 if (panel->defIcon)
591 RReleaseImage(panel->defIcon);
592 if (panel->tile)
593 RReleaseImage(panel->tile);
594 if (panel->tileTmp)
595 RReleaseImage(panel->tileTmp);
596 if (panel->bg)
597 RReleaseImage(panel->bg);
598 if (panel->font)
599 WMReleaseFont(panel->font);
600 if (panel->white)
601 WMReleaseColor(panel->white);
602 wfree(panel);
605 WWindow *wSwitchPanelSelectNext(WSwitchPanel * panel, int back)
607 WWindow *wwin;
608 int count = WMGetArrayItemCount(panel->windows);
610 if (count == 0)
611 return NULL;
613 if (panel->win)
614 changeImage(panel, panel->current, 0);
616 if (back)
617 panel->current--;
618 else
619 panel->current++;
621 wwin = WMGetFromArray(panel->windows, (count + panel->current) % count);
623 if (back) {
624 if (panel->current < 0)
625 scrollIcons(panel, count);
626 else if (panel->current < panel->firstVisible)
627 scrollIcons(panel, -1);
628 } else {
629 if (panel->current >= count)
630 scrollIcons(panel, -count);
631 else if (panel->current - panel->firstVisible >= panel->visibleCount)
632 scrollIcons(panel, 1);
635 panel->current = (count + panel->current) % count;
637 if (panel->win) {
638 drawTitle(panel, panel->current, wwin->frame->title);
640 changeImage(panel, panel->current, 1);
643 return wwin;
646 WWindow *wSwitchPanelSelectFirst(WSwitchPanel * panel, int back)
648 WWindow *wwin;
649 int count = WMGetArrayItemCount(panel->windows);
651 if (count == 0)
652 return NULL;
654 if (panel->win)
655 changeImage(panel, panel->current, 0);
657 if (back) {
658 panel->current = count - 1;
659 scrollIcons(panel, count);
660 } else {
661 panel->current = 0;
662 scrollIcons(panel, -count);
665 wwin = WMGetFromArray(panel->windows, panel->current);
667 if (panel->win) {
668 drawTitle(panel, panel->current, wwin->frame->title);
670 changeImage(panel, panel->current, 1);
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 == LeaveNotify) {
685 if (event->xcrossing.window == WMWidgetXID(panel->win))
686 focus= 0;
687 } else */ if (event->type == MotionNotify) {
689 WM_ITERATE_ARRAY(panel->icons, icon, i) {
690 if (WMWidgetXID(icon) == event->xmotion.window) {
691 focus = i;
692 break;
696 if (focus >= 0 && panel->current != focus) {
697 WWindow *wwin;
699 changeImage(panel, panel->current, 0);
700 changeImage(panel, focus, 1);
701 panel->current = focus;
703 wwin = WMGetFromArray(panel->windows, focus);
705 drawTitle(panel, panel->current, wwin->frame->title);
707 return wwin;
710 return NULL;
713 Window wSwitchPanelGetWindow(WSwitchPanel * swpanel)
715 if (!swpanel->win)
716 return None;
717 return WMWidgetXID(swpanel->win);