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.
28 #include "WindowMaker.h"
34 #include "switchpanel.h"
40 #include <X11/extensions/shape.h>
67 /* these values will be updated whenever the switch panel
68 * is created to to match the size defined in switch_panel_icon_size
69 * and the selected font size */
70 static short int icon_size
;
71 static short int border_space
;
72 static short int icon_tile_size
;
73 static short int label_height
;
75 #define SCREEN_BORDER_SPACING 2*20
77 #define ICON_SELECTED (1<<1)
78 #define ICON_DIM (1<<2)
80 static int canReceiveFocus(WWindow
*wwin
)
82 if (wwin
->frame
->workspace
!= wwin
->screen_ptr
->current_workspace
)
85 if (wPreferences
.cycle_active_head_only
&&
86 wGetHeadForWindow(wwin
) != wGetHeadForPointerLocation(wwin
->screen_ptr
))
89 if (WFLAGP(wwin
, no_focusable
))
92 if (!wwin
->flags
.mapped
) {
93 if (!wwin
->flags
.shaded
&& !wwin
->flags
.miniaturized
&& !wwin
->flags
.hidden
)
102 static Bool
sameWindowClass(WWindow
*wwin
, WWindow
*curwin
)
104 if (!wwin
->wm_class
|| !curwin
->wm_class
)
106 if (strcmp(wwin
->wm_class
, curwin
->wm_class
))
112 static void changeImage(WSwitchPanel
*panel
, int idecks
, int selected
, Bool dim
, Bool force
)
114 WMFrame
*icon
= NULL
;
115 RImage
*image
= NULL
;
119 /* This whole function is a no-op if we aren't drawing the panel */
120 if (!wPreferences
.swtileImage
)
123 icon
= WMGetFromArray(panel
->icons
, idecks
);
124 image
= WMGetFromArray(panel
->images
, idecks
);
125 flags
= (int) (uintptr_t) WMGetFromArray(panel
->flags
, idecks
);
128 desired
|= ICON_SELECTED
;
132 if (flags
== desired
&& !force
)
135 WMReplaceInArray(panel
->flags
, idecks
, (void *) (uintptr_t) desired
);
137 if (!panel
->bg
&& !panel
->tile
&& !selected
)
138 WMSetFrameRelief(icon
, WRFlat
);
142 int opaq
= (dim
) ? 75 : 255;
147 if (canReceiveFocus(WMGetFromArray(panel
->windows
, idecks
)) < 0)
150 pos
= WMGetViewPosition(WMWidgetView(icon
));
151 back
= panel
->tileTmp
;
153 RCopyArea(back
, panel
->bg
,
154 border_space
+ pos
.x
- panel
->firstVisible
* icon_tile_size
,
155 border_space
+ pos
.y
, back
->width
, back
->height
, 0, 0);
158 color
.red
= WMRedComponentOfColor(panel
->gray
) >> 8;
159 color
.green
= WMGreenComponentOfColor(panel
->gray
) >> 8;
160 color
.blue
= WMBlueComponentOfColor(panel
->gray
) >> 8;
162 RFillImage(back
, &color
);
167 RCombineArea(back
, tile
, 0, 0, tile
->width
, tile
->height
,
168 (back
->width
- tile
->width
) / 2, (back
->height
- tile
->height
) / 2);
171 RCombineAreaWithOpaqueness(back
, image
, 0, 0, image
->width
, image
->height
,
172 (back
->width
- image
->width
) / 2, (back
->height
- image
->height
) / 2,
175 RConvertImage(panel
->scr
->rcontext
, back
, &p
);
176 XSetWindowBackgroundPixmap(dpy
, WMWidgetXID(icon
), p
);
177 XClearWindow(dpy
, WMWidgetXID(icon
));
181 if (!panel
->bg
&& !panel
->tile
&& selected
)
182 WMSetFrameRelief(icon
, WRSimple
);
185 static void addIconForWindow(WSwitchPanel
*panel
, WMWidget
*parent
, WWindow
*wwin
, int x
, int y
)
187 WMFrame
*icon
= WMCreateFrame(parent
);
188 RImage
*image
= NULL
;
190 WMSetFrameRelief(icon
, WRFlat
);
191 WMResizeWidget(icon
, icon_tile_size
, icon_tile_size
);
192 WMMoveWidget(icon
, x
, y
);
194 if (!WFLAGP(wwin
, always_user_icon
) && wwin
->net_icon_image
)
195 image
= RRetainImage(wwin
->net_icon_image
);
197 /* get_icon_image() includes the default icon image */
199 image
= get_icon_image(panel
->scr
, wwin
->wm_instance
, wwin
->wm_class
, icon_tile_size
);
201 /* We must resize the icon size (~64) to the switch panel icon size (~48) */
202 image
= wIconValidateIconSize(image
, icon_size
);
204 WMAddToArray(panel
->images
, image
);
205 WMAddToArray(panel
->icons
, icon
);
208 static void scrollIcons(WSwitchPanel
*panel
, int delta
)
210 int nfirst
= panel
->firstVisible
+ delta
;
212 int count
= WMGetArrayItemCount(panel
->windows
);
215 if (count
<= panel
->visibleCount
)
220 else if (nfirst
>= count
- panel
->visibleCount
)
221 nfirst
= count
- panel
->visibleCount
;
223 if (nfirst
== panel
->firstVisible
)
226 WMMoveWidget(panel
->iconBox
, -nfirst
* icon_tile_size
, 0);
228 panel
->firstVisible
= nfirst
;
230 for (i
= panel
->firstVisible
; i
< panel
->firstVisible
+ panel
->visibleCount
; i
++) {
231 if (i
== panel
->current
)
233 dim
= ((int) (uintptr_t) WMGetFromArray(panel
->flags
, i
) & ICON_DIM
);
234 changeImage(panel
, i
, 0, dim
, True
);
243 static RImage
*assemblePuzzleImage(RImage
**images
, int width
, int height
)
250 tw
= width
- images
[0]->width
- images
[2]->width
;
251 th
= height
- images
[0]->height
- images
[6]->height
;
253 if (tw
<= 0 || th
<= 0)
256 img
= RCreateImage(width
, height
, 1);
264 RFillImage(img
, &color
);
267 tmp
= RSmoothScaleImage(images
[1], tw
, images
[1]->height
);
268 RCopyArea(img
, tmp
, 0, 0, tmp
->width
, tmp
->height
, images
[0]->width
, 0);
272 tmp
= RSmoothScaleImage(images
[7], tw
, images
[7]->height
);
273 RCopyArea(img
, tmp
, 0, 0, tmp
->width
, tmp
->height
, images
[6]->width
, height
- images
[6]->height
);
277 tmp
= RSmoothScaleImage(images
[3], images
[3]->width
, th
);
278 RCopyArea(img
, tmp
, 0, 0, tmp
->width
, tmp
->height
, 0, images
[0]->height
);
282 tmp
= RSmoothScaleImage(images
[5], images
[5]->width
, th
);
283 RCopyArea(img
, tmp
, 0, 0, tmp
->width
, tmp
->height
, width
- images
[5]->width
, images
[2]->height
);
287 tmp
= RSmoothScaleImage(images
[4], tw
, th
);
288 RCopyArea(img
, tmp
, 0, 0, tmp
->width
, tmp
->height
, images
[0]->width
, images
[0]->height
);
292 RCopyArea(img
, images
[0], 0, 0, images
[0]->width
, images
[0]->height
, 0, 0);
293 RCopyArea(img
, images
[2], 0, 0, images
[2]->width
, images
[2]->height
, width
- images
[2]->width
, 0);
294 RCopyArea(img
, images
[6], 0, 0, images
[6]->width
, images
[6]->height
, 0, height
- images
[6]->height
);
295 RCopyArea(img
, images
[8], 0, 0, images
[8]->width
, images
[8]->height
,
296 width
- images
[8]->width
, height
- images
[8]->height
);
301 static RImage
*createBackImage(int width
, int height
)
303 return assemblePuzzleImage(wPreferences
.swbackImage
, width
, height
);
306 static RImage
*getTile(void)
310 if (!wPreferences
.swtileImage
)
313 stile
= RScaleImage(wPreferences
.swtileImage
, icon_tile_size
, icon_tile_size
);
315 return wPreferences
.swtileImage
;
320 static void drawTitle(WSwitchPanel
*panel
, int idecks
, const char *title
)
323 int width
= WMWidgetWidth(panel
->win
);
327 ntitle
= ShrinkString(panel
->font
, title
, width
- 2 * border_space
);
333 if (strcmp(ntitle
, title
) != 0) {
336 int w
= WMWidthOfString(panel
->font
, ntitle
, strlen(ntitle
));
338 x
= border_space
+ (idecks
- panel
->firstVisible
) * icon_tile_size
+
339 icon_tile_size
/ 2 - w
/ 2;
340 if (x
< border_space
)
342 else if (x
+ w
> width
- border_space
)
343 x
= width
- border_space
- w
;
347 XClearWindow(dpy
, WMWidgetXID(panel
->win
));
349 WMDrawString(panel
->scr
->wmscreen
,
350 WMWidgetXID(panel
->win
),
351 panel
->white
, panel
->font
,
353 WMWidgetHeight(panel
->win
) - border_space
- label_height
+
354 WMFontHeight(panel
->font
) / 2, ntitle
, strlen(ntitle
));
357 WMSetLabelText(panel
->label
, ntitle
);
364 static WMArray
*makeWindowListArray(WScreen
*scr
, int include_unmapped
, Bool class_only
)
366 WMArray
*windows
= WMCreateArray(10);
367 WWindow
*wwin
= scr
->focused_window
;
370 if ((canReceiveFocus(wwin
) != 0) &&
371 (wwin
->flags
.mapped
|| wwin
->flags
.shaded
|| include_unmapped
)) {
373 if (!sameWindowClass(scr
->focused_window
, wwin
)) {
377 if (!WFLAGP(wwin
, skip_switchpanel
))
378 WMAddToArray(windows
, wwin
);
385 static WMArray
*makeWindowFlagsArray(int count
)
387 WMArray
*flags
= WMCreateArray(count
);
390 for (i
= 0; i
< count
; i
++)
391 WMAddToArray(flags
, (void *) 0);
396 WSwitchPanel
*wInitSwitchPanel(WScreen
*scr
, WWindow
*curwin
, Bool class_only
)
398 int wmScaleWidth
, wmScaleHeight
;
399 WMGetScaleBaseFromSystemFont(scr
->wmscreen
, &wmScaleWidth
, &wmScaleHeight
);
401 icon_size
= wPreferences
.switch_panel_icon_size
;
402 icon_tile_size
= (short int)(((float)icon_size
* 1.2F
) + 0.5F
);
403 border_space
= WMScaleY(10);
404 label_height
= WMScaleY(25);
407 WSwitchPanel
*panel
= wmalloc(sizeof(WSwitchPanel
));
409 int i
, width
, height
, iconsThatFitCount
, count
;
410 WMRect rect
= wGetRectForHead(scr
, wGetHeadForPointerLocation(scr
));
413 panel
->windows
= makeWindowListArray(scr
, wPreferences
.swtileImage
!= NULL
, class_only
);
414 count
= WMGetArrayItemCount(panel
->windows
);
416 panel
->flags
= makeWindowFlagsArray(count
);
419 WMFreeArray(panel
->windows
);
424 width
= icon_tile_size
* count
;
425 iconsThatFitCount
= count
;
427 if (width
> (int)rect
.size
.width
) {
428 iconsThatFitCount
= (rect
.size
.width
- SCREEN_BORDER_SPACING
) / icon_tile_size
;
429 width
= iconsThatFitCount
* icon_tile_size
;
432 panel
->visibleCount
= iconsThatFitCount
;
434 if (!wPreferences
.swtileImage
)
437 height
= label_height
+ icon_tile_size
;
439 panel
->tileTmp
= RCreateImage(icon_tile_size
, icon_tile_size
, 1);
440 panel
->tile
= getTile();
441 if (panel
->tile
&& wPreferences
.swbackImage
[8])
442 panel
->bg
= createBackImage(width
+ 2 * border_space
, height
+ 2 * border_space
);
444 if (!panel
->tileTmp
|| !panel
->tile
) {
446 RReleaseImage(panel
->bg
);
449 RReleaseImage(panel
->tile
);
452 RReleaseImage(panel
->tileTmp
);
453 panel
->tileTmp
= NULL
;
456 panel
->white
= WMWhiteColor(scr
->wmscreen
);
457 panel
->gray
= WMGrayColor(scr
->wmscreen
);
458 panel
->font
= WMBoldSystemFontOfSize(scr
->wmscreen
, WMScaleY(12));
459 panel
->icons
= WMCreateArray(count
);
460 panel
->images
= WMCreateArray(count
);
462 panel
->win
= WMCreateWindow(scr
->wmscreen
, "");
465 WMFrame
*frame
= WMCreateFrame(panel
->win
);
466 WMColor
*darkGray
= WMDarkGrayColor(scr
->wmscreen
);
467 WMSetFrameRelief(frame
, WRSimple
);
468 WMSetViewExpandsToParent(WMWidgetView(frame
), 0, 0, 0, 0);
470 panel
->label
= WMCreateLabel(panel
->win
);
471 WMResizeWidget(panel
->label
, width
, label_height
);
472 WMMoveWidget(panel
->label
, border_space
, border_space
+ icon_tile_size
+ 5);
473 WMSetLabelRelief(panel
->label
, WRSimple
);
474 WMSetWidgetBackgroundColor(panel
->label
, darkGray
);
475 WMSetLabelFont(panel
->label
, panel
->font
);
476 WMSetLabelTextColor(panel
->label
, panel
->white
);
478 WMReleaseColor(darkGray
);
482 WMResizeWidget(panel
->win
, width
+ 2 * border_space
, height
+ 2 * border_space
);
484 viewport
= WMCreateFrame(panel
->win
);
485 WMResizeWidget(viewport
, width
, icon_tile_size
);
486 WMMoveWidget(viewport
, border_space
, border_space
);
487 WMSetFrameRelief(viewport
, WRFlat
);
489 panel
->iconBox
= WMCreateFrame(viewport
);
490 WMMoveWidget(panel
->iconBox
, 0, 0);
491 WMResizeWidget(panel
->iconBox
, icon_tile_size
* count
, icon_tile_size
);
492 WMSetFrameRelief(panel
->iconBox
, WRFlat
);
494 WM_ITERATE_ARRAY(panel
->windows
, wwin
, i
) {
495 addIconForWindow(panel
, panel
->iconBox
, wwin
, i
* icon_tile_size
, 0);
498 WMMapSubwidgets(panel
->win
);
499 WMRealizeWidget(panel
->win
);
501 WM_ITERATE_ARRAY(panel
->windows
, wwin
, i
) {
502 changeImage(panel
, i
, 0, False
, True
);
508 RConvertImageMask(scr
->rcontext
, panel
->bg
, &pixmap
, &mask
, 250);
510 XSetWindowBackgroundPixmap(dpy
, WMWidgetXID(panel
->win
), pixmap
);
513 if (mask
&& w_global
.xext
.shape
.supported
)
514 XShapeCombineMask(dpy
, WMWidgetXID(panel
->win
), ShapeBounding
, 0, 0, mask
, ShapeSet
);
517 XFreePixmap(dpy
, pixmap
);
520 XFreePixmap(dpy
, mask
);
525 center
= wGetPointToCenterRectInHead(scr
, wGetHeadForPointerLocation(scr
),
526 width
+ 2 * border_space
, height
+ 2 * border_space
);
527 WMMoveWidget(panel
->win
, center
.x
, center
.y
);
530 panel
->current
= WMGetFirstInArray(panel
->windows
, curwin
);
531 if (panel
->current
>= 0)
532 changeImage(panel
, panel
->current
, 1, False
, False
);
534 WMMapWidget(panel
->win
);
539 void wSwitchPanelDestroy(WSwitchPanel
*panel
)
545 Window info_win
= panel
->scr
->info_window
;
547 ev
.xclient
.type
= ClientMessage
;
548 ev
.xclient
.message_type
= w_global
.atom
.wm
.ignore_focus_events
;
549 ev
.xclient
.format
= 32;
550 ev
.xclient
.data
.l
[0] = True
;
552 XSendEvent(dpy
, info_win
, True
, EnterWindowMask
, &ev
);
553 WMUnmapWidget(panel
->win
);
555 ev
.xclient
.data
.l
[0] = False
;
556 XSendEvent(dpy
, info_win
, True
, EnterWindowMask
, &ev
);
560 WM_ITERATE_ARRAY(panel
->images
, image
, i
) {
562 RReleaseImage(image
);
564 WMFreeArray(panel
->images
);
568 WMDestroyWidget(panel
->win
);
571 WMFreeArray(panel
->icons
);
574 WMFreeArray(panel
->flags
);
576 WMFreeArray(panel
->windows
);
579 RReleaseImage(panel
->tile
);
582 RReleaseImage(panel
->tileTmp
);
585 RReleaseImage(panel
->bg
);
588 WMReleaseFont(panel
->font
);
591 WMReleaseColor(panel
->white
);
594 WMReleaseColor(panel
->gray
);
599 WWindow
*wSwitchPanelSelectNext(WSwitchPanel
*panel
, int back
, int ignore_minimized
, Bool class_only
)
601 WWindow
*wwin
, *curwin
, *tmpwin
;
602 int count
= WMGetArrayItemCount(panel
->windows
);
603 int orig
= panel
->current
;
610 if (!wPreferences
.cycle_ignore_minimized
)
611 ignore_minimized
= False
;
613 if (ignore_minimized
&& canReceiveFocus(WMGetFromArray(panel
->windows
, (count
+ panel
->current
) % count
)) < 0)
614 ignore_minimized
= False
;
616 curwin
= WMGetFromArray(panel
->windows
, orig
);
624 panel
->current
= (count
+ panel
->current
) % count
;
625 wwin
= WMGetFromArray(panel
->windows
, panel
->current
);
629 if (panel
->current
== orig
)
631 } while (!sameWindowClass(wwin
, curwin
));
632 } while (ignore_minimized
&& panel
->current
!= orig
&& canReceiveFocus(wwin
) < 0);
634 WM_ITERATE_ARRAY(panel
->windows
, tmpwin
, i
) {
635 if (i
== panel
->current
)
637 if (!class_only
|| sameWindowClass(tmpwin
, curwin
))
638 changeImage(panel
, i
, 0, False
, False
);
642 changeImage(panel
, i
, 0, True
, False
);
647 if (panel
->current
< panel
->firstVisible
)
648 scrollIcons(panel
, panel
->current
- panel
->firstVisible
);
649 else if (panel
->current
- panel
->firstVisible
>= panel
->visibleCount
)
650 scrollIcons(panel
, panel
->current
- panel
->firstVisible
- panel
->visibleCount
+ 1);
653 drawTitle(panel
, panel
->current
, wwin
->frame
->title
);
654 if (panel
->current
!= orig
)
655 changeImage(panel
, orig
, 0, dim
, False
);
656 changeImage(panel
, panel
->current
, 1, False
, False
);
662 WWindow
*wSwitchPanelSelectFirst(WSwitchPanel
*panel
, int back
)
665 int count
= WMGetArrayItemCount(panel
->windows
);
673 panel
->current
= count
- 1;
674 scrollIcons(panel
, count
);
677 scrollIcons(panel
, -count
);
680 wwin
= WMGetFromArray(panel
->windows
, panel
->current
);
681 title
= wwin
->frame
->title
;
684 for (WMArrayFirst(panel
->windows
, &(i
)); (i
) != WANotFound
; WMArrayNext(panel
->windows
, &(i
)))
685 changeImage(panel
, i
, i
== panel
->current
, False
, False
);
687 drawTitle(panel
, panel
->current
, title
);
693 WWindow
*wSwitchPanelHandleEvent(WSwitchPanel
*panel
, XEvent
*event
)
702 if (event
->type
== MotionNotify
) {
703 WM_ITERATE_ARRAY(panel
->icons
, icon
, i
) {
704 if (WMWidgetXID(icon
) == event
->xmotion
.window
) {
711 if (focus
>= 0 && panel
->current
!= focus
) {
714 WM_ITERATE_ARRAY(panel
->windows
, wwin
, i
) {
715 changeImage(panel
, i
, i
== focus
, False
, False
);
717 panel
->current
= focus
;
719 wwin
= WMGetFromArray(panel
->windows
, focus
);
721 drawTitle(panel
, panel
->current
, wwin
->frame
->title
);
729 Window
wSwitchPanelGetWindow(WSwitchPanel
*swpanel
)
734 return WMWidgetXID(swpanel
->win
);