From f54f0856c140fdff53314901aea899f265a66c2a Mon Sep 17 00:00:00 2001 From: kojima Date: Thu, 14 Oct 2004 05:02:24 +0000 Subject: [PATCH] - MacOS X style window switching panel (navigate through windows with Alt-Tab or arrow keys) --- ChangeLog | 1 + src/Makefile.am | 1 + src/cycling.c | 87 +++++++++++------- src/geomview.c | 2 +- src/monitor.c | 4 +- src/switchpanel.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 331 insertions(+), 35 deletions(-) create mode 100644 src/switchpanel.c diff --git a/ChangeLog b/ChangeLog index 7010569e..54e77953 100644 --- a/ChangeLog +++ b/ChangeLog @@ -158,6 +158,7 @@ Changes since version 0.80.2: - Made NetWM support be enabled by default - Removed old code to store/restore workspace state (now relies on netwm) - Added a (simple) Font Configuration for fontconfig fonts +- MacOS X style window switching panel (navigate through windows with Alt-Tab or arrow keys) Changes since version 0.80.1: ............................. diff --git a/src/Makefile.am b/src/Makefile.am index c18cf9c1..ccb0d754 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -69,6 +69,7 @@ wmaker_SOURCES = \ session.h \ session.c \ shutdown.c \ + switchpanel.c \ stacking.c \ stacking.h \ startup.c \ diff --git a/src/cycling.c b/src/cycling.c index 800f4068..5171168b 100644 --- a/src/cycling.c +++ b/src/cycling.c @@ -24,7 +24,9 @@ #include #include +#include +#define MOX_CYCLING #include "WindowMaker.h" #include "GNUstep.h" @@ -37,6 +39,7 @@ #include "stacking.h" #include "funcs.h" #include "xinerama.h" +#include "switchpanel.h" /* Globals */ extern WPreferences wPreferences; @@ -47,7 +50,7 @@ extern WShortKey wKeyBindings[WKBD_LAST]; - +#ifndef MOX_CYCLING static WWindow* nextToFocusAfter(WWindow *wwin) { @@ -169,8 +172,7 @@ prevFocusWindow(WWindow *wwin) return max; return closest; } - - +#endif /* !MOX_CYCLING */ void @@ -178,7 +180,6 @@ StartWindozeCycle(WWindow *wwin, XEvent *event, Bool next) { WScreen *scr = wScreenForRootWindow(event->xkey.root); Bool done = False; - Bool openedSwitchMenu = False; WWindow *newFocused; WWindow *oldFocused; int modifiers; @@ -186,9 +187,16 @@ StartWindozeCycle(WWindow *wwin, XEvent *event, Bool next) Bool hasModifier; Bool somethingElse = False; XEvent ev; +#ifdef MOX_CYCLING + WSwitchPanel *swpanel = NULL; +#endif + KeyCode leftKey, rightKey; if (!wwin) return; + + leftKey = XKeysymToKeycode(dpy, XK_Left); + rightKey = XKeysymToKeycode(dpy, XK_Right); if (next) hasModifier = (wKeyBindings[WKBD_FOCUSNEXT].modifier != 0); @@ -205,6 +213,16 @@ StartWindozeCycle(WWindow *wwin, XEvent *event, Bool next) CurrentTime); } + scr->flags.doing_alt_tab = 1; + +#ifdef MOX_CYCLING + swpanel = wInitSwitchPanel(scr, 0); + oldFocused = wwin; + + newFocused = wSwitchPanelSelectNext(swpanel, next); + wWindowFocus(newFocused, oldFocused); + oldFocused = newFocused; +#else /* !MOX_CYCLING */ if (next) { if (wPreferences.windows_cycling) newFocused = nextToFocusAfter(wwin); @@ -217,25 +235,14 @@ StartWindozeCycle(WWindow *wwin, XEvent *event, Bool next) newFocused = prevFocusWindow(wwin); } - scr->flags.doing_alt_tab = 1; - - if (wPreferences.circ_raise) XRaiseWindow(dpy, newFocused->frame->core->window); wWindowFocus(newFocused, scr->focused_window); oldFocused = newFocused; - -#if 0 - if (wPreferences.popup_switchmenu && - (!scr->switch_menu || !scr->switch_menu->flags.mapped)) { - - OpenSwitchMenu(scr, scr->scr_width/2, scr->scr_height/2, False); - openedSwitchMenu = True; - } -#endif +#endif /* !MOX_CYCLING */ while (hasModifier && !done) { - WMMaskEvent(dpy, KeyPressMask|KeyReleaseMask|ExposureMask, &ev); + WMMaskEvent(dpy, KeyPressMask|KeyReleaseMask|ExposureMask|PointerMotionMask, &ev); if (ev.type != KeyRelease && ev.type != KeyPress) { WMHandleEvent(&ev); @@ -248,9 +255,15 @@ StartWindozeCycle(WWindow *wwin, XEvent *event, Bool next) #ifdef DEBUG printf("Got key press\n"); #endif - if (wKeyBindings[WKBD_FOCUSNEXT].keycode == ev.xkey.keycode - && wKeyBindings[WKBD_FOCUSNEXT].modifier == modifiers) { + if ((wKeyBindings[WKBD_FOCUSNEXT].keycode == ev.xkey.keycode + && wKeyBindings[WKBD_FOCUSNEXT].modifier == modifiers) + || ev.xkey.keycode == rightKey) { +#ifdef MOX_CYCLING + newFocused = wSwitchPanelSelectNext(swpanel, False); + wWindowFocus(newFocused, oldFocused); + oldFocused = newFocused; +#else /* !MOX_CYCLING */ newFocused = nextToFocusAfter(newFocused); wWindowFocus(newFocused, oldFocused); oldFocused = newFocused; @@ -260,10 +273,16 @@ StartWindozeCycle(WWindow *wwin, XEvent *event, Bool next) CommitStacking(scr); XRaiseWindow(dpy, newFocused->frame->core->window); } +#endif /* !MOX_CYCLING */ + } else if ((wKeyBindings[WKBD_FOCUSPREV].keycode == ev.xkey.keycode + && wKeyBindings[WKBD_FOCUSPREV].modifier == modifiers) + || ev.xkey.keycode == leftKey) { - } else if (wKeyBindings[WKBD_FOCUSPREV].keycode == ev.xkey.keycode - && wKeyBindings[WKBD_FOCUSPREV].modifier == modifiers) { - +#ifdef MOX_CYCLING + newFocused = wSwitchPanelSelectNext(swpanel, True); + wWindowFocus(newFocused, oldFocused); + oldFocused = newFocused; +#else /* !MOX_CYCLING */ newFocused = nextToFocusBefore(newFocused); wWindowFocus(newFocused, oldFocused); oldFocused = newFocused; @@ -273,7 +292,15 @@ StartWindozeCycle(WWindow *wwin, XEvent *event, Bool next) CommitStacking(scr); XRaiseWindow(dpy, newFocused->frame->core->window); } - +#endif /* !MOX_CYCLING */ + } else if (ev.type == MotionNotify) { + WWindow *tmp; + tmp = wSwitchPanelHandleEvent(swpanel, &ev); + if (tmp) { + newFocused = tmp; + wWindowFocus(newFocused, oldFocused); + oldFocused = newFocused; + } } else { #ifdef DEBUG printf("Got something else\n"); @@ -308,21 +335,17 @@ StartWindozeCycle(WWindow *wwin, XEvent *event, Bool next) } wSetFocusTo(scr, newFocused); +#ifdef MOX_CYCLING + if (swpanel) + wSwitchPanelDestroy(swpanel); +#endif + if (wPreferences.circ_raise) { wRaiseFrame(newFocused->frame->core); CommitStacking(scr); } scr->flags.doing_alt_tab = 0; - if (openedSwitchMenu) { - /* - * place window in center of current head - */ - WMPoint center = - wGetPointToCenterRectInHead(scr, wGetHeadForPointerLocation(scr), - 0, 0); - OpenSwitchMenu(scr, center.x, center.y, False); - } if (somethingElse) { WMHandleEvent(&ev); diff --git a/src/geomview.c b/src/geomview.c index 5a46b024..bd5d9ff5 100644 --- a/src/geomview.c +++ b/src/geomview.c @@ -132,7 +132,7 @@ paint(WGeometryView *gview) W_DrawRelief(W_VIEW_SCREEN(gview->view), W_VIEW_DRAWABLE(gview->view), 0, 0, W_VIEW_WIDTH(gview->view), W_VIEW_HEIGHT(gview->view), - WRRaised); + WRSimple); } diff --git a/src/monitor.c b/src/monitor.c index 683ca532..3db15929 100644 --- a/src/monitor.c +++ b/src/monitor.c @@ -134,9 +134,9 @@ int MonitorLoop(int argc, char **argv) WTERMSIG(status) == SIGFPE)) { /* If so, we check when was the last restart. - * If it was less than 5s ago, it's a bad sign, so we show + * If it was less than 3s ago, it's a bad sign, so we show * the crash panel and ask the user what to do */ - if (time(NULL) - last_start < 5) + if (time(NULL) - last_start < 3) { if (showCrashDialog(WTERMSIG(status)) == 0) return 1; diff --git a/src/switchpanel.c b/src/switchpanel.c new file mode 100644 index 00000000..823dc11e --- /dev/null +++ b/src/switchpanel.c @@ -0,0 +1,271 @@ +/* + * 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 + +#include "WindowMaker.h" +#include "screen.h" +#include "wcore.h" +#include "framewin.h" +#include "window.h" +#include "defaults.h" +#include "switchpanel.h" +#include "funcs.h" + +struct SwitchPanel { + WScreen *scr; + WMWindow *win; + WMBox *hbox; + WMLabel *label; + WMArray *icons; + WMArray *windows; + int current; + + WMColor *normalColor; + WMColor *selectColor; + + WMPixmap *defIcon; +}; + + +extern WPreferences wPreferences; + +#define ICON_EXTRASPACE 4 + +static void addIconForWindow(WSwitchPanel *panel, WWindow *wwin, int iconWidth) +{ + WMLabel *label= WMCreateLabel(panel->hbox); + WMAddBoxSubviewAtEnd(panel->hbox, WMWidgetView(label), False, True, iconWidth + ICON_EXTRASPACE, 0, 0); + RImage *image = NULL; + WMPixmap *pixmap; + WMScreen *wscr = WMWidgetScreen(label); + + if (!WFLAGP(wwin, always_user_icon) && wwin->net_icon_image) + image = RRetainImage(wwin->net_icon_image); + if (!image) + image = wDefaultGetImage(panel->scr, wwin->wm_instance, wwin->wm_class); + if (!image) + + if (image && (abs(image->width - iconWidth) > 4 || abs(image->height - iconWidth) > 4)) { + RImage *nimage; + + nimage= RScaleImage(image, iconWidth, (image->height * iconWidth / image->width)); + RReleaseImage(image); + image= nimage; + } + + if (image) { + pixmap= WMCreatePixmapFromRImage(wscr, image, 100); + RReleaseImage(image); + } else { + if (!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); + + panel->defIcon= WMCreatePixmapFromRImage(wscr, image, 100); + RReleaseImage(image); + } + } + } + + if (panel->defIcon) + pixmap= WMRetainPixmap(panel->defIcon); + else + pixmap= NULL; + } + + if (pixmap) { + WMSetLabelImage(label, pixmap); + WMSetLabelImagePosition(label, WIPImageOnly); + WMReleasePixmap(pixmap); + } + + WMAddToArray(panel->icons, label); +} + + +WSwitchPanel *wInitSwitchPanel(WScreen *scr, int workspace) +{ + WWindow *wwin; + WSwitchPanel *panel= wmalloc(sizeof(WSwitchPanel)); + int i; + int width; + int height; + int iconWidth = 48; + WMBox *vbox; + + panel->current= 0; + panel->defIcon= NULL; + + panel->normalColor = WMGrayColor(scr->wmscreen); + panel->selectColor = WMWhiteColor(scr->wmscreen); + + panel->scr= scr; + panel->windows= WMCreateArray(10); + + for (wwin= scr->focused_window; wwin; wwin= wwin->prev) { + if (wwin->frame->workspace == workspace && wWindowCanReceiveFocus(wwin) && + (!WFLAGP(wwin, skip_window_list) || wwin->flags.internal_window)) { + WMInsertInArray(panel->windows, 0, wwin); + } + } + + width= (iconWidth + ICON_EXTRASPACE)*WMGetArrayItemCount(panel->windows); + + if (width > WMScreenWidth(scr->wmscreen)) + { + width= WMScreenWidth(scr->wmscreen) - 100; + iconWidth = width / WMGetArrayItemCount(panel->windows) - ICON_EXTRASPACE; + } + + if (iconWidth < 16) + { + /* if there are too many windows, don't bother trying to show the panel */ + WMFreeArray(panel->windows); + wfree(panel); + return NULL; + } + + height= 48 + 20 + 10 + ICON_EXTRASPACE; + + panel->icons= WMCreateArray(WMGetArrayItemCount(panel->windows)); + + panel->win = WMCreateWindow(scr->wmscreen, ""); + WMResizeWidget(panel->win, width + 10, height); + + { + WMFrame *frame = WMCreateFrame(panel->win); + WMSetFrameRelief(frame, WRSimple); + WMSetViewExpandsToParent(WMWidgetView(frame), 0, 0, 0, 0); + + vbox = WMCreateBox(panel->win); + } + WMSetViewExpandsToParent(WMWidgetView(vbox), 5, 5, 5, 5); + WMSetBoxHorizontal(vbox, False); + + panel->label = WMCreateLabel(vbox); + WMAddBoxSubviewAtEnd(vbox, WMWidgetView(panel->label), False, True, 20, 0, 0); + if (scr->focused_window && scr->focused_window->frame->title) + WMSetLabelText(panel->label, scr->focused_window->frame->title); + else + WMSetLabelText(panel->label, ""); + { + WMColor *color; + WMFont *boldFont= WMBoldSystemFontOfSize(scr->wmscreen, 12); + + WMSetLabelRelief(panel->label, WRSimple); + WMSetLabelFont(panel->label, boldFont); + color = WMDarkGrayColor(scr->wmscreen); + WMSetWidgetBackgroundColor(panel->label, color); + WMReleaseColor(color); + color = WMWhiteColor(scr->wmscreen); + WMSetLabelTextColor(panel->label, color); + WMReleaseColor(color); + + WMReleaseFont(boldFont); + } + + panel->hbox = WMCreateBox(vbox); + WMSetBoxHorizontal(panel->hbox, True); + WMAddBoxSubviewAtEnd(vbox, WMWidgetView(panel->hbox), True, True, 20, 0, 2); + + WM_ITERATE_ARRAY(panel->windows, wwin, i) { + addIconForWindow(panel, wwin, iconWidth); + } + + WMMapSubwidgets(panel->win); + WMRealizeWidget(panel->win); + WMMapWidget(panel->win); + WMMoveWidget(panel->win, + (WMScreenWidth(scr->wmscreen) - (width+10))/2, + (WMScreenHeight(scr->wmscreen) - height)/2); + + WMSetWidgetBackgroundColor(WMGetFromArray(panel->icons, 0), + panel->selectColor); + + return panel; +} + + +void wSwitchPanelDestroy(WSwitchPanel *panel) +{ + WMDestroyWidget(panel->win); + WMFreeArray(panel->icons); + WMFreeArray(panel->windows); + WMReleaseColor(panel->selectColor); + WMReleaseColor(panel->normalColor); + if (panel->defIcon) + WMReleasePixmap(panel->defIcon); + wfree(panel); +} + + +WWindow *wSwitchPanelSelectNext(WSwitchPanel *panel, int back) +{ + WWindow *wwin; + int count = WMGetArrayItemCount(panel->windows); + WMLabel *label; + + label= WMGetFromArray(panel->icons, panel->current); + WMSetWidgetBackgroundColor(label, panel->normalColor); + WMSetLabelRelief(label, WRFlat); + + if (!back) + panel->current = (count + panel->current - 1) % count; + else + panel->current = (panel->current + 1) % count; + + wwin = WMGetFromArray(panel->windows, panel->current); + + WMSetLabelText(panel->label, wwin->frame->title); + + label= WMGetFromArray(panel->icons, panel->current); + WMSetWidgetBackgroundColor(label, panel->selectColor); + WMSetLabelRelief(label, WRSimple); + + return wwin; +} + + + +WWindow *wSwitchPanelHandleEvent(WSwitchPanel *panel, XEvent *event) +{ + WMLabel *label; + int i; + + printf("%i %i\n", event->xmotion.x, event->xmotion.y); + + WM_ITERATE_ARRAY(panel->icons, label, i) { + if (WMWidgetXID(label) == event->xmotion.subwindow) + puts("HOORAY"); + } + + return NULL; +} + + -- 2.11.4.GIT