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.
27 #include "WindowMaker.h"
33 #include "switchpanel.h"
37 extern Atom _XA_WM_IGNORE_FOCUS_EVENTS
;
40 #include <X11/extensions/shape.h>
42 extern Bool wShapeSupported
;
67 extern WPreferences wPreferences
;
69 #define BORDER_SPACE 10
71 #define ICON_TILE_SIZE 64
72 #define LABEL_HEIGHT 25
73 #define SCREEN_BORDER_SPACING 2*20
74 #define SCROLL_STEPS (ICON_TILE_SIZE/2)
76 static int canReceiveFocus(WWindow
*wwin
)
78 if (wwin
->frame
->workspace
!= wwin
->screen_ptr
->current_workspace
)
81 if (wPreferences
.cycle_active_head_only
&&
82 wGetHeadForWindow(wwin
) != wGetHeadForPointerLocation(wwin
->screen_ptr
))
85 if (WFLAGP(wwin
, no_focusable
))
88 if (!wwin
->flags
.mapped
) {
89 if (!wwin
->flags
.shaded
&& !wwin
->flags
.miniaturized
&& !wwin
->flags
.hidden
)
98 static Bool
sameWindowClass(WWindow
*wwin
, WWindow
*curwin
)
100 if (!wwin
->wm_class
|| !curwin
->wm_class
)
102 if (strcmp(wwin
->wm_class
, curwin
->wm_class
))
108 static void changeImage(WSwitchPanel
*panel
, int idecks
, int selected
, Bool dim
)
110 WMFrame
*icon
= WMGetFromArray(panel
->icons
, idecks
);
111 RImage
*image
= WMGetFromArray(panel
->images
, idecks
);
113 if (!panel
->bg
&& !panel
->tile
&& !selected
)
114 WMSetFrameRelief(icon
, WRFlat
);
118 int opaq
= (dim
) ? 75 : 255;
123 if (canReceiveFocus(WMGetFromArray(panel
->windows
, idecks
)) < 0)
126 pos
= WMGetViewPosition(WMWidgetView(icon
));
127 back
= panel
->tileTmp
;
129 RCopyArea(back
, panel
->bg
,
130 BORDER_SPACE
+ pos
.x
- panel
->firstVisible
* ICON_TILE_SIZE
,
131 BORDER_SPACE
+ pos
.y
, back
->width
, back
->height
, 0, 0);
134 WMScreen
*wscr
= WMWidgetScreen(icon
);
136 color
.red
= WMRedComponentOfColor(WMGrayColor(wscr
)) >> 8;
137 color
.green
= WMGreenComponentOfColor(WMGrayColor(wscr
)) >> 8;
138 color
.blue
= WMBlueComponentOfColor(WMGrayColor(wscr
)) >> 8;
139 RFillImage(back
, &color
);
144 RCombineArea(back
, tile
, 0, 0, tile
->width
, tile
->height
,
145 (back
->width
- tile
->width
) / 2, (back
->height
- tile
->height
) / 2);
148 RCombineAreaWithOpaqueness(back
, image
, 0, 0, image
->width
, image
->height
,
149 (back
->width
- image
->width
) / 2, (back
->height
- image
->height
) / 2,
152 RConvertImage(panel
->scr
->rcontext
, back
, &p
);
153 XSetWindowBackgroundPixmap(dpy
, WMWidgetXID(icon
), p
);
154 XClearWindow(dpy
, WMWidgetXID(icon
));
158 if (!panel
->bg
&& !panel
->tile
&& selected
)
159 WMSetFrameRelief(icon
, WRSimple
);
162 static void addIconForWindow(WSwitchPanel
*panel
, WMWidget
*parent
, WWindow
*wwin
, int x
, int y
)
164 WMFrame
*icon
= WMCreateFrame(parent
);
165 RImage
*image
= NULL
;
167 WMSetFrameRelief(icon
, WRFlat
);
168 WMResizeWidget(icon
, ICON_TILE_SIZE
, ICON_TILE_SIZE
);
169 WMMoveWidget(icon
, x
, y
);
171 if (!WFLAGP(wwin
, always_user_icon
) && wwin
->net_icon_image
)
172 image
= RRetainImage(wwin
->net_icon_image
);
174 /* get_icon_image() includes the default icon image */
176 image
= get_icon_image(panel
->scr
, wwin
->wm_instance
, wwin
->wm_class
, ICON_TILE_SIZE
);
178 /* We must resize the icon size (~64) to the switchpanel icon size (~48) */
179 image
= wIconValidateIconSize(image
, ICON_SIZE
);
181 WMAddToArray(panel
->images
, image
);
182 WMAddToArray(panel
->icons
, icon
);
185 static void scrollIcons(WSwitchPanel
*panel
, int delta
)
187 int nfirst
= panel
->firstVisible
+ delta
;
189 int count
= WMGetArrayItemCount(panel
->windows
);
191 if (count
<= panel
->visibleCount
)
196 else if (nfirst
>= count
- panel
->visibleCount
)
197 nfirst
= count
- panel
->visibleCount
;
199 if (nfirst
== panel
->firstVisible
)
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
, 0);
215 static RImage
*assemblePuzzleImage(RImage
**images
, int width
, int height
)
217 RImage
*img
= RCreateImage(width
, height
, 1);
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)
239 tmp
= RSmoothScaleImage(images
[1], tw
, images
[1]->height
);
240 RCopyArea(img
, tmp
, 0, 0, tmp
->width
, tmp
->height
, images
[0]->width
, 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
);
251 tmp
= RSmoothScaleImage(images
[3], images
[3]->width
, th
);
252 RCopyArea(img
, tmp
, 0, 0, tmp
->width
, tmp
->height
, 0, images
[0]->height
);
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
);
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
);
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
);
278 static RImage
*createBackImage(int width
, int height
)
280 return assemblePuzzleImage(wPreferences
.swbackImage
, width
, height
);
283 static RImage
*getTile(void)
287 if (!wPreferences
.swtileImage
)
290 stile
= RScaleImage(wPreferences
.swtileImage
, ICON_TILE_SIZE
, ICON_TILE_SIZE
);
292 return wPreferences
.swtileImage
;
297 static void drawTitle(WSwitchPanel
*panel
, int idecks
, char *title
)
300 int width
= WMWidgetWidth(panel
->win
);
304 ntitle
= ShrinkString(panel
->font
, title
, width
- 2 * BORDER_SPACE
);
310 if (strcmp(ntitle
, title
) != 0) {
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
)
319 else if (x
+ w
> width
- BORDER_SPACE
)
320 x
= width
- BORDER_SPACE
- w
;
324 XClearWindow(dpy
, WMWidgetXID(panel
->win
));
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
));
334 WMSetLabelText(panel
->label
, ntitle
);
341 static WMArray
*makeWindowListArray(WWindow
*curwin
, int include_unmapped
, Bool class_only
)
343 WMArray
*windows
= WMCreateArray(10);
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
)) {
352 if (!sameWindowClass(wwin
, curwin
))
355 if (!WFLAGP(wwin
, skip_switchpanel
))
356 WMAddToArray(windows
, wwin
);
360 /* start over from the beginning of the list */
364 for (wwin
= curwin
; wwin
&& wwin
!= curwin
; wwin
= wwin
->prev
) {
365 if (((!fl
&& canReceiveFocus(wwin
) > 0) || (fl
&& canReceiveFocus(wwin
) < 0)) &&
366 (wwin
->flags
.mapped
|| include_unmapped
)) {
368 if (!sameWindowClass(wwin
, curwin
))
370 if (!WFLAGP(wwin
, skip_switchpanel
))
371 WMAddToArray(windows
, wwin
);
379 WSwitchPanel
*wInitSwitchPanel(WScreen
*scr
, WWindow
*curwin
, Bool class_only
)
382 WSwitchPanel
*panel
= wmalloc(sizeof(WSwitchPanel
));
384 int i
, width
, height
, iconsThatFitCount
, count
;
385 WMRect rect
= wGetRectForHead(scr
, wGetHeadForPointerLocation(scr
));
388 panel
->windows
= makeWindowListArray(curwin
, wPreferences
.swtileImage
!= NULL
, class_only
);
389 count
= WMGetArrayItemCount(panel
->windows
);
392 WMFreeArray(panel
->windows
);
397 width
= ICON_TILE_SIZE
* count
;
398 iconsThatFitCount
= count
;
400 if (width
> rect
.size
.width
) {
401 iconsThatFitCount
= (rect
.size
.width
- SCREEN_BORDER_SPACING
) / ICON_TILE_SIZE
;
402 width
= iconsThatFitCount
* ICON_TILE_SIZE
;
405 panel
->visibleCount
= iconsThatFitCount
;
407 if (!wPreferences
.swtileImage
)
410 height
= LABEL_HEIGHT
+ ICON_TILE_SIZE
;
412 panel
->tileTmp
= RCreateImage(ICON_TILE_SIZE
, ICON_TILE_SIZE
, 1);
413 panel
->tile
= getTile();
414 if (panel
->tile
&& wPreferences
.swbackImage
[8])
415 panel
->bg
= createBackImage(width
+ 2 * BORDER_SPACE
, height
+ 2 * BORDER_SPACE
);
417 if (!panel
->tileTmp
|| !panel
->tile
) {
419 RReleaseImage(panel
->bg
);
422 RReleaseImage(panel
->tile
);
425 RReleaseImage(panel
->tileTmp
);
426 panel
->tileTmp
= NULL
;
429 panel
->white
= WMWhiteColor(scr
->wmscreen
);
430 panel
->font
= WMBoldSystemFontOfSize(scr
->wmscreen
, 12);
431 panel
->icons
= WMCreateArray(count
);
432 panel
->images
= WMCreateArray(count
);
434 panel
->win
= WMCreateWindow(scr
->wmscreen
, "");
437 WMFrame
*frame
= WMCreateFrame(panel
->win
);
438 WMColor
*darkGray
= WMDarkGrayColor(scr
->wmscreen
);
439 WMSetFrameRelief(frame
, WRSimple
);
440 WMSetViewExpandsToParent(WMWidgetView(frame
), 0, 0, 0, 0);
442 panel
->label
= WMCreateLabel(panel
->win
);
443 WMResizeWidget(panel
->label
, width
, LABEL_HEIGHT
);
444 WMMoveWidget(panel
->label
, BORDER_SPACE
, BORDER_SPACE
+ ICON_TILE_SIZE
+ 5);
445 WMSetLabelRelief(panel
->label
, WRSimple
);
446 WMSetWidgetBackgroundColor(panel
->label
, darkGray
);
447 WMSetLabelFont(panel
->label
, panel
->font
);
448 WMSetLabelTextColor(panel
->label
, panel
->white
);
450 WMReleaseColor(darkGray
);
454 WMResizeWidget(panel
->win
, width
+ 2 * BORDER_SPACE
, height
+ 2 * BORDER_SPACE
);
456 viewport
= WMCreateFrame(panel
->win
);
457 WMResizeWidget(viewport
, width
, ICON_TILE_SIZE
);
458 WMMoveWidget(viewport
, BORDER_SPACE
, BORDER_SPACE
);
459 WMSetFrameRelief(viewport
, WRFlat
);
461 panel
->iconBox
= WMCreateFrame(viewport
);
462 WMMoveWidget(panel
->iconBox
, 0, 0);
463 WMResizeWidget(panel
->iconBox
, ICON_TILE_SIZE
* count
, ICON_TILE_SIZE
);
464 WMSetFrameRelief(panel
->iconBox
, WRFlat
);
466 WM_ITERATE_ARRAY(panel
->windows
, wwin
, i
) {
467 addIconForWindow(panel
, panel
->iconBox
, wwin
, i
* ICON_TILE_SIZE
, 0);
470 WMMapSubwidgets(panel
->win
);
471 WMRealizeWidget(panel
->win
);
473 WM_ITERATE_ARRAY(panel
->windows
, wwin
, i
) {
474 changeImage(panel
, i
, 0, False
);
480 RConvertImageMask(scr
->rcontext
, panel
->bg
, &pixmap
, &mask
, 250);
482 XSetWindowBackgroundPixmap(dpy
, WMWidgetXID(panel
->win
), pixmap
);
485 if (mask
&& wShapeSupported
)
486 XShapeCombineMask(dpy
, WMWidgetXID(panel
->win
), ShapeBounding
, 0, 0, mask
, ShapeSet
);
489 XFreePixmap(dpy
, pixmap
);
492 XFreePixmap(dpy
, mask
);
497 center
= wGetPointToCenterRectInHead(scr
, wGetHeadForPointerLocation(scr
),
498 width
+ 2 * BORDER_SPACE
, height
+ 2 * BORDER_SPACE
);
499 WMMoveWidget(panel
->win
, center
.x
, center
.y
);
502 panel
->current
= WMGetFirstInArray(panel
->windows
, curwin
);
503 if (panel
->current
>= 0)
504 changeImage(panel
, panel
->current
, 1, False
);
506 WMMapWidget(panel
->win
);
511 void wSwitchPanelDestroy(WSwitchPanel
*panel
)
517 Window info_win
= panel
->scr
->info_window
;
519 ev
.xclient
.type
= ClientMessage
;
520 ev
.xclient
.message_type
= _XA_WM_IGNORE_FOCUS_EVENTS
;
521 ev
.xclient
.format
= 32;
522 ev
.xclient
.data
.l
[0] = True
;
524 XSendEvent(dpy
, info_win
, True
, EnterWindowMask
, &ev
);
525 WMUnmapWidget(panel
->win
);
527 ev
.xclient
.data
.l
[0] = False
;
528 XSendEvent(dpy
, info_win
, True
, EnterWindowMask
, &ev
);
532 WM_ITERATE_ARRAY(panel
->images
, image
, i
) {
534 RReleaseImage(image
);
536 WMFreeArray(panel
->images
);
540 WMDestroyWidget(panel
->win
);
543 WMFreeArray(panel
->icons
);
545 WMFreeArray(panel
->windows
);
548 RReleaseImage(panel
->tile
);
551 RReleaseImage(panel
->tileTmp
);
554 RReleaseImage(panel
->bg
);
557 WMReleaseFont(panel
->font
);
560 WMReleaseColor(panel
->white
);
565 WWindow
*wSwitchPanelSelectNext(WSwitchPanel
*panel
, int back
, int ignore_minimized
, Bool class_only
)
567 WWindow
*wwin
, *curwin
, *tmpwin
;
568 int count
= WMGetArrayItemCount(panel
->windows
);
569 int orig
= panel
->current
;
576 changeImage(panel
, panel
->current
, 0, False
);
578 if (!wPreferences
.cycle_ignore_minimized
)
579 ignore_minimized
= False
;
581 if (ignore_minimized
&& canReceiveFocus(WMGetFromArray(panel
->windows
, (count
+ panel
->current
) % count
)) < 0)
582 ignore_minimized
= False
;
584 curwin
= WMGetFromArray(panel
->windows
, orig
);
592 panel
->current
= (count
+ panel
->current
) % count
;
593 wwin
= WMGetFromArray(panel
->windows
, panel
->current
);
597 if (panel
->current
== orig
)
599 } while (!sameWindowClass(wwin
, curwin
));
600 } while (ignore_minimized
&& panel
->current
!= orig
&& canReceiveFocus(wwin
) < 0);
602 WM_ITERATE_ARRAY(panel
->windows
, tmpwin
, i
) {
603 if (!class_only
|| sameWindowClass(tmpwin
, curwin
))
604 changeImage(panel
, i
, 0, False
);
606 changeImage(panel
, i
, 0, True
);
610 if (panel
->current
< panel
->firstVisible
)
611 scrollIcons(panel
, panel
->current
- panel
->firstVisible
);
612 else if (panel
->current
- panel
->firstVisible
>= panel
->visibleCount
)
613 scrollIcons(panel
, panel
->current
- panel
->firstVisible
- panel
->visibleCount
+ 1);
616 drawTitle(panel
, panel
->current
, wwin
->frame
->title
);
618 changeImage(panel
, panel
->current
, 1, False
);
624 WWindow
*wSwitchPanelSelectFirst(WSwitchPanel
*panel
, int back
)
627 int count
= WMGetArrayItemCount(panel
->windows
);
633 changeImage(panel
, panel
->current
, 0, False
);
636 panel
->current
= count
- 1;
637 scrollIcons(panel
, count
);
640 scrollIcons(panel
, -count
);
643 wwin
= WMGetFromArray(panel
->windows
, panel
->current
);
646 drawTitle(panel
, panel
->current
, wwin
->frame
->title
);
647 changeImage(panel
, panel
->current
, 1, False
);
653 WWindow
*wSwitchPanelHandleEvent(WSwitchPanel
*panel
, XEvent
*event
)
662 if (event
->type
== MotionNotify
) {
663 WM_ITERATE_ARRAY(panel
->icons
, icon
, i
) {
664 if (WMWidgetXID(icon
) == event
->xmotion
.window
) {
671 if (focus
>= 0 && panel
->current
!= focus
) {
674 changeImage(panel
, panel
->current
, 0, False
);
675 changeImage(panel
, focus
, 1, False
);
676 panel
->current
= focus
;
678 wwin
= WMGetFromArray(panel
->windows
, focus
);
680 drawTitle(panel
, panel
->current
, wwin
->frame
->title
);
688 Window
wSwitchPanelGetWindow(WSwitchPanel
*swpanel
)
693 return WMWidgetXID(swpanel
->win
);