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,
29 #include <X11/extensions/shape.h>
31 extern Bool wShapeSupported
;
34 #include "WindowMaker.h"
40 #include "switchpanel.h"
72 extern WPreferences wPreferences
;
74 #define BORDER_SPACE 10
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
)
85 if (!wwin
->flags
.mapped
)
87 if (!wwin
->flags
.shaded
&& !wwin
->flags
.miniaturized
&& !wwin
->flags
.hidden
)
92 if (WFLAGP(wwin
, no_focusable
))
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
) {
105 WMSetFrameRelief(icon
, WRFlat
);
115 if (canReceiveFocus(WMGetFromArray(panel
->windows
, index
)) < 0)
118 pos
= WMGetViewPosition(WMWidgetView(icon
));
119 back
= panel
->tileTmp
;
121 RCopyArea(back
, panel
->bg
,
122 BORDER_SPACE
+pos
.x
-panel
->firstVisible
*ICON_TILE_SIZE
, BORDER_SPACE
+pos
.y
,
123 back
->width
, back
->height
,
128 WMScreen
*wscr
= WMWidgetScreen(icon
);
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
);
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,
145 RConvertImage(panel
->scr
->rcontext
, back
, &p
);
146 XSetWindowBackgroundPixmap(dpy
, WMWidgetXID(icon
), p
);
147 XClearWindow(dpy
, WMWidgetXID(icon
));
151 if (!panel
->bg
&& !panel
->tile
) {
153 WMSetFrameRelief(icon
, WRSimple
);
158 static RImage
*scaleDownIfNeeded(RImage
*image
)
160 if (image
&& ((image
->width
- ICON_SIZE
) > 2 || (image
->height
- ICON_SIZE
) > 2)) {
162 nimage
= RScaleImage(image
, ICON_SIZE
, (image
->height
* ICON_SIZE
/ image
->width
));
163 RReleaseImage(image
);
170 static void addIconForWindow(WSwitchPanel
*panel
, WMWidget
*parent
, WWindow
*wwin
,
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.
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
);
192 char *path
= FindImage(wPreferences
.icon_path
, file
);
194 image
= RLoadImage(panel
->scr
->rcontext
, path
, 0);
199 panel
->defIcon
= scaleDownIfNeeded(image
);
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
;
216 int count
= WMGetArrayItemCount(panel
->windows
);
218 // struct timeval tv1, tv2;
220 if (count
<= panel
->visibleCount
)
225 else if (nfirst
>= count
-panel
->visibleCount
)
226 nfirst
= count
-panel
->visibleCount
;
228 if (nfirst
== panel
->firstVisible
)
231 ox = -panel->firstVisible * ICON_TILE_SIZE;
232 nx = -nfirst * ICON_TILE_SIZE;
233 for (i= 0; i < SCROLL_STEPS; i++) {
235 gettimeofday(&tv1, NULL);
236 WMMoveWidget(panel->iconBox, (nx*i + ox*(SCROLL_STEPS-i))/(SCROLL_STEPS-1), 0);
238 gettimeofday(&tv2, NULL);
239 diff = (tv2.tv_sec-tv1.tv_sec)*10000+(tv2.tv_usec-tv1.tv_usec)/100;
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
);
259 static RImage
*assemblePuzzleImage(RImage
**images
, int width
, int height
)
261 RImage
*img
= RCreateImage(width
, height
, 1);
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) {
285 tmp
= RSmoothScaleImage(images
[1], tw
, images
[1]->height
);
286 RCopyArea(img
, tmp
, 0, 0, tmp
->width
, tmp
->height
,
287 images
[0]->width
, 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
);
299 tmp
= RSmoothScaleImage(images
[3], images
[3]->width
, th
);
300 RCopyArea(img
, tmp
, 0, 0, tmp
->width
, tmp
->height
,
301 0, images
[0]->height
);
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
);
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
);
320 RCopyArea(img
, images
[0], 0, 0, images
[0]->width
, images
[0]->height
,
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
);
335 static RImage
*createBackImage(WScreen
*scr
, int width
, int height
)
337 return assemblePuzzleImage(wPreferences
.swbackImage
, width
, height
);
341 static RImage
*getTile(WSwitchPanel
*panel
)
345 if (!wPreferences
.swtileImage
)
348 stile
= RScaleImage(wPreferences
.swtileImage
, ICON_TILE_SIZE
, ICON_TILE_SIZE
);
350 return wPreferences
.swtileImage
;
357 drawTitle(WSwitchPanel
*panel
, int index
, char *title
)
360 int width
= WMWidgetWidth(panel
->win
);
364 ntitle
= ShrinkString(panel
->font
, title
, width
-2*BORDER_SPACE
);
370 if (strcmp(ntitle
, title
)!=0)
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
)
379 else if (x
+ w
> width
-BORDER_SPACE
)
380 x
= width
-BORDER_SPACE
-w
;
383 XClearWindow(dpy
, WMWidgetXID(panel
->win
));
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
));
392 WMSetLabelText(panel
->label
, ntitle
);
400 static WMArray
*makeWindowListArray(WScreen
*scr
, WWindow
*curwin
, int workspace
)
402 WMArray
*windows
= WMCreateArray(10);
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
);
414 /* start over from the beginning of the list */
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
);
433 WSwitchPanel
*wInitSwitchPanel(WScreen
*scr
, WWindow
*curwin
, int workspace
)
436 WSwitchPanel
*panel
= wmalloc(sizeof(WSwitchPanel
));
440 int iconsThatFitCount
;
443 rect
= wGetRectForHead(scr
, wGetHeadForPointerLocation(scr
));
445 memset(panel
, 0, sizeof(WSwitchPanel
));
449 panel
->windows
= makeWindowListArray(scr
, curwin
, workspace
);
450 count
= WMGetArrayItemCount(panel
->windows
);
453 WMFreeArray(panel
->windows
);
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
) {
477 RReleaseImage(panel
->bg
);
480 RReleaseImage(panel
->tile
);
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
, "");
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
);
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);
536 RConvertImageMask(scr
->rcontext
, panel
->bg
, &pixmap
, &mask
, 250);
538 XSetWindowBackgroundPixmap(dpy
, WMWidgetXID(panel
->win
), pixmap
);
541 if (mask
&& wShapeSupported
)
542 XShapeCombineMask(dpy
, WMWidgetXID(panel
->win
),
543 ShapeBounding
, 0, 0, mask
, ShapeSet
);
547 XFreePixmap(dpy
, pixmap
);
549 XFreePixmap(dpy
, mask
);
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
);
569 void wSwitchPanelDestroy(WSwitchPanel
*panel
)
575 WMUnmapWidget(panel
->win
);
578 WM_ITERATE_ARRAY(panel
->images
, image
, i
) {
580 RReleaseImage(image
);
582 WMFreeArray(panel
->images
);
585 WMDestroyWidget(panel
->win
);
587 WMFreeArray(panel
->icons
);
588 WMFreeArray(panel
->windows
);
590 RReleaseImage(panel
->defIcon
);
592 RReleaseImage(panel
->tile
);
594 RReleaseImage(panel
->tileTmp
);
596 RReleaseImage(panel
->bg
);
598 WMReleaseFont(panel
->font
);
603 WWindow
*wSwitchPanelSelectNext(WSwitchPanel
*panel
, int back
)
606 int count
= WMGetArrayItemCount(panel
->windows
);
612 changeImage(panel
, panel
->current
, 0);
619 wwin
= WMGetFromArray(panel
->windows
, (count
+panel
->current
)%count
);
623 if (panel
->current
< 0)
624 scrollIcons(panel
, count
);
625 else if (panel
->current
< panel
->firstVisible
)
626 scrollIcons(panel
, -1);
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
;
639 drawTitle(panel
, panel
->current
, wwin
->frame
->title
);
641 changeImage(panel
, panel
->current
, 1);
648 WWindow
*wSwitchPanelSelectFirst(WSwitchPanel
*panel
, int back
)
651 int count
= WMGetArrayItemCount(panel
->windows
);
657 changeImage(panel
, panel
->current
, 0);
660 panel
->current
= count
-1;
661 scrollIcons(panel
, count
);
664 scrollIcons(panel
, -count
);
667 wwin
= WMGetFromArray(panel
->windows
, panel
->current
);
670 drawTitle(panel
, panel
->current
, wwin
->frame
->title
);
672 changeImage(panel
, panel
->current
, 1);
678 WWindow
*wSwitchPanelHandleEvent(WSwitchPanel
*panel
, XEvent
*event
)
686 if (event
->type
== MotionNotify
) {
689 WM_ITERATE_ARRAY(panel
->icons
, icon
, i
) {
690 if (WMWidgetXID(icon
) == event
->xmotion
.window
) {
699 if (focus
>= 0 && panel
->current
!= focus
) {
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
);
718 Window
wSwitchPanelGetWindow(WSwitchPanel
*swpanel
)
722 return WMWidgetXID(swpanel
->win
);