Change to the linux kernel coding style
[wmaker-crm.git] / src / switchpanel.c
dissimilarity index 89%
index 07c37f9..0cdb529 100644 (file)
-/*
- *  Window Maker window manager
- *
- *  Copyright (c) 1997-2004 Alfredo K. Kojima
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or
- *  (at your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License
- *  along with this program; if not, write to the Free Software
- *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
- *  USA.
- */
-
-#include "wconfig.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <sys/time.h>
-
-#include "WindowMaker.h"
-#include "screen.h"
-#include "wcore.h"
-#include "framewin.h"
-#include "window.h"
-#include "defaults.h"
-#include "switchpanel.h"
-#include "funcs.h"
-#include "xinerama.h"
-
-#ifdef SHAPE
-#include <X11/extensions/shape.h>
-
-extern Bool wShapeSupported;
-#endif
-
-
-struct SwitchPanel {
-    WScreen *scr;
-    WMWindow *win;
-    WMFrame *iconBox;
-    
-    WMArray *icons;
-    WMArray *images;
-    WMArray *windows;
-    RImage *bg;
-    int current;
-    int firstVisible;
-    int visibleCount;
-
-    WMLabel *label;
-    
-    RImage *defIcon;
-    
-    RImage *tileTmp;
-    RImage *tile;
-    
-    WMFont *font;
-    WMColor *white;
-};
-
-
-
-
-
-extern WPreferences wPreferences;
-
-#define BORDER_SPACE 10
-#define ICON_SIZE 48
-#define ICON_TILE_SIZE 64
-#define LABEL_HEIGHT 25
-#define SCREEN_BORDER_SPACING 2*20
-#define SCROLL_STEPS (ICON_TILE_SIZE/2)
-
-static int canReceiveFocus(WWindow *wwin)
-{
-  if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
-    return 0;
-  if (!wwin->flags.mapped)
-  {
-    if (!wwin->flags.shaded && !wwin->flags.miniaturized && !wwin->flags.hidden)
-      return 0;
-    else
-      return -1;
-  }
-  if (WFLAGP(wwin, no_focusable))
-    return 0;
-  return 1;
-}
-
-
-static void changeImage(WSwitchPanel *panel, int idecks, int selected)
-{
-    WMFrame *icon = WMGetFromArray(panel->icons, idecks);
-    RImage *image= WMGetFromArray(panel->images, idecks);
-
-    if (!panel->bg && !panel->tile) {
-        if (!selected)
-          WMSetFrameRelief(icon, WRFlat);
-    }
-
-    if (image && icon) {
-        RImage *back;
-        int opaq= 255;
-        RImage *tile;
-        WMPoint pos;
-        Pixmap p;
-
-        if (canReceiveFocus(WMGetFromArray(panel->windows, idecks)) < 0)
-          opaq= 50;
-
-        pos= WMGetViewPosition(WMWidgetView(icon));
-        back= panel->tileTmp;
-        if (panel->bg)
-          RCopyArea(back, panel->bg,
-                    BORDER_SPACE+pos.x-panel->firstVisible*ICON_TILE_SIZE, BORDER_SPACE+pos.y,
-                    back->width, back->height,
-                    0, 0);
-        else
-        {
-            RColor color;
-            WMScreen *wscr= WMWidgetScreen(icon);
-            color.red= 255;
-            color.red = WMRedComponentOfColor(WMGrayColor(wscr))>>8;
-            color.green = WMGreenComponentOfColor(WMGrayColor(wscr))>>8;
-            color.blue = WMBlueComponentOfColor(WMGrayColor(wscr))>>8;
-            RFillImage(back, &color);
-        }
-        if (selected)
-        {
-            tile= panel->tile;
-            RCombineArea(back, tile, 0, 0, tile->width, tile->height,
-                         (back->width - tile->width)/2, (back->height - tile->height)/2);
-        }
-        RCombineAreaWithOpaqueness(back, image, 0, 0, image->width, image->height,
-                                   (back->width - image->width)/2, (back->height - image->height)/2,
-                                   opaq);
-
-        RConvertImage(panel->scr->rcontext, back, &p);
-        XSetWindowBackgroundPixmap(dpy, WMWidgetXID(icon), p);
-        XClearWindow(dpy, WMWidgetXID(icon));
-        XFreePixmap(dpy, p);
-    }
-
-    if (!panel->bg && !panel->tile) {
-        if (selected)
-          WMSetFrameRelief(icon, WRSimple);
-    }
-}
-
-
-static RImage *scaleDownIfNeeded(RImage *image)
-{
-    if (image && ((image->width - ICON_SIZE) > 2 || (image->height - ICON_SIZE) > 2)) {
-        RImage *nimage;
-        nimage= RScaleImage(image, ICON_SIZE, (image->height * ICON_SIZE / image->width));
-        RReleaseImage(image);
-        image= nimage;
-    }
-    return image;
-}
-
-
-static void addIconForWindow(WSwitchPanel *panel, WMWidget *parent, WWindow *wwin,
-                             int x, int y)
-{
-    WMFrame *icon= WMCreateFrame(parent);
-    RImage *image = NULL;
-
-    WMSetFrameRelief(icon, WRFlat);
-    WMResizeWidget(icon, ICON_TILE_SIZE, ICON_TILE_SIZE);
-    WMMoveWidget(icon, x, y);
-    
-    if (!WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
-      image = RRetainImage(wwin->net_icon_image);
-    
-    // Make this use a caching thing. When there are many windows,
-    // it's very likely that most of them are instances of the same thing,
-    // so caching them should get performance acceptable in these cases.
-    if (!image)
-      image = wDefaultGetImage(panel->scr, wwin->wm_instance, wwin->wm_class);
-    
-    if (!image && !panel->defIcon) {
-        char *file = wDefaultGetIconFile(panel->scr, NULL, NULL, False);
-        if (file) {
-            char *path = FindImage(wPreferences.icon_path, file);
-            if (path) {
-                image = RLoadImage(panel->scr->rcontext, path, 0);
-                wfree(path);
-            }
-        }
-        if (image)
-          panel->defIcon= scaleDownIfNeeded(image);
-        image= NULL;
-    }
-    if (!image && panel->defIcon)
-      image= RRetainImage(panel->defIcon);
-    
-    image= scaleDownIfNeeded(image);
-    
-    WMAddToArray(panel->images, image);
-    WMAddToArray(panel->icons, icon);
-}
-
-
-static void scrollIcons(WSwitchPanel *panel, int delta)
-{
-    int nfirst= panel->firstVisible + delta;
-    int i;
-    int count= WMGetArrayItemCount(panel->windows);
-//    int nx, ox;
-//    struct timeval tv1, tv2;
-
-    if (count <= panel->visibleCount)
-      return;
-
-    if (nfirst < 0)
-      nfirst= 0;
-    else if (nfirst >= count-panel->visibleCount)
-      nfirst= count-panel->visibleCount;
-
-    if (nfirst == panel->firstVisible)
-      return;
-/*
-    ox = -panel->firstVisible * ICON_TILE_SIZE;
-    nx = -nfirst * ICON_TILE_SIZE;
-    for (i= 0; i < SCROLL_STEPS; i++) {
-        unsigned int diff;
-        gettimeofday(&tv1, NULL);
-        WMMoveWidget(panel->iconBox, (nx*i + ox*(SCROLL_STEPS-i))/(SCROLL_STEPS-1), 0);
-        XSync(dpy, False);
-        gettimeofday(&tv2, NULL);
-        diff = (tv2.tv_sec-tv1.tv_sec)*10000+(tv2.tv_usec-tv1.tv_usec)/100;
-        if (diff < 200)
-          wusleep(300-diff);
-    }
- */    
-    WMMoveWidget(panel->iconBox, -nfirst*ICON_TILE_SIZE, 0);
-
-    panel->firstVisible= nfirst;
-    
-    for (i= panel->firstVisible; i < panel->firstVisible+panel->visibleCount; i++) {
-        changeImage(panel, i, i == panel->current);
-    }
-}
-
-
-/*
- * 0 1 2
- * 3 4 5
- * 6 7 8
- */
-static RImage *assemblePuzzleImage(RImage **images, int width, int height)
-{
-    RImage *img= RCreateImage(width, height, 1);
-    RImage *tmp;
-    int tw, th;
-    RColor color;
-    if (!img)
-      return NULL;
-
-    color.red= 0;
-    color.green= 0;
-    color.blue= 0;
-    color.alpha= 255;
-
-    RFillImage(img, &color);
-
-    tw= width-images[0]->width-images[2]->width;
-    th= height-images[0]->height-images[6]->height;
-
-    if (tw <= 0 || th <= 0) {
-        //XXX
-        return NULL;
-    }
-
-    /* top */
-    if (tw > 0) {
-        tmp= RSmoothScaleImage(images[1], tw, images[1]->height);
-        RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
-                  images[0]->width, 0);
-        RReleaseImage(tmp);
-    }
-    /* bottom */
-    if (tw > 0) {
-        tmp= RSmoothScaleImage(images[7], tw, images[7]->height);
-        RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
-                  images[6]->width, height-images[6]->height);
-        RReleaseImage(tmp);
-    }
-    /* left */
-    if (th > 0) {
-        tmp= RSmoothScaleImage(images[3], images[3]->width, th);
-        RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
-                  0, images[0]->height);
-        RReleaseImage(tmp);
-    }
-    /* right */
-    if (th > 0) {
-        tmp= RSmoothScaleImage(images[5], images[5]->width, th);
-        RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
-                  width-images[5]->width, images[2]->height);
-        RReleaseImage(tmp);
-    }
-    /* center */
-    if (tw > 0 && th > 0) {
-        tmp= RSmoothScaleImage(images[4], tw, th);
-        RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height,
-                  images[0]->width, images[0]->height);
-        RReleaseImage(tmp);
-    }
-
-    /* corners */
-    RCopyArea(img, images[0], 0, 0, images[0]->width, images[0]->height,
-              0, 0);
-
-    RCopyArea(img, images[2], 0, 0, images[2]->width, images[2]->height,
-              width-images[2]->width, 0);
-
-    RCopyArea(img, images[6], 0, 0, images[6]->width, images[6]->height,
-              0, height-images[6]->height);
-
-    RCopyArea(img, images[8], 0, 0, images[8]->width, images[8]->height,
-              width-images[8]->width, height-images[8]->height);
-
-    return img;
-}
-
-static RImage *createBackImage(WScreen *scr, int width, int height)
-{
-    return assemblePuzzleImage(wPreferences.swbackImage, width, height);
-}
-
-
-static RImage *getTile(WSwitchPanel *panel)
-{
-    RImage *stile;
-
-    if (!wPreferences.swtileImage)
-        return NULL;
-
-    stile = RScaleImage(wPreferences.swtileImage, ICON_TILE_SIZE, ICON_TILE_SIZE);
-    if (!stile)
-        return wPreferences.swtileImage;
-    
-    return stile;
-}
-
-
-static void
-drawTitle(WSwitchPanel *panel, int idecks, char *title)
-{
-    char *ntitle;
-    int width= WMWidgetWidth(panel->win);
-    int x;
-
-    if (title)
-        ntitle= ShrinkString(panel->font, title, width-2*BORDER_SPACE);
-    else
-        ntitle= NULL;
-
-    if (panel->bg) {
-        if (ntitle) {
-            if (strcmp(ntitle, title)!=0)
-              x= BORDER_SPACE;
-            else
-            {
-                int w= WMWidthOfString(panel->font, ntitle, strlen(ntitle));
-            
-                x= BORDER_SPACE+(idecks-panel->firstVisible)*ICON_TILE_SIZE + ICON_TILE_SIZE/2 - w/2;
-                if (x < BORDER_SPACE)
-                    x= BORDER_SPACE;
-                else if (x + w > width-BORDER_SPACE)
-                    x= width-BORDER_SPACE-w;
-            }
-        }       
-        XClearWindow(dpy, WMWidgetXID(panel->win));
-        if (ntitle)
-            WMDrawString(panel->scr->wmscreen, 
-                         WMWidgetXID(panel->win),
-                         panel->white, panel->font,
-                         x, WMWidgetHeight(panel->win) - BORDER_SPACE - LABEL_HEIGHT + WMFontHeight(panel->font)/2,
-                         ntitle, strlen(ntitle));
-    } else {
-        if (ntitle)
-            WMSetLabelText(panel->label, ntitle);
-    }
-    if (ntitle)
-        free(ntitle);
-}
-
-
-
-static WMArray *makeWindowListArray(WScreen *scr, WWindow *curwin, int workspace,
-                                    int include_unmapped)
-{
-    WMArray *windows= WMCreateArray(10);
-    int fl;
-    WWindow *wwin;
-
-    for (fl= 0; fl < 2; fl++) {
-        for (wwin= curwin; wwin; wwin= wwin->prev) {
-            if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
-                (!WFLAGP(wwin, skip_window_list) || wwin->flags.internal_window) &&
-                (wwin->flags.mapped || include_unmapped)) {
-                WMAddToArray(windows, wwin);
-            }
-        }
-        wwin = curwin;
-        /* start over from the beginning of the list */
-        while (wwin->next)
-          wwin = wwin->next;
-        
-        for (wwin= curwin; wwin && wwin != curwin; wwin= wwin->prev) {
-            if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
-                (!WFLAGP(wwin, skip_window_list) || wwin->flags.internal_window) &&
-                (wwin->flags.mapped || include_unmapped)) {
-                WMAddToArray(windows, wwin);
-            }
-        }
-    }
-
-    return windows;
-}
-
-
-
-
-
-WSwitchPanel *wInitSwitchPanel(WScreen *scr, WWindow *curwin, int workspace)
-{
-    WWindow *wwin;
-    WSwitchPanel *panel= wmalloc(sizeof(WSwitchPanel));
-    WMFrame *viewport;
-    int i;
-    int width, height;
-    int iconsThatFitCount;
-    int count;
-    WMRect rect;
-    rect= wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
-
-    memset(panel, 0, sizeof(WSwitchPanel));
-
-    panel->scr= scr;
-
-    panel->windows= makeWindowListArray(scr, curwin, workspace,
-                                        wPreferences.swtileImage!=0);
-    count= WMGetArrayItemCount(panel->windows);
-    
-    if (count == 0) {
-        WMFreeArray(panel->windows);
-        wfree(panel);
-        return NULL;
-    }
-
-    width= ICON_TILE_SIZE*count;
-    iconsThatFitCount= count;
-
-    if (width > rect.size.width) {
-        iconsThatFitCount = (rect.size.width - SCREEN_BORDER_SPACING)/ICON_TILE_SIZE;
-        width= iconsThatFitCount*ICON_TILE_SIZE;
-    }
-    
-    panel->visibleCount= iconsThatFitCount;
-
-    if (!wPreferences.swtileImage)
-        return panel;
-
-    height= LABEL_HEIGHT + ICON_TILE_SIZE;
-
-    panel->tileTmp= RCreateImage(ICON_TILE_SIZE, ICON_TILE_SIZE, 1);
-    panel->tile= getTile(panel);
-    if (panel->tile && wPreferences.swbackImage[8]) {
-        panel->bg= createBackImage(scr, width+2*BORDER_SPACE, height+2*BORDER_SPACE);
-    }
-    if (!panel->tileTmp || !panel->tile) {
-        if (panel->bg)
-          RReleaseImage(panel->bg);
-        panel->bg= NULL;
-        if (panel->tile)
-          RReleaseImage(panel->tile);
-        panel->tile= NULL;
-        if (panel->tileTmp)
-          RReleaseImage(panel->tileTmp);
-        panel->tileTmp= NULL;
-    }
-
-    panel->white= WMWhiteColor(scr->wmscreen);
-    panel->font= WMBoldSystemFontOfSize(scr->wmscreen, 12);
-    panel->icons= WMCreateArray(count);
-    panel->images= WMCreateArray(count);
-
-    panel->win = WMCreateWindow(scr->wmscreen, "");
-
-    if (!panel->bg) {
-        WMFrame *frame = WMCreateFrame(panel->win);
-        WMColor *darkGray = WMDarkGrayColor(scr->wmscreen);
-        WMSetFrameRelief(frame, WRSimple);
-        WMSetViewExpandsToParent(WMWidgetView(frame), 0, 0, 0, 0);
-        
-        panel->label = WMCreateLabel(panel->win);
-        WMResizeWidget(panel->label, width, LABEL_HEIGHT);
-        WMMoveWidget(panel->label, BORDER_SPACE, BORDER_SPACE+ICON_TILE_SIZE+5);
-        WMSetLabelRelief(panel->label, WRSimple);
-        WMSetWidgetBackgroundColor(panel->label, darkGray);
-        WMSetLabelFont(panel->label, panel->font);
-        WMSetLabelTextColor(panel->label, panel->white);
-        
-        WMReleaseColor(darkGray);
-        height+= 5;
-    }
-    
-    WMResizeWidget(panel->win, width + 2*BORDER_SPACE, height + 2*BORDER_SPACE);
-    
-    viewport= WMCreateFrame(panel->win);
-    WMResizeWidget(viewport, width, ICON_TILE_SIZE);
-    WMMoveWidget(viewport, BORDER_SPACE, BORDER_SPACE);
-    WMSetFrameRelief(viewport, WRFlat);
-    
-    panel->iconBox= WMCreateFrame(viewport);
-    WMMoveWidget(panel->iconBox, 0, 0);
-    WMResizeWidget(panel->iconBox, ICON_TILE_SIZE*count, ICON_TILE_SIZE);
-    WMSetFrameRelief(panel->iconBox, WRFlat);
-    
-    WM_ITERATE_ARRAY(panel->windows, wwin, i) {
-        addIconForWindow(panel, panel->iconBox, wwin, i*ICON_TILE_SIZE, 0);
-    }
-
-    WMMapSubwidgets(panel->win);
-    WMRealizeWidget(panel->win);
-    
-    WM_ITERATE_ARRAY(panel->windows, wwin, i) {
-        changeImage(panel, i, 0);
-    }
-    
-    if (panel->bg) {
-        Pixmap pixmap, mask;
-
-        RConvertImageMask(scr->rcontext, panel->bg, &pixmap, &mask, 250);
-
-        XSetWindowBackgroundPixmap(dpy, WMWidgetXID(panel->win), pixmap);
-
-#ifdef SHAPE
-        if (mask && wShapeSupported)
-          XShapeCombineMask(dpy, WMWidgetXID(panel->win),
-                            ShapeBounding, 0, 0, mask, ShapeSet);
-#endif
-        
-        if (pixmap)
-          XFreePixmap(dpy, pixmap);
-        if (mask)
-          XFreePixmap(dpy, mask);
-    }
-
-    {
-        WMPoint center;
-        center= wGetPointToCenterRectInHead(scr, wGetHeadForPointerLocation(scr),
-                                            width+2*BORDER_SPACE, height+2*BORDER_SPACE);
-        WMMoveWidget(panel->win, center.x, center.y);
-    }
-
-    panel->current= WMGetFirstInArray(panel->windows, curwin);
-    if (panel->current >= 0)
-        changeImage(panel, panel->current, 1);
-
-    WMMapWidget(panel->win);
-
-    return panel;
-}
-
-
-void wSwitchPanelDestroy(WSwitchPanel *panel)
-{
-    int i;
-    RImage *image;
-    
-    if (panel->win)
-      WMUnmapWidget(panel->win);
-
-    if (panel->images) {
-        WM_ITERATE_ARRAY(panel->images, image, i) {
-            if (image)
-              RReleaseImage(image);
-        }
-        WMFreeArray(panel->images);
-    }
-    if (panel->win)
-      WMDestroyWidget(panel->win);
-    if (panel->icons)
-      WMFreeArray(panel->icons);
-    WMFreeArray(panel->windows);
-    if (panel->defIcon)
-      RReleaseImage(panel->defIcon);
-    if (panel->tile)
-      RReleaseImage(panel->tile);
-    if (panel->tileTmp)
-      RReleaseImage(panel->tileTmp);
-    if (panel->bg)
-      RReleaseImage(panel->bg);
-    if (panel->font)
-      WMReleaseFont(panel->font);
-    if (panel->white)
-      WMReleaseColor(panel->white);
-    wfree(panel);
-}
-
-
-WWindow *wSwitchPanelSelectNext(WSwitchPanel *panel, int back)
-{
-    WWindow *wwin;
-    int count = WMGetArrayItemCount(panel->windows);
-    
-    if (count == 0)
-      return NULL;
-    
-    if (panel->win)
-      changeImage(panel, panel->current, 0);
-    
-    if (back)
-      panel->current--;
-    else
-      panel->current++;
-
-    wwin = WMGetFromArray(panel->windows, (count+panel->current)%count);
-
-    if (back)
-    {
-        if (panel->current < 0)
-          scrollIcons(panel, count);
-        else if (panel->current < panel->firstVisible)
-          scrollIcons(panel, -1);
-    }
-    else
-    {
-        if (panel->current >= count)
-          scrollIcons(panel, -count);
-        else if (panel->current - panel->firstVisible >= panel->visibleCount)
-          scrollIcons(panel, 1);
-    }
-
-    panel->current= (count+panel->current)%count;
-
-    if (panel->win) {
-        drawTitle(panel, panel->current, wwin->frame->title);
-
-        changeImage(panel, panel->current, 1);
-    }
-
-    return wwin;
-}
-
-
-WWindow *wSwitchPanelSelectFirst(WSwitchPanel *panel, int back)
-{
-    WWindow *wwin;
-    int count = WMGetArrayItemCount(panel->windows);
-
-    if (count == 0)
-      return NULL;
-
-    if (panel->win)
-      changeImage(panel, panel->current, 0);
-    
-    if (back) {
-        panel->current = count-1;
-        scrollIcons(panel, count);
-    } else {
-        panel->current = 0;
-        scrollIcons(panel, -count);
-    }
-    
-    wwin = WMGetFromArray(panel->windows, panel->current);
-    
-    if (panel->win) {
-        drawTitle(panel, panel->current, wwin->frame->title);
-
-        changeImage(panel, panel->current, 1);
-    }
-    return wwin;
-}
-
-
-WWindow *wSwitchPanelHandleEvent(WSwitchPanel *panel, XEvent *event)
-{
-    WMFrame *icon;
-    int i;
-    int focus= -1;
-    
-    if (!panel->win)
-      return NULL;
-
- /*   if (event->type == LeaveNotify) {
-        if (event->xcrossing.window == WMWidgetXID(panel->win))
-            focus= 0;
-    } else*/ if (event->type == MotionNotify) {
-
-        WM_ITERATE_ARRAY(panel->icons, icon, i) {
-            if (WMWidgetXID(icon) == event->xmotion.window) {
-                focus= i;
-                break;
-            }
-        }
-    }  
-    if (focus >= 0 && panel->current != focus) {
-        WWindow *wwin;
-            
-        changeImage(panel, panel->current, 0);
-        changeImage(panel, focus, 1);
-        panel->current= focus;
-            
-        wwin= WMGetFromArray(panel->windows, focus);
-            
-        drawTitle(panel, panel->current, wwin->frame->title);
-            
-        return wwin;
-    }
-
-    return NULL;
-}
-
-
-Window wSwitchPanelGetWindow(WSwitchPanel *swpanel)
-{
-    if (!swpanel->win)
-      return None;
-    return WMWidgetXID(swpanel->win);
-}
+/*
+ *  Window Maker window manager
+ *
+ *  Copyright (c) 1997-2004 Alfredo K. Kojima
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ *  USA.
+ */
+
+#include "wconfig.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "WindowMaker.h"
+#include "screen.h"
+#include "wcore.h"
+#include "framewin.h"
+#include "window.h"
+#include "defaults.h"
+#include "switchpanel.h"
+#include "funcs.h"
+#include "xinerama.h"
+
+#ifdef SHAPE
+#include <X11/extensions/shape.h>
+
+extern Bool wShapeSupported;
+#endif
+
+struct SwitchPanel {
+       WScreen *scr;
+       WMWindow *win;
+       WMFrame *iconBox;
+
+       WMArray *icons;
+       WMArray *images;
+       WMArray *windows;
+       RImage *bg;
+       int current;
+       int firstVisible;
+       int visibleCount;
+
+       WMLabel *label;
+
+       RImage *defIcon;
+
+       RImage *tileTmp;
+       RImage *tile;
+
+       WMFont *font;
+       WMColor *white;
+};
+
+extern WPreferences wPreferences;
+
+#define BORDER_SPACE 10
+#define ICON_SIZE 48
+#define ICON_TILE_SIZE 64
+#define LABEL_HEIGHT 25
+#define SCREEN_BORDER_SPACING 2*20
+#define SCROLL_STEPS (ICON_TILE_SIZE/2)
+
+static int canReceiveFocus(WWindow * wwin)
+{
+       if (wwin->frame->workspace != wwin->screen_ptr->current_workspace)
+               return 0;
+       if (!wwin->flags.mapped) {
+               if (!wwin->flags.shaded && !wwin->flags.miniaturized && !wwin->flags.hidden)
+                       return 0;
+               else
+                       return -1;
+       }
+       if (WFLAGP(wwin, no_focusable))
+               return 0;
+       return 1;
+}
+
+static void changeImage(WSwitchPanel * panel, int idecks, int selected)
+{
+       WMFrame *icon = WMGetFromArray(panel->icons, idecks);
+       RImage *image = WMGetFromArray(panel->images, idecks);
+
+       if (!panel->bg && !panel->tile) {
+               if (!selected)
+                       WMSetFrameRelief(icon, WRFlat);
+       }
+
+       if (image && icon) {
+               RImage *back;
+               int opaq = 255;
+               RImage *tile;
+               WMPoint pos;
+               Pixmap p;
+
+               if (canReceiveFocus(WMGetFromArray(panel->windows, idecks)) < 0)
+                       opaq = 50;
+
+               pos = WMGetViewPosition(WMWidgetView(icon));
+               back = panel->tileTmp;
+               if (panel->bg)
+                       RCopyArea(back, panel->bg,
+                                 BORDER_SPACE + pos.x - panel->firstVisible * ICON_TILE_SIZE,
+                                 BORDER_SPACE + pos.y, back->width, back->height, 0, 0);
+               else {
+                       RColor color;
+                       WMScreen *wscr = WMWidgetScreen(icon);
+                       color.red = 255;
+                       color.red = WMRedComponentOfColor(WMGrayColor(wscr)) >> 8;
+                       color.green = WMGreenComponentOfColor(WMGrayColor(wscr)) >> 8;
+                       color.blue = WMBlueComponentOfColor(WMGrayColor(wscr)) >> 8;
+                       RFillImage(back, &color);
+               }
+               if (selected) {
+                       tile = panel->tile;
+                       RCombineArea(back, tile, 0, 0, tile->width, tile->height,
+                                    (back->width - tile->width) / 2, (back->height - tile->height) / 2);
+               }
+               RCombineAreaWithOpaqueness(back, image, 0, 0, image->width, image->height,
+                                          (back->width - image->width) / 2, (back->height - image->height) / 2,
+                                          opaq);
+
+               RConvertImage(panel->scr->rcontext, back, &p);
+               XSetWindowBackgroundPixmap(dpy, WMWidgetXID(icon), p);
+               XClearWindow(dpy, WMWidgetXID(icon));
+               XFreePixmap(dpy, p);
+       }
+
+       if (!panel->bg && !panel->tile) {
+               if (selected)
+                       WMSetFrameRelief(icon, WRSimple);
+       }
+}
+
+static RImage *scaleDownIfNeeded(RImage * image)
+{
+       if (image && ((image->width - ICON_SIZE) > 2 || (image->height - ICON_SIZE) > 2)) {
+               RImage *nimage;
+               nimage = RScaleImage(image, ICON_SIZE, (image->height * ICON_SIZE / image->width));
+               RReleaseImage(image);
+               image = nimage;
+       }
+       return image;
+}
+
+static void addIconForWindow(WSwitchPanel * panel, WMWidget * parent, WWindow * wwin, int x, int y)
+{
+       WMFrame *icon = WMCreateFrame(parent);
+       RImage *image = NULL;
+
+       WMSetFrameRelief(icon, WRFlat);
+       WMResizeWidget(icon, ICON_TILE_SIZE, ICON_TILE_SIZE);
+       WMMoveWidget(icon, x, y);
+
+       if (!WFLAGP(wwin, always_user_icon) && wwin->net_icon_image)
+               image = RRetainImage(wwin->net_icon_image);
+
+       // Make this use a caching thing. When there are many windows,
+       // it's very likely that most of them are instances of the same thing,
+       // so caching them should get performance acceptable in these cases.
+       if (!image)
+               image = wDefaultGetImage(panel->scr, wwin->wm_instance, wwin->wm_class);
+
+       if (!image && !panel->defIcon) {
+               char *file = wDefaultGetIconFile(panel->scr, NULL, NULL, False);
+               if (file) {
+                       char *path = FindImage(wPreferences.icon_path, file);
+                       if (path) {
+                               image = RLoadImage(panel->scr->rcontext, path, 0);
+                               wfree(path);
+                       }
+               }
+               if (image)
+                       panel->defIcon = scaleDownIfNeeded(image);
+               image = NULL;
+       }
+       if (!image && panel->defIcon)
+               image = RRetainImage(panel->defIcon);
+
+       image = scaleDownIfNeeded(image);
+
+       WMAddToArray(panel->images, image);
+       WMAddToArray(panel->icons, icon);
+}
+
+static void scrollIcons(WSwitchPanel * panel, int delta)
+{
+       int nfirst = panel->firstVisible + delta;
+       int i;
+       int count = WMGetArrayItemCount(panel->windows);
+//    int nx, ox;
+//    struct timeval tv1, tv2;
+
+       if (count <= panel->visibleCount)
+               return;
+
+       if (nfirst < 0)
+               nfirst = 0;
+       else if (nfirst >= count - panel->visibleCount)
+               nfirst = count - panel->visibleCount;
+
+       if (nfirst == panel->firstVisible)
+               return;
+/*
+    ox = -panel->firstVisible * ICON_TILE_SIZE;
+    nx = -nfirst * ICON_TILE_SIZE;
+    for (i= 0; i < SCROLL_STEPS; i++) {
+        unsigned int diff;
+        gettimeofday(&tv1, NULL);
+        WMMoveWidget(panel->iconBox, (nx*i + ox*(SCROLL_STEPS-i))/(SCROLL_STEPS-1), 0);
+        XSync(dpy, False);
+        gettimeofday(&tv2, NULL);
+        diff = (tv2.tv_sec-tv1.tv_sec)*10000+(tv2.tv_usec-tv1.tv_usec)/100;
+        if (diff < 200)
+          wusleep(300-diff);
+    }
+ */
+       WMMoveWidget(panel->iconBox, -nfirst * ICON_TILE_SIZE, 0);
+
+       panel->firstVisible = nfirst;
+
+       for (i = panel->firstVisible; i < panel->firstVisible + panel->visibleCount; i++) {
+               changeImage(panel, i, i == panel->current);
+       }
+}
+
+/*
+ * 0 1 2
+ * 3 4 5
+ * 6 7 8
+ */
+static RImage *assemblePuzzleImage(RImage ** images, int width, int height)
+{
+       RImage *img = RCreateImage(width, height, 1);
+       RImage *tmp;
+       int tw, th;
+       RColor color;
+       if (!img)
+               return NULL;
+
+       color.red = 0;
+       color.green = 0;
+       color.blue = 0;
+       color.alpha = 255;
+
+       RFillImage(img, &color);
+
+       tw = width - images[0]->width - images[2]->width;
+       th = height - images[0]->height - images[6]->height;
+
+       if (tw <= 0 || th <= 0) {
+               //XXX
+               return NULL;
+       }
+
+       /* top */
+       if (tw > 0) {
+               tmp = RSmoothScaleImage(images[1], tw, images[1]->height);
+               RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, 0);
+               RReleaseImage(tmp);
+       }
+       /* bottom */
+       if (tw > 0) {
+               tmp = RSmoothScaleImage(images[7], tw, images[7]->height);
+               RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[6]->width, height - images[6]->height);
+               RReleaseImage(tmp);
+       }
+       /* left */
+       if (th > 0) {
+               tmp = RSmoothScaleImage(images[3], images[3]->width, th);
+               RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, 0, images[0]->height);
+               RReleaseImage(tmp);
+       }
+       /* right */
+       if (th > 0) {
+               tmp = RSmoothScaleImage(images[5], images[5]->width, th);
+               RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, width - images[5]->width, images[2]->height);
+               RReleaseImage(tmp);
+       }
+       /* center */
+       if (tw > 0 && th > 0) {
+               tmp = RSmoothScaleImage(images[4], tw, th);
+               RCopyArea(img, tmp, 0, 0, tmp->width, tmp->height, images[0]->width, images[0]->height);
+               RReleaseImage(tmp);
+       }
+
+       /* corners */
+       RCopyArea(img, images[0], 0, 0, images[0]->width, images[0]->height, 0, 0);
+
+       RCopyArea(img, images[2], 0, 0, images[2]->width, images[2]->height, width - images[2]->width, 0);
+
+       RCopyArea(img, images[6], 0, 0, images[6]->width, images[6]->height, 0, height - images[6]->height);
+
+       RCopyArea(img, images[8], 0, 0, images[8]->width, images[8]->height,
+                 width - images[8]->width, height - images[8]->height);
+
+       return img;
+}
+
+static RImage *createBackImage(WScreen * scr, int width, int height)
+{
+       return assemblePuzzleImage(wPreferences.swbackImage, width, height);
+}
+
+static RImage *getTile(WSwitchPanel * panel)
+{
+       RImage *stile;
+
+       if (!wPreferences.swtileImage)
+               return NULL;
+
+       stile = RScaleImage(wPreferences.swtileImage, ICON_TILE_SIZE, ICON_TILE_SIZE);
+       if (!stile)
+               return wPreferences.swtileImage;
+
+       return stile;
+}
+
+static void drawTitle(WSwitchPanel * panel, int idecks, char *title)
+{
+       char *ntitle;
+       int width = WMWidgetWidth(panel->win);
+       int x;
+
+       if (title)
+               ntitle = ShrinkString(panel->font, title, width - 2 * BORDER_SPACE);
+       else
+               ntitle = NULL;
+
+       if (panel->bg) {
+               if (ntitle) {
+                       if (strcmp(ntitle, title) != 0)
+                               x = BORDER_SPACE;
+                       else {
+                               int w = WMWidthOfString(panel->font, ntitle, strlen(ntitle));
+
+                               x = BORDER_SPACE + (idecks - panel->firstVisible) * ICON_TILE_SIZE +
+                                   ICON_TILE_SIZE / 2 - w / 2;
+                               if (x < BORDER_SPACE)
+                                       x = BORDER_SPACE;
+                               else if (x + w > width - BORDER_SPACE)
+                                       x = width - BORDER_SPACE - w;
+                       }
+               }
+               XClearWindow(dpy, WMWidgetXID(panel->win));
+               if (ntitle)
+                       WMDrawString(panel->scr->wmscreen,
+                                    WMWidgetXID(panel->win),
+                                    panel->white, panel->font,
+                                    x,
+                                    WMWidgetHeight(panel->win) - BORDER_SPACE - LABEL_HEIGHT +
+                                    WMFontHeight(panel->font) / 2, ntitle, strlen(ntitle));
+       } else {
+               if (ntitle)
+                       WMSetLabelText(panel->label, ntitle);
+       }
+       if (ntitle)
+               free(ntitle);
+}
+
+static WMArray *makeWindowListArray(WScreen * scr, WWindow * curwin, int workspace, int include_unmapped)
+{
+       WMArray *windows = WMCreateArray(10);
+       int fl;
+       WWindow *wwin;
+
+       for (fl = 0; fl < 2; fl++) {
+               for (wwin = curwin; wwin; wwin = wwin->prev) {
+                       if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
+                           (!WFLAGP(wwin, skip_window_list) || wwin->flags.internal_window) &&
+                           (wwin->flags.mapped || include_unmapped)) {
+                               WMAddToArray(windows, wwin);
+                       }
+               }
+               wwin = curwin;
+               /* start over from the beginning of the list */
+               while (wwin->next)
+                       wwin = wwin->next;
+
+               for (wwin = curwin; wwin && wwin != curwin; wwin = wwin->prev) {
+                       if (((!fl && canReceiveFocus(wwin) > 0) || (fl && canReceiveFocus(wwin) < 0)) &&
+                           (!WFLAGP(wwin, skip_window_list) || wwin->flags.internal_window) &&
+                           (wwin->flags.mapped || include_unmapped)) {
+                               WMAddToArray(windows, wwin);
+                       }
+               }
+       }
+
+       return windows;
+}
+
+WSwitchPanel *wInitSwitchPanel(WScreen * scr, WWindow * curwin, int workspace)
+{
+       WWindow *wwin;
+       WSwitchPanel *panel = wmalloc(sizeof(WSwitchPanel));
+       WMFrame *viewport;
+       int i;
+       int width, height;
+       int iconsThatFitCount;
+       int count;
+       WMRect rect;
+       rect = wGetRectForHead(scr, wGetHeadForPointerLocation(scr));
+
+       memset(panel, 0, sizeof(WSwitchPanel));
+
+       panel->scr = scr;
+
+       panel->windows = makeWindowListArray(scr, curwin, workspace, wPreferences.swtileImage != 0);
+       count = WMGetArrayItemCount(panel->windows);
+
+       if (count == 0) {
+               WMFreeArray(panel->windows);
+               wfree(panel);
+               return NULL;
+       }
+
+       width = ICON_TILE_SIZE * count;
+       iconsThatFitCount = count;
+
+       if (width > rect.size.width) {
+               iconsThatFitCount = (rect.size.width - SCREEN_BORDER_SPACING) / ICON_TILE_SIZE;
+               width = iconsThatFitCount * ICON_TILE_SIZE;
+       }
+
+       panel->visibleCount = iconsThatFitCount;
+
+       if (!wPreferences.swtileImage)
+               return panel;
+
+       height = LABEL_HEIGHT + ICON_TILE_SIZE;
+
+       panel->tileTmp = RCreateImage(ICON_TILE_SIZE, ICON_TILE_SIZE, 1);
+       panel->tile = getTile(panel);
+       if (panel->tile && wPreferences.swbackImage[8]) {
+               panel->bg = createBackImage(scr, width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
+       }
+       if (!panel->tileTmp || !panel->tile) {
+               if (panel->bg)
+                       RReleaseImage(panel->bg);
+               panel->bg = NULL;
+               if (panel->tile)
+                       RReleaseImage(panel->tile);
+               panel->tile = NULL;
+               if (panel->tileTmp)
+                       RReleaseImage(panel->tileTmp);
+               panel->tileTmp = NULL;
+       }
+
+       panel->white = WMWhiteColor(scr->wmscreen);
+       panel->font = WMBoldSystemFontOfSize(scr->wmscreen, 12);
+       panel->icons = WMCreateArray(count);
+       panel->images = WMCreateArray(count);
+
+       panel->win = WMCreateWindow(scr->wmscreen, "");
+
+       if (!panel->bg) {
+               WMFrame *frame = WMCreateFrame(panel->win);
+               WMColor *darkGray = WMDarkGrayColor(scr->wmscreen);
+               WMSetFrameRelief(frame, WRSimple);
+               WMSetViewExpandsToParent(WMWidgetView(frame), 0, 0, 0, 0);
+
+               panel->label = WMCreateLabel(panel->win);
+               WMResizeWidget(panel->label, width, LABEL_HEIGHT);
+               WMMoveWidget(panel->label, BORDER_SPACE, BORDER_SPACE + ICON_TILE_SIZE + 5);
+               WMSetLabelRelief(panel->label, WRSimple);
+               WMSetWidgetBackgroundColor(panel->label, darkGray);
+               WMSetLabelFont(panel->label, panel->font);
+               WMSetLabelTextColor(panel->label, panel->white);
+
+               WMReleaseColor(darkGray);
+               height += 5;
+       }
+
+       WMResizeWidget(panel->win, width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
+
+       viewport = WMCreateFrame(panel->win);
+       WMResizeWidget(viewport, width, ICON_TILE_SIZE);
+       WMMoveWidget(viewport, BORDER_SPACE, BORDER_SPACE);
+       WMSetFrameRelief(viewport, WRFlat);
+
+       panel->iconBox = WMCreateFrame(viewport);
+       WMMoveWidget(panel->iconBox, 0, 0);
+       WMResizeWidget(panel->iconBox, ICON_TILE_SIZE * count, ICON_TILE_SIZE);
+       WMSetFrameRelief(panel->iconBox, WRFlat);
+
+       WM_ITERATE_ARRAY(panel->windows, wwin, i) {
+               addIconForWindow(panel, panel->iconBox, wwin, i * ICON_TILE_SIZE, 0);
+       }
+
+       WMMapSubwidgets(panel->win);
+       WMRealizeWidget(panel->win);
+
+       WM_ITERATE_ARRAY(panel->windows, wwin, i) {
+               changeImage(panel, i, 0);
+       }
+
+       if (panel->bg) {
+               Pixmap pixmap, mask;
+
+               RConvertImageMask(scr->rcontext, panel->bg, &pixmap, &mask, 250);
+
+               XSetWindowBackgroundPixmap(dpy, WMWidgetXID(panel->win), pixmap);
+
+#ifdef SHAPE
+               if (mask && wShapeSupported)
+                       XShapeCombineMask(dpy, WMWidgetXID(panel->win), ShapeBounding, 0, 0, mask, ShapeSet);
+#endif
+
+               if (pixmap)
+                       XFreePixmap(dpy, pixmap);
+               if (mask)
+                       XFreePixmap(dpy, mask);
+       }
+
+       {
+               WMPoint center;
+               center = wGetPointToCenterRectInHead(scr, wGetHeadForPointerLocation(scr),
+                                                    width + 2 * BORDER_SPACE, height + 2 * BORDER_SPACE);
+               WMMoveWidget(panel->win, center.x, center.y);
+       }
+
+       panel->current = WMGetFirstInArray(panel->windows, curwin);
+       if (panel->current >= 0)
+               changeImage(panel, panel->current, 1);
+
+       WMMapWidget(panel->win);
+
+       return panel;
+}
+
+void wSwitchPanelDestroy(WSwitchPanel * panel)
+{
+       int i;
+       RImage *image;
+
+       if (panel->win)
+               WMUnmapWidget(panel->win);
+
+       if (panel->images) {
+               WM_ITERATE_ARRAY(panel->images, image, i) {
+                       if (image)
+                               RReleaseImage(image);
+               }
+               WMFreeArray(panel->images);
+       }
+       if (panel->win)
+               WMDestroyWidget(panel->win);
+       if (panel->icons)
+               WMFreeArray(panel->icons);
+       WMFreeArray(panel->windows);
+       if (panel->defIcon)
+               RReleaseImage(panel->defIcon);
+       if (panel->tile)
+               RReleaseImage(panel->tile);
+       if (panel->tileTmp)
+               RReleaseImage(panel->tileTmp);
+       if (panel->bg)
+               RReleaseImage(panel->bg);
+       if (panel->font)
+               WMReleaseFont(panel->font);
+       if (panel->white)
+               WMReleaseColor(panel->white);
+       wfree(panel);
+}
+
+WWindow *wSwitchPanelSelectNext(WSwitchPanel * panel, int back)
+{
+       WWindow *wwin;
+       int count = WMGetArrayItemCount(panel->windows);
+
+       if (count == 0)
+               return NULL;
+
+       if (panel->win)
+               changeImage(panel, panel->current, 0);
+
+       if (back)
+               panel->current--;
+       else
+               panel->current++;
+
+       wwin = WMGetFromArray(panel->windows, (count + panel->current) % count);
+
+       if (back) {
+               if (panel->current < 0)
+                       scrollIcons(panel, count);
+               else if (panel->current < panel->firstVisible)
+                       scrollIcons(panel, -1);
+       } else {
+               if (panel->current >= count)
+                       scrollIcons(panel, -count);
+               else if (panel->current - panel->firstVisible >= panel->visibleCount)
+                       scrollIcons(panel, 1);
+       }
+
+       panel->current = (count + panel->current) % count;
+
+       if (panel->win) {
+               drawTitle(panel, panel->current, wwin->frame->title);
+
+               changeImage(panel, panel->current, 1);
+       }
+
+       return wwin;
+}
+
+WWindow *wSwitchPanelSelectFirst(WSwitchPanel * panel, int back)
+{
+       WWindow *wwin;
+       int count = WMGetArrayItemCount(panel->windows);
+
+       if (count == 0)
+               return NULL;
+
+       if (panel->win)
+               changeImage(panel, panel->current, 0);
+
+       if (back) {
+               panel->current = count - 1;
+               scrollIcons(panel, count);
+       } else {
+               panel->current = 0;
+               scrollIcons(panel, -count);
+       }
+
+       wwin = WMGetFromArray(panel->windows, panel->current);
+
+       if (panel->win) {
+               drawTitle(panel, panel->current, wwin->frame->title);
+
+               changeImage(panel, panel->current, 1);
+       }
+       return wwin;
+}
+
+WWindow *wSwitchPanelHandleEvent(WSwitchPanel * panel, XEvent * event)
+{
+       WMFrame *icon;
+       int i;
+       int focus = -1;
+
+       if (!panel->win)
+               return NULL;
+
+       /*   if (event->type == LeaveNotify) {
+          if (event->xcrossing.window == WMWidgetXID(panel->win))
+          focus= 0;
+          } else */ if (event->type == MotionNotify) {
+
+               WM_ITERATE_ARRAY(panel->icons, icon, i) {
+                       if (WMWidgetXID(icon) == event->xmotion.window) {
+                               focus = i;
+                               break;
+                       }
+               }
+       }
+       if (focus >= 0 && panel->current != focus) {
+               WWindow *wwin;
+
+               changeImage(panel, panel->current, 0);
+               changeImage(panel, focus, 1);
+               panel->current = focus;
+
+               wwin = WMGetFromArray(panel->windows, focus);
+
+               drawTitle(panel, panel->current, wwin->frame->title);
+
+               return wwin;
+       }
+
+       return NULL;
+}
+
+Window wSwitchPanelGetWindow(WSwitchPanel * swpanel)
+{
+       if (!swpanel->win)
+               return None;
+       return WMWidgetXID(swpanel->win);
+}