fixed crash bug with alt-tab + run-dialog (or internal windows in general)
[wmaker-crm.git] / src / switchpanel.c
blob650a19ccfc6a8cc09ac83952820cd0b47b90cd42
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 #ifdef SHAPE
29 #include <X11/extensions/shape.h>
31 extern Bool wShapeSupported;
32 #endif
34 #include "WindowMaker.h"
35 #include "screen.h"
36 #include "wcore.h"
37 #include "framewin.h"
38 #include "window.h"
39 #include "defaults.h"
40 #include "switchpanel.h"
41 #include "funcs.h"
42 #include "xinerama.h"
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 *defIcon;
61 RImage *tileTmp;
62 RImage *tile;
64 WMFont *font;
65 WMColor *white;
72 extern WPreferences wPreferences;
74 #define BORDER_SPACE 10
75 #define ICON_SIZE 48
76 #define ICON_TILE_SIZE 64
77 #define LABEL_HEIGHT 25
78 #define SCREEN_BORDER_SPACING 2*20
79 #define SCROLL_STEPS (ICON_TILE_SIZE/2)
81 static int canReceiveFocus(WWindow *wwin)
83 if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
84 return 0;
85 if (!wwin->flags.mapped)
87 if (!wwin->flags.shaded && !wwin->flags.miniaturized && !wwin->flags.hidden)
88 return 0;
89 else
90 return -1;
92 if (WFLAGP(wwin, no_focusable))
93 return 0;
94 return 1;
98 static void changeImage(WSwitchPanel *panel, int index, int selected)
100 WMFrame *icon = WMGetFromArray(panel->icons, index);
101 RImage *image= WMGetFromArray(panel->images, index);
103 if (!panel->bg && !panel->tile) {
104 if (!selected)
105 WMSetFrameRelief(icon, WRFlat);
108 if (image && icon) {
109 RImage *back;
110 int opaq= 255;
111 RImage *tile;
112 WMPoint pos;
113 Pixmap p;
115 if (canReceiveFocus(WMGetFromArray(panel->windows, index)) < 0)
116 opaq= 50;
118 pos= WMGetViewPosition(WMWidgetView(icon));
119 back= panel->tileTmp;
120 if (panel->bg)
121 RCopyArea(back, panel->bg,
122 BORDER_SPACE+pos.x-panel->firstVisible*ICON_TILE_SIZE, BORDER_SPACE+pos.y,
123 back->width, back->height,
124 0, 0);
125 else
127 RColor color;
128 WMScreen *wscr= WMWidgetScreen(icon);
129 color.red= 255;
130 color.red = WMRedComponentOfColor(WMGrayColor(wscr))>>8;
131 color.green = WMGreenComponentOfColor(WMGrayColor(wscr))>>8;
132 color.blue = WMBlueComponentOfColor(WMGrayColor(wscr))>>8;
133 RFillImage(back, &color);
135 if (selected)
137 tile= panel->tile;
138 RCombineArea(back, tile, 0, 0, tile->width, tile->height,
139 (back->width - tile->width)/2, (back->height - tile->height)/2);
141 RCombineAreaWithOpaqueness(back, image, 0, 0, image->width, image->height,
142 (back->width - image->width)/2, (back->height - image->height)/2,
143 opaq);
145 RConvertImage(panel->scr->rcontext, back, &p);
146 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(icon), p);
147 XClearWindow(dpy, WMWidgetXID(icon));
148 XFreePixmap(dpy, p);
151 if (!panel->bg && !panel->tile) {
152 if (selected)
153 WMSetFrameRelief(icon, WRSimple);
158 static RImage *scaleDownIfNeeded(RImage *image)
160 if (image && ((image->width - ICON_SIZE) > 2 || (image->height - ICON_SIZE) > 2)) {
161 RImage *nimage;
162 nimage= RScaleImage(image, ICON_SIZE, (image->height * ICON_SIZE / image->width));
163 RReleaseImage(image);
164 image= nimage;
166 return image;
170 static void addIconForWindow(WSwitchPanel *panel, WMWidget *parent, WWindow *wwin,
171 int x, int y)
173 WMFrame *icon= WMCreateFrame(parent);
174 RImage *image = NULL;
176 WMSetFrameRelief(icon, WRFlat);
177 WMResizeWidget(icon, ICON_TILE_SIZE, ICON_TILE_SIZE);
178 WMMoveWidget(icon, x, y);
180 if (!WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
181 image = RRetainImage(wwin->net_icon_image);
183 // Make this use a caching thing. When there are many windows,
184 // it's very likely that most of them are instances of the same thing,
185 // so caching them should get performance acceptable in these cases.
186 if (!image)
187 image = wDefaultGetImage(panel->scr, wwin->wm_instance, wwin->wm_class);
189 if (!image && !panel->defIcon) {
190 char *file = wDefaultGetIconFile(panel->scr, NULL, NULL, False);
191 if (file) {
192 char *path = FindImage(wPreferences.icon_path, file);
193 if (path) {
194 image = RLoadImage(panel->scr->rcontext, path, 0);
195 wfree(path);
198 if (image)
199 panel->defIcon= scaleDownIfNeeded(image);
200 image= NULL;
202 if (!image && panel->defIcon)
203 image= RRetainImage(panel->defIcon);
205 image= scaleDownIfNeeded(image);
207 WMAddToArray(panel->images, image);
208 WMAddToArray(panel->icons, icon);
212 static void scrollIcons(WSwitchPanel *panel, int delta)
214 int nfirst= panel->firstVisible + delta;
215 int i;
216 int count= WMGetArrayItemCount(panel->windows);
217 // int nx, ox;
218 // struct timeval tv1, tv2;
220 if (count <= panel->visibleCount)
221 return;
223 if (nfirst < 0)
224 nfirst= 0;
225 else if (nfirst >= count-panel->visibleCount)
226 nfirst= count-panel->visibleCount;
228 if (nfirst == panel->firstVisible)
229 return;
231 ox = -panel->firstVisible * ICON_TILE_SIZE;
232 nx = -nfirst * ICON_TILE_SIZE;
233 for (i= 0; i < SCROLL_STEPS; i++) {
234 unsigned int diff;
235 gettimeofday(&tv1, NULL);
236 WMMoveWidget(panel->iconBox, (nx*i + ox*(SCROLL_STEPS-i))/(SCROLL_STEPS-1), 0);
237 XSync(dpy, False);
238 gettimeofday(&tv2, NULL);
239 diff = (tv2.tv_sec-tv1.tv_sec)*10000+(tv2.tv_usec-tv1.tv_usec)/100;
240 if (diff < 200)
241 wusleep(300-diff);
244 WMMoveWidget(panel->iconBox, -nfirst*ICON_TILE_SIZE, 0);
246 panel->firstVisible= nfirst;
248 for (i= panel->firstVisible; i < panel->firstVisible+panel->visibleCount; i++) {
249 changeImage(panel, i, i == panel->current);
255 * 0 1 2
256 * 3 4 5
257 * 6 7 8
259 static RImage *assemblePuzzleImage(RImage **images, int width, int height)
261 RImage *img= RCreateImage(width, height, 1);
262 RImage *tmp;
263 int tw, th;
264 RColor color;
265 if (!img)
266 return NULL;
268 color.red= 0;
269 color.green= 0;
270 color.blue= 0;
271 color.alpha= 255;
273 RFillImage(img, &color);
275 tw= width-images[0]->width-images[2]->width;
276 th= height-images[0]->height-images[6]->height;
278 if (tw <= 0 || th <= 0) {
279 //XXX
280 return NULL;
283 /* top */
284 if (tw > 0) {
285 tmp= RSmoothScaleImage(images[1], tw, images[1]->height);
286 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
287 images[0]->width, 0);
288 RReleaseImage(tmp);
290 /* bottom */
291 if (tw > 0) {
292 tmp= RSmoothScaleImage(images[7], tw, images[7]->height);
293 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
294 images[6]->width, height-images[6]->height);
295 RReleaseImage(tmp);
297 /* left */
298 if (th > 0) {
299 tmp= RSmoothScaleImage(images[3], images[3]->width, th);
300 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
301 0, images[0]->height);
302 RReleaseImage(tmp);
304 /* right */
305 if (th > 0) {
306 tmp= RSmoothScaleImage(images[5], images[5]->width, th);
307 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
308 width-images[5]->width, images[2]->height);
309 RReleaseImage(tmp);
311 /* center */
312 if (tw > 0 && th > 0) {
313 tmp= RSmoothScaleImage(images[4], tw, th);
314 RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
315 images[0]->width, images[0]->height);
316 RReleaseImage(tmp);
319 /* corners */
320 RCopyArea(img, images[0], 0, 0, images[0]->width, images[0]->height,
321 0, 0);
323 RCopyArea(img, images[2], 0, 0, images[2]->width, images[2]->height,
324 width-images[2]->width, 0);
326 RCopyArea(img, images[6], 0, 0, images[6]->width, images[6]->height,
327 0, height-images[6]->height);
329 RCopyArea(img, images[8], 0, 0, images[8]->width, images[8]->height,
330 width-images[8]->width, height-images[8]->height);
332 return img;
335 static RImage *createBackImage(WScreen *scr, int width, int height)
337 return assemblePuzzleImage(wPreferences.swbackImage, width, height);
341 static RImage *getTile(WSwitchPanel *panel)
343 RImage *stile;
345 if (!wPreferences.swtileImage)
346 return NULL;
348 stile = RScaleImage(wPreferences.swtileImage, ICON_TILE_SIZE, ICON_TILE_SIZE);
349 if (!stile)
350 return wPreferences.swtileImage;
352 return stile;
356 static void
357 drawTitle(WSwitchPanel *panel, int index, char *title)
359 char *ntitle;
360 int width= WMWidgetWidth(panel->win);
361 int x;
363 if (title)
364 ntitle= ShrinkString(panel->font, title, width-2*BORDER_SPACE);
365 else
366 ntitle= NULL;
368 if (panel->bg) {
369 if (ntitle) {
370 if (strcmp(ntitle, title)!=0)
371 x= BORDER_SPACE;
372 else
374 int w= WMWidthOfString(panel->font, ntitle, strlen(ntitle));
376 x= BORDER_SPACE+(index-panel->firstVisible)*ICON_TILE_SIZE + ICON_TILE_SIZE/2 - w/2;
377 if (x < BORDER_SPACE)
378 x= BORDER_SPACE;
379 else if (x + w > width-BORDER_SPACE)
380 x= width-BORDER_SPACE-w;
383 XClearWindow(dpy, WMWidgetXID(panel->win));
384 if (ntitle)
385 WMDrawString(panel->scr->wmscreen,
386 WMWidgetXID(panel->win),
387 panel->white, panel->font,
388 x, WMWidgetHeight(panel->win) - BORDER_SPACE - LABEL_HEIGHT + WMFontHeight(panel->font)/2,
389 ntitle, strlen(ntitle));
390 } else {
391 if (ntitle)
392 WMSetLabelText(panel->label, ntitle);
394 if (ntitle)
395 free(ntitle);
400 static WMArray *makeWindowListArray(WScreen *scr, WWindow *curwin, int workspace)
402 WMArray *windows= WMCreateArray(10);
403 int fl;
404 WWindow *wwin;
406 for (fl= 0; fl < 2; fl++) {
407 for (wwin= curwin; wwin; wwin= wwin->prev) {
408 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
409 (!WFLAGP(wwin, skip_window_list) || wwin->flags.internal_window)) {
410 WMAddToArray(windows, wwin);
413 wwin = curwin;
414 /* start over from the beginning of the list */
415 while (wwin->next)
416 wwin = wwin->next;
418 for (wwin= curwin; wwin && wwin != curwin; wwin= wwin->prev) {
419 if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
420 (!WFLAGP(wwin, skip_window_list) || wwin->flags.internal_window)) {
421 WMAddToArray(windows, wwin);
426 return windows;
433 WSwitchPanel *wInitSwitchPanel(WScreen *scr, WWindow *curwin, int workspace)
435 WWindow *wwin;
436 WSwitchPanel *panel= wmalloc(sizeof(WSwitchPanel));
437 WMFrame *viewport;
438 int i;
439 int width, height;
440 int iconsThatFitCount;
441 int count;
442 WMRect rect;
443 rect= wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
445 memset(panel, 0, sizeof(WSwitchPanel));
447 panel->scr= scr;
449 panel->windows= makeWindowListArray(scr, curwin, workspace);
450 count= WMGetArrayItemCount(panel->windows);
452 if (count == 0) {
453 WMFreeArray(panel->windows);
454 wfree(panel);
455 return NULL;
458 width= ICON_TILE_SIZE*count;
459 iconsThatFitCount= count;
461 if (width > rect.size.width) {
462 iconsThatFitCount = (WMScreenWidth(scr->wmscreen)-SCREEN_BORDER_SPACING)/ICON_TILE_SIZE;
463 width= iconsThatFitCount*ICON_TILE_SIZE;
466 panel->visibleCount= iconsThatFitCount;
468 height= LABEL_HEIGHT + ICON_TILE_SIZE;
470 panel->tileTmp= RCreateImage(ICON_TILE_SIZE, ICON_TILE_SIZE, 1);
471 panel->tile= getTile(panel);
472 if (panel->tile && wPreferences.swbackImage[8]) {
473 panel->bg= createBackImage(scr, width+2*BORDER_SPACE, height+2*BORDER_SPACE);
475 if (!panel->tileTmp || !panel->tile) {
476 if (panel->bg)
477 RReleaseImage(panel->bg);
478 panel->bg= NULL;
479 if (panel->tile)
480 RReleaseImage(panel->tile);
481 panel->tile= NULL;
482 if (panel->tileTmp)
483 RReleaseImage(panel->tileTmp);
484 panel->tileTmp= NULL;
487 panel->white= WMWhiteColor(scr->wmscreen);
488 panel->font= WMBoldSystemFontOfSize(scr->wmscreen, 12);
489 panel->icons= WMCreateArray(count);
490 panel->images= WMCreateArray(count);
492 panel->win = WMCreateWindow(scr->wmscreen, "");
494 if (!panel->bg) {
495 WMFrame *frame = WMCreateFrame(panel->win);
496 WMSetFrameRelief(frame, WRSimple);
497 WMSetViewExpandsToParent(WMWidgetView(frame), 0, 0, 0, 0);
499 panel->label = WMCreateLabel(panel->win);
500 WMResizeWidget(panel->label, width, LABEL_HEIGHT);
501 WMMoveWidget(panel->label, BORDER_SPACE, BORDER_SPACE+ICON_TILE_SIZE+5);
502 WMSetLabelRelief(panel->label, WRSimple);
503 WMSetWidgetBackgroundColor(panel->label, WMDarkGrayColor(scr->wmscreen));
504 WMSetLabelFont(panel->label, panel->font);
505 WMSetLabelTextColor(panel->label, panel->white);
507 height+= 5;
510 WMResizeWidget(panel->win, width + 2*BORDER_SPACE, height + 2*BORDER_SPACE);
512 viewport= WMCreateFrame(panel->win);
513 WMResizeWidget(viewport, width, ICON_TILE_SIZE);
514 WMMoveWidget(viewport, BORDER_SPACE, BORDER_SPACE);
515 WMSetFrameRelief(viewport, WRFlat);
517 panel->iconBox= WMCreateFrame(viewport);
518 WMMoveWidget(panel->iconBox, 0, 0);
519 WMResizeWidget(panel->iconBox, ICON_TILE_SIZE*count, ICON_TILE_SIZE);
520 WMSetFrameRelief(panel->iconBox, WRFlat);
522 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
523 addIconForWindow(panel, panel->iconBox, wwin, i*ICON_TILE_SIZE, 0);
526 WMMapSubwidgets(panel->win);
527 WMRealizeWidget(panel->win);
529 WM_ITERATE_ARRAY(panel->windows, wwin, i) {
530 changeImage(panel, i, 0);
533 if (panel->bg) {
534 Pixmap pixmap, mask;
536 RConvertImageMask(scr->rcontext, panel->bg, &pixmap, &mask, 250);
538 XSetWindowBackgroundPixmap(dpy, WMWidgetXID(panel->win), pixmap);
540 #ifdef SHAPE
541 if (mask && wShapeSupported)
542 XShapeCombineMask(dpy, WMWidgetXID(panel->win),
543 ShapeBounding, 0, 0, mask, ShapeSet);
544 #endif
546 if (pixmap)
547 XFreePixmap(dpy, pixmap);
548 if (mask)
549 XFreePixmap(dpy, mask);
553 WMPoint center;
554 center= wGetPointToCenterRectInHead(scr, wGetHeadForPointerLocation(scr),
555 width+2*BORDER_SPACE, height+2*BORDER_SPACE);
556 WMMoveWidget(panel->win, center.x, center.y);
559 panel->current= WMGetFirstInArray(panel->windows, curwin);
560 if (panel->current >= 0)
561 changeImage(panel, panel->current, 1);
563 WMMapWidget(panel->win);
565 return panel;
569 void wSwitchPanelDestroy(WSwitchPanel *panel)
571 int i;
572 RImage *image;
574 if (panel->win)
575 WMUnmapWidget(panel->win);
577 if (panel->images) {
578 WM_ITERATE_ARRAY(panel->images, image, i) {
579 if (image)
580 RReleaseImage(image);
582 WMFreeArray(panel->images);
584 if (panel->win)
585 WMDestroyWidget(panel->win);
586 if (panel->icons)
587 WMFreeArray(panel->icons);
588 WMFreeArray(panel->windows);
589 if (panel->defIcon)
590 RReleaseImage(panel->defIcon);
591 if (panel->tile)
592 RReleaseImage(panel->tile);
593 if (panel->tileTmp)
594 RReleaseImage(panel->tileTmp);
595 if (panel->bg)
596 RReleaseImage(panel->bg);
597 if (panel->font)
598 WMReleaseFont(panel->font);
599 wfree(panel);
603 WWindow *wSwitchPanelSelectNext(WSwitchPanel *panel, int back)
605 WWindow *wwin;
606 int count = WMGetArrayItemCount(panel->windows);
608 if (count == 0)
609 return NULL;
611 if (panel->win)
612 changeImage(panel, panel->current, 0);
614 if (back)
615 panel->current--;
616 else
617 panel->current++;
619 wwin = WMGetFromArray(panel->windows, (count+panel->current)%count);
621 if (back)
623 if (panel->current < 0)
624 scrollIcons(panel, count);
625 else if (panel->current < panel->firstVisible)
626 scrollIcons(panel, -1);
628 else
630 if (panel->current >= count)
631 scrollIcons(panel, -count);
632 else if (panel->current - panel->firstVisible >= panel->visibleCount)
633 scrollIcons(panel, 1);
636 panel->current= (count+panel->current)%count;
638 if (panel->win) {
639 drawTitle(panel, panel->current, wwin->frame->title);
641 changeImage(panel, panel->current, 1);
644 return wwin;
648 WWindow *wSwitchPanelSelectFirst(WSwitchPanel *panel, int back)
650 WWindow *wwin;
651 int count = WMGetArrayItemCount(panel->windows);
653 if (count == 0)
654 return NULL;
656 if (panel->win)
657 changeImage(panel, panel->current, 0);
659 if (back) {
660 panel->current = count-1;
661 scrollIcons(panel, count);
662 } else {
663 panel->current = 0;
664 scrollIcons(panel, -count);
667 wwin = WMGetFromArray(panel->windows, panel->current);
669 if (panel->win) {
670 drawTitle(panel, panel->current, wwin->frame->title);
672 changeImage(panel, panel->current, 1);
674 return wwin;
678 WWindow *wSwitchPanelHandleEvent(WSwitchPanel *panel, XEvent *event)
680 WMFrame *icon;
681 int i;
683 if (!panel->win)
684 return NULL;
686 if (event->type == MotionNotify) {
687 int focus= -1;
689 WM_ITERATE_ARRAY(panel->icons, icon, i) {
690 if (WMWidgetXID(icon) == event->xmotion.window) {
691 focus= i;
692 break;
696 if (focus < 0)
697 focus= 0;
699 if (focus >= 0 && panel->current != focus) {
700 WWindow *wwin;
702 changeImage(panel, panel->current, 0);
703 changeImage(panel, focus, 1);
704 panel->current= focus;
706 wwin= WMGetFromArray(panel->windows, focus);
708 drawTitle(panel, panel->current, wwin->frame->title);
710 return wwin;
714 return NULL;
718 Window wSwitchPanelGetWindow(WSwitchPanel *swpanel)
720 if (!swpanel->win)
721 return None;
722 return WMWidgetXID(swpanel->win);