diff --git a/WPrefs.app/Configurations.c b/WPrefs.app/Configurations.c index e64d8c11..55b15375 100644 --- a/WPrefs.app/Configurations.c +++ b/WPrefs.app/Configurations.c @@ -111,43 +111,6 @@ static void updateLabel(WMWidget *self, void *data) WMSetLabelText(panel->dithL, buffer); } -static void -createImages(WMScreen *scr, RContext *rc, RImage *xis, char *file, - WMPixmap **icon1, WMPixmap **icon2) -{ - RImage *icon; - char *path; - RColor gray = { 0xae, 0xaa, 0xae, 0 }; - - *icon1 = NULL; - *icon2 = NULL; - - path = LocateImage(file); - if (!path) - return; - - *icon1 = WMCreatePixmapFromFile(scr, path); - if (!*icon1) { - wwarning(_("could not load icon %s"), path); - wfree(path); - return; - } - icon = RLoadImage(rc, path, 0); - if (!icon) { - wwarning(_("could not load icon %s"), path); - wfree(path); - return; - } - RCombineImageWithColor(icon, &gray); - if (xis) { - RCombineImagesWithOpaqueness(icon, xis, 180); - if (!(*icon2 = WMCreatePixmapFromRImage(scr, icon, 127))) - wwarning(_("could not process icon %s: %s"), file, RMessageForError(RErrorCode)); - } - RReleaseImage(icon); - wfree(path); -} - static void createPanel(Panel *p) { _Panel *panel = (_Panel *) p; @@ -342,7 +305,7 @@ static void createPanel(Panel *p) WMSetButtonFont(panel->animB, font); WMSetButtonText(panel->animB, _("Animations")); WMSetButtonImagePosition(panel->animB, WIPAbove); - createImages(scr, rc, xis, ANIM_IMAGE, &altIcon, &icon); + CreateImages(scr, rc, xis, ANIM_IMAGE, &altIcon, &icon); if (icon) { WMSetButtonImage(panel->animB, icon); WMReleasePixmap(icon); @@ -360,7 +323,7 @@ static void createPanel(Panel *p) WMSetButtonFont(panel->supB, font); WMSetButtonText(panel->supB, _("Superfluous")); WMSetButtonImagePosition(panel->supB, WIPAbove); - createImages(scr, rc, xis, SUPERF_IMAGE, &altIcon, &icon); + CreateImages(scr, rc, xis, SUPERF_IMAGE, &altIcon, &icon); if (icon) { WMSetButtonImage(panel->supB, icon); WMReleasePixmap(icon); diff --git a/WPrefs.app/Docks.c b/WPrefs.app/Docks.c new file mode 100644 index 00000000..211462d1 --- /dev/null +++ b/WPrefs.app/Docks.c @@ -0,0 +1,310 @@ +/* Workspace.c- workspace options + * + * WPrefs - Window Maker Preferences Program + * + * Copyright (c) 2012 Daniel Déchelotte (heavily inspired from file (c) 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., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "WPrefs.h" + +typedef struct _Panel { + WMBox *box; + + char *sectionName; + + char *description; + + CallbackRec callbacks; + + WMWidget *parent; + + WMFrame *autoDelayF[2]; + WMLabel *autoDelayL[4]; + WMButton *autoDelayB[4][5]; + WMTextField *autoDelayT[4]; + + WMFrame *dockF; + WMButton *docksB[3]; +} _Panel; + +#define ICON_FILE "dockclipdrawersection" + +#define ARQUIVO_XIS "xis" +#define DELAY_ICON "timer%i" +#define DELAY_ICON_S "timer%is" + +static char *autoDelayStrings[4]; +static char *autoDelayKeys[4] = { "ClipAutoexpandDelay", "ClipAutocollapseDelay", "ClipAutoraiseDelay", "ClipAutolowerDelay" }; +static char *autoDelayPresetValues[5] = { "0", "100", "250", "600", "1000" }; +static char *dockDisablingKeys[3] = { "DisableDock", "DisableClip", "DisableDrawers" }; +static char *dockFiles[3] = { "dock", "clip", "drawer" }; + +static void showData(_Panel *panel); +static void storeData(_Panel *panel); + + +static void pushAutoDelayButton(WMWidget *w, void *data) +{ + _Panel *panel = (_Panel *) data; + int i, j; + for (i = 0; i < 4; i++) + { + for (j = 0; j < 5; j++) + { + if (w == panel->autoDelayB[i][j]) + { + WMSetTextFieldText(panel->autoDelayT[i], autoDelayPresetValues[j]); + return; + } + } + } +} + +static void adjustButtonSelectionBasedOnValue(_Panel *panel, int row, char *value) +{ + int j; + for (j = 0; j < 5; j++) + { + int isThatOne = !strcmp(autoDelayPresetValues[j], value); + WMSetButtonSelected(panel->autoDelayB[row][j], isThatOne); + if (isThatOne) + return; + } +} + +static void autoDelayChanged(void *observerData, WMNotification *notification) +{ + _Panel *panel = (_Panel *) observerData; + int row; + WMTextField *anAutoDelayT = (WMTextField *) WMGetNotificationObject(notification); + for (row = 0; row < 4; row++) + { + if (anAutoDelayT != panel->autoDelayT[row]) + { + continue; + } + char *value = WMGetTextFieldText(anAutoDelayT); + adjustButtonSelectionBasedOnValue(panel, row, value); + return; + } +} + +static void pushDockButton(WMWidget *w, void *data) +{ + _Panel *panel = (_Panel *) data; + WMButton *button = (WMButton *) w; + if (button == panel->docksB[0] && + !WMGetButtonSelected(panel->docksB[0])) + { + WMSetButtonSelected(panel->docksB[2], False); + } + if (button == panel->docksB[2] && + WMGetButtonSelected(panel->docksB[2])) + { + WMSetButtonSelected(panel->docksB[0], True); + } +} + +static void createPanel(Panel *p) +{ + _Panel *panel = (_Panel *) p; + WMScreen *scr = WMWidgetScreen(panel->parent); + WMPixmap *icon1, *icon2; + RImage *xis = NULL; + RContext *rc = WMScreenRContext(scr); + char *path; + int i, j, k; + char *buf1, *buf2; + + path = LocateImage(ARQUIVO_XIS); + if (path) { + xis = RLoadImage(rc, path, 0); + if (!xis) { + wwarning(_("could not load image file %s"), path); + } + wfree(path); + } + + panel->box = WMCreateBox(panel->parent); + WMSetViewExpandsToParent(WMWidgetView(panel->box), 2, 2, 2, 2); + + /***************** Auto-delays *****************/ + buf1 = wmalloc(strlen(DELAY_ICON) + 1); + buf2 = wmalloc(strlen(DELAY_ICON_S) + 1); + + for (k = 0; k < 2; k++) + { + panel->autoDelayF[k] = WMCreateFrame(panel->box); + WMResizeWidget(panel->autoDelayF[k], 365, 100); + WMMoveWidget(panel->autoDelayF[k], 15, 10 + k * 110); + if (k == 0) + WMSetFrameTitle(panel->autoDelayF[k], _("Delays in milliseconds for autocollapsing clips")); + else + WMSetFrameTitle(panel->autoDelayF[k], _("Delays in milliseconds for autoraising clips")); + + for (i = 0; i < 2; i++) + { + panel->autoDelayL[i + k * 2] = WMCreateLabel(panel->autoDelayF[k]); + WMResizeWidget(panel->autoDelayL[i + k * 2], 165, 20); + WMMoveWidget(panel->autoDelayL[i + k * 2], 10, 27 + 40 * i); + WMSetLabelText(panel->autoDelayL[i + k * 2], autoDelayStrings[i + k * 2]); + WMSetLabelTextAlignment(panel->autoDelayL[i + k * 2], WARight); + + for (j = 0; j < 5; j++) + { + panel->autoDelayB[i + k * 2][j] = WMCreateCustomButton(panel->autoDelayF[k], WBBStateChangeMask); + WMResizeWidget(panel->autoDelayB[i + k * 2][j], 25, 25); + WMMoveWidget(panel->autoDelayB[i + k * 2][j], 175 + (25 * j), 25 + 40 * i); + WMSetButtonBordered(panel->autoDelayB[i + k * 2][j], False); + WMSetButtonImagePosition(panel->autoDelayB[i + k * 2][j], WIPImageOnly); + WMSetButtonAction(panel->autoDelayB[i + k * 2][j], pushAutoDelayButton, panel); + if (j > 0) + WMGroupButtons(panel->autoDelayB[i + k * 2][0], panel->autoDelayB[i + k * 2][j]); + sprintf(buf1, DELAY_ICON, j); + CreateImages(scr, rc, NULL, buf1, &icon1, NULL); + if (icon1) { + WMSetButtonImage(panel->autoDelayB[i + k * 2][j], icon1); + WMReleasePixmap(icon1); + } else { + wwarning(_("could not load icon file %s"), buf1); + } + sprintf(buf2, DELAY_ICON_S, j); + CreateImages(scr, rc, NULL, buf2, &icon2, NULL); + if (icon2) { + WMSetButtonAltImage(panel->autoDelayB[i + k * 2][j], icon2); + WMReleasePixmap(icon2); + } else { + wwarning(_("could not load icon file %s"), buf2); + } + } + + panel->autoDelayT[i + k * 2] = WMCreateTextField(panel->autoDelayF[k]); + WMResizeWidget(panel->autoDelayT[i + k * 2], 36, 20); + WMMoveWidget(panel->autoDelayT[i + k * 2], 310, 27 + 40 * i); + WMAddNotificationObserver(autoDelayChanged, panel, WMTextDidChangeNotification, panel->autoDelayT[i + k * 2]); + } + + WMMapSubwidgets(panel->autoDelayF[k]); + } + wfree(buf1); + wfree(buf2); + + /***************** Enable/disable clip/dock/drawers *****************/ + panel->dockF = WMCreateFrame(panel->box); + WMResizeWidget(panel->dockF, 115, 210); + WMMoveWidget(panel->dockF, 390, 10); + WMSetFrameTitle(panel->dockF, _("Dock/Clip/Drawer")); + + for (i = 0; i < 3; i++) + { + panel->docksB[i] = WMCreateButton(panel->dockF, WBTToggle); + WMResizeWidget(panel->docksB[i], 56, 56); + WMMoveWidget(panel->docksB[i], 30, 20 + 62 * i); + WMSetButtonImagePosition(panel->docksB[i], WIPImageOnly); + CreateImages(scr, rc, xis, dockFiles[i], &icon1, &icon2); + if (icon2) { + WMSetButtonImage(panel->docksB[i], icon2); + WMReleasePixmap(icon2); + } + if (icon1) { + WMSetButtonAltImage(panel->docksB[i], icon1); + WMReleasePixmap(icon1); + } + switch(i) + { + case 0: + WMSetBalloonTextForView(_("Disable/enable the application Dock (the\n" + "vertical icon bar in the side of the screen)."), WMWidgetView(panel->docksB[i])); + break; + case 1: + WMSetBalloonTextForView(_("Disable/enable the Clip (that thing with\n" + "a paper clip icon)."), WMWidgetView(panel->docksB[i])); + break; + case 2: + WMSetBalloonTextForView(_("Disable/enable Drawers (a dock that stores\n" + "application icons horizontally). The dock is required."), WMWidgetView(panel->docksB[i])); + break; + } + WMSetButtonAction(panel->docksB[i], pushDockButton, panel); + } + + WMMapSubwidgets(panel->dockF); + + if (xis) + RReleaseImage(xis); + + WMRealizeWidget(panel->box); + WMMapSubwidgets(panel->box); + + showData(panel); +} + +static void storeData(_Panel *panel) +{ + int i; + for (i = 0; i < 4; i++) + { + SetStringForKey(WMGetTextFieldText(panel->autoDelayT[i]), autoDelayKeys[i]); + } + for (i = 0; i < 3; i++) + { + SetBoolForKey(!WMGetButtonSelected(panel->docksB[i]), dockDisablingKeys[i]); + } +} + +static void showData(_Panel *panel) +{ + char *value; + int i; + for (i = 0; i < 4; i++) + { + value = GetStringForKey(autoDelayKeys[i]); + WMSetTextFieldText(panel->autoDelayT[i], value); + adjustButtonSelectionBasedOnValue(panel, i, value); + } + for (i = 0; i < 3; i++) + { + WMSetButtonSelected(panel->docksB[i], !GetBoolForKey(dockDisablingKeys[i])); + } +} + +Panel *InitDocks(WMScreen *scr, WMWidget *parent) +{ + _Panel *panel; + + autoDelayStrings[0] = _("Delay before auto-expansion"); + autoDelayStrings[1] = _("Delay before auto-collapsing"); + autoDelayStrings[2] = _("Delay before auto-raise"); + autoDelayStrings[3] = _("Delay before auto-lowering"); + + panel = wmalloc(sizeof(_Panel)); + memset(panel, 0, sizeof(_Panel)); + + panel->sectionName = _("Dock Preferences"); + + panel->description = _("Dock and clip features.\n" + "Enable/disable the Dock and Clip, and tune some delays."); + + panel->parent = parent; + + panel->callbacks.createWidgets = createPanel; + panel->callbacks.updateDomain = storeData; + + AddSection(panel, ICON_FILE); + + return panel; +} diff --git a/WPrefs.app/Makefile.am b/WPrefs.app/Makefile.am index 7929f4a1..15f0b6e8 100644 --- a/WPrefs.app/Makefile.am +++ b/WPrefs.app/Makefile.am @@ -18,6 +18,7 @@ WPrefs_SOURCES = \ WPrefs.h \ Appearance.c \ Configurations.c \ + Docks.c \ Expert.c \ Focus.c \ FontSimple.c \ diff --git a/WPrefs.app/WPrefs.c b/WPrefs.app/WPrefs.c index 2b69b51f..94b248fe 100644 --- a/WPrefs.app/WPrefs.c +++ b/WPrefs.app/WPrefs.c @@ -32,6 +32,8 @@ extern Panel *InitKeyboardShortcuts(WMScreen * scr, WMWidget * parent); extern Panel *InitWorkspace(WMScreen * scr, WMWidget * parent); +extern Panel *InitDocks(WMScreen *scr, WMWidget *parent); + extern Panel *InitFocus(WMScreen * scr, WMWidget * parent); extern Panel *InitPreferences(WMScreen * scr, WMWidget * parent); @@ -392,6 +394,61 @@ char *LocateImage(char *name) return path; } +void CreateImages(WMScreen *scr, RContext *rc, RImage *xis, char *file, + WMPixmap **icon_normal, WMPixmap **icon_greyed) +{ + RImage *icon; + char *path; + RColor gray = { 0xae, 0xaa, 0xae, 0 }; + + path = LocateImage(file); + if (!path) + { + *icon_normal = NULL; + if (icon_greyed) + *icon_greyed = NULL; + return; + } + + *icon_normal = WMCreatePixmapFromFile(scr, path); + if (!*icon_normal) + { + wwarning(_("could not load icon %s"), path); + if (icon_greyed) + *icon_greyed = NULL; + wfree(path); + return; + } + + if (!icon_greyed) // Greyed-out version not requested, we are done + { + wfree(path); + return; + } + + icon = RLoadImage(rc, path, 0); + if (!icon) + { + wwarning(_("could not load icon %s"), path); + *icon_greyed = NULL; + wfree(path); + return; + } + RCombineImageWithColor(icon, &gray); + if (xis) + { + RCombineImagesWithOpaqueness(icon, xis, 180); + } + *icon_greyed = WMCreatePixmapFromRImage(scr, icon, 127); + if (!*icon_greyed) + { + wwarning(_("could not process icon %s: %s"), path, RMessageForError(RErrorCode)); + } + RReleaseImage(icon); + wfree(path); +} + + static WMPixmap *makeTitledIcon(WMScreen * scr, WMPixmap * icon, char *title1, char *title2) { return WMRetainPixmap(icon); @@ -608,6 +665,7 @@ void Initialize(WMScreen * scr) InitPreferences(scr, WPrefs.banner); InitPaths(scr, WPrefs.banner); + InitDocks(scr, WPrefs.banner); InitWorkspace(scr, WPrefs.banner); InitConfigurations(scr, WPrefs.banner); diff --git a/WPrefs.app/WPrefs.h b/WPrefs.app/WPrefs.h index 900afdcb..5100ef66 100644 --- a/WPrefs.app/WPrefs.h +++ b/WPrefs.app/WPrefs.h @@ -76,6 +76,13 @@ char *LocateImage(char *name); void SetButtonAlphaImage(WMScreen *scr, WMButton *bPtr, char *file, char *title1, char *title2); +/* Loads `file' into `icon_normal'. If `icon_greyed' is not NULL, + * combine `icon_normal' with some grey and then optionally with image + * `xis', and store it in `icon_greyed' (typically to produce a + * greyed-out, red-crossed version of `icon_normal') */ +void CreateImages(WMScreen *scr, RContext *rc, RImage *xis, char *file, + WMPixmap **icon_normal, WMPixmap **icon_greyed); + WMWindow *GetWindow(Panel *panel); /* manipulate the dictionary for the WindowMaker domain */ diff --git a/WPrefs.app/Workspace.c b/WPrefs.app/Workspace.c index 6ba26fcd..24064a48 100644 --- a/WPrefs.app/Workspace.c +++ b/WPrefs.app/Workspace.c @@ -44,10 +44,6 @@ typedef struct _Panel { WMLabel *posiL; WMLabel *posL; WMPopUpButton *posP; - - WMFrame *dockF; - WMButton *dockB; - WMButton *clipB; } _Panel; #define ICON_FILE "workspace" @@ -57,8 +53,6 @@ typedef struct _Panel { #define CYCLE_FILE "cycleworkspaces" #define ADVANCE_FILE "advancetonewworkspace" #define WSNAME_FILE "workspacename" -#define DOCK_FILE "dock" -#define CLIP_FILE "clip" static char *WSNamePositions[] = { "none", @@ -71,40 +65,6 @@ static char *WSNamePositions[] = { "bottomright" }; -static void -createImages(WMScreen * scr, RContext * rc, RImage * xis, char *file, WMPixmap ** icon1, WMPixmap ** icon2) -{ - RImage *icon; - RColor gray = { 0xae, 0xaa, 0xae, 0 }; - - *icon1 = WMCreatePixmapFromFile(scr, file); - if (!*icon1) { - wwarning(_("could not load icon %s"), file); - if (icon2) - *icon2 = NULL; - return; - } - - if (!icon2) - return; - - icon = RLoadImage(rc, file, 0); - if (!icon) { - wwarning(_("could not load icon %s"), file); - *icon2 = NULL; - return; - } - RCombineImageWithColor(icon, &gray); - if (xis) { - RCombineImagesWithOpaqueness(icon, xis, 180); - if (!(*icon2 = WMCreatePixmapFromRImage(scr, icon, 127))) { - wwarning(_("could not process icon %s: %s"), file, RMessageForError(RErrorCode)); - *icon2 = NULL; - } - } - RReleaseImage(icon); -} - static void showData(_Panel * panel) { int i, idx; @@ -116,10 +76,6 @@ static void showData(_Panel * panel) WMSetButtonSelected(panel->newB, GetBoolForKey("AdvanceToNewWorkspace")); - WMSetButtonSelected(panel->dockB, !GetBoolForKey("DisableDock")); - - WMSetButtonSelected(panel->clipB, !GetBoolForKey("DisableClip")); - str = GetStringForKey("WorkspaceNameDisplayPosition"); if (!str) str = "center"; @@ -157,12 +113,12 @@ static void createPanel(Panel * p) /***************** Workspace Navigation *****************/ panel->navF = WMCreateFrame(panel->box); - WMResizeWidget(panel->navF, 365, 210); + WMResizeWidget(panel->navF, 490, 210); WMMoveWidget(panel->navF, 15, 10); WMSetFrameTitle(panel->navF, _("Workspace Navigation")); panel->cyclB = WMCreateSwitchButton(panel->navF); - WMResizeWidget(panel->cyclB, 280, 34); + WMResizeWidget(panel->cyclB, 410, 34); WMMoveWidget(panel->cyclB, 75, 30); WMSetButtonText(panel->cyclB, _("Wrap to the first workspace from the last workspace.")); @@ -170,18 +126,15 @@ static void createPanel(Panel * p) WMResizeWidget(panel->cyclL, 60, 60); WMMoveWidget(panel->cyclL, 10, 15); WMSetLabelImagePosition(panel->cyclL, WIPImageOnly); - path = LocateImage(CYCLE_FILE); - if (path) { - createImages(scr, rc, xis, path, &icon1, NULL); - if (icon1) { - WMSetLabelImage(panel->cyclL, icon1); - WMReleasePixmap(icon1); - } - wfree(path); + CreateImages(scr, rc, xis, CYCLE_FILE, &icon1, NULL); + if (icon1) + { + WMSetLabelImage(panel->cyclL, icon1); + WMReleasePixmap(icon1); } /**/ panel->linkB = WMCreateSwitchButton(panel->navF); - WMResizeWidget(panel->linkB, 280, 34); + WMResizeWidget(panel->linkB, 410, 34); WMMoveWidget(panel->linkB, 75, 75); WMSetButtonText(panel->linkB, _("Switch workspaces while dragging windows.")); @@ -189,18 +142,15 @@ static void createPanel(Panel * p) WMResizeWidget(panel->linkL, 60, 40); WMMoveWidget(panel->linkL, 10, 80); WMSetLabelImagePosition(panel->linkL, WIPImageOnly); - path = LocateImage(DONT_LINK_FILE); - if (path) { - createImages(scr, rc, xis, path, &icon1, NULL); - if (icon1) { - WMSetLabelImage(panel->linkL, icon1); - WMReleasePixmap(icon1); - } - wfree(path); + CreateImages(scr, rc, xis, DONT_LINK_FILE, &icon1, NULL); + if (icon1) + { + WMSetLabelImage(panel->linkL, icon1); + WMReleasePixmap(icon1); } /**/ panel->newB = WMCreateSwitchButton(panel->navF); - WMResizeWidget(panel->newB, 280, 34); + WMResizeWidget(panel->newB, 410, 34); WMMoveWidget(panel->newB, 75, 120); WMSetButtonText(panel->newB, _("Automatically create new workspaces.")); @@ -208,39 +158,33 @@ static void createPanel(Panel * p) WMResizeWidget(panel->newL, 60, 20); WMMoveWidget(panel->newL, 10, 130); WMSetLabelImagePosition(panel->newL, WIPImageOnly); - path = LocateImage(ADVANCE_FILE); - if (path) { - createImages(scr, rc, xis, path, &icon1, NULL); - if (icon1) { - WMSetLabelImage(panel->newL, icon1); - WMReleasePixmap(icon1); - } - wfree(path); + CreateImages(scr, rc, xis, ADVANCE_FILE, &icon1, NULL); + if (icon1) + { + WMSetLabelImage(panel->newL, icon1); + WMReleasePixmap(icon1); } /**/ panel->posL = WMCreateLabel(panel->navF); - WMResizeWidget(panel->posL, 140, 30); + WMResizeWidget(panel->posL, 200, 30); WMMoveWidget(panel->posL, 75, 165); - WMSetLabelTextAlignment(panel->posL, WARight); - WMSetLabelText(panel->posL, _("Position of workspace\nname display")); + // WMSetLabelTextAlignment(panel->posL, WARight); + WMSetLabelText(panel->posL, _("Position of workspace name display")); panel->posiL = WMCreateLabel(panel->navF); WMResizeWidget(panel->posiL, 60, 40); WMMoveWidget(panel->posiL, 10, 160); WMSetLabelImagePosition(panel->posiL, WIPImageOnly); - path = LocateImage(WSNAME_FILE); - if (path) { - createImages(scr, rc, xis, path, &icon1, NULL); - if (icon1) { - WMSetLabelImage(panel->posiL, icon1); - WMReleasePixmap(icon1); - } - wfree(path); + CreateImages(scr, rc, xis, WSNAME_FILE, &icon1, NULL); + if (icon1) + { + WMSetLabelImage(panel->posiL, icon1); + WMReleasePixmap(icon1); } panel->posP = WMCreatePopUpButton(panel->navF); WMResizeWidget(panel->posP, 125, 20); - WMMoveWidget(panel->posP, 225, 175); + WMMoveWidget(panel->posP, 290, 170); WMAddPopUpButtonItem(panel->posP, _("Disable")); WMAddPopUpButtonItem(panel->posP, _("Center")); WMAddPopUpButtonItem(panel->posP, _("Top")); @@ -252,54 +196,6 @@ static void createPanel(Panel * p) WMMapSubwidgets(panel->navF); - /***************** Dock/Clip *****************/ - panel->dockF = WMCreateFrame(panel->box); - WMResizeWidget(panel->dockF, 115, 210); - WMMoveWidget(panel->dockF, 390, 10); - WMSetFrameTitle(panel->dockF, _("Dock/Clip")); - - panel->dockB = WMCreateButton(panel->dockF, WBTToggle); - WMResizeWidget(panel->dockB, 64, 64); - WMMoveWidget(panel->dockB, 25, 35); - WMSetButtonImagePosition(panel->dockB, WIPImageOnly); - path = LocateImage(DOCK_FILE); - if (path) { - createImages(scr, rc, xis, path, &icon1, &icon2); - if (icon2) { - WMSetButtonImage(panel->dockB, icon2); - WMReleasePixmap(icon2); - } - if (icon1) { - WMSetButtonAltImage(panel->dockB, icon1); - WMReleasePixmap(icon1); - } - wfree(path); - } - WMSetBalloonTextForView(_("Disable/enable the application Dock (the\n" - "vertical icon bar in the side of the screen)."), WMWidgetView(panel->dockB)); - - panel->clipB = WMCreateButton(panel->dockF, WBTToggle); - WMResizeWidget(panel->clipB, 64, 64); - WMMoveWidget(panel->clipB, 25, 120); - WMSetButtonImagePosition(panel->clipB, WIPImageOnly); - path = LocateImage(CLIP_FILE); - if (path) { - createImages(scr, rc, xis, path, &icon1, &icon2); - if (icon2) { - WMSetButtonImage(panel->clipB, icon2); - WMReleasePixmap(icon2); - } - if (icon1) { - WMSetButtonAltImage(panel->clipB, icon1); - WMReleasePixmap(icon1); - } - wfree(path); - } - WMSetBalloonTextForView(_("Disable/enable the Clip (that thing with\n" - "a paper clip icon)."), WMWidgetView(panel->clipB)); - - WMMapSubwidgets(panel->dockF); - if (xis) RReleaseImage(xis); @@ -315,9 +211,6 @@ static void storeData(_Panel * panel) SetBoolForKey(WMGetButtonSelected(panel->cyclB), "CycleWorkspaces"); SetBoolForKey(WMGetButtonSelected(panel->newB), "AdvanceToNewWorkspace"); - SetBoolForKey(!WMGetButtonSelected(panel->dockB), "DisableDock"); - SetBoolForKey(!WMGetButtonSelected(panel->clipB), "DisableClip"); - SetStringForKey(WSNamePositions[WMGetPopUpButtonSelectedItem(panel->posP)], "WorkspaceNameDisplayPosition"); } diff --git a/WPrefs.app/tiff/Makefile.am b/WPrefs.app/tiff/Makefile.am index fe21243a..c370b762 100644 --- a/WPrefs.app/tiff/Makefile.am +++ b/WPrefs.app/tiff/Makefile.am @@ -8,6 +8,7 @@ EXTRA_DIST = \ configs.tiff \ cycleworkspaces.tiff \ dock.tiff \ + dockclipdrawersection.tiff \ dontlinkworkspaces.tiff \ ergonomic.tiff \ expert.tiff \ diff --git a/WPrefs.app/tiff/dockclipdrawersection.tiff b/WPrefs.app/tiff/dockclipdrawersection.tiff new file mode 100644 index 00000000..4022abd0 Binary files /dev/null and b/WPrefs.app/tiff/dockclipdrawersection.tiff differ diff --git a/WPrefs.app/tiff/drawer.tiff b/WPrefs.app/tiff/drawer.tiff new file mode 100644 index 00000000..6a2fee55 Binary files /dev/null and b/WPrefs.app/tiff/drawer.tiff differ diff --git a/WPrefs.app/xpm/Makefile.am b/WPrefs.app/xpm/Makefile.am index 0f08ef4f..b0149b39 100644 --- a/WPrefs.app/xpm/Makefile.am +++ b/WPrefs.app/xpm/Makefile.am @@ -9,6 +9,7 @@ EXTRA_DIST = \ configs.xpm \ cycleworkspaces.xpm \ dock.xpm \ + dockclipdrawersection.xpm \ dontlinkworkspaces.xpm \ ergonomic.xpm \ expert.xpm \ diff --git a/WPrefs.app/xpm/dockclipdrawersection.xpm b/WPrefs.app/xpm/dockclipdrawersection.xpm new file mode 100644 index 00000000..f482574d --- /dev/null +++ b/WPrefs.app/xpm/dockclipdrawersection.xpm @@ -0,0 +1,307 @@ +/* XPM */ +static char * dockclipdrawersection_xpm[] = { +"48 48 256 2", +" c #000100", +". c #000609", +"+ c #030601", +"@ c #0B080D", +"# c #070A06", +"$ c #080E10", +"% c #110E12", +"& c #131114", +"* c #0E1315", +"= c #121311", +"- c #161417", +"; c #18171A", +"> c #191A21", +", c #1C1A1D", +"' c #1C1C24", +") c #1E1F26", +"! c #232124", +"~ c #202426", +"{ c #222421", +"] c #22232B", +"^ c #252427", +"/ c #25252D", +"( c #27282F", +"_ c #29282B", +": c #2B2B33", +"< c #2B2D2A", +"[ c #2D2E36", +"} c #303038", +"| c #323034", +"1 c #2F3335", +"2 c #34353D", +"3 c #373538", +"4 c #383941", +"5 c #3C3A3D", +"6 c #393D4A", +"7 c #3D3E46", +"8 c #403E41", +"9 c #42424B", +"0 c #3F4350", +"a c #424441", +"b c #454346", +"c c #46464F", +"d c #464845", +"e c #4A484B", +"f c #434A51", +"g c #484951", +"h c #4D4B4F", +"i c #4D4D56", +"j c #4B4E5C", +"k c #505059", +"l c #515351", +"m c #53545D", +"n c #525563", +"o c #6C5612", +"p c #4F575E", +"q c #575760", +"r c #5A585C", +"s c #6F591D", +"t c #6C5B16", +"u c #5D5B59", +"v c #5B5B64", +"w c #585C6A", +"x c #725D20", +"y c #5F5D61", +"z c #5B5F6C", +"A c #72611F", +"B c #606069", +"C c #5D616F", +"D c #5E627B", +"E c #5F6371", +"F c #7A6327", +"G c #646563", +"H c #64646D", +"I c #5E666D", +"J c #5E6578", +"K c #616668", +"L c #616572", +"M c #7C6529", +"N c #676568", +"O c #6A646E", +"P c #636774", +"Q c #676770", +"R c #656876", +"S c #666983", +"T c #676B79", +"U c #816D19", +"V c #72696F", +"W c #726C57", +"X c #706A74", +"Y c #696D70", +"Z c #6B6C75", +"` c #6F6C70", +" . c #7D6E3B", +".. c #6A6E7C", +"+. c #837024", +"@. c #6B6F89", +"#. c #6D707F", +"$. c #70717A", +"%. c #887320", +"&. c #6E718C", +"*. c #797076", +"=. c #747276", +"-. c #927503", +";. c #8B7519", +">. c #87742F", +",. c #6F7381", +"'. c #7D7367", +"). c #767382", +"!. c #717583", +"~. c #6F768A", +"{. c #8D7825", +"]. c #78767A", +"^. c #737785", +"/. c #7A7772", +"(. c #8D792D", +"_. c #8C7933", +":. c #757988", +"<. c #7B797D", +"[. c #747B8F", +"}. c #7D798A", +"|. c #807984", +"1. c #7A7B84", +"2. c #787C8A", +"3. c #837A80", +"4. c #977C32", +"5. c #877986", +"6. c #917E3F", +"7. c #777E92", +"8. c #7A7E8C", +"9. c #807D8D", +"0. c #877D7A", +"a. c #7C808F", +"b. c #837F90", +"c. c #7B8296", +"d. c #7E8291", +"e. c #887F94", +"f. c #98843E", +"g. c #90826B", +"h. c #9E8339", +"i. c #828297", +"j. c #898471", +"k. c #9D862C", +"l. c #A38525", +"m. c #818593", +"n. c #A28534", +"o. c #8D8489", +"p. c #85868F", +"q. c #85859A", +"r. c #A5872F", +"s. c #8B8590", +"t. c #838796", +"u. c #918585", +"v. c #8D8875", +"w. c #8A8697", +"x. c #9D884E", +"y. c #868A99", +"z. c #8C8993", +"A. c #AD8932", +"B. c #8C8A8E", +"C. c #8A8A9F", +"D. c #A58D33", +"E. c #AB8C2C", +"F. c #928C79", +"G. c #858CA0", +"H. c #AA8C34", +"I. c #A58B4D", +"J. c #9089A1", +"K. c #A58E3B", +"L. c #898D9C", +"M. c #AC8E3D", +"N. c #A99036", +"O. c #938D98", +"P. c #8B8F9E", +"Q. c #9B8E7D", +"R. c #8E8EA4", +"S. c #AE9037", +"T. c #B09131", +"U. c #AB9145", +"V. c #8D91A0", +"W. c #AC9331", +"X. c #9390A0", +"Y. c #8F9396", +"Z. c #8F93A2", +"`. c #AE953B", +" + c #9592A3", +".+ c #B3943C", +"++ c #A19571", +"@+ c #9195A4", +"#+ c #A49194", +"$+ c #9B94A0", +"%+ c #9B93AB", +"&+ c #B1983E", +"*+ c #9497A6", +"=+ c #9996A6", +"-+ c #979996", +";+ c #9798A2", +">+ c #B5984D", +",+ c #B09A53", +"'+ c #9D99AA", +")+ c #979BAA", +"!+ c #A098B0", +"~+ c #BC9C43", +"{+ c #9A9FA1", +"]+ c #BBA03F", +"^+ c #C2A141", +"/+ c #9EA2B1", +"(+ c #AD9EB8", +"_+ c #ACA2A8", +":+ c #AFA1AF", +"<+ c #B9A66E", +"[+ c #A9A7AB", +"}+ c #A9ABA8", +"|+ c #B7A9B6", +"1+ c #B2ABB6", +"2+ c #AFACBD", +"3+ c #B0AEB2", +"4+ c #BCABB3", +"5+ c #C7B068", +"6+ c #BDAEC8", +"7+ c #CBB46B", +"8+ c #BBB1B8", +"9+ c #D3B56E", +"0+ c #D3B769", +"a+ c #CFB86F", +"b+ c #B5BABD", +"c+ c #B8BAB7", +"d+ c #BFBDC1", +"e+ c #BFBCCD", +"f+ c #BEC1BD", +"g+ c #C6C8C5", +"h+ c #C6CCCE", +"i+ c #E0CD7B", +"j+ c #D0CDD1", +"k+ c #D2D0D4", +"l+ c #D6D4D8", +"m+ c #D7D8E2", +"n+ c #DBD8DD", +"o+ c #DADCD9", +"p+ c #D8DDE0", +"q+ c #DCDDE7", +"r+ c #F4E08D", +"s+ c #E1E2EC", +"t+ c #E3E5E1", +"u+ c #E8E5EA", +"v+ c #E5E6F0", +"w+ c #ECE9EE", +"x+ c #E9EAF4", +"y+ c #EFEDF1", +"z+ c #EDEEF8", +"A+ c #F0F2EF", +"B+ c #F4F1F6", +"C+ c #F2F3FD", +"D+ c #F8F5FA", +"E+ c #FBF8FD", +"F+ c #FCFEFB", +"G+ c #FEFFFC", +"+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ", +"+ D D D D D D D D D D D D D D D D D D D D D D D E+B+B+B+y+y+z+y+y+y+x+x+x+w+w+u+u+u+s+s+s+s+[+G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D B+=+=+*+*+ +V.P.L.L.y.y.t.m.m.d.a.8.2.:.:.^.} G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D B+'+)+)+'+*+*+Z.V.P.L.L.y.y.t.m.m.d.a.8.2.2.2 G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D z+*+)+*+*+*+Z.*+Z.1.Z Z Z $.y.m.d.a.8.2.2.2.2 G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D z+=+*+*+*+Z.=+K @ # . . . + = ^.m.8.2.:.:.^.} G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D y+*+*+*+V.P._ + + + + + + + + + 4 #.#.,.^.^.} G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D y+ +*+Z./+, + + + + + + + + /.f+g+3 $ 2 !.!.} G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D y+X.Z.*+; + + + + + + + + + -+G+G+G+% _ !.,.} G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D y+P.V.2.@ + + + + + + + + + -+G+G+G+3+! ,.,.} G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D y+L.P.B + + + + + + + + + + -+G+G+G+o+5 #.#.[ G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D x+L.L.B + + + + + + + + + + -+G+G+G+A+y #...: G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D x+L.L.B + + + + % b d a l d f+G+G+G+A+y ....: G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D w+y.y.B + + + + = t+G+G+G+G+G+G+G+G+A+r ..T : G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D w+t.y.v + + + + = o+G+G+G+G+G+G+G+G+o+3 R R : G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D u+m.t.1.$ + + + = o+G+G+G+G+G+G+G+G+g+> R R ( G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D u+m.m.y.c + + + = o+G+G+G+G+G+G+G+D+= ~ R L ( G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D u+d.m.H ^ . l a l t+G+G+G+G+G+G+G+1 + ^ L E ( G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D s+a.d.q + + G t+G+G+G+G+G+G+G+p+b + + ! E C / G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D s+8.a.m + + + + u }+g+G+o+c+u # + + + ) z z / G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D s+2.8.,.q q q m k g c 7 9 c g g g c c j z z / G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D s+2.2.2.2.:.^.^.,.,.,.#.....R R P L E z z z ] G+", +"+ D D D D D D D D D D D D D D D D D D D D D D D s+:.2.:.:.!.!.,.#.#.....T R R L E C z z w w ] G+", +"+ &.&.&.&.&.&.&.&.&.&.&.&.&.@.@.&.@.@.@.@.@.@.S Y.} 2 } } } } } } } : : : : : ( ( ( ( / / ] ) G+", +"+ D+s+s+s+s+q+q+q+q+q+q+m+m+m+b+m+m+m+m+m+m+m+B.B+n+n+n+n+n+n+n+l+l+l+l+l+l+k+k+j+j+j+j+j+j+{+G+", +"+ B+=+)+'+*+Z.=+Z.V.P.L.L.y.t.V.h 2.a.E z z R ( D+'+'+)+'+*+=+Z.V.P.L.L.y.y.t.m.m.d.a.8.2.2.2 G+", +"+ B+=+'+'+,.@ H Z.V.P.L.L.y.y.t.y.q 2.z . + | ( C+)+'+)+*+*+=+Z.V.P.L.L.y.y.t.m.m.d.a.8.2.2.2 G+", +"+ B+=+)+=+y.t.i V.P.L.y.y.t.m.m.d.m.5 2.L + 3 / C+'+)+*+=+Z.Z.V.P.L.L.y.t.t.m.d.d.a.8.2.2.:.2 G+", +"+ B+Z.*+=+P.9 t.P.L.y.y.t.m.m.d.a.8.m.7 #.E 4 / C+*+*+=+Z.Z.R.P.Q.Q.F.v.v.v.v.j.j.j.j.1.:.:.} G+", +"+ y+X.*+Z.Z.Z.P.L.y.y.t.m.m.d.a.w.s.3.<.| ^.,.] C+=+*+Z.Z.R.++>+.+T.E.E.l.H.H.H.E.T.f.<.^.^.} G+", +"+ z+X.*+V.V.P.L.y.y.t.m.m.d.J.(+o.I v ].o.! ..] C+Z.*+Z.R.0+a+7+7+5+7+5+5+5+5+5+5+h.6.].!.!.} G+", +"+ z+P.Z.P.P.L.y.y.t.m.m.C.3.%+2+1+=.v B y ^.4 ' C+V.Z.V.R.a+^+^+^+^+^+]+]+]+^+~+A 4._.].,.,.} G+", +"+ z+L.V.P.L.y.y.t.m.m.9.e.6+f 8 c V B Z #.,.!.. C+P.V.P.G.a+~+~+~+~+~+&+&+&+`..+t (._.=.#.#.} G+", +"+ z+y.P.L.y.y.t.t.w.|.|+H b 8 k H O q $.v #.#.] z+L.P.a.q.a+~+~+`.r.H.N.E.,+`.`.t {._.=.#.#.[ G+", +"+ x+y.P.y.y.t.m.:.|+:+v 8 9 B #.$.}.q.p m ....) z+L.*+< [.a+~+~+&+H.i+r+N.>+`.H.t (.>.=.....: G+", +"+ x+t.L.y.t.d.=+_+|.h 8 i Q ,.2.Z 9.r i q ....) z+V.< < [.a+~+~+`.r.0+9++.,+N.H.t %.+.` ....: G+", +"+ w+t.y.t.b.8+O.|.8 b v ..,.}.e.*.q 8 k B T T ' z+L.$.{ [.a+~+&+.+U.>+6.x.<+E.D.t %.U Y R R : G+", +"+ u+m.t.;+u.1+k 8 h Q ,.,.!+5.).c 8 i B R R R ' z+t.y.Q ~.7+~+&+&+`.`.W.S.H.N.N.t U +.` R R : G+", +"+ d+,.m.4+O.c 9 q $...t.:+B X 7 9 q Q R R R R > x+t.t.m.c.7+M.K.K.D.k.k.n.k.k.k.t %.+.` R R ( G+", +"+ w+4 P.o.B b B ,.$.e+O s.g 8 i B R R R R P P > x+m.t.d.i.<+M F F F x s s s o o o %.+.N P L ( G+", +"+ u+)+: ).0.m Q P.$+s.H 8 9 q R R R R R L L L > x+d.m.d.7..+U.I.I.x.A.h.l._.6.(.-.;. .J L L ( G+", +"+ u+,.L.k $.#+_+X.].i 8 i B R R R R T L E E C > v+a.d.8.8.g.0.^.!.!./.=.'.....R W W J E C C / G+", +"+ v+6 L L./ |._+N 8 b q H ....R R R , R C z C > v+8.a.8.2.:.^.!.!.,.#.#.....R R R L E C z z / G+", +"+ s+0 + j L.g i i 5 4 + ^ B 2 k } _ . c 7 c c $ v+2.8.2.:.^.!.!.,.#.#.....R R R L E C z z z / G+", +"+ s+0 + + n :.7 :.,.* z , w - : + #.$ * z ] + % v+2.2.2.:.^.!.,.,.#.....T R R L L E z z w w ] G+", +"+ u+2.z.p.p.8.y.2 C B ,.R #.q R k R j E c c 9 @ x+8.a.8.2.:.:.^.!.,.#.#.....R R P L E z z z ] G+", +"+ f+; ; ; ; ; - , $ & - - - - & & & % % % % $ @ h+; , ; ; ; ; - - - - - - & & * & % % % % % . G+", +"+ G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+G+"}; diff --git a/WPrefs.app/xpm/drawer.xpm b/WPrefs.app/xpm/drawer.xpm new file mode 100644 index 00000000..f207fcb0 --- /dev/null +++ b/WPrefs.app/xpm/drawer.xpm @@ -0,0 +1,1149 @@ +/* XPM */ +static char * drawer_xpm[] = { +"48 48 1098 2", +" c None", +". c #DEDFEE", +"+ c #9091A0", +"@ c #9293A2", +"# c #7D7E88", +"$ c #B2B2B2", +"% c #363838", +"& c #6D6E6D", +"* c #1D201F", +"= c #191D1B", +"- c #646665", +"; c #565857", +"> c #1E2220", +", c #3A3E3D", +"' c #565958", +") c #464847", +"! c #343636", +"~ c #3A3C3B", +"{ c #91928F", +"] c #707281", +"^ c #707380", +"/ c #727482", +"( c #303139", +"_ c #DCDDEC", +": c #8E8F9E", +"< c #7B7C86", +"[ c #B6B6B6", +"} c #363A39", +"| c #727574", +"1 c #2B2F2D", +"2 c #2F3533", +"3 c #616664", +"4 c #454B49", +"5 c #2B312F", +"6 c #575C5A", +"7 c #545857", +"8 c #464948", +"9 c #353837", +"0 c #3D403F", +"a c #8F8F8D", +"b c #6D707F", +"c c #6F717F", +"d c #6F727F", +"e c #2F3138", +"f c #DADBEA", +"g c #8C8D9C", +"h c #777983", +"i c #BBBBBB", +"j c #101414", +"k c #252B29", +"l c #373D3A", +"m c #4E5553", +"n c #595F5E", +"o c #3B4340", +"p c #414845", +"q c #5C6260", +"r c #505553", +"s c #424746", +"t c #333736", +"u c #3F4241", +"v c #8D8E8B", +"w c #6C6E7D", +"x c #6C6F7C", +"y c #6D707E", +"z c #2E2F37", +"A c #D8D9E8", +"B c #8A8C9A", +"C c #757780", +"D c #BEBEBE", +"E c #171C1B", +"F c #2E3533", +"G c #464D4B", +"H c #606865", +"I c #505856", +"J c #404A47", +"K c #58615E", +"L c #545C5A", +"M c #49514E", +"N c #3C4340", +"O c #424645", +"P c #8B8C88", +"Q c #696C7B", +"R c #6B6D7B", +"S c #6B6E7B", +"T c #2D2E36", +"U c #D6D8E6", +"V c #888998", +"W c #8A8B9A", +"X c #72737D", +"Y c #C3C3C3", +"Z c #1A211F", +"` c #343C39", +" . c #3B4441", +".. c #4A5451", +"+. c #4C5653", +"@. c #4F5B57", +"#. c #56605C", +"$. c #4D5854", +"%. c #434D49", +"&. c #39423F", +"*. c #333A38", +"=. c #474C4B", +"-. c #888987", +";. c #686B79", +">. c #696B79", +",. c #6A6D7A", +"'. c #2B2D35", +"). c #D4D6E4", +"!. c #868896", +"~. c #6F717A", +"{. c #C8C8C8", +"]. c #1D2321", +"^. c #38403E", +"/. c #404946", +"(. c #48524F", +"_. c #4E5A56", +":. c #55625E", +"<. c #53605B", +"[. c #4C5854", +"}. c #45504C", +"|. c #3D4643", +"1. c #363D3B", +"2. c #4C504F", +"3. c #878885", +"4. c #666977", +"5. c #676A77", +"6. c #676A78", +"7. c #2A2C34", +"8. c #D3D4E3", +"9. c #848694", +"0. c #868796", +"a. c #6C6E77", +"b. c #CDCDCD", +"c. c #1C2321", +"d. c #38413E", +"e. c #48534F", +"f. c #505C58", +"g. c #576460", +"h. c #4D5A55", +"i. c #46504D", +"j. c #3E4744", +"k. c #4F5352", +"l. c #848582", +"m. c #646775", +"n. c #656875", +"o. c #666976", +"p. c #292B33", +"q. c #D0D2E0", +"r. c #828492", +"s. c #696B75", +"t. c #D3D3D3", +"u. c #0A100E", +"v. c #232A28", +"w. c #29312E", +"x. c #2F3835", +"y. c #333E3B", +"z. c #36413E", +"A. c #36403E", +"B. c #323C39", +"C. c #2D3633", +"D. c #272F2D", +"E. c #212826", +"F. c #444848", +"G. c #828381", +"H. c #626574", +"I. c #636673", +"J. c #636674", +"K. c #282A31", +"L. c #CFD0DF", +"M. c #808290", +"N. c #828392", +"O. c #62636D", +"P. c #D8D8D8", +"Q. c #D5D6D6", +"R. c #D7D7D7", +"S. c #D7D8D7", +"T. c #D7D8D8", +"U. c #D8D9D8", +"V. c #DFDFDF", +"W. c #858684", +"X. c #555865", +"Y. c #616471", +"Z. c #626572", +"`. c #272930", +" + c #CCCEDC", +".+ c #7F808F", +"++ c #686A76", +"@+ c #2E3032", +"#+ c #2B2C2F", +"$+ c #2A2B2E", +"%+ c #292B2E", +"&+ c #292A2D", +"*+ c #282A2D", +"=+ c #28292C", +"-+ c #27292B", +";+ c #26282B", +">+ c #26272A", +",+ c #252729", +"'+ c #242629", +")+ c #4D4F5A", +"!+ c #5F626F", +"~+ c #5F6370", +"{+ c #26282F", +"]+ c #CBCCDB", +"^+ c #7C7E8C", +"/+ c #7F818F", +"(+ c #7A7B89", +"_+ c #727480", +":+ c #6F717E", +"<+ c #6D6F7C", +"[+ c #6C6E7B", +"}+ c #6A6D78", +"|+ c #686A77", +"1+ c #666975", +"2+ c #656773", +"3+ c #61646F", +"4+ c #5F616E", +"5+ c #5D606C", +"6+ c #5B5E6B", +"7+ c #5A5D69", +"8+ c #5D606D", +"9+ c #5D616D", +"0+ c #5E616E", +"a+ c #25272E", +"b+ c #C8CAD8", +"c+ c #7B7D8B", +"d+ c #7A7C8A", +"e+ c #787A88", +"f+ c #767886", +"g+ c #747784", +"h+ c #6E707E", +"i+ c #686B78", +"j+ c #646774", +"k+ c #5E626E", +"l+ c #5C5F6C", +"m+ c #23252D", +"n+ c #C7C9D7", +"o+ c #777987", +"p+ c #737583", +"q+ c #6C707C", +"r+ c #696C79", +"s+ c #595D69", +"t+ c #5A5D6A", +"u+ c #22242C", +"v+ c #C0C2D0", +"w+ c #747684", +"x+ c #757785", +"y+ c #717481", +"z+ c #707280", +"A+ c #6D707D", +"B+ c #6C6E7C", +"C+ c #656876", +"D+ c #606370", +"E+ c #5A5E6A", +"F+ c #585B68", +"G+ c #565A66", +"H+ c #545864", +"I+ c #545865", +"J+ c #202229", +"K+ c #7C7D84", +"L+ c #6B6C74", +"M+ c #696A73", +"N+ c #696973", +"O+ c #676871", +"P+ c #666770", +"Q+ c #65666F", +"R+ c #64656E", +"S+ c #62636C", +"T+ c #61626B", +"U+ c #60616A", +"V+ c #5F6069", +"W+ c #5D5F67", +"X+ c #5C5D66", +"Y+ c #5B5D65", +"Z+ c #5A5B64", +"`+ c #595A63", +" @ c #575961", +".@ c #565860", +"+@ c #565861", +"@@ c #2B2D31", +"#@ c #A5A7B5", +"$@ c #A3A5B3", +"%@ c #A2A4B2", +"&@ c #464851", +"*@ c #F7F7FF", +"=@ c #C3C4D2", +"-@ c #C4C4D3", +";@ c #C2C3D2", +">@ c #C0C1D0", +",@ c #BEBFCE", +"'@ c #BCBDCC", +")@ c #BABBCA", +"!@ c #B8B9C8", +"~@ c #B7B8C7", +"{@ c #B4B5C4", +"]@ c #B3B4C3", +"^@ c #B0B2C0", +"/@ c #AFB0BF", +"(@ c #ACAEBC", +"_@ c #ABACBB", +":@ c #A8ABB8", +"<@ c #A7A8B7", +"[@ c #3A3B44", +"}@ c #FFFFFF", +"|@ c #C8C8D7", +"1@ c #C6C7D6", +"2@ c #C4C5D4", +"3@ c #C2C2D2", +"4@ c #BEBECE", +"5@ c #BABBCB", +"6@ c #B6B8C7", +"7@ c #B2B4C3", +"8@ c #B0B1C0", +"9@ c #AEB0BF", +"0@ c #AAACBB", +"a@ c #A8AAB8", +"b@ c #A7A9B7", +"c@ c #A8AAB9", +"d@ c #404149", +"e@ c #7D7F8D", +"f@ c #797B89", +"g@ c #373841", +"h@ c #F4F4FF", +"i@ c #9898A9", +"j@ c #9B9CAB", +"k@ c #9D99AE", +"l@ c #9B97AC", +"m@ c #9995AA", +"n@ c #9793A8", +"o@ c #9591A6", +"p@ c #9390A4", +"q@ c #918DA2", +"r@ c #8F8CA0", +"s@ c #8D8A9E", +"t@ c #8B889C", +"u@ c #89869A", +"v@ c #878498", +"w@ c #858396", +"x@ c #838094", +"y@ c #807F92", +"z@ c #7F7D90", +"A@ c #7A7C8B", +"B@ c #2F3038", +"C@ c #9999AA", +"D@ c #999AA9", +"E@ c #9798A7", +"F@ c #9596A5", +"G@ c #9394A3", +"H@ c #9193A1", +"I@ c #848594", +"J@ c #808190", +"K@ c #7E808E", +"L@ c #363740", +"M@ c #F2F2FF", +"N@ c #9999A9", +"O@ c #9C9CAC", +"P@ c #5EAA64", +"Q@ c #61A768", +"R@ c #60A566", +"S@ c #60A364", +"T@ c #5FA163", +"U@ c #5F9F62", +"V@ c #5E9E61", +"W@ c #5D9C5F", +"X@ c #5D9B5E", +"Y@ c #5C995D", +"Z@ c #5C985C", +"`@ c #5B955A", +" # c #5B9459", +".# c #5A9258", +"+# c #589255", +"@# c #758081", +"## c #2E3038", +"$# c #9797A7", +"%# c #9495A4", +"&# c #9192A1", +"*# c #8D8E9D", +"=# c #898B99", +"-# c #858795", +";# c #818391", +"># c #36373F", +",# c #35373F", +"'# c #F0F1FF", +")# c #9A9AAA", +"!# c #729D78", +"~# c #605B65", +"{# c #615D65", +"]# c #75717E", +"^# c #7E8B86", +"/# c #7A8883", +"(# c #807D8C", +"_# c #93909D", +":# c #9794A0", +"<# c #7C7987", +"[# c #777683", +"}# c #618465", +"|# c #747280", +"1# c #716F7D", +"2# c #706D7A", +"3# c #65736A", +"4# c #707D7C", +"5# c #787A89", +"6# c #F0F0FF", +"7# c #9898A8", +"8# c #9697A6", +"9# c #9595A5", +"0# c #8D8FA2", +"a# c #8A8EA2", +"b# c #888CA0", +"c# c #878A9F", +"d# c #85889D", +"e# c #83869B", +"f# c #808498", +"g# c #7F8296", +"h# c #7C8094", +"i# c #7A7E92", +"j# c #787C90", +"k# c #797B8A", +"l# c #34363E", +"m# c #757885", +"n# c #34353F", +"o# c #EEEFFE", +"p# c #9899A8", +"q# c #729F79", +"r# c #212023", +"s# c #E9E9ED", +"t# c #333336", +"u# c #82938B", +"v# c #77887E", +"w# c #B9BABF", +"x# c #BDBEC5", +"y# c #BCBDC5", +"z# c #BCBDC1", +"A# c #888990", +"B# c #5D805F", +"C# c #70727B", +"D# c #6E6F78", +"E# c #6C6D77", +"F# c #617266", +"G# c #6F7C7B", +"H# c #767986", +"I# c #2C2D36", +"J# c #9696A6", +"K# c #8D8FA3", +"L# c #A89560", +"M# c #B29642", +"N# c #AD903E", +"O# c #A88C39", +"P# c #A58936", +"Q# c #A98D39", +"R# c #A98D3A", +"S# c #A78B38", +"T# c #A68A36", +"U# c #74788D", +"V# c #777A87", +"W# c #34353D", +"X# c #33343D", +"Y# c #ECECFC", +"Z# c #6C9871", +"`# c #7B7B85", +" $ c #4E4E51", +".$ c #A3A3A5", +"+$ c #3B433B", +"@$ c #707972", +"#$ c #38383A", +"$$ c #93939A", +"%$ c #BBBCC4", +"&$ c #B9BAC2", +"*$ c #C1C2C8", +"=$ c #AEACB5", +"-$ c #878990", +";$ c #6D6D77", +">$ c #686972", +",$ c #5F7064", +"'$ c #6D7A78", +")$ c #747685", +"!$ c #2B2C34", +"~$ c #9294A2", +"{$ c #AC9B72", +"]$ c #C09F40", +"^$ c #BC9D41", +"/$ c #B89B41", +"($ c #B5973E", +"_$ c #B4963C", +":$ c #B1933A", +"<$ c #B3943C", +"[$ c #B2953C", +"}$ c #B3953C", +"|$ c #B1923A", +"1$ c #977F34", +"2$ c #73778B", +"3$ c #757786", +"4$ c #32333C", +"5$ c #EAEBFA", +"6$ c #9191A1", +"7$ c #69966F", +"8$ c #85858F", +"9$ c #95969E", +"0$ c #1C1C1C", +"a$ c #AAAAAC", +"b$ c #101010", +"c$ c #030303", +"d$ c #7F8086", +"e$ c #B9BAC1", +"f$ c #B8B9C0", +"g$ c #B7B8C0", +"h$ c #B5B6BE", +"i$ c #B2B2B7", +"j$ c #7A7B83", +"k$ c #596B5F", +"l$ c #6C7877", +"m$ c #727582", +"n$ c #2A2B33", +"o$ c #9292A2", +"p$ c #8E8F9F", +"q$ c #D8BB65", +"r$ c #CEB25B", +"s$ c #CCB059", +"t$ c #CBAF59", +"u$ c #C9AC57", +"v$ c #C7AB57", +"w$ c #C5A955", +"x$ c #C8AC57", +"y$ c #C1A861", +"z$ c #7F6A29", +"A$ c #947C2F", +"B$ c #717689", +"C$ c #737683", +"D$ c #31333B", +"E$ c #717381", +"F$ c #30323A", +"G$ c #E8E8F8", +"H$ c #8F909F", +"I$ c #9393A3", +"J$ c #5E9860", +"K$ c #909A97", +"L$ c #343335", +"M$ c #000000", +"N$ c #141415", +"O$ c #959597", +"P$ c #6C6C71", +"Q$ c #BEBFC8", +"R$ c #B8B9C1", +"S$ c #B6B7BF", +"T$ c #B4B5BC", +"U$ c #B2B3BB", +"V$ c #BFC0C6", +"W$ c #8B9E8A", +"X$ c #6A7875", +"Y$ c #282A32", +"Z$ c #8C8E9E", +"`$ c #CCAE53", +" % c #B99D43", +".% c #B89C42", +"+% c #BA9D43", +"@% c #B99C43", +"#% c #B59940", +"$% c #B3973E", +"%% c #B4983F", +"&% c #B2973D", +"*% c #A18735", +"=% c #786723", +"-% c #907A2A", +";% c #707388", +">% c #717382", +",% c #2F3139", +"'% c #E6E7F6", +")% c #76AA77", +"!% c #252A24", +"~% c #131313", +"{% c #ACACAF", +"]% c #87888D", +"^% c #BFC0C8", +"/% c #BABBC4", +"(% c #B4B5BD", +"_% c #66666A", +":% c #5C6D59", +"<% c #6B7776", +"[% c #6E717E", +"}% c #272931", +"|% c #8A8C9C", +"1% c #CBAC52", +"2% c #BB9D44", +"3% c #A3882B", +"4% c #AA8E34", +"5% c #AA8F34", +"6% c #AC9139", +"7% c #B19747", +"8% c #B1963C", +"9% c #B2973C", +"0% c #9C832E", +"a% c #75641D", +"b% c #917A2B", +"c% c #6D7285", +"d% c #2E2F38", +"e% c #E4E5F5", +"f% c #8B8C9B", +"g% c #8F8F9F", +"h% c #5F885E", +"i% c #050405", +"j% c #0F0F0F", +"k% c #88888A", +"l% c #606164", +"m% c #7B7B80", +"n% c #B3B4BB", +"o% c #68686C", +"p% c #788878", +"q% c #667271", +"r% c #6D6F7D", +"s% c #262730", +"t% c #E4E5F4", +"u% c #818290", +"v% c #44454B", +"w% c #888A99", +"x% c #CAAC51", +"y% c #AA8F35", +"z% c #B89E45", +"A% c #E0C874", +"B% c #B79B45", +"C% c #B2984A", +"D% c #B0953B", +"E% c #AB9036", +"F% c #75641C", +"G% c #8C7525", +"H% c #6C7084", +"I% c #6A6C7A", +"J% c #2C2E37", +"K% c #E2E3F2", +"L% c #497246", +"M% c #64646B", +"N% c #656568", +"O% c #D3D4D7", +"P% c #D6D6D9", +"Q% c #7B7B7F", +"R% c #9E9FA5", +"S% c #494A4C", +"T% c #5B6A59", +"U% c #687372", +"V% c #25272F", +"W% c #E2E3F3", +"X% c #676875", +"Y% c #0E0E10", +"Z% c #2A2B2D", +"`% c #868897", +" & c #A88D33", +".& c #BCA049", +"+& c #F3D890", +"@& c #927D3E", +"#& c #AB9246", +"$& c #AD9238", +"%& c #AC9037", +"&& c #9B822D", +"*& c #716016", +"=& c #88731F", +"-& c #6A6E82", +";& c #E1E2F1", +">& c #878897", +",& c #8B8B9B", +"'& c #588056", +")& c #050506", +"!& c #8F9097", +"~& c #ACADB4", +"{& c #A8A9AC", +"]& c #DFDFE2", +"^& c #E0E0E3", +"/& c #E5E5E8", +"(& c #626264", +"_& c #606D5D", +":& c #64706F", +"<& c #24252D", +"[& c #E0E1F1", +"}& c #7C7D8B", +"|& c #1B1B20", +"1& c #2B2B2D", +"2& c #848696", +"3& c #CAAC52", +"4& c #B69A41", +"5& c #A98E34", +"6& c #B09643", +"7& c #937D34", +"8& c #877336", +"9& c #AC944E", +"0& c #AB8F35", +"a& c #9B812D", +"b& c #705F13", +"c& c #867018", +"d& c #686C81", +"e& c #696C7A", +"f& c #749875", +"g& c #070507", +"h& c #1E1E1F", +"i& c #999AA0", +"j& c #94949A", +"k& c #7D7E7F", +"l& c #E9EAEC", +"m& c #DFDFE3", +"n& c #DEDFE1", +"o& c #626364", +"p& c #121013", +"q& c #7A867D", +"r& c #616C6B", +"s& c #23242C", +"t& c #4D4D56", +"u& c #828493", +"v& c #C8AB51", +"w& c #B69A40", +"x& c #B39842", +"y& c #B29741", +"z& c #B09540", +"A& c #AD923E", +"B& c #AE9440", +"C& c #9E8430", +"D& c #6F5F12", +"E& c #867117", +"F& c #666A7F", +"G& c #292A33", +"H& c #DDDEED", +"I& c #838593", +"J& c #878797", +"K& c #499D41", +"L& c #789275", +"M& c #080809", +"N& c #98999E", +"O& c #7E7E84", +"P& c #A9AAAC", +"Q& c #DFE0E2", +"R& c #DEDEE1", +"S& c #E1E2E5", +"T& c #5D5C5F", +"U& c #768F6E", +"V& c #447933", +"W& c #5F6D67", +"X& c #21232B", +"Y& c #DCDDED", +"Z& c #818292", +"`& c #C4A74F", +" * c #AF933C", +".* c #B0943D", +"+* c #AC923A", +"@* c #AA8F37", +"#* c #A68B34", +"$* c #A58A32", +"%* c #726017", +"&* c #8A741F", +"** c #64687C", +"=* c #272932", +"-* c #DADCEA", +";* c #848695", +">* c #5F8362", +",* c #7D7C88", +"'* c #747679", +")* c #161615", +"!* c #111111", +"~* c #252627", +"{* c #0E0D0E", +"]* c #B1B1B4", +"^* c #E4E5E8", +"/* c #DDDEE0", +"(* c #DADBDD", +"_* c #66666B", +":* c #424E46", +"<* c #5F6969", +"[* c #626573", +"}* c #20222A", +"|* c #818291", +"1* c #7F8190", +"2* c #816925", +"3* c #806926", +"4* c #806922", +"5* c #7F6823", +"6* c #79611F", +"7* c #76601C", +"8* c #745E1B", +"9* c #735D17", +"0* c #8A731A", +"a* c #61667A", +"b* c #262931", +"c* c #D9DAE9", +"d* c #838393", +"e* c #5D835E", +"f* c #8C8D94", +"g* c #6F7072", +"h* c #10100F", +"i* c #B4B4B6", +"j* c #DDDEE1", +"k* c #DDDDE0", +"l* c #E3E4E7", +"m* c #3C4A39", +"n* c #5C6766", +"o* c #1F2129", +"p* c #D8D9E9", +"q* c #7D7F8E", +"r* c #B49239", +"s* c #B08F3B", +"t* c #918466", +"u* c #9A854F", +"v* c #A78834", +"w* c #A48533", +"x* c #9F812C", +"y* c #937B34", +"z* c #616574", +"A* c #616472", +"B* c #7E7F8E", +"C* c #808291", +"D* c #5C815D", +"E* c #65726B", +"F* c #7E8C84", +"G* c #88898F", +"H* c #262626", +"I* c #7B7C80", +"J* c #797B82", +"K* c #45603F", +"L* c #B7B8BA", +"M* c #E6E7EA", +"N* c #EAEBEE", +"O* c #3D4938", +"P* c #5B6565", +"Q* c #1E2027", +"R* c #96855C", +"S* c #767989", +"T* c #767885", +"U* c #707587", +"V* c #7A7777", +"W* c #6F7281", +"X* c #7B756A", +"Y* c #6C6F7D", +"Z* c #26272F", +"`* c #24262E", +" = c #D5D6E5", +".= c #5A805B", +"+= c #666871", +"@= c #5F6D65", +"#= c #5D6A62", +"$= c #5F626B", +"%= c #77797F", +"&= c #7D7E84", +"*= c #595B64", +"== c #555860", +"-= c #4E694B", +";= c #41434A", +">= c #646565", +",= c #353534", +"'= c #394639", +")= c #596463", +"!= c #1D1F26", +"~= c #D4D5E5", +"{= c #777988", +"]= c #6B6E7C", +"^= c #5D606E", +"/= c #5B5F6B", +"(= c #22252C", +"_= c #D2D4E2", +":= c #7D7E8D", +"<= c #578455", +"[= c #637667", +"}= c #617566", +"|= c #5F7364", +"1= c #5A755B", +"2= c #587459", +"3= c #5A6C5E", +"4= c #56685A", +"5= c #546658", +"6= c #536456", +"7= c #4D6F47", +"8= c #516254", +"9= c #4D5D50", +"0= c #4F5E52", +"a= c #4A6246", +"b= c #586262", +"c= c #1B1E25", +"d= c #5C606C", +"e= c #5B5F6C", +"f= c #585C68", +"g= c #21232C", +"h= c #D1D3E1", +"i= c #737D80", +"j= c #727C7F", +"k= c #707A7D", +"l= c #6F787C", +"m= c #6C7679", +"n= c #6B7478", +"o= c #697376", +"p= c #687174", +"q= c #656F72", +"r= c #646E70", +"s= c #626B6E", +"t= c #616A6D", +"u= c #5F686A", +"v= c #5D6669", +"w= c #5B6567", +"x= c #5A6265", +"y= c #595E68", +"z= c #1B1D24", +"A= c #D0D2E1", +"B= c #686C78", +"C= c #595D6A", +"D= c #565966", +"E= c #525663", +"F= c #737584", +"G= c #6F7180", +"H= c #6B6D7C", +"I= c #535663", +"J= c #191B23", +"K= c #C6C8D6", +"L= c #666876", +"M= c #5F6270", +"N= c #545764", +"O= c #525662", +"P= c #525562", +"Q= c #1F2229", +"R= c #16181C", +"S= c #16171C", +"T= c #15171B", +"U= c #09090C", +"V= c #37383D", +"W= c #212126", +"X= c #212227", +"Y= c #212226", +"Z= c #202126", +"`= c #1F2025", +" - c #1F1F24", +".- c #1E1F24", +"+- c #1D1E23", +"@- c #1D1D22", +"#- c #1C1D21", +"$- c #1B1C21", +"%- c #1A1C20", +"&- c #1A1B20", +"*- c #191A1F", +"=- c #191A1E", +"-- c #18191D", +";- c #17181D", +">- c #7D7D84", +",- c #6E6E76", +"'- c #6D6E76", +")- c #6C6C75", +"!- c #6A6B73", +"~- c #696972", +"{- c #676770", +"]- c #65666E", +"^- c #63646C", +"/- c #62636B", +"(- c #61626A", +"_- c #606169", +":- c #5F6068", +"<- c #5D5E67", +"[- c #5C5E66", +"}- c #5B5C64", +"|- c #5A5B63", +"1- c #2E2F33", +"2- c #F6F6FF", +"3- c #C7C7D6", +"4- c #C5C5D5", +"5- c #C3C3D3", +"6- c #C1C1D1", +"7- c #BFBFCF", +"8- c #BDBDCD", +"9- c #BBBCCB", +"0- c #B9BAC9", +"a- c #B6B7C7", +"b- c #B5B6C5", +"c- c #B1B2C1", +"d- c #ADAEBD", +"e- c #A9AAB9", +"f- c #A6A8B7", +"g- c #3F4049", +"h- c #E9EAF7", +"i- c #9494A4", +"j- c #8F919E", +"k- c #8E909C", +"l- c #8B8D9A", +"m- c #888A98", +"n- c #7B7D8C", +"o- c #E8E8F7", +"p- c #9495A3", +"q- c #8F90A1", +"r- c #8585A1", +"s- c #7675A2", +"t- c #726FA2", +"u- c #7675A0", +"v- c #81829A", +"w- c #E5E6F6", +"x- c #7574A4", +"y- c #605CA6", +"z- c #5955A9", +"A- c #5D59AB", +"B- c #5F5BAC", +"C- c #5955AA", +"D- c #514EA6", +"E- c #58569F", +"F- c #6E6E98", +"G- c #81838E", +"H- c #9394A2", +"I- c #6A67A6", +"J- c #5955A8", +"K- c #6360AD", +"L- c #6E6AB2", +"M- c #7571B6", +"N- c #7B78B9", +"O- c #A7A4D0", +"P- c #CECDE5", +"Q- c #D3D2E7", +"R- c #8B88C3", +"S- c #66659D", +"T- c #7C7E8B", +"U- c #E2E2F2", +"V- c #9495A2", +"W- c #6C6AA5", +"X- c #5652A8", +"Y- c #918EC5", +"Z- c #D4D3E8", +"`- c #D0CEE5", +" ; c #C5C3E0", +".; c #B5B4D7", +"+; c #A9A7D2", +"@; c #DEDDED", +"#; c #6B68B2", +"$; c #686797", +"%; c #32333B", +"&; c #E0E1F0", +"*; c #7F7EA3", +"=; c #5955A7", +"-; c #5C58A9", +";; c #6C69B2", +">; c #A3A1CE", +",; c #FEFEFE", +"'; c #FBFBFC", +"); c #E8E8F2", +"!; c #E6E6F1", +"~; c #F2F2F7", +"{; c #FFFFFE", +"]; c #615DAC", +"^; c #5854A7", +"/; c #747686", +"(; c #31323B", +"_; c #DEDEEE", +":; c #9192A2", +"<; c #6967A5", +"[; c #5D59AA", +"}; c #6F6BB4", +"|; c #8380BD", +"1; c #D9D8EA", +"2; c #F6F6F9", +"3; c #B1AED4", +"4; c #9D9ACA", +"5; c #8A86C1", +"6; c #7B77B9", +"7; c #6A66B0", +"8; c #5956A8", +"9; c #524FA6", +"0; c #636383", +"a; c #717380", +"b; c #8E8FA0", +"c; c #5D5AA6", +"d; c #5854A8", +"e; c #5B57A9", +"f; c #6A66B1", +"g; c #7875B8", +"h; c #8986C0", +"i; c #C5C3DF", +"j; c #F8F8FA", +"k; c #A4A1CE", +"l; c #8885BF", +"m; c #7975B7", +"n; c #6864AF", +"o; c #5451A3", +"p; c #494D85", +"q; c #35346B", +"r; c #747781", +"s; c #8C8D9E", +"t; c #5753A8", +"u; c #6764AF", +"v; c #A29FCE", +"w; c #D2D1E7", +"x; c #E3E2EF", +"y; c #E0DFED", +"z; c #EDEDF5", +"A; c #AAA8D2", +"B; c #6E6BB2", +"C; c #5653A6", +"D; c #49509D", +"E; c #383861", +"F; c #514D9E", +"G; c #6E707F", +"H; c #8A8B9C", +"I; c #5753A7", +"J; c #E5E5F0", +"K; c #ECECF5", +"L; c #B8B7DA", +"M; c #A5A2CF", +"N; c #9A98C9", +"O; c #8C89C3", +"P; c #625FAC", +"Q; c #6367AD", +"R; c #26275A", +"S; c #3D3B81", +"T; c #5753A9", +"U; c #6B6D7D", +" . + @ # $ % & * = - ; > , ' ) ! ~ { ] ^ / ( ", +" _ : + < [ } | 1 2 3 4 5 6 7 8 9 0 a b c d e ", +" f g : h i j k l m n o p q r s t u v w x y z ", +" A B g C D E F G H I J K L M N F O P Q R S T ", +" U V W X Y Z ` ...+.@.#.$.%.&.*.=.-.;.>.,.'.", +" ).!.V ~.{.].^./.(._.:.<.[.}.|.1.2.3.4.5.6.7.", +" 8.9.0.a.b.c.d.J e.f.g.:.h.i.j.1.k.l.m.n.o.p.", +" q.r.9.s.t.u.v.w.x.y.z.A.B.C.D.E.F.G.H.I.J.K.", +" L.M.N.O.P.Q.R.S.T.P.U.U.P.T.R.R.V.W.X.Y.Z.`.", +" +.+M.++@+#+$+%+&+*+=+-+;+>+>+,+'+;+)+!+~+{+", +" ]+^+/+(+_+:+<+[+}+|+1+2+Z.3+4+5+6+7+8+9+0+a+", +" b+c+^+d+e+f+g+/ ^ h+x ,.i+o.j+Z.Y.k+8+6+l+m+", +" n+e+c+e+o+g+p+^ c q+R r+5.n.I.Y.!+9+6+s+t+u+", +" v+w+x+w+y+z+A+B+r+i+C+j+Z.D+0+l+E+F+G+H+I+J+", +" K+L+L+M+N+O+P+Q+R+S+T+U+V+W+X+Y+Z+`+ @.@+@@@", +"#@$@%@&@*@=@-@;@>@,@'@)@!@~@{@]@^@/@(@_@:@<@#@$@$@[@}@|@|@1@2@3@>@4@'@5@!@6@{@7@8@9@(@0@a@b@c@d@", +"e@d+f@g@h@i@j@k@l@m@n@o@p@q@r@s@t@u@v@w@x@y@z@A@d+B@h@C@j@D@E@F@G@H@+ : g W V 0.I@r.J@K@^+d+c+L@", +"^+c+f@g@M@N@O@P@Q@R@R@S@T@U@V@W@X@Y@Z@`@ #.#+#@#d+##M@D@j@D@$#F@%#&#+ *#g =#V -#I@;#M.K@^+c+c+>#", +"c+e+e+,#'#E@)#!#~#{#]#^#/#(#_#:#<#[#}#|#1#2#3#4#5#T 6#7#D@8#9#G@�#a#b#c#d#e#f#g#h#i#j#(+e+k#l#", +"e+o+m#n#o#9#p#q#r#s#t#u#v#w#x#y#z#A#B#C#D#E#F#G#H#I#o#J#E@%#G@&#K#L#M#N#O#P#Q#R#R#S#R#T#U#o+V#W#", +"o+x+w+X#Y#G@J#Z#`# $.$+$@$#$$$%$&$*$=$-$;$>$,$'$)$!$Y#%#J#~$&#{$]$^$/$($_$:$<$[$}$|$S#1$2$g+3$4$", +"x+p+y+4$5$6$%#7$8$9$0$a$b$c$d$%$e$f$g$h$i$j$k$l$m$n$5$o$%#&#p$q$r$s$t$t$t$u$v$w$x$y$z$A$B$p+C$D$", +"p+E$z+F$G$H$I$J$K$L$M$N$O$P$Q$R$f$S$h$T$U$V$W$X$] Y$G$+ @ : Z$`$ %.%+%@%.%#%$%%%&%*%=%-%;%^ >%( ", +"E$c A+,%'%*#+ )%!%M$M$M$~%{%]%^%/%S$(%T$e$_%:%<%[%}%'%: + 8#|%1%+%2%3%4%5%6%7%8%9%0%a%b%c%c d e ", +"c A+x d%e%f%g%h%i%M$M$M$M$j%k%l%m%T$(%n%o%M$p%q%r%s%t%: u%v%w%x%+%+%y%z%A%B%C%D%E%0%F%G%H%x y z ", +"A+S I%J%K%=#g L%M$M$M$M$M$M%N%O%P%Q%R%S%M$M$T%U%,.V%W%X%Y%Z%`%x%+% % &.&+&@&#&$&%&&&*&=&-&R S T ", +"S r+i+'.;&>&,&'&c$M$M$)&!&~&{&]&^&/&(&M$M$M$_&:&r+<&[&}&|&1&2&3&.%4&5&6&7&8&9&0& &a&b&c&d&i+e&'.", +"r+5.o.7.. -#V f&g&M$M$M$h&i&j&k&l&m&n&o&M$p&q&r&o.s&. >&-#t&u&v&w&%%x&y&z&A&B&}@}@C&D&E&F&5.5.7.", +"5.n.j+G&H&I&J&K&L&M$M$M$M$M&N&O&P&Q&R&S&T&U&V&W&n.X&Y&I@0.!.Z&`& *.*+*@*#*#*$*}@M$}@%*&***n.C+G&", +"n.I.Z.=*-*;#;*>*,*'*)*M$M$M$!*~*{*]*^*/*(*_*:*<*[*}*f r.9.|*1*2*3*4*5*6*7*8*9*}@M$M$}@0*a*I.I.K.", +"I.Y.D+b*c*/+d*e*C#D#f*g*h*M$M$M$M$N$i*j*k*l*m*n*Y.o*p*M.N./+q*r*s*t*u*v*w*x*y*}@M$M$M$}@z*Y.A*`.", +"Y.!+k+V%U B*C*D*D#E#O+E*F*G*H*b$I*J*K*L*M*N*O*P*!+Q*U K@M.e@c+R*S*T*U*V*W*X*Y*}@M$M$M$M$}@!+~+Z*", +"!+9+l+`* =c+/+.=E#M++=@=#=$=%=&=*===-=;=>=,='=)=8+!=~=^+/+c+d+{=f+C$/ d h+]=I%}@M$M$M$}@0+9+^=a+", +"0+/=E+(=_=d+:=<=[=}=|=1=2=3=4=5=5=6=7=8=9=0=a=b=6+c=_=c+^+d+o+f+w+/ z+h+B+,.i+}@}@}@M$}@d=6+e=m+", +"l+s+f=g=h=o+c+i=j=k=l=m=n=o=p=q=r=s=t=u=v=w=x=y=s+z=A=e+c+e+o+g+p+^ c x R B=5.n.I.}@M$M$}@s+C=u+", +"D=H+E=o*n+p+x+F=y+G=A+H=r+6.n.J.Y.D+^=l+C=F+D=H+I=J=K=/ p+/ d h+S I%5.L=J.Z.M=0+l+t+}@}@N=O=P=Q=", +"R=S=T=U=V=W=X=Y=Z=`= -.-+-@-#-$-%-&-*-=---;-R=S=T=M$>-,-'-)-L+!-~-O+{-]-R+^-/-(-_-:-<-[-}-|-}-1-", +" 2-1@3-4-5-6-7-8-9-0-a-b-7@c-9@d-0@e-f-#@f-g-", +" h-)#j@)#7#8#i-@ + j-k-l-m-0.9.r.M.K@e@d+n-L@", +" o-D@j@D@$#F@p-q-r-s-t-u-v-2&I@;#J@e@^+c+c+>#", +" w-7#D@8#%#@ x-y-z-A-B-C-D-E-F-G-e@c+f@e+f@l#", +" t%F@E@%#H-I-J-K-L-M-N-O-P-Q-R-S-T-d+o+o+V#W#", +" U-i-F@V-W-X-K-Y-Z-`- ;.;+;@;}@#;$;o+m#g+x+%;", +" &;o$p-*;=;-;;;>;,;';);!;~;}@{;];^;/;C$p+C$(;", +" _;+ :;<;^;[;};|;1;2;3;4;5;6;7;8;9;0;a;^ >%( ", +" _ : b;c;d;e;f;g;h;i;j;k;l;m;n;o;p;q;r;c d B@", +" f g s;t;^;u;v;w;x;y;z;}@A;B;C;D;E;F;G;x y z ", +" A W H;t;I;J;}@}@K;L;M;N;O;P;Q;R;S;T;U;R S T "}; diff --git a/WindowMaker/Defaults/WindowMaker.in b/WindowMaker/Defaults/WindowMaker.in index 3e576bea..d2f3d58d 100644 --- a/WindowMaker/Defaults/WindowMaker.in +++ b/WindowMaker/Defaults/WindowMaker.in @@ -43,6 +43,7 @@ NewStyle = "new"; DisableDock = NO; DisableClip = NO; + DisableDrawers = NO; Superfluous = YES; StickyIcons = NO; SaveSessionOnExit = NO; @@ -131,4 +132,8 @@ MenuTitleBack = (solid, black); MenuTextBack = (solid, "rgb:aa/aa/aa"); IconBack = (dgradient, "rgb:a6/a6/b6", "rgb:51/55/61"); + ClipAutocollapseDelay = 1000; + ClipAutolowerDelay = 1000; + ClipAutoexpandDelay = 600; + ClipAutoraiseDelay = 600; } diff --git a/WindowMaker/Icons/Drawer.png b/WindowMaker/Icons/Drawer.png new file mode 100644 index 00000000..8a33abe1 Binary files /dev/null and b/WindowMaker/Icons/Drawer.png differ diff --git a/WindowMaker/Icons/Makefile.am b/WindowMaker/Icons/Makefile.am index de5c1163..78868c3a 100644 --- a/WindowMaker/Icons/Makefile.am +++ b/WindowMaker/Icons/Makefile.am @@ -13,6 +13,7 @@ defsdata_DATA = \ defaultterm.xpm\ draw.tiff\ draw.xpm\ + Drawer.png\ Ear.png\ Ftp.png\ GNUstep3D.tiff\ diff --git a/debian/debianfiles/conf/WMWindowAttributes b/debian/debianfiles/conf/WMWindowAttributes index f1388c17..626f47a8 100644 --- a/debian/debianfiles/conf/WMWindowAttributes +++ b/debian/debianfiles/conf/WMWindowAttributes @@ -3,6 +3,7 @@ Logo.WMPanel = {Icon = GNUstep.tiff;}; Tile.WMClip = {Icon = clip.tiff;}; WPrefs = {Icon = "/usr/share/lib/GNUstep/System/Applications/WPrefs.app/WPrefs.tiff";}; + WMDrawer = { Icon = Drawer.png; }; Dockit = {Icon = GNUstep.tiff;}; WMSoundServer = {Icon = Sound.tiff;}; "*" = {Icon = defaultAppIcon.tiff;}; diff --git a/src/WindowMaker.h b/src/WindowMaker.h index 039a67ba..50d5dc27 100644 --- a/src/WindowMaker.h +++ b/src/WindowMaker.h @@ -419,6 +419,12 @@ typedef struct WPreferences { char cycle_active_head_only; /* Cycle only windows on the active head */ char cycle_ignore_minimized; /* Ignore minimized windows when cycling */ + /* All delays here are in ms. 0 means instant auto-action. */ + int clip_auto_raise_delay; /* Delay after which the clip will be raised when entered */ + int clip_auto_lower_delay; /* Delay after which the clip will be lowered when leaved */ + int clip_auto_expand_delay; /* Delay after which the clip will expand when entered */ + int clip_auto_collapse_delay; /* Delay after which the clip will collapse when leaved */ + RImage *swtileImage; RImage *swbackImage[9]; @@ -427,6 +433,9 @@ typedef struct WPreferences { struct { unsigned int nodock:1; /* don't display the dock */ unsigned int noclip:1; /* don't display the clip */ + unsigned int clip_merged_in_dock:1; /* disable clip, dock gets its workspace switching functionality */ + unsigned int nodrawer:1; /* don't use drawers */ + unsigned int wrap_appicons_in_dock:1; /* Whether to wrap appicons when Dock is moved up and down */ unsigned int noupdates:1; /* don't require ~/GNUstep (-static) */ unsigned int noautolaunch:1; /* don't autolaunch apps */ unsigned int norestore:1; /* don't restore session */ diff --git a/src/appicon.c b/src/appicon.c index b196931f..72fb3d00 100644 --- a/src/appicon.c +++ b/src/appicon.c @@ -64,6 +64,7 @@ extern WDDomain *WDWindowAttributes; extern XContext wWinContext; #define MOD_MASK wPreferences.modifier_mask +#define ICON_SIZE wPreferences.icon_size void appIconMouseDown(WObjDescriptor * desc, XEvent * event); static void iconDblClick(WObjDescriptor * desc, XEvent * event); @@ -129,6 +130,8 @@ WAppIcon *wAppIconCreateForDock(WScreen *scr, char *command, char *wm_instance, if (wm_instance) aicon->wm_instance = wstrdup(wm_instance); + if (strcmp(wm_class, "WMDock") == 0 && wPreferences.flags.clip_merged_in_dock) + tile = TILE_CLIP; aicon->icon = icon_create_for_dock(scr, command, wm_instance, wm_class, tile); #ifdef XDND @@ -199,7 +202,7 @@ void paint_app_icon(WApplication *wapp) { WIcon *icon; WScreen *scr = wapp->main_window_desc->screen_ptr; - WDock *clip = scr->workspaces[scr->current_workspace]->clip; + WDock *attracting_dock; int x = 0, y = 0; Bool update_icon = False; @@ -213,13 +216,17 @@ void paint_app_icon(WApplication *wapp) if (wapp->app_icon->docked) return; - if (clip && clip->attract_icons && wDockFindFreeSlot(clip, &x, &y)) { + attracting_dock = scr->attracting_drawer != NULL ? + scr->attracting_drawer : + scr->workspaces[scr->current_workspace]->clip; + if (attracting_dock && attracting_dock->attract_icons && + wDockFindFreeSlot(attracting_dock, &x, &y)) { wapp->app_icon->attracted = 1; if (!icon->shadowed) { icon->shadowed = 1; update_icon = True; } - wDockAttachIcon(clip, wapp->app_icon, x, y, update_icon); + wDockAttachIcon(attracting_dock, wapp->app_icon, x, y, update_icon); } else { /* We must know if the icon is painted in the screen, * because if painted, then PlaceIcon will return the next @@ -238,7 +245,7 @@ void paint_app_icon(WApplication *wapp) wapp->app_icon->next == NULL && wapp->app_icon->prev == NULL) add_to_appicon_list(scr, wapp->app_icon); - if (!clip || !wapp->app_icon->attracted || !clip->collapsed) + if (!attracting_dock || !wapp->app_icon->attracted || !attracting_dock->collapsed) XMapWindow(dpy, icon->core->window); if (wPreferences.auto_arrange_icons && !wapp->app_icon->attracted) @@ -273,6 +280,9 @@ void removeAppIconFor(WApplication *wapp) wAppIconPaint(wapp->app_icon); } else if (wapp->app_icon->docked) { wapp->app_icon->running = 0; + if (wapp->app_icon->dock->type == WM_DRAWER) { + wDrawerFillTheGap(wapp->app_icon->dock, wapp->app_icon, True); + } wDockDetach(wapp->app_icon->dock, wapp->app_icon); } else { wAppIconDestroy(wapp->app_icon); @@ -671,24 +681,8 @@ static void iconDblClick(WObjDescriptor *desc, XEvent *event) void appIconMouseDown(WObjDescriptor * desc, XEvent * event) { WAppIcon *aicon = desc->parent; - WIcon *icon = aicon->icon; - XEvent ev; - int x = aicon->x_pos, y = aicon->y_pos; - int dx = event->xbutton.x, dy = event->xbutton.y; - int grabbed = 0; - int done = 0; - int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */ - WScreen *scr = icon->core->screen_ptr; - WWorkspace *workspace = scr->workspaces[scr->current_workspace]; - int shad_x = 0, shad_y = 0, docking = 0, dockable, collapsed = 0; - int ix, iy; - int clickButton = event->xbutton.button; - Pixmap ghost = None; - Window wins[2]; - Bool movingSingle = False; - int oldX = x; - int oldY = y; - Bool hasMoved = False; + WScreen *scr = aicon->icon->core->screen_ptr; + Bool hasMoved; if (aicon->editing || WCHECK_STATE(WSTATE_MODAL)) return; @@ -733,24 +727,98 @@ void appIconMouseDown(WObjDescriptor * desc, XEvent * event) return; } - if (event->xbutton.state & MOD_MASK) - wLowerFrame(icon->core); - else + hasMoved = wHandleAppIconMove(aicon, event); + if (wPreferences.single_click && !hasMoved && aicon->dock != NULL) + { + iconDblClick(desc, event); + } +} + +Bool wHandleAppIconMove(WAppIcon *aicon, XEvent *event) +{ + WIcon *icon = aicon->icon; + WScreen *scr = icon->core->screen_ptr; + WDock *originalDock = aicon->dock; /* can be NULL */ + WDock *lastDock = originalDock; + WDock *allDocks[scr->drawer_count + 2]; /* clip, dock and drawers (order determined at runtime) */ + WDrawerChain *dc; + Bool done = False, dockable, ondock; + Bool grabbed = False; + Bool collapsed = False; /* Stores the collapsed state of lastDock, before the moving appicon entered it */ + int superfluous = wPreferences.superfluous; /* we cache it to avoid problems */ + int omnipresent = aicon->omnipresent; /* this must be cached */ + Bool showed_all_clips = False; + + int clickButton = event->xbutton.button; + Pixmap ghost = None; + Window wins[2]; /* Managing shadow window */ + XEvent ev; + + int x = aicon->x_pos, y = aicon->y_pos; + int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y; + int shad_x = x, shad_y = y; + int ix = aicon->xindex, iy = aicon->yindex; + int i; + int oldX = x; + int oldY = y; + Bool hasMoved = False; + + if (wPreferences.flags.noupdates && originalDock != NULL) + return False; + + if (!(event->xbutton.state & MOD_MASK)) wRaiseFrame(icon->core); + else { + /* If Mod is pressed for an docked appicon, assume it is to undock it, + * so don't lower it */ + if (originalDock == NULL) + wLowerFrame(icon->core); + } if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask | ButtonReleaseMask | ButtonPressMask, GrabModeAsync, - GrabModeAsync, None, None, CurrentTime) != GrabSuccess) - wwarning("pointer grab failed for appicon move"); + GrabModeAsync, None, None, CurrentTime) != GrabSuccess) { + wwarning("Pointer grab failed in wHandleAppIconMove"); + } - if (wPreferences.flags.nodock && wPreferences.flags.noclip) - dockable = 0; - else - dockable = canBeDocked(icon->owner); + if (originalDock != NULL) { + dockable = True; + ondock = True; + } + else { + ondock = False; + if (wPreferences.flags.nodock && wPreferences.flags.noclip && wPreferences.flags.nodrawer) + dockable = 0; + else + dockable = canBeDocked(icon->owner); + } + + /* We try the various docks in that order: + * - First, the dock the appicon comes from, if any + * - Then, the drawers + * - Then, the "dock" (WM_DOCK) + * - Finally, the clip + */ + i = 0; + if (originalDock != NULL) + allDocks[ i++ ] = originalDock; + /* Testing scr->drawers is enough, no need to test wPreferences.flags.nodrawer */ + for (dc = scr->drawers; dc != NULL; dc = dc->next) { + if (dc->adrawer != originalDock) + allDocks[ i++ ] = dc->adrawer; + } + if (!wPreferences.flags.nodock && scr->dock != originalDock) + allDocks[ i++ ] = scr->dock; + if (!wPreferences.flags.noclip && + originalDock != scr->workspaces[scr->current_workspace]->clip) + allDocks[ i++ ] = scr->workspaces[scr->current_workspace]->clip; + for ( ; i < scr->drawer_count + 2; i++) /* In case the clip, the dock, or both, are disabled */ + allDocks[ i ] = NULL; wins[0] = icon->core->window; wins[1] = scr->dock_shadow; XRestackWindows(dpy, wins, 2); + XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos, ICON_SIZE, ICON_SIZE); if (superfluous) { if (icon->pixmap != None) ghost = MakeGhostIcon(scr, icon->pixmap); @@ -759,6 +827,8 @@ void appIconMouseDown(WObjDescriptor * desc, XEvent * event) XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost); XClearWindow(dpy, scr->dock_shadow); } + if (ondock) + XMapWindow(dpy, scr->dock_shadow); while (!done) { WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask @@ -768,18 +838,18 @@ void appIconMouseDown(WObjDescriptor * desc, XEvent * event) WMHandleEvent(&ev); break; - case EnterNotify: - /* It means the cursor moved so fast that it entered - * something else (if moving slowly, it would have - * stayed in the appIcon that is being moved. Ignore - * such "spurious" EnterNotifiy's */ - break; + case EnterNotify: + /* It means the cursor moved so fast that it entered + * something else (if moving slowly, it would have + * stayed in the appIcon that is being moved. Ignore + * such "spurious" EnterNotifiy's */ + break; case MotionNotify: hasMoved = True; if (!grabbed) { - if (abs(dx - ev.xmotion.x) >= MOVE_THRESHOLD - || abs(dy - ev.xmotion.y) >= MOVE_THRESHOLD) { + if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD + || abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) { XChangeActivePointerGrab(dpy, ButtonMotionMask | ButtonReleaseMask | ButtonPressMask, wCursor[WCUR_MOVE], CurrentTime); @@ -788,68 +858,92 @@ void appIconMouseDown(WObjDescriptor * desc, XEvent * event) break; } } - x = ev.xmotion.x_root - dx; - y = ev.xmotion.y_root - dy; - if (movingSingle) - XMoveWindow(dpy, icon->core->window, x, y); - else - wAppIconMove(aicon, x, y); - - if (dockable) { - if (scr->dock && wDockSnapIcon(scr->dock, aicon, x, y, &ix, &iy, False)) { - shad_x = scr->dock->x_pos + ix * wPreferences.icon_size; - shad_y = scr->dock->y_pos + iy * wPreferences.icon_size; - - if (scr->last_dock != scr->dock && collapsed) { - scr->last_dock->collapsed = 1; - wDockHideIcons(scr->last_dock); - collapsed = 0; - } - if (!collapsed && (collapsed = scr->dock->collapsed)) { - scr->dock->collapsed = 0; - wDockShowIcons(scr->dock); - } - - if (scr->dock->auto_raise_lower) - wDockRaise(scr->dock); - - scr->last_dock = scr->dock; - - XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y); - if (!docking) - XMapWindow(dpy, scr->dock_shadow); - - docking = 1; - } else if (workspace->clip && - wDockSnapIcon(workspace->clip, aicon, x, y, &ix, &iy, False)) { - shad_x = workspace->clip->x_pos + ix * wPreferences.icon_size; - shad_y = workspace->clip->y_pos + iy * wPreferences.icon_size; - - if (scr->last_dock != workspace->clip && collapsed) { - scr->last_dock->collapsed = 1; - wDockHideIcons(scr->last_dock); - collapsed = 0; - } - if (!collapsed && (collapsed = workspace->clip->collapsed)) { - workspace->clip->collapsed = 0; - wDockShowIcons(workspace->clip); - } - - if (workspace->clip->auto_raise_lower) - wDockRaise(workspace->clip); - - scr->last_dock = workspace->clip; - - XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y); - if (!docking) - XMapWindow(dpy, scr->dock_shadow); - - docking = 1; - } else if (docking) { - XUnmapWindow(dpy, scr->dock_shadow); - docking = 0; + if (omnipresent && !showed_all_clips) { + int i; + for (i = 0; i < scr->workspace_count; i++) { + if (i == scr->current_workspace) + continue; + wDockShowIcons(scr->workspaces[i]->clip); + /* Note: if dock is collapsed (for instance, because it + auto-collapses), its icons still won't show up */ } + showed_all_clips = True; /* To prevent flickering */ + } + + x = ev.xmotion.x_root - ofs_x; + y = ev.xmotion.y_root - ofs_y; + wAppIconMove(aicon, x, y); + + WDock *theNewDock = NULL; + if (!(ev.xmotion.state & MOD_MASK) || aicon->launching || aicon->lock) { + for (i = 0; dockable && i < scr->drawer_count + 2; i++) { + WDock *theDock = allDocks[i]; + if (theDock == NULL) + break; + if (wDockSnapIcon(theDock, aicon, x, y, &ix, &iy, (theDock == originalDock))) { + theNewDock = theDock; + break; + } + } + if (originalDock != NULL && theNewDock == NULL && + (aicon->launching || aicon->lock || aicon->running)) { + /* In those cases, stay in lastDock if no dock really wants us */ + theNewDock = lastDock; + } + } + if (lastDock != NULL && lastDock != theNewDock) { + /* Leave lastDock in the state we found it */ + if (lastDock->type == WM_DRAWER) { + wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock)); + } + if (collapsed) { + lastDock->collapsed = 1; + wDockHideIcons(lastDock); + collapsed = False; + } + if (lastDock->auto_raise_lower) { + wDockLower(lastDock); + } + } + if (theNewDock != NULL) { + if (lastDock != theNewDock) { + collapsed = theNewDock->collapsed; + if (collapsed) { + theNewDock->collapsed = 0; + wDockShowIcons(theNewDock); + } + if (theNewDock->auto_raise_lower) { + wDockRaise(theNewDock); + /* And raise the moving tile above it */ + wRaiseFrame(aicon->icon->core); + } + lastDock = theNewDock; + } + + shad_x = lastDock->x_pos + ix*wPreferences.icon_size; + shad_y = lastDock->y_pos + iy*wPreferences.icon_size; + + XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y); + + if (!ondock) { + XMapWindow(dpy, scr->dock_shadow); + } + ondock = 1; + } else { + lastDock = theNewDock; // i.e., NULL + if (ondock) { + XUnmapWindow(dpy, scr->dock_shadow); + /* + * Leaving that weird comment for now. + * But if we see no gap, there is no need to fill one! + * We could test ondock first and the lastDock to NULL afterwards + if (lastDock_before_it_was_null->type == WM_DRAWER) { + wDrawerFillTheGap(lastDock, aicon, (lastDock == originalDock)); + } */ + + } + ondock = 0; } break; @@ -861,59 +955,130 @@ void appIconMouseDown(WObjDescriptor * desc, XEvent * event) break; XUngrabPointer(dpy, CurrentTime); - if (docking) { - Bool docked; - - /* icon is trying to be docked */ + Bool docked = False; + if (ondock) { SlideWindow(icon->core->window, x, y, shad_x, shad_y); XUnmapWindow(dpy, scr->dock_shadow); - docked = wDockAttachIcon(scr->last_dock, aicon, ix, iy, False); - if (scr->last_dock->auto_collapse) + if (originalDock == NULL) { // docking an undocked appicon + docked = wDockAttachIcon(lastDock, aicon, ix, iy, False); + if (!docked) { + /* AppIcon got rejected (happens only when we can't get the + command for that appicon, and the user cancels the + wInputDialog asking for one). Make the rejection obvious by + sliding the icon to its old position */ + if (lastDock->type == WM_DRAWER) { + // Also fill the gap left in the drawer + wDrawerFillTheGap(lastDock, aicon, False); + } + SlideWindow(icon->core->window, x, y, oldX, oldY); + } + } + else { // moving a docked appicon to a dock + if (originalDock == lastDock) { + docked = True; + wDockReattachIcon(originalDock, aicon, ix, iy); + } + else { + docked = wDockMoveIconBetweenDocks(originalDock, lastDock, aicon, ix, iy); + if (!docked) { + /* Possible scenario: user moved an auto-attracted appicon + from the clip to the dock, and cancelled the wInputDialog + asking for a command */ + if (lastDock->type == WM_DRAWER) { + wDrawerFillTheGap(lastDock, aicon, False); + } + /* If aicon comes from a drawer, make some room to reattach it */ + if (originalDock->type == WM_DRAWER) { + WAppIcon *aiconsToShift[ originalDock->icon_count ]; + int j = 0; + + for (i = 0; i < originalDock->max_icons; i++) { + WAppIcon *ai = originalDock->icon_array[ i ]; + if (ai && ai != aicon && + abs(ai->xindex) >= abs(aicon->xindex)) + aiconsToShift[j++] = ai; + } + if (j != originalDock->icon_count - abs(aicon->xindex) - 1) + // Trust this never happens? + wwarning("Shifting j=%d appicons (instead of %d!) to reinsert aicon at index %d.", + j, originalDock->icon_count - abs(aicon->xindex) - 1, aicon->xindex); + wSlideAppicons(aiconsToShift, j, originalDock->on_right_side); + // Trust the appicon is inserted at exactly the same place, so its oldX/oldY are consistent with its "new" location? + } + + SlideWindow(icon->core->window, x, y, oldX, oldY); + wDockReattachIcon(originalDock, aicon, aicon->xindex, aicon->yindex); + } + else { + if (originalDock->auto_collapse && !originalDock->collapsed) { + originalDock->collapsed = 1; + wDockHideIcons(originalDock); + } + if (originalDock->auto_raise_lower) + wDockLower(originalDock); + } + } + } + // No matter what happened above, check to lower lastDock + // Don't see why I commented out the following 2 lines + /* if (lastDock->auto_raise_lower) + wDockLower(lastDock); */ + /* If docked (or tried to dock) to a auto_collapsing dock, unset + * collapsed, so that wHandleAppIconMove doesn't collapse it + * right away (the timer will take care of it) */ + if (lastDock->auto_collapse) collapsed = 0; - - if (workspace->clip && - workspace->clip != scr->last_dock && workspace->clip->auto_raise_lower) - wDockLower(workspace->clip); - - if (!docked) { - /* If icon could not be docked, slide it back to the old - * position */ - SlideWindow(icon->core->window, x, y, oldX, oldY); - } - } else { - if (movingSingle) { - /* move back to its place */ - SlideWindow(icon->core->window, x, y, oldX, oldY); - wAppIconMove(aicon, oldX, oldY); - } else { - XMoveWindow(dpy, icon->core->window, x, y); - aicon->x_pos = x; - aicon->y_pos = y; - } - if (workspace->clip && workspace->clip->auto_raise_lower) - wDockLower(workspace->clip); } - if (collapsed) { - scr->last_dock->collapsed = 1; - wDockHideIcons(scr->last_dock); + else { + if (originalDock != NULL) { /* Detaching a docked appicon */ + if (superfluous) { + if (!aicon->running && !wPreferences.no_animations) { + /* We need to deselect it, even if is deselected in + * wDockDetach(), because else DoKaboom() will fail. + */ + if (aicon->icon->selected) + wIconSelect(aicon->icon); + DoKaboom(scr, aicon->icon->core->window, x, y); + } + } + wDockDetach(originalDock, aicon); + if (originalDock->auto_collapse && !originalDock->collapsed) { + originalDock->collapsed = 1; + wDockHideIcons(originalDock); + } + if (originalDock->auto_raise_lower) + wDockLower(originalDock); + } + } + // Can't remember why the icon hiding is better done above than below (commented out) + // Also, lastDock is quite different from originalDock + /* + if (collapsed) { + lastDock->collapsed = 1; + wDockHideIcons(lastDock); collapsed = 0; } + */ if (superfluous) { if (ghost != None) XFreePixmap(dpy, ghost); XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel); } - - if (wPreferences.auto_arrange_icons) + if (showed_all_clips) { + int i; + for (i = 0; i < scr->workspace_count; i++) { + if (i == scr->current_workspace) + continue; + wDockHideIcons(scr->workspaces[i]->clip); + } + } + if (wPreferences.auto_arrange_icons && !(originalDock != NULL && docked)) + /* Need to rearrange unless moving from dock to dock */ wArrangeIcons(scr, True); - - if (wPreferences.single_click && !hasMoved) - iconDblClick(desc, event); - - done = 1; - break; + return hasMoved; } } + return False; /* Never reached */ } /* This function save the application icon and store the path in the Dictionary */ @@ -980,7 +1145,7 @@ static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window m if (!wapp->app_icon && scr->dock) wapp->app_icon = findDockIconFor(scr->dock, main_window); - /* finally check clips */ + /* check clips */ if (!wapp->app_icon) { int i; for (i = 0; i < scr->workspace_count; i++) { @@ -992,6 +1157,16 @@ static void create_appicon_from_dock(WWindow *wwin, WApplication *wapp, Window m } } + /* Finally check drawers */ + if (!wapp->app_icon) { + WDrawerChain *dc; + for (dc = scr->drawers; dc != NULL; dc = dc->next) { + wapp->app_icon = findDockIconFor(dc->adrawer, main_window); + if (wapp->app_icon) + break; + } + } + /* If created, then set some flags */ if (wapp->app_icon) { WWindow *mainw = wapp->main_window_desc; diff --git a/src/appicon.h b/src/appicon.h index d63d9a7f..f5aa0d58 100644 --- a/src/appicon.h +++ b/src/appicon.h @@ -71,6 +71,7 @@ typedef struct WAppIcon { WAppIcon *wAppIconCreateForDock(WScreen *scr, char *command, char *wm_instance, char *wm_class, int tile); +Bool wHandleAppIconMove(WAppIcon *aicon, XEvent *event); void wAppIconDestroy(WAppIcon *aicon); void wAppIconPaint(WAppIcon *aicon); diff --git a/src/defaults.c b/src/defaults.c index 6c23064d..5300a23f 100644 --- a/src/defaults.c +++ b/src/defaults.c @@ -113,6 +113,8 @@ static int getPropList(); static int setJustify(); static int setClearance(); static int setIfDockPresent(); +static int setClipMergedInDock(); +static int setWrapAppiconsInDock(); static int setStickyIcons(); static int setWidgetColor(); static int setIconTile(); @@ -332,6 +334,10 @@ WDefaultEntry staticOptionList[] = { NULL, getBool, setIfDockPresent, NULL, NULL}, {"DisableClip", "NO", (void *)WM_CLIP, NULL, getBool, setIfDockPresent, NULL, NULL}, + {"DisableDrawers", "NO", (void *)WM_DRAWER, + NULL, getBool, setIfDockPresent, NULL, NULL}, + {"ClipMergedInDock", "NO", NULL, + NULL, getBool, setClipMergedInDock, NULL, NULL}, {"DisableMiniwindows", "NO", NULL, &wPreferences.disable_miniwindows, getBool, NULL, NULL, NULL} }; @@ -401,6 +407,16 @@ WDefaultEntry optionList[] = { &wPreferences.do_not_make_appicons_bounce, getBool, NULL, NULL, NULL}, {"DoubleClickTime", "250", (void *)&wPreferences.dblclick_time, &wPreferences.dblclick_time, getInt, setDoubleClick, NULL, NULL}, + {"ClipAutoraiseDelay", "600", NULL, + &wPreferences.clip_auto_raise_delay, getInt, NULL, NULL, NULL}, + {"ClipAutolowerDelay", "1000", NULL, + &wPreferences.clip_auto_lower_delay, getInt, NULL, NULL, NULL}, + {"ClipAutoexpandDelay", "600", NULL, + &wPreferences.clip_auto_expand_delay, getInt, NULL, NULL, NULL}, + {"ClipAutocollapseDelay", "1000", NULL, + &wPreferences.clip_auto_collapse_delay, getInt, NULL, NULL, NULL}, + {"WrapAppiconsInDock", "YES", NULL, + NULL, getBool, setWrapAppiconsInDock, NULL, NULL}, {"AlignSubmenus", "NO", NULL, &wPreferences.align_menus, getBool, NULL, NULL, NULL}, {"ViKeyMenus", "NO", NULL, @@ -1180,6 +1196,7 @@ void wReadDefaults(WScreen * scr, WMPropList * new_dict) void wDefaultUpdateIcons(WScreen *scr) { WAppIcon *aicon = scr->app_icon_list; + WDrawerChain *dc; WWindow *wwin = scr->focused_window; while (aicon) { @@ -1189,9 +1206,12 @@ void wDefaultUpdateIcons(WScreen *scr) aicon = aicon->next; } - if (!wPreferences.flags.noclip) + if (!wPreferences.flags.noclip || wPreferences.flags.clip_merged_in_dock) wClipIconPaint(scr->clip_icon); + for (dc = scr->drawers; dc != NULL; dc = dc->next) + wDrawerIconPaint(dc->adrawer->icon_array[0]); + while (wwin) { if (wwin->icon && wwin->flags.miniaturized) wIconChangeImageFile(wwin->icon, NULL); @@ -2373,16 +2393,34 @@ static int setIfDockPresent(WScreen * scr, WDefaultEntry * entry, char *flag, lo switch (which) { case WM_DOCK: wPreferences.flags.nodock = wPreferences.flags.nodock || *flag; + // Drawers require the dock + wPreferences.flags.nodrawer = wPreferences.flags.nodrawer || wPreferences.flags.nodock; break; case WM_CLIP: wPreferences.flags.noclip = wPreferences.flags.noclip || *flag; break; + case WM_DRAWER: + wPreferences.flags.nodrawer = wPreferences.flags.nodrawer || *flag; + break; default: break; } return 0; } +static int setClipMergedInDock(WScreen *scr, WDefaultEntry *entry, char *flag, void *foo) +{ + wPreferences.flags.clip_merged_in_dock = *flag; + wPreferences.flags.noclip = wPreferences.flags.noclip || *flag; + return 0; +} + +static int setWrapAppiconsInDock(WScreen *scr, WDefaultEntry *entry, char *flag, void *foo) +{ + wPreferences.flags.wrap_appicons_in_dock = *flag; + return 0; +} + static int setStickyIcons(WScreen * scr, WDefaultEntry * entry, void *bar, void *foo) { if (scr->workspaces) { @@ -2420,13 +2458,20 @@ static int setIconTile(WScreen * scr, WDefaultEntry * entry, WTexture ** texture /* put the icon in the noticeboard hint */ PropSetIconTileHint(scr, img); - if (!wPreferences.flags.noclip) { + if (!wPreferences.flags.noclip || wPreferences.flags.clip_merged_in_dock) { if (scr->clip_tile) { RReleaseImage(scr->clip_tile); } scr->clip_tile = wClipMakeTile(scr, img); } + if (!wPreferences.flags.nodrawer) { + if (scr->drawer_tile) { + RReleaseImage(scr->drawer_tile); + } + scr->drawer_tile = wDrawerMakeTile(scr, img); + } + scr->icon_tile_pixmap = pixmap; if (scr->def_icon_rimage) { diff --git a/src/defaults.h b/src/defaults.h index a2b9f24b..2f82fd6c 100644 --- a/src/defaults.h +++ b/src/defaults.h @@ -54,4 +54,7 @@ char *get_icon_filename(WScreen *scr, char *winstance, char *wclass, char *comma int wDefaultGetStartWorkspace(WScreen *scr, char *instance, char *class); void wDefaultChangeIcon(WScreen *scr, char *instance, char* class, char *file); RImage *get_rimage_from_file(WScreen *scr, char *file_name, int max_size); + +void wDefaultPurgeInfo(WScreen *scr, char *instance, char *class); + #endif /* WMDEFAULTS_H_ */ diff --git a/src/dock.c b/src/dock.c index 60f87b5e..38f82b9f 100644 --- a/src/dock.c +++ b/src/dock.c @@ -86,6 +86,8 @@ static WMPropList *dPosition, *dApplications, *dLowered, *dCollapsed; static WMPropList *dAutoCollapse, *dAutoRaiseLower, *dOmnipresent; +static WMPropList *dDrawers = NULL; + static void dockIconPaint(WAppIcon *btn); static void iconMouseDown(WObjDescriptor *desc, XEvent *event); @@ -106,8 +108,6 @@ static void clipLeave(WDock *dock); static void handleClipChangeWorkspace(WScreen *scr, XEvent *event); -static Bool moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y); - static void clipEnterNotify(WObjDescriptor *desc, XEvent *event); static void clipLeaveNotify(WObjDescriptor *desc, XEvent *event); static void clipAutoCollapse(void *cdata); @@ -116,8 +116,19 @@ static void launchDockedApplication(WAppIcon *btn, Bool withSelection); static void clipAutoLower(void *cdata); static void clipAutoRaise(void *cdata); -static void reattachIcon(WDock *dock, WAppIcon *icon, int x, int y); -static WAppIcon *mainIconCreate(WScreen *scr, int type); +static WAppIcon *mainIconCreate(WScreen *scr, int type, char *name); + +static void drawerIconExpose(WObjDescriptor *desc, XEvent *event); +static void removeDrawerCallback(WMenu *menu, WMenuEntry *entry); +static void drawerAppendToChain(WScreen *scr, WDock *drawer); +static char *findUniqueName(WScreen *scr, char *instance_basename); +static void addADrawerCallback(WMenu *menu, WMenuEntry *entry); +static void swapDrawers(WScreen *scr, int on_right_side, int new_x); +static WDock* getDrawer(WScreen *scr, int y_index); +static int indexOfHole(WDock *drawer, WAppIcon *moving_aicon, int redocking); +static void drawerConsolidateIcons(WDock *drawer); + +static int onScreen(WScreen *scr, int x, int y); static void make_keys(void) { @@ -150,6 +161,7 @@ static void make_keys(void) dDock = WMCreatePLString("Dock"); dClip = WMCreatePLString("Clip"); + dDrawers = WMCreatePLString("Drawers"); } static void renameCallback(WMenu *menu, WMenuEntry *entry) @@ -445,14 +457,35 @@ static void omnipresentCallback(WMenu *menu, WMenuEntry *entry) } } +static void removeIcons(WMArray *icons, WDock *dock) +{ + WAppIcon *aicon; + int keepit; + WMArrayIterator it; + + WM_ITERATE_ARRAY(icons, aicon, it) { + keepit = aicon->running && wApplicationOf(aicon->main_window); + wDockDetach(dock, aicon); + if (keepit) { + /* XXX: can: aicon->icon == NULL ? */ + PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos, + wGetHeadForWindow(aicon->icon->owner)); + XMoveWindow(dpy, aicon->icon->core->window, aicon->x_pos, aicon->y_pos); + if (!dock->mapped || dock->collapsed) + XMapWindow(dpy, aicon->icon->core->window); + } + } + WMFreeArray(icons); + + if (wPreferences.auto_arrange_icons) + wArrangeIcons(dock->screen_ptr, True); +} + static void removeIconsCallback(WMenu *menu, WMenuEntry *entry) { WAppIcon *clickedIcon = (WAppIcon *) entry->clientdata; WDock *dock; - WAppIcon *aicon; WMArray *selectedIcons; - int keepit; - WMArrayIterator it; assert(clickedIcon != NULL); @@ -461,9 +494,10 @@ static void removeIconsCallback(WMenu *menu, WMenuEntry *entry) selectedIcons = getSelected(dock); if (WMGetArrayItemCount(selectedIcons)) { - if (wMessageDialog(dock->screen_ptr, _("Workspace Clip"), - _("All selected icons will be removed!"), - _("OK"), _("Cancel"), NULL) != WAPRDefault) { + if (wMessageDialog(dock->screen_ptr, + dock->type == WM_CLIP ? _("Workspace Clip") : _("Drawer"), + _("All selected icons will be removed!"), + _("OK"), _("Cancel"), NULL) != WAPRDefault) { WMFreeArray(selectedIcons); return; } @@ -475,22 +509,11 @@ static void removeIconsCallback(WMenu *menu, WMenuEntry *entry) WMAddToArray(selectedIcons, clickedIcon); } - WM_ITERATE_ARRAY(selectedIcons, aicon, it) { - keepit = aicon->running && wApplicationOf(aicon->main_window); - wDockDetach(dock, aicon); - if (keepit) { - /* XXX: can: aicon->icon == NULL ? */ - PlaceIcon(dock->screen_ptr, &aicon->x_pos, &aicon->y_pos, - wGetHeadForWindow(aicon->icon->owner)); - XMoveWindow(dpy, aicon->icon->core->window, aicon->x_pos, aicon->y_pos); - if (!dock->mapped || dock->collapsed) - XMapWindow(dpy, aicon->icon->core->window); - } - } - WMFreeArray(selectedIcons); + removeIcons(selectedIcons, dock); - if (wPreferences.auto_arrange_icons) - wArrangeIcons(dock->screen_ptr, True); + if (dock->type == WM_DRAWER) { + drawerConsolidateIcons(dock); + } } static void keepIconsCallback(WMenu *menu, WMenuEntry *entry) @@ -559,14 +582,64 @@ static void keepIconsCallback(WMenu *menu, WMenuEntry *entry) static void toggleAutoAttractCallback(WMenu *menu, WMenuEntry *entry) { WDock *dock = (WDock *) entry->clientdata; + WScreen *scr = dock->screen_ptr; assert(entry->clientdata != NULL); + /* Setting a clip as autoattracting will disable an autoattracting drawer, + * and reciprocally. Ask for confirmation? */ +#ifdef CONFIRM_AUTOATTRACT_CHANGE + if (dock->type == WM_CLIP && scr->attracting_drawer) { + if (wMessageDialog(scr, _("Workspace Clip"), + _("The drawer that auto-attracted icons so far won't do so anymore!"), + _("OK"), _("Cancel"), NULL) != WAPRDefault) + return; + } else if (dock->type == WM_DRAWER) { + /* check if at least one clip already auto attracts icons */ + int i, ask_for_confirmation = False; + for (i = 0; i < scr->workspace_count; i++) { + if (scr->workspaces[ i ]->clip->attract_icons) { + ask_for_confirmation = True; + break; + } + } + if (ask_for_confirmation) { + if (wMessageDialog(scr, _("Drawer"), + _("Any clips that auto-attracted icons so far won't do so anymore!"), + _("OK"), _("Cancel"), NULL) != WAPRDefault) + return; + } + } +#endif + dock->attract_icons = !dock->attract_icons; entry->flags.indicator_on = dock->attract_icons; wMenuPaint(menu); + + if (dock->attract_icons) { + if (dock->type == WM_DRAWER) { + /* The newly auto-attracting dock is a drawer: disable any clip and + * previously attracting drawer */ + int i; + for (i = 0; i < scr->workspace_count; i++) { + scr->workspaces[ i ]->clip->attract_icons = False; + /* dock menu will be updated later, when opened */ + } + if (scr->attracting_drawer != NULL) + scr->attracting_drawer->attract_icons = False; + scr->attracting_drawer = dock; + } else { + /* The newly auto-attracting dock is a clip: disable + * previously attracting drawer, if applicable */ + if (scr->attracting_drawer != NULL) { + scr->attracting_drawer->attract_icons = False; + /* again, its menu will be updated, later. */ + scr->attracting_drawer = NULL; + } + } + } } static void selectCallback(WMenu *menu, WMenuEntry *entry) @@ -583,7 +656,7 @@ static void selectCallback(WMenu *menu, WMenuEntry *entry) static void colectIconsCallback(WMenu *menu, WMenuEntry *entry) { WAppIcon *clickedIcon = (WAppIcon *) entry->clientdata; - WDock *clip; + WDock *clip; /* clip... is a WM_CLIP or a WM_DRAWER */ WAppIcon *aicon; int x, y, x_pos, y_pos; Bool update_icon = False; @@ -668,6 +741,19 @@ static void toggleAutoCollapseCallback(WMenu *menu, WMenuEntry *entry) wMenuPaint(menu); } +static void toggleAutoRaiseLower(WDock *dock) +{ + WDrawerChain *dc; + + dock->auto_raise_lower = !dock->auto_raise_lower; + if (dock->type == WM_DOCK) + { + for (dc = dock->screen_ptr->drawers; dc != NULL; dc = dc->next) { + toggleAutoRaiseLower(dc->adrawer); + } + } +} + static void toggleAutoRaiseLowerCallback(WMenu *menu, WMenuEntry *entry) { WDock *dock; @@ -675,7 +761,7 @@ static void toggleAutoRaiseLowerCallback(WMenu *menu, WMenuEntry *entry) dock = (WDock *) entry->clientdata; - dock->auto_raise_lower = !dock->auto_raise_lower; + toggleAutoRaiseLower(dock); entry->flags.indicator_on = ((WDock *) entry->clientdata)->auto_raise_lower; @@ -723,35 +809,51 @@ static void unhideHereCallback(WMenu *menu, WMenuEntry *entry) wUnhideApplication(wapp, False, True); } -static WAppIcon *mainIconCreate(WScreen *scr, int type) +/* Name is only used when type == WM_DRAWER and when restoring a specific + * drawer, with a specific name. When creating a drawer, leave name to NULL + * and mainIconCreate will find the first unused unique name */ +WAppIcon *mainIconCreate(WScreen *scr, int type, char *name) { WAppIcon *btn; int x_pos; - if (type == WM_CLIP) { + switch(type) { + case WM_CLIP: if (scr->clip_icon) return scr->clip_icon; btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMClip", TILE_CLIP); btn->icon->core->descriptor.handle_expose = clipIconExpose; - btn->icon->core->descriptor.handle_enternotify = clipEnterNotify; - btn->icon->core->descriptor.handle_leavenotify = clipLeaveNotify; x_pos = 0; - } else { + break; + case WM_DOCK: + default: /* to avoid a warning about btn and x_pos, basically */ btn = wAppIconCreateForDock(scr, NULL, "Logo", "WMDock", TILE_NORMAL); + if (wPreferences.flags.clip_merged_in_dock) + btn->icon->core->descriptor.handle_expose = clipIconExpose; x_pos = scr->scr_width - ICON_SIZE - DOCK_EXTRA_SPACE; + break; + case WM_DRAWER: + if (name == NULL) + name = findUniqueName(scr, "Drawer"); + btn = wAppIconCreateForDock(scr, NULL, name, "WMDrawer", TILE_DRAWER); + btn->icon->core->descriptor.handle_expose = drawerIconExpose; + x_pos = 0; } btn->xindex = 0; btn->yindex = 0; btn->icon->core->descriptor.handle_mousedown = iconMouseDown; + btn->icon->core->descriptor.handle_enternotify = clipEnterNotify; + btn->icon->core->descriptor.handle_leavenotify = clipLeaveNotify; btn->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON; btn->icon->core->descriptor.parent = btn; XMapWindow(dpy, btn->icon->core->window); btn->x_pos = x_pos; btn->y_pos = 0; btn->docked = 1; - if (type == WM_CLIP) + if (type == WM_CLIP || + (type == WM_DOCK && wPreferences.flags.clip_merged_in_dock)) scr->clip_icon = btn; return btn; @@ -777,13 +879,13 @@ static void switchWSCommand(WMenu *menu, WMenuEntry *entry) WM_ITERATE_ARRAY(selectedIcons, btn, iter) { if (wDockFindFreeSlot(dest, &x, &y)) { - moveIconBetweenDocks(src, dest, btn, x, y); + wDockMoveIconBetweenDocks(src, dest, btn, x, y); XUnmapWindow(dpy, btn->icon->core->window); } } } else if (icon != scr->clip_icon) { if (wDockFindFreeSlot(dest, &x, &y)) { - moveIconBetweenDocks(src, dest, icon, x, y); + wDockMoveIconBetweenDocks(src, dest, icon, x, y); XUnmapWindow(dpy, icon->icon->core->window); } } @@ -897,6 +999,7 @@ static void updateClipOptionsMenu(WMenu *menu, WDock *dock) entry = menu->entries[index]; entry->flags.indicator_on = !dock->lowered; entry->clientdata = dock; + wMenuSetEnabled(menu, index, dock->type == WM_CLIP); /* collapsed */ entry = menu->entries[++index]; @@ -912,7 +1015,7 @@ static void updateClipOptionsMenu(WMenu *menu, WDock *dock) entry = menu->entries[++index]; entry->flags.indicator_on = dock->auto_raise_lower; entry->clientdata = dock; - wMenuSetEnabled(menu, index, dock->lowered); + wMenuSetEnabled(menu, index, dock->lowered && (dock->type == WM_CLIP)); /* attract icons */ entry = menu->entries[++index]; @@ -923,6 +1026,7 @@ static void updateClipOptionsMenu(WMenu *menu, WDock *dock) wMenuRealize(menu); } + static WMenu *makeClipOptionsMenu(WScreen *scr) { WMenu *menu; @@ -965,6 +1069,121 @@ static WMenu *makeClipOptionsMenu(WScreen *scr) return menu; } + +static void setDockPositionNormalCallback(WMenu *menu, WMenuEntry *entry) +{ + WDock *dock = (WDock *) entry->clientdata; + WDrawerChain *dc; + if (entry->flags.indicator_on) // already set, nothing to do + return; + // Do we come from auto raise lower or keep on top? + if (dock->auto_raise_lower) + { + dock->auto_raise_lower = 0; + // Only for aesthetic purposes, can be removed when Autoraise status is no longer exposed in drawer option menu + for (dc = dock->screen_ptr->drawers; dc != NULL; dc = dc->next) { + dc->adrawer->auto_raise_lower = 0; + } + } + else + { + // Will take care of setting lowered = 0 in drawers + toggleLowered(dock); + } + entry->flags.indicator_on = 1; +} + +static void setDockPositionAutoRaiseLowerCallback(WMenu *menu, WMenuEntry *entry) +{ + WDock *dock = (WDock *) entry->clientdata; + WDrawerChain *dc; + if (entry->flags.indicator_on) // already set, nothing to do + return; + // Do we come from normal or keep on top? + if (!dock->lowered) + { + toggleLowered(dock); + } + dock->auto_raise_lower = 1; + // Only for aesthetic purposes, can be removed when Autoraise status is no longer exposed in drawer option menu + for (dc = dock->screen_ptr->drawers; dc != NULL; dc = dc->next) { + dc->adrawer->auto_raise_lower = 1; + } + entry->flags.indicator_on = 1; +} + +static void setDockPositionKeepOnTopCallback(WMenu *menu, WMenuEntry *entry) +{ + WDock *dock = (WDock *) entry->clientdata; + WDrawerChain *dc; + if (entry->flags.indicator_on) // already set, nothing to do + return; + dock->auto_raise_lower = 0; + // Only for aesthetic purposes, can be removed when Autoraise status is no longer exposed in drawer option menu + for (dc = dock->screen_ptr->drawers; dc != NULL; dc = dc->next) { + dc->adrawer->auto_raise_lower = 0; + } + toggleLowered(dock); + entry->flags.indicator_on = 1; +} + +static void updateDockPositionMenu(WMenu *menu, WDock *dock) +{ + WMenuEntry *entry; + int index = 0; + + assert(menu); + assert(dock); + + /* Normal level */ + entry = menu->entries[index++]; + entry->flags.indicator_on = (dock->lowered && !dock->auto_raise_lower); + entry->clientdata = dock; + + /* Auto-raise/lower */ + entry = menu->entries[index++]; + entry->flags.indicator_on = dock->auto_raise_lower; + entry->clientdata = dock; + + /* Keep on top */ + entry = menu->entries[index++]; + entry->flags.indicator_on = !dock->lowered; + entry->clientdata = dock; +} + +static WMenu *makeDockPositionMenu(WScreen *scr) +{ + /* When calling this, the dock is being created, so scr->dock is still not set + * Therefore the callbacks' clientdata and the indicators can't be set, + * they will be updated when the dock menu is opened. */ + WMenu *menu; + WMenuEntry *entry; + + menu = wMenuCreate(scr, NULL, False); + if (!menu) { + wwarning(_("could not create options submenu for dock position menu")); + return NULL; + } + + entry = wMenuAddCallback(menu, _("Normal"), setDockPositionNormalCallback, NULL); + entry->flags.indicator = 1; + entry->flags.indicator_type = MI_DIAMOND; + + entry = wMenuAddCallback(menu, _("Auto raise & lower"), setDockPositionAutoRaiseLowerCallback, NULL); + entry->flags.indicator = 1; + entry->flags.indicator_type = MI_DIAMOND; + + entry = wMenuAddCallback(menu, _("Keep on Top"), setDockPositionKeepOnTopCallback, NULL); + entry->flags.indicator = 1; + entry->flags.indicator_type = MI_DIAMOND; + + menu->flags.realized = 0; + wMenuRealize(menu); + + return menu; +} + + static WMenu *dockMenuCreate(WScreen *scr, int type) { WMenu *menu; @@ -973,21 +1192,38 @@ static WMenu *dockMenuCreate(WScreen *scr, int type) if (type == WM_CLIP && scr->clip_menu) return scr->clip_menu; - menu = wMenuCreate(scr, NULL, False); - if (type != WM_CLIP) { - entry = wMenuAddCallback(menu, _("Keep on Top"), toggleLoweredCallback, NULL); - entry->flags.indicator = 1; - entry->flags.indicator_on = 1; - entry->flags.indicator_type = MI_CHECK; - } else { - entry = wMenuAddCallback(menu, _("Clip Options"), NULL, NULL); - scr->clip_options = makeClipOptionsMenu(scr); - if (scr->clip_options) - wMenuEntrySetCascade(menu, entry, scr->clip_options); + if (type == WM_DRAWER && scr->drawer_menu) + return scr->drawer_menu; - entry = wMenuAddCallback(menu, _("Rename Workspace"), renameCallback, NULL); - wfree(entry->text); - entry->text = _("Rename Workspace"); + menu = wMenuCreate(scr, NULL, False); + if (type == WM_DOCK) { + entry = wMenuAddCallback(menu, _("Dock position"), NULL, NULL); + if (scr->dock_pos_menu == NULL) + scr->dock_pos_menu = makeDockPositionMenu(scr); + wMenuEntrySetCascade(menu, entry, scr->dock_pos_menu); + + if (!wPreferences.flags.nodrawer) { + entry = wMenuAddCallback(menu, _("Add a drawer"), addADrawerCallback, NULL); + } + } else { + if (type == WM_CLIP) + entry = wMenuAddCallback(menu, _("Clip Options"), NULL, NULL); + else /* if (type == WM_DRAWER) */ + entry = wMenuAddCallback(menu, _("Drawer options"), NULL, NULL); + + if (scr->clip_options == NULL) + scr->clip_options = makeClipOptionsMenu(scr); + wMenuEntrySetCascade(menu, entry, scr->clip_options); + + /* The same menu is used for the dock and its appicons. If the menu + * entry text is different between the two contexts, or if it can + * change depending on some state, free the duplicated string (from + * wMenuInsertCallback) and use gettext's string */ + if (type == WM_CLIP) { + entry = wMenuAddCallback(menu, _("Rename Workspace"), renameCallback, NULL); + wfree(entry->text); + entry->text = _("Rename Workspace"); /* can be: (Toggle) Omnipresent */ + } entry = wMenuAddCallback(menu, _("Selected"), selectCallback, NULL); entry->flags.indicator = 1; @@ -996,22 +1232,24 @@ static WMenu *dockMenuCreate(WScreen *scr, int type) entry = wMenuAddCallback(menu, _("Select All Icons"), selectIconsCallback, NULL); wfree(entry->text); - entry->text = _("Select All Icons"); + entry->text = _("Select All Icons"); /* can be: Unselect all icons */ entry = wMenuAddCallback(menu, _("Keep Icon"), keepIconsCallback, NULL); wfree(entry->text); - entry->text = _("Keep Icon"); + entry->text = _("Keep Icon"); /* can be: Keep Icons */ - entry = wMenuAddCallback(menu, _("Move Icon To"), NULL, NULL); - wfree(entry->text); - entry->text = _("Move Icon To"); - scr->clip_submenu = makeWorkspaceMenu(scr); - if (scr->clip_submenu) - wMenuEntrySetCascade(menu, entry, scr->clip_submenu); + if (type == WM_CLIP) { + entry = wMenuAddCallback(menu, _("Move Icon To"), NULL, NULL); + wfree(entry->text); + entry->text = _("Move Icon To"); /* can be: Move Icons to */ + scr->clip_submenu = makeWorkspaceMenu(scr); + if (scr->clip_submenu) + wMenuEntrySetCascade(menu, entry, scr->clip_submenu); + } entry = wMenuAddCallback(menu, _("Remove Icon"), removeIconsCallback, NULL); wfree(entry->text); - entry->text = _("Remove Icon"); + entry->text = _("Remove Icon"); /* can be: Remove Icons */ wMenuAddCallback(menu, _("Attract Icons"), colectIconsCallback, NULL); } @@ -1022,19 +1260,24 @@ static WMenu *dockMenuCreate(WScreen *scr, int type) entry = wMenuAddCallback(menu, _("Hide"), hideCallback, NULL); wfree(entry->text); - entry->text = _("Hide"); + entry->text = _("Hide"); /* can be: Unhide */ wMenuAddCallback(menu, _("Settings..."), settingsCallback, NULL); - wMenuAddCallback(menu, _("Kill"), killCallback, NULL); + entry = wMenuAddCallback(menu, _("Kill"), killCallback, NULL); + wfree(entry->text); + entry->text = _("Kill"); /* can be: Remove drawer */ if (type == WM_CLIP) scr->clip_menu = menu; + if (type == WM_DRAWER) + scr->drawer_menu = menu; + return menu; } -WDock *wDockCreate(WScreen *scr, int type) +WDock *wDockCreate(WScreen *scr, int type, char *name) { WDock *dock; WAppIcon *btn; @@ -1043,11 +1286,21 @@ WDock *wDockCreate(WScreen *scr, int type) dock = wmalloc(sizeof(WDock)); - dock->max_icons = DOCK_MAX_ICONS; + switch (type) { + case WM_CLIP: + dock->max_icons = DOCK_MAX_ICONS; + break; + case WM_DRAWER: + dock->max_icons = scr->scr_width / wPreferences.icon_size; + break; + case WM_DOCK: + default: + dock->max_icons = scr->scr_height / wPreferences.icon_size; + } dock->icon_array = wmalloc(sizeof(WAppIcon *) * dock->max_icons); - btn = mainIconCreate(scr, type); + btn = mainIconCreate(scr, type, name); btn->dock = dock; @@ -1056,7 +1309,10 @@ WDock *wDockCreate(WScreen *scr, int type) dock->screen_ptr = scr; dock->type = type; dock->icon_count = 1; - dock->on_right_side = 1; + if (type == WM_DRAWER) + dock->on_right_side = scr->dock->on_right_side; + else + dock->on_right_side = 1; dock->collapsed = 0; dock->auto_collapse = 0; dock->auto_collapse_magic = NULL; @@ -1072,6 +1328,11 @@ WDock *wDockCreate(WScreen *scr, int type) /* create dock menu */ dock->menu = dockMenuCreate(scr, type); + if (type == WM_DRAWER) { + drawerAppendToChain(scr, dock); + dock->auto_collapse = 1; + } + return dock; } @@ -1123,7 +1384,7 @@ void wClipIconPaint(WAppIcon *aicon) snprintf(ws_number, sizeof(ws_number), "%i", scr->current_workspace + 1); nlength = strlen(ws_number); - if (!workspace->clip->collapsed) + if (wPreferences.flags.noclip || !workspace->clip->collapsed) color = scr->clip_title_color[CLIP_NORMAL]; else color = scr->clip_title_color[CLIP_COLLAPSED]; @@ -1157,6 +1418,8 @@ static void dockIconPaint(WAppIcon *btn) { if (btn == btn->icon->core->screen_ptr->clip_icon) { wClipIconPaint(btn); + } else if (wIsADrawer(btn->icon->core->screen_ptr, btn)) { + wDrawerIconPaint(btn); } else { wAppIconPaint(btn); save_appicon(btn, True); @@ -1191,7 +1454,7 @@ static WMPropList *make_icon_state(WAppIcon *btn) buggy = btn->buggy_app ? dYes : dNo; - if (btn == btn->icon->core->screen_ptr->clip_icon) + if (!wPreferences.flags.clip_merged_in_dock && btn == btn->icon->core->screen_ptr->clip_icon) snprintf(buffer, sizeof(buffer), "%i,%i", btn->x_pos, btn->y_pos); else snprintf(buffer, sizeof(buffer), "%hi,%hi", btn->xindex, btn->yindex); @@ -1265,23 +1528,25 @@ static WMPropList *dockSaveState(WDock *dock) } WMReleasePropList(list); - value = (dock->lowered ? dYes : dNo); - WMPutInPLDictionary(dock_state, dLowered, value); - - if (dock->type == WM_CLIP) { + if (dock->type == WM_CLIP || dock->type == WM_DRAWER) { value = (dock->collapsed ? dYes : dNo); WMPutInPLDictionary(dock_state, dCollapsed, value); value = (dock->auto_collapse ? dYes : dNo); WMPutInPLDictionary(dock_state, dAutoCollapse, value); - value = (dock->auto_raise_lower ? dYes : dNo); - WMPutInPLDictionary(dock_state, dAutoRaiseLower, value); - value = (dock->attract_icons ? dYes : dNo); WMPutInPLDictionary(dock_state, dAutoAttractIcons, value); } + if (dock->type == WM_DOCK || dock->type == WM_CLIP) { + value = (dock->lowered ? dYes : dNo); + WMPutInPLDictionary(dock_state, dLowered, value); + + value = (dock->auto_raise_lower ? dYes : dNo); + WMPutInPLDictionary(dock_state, dAutoRaiseLower, value); + } + return dock_state; } @@ -1392,10 +1657,8 @@ static WAppIcon *restore_icon_state(WScreen *scr, WMPropList *info, int type, in wfree(command); aicon->icon->core->descriptor.handle_mousedown = iconMouseDown; - if (type == WM_CLIP) { - aicon->icon->core->descriptor.handle_enternotify = clipEnterNotify; - aicon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify; - } + aicon->icon->core->descriptor.handle_enternotify = clipEnterNotify; + aicon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify; aicon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON; aicon->icon->core->descriptor.parent = aicon; @@ -1436,12 +1699,9 @@ static WAppIcon *restore_icon_state(WScreen *scr, WMPropList *info, int type, in wwarning(_("bad value in docked icon state info %s"), WMGetFromPLString(dPosition)); /* check position sanity */ - /* incomplete section! */ + /* *Very* incomplete section! */ if (type == WM_DOCK) { aicon->xindex = 0; - if (aicon->yindex < 0) - wwarning(_("bad value in docked icon position %i,%i"), - aicon->xindex, aicon->yindex); } } else { aicon->yindex = index; @@ -1466,7 +1726,7 @@ WAppIcon *wClipRestoreState(WScreen *scr, WMPropList *clip_state) WAppIcon *icon; WMPropList *value; - icon = mainIconCreate(scr, WM_CLIP); + icon = mainIconCreate(scr, WM_CLIP, NULL); if (!clip_state) return icon; @@ -1481,19 +1741,11 @@ WAppIcon *wClipRestoreState(WScreen *scr, WMPropList *clip_state) if (!WMIsPLString(value)) { COMPLAIN("Position"); } else { - WMRect rect; - int flags; - if (sscanf(WMGetFromPLString(value), "%i,%i", &icon->x_pos, &icon->y_pos) != 2) COMPLAIN("Position"); /* check position sanity */ - rect.pos.x = icon->x_pos; - rect.pos.y = icon->y_pos; - rect.size.width = rect.size.height = ICON_SIZE; - - wGetRectPlacementInfo(scr, rect, &flags); - if (flags & (XFLAG_DEAD | XFLAG_PARTIAL)) + if (!onScreen(scr, icon->x_pos, icon->y_pos)) wScreenKeepInside(scr, &icon->x_pos, &icon->y_pos, ICON_SIZE, ICON_SIZE); } } @@ -1520,7 +1772,7 @@ WDock *wDockRestoreState(WScreen *scr, WMPropList *dock_state, int type) WAppIcon *aicon, *old_top; int count, i; - dock = wDockCreate(scr, type); + dock = wDockCreate(scr, type, NULL); if (!dock_state) return dock; @@ -1533,19 +1785,11 @@ WDock *wDockRestoreState(WScreen *scr, WMPropList *dock_state, int type) if (!WMIsPLString(value)) { COMPLAIN("Position"); } else { - WMRect rect; - int flags; - if (sscanf(WMGetFromPLString(value), "%i,%i", &dock->x_pos, &dock->y_pos) != 2) COMPLAIN("Position"); /* check position sanity */ - rect.pos.x = dock->x_pos; - rect.pos.y = dock->y_pos; - rect.size.width = rect.size.height = ICON_SIZE; - - wGetRectPlacementInfo(scr, rect, &flags); - if (flags & (XFLAG_DEAD | XFLAG_PARTIAL)) { + if (!onScreen(scr, dock->x_pos, dock->y_pos)) { int x = dock->x_pos; wScreenKeepInside(scr, &x, &dock->y_pos, ICON_SIZE, ICON_SIZE); } @@ -1721,7 +1965,7 @@ WDock *wDockRestoreState(WScreen *scr, WMPropList *dock_state, int type) * incremented in the loop above. */ } else if (old_top != dock->icon_array[0]) { - if (old_top == scr->clip_icon) + if (old_top == scr->clip_icon) // TODO dande: understand the logic scr->clip_icon = dock->icon_array[0]; wAppIconDestroy(old_top); @@ -1933,10 +2177,8 @@ Bool wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y, Bool update_icon icon->docked = 1; icon->dock = dock; icon->icon->core->descriptor.handle_mousedown = iconMouseDown; - if (dock->type == WM_CLIP) { - icon->icon->core->descriptor.handle_enternotify = clipEnterNotify; - icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify; - } + icon->icon->core->descriptor.handle_enternotify = clipEnterNotify; + icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify; icon->icon->core->descriptor.parent_type = WCLASS_DOCK_ICON; icon->icon->core->descriptor.parent = icon; @@ -1976,7 +2218,7 @@ Bool wDockAttachIcon(WDock *dock, WAppIcon *icon, int x, int y, Bool update_icon return True; } -static void reattachIcon(WDock *dock, WAppIcon *icon, int x, int y) +void wDockReattachIcon(WDock *dock, WAppIcon *icon, int x, int y) { int index; @@ -1993,7 +2235,7 @@ static void reattachIcon(WDock *dock, WAppIcon *icon, int x, int y) icon->y_pos = dock->y_pos + y * ICON_SIZE; } -static Bool moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y) +Bool wDockMoveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y) { WWindow *wwin; char *command = NULL; @@ -2041,7 +2283,7 @@ static Bool moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, } } - if (dest->type == WM_DOCK) + if (dest->type == WM_DOCK || dest->type == WM_DRAWER) wClipMakeIconOmnipresent(icon, False); for (index = 1; index < src->max_icons; index++) { @@ -2067,18 +2309,13 @@ static Bool moveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, if (icon->icon->selected) wIconSelect(icon->icon); - if (dest->type == WM_DOCK) { - icon->icon->core->descriptor.handle_enternotify = NULL; - icon->icon->core->descriptor.handle_leavenotify = NULL; - } else { - icon->icon->core->descriptor.handle_enternotify = clipEnterNotify; - icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify; - } + icon->icon->core->descriptor.handle_enternotify = clipEnterNotify; + icon->icon->core->descriptor.handle_leavenotify = clipLeaveNotify; /* set it to be kept when moving to dock. * Unless the icon does not have a command set */ - if (icon->command && dest->type == WM_DOCK) { + if (icon->command && (dest->type == WM_DOCK || dest->type == WM_DRAWER)) { icon->attracted = 0; if (icon->icon->shadowed) { icon->icon->shadowed = 0; @@ -2206,6 +2443,8 @@ void wDockDetach(WDock *dock, WAppIcon *icon) * Note: this function should NEVER alter ret_x or ret_y, unless it will * return True. -Dan */ +/* Redocking == true means either icon->dock == dock (normal case) + * or we are called from handleDockMove for a drawer */ Bool wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y, int *ret_x, int *ret_y, int redocking) { WScreen *scr = dock->screen_ptr; @@ -2214,10 +2453,6 @@ Bool wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y, int *ret_x int i, offset = ICON_SIZE / 2; WAppIcon *aicon = NULL; WAppIcon *nicon = NULL; - int max_y_icons; - - /* TODO: XINERAMA, for these */ - max_y_icons = scr->scr_height / ICON_SIZE - 1; if (wPreferences.flags.noupdates) return False; @@ -2241,21 +2476,24 @@ Bool wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y, int *ret_x ex_x = (req_x + offset - dx) / ICON_SIZE; /* check if the icon is outside the screen boundaries */ - { - WMRect rect; - int flags; + if (!onScreen(scr, dx + ex_x * ICON_SIZE, dy + ex_y * ICON_SIZE)) + return False; - rect.pos.x = dx + ex_x * ICON_SIZE; - rect.pos.y = dy + ex_y * ICON_SIZE; - rect.size.width = rect.size.height = ICON_SIZE; - - wGetRectPlacementInfo(scr, rect, &flags); - if (flags & (XFLAG_DEAD | XFLAG_PARTIAL)) + switch (dock->type) { + case WM_DOCK: + /* We can return False right away if + * - we do not come from this dock (which is a WM_DOCK), + * - we are not right over it, and + * - we are not the main tile of a drawer. + * In the latter case, we are called from handleDockMove. */ + if (icon->dock != dock && ex_x != 0 && + !(icon->dock && icon->dock->type == WM_DRAWER && icon == icon->dock->icon_array[0])) return False; - } - if (dock->type == WM_DOCK) { - if (icon->dock != dock && ex_x != 0) + if (!redocking && ex_x != 0) + return False; + + if (getDrawer(scr, ex_y)) /* Return false so that the drawer gets it. */ return False; aicon = NULL; @@ -2273,6 +2511,7 @@ Bool wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y, int *ret_x /* Possible cases when redocking: * * icon dragged out of range of any slot -> false + * icon dragged on a drawer -> false (to open the drawer) * icon dragged to range of free slot * icon dragged to range of same slot * icon dragged to range of different icon @@ -2280,7 +2519,7 @@ Bool wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y, int *ret_x if (abs(ex_x) > DOCK_DETTACH_THRESHOLD) return False; - if (ex_y >= 0 && ex_y <= max_y_icons && (aicon == icon || !aicon)) { + if (aicon == icon || !aicon) { *ret_x = 0; *ret_y = ex_y; return True; @@ -2292,7 +2531,6 @@ Bool wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y, int *ret_x else sig = -1; - closest = -1; done = 0; /* look for closest free slot */ for (i = 0; i < (DOCK_DETTACH_THRESHOLD + 1) * 2 && !done; i++) { @@ -2300,23 +2538,27 @@ Bool wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y, int *ret_x done = 1; closest = sig * (i / 2) + ex_y; - /* check if this slot is used */ - if (closest >= 0) { + /* check if this slot is fully on the screen and not used */ + if (onScreen(scr, dx, dy + closest * ICON_SIZE)) { for (j = 0; j < dock->max_icons; j++) { if (dock->icon_array[j] - && dock->icon_array[j]->yindex == closest) { + && dock->icon_array[j]->yindex == closest) { /* slot is used by someone else */ if (dock->icon_array[j] != icon) done = 0; break; } } + /* slot is used by a drawer */ + done = done && !getDrawer(scr, closest); } + else // !onScreen + done = 0; sig = -sig; } - if (done && closest >= 0 && closest <= max_y_icons && - ((ex_y >= closest && ex_y - closest < DOCK_DETTACH_THRESHOLD + 1) - || (ex_y < closest && closest - ex_y <= DOCK_DETTACH_THRESHOLD + 1))) { + if (done && + ((ex_y >= closest && ex_y - closest < DOCK_DETTACH_THRESHOLD + 1) + || (ex_y < closest && closest - ex_y <= DOCK_DETTACH_THRESHOLD + 1))) { *ret_x = 0; *ret_y = closest; return True; @@ -2324,13 +2566,15 @@ Bool wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y, int *ret_x } else { /* !redocking */ /* if slot is free and the icon is close enough, return it */ - if (!aicon && ex_x == 0 && ex_y >= 0 && ex_y <= max_y_icons) { + if (!aicon && ex_x == 0) { *ret_x = 0; *ret_y = ex_y; return True; } } - } else { /* CLIP */ + break; + case WM_CLIP: + { int neighbours = 0; int start, stop, k; @@ -2374,15 +2618,63 @@ Bool wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y, int *ret_x *ret_y = ex_y; return True; } + break; + } + case WM_DRAWER: + { + WAppIcon *aicons_to_shift[ dock->icon_count ]; + int index_of_hole, j; + + if (ex_y != 0 || + abs(ex_x) - dock->icon_count > DOCK_DETTACH_THRESHOLD || + (ex_x < 0 && !dock->on_right_side) || + (ex_x > 0 && dock->on_right_side)) { + return False; + } + + if (ex_x == 0) + ex_x = (dock->on_right_side ? -1 : 1); + + /* "Reduce" ex_x but keep its sign */ + if (redocking) { + if (abs(ex_x) > dock->icon_count - 1) /* minus 1: do not take icon_array[0] into account */ + ex_x = ex_x * (dock->icon_count - 1) / abs(ex_x); /* don't use *= ! */ + } else { + if (abs(ex_x) > dock->icon_count) + ex_x = ex_x * dock->icon_count / abs(ex_x); + } + index_of_hole = indexOfHole(dock, icon, redocking); + + /* Find the appicons between where icon was (index_of_hole) and where + * it wants to be (ex_x) and slide them. */ + j = 0; + for (i = 1; i < dock->max_icons; i++) { + if ((aicon = dock->icon_array[ i ]) && aicon != icon && + ((ex_x <= aicon->xindex && aicon->xindex < index_of_hole) || + (index_of_hole < aicon->xindex && aicon->xindex <= ex_x))) + aicons_to_shift[ j++ ] = aicon; + } + assert(j == abs(ex_x - index_of_hole)); + + wSlideAppicons(aicons_to_shift, j, (index_of_hole < ex_x)); + + *ret_x = ex_x; + *ret_y = ex_y; + return True; + } } return False; } -static int onScreen(WScreen *scr, int x, int y, int sx, int ex, int sy, int ey) +static int onScreen(WScreen *scr, int x, int y) { - WMRect rect = wmkrect(x, y, ICON_SIZE, ICON_SIZE); + WMRect rect; int flags; + rect.pos.x = x; + rect.pos.y = y; + rect.size.width = rect.size.height = ICON_SIZE; + wGetRectPlacementInfo(scr, rect, &flags); return !(flags & (XFLAG_DEAD | XFLAG_PARTIAL)); @@ -2404,9 +2696,18 @@ Bool wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos) int x, y; int i, done = False; int corner; - int sx = 0, sy = 0, ex = scr->scr_width, ey = scr->scr_height; + int sx = 0, ex = scr->scr_width, ey = scr->scr_height; int extra_count = 0; + if (dock->type == WM_DRAWER) { + if (dock->icon_count >= dock->max_icons) { /* drawer is full */ + return False; + } + *x_pos = dock->icon_count * (dock->on_right_side ? -1 : 1); + *y_pos = 0; + return True; + } + if (dock->type == WM_CLIP && dock != scr->workspaces[scr->current_workspace]->clip) extra_count = scr->global_icon_count; @@ -2616,7 +2917,7 @@ Bool wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos) tx = dock->x_pos + x * ICON_SIZE; y = -i; ty = dock->y_pos + y * ICON_SIZE; - if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty, sx, ex, sy, ey)) { + if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty)) { *x_pos = x; *y_pos = y; done = 1; @@ -2624,7 +2925,7 @@ Bool wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos) } y = i; ty = dock->y_pos + y * ICON_SIZE; - if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty, sx, ex, sy, ey)) { + if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty)) { *x_pos = x; *y_pos = y; done = 1; @@ -2636,7 +2937,7 @@ Bool wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos) ty = dock->y_pos + y * ICON_SIZE; x = -i; tx = dock->x_pos + x * ICON_SIZE; - if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty, sx, ex, sy, ey)) { + if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty)) { *x_pos = x; *y_pos = y; done = 1; @@ -2644,7 +2945,7 @@ Bool wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos) } x = i; tx = dock->x_pos + x * ICON_SIZE; - if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty, sx, ex, sy, ey)) { + if (slot_map[XY2OFS(x, y)] == 0 && onScreen(scr, tx, ty)) { *x_pos = x; *y_pos = y; done = 1; @@ -2660,8 +2961,14 @@ Bool wDockFindFreeSlot(WDock *dock, int *x_pos, int *y_pos) static void moveDock(WDock *dock, int new_x, int new_y) { WAppIcon *btn; + WDrawerChain *dc; int i; + if (dock->type == WM_DOCK) { + for (dc = dock->screen_ptr->drawers; dc != NULL; dc = dc->next) + moveDock(dc->adrawer, new_x, dc->adrawer->y_pos - dock->y_pos + new_y); + } + dock->x_pos = new_x; dock->y_pos = new_y; for (i = 0; i < dock->max_icons; i++) { @@ -2685,6 +2992,8 @@ static void swapDock(WDock *dock) else x = dock->x_pos = DOCK_EXTRA_SPACE; + swapDrawers(scr, dock->on_right_side, x); + for (i = 0; i < dock->max_icons; i++) { btn = dock->icon_array[i]; if (btn) { @@ -2753,7 +3062,7 @@ static pid_t execCommand(WAppIcon *btn, char *command, WSavedState *state) state->hidden = -1; state->miniaturized = -1; state->shaded = -1; - if (btn->dock == scr->dock || btn->omnipresent) + if (btn->dock == scr->dock || btn->dock->type == WM_DRAWER || btn->omnipresent) state->workspace = -1; else state->workspace = scr->current_workspace; @@ -2785,7 +3094,7 @@ void wDockHideIcons(WDock *dock) void wDockShowIcons(WDock *dock) { - int i, newlevel; + int i; WAppIcon *btn; if (dock == NULL) @@ -2794,15 +3103,8 @@ void wDockShowIcons(WDock *dock) btn = dock->icon_array[0]; moveDock(dock, btn->x_pos, btn->y_pos); - newlevel = dock->lowered ? WMNormalLevel : WMDockLevel; - ChangeStackingLevel(btn->icon->core, newlevel); - - for (i = 1; i < dock->max_icons; i++) { - if (dock->icon_array[i]) { - MoveInStackListAbove(dock->icon_array[i]->icon->core, btn->icon->core); - break; - } - } + /* Deleting any change in stacking level, this function is now only about + mapping icons */ if (!dock->collapsed) { for (i = 1; i < dock->max_icons; i++) { @@ -2818,7 +3120,12 @@ void wDockShowIcons(WDock *dock) void wDockLower(WDock *dock) { int i; + WDrawerChain *dc; + if (dock->type == WM_DOCK) { + for (dc = dock->screen_ptr->drawers; dc != NULL; dc = dc->next) + wDockLower(dc->adrawer); + } for (i = 0; i < dock->max_icons; i++) { if (dock->icon_array[i]) wLowerFrame(dock->icon_array[i]->icon->core); @@ -2828,11 +3135,16 @@ void wDockLower(WDock *dock) void wDockRaise(WDock *dock) { int i; + WDrawerChain *dc; for (i = dock->max_icons - 1; i >= 0; i--) { if (dock->icon_array[i]) wRaiseFrame(dock->icon_array[i]->icon->core); } + if (dock->type == WM_DOCK) { + for (dc = dock->screen_ptr->drawers; dc != NULL; dc = dc->next) + wDockRaise(dc->adrawer); + } } void wDockRaiseLower(WDock *dock) @@ -2968,7 +3280,7 @@ void wClipUpdateForWorkspaceChange(WScreen *scr, int workspace) WAppIconChain *chain = scr->global_icons; while (chain) { - moveIconBetweenDocks(chain->aicon->dock, + wDockMoveIconBetweenDocks(chain->aicon->dock, scr->workspaces[workspace]->clip, chain->aicon, chain->aicon->xindex, chain->aicon->yindex); if (scr->workspaces[workspace]->clip->collapsed) @@ -3036,12 +3348,16 @@ static void trackDeadProcess(pid_t pid, unsigned char status, WDock *dock) } } +/* This function is called when the dock switches state between + * "normal" (including auto-raise/lower) and "keep on top". It is + * therefore clearly distinct from wDockLower/Raise, which are called + * each time a not-kept-on-top dock is lowered/raised. */ static void toggleLowered(WDock *dock) { WAppIcon *tmp; + WDrawerChain *dc; int newlevel, i; - /* lower/raise Dock */ if (!dock->lowered) { newlevel = WMNormalLevel; dock->lowered = 1; @@ -3056,12 +3372,20 @@ static void toggleLowered(WDock *dock) continue; ChangeStackingLevel(tmp->icon->core, newlevel); + + /* When the dock is no longer "on top", explicitly lower it as well. + * It saves some CPU cycles (probably) to do it ourselves here + * rather than calling wDockLower at the end of toggleLowered */ if (dock->lowered) wLowerFrame(tmp->icon->core); } - if (dock->type == WM_DOCK) + if (dock->type == WM_DOCK) { + for (dc = dock->screen_ptr->drawers; dc != NULL; dc = dc->next) { + toggleLowered(dc->adrawer); + } wScreenUpdateUsableArea(dock->screen_ptr); + } } static void toggleCollapsed(WDock *dock) @@ -3087,36 +3411,42 @@ static void openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event) int appIsRunning = aicon->running && aicon->icon && aicon->icon->owner; if (dock->type == WM_DOCK) { - /* keep on top */ - entry = dock->menu->entries[index]; - entry->flags.indicator_on = !dock->lowered; - entry->clientdata = dock; + /* Dock position menu */ + updateDockPositionMenu(scr->dock_pos_menu, dock); dock->menu->flags.realized = 0; + if (!wPreferences.flags.nodrawer) { + /* add a drawer */ + entry = dock->menu->entries[++index]; + entry->clientdata = aicon; + wMenuSetEnabled(dock->menu, index, True); + } } else { - /* clip options */ + /* clip/drawer options */ if (scr->clip_options) updateClipOptionsMenu(scr->clip_options, dock); n_selected = numberOfSelectedIcons(dock); - /* Rename Workspace */ - entry = dock->menu->entries[++index]; - if (aicon == scr->clip_icon) { - entry->callback = renameCallback; - entry->clientdata = dock; - entry->flags.indicator = 0; - entry->text = _("Rename Workspace"); - } else { - entry->callback = omnipresentCallback; - entry->clientdata = aicon; - if (n_selected > 0) { + if (dock->type == WM_CLIP) { + /* Rename Workspace */ + entry = dock->menu->entries[++index]; + if (aicon == scr->clip_icon) { + entry->callback = renameCallback; + entry->clientdata = dock; entry->flags.indicator = 0; - entry->text = _("Toggle Omnipresent"); + entry->text = _("Rename Workspace"); } else { - entry->flags.indicator = 1; - entry->flags.indicator_on = aicon->omnipresent; - entry->flags.indicator_type = MI_CHECK; - entry->text = _("Omnipresent"); + entry->callback = omnipresentCallback; + entry->clientdata = aicon; + if (n_selected > 0) { + entry->flags.indicator = 0; + entry->text = _("Toggle Omnipresent"); + } else { + entry->flags.indicator = 1; + entry->flags.indicator_on = aicon->omnipresent; + entry->flags.indicator_type = MI_CHECK; + entry->text = _("Omnipresent"); + } } } @@ -3124,7 +3454,7 @@ static void openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event) entry = dock->menu->entries[++index]; entry->clientdata = aicon; entry->flags.indicator_on = aicon->icon->selected; - wMenuSetEnabled(dock->menu, index, aicon != scr->clip_icon); + wMenuSetEnabled(dock->menu, index, aicon != scr->clip_icon && !wIsADrawer(scr, aicon)); /* select/unselect all icons */ entry = dock->menu->entries[++index]; @@ -3146,17 +3476,19 @@ static void openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event) wMenuSetEnabled(dock->menu, index, dock->icon_count > 1); - /* this is the workspace submenu part */ - entry = dock->menu->entries[++index]; - if (n_selected > 1) - entry->text = _("Move Icons To"); - else - entry->text = _("Move Icon To"); + if (dock->type == WM_CLIP) { + /* this is the workspace submenu part */ + entry = dock->menu->entries[++index]; + if (n_selected > 1) + entry->text = _("Move Icons To"); + else + entry->text = _("Move Icon To"); - if (scr->clip_submenu) - updateWorkspaceMenu(scr->clip_submenu, aicon); + if (scr->clip_submenu) + updateWorkspaceMenu(scr->clip_submenu, aicon); - wMenuSetEnabled(dock->menu, index, !aicon->omnipresent); + wMenuSetEnabled(dock->menu, index, !aicon->omnipresent); + } /* remove icon(s) */ entry = dock->menu->entries[++index]; @@ -3211,15 +3543,23 @@ static void openDockMenu(WDock *dock, WAppIcon *aicon, XEvent *event) entry->clientdata = aicon; wMenuSetEnabled(dock->menu, index, !aicon->editing && !wPreferences.flags.noupdates); - /* kill */ + /* kill or remove drawer */ entry = dock->menu->entries[++index]; entry->clientdata = aicon; - wMenuSetEnabled(dock->menu, index, appIsRunning); + if (wIsADrawer(scr, aicon)) { + entry->callback = removeDrawerCallback; + entry->text = _("Remove drawer"); + wMenuSetEnabled(dock->menu, index, True); + } else { + entry->callback = killCallback; + entry->text = _("Kill"); + wMenuSetEnabled(dock->menu, index, appIsRunning); + } if (!dock->menu->flags.realized) wMenuRealize(dock->menu); - if (dock->type == WM_CLIP) { + if (dock->type == WM_CLIP || dock->type == WM_DRAWER) { /*x_pos = event->xbutton.x_root+2; */ x_pos = event->xbutton.x_root - dock->menu->frame->core->width / 2 - 1; if (x_pos < 0) { @@ -3268,13 +3608,29 @@ static void iconDblClick(WObjDescriptor *desc, XEvent *event) /* raise/lower dock */ toggleLowered(dock); } else if (btn == dock->screen_ptr->clip_icon) { - if (getClipButton(event->xbutton.x, event->xbutton.y) == CLIP_IDLE) - toggleCollapsed(dock); - else + if (getClipButton(event->xbutton.x, event->xbutton.y) != CLIP_IDLE) handleClipChangeWorkspace(dock->screen_ptr, event); + else if (wPreferences.flags.clip_merged_in_dock) { + // Is actually the dock + if (btn->command) + { + if (!btn->launching && (!btn->running || (event->xbutton.state & ControlMask))) + launchDockedApplication(btn, False); + } + else + { + wShowInfoPanel(dock->screen_ptr); + } + } + else + toggleCollapsed(dock); + } else if (wIsADrawer(dock->screen_ptr, btn)) { + toggleCollapsed(dock); } else if (btn->command) { if (!btn->launching && (!btn->running || (event->xbutton.state & ControlMask))) launchDockedApplication(btn, False); + } else if (btn->xindex == 0 && btn->yindex == 0 && btn->dock->type == WM_DOCK) { + wShowInfoPanel(dock->screen_ptr); } } } @@ -3284,9 +3640,13 @@ static void handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event) { WScreen *scr = dock->screen_ptr; int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y; - int x, y; + WIcon *icon = aicon->icon; + WAppIcon *tmpaicon; + WDrawerChain *dc; + int x = aicon->x_pos, y = aicon->y_pos;; + int shad_x = x, shad_y = y; XEvent ev; - int grabbed = 0, swapped = 0, done; + int grabbed = 0, done, previously_on_right, now_on_right, previous_x_pos, i; Pixmap ghost = None; int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */ @@ -3295,14 +3655,27 @@ static void handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event) GrabModeAsync, None, None, CurrentTime) != GrabSuccess) wwarning("pointer grab failed for dock move"); - y = 0; - for (x = 0; x < dock->max_icons; x++) { - if (dock->icon_array[x] != NULL && dock->icon_array[x]->yindex > y) - y = dock->icon_array[x]->yindex; - } - y++; - XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE * y); + if (dock->type == WM_DRAWER) { + Window wins[2]; + wins[0] = icon->core->window; + wins[1] = scr->dock_shadow; + XRestackWindows(dpy, wins, 2); + XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos, + ICON_SIZE, ICON_SIZE); + if (superfluous) { + if (icon->pixmap!=None) + ghost = MakeGhostIcon(scr, icon->pixmap); + else + ghost = MakeGhostIcon(scr, icon->core->window); + XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost); + XClearWindow(dpy, scr->dock_shadow); + } + XMapWindow(dpy, scr->dock_shadow); + } + + previously_on_right = now_on_right = dock->on_right_side; + previous_x_pos = dock->x_pos; done = 0; while (!done) { WMMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask @@ -3330,68 +3703,101 @@ static void handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event) } break; } - if (dock->type == WM_CLIP) { + switch (dock->type) { + case WM_CLIP: x = ev.xmotion.x_root - ofs_x; y = ev.xmotion.y_root - ofs_y; wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE); - moveDock(dock, x, y); - } else { - /* move vertically if pointer is inside the dock */ - if ((dock->on_right_side && ev.xmotion.x_root >= dock->x_pos - ICON_SIZE) - || (!dock->on_right_side && ev.xmotion.x_root <= dock->x_pos + ICON_SIZE * 2)) { - - x = ev.xmotion.x_root - ofs_x; - y = ev.xmotion.y_root - ofs_y; - wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE); - moveDock(dock, dock->x_pos, y); - } - /* move horizontally to change sides */ + break; + case WM_DOCK: x = ev.xmotion.x_root - ofs_x; - if (!dock->on_right_side) { - - /* is on left */ - if (ev.xmotion.x_root > dock->x_pos + ICON_SIZE * 2) { - XMoveWindow(dpy, scr->dock_shadow, scr->scr_width - ICON_SIZE - - DOCK_EXTRA_SPACE - 1, dock->y_pos); - if (superfluous && ghost == None) { - ghost = MakeGhostDock(dock, dock->x_pos, - scr->scr_width - ICON_SIZE - - DOCK_EXTRA_SPACE - 1, dock->y_pos); - XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost); - XClearWindow(dpy, scr->dock_shadow); + y = ev.xmotion.y_root - ofs_y; + if (previously_on_right) + { + now_on_right = (ev.xmotion.x_root >= previous_x_pos - ICON_SIZE); + } + else + { + now_on_right = (ev.xmotion.x_root > previous_x_pos + ICON_SIZE * 2); + } + if (now_on_right != dock->on_right_side) + { + dock->on_right_side = now_on_right; + swapDock(dock); + wArrangeIcons(scr, False); + } + // Also perform the vertical move + wScreenKeepInside(scr, &x, &y, ICON_SIZE, ICON_SIZE); + moveDock(dock, dock->x_pos, y); + if (wPreferences.flags.wrap_appicons_in_dock) + { + for (i = 0; i < dock->max_icons; i++) { + int new_y, new_index, j, ok; + if ((tmpaicon = dock->icon_array[i]) == NULL) + continue; + if (onScreen(scr, tmpaicon->x_pos, tmpaicon->y_pos)) + continue; + new_y = (tmpaicon->y_pos + ICON_SIZE * dock->max_icons) % (ICON_SIZE * dock->max_icons); + new_index = (new_y - dock->y_pos) / ICON_SIZE; + if (!onScreen(scr, tmpaicon->x_pos, new_y)) + continue; + ok = 1; + for (j = 0; j < dock->max_icons; j++) + { + if (dock->icon_array[j] != NULL && + dock->icon_array[j]->yindex == new_index) + { + ok = 0; + break; + } } - XMapRaised(dpy, scr->dock_shadow); - swapped = 1; - } else { - if (superfluous && ghost != None) { - XFreePixmap(dpy, ghost); - ghost = None; - } - XUnmapWindow(dpy, scr->dock_shadow); - swapped = 0; + if (!ok || getDrawer(scr, new_index) != NULL) + continue; + wDockReattachIcon(dock, tmpaicon, tmpaicon->xindex, new_index); } - } else { - /* is on right */ - if (ev.xmotion.x_root < dock->x_pos - ICON_SIZE) { - XMoveWindow(dpy, scr->dock_shadow, DOCK_EXTRA_SPACE, dock->y_pos); - if (superfluous && ghost == None) { - ghost = MakeGhostDock(dock, dock->x_pos, - DOCK_EXTRA_SPACE, dock->y_pos); - XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost); - XClearWindow(dpy, scr->dock_shadow); - } - XMapRaised(dpy, scr->dock_shadow); - swapped = -1; - } else { - XUnmapWindow(dpy, scr->dock_shadow); - swapped = 0; - if (superfluous && ghost != None) { - XFreePixmap(dpy, ghost); - ghost = None; + for (dc = scr->drawers; dc != NULL; dc = dc->next) + { + int new_y, new_index, j, ok; + tmpaicon = dc->adrawer->icon_array[0]; + if (onScreen(scr, tmpaicon->x_pos, tmpaicon->y_pos)) + continue; + new_y = (tmpaicon->y_pos + ICON_SIZE * dock->max_icons) % (ICON_SIZE * dock->max_icons); + new_index = (new_y - dock->y_pos) / ICON_SIZE; + if (!onScreen(scr, tmpaicon->x_pos, new_y)) + continue; + ok = 1; + for (j = 0; j < dock->max_icons; j++) + { + if (dock->icon_array[j] != NULL && + dock->icon_array[j]->yindex == new_index) + { + ok = 0; + break; + } } + if (!ok || getDrawer(scr, new_index) != NULL) + continue; + moveDock(dc->adrawer, tmpaicon->x_pos, new_y); } } + break; + case WM_DRAWER: + { + WDock *real_dock = dock->screen_ptr->dock; + Bool snapped; + int ix, iy; + x = ev.xmotion.x_root - ofs_x; + y = ev.xmotion.y_root - ofs_y; + snapped = wDockSnapIcon(real_dock, aicon, x, y, &ix, &iy, True); + if (snapped) { + shad_x = real_dock->x_pos + ix * wPreferences.icon_size; + shad_y = real_dock->y_pos + iy * wPreferences.icon_size; + XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y); + } + moveDock(dock, x, y); + break; + } } break; @@ -3402,17 +3808,22 @@ static void handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event) if (ev.xbutton.button != event->xbutton.button) break; XUngrabPointer(dpy, CurrentTime); - XUnmapWindow(dpy, scr->dock_shadow); - XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE); - if (dock->type == WM_DOCK) { - if (swapped != 0) { - if (swapped > 0) - dock->on_right_side = 1; - else - dock->on_right_side = 0; - swapDock(dock); - wArrangeIcons(scr, False); + if (dock->type == WM_DRAWER) { + Window *wins[dock->icon_count]; + + for (i = 0; i < dock->max_icons; i++) { + if ((tmpaicon = dock->icon_array[i]) == NULL) + continue; + wins[ tmpaicon->xindex + (dock->on_right_side ? dock->icon_count - 1 : 0) ] = &tmpaicon->icon->core->window; } + SlideWindows(wins, dock->icon_count, + (dock->on_right_side ? x - (dock->icon_count - 1) * ICON_SIZE : x), + y, + (dock->on_right_side ? shad_x - (dock->icon_count - 1) * ICON_SIZE : shad_x), + shad_y); + XUnmapWindow(dpy, scr->dock_shadow); + moveDock(dock, shad_x, shad_y); + XResizeWindow(dpy, scr->dock_shadow, ICON_SIZE, ICON_SIZE); } done = 1; break; @@ -3425,212 +3836,6 @@ static void handleDockMove(WDock *dock, WAppIcon *aicon, XEvent *event) } } -static Bool handleIconMove(WDock *dock, WAppIcon *aicon, XEvent *event) -{ - WScreen *scr = dock->screen_ptr; - Window wins[2]; - WIcon *icon = aicon->icon; - WDock *dock2 = NULL, *last_dock = dock, *clip = NULL; - int ondock, grabbed = 0, change_dock = 0, collapsed = 0; - XEvent ev; - int x = aicon->x_pos, y = aicon->y_pos; - int ofs_x = event->xbutton.x, ofs_y = event->xbutton.y; - int shad_x = x, shad_y = y; - int ix = aicon->xindex, iy = aicon->yindex; - int tmp; - Pixmap ghost = None; - Bool docked; - int superfluous = wPreferences.superfluous; /* we catch it to avoid problems */ - int omnipresent = aicon->omnipresent; /* this must be cached!!! */ - Bool hasMoved = False; - - if (wPreferences.flags.noupdates) - return hasMoved; - - if (XGrabPointer(dpy, icon->core->window, True, ButtonMotionMask - | ButtonReleaseMask | ButtonPressMask, GrabModeAsync, - GrabModeAsync, None, None, CurrentTime) != GrabSuccess) { - } - - if (!(event->xbutton.state & MOD_MASK)) - wRaiseFrame(icon->core); - - if (!wPreferences.flags.noclip) - clip = scr->workspaces[scr->current_workspace]->clip; - - if (dock == scr->dock && !wPreferences.flags.noclip) - dock2 = clip; - else if (dock != scr->dock && !wPreferences.flags.nodock) - dock2 = scr->dock; - - wins[0] = icon->core->window; - wins[1] = scr->dock_shadow; - XRestackWindows(dpy, wins, 2); - XMoveResizeWindow(dpy, scr->dock_shadow, aicon->x_pos, aicon->y_pos, ICON_SIZE, ICON_SIZE); - if (superfluous) { - if (icon->pixmap != None) - ghost = MakeGhostIcon(scr, icon->pixmap); - else - ghost = MakeGhostIcon(scr, icon->core->window); - - XSetWindowBackgroundPixmap(dpy, scr->dock_shadow, ghost); - XClearWindow(dpy, scr->dock_shadow); - } - XMapWindow(dpy, scr->dock_shadow); - - ondock = 1; - - while (1) { - XMaskEvent(dpy, PointerMotionMask | ButtonReleaseMask | ButtonPressMask - | ButtonMotionMask | ExposureMask, &ev); - switch (ev.type) { - case Expose: - WMHandleEvent(&ev); - break; - - case MotionNotify: - hasMoved = True; - if (!grabbed) { - if (abs(ofs_x - ev.xmotion.x) >= MOVE_THRESHOLD - || abs(ofs_y - ev.xmotion.y) >= MOVE_THRESHOLD) { - XChangeActivePointerGrab(dpy, ButtonMotionMask - | ButtonReleaseMask | ButtonPressMask, - wCursor[WCUR_MOVE], CurrentTime); - grabbed = 1; - } else { - break; - } - } - - if (omnipresent) { - int i; - for (i = 0; i < scr->workspace_count; i++) { - if (i == scr->current_workspace) - continue; - wDockShowIcons(scr->workspaces[i]->clip); - } - } - - x = ev.xmotion.x_root - ofs_x; - y = ev.xmotion.y_root - ofs_y; - tmp = wDockSnapIcon(dock, aicon, x, y, &ix, &iy, True); - if (tmp && dock2) { - change_dock = 0; - if (last_dock != dock && collapsed) { - last_dock->collapsed = 1; - wDockHideIcons(last_dock); - collapsed = 0; - } - if (!collapsed && (collapsed = dock->collapsed)) { - dock->collapsed = 0; - wDockShowIcons(dock); - } - if (dock->auto_raise_lower) - wDockRaise(dock); - last_dock = dock; - } else if (dock2) { - tmp = wDockSnapIcon(dock2, aicon, x, y, &ix, &iy, False); - if (tmp) { - change_dock = 1; - if (last_dock != dock2 && collapsed) { - last_dock->collapsed = 1; - wDockHideIcons(last_dock); - collapsed = 0; - } - if (!collapsed && (collapsed = dock2->collapsed)) { - dock2->collapsed = 0; - wDockShowIcons(dock2); - } - if (dock2->auto_raise_lower) - wDockRaise(dock2); - last_dock = dock2; - } - } - if (aicon->launching || aicon->lock || (aicon->running && !(ev.xmotion.state & MOD_MASK)) - || (!aicon->running && tmp)) { - shad_x = last_dock->x_pos + ix * wPreferences.icon_size; - shad_y = last_dock->y_pos + iy * wPreferences.icon_size; - - XMoveWindow(dpy, scr->dock_shadow, shad_x, shad_y); - - if (!ondock) - XMapWindow(dpy, scr->dock_shadow); - - ondock = 1; - } else { - if (ondock) - XUnmapWindow(dpy, scr->dock_shadow); - - ondock = 0; - } - XMoveWindow(dpy, icon->core->window, x, y); - break; - - case ButtonPress: - break; - - case ButtonRelease: - if (ev.xbutton.button != event->xbutton.button) - break; - XUngrabPointer(dpy, CurrentTime); - if (ondock) { - SlideWindow(icon->core->window, x, y, shad_x, shad_y); - XUnmapWindow(dpy, scr->dock_shadow); - if (!change_dock) { - reattachIcon(dock, aicon, ix, iy); - if (clip && dock != clip && clip->auto_raise_lower) - wDockLower(clip); - } else { - docked = moveIconBetweenDocks(dock, dock2, aicon, ix, iy); - if (!docked) { - /* Slide it back if dock rejected it */ - SlideWindow(icon->core->window, x, y, aicon->x_pos, aicon->y_pos); - reattachIcon(dock, aicon, aicon->xindex, aicon->yindex); - } - if (last_dock->type == WM_CLIP && last_dock->auto_collapse) - collapsed = 0; - } - } else { - aicon->x_pos = x; - aicon->y_pos = y; - if (superfluous) { - if (!aicon->running && !wPreferences.no_animations) { - /* We need to deselect it, even if is deselected in - * wDockDetach(), because else DoKaboom() will fail. - */ - if (aicon->icon->selected) - wIconSelect(aicon->icon); - - DoKaboom(scr, aicon->icon->core->window, x, y); - } - } - if (clip && clip->auto_raise_lower) - wDockLower(clip); - wDockDetach(dock, aicon); - } - if (collapsed) { - last_dock->collapsed = 1; - wDockHideIcons(last_dock); - collapsed = 0; - } - if (superfluous) { - if (ghost != None) - XFreePixmap(dpy, ghost); - XSetWindowBackground(dpy, scr->dock_shadow, scr->white_pixel); - } - if (omnipresent) { - int i; - for (i = 0; i < scr->workspace_count; i++) { - if (i == scr->current_workspace) - continue; - wDockHideIcons(scr->workspaces[i]->clip); - } - } - return hasMoved;; - } - } - return False; /* never reached */ -} static int getClipButton(int px, int py) { @@ -3744,17 +3949,17 @@ static void iconMouseDown(WObjDescriptor *desc, XEvent *event) } if (aicon->yindex == 0 && aicon->xindex == 0) { - if (getClipButton(event->xbutton.x, event->xbutton.y) != CLIP_IDLE - && dock->type == WM_CLIP) + if (getClipButton(event->xbutton.x, event->xbutton.y) != CLIP_IDLE && + (dock->type == WM_CLIP || (dock->type == WM_DOCK && wPreferences.flags.clip_merged_in_dock))) handleClipChangeWorkspace(scr, event); else handleDockMove(dock, aicon, event); } else { - Bool hasMoved = handleIconMove(dock, aicon, event); + Bool hasMoved = wHandleAppIconMove(aicon, event); if (wPreferences.single_click && !hasMoved) iconDblClick(desc, event); } - } else if (event->xbutton.button == Button2 && dock->type == WM_CLIP && aicon == scr->clip_icon) { + } else if (event->xbutton.button == Button2 && aicon == scr->clip_icon) { if (!scr->clip_ws_menu) { scr->clip_ws_menu = wWorkspaceMenuMake(scr, False); } @@ -3800,7 +4005,7 @@ static void iconMouseDown(WObjDescriptor *desc, XEvent *event) static void clipEnterNotify(WObjDescriptor *desc, XEvent *event) { WAppIcon *btn = (WAppIcon *) desc->parent; - WDock *dock; + WDock *dock, *tmp; WScreen *scr; assert(event->type == EnterNotify); @@ -3809,21 +4014,22 @@ static void clipEnterNotify(WObjDescriptor *desc, XEvent *event) return; scr = btn->icon->core->screen_ptr; - if (!btn->omnipresent) - dock = btn->dock; - else - dock = scr->workspaces[scr->current_workspace]->clip; + dock = btn->dock; - if (!dock || dock->type != WM_CLIP) + if (dock == NULL) return; /* The auto raise/lower code */ - if (dock->auto_lower_magic) { - WMDeleteTimerHandler(dock->auto_lower_magic); - dock->auto_lower_magic = NULL; + tmp = (dock->type == WM_DRAWER ? scr->dock : dock); + if (tmp->auto_lower_magic) { + WMDeleteTimerHandler(tmp->auto_lower_magic); + tmp->auto_lower_magic = NULL; } - if (dock->auto_raise_lower && !dock->auto_raise_magic) - dock->auto_raise_magic = WMAddTimerHandler(AUTO_RAISE_DELAY, clipAutoRaise, (void *)dock); + if (tmp->auto_raise_lower && !tmp->auto_raise_magic) + tmp->auto_raise_magic = WMAddTimerHandler(wPreferences.clip_auto_raise_delay, clipAutoRaise, (void *) tmp); + + if (dock->type != WM_CLIP && dock->type != WM_DRAWER) + return; /* The auto expand/collapse code */ if (dock->auto_collapse_magic) { @@ -3831,23 +4037,24 @@ static void clipEnterNotify(WObjDescriptor *desc, XEvent *event) dock->auto_collapse_magic = NULL; } if (dock->auto_collapse && !dock->auto_expand_magic) - dock->auto_expand_magic = WMAddTimerHandler(AUTO_EXPAND_DELAY, clipAutoExpand, (void *)dock); + dock->auto_expand_magic = WMAddTimerHandler(wPreferences.clip_auto_expand_delay, clipAutoExpand, (void *)dock); } static void clipLeave(WDock *dock) { XEvent event; WObjDescriptor *desc = NULL; + WDock *tmp; - if (!dock || dock->type != WM_CLIP) + if (dock == NULL) return; if (XCheckTypedEvent(dpy, EnterNotify, &event) != False) { if (XFindContext(dpy, event.xcrossing.window, wWinContext, (XPointer *) & desc) != XCNOENT && desc && desc->parent_type == WCLASS_DOCK_ICON - && ((WAppIcon *) desc->parent)->dock && ((WAppIcon *) desc->parent)->dock->type == WM_CLIP) { - /* We didn't left the Clip yet */ + && ((WAppIcon *) desc->parent)->dock == dock) { + /* We haven't left the dock/clip/drawer yet */ XPutBackEvent(dpy, &event); return; } @@ -3858,19 +4065,23 @@ static void clipLeave(WDock *dock) return; } - if (dock->auto_raise_magic) { - WMDeleteTimerHandler(dock->auto_raise_magic); - dock->auto_raise_magic = NULL; + tmp = (dock->type == WM_DRAWER ? dock->screen_ptr->dock : dock); + if (tmp->auto_raise_magic) { + WMDeleteTimerHandler(tmp->auto_raise_magic); + tmp->auto_raise_magic = NULL; } - if (dock->auto_raise_lower && !dock->auto_lower_magic) - dock->auto_lower_magic = WMAddTimerHandler(AUTO_LOWER_DELAY, clipAutoLower, (void *)dock); + if (tmp->auto_raise_lower && !tmp->auto_lower_magic) + tmp->auto_lower_magic = WMAddTimerHandler(wPreferences.clip_auto_lower_delay, clipAutoLower, (void *)tmp); + + if (dock->type != WM_CLIP && dock->type != WM_DRAWER) + return; if (dock->auto_expand_magic) { WMDeleteTimerHandler(dock->auto_expand_magic); dock->auto_expand_magic = NULL; } if (dock->auto_collapse && !dock->auto_collapse_magic) - dock->auto_collapse_magic = WMAddTimerHandler(AUTO_COLLAPSE_DELAY, clipAutoCollapse, (void *)dock); + dock->auto_collapse_magic = WMAddTimerHandler(wPreferences.clip_auto_collapse_delay, clipAutoCollapse, (void *)dock); } static void clipLeaveNotify(WObjDescriptor *desc, XEvent *event) @@ -3889,7 +4100,7 @@ static void clipAutoCollapse(void *cdata) { WDock *dock = (WDock *) cdata; - if (dock->type != WM_CLIP) + if (dock->type != WM_CLIP && dock->type != WM_DRAWER) return; if (dock->auto_collapse) { @@ -3903,7 +4114,7 @@ static void clipAutoExpand(void *cdata) { WDock *dock = (WDock *) cdata; - if (dock->type != WM_CLIP) + if (dock->type != WM_CLIP && dock->type != WM_DRAWER) return; if (dock->auto_collapse) { @@ -3917,9 +4128,6 @@ static void clipAutoLower(void *cdata) { WDock *dock = (WDock *) cdata; - if (dock->type != WM_CLIP) - return; - if (dock->auto_raise_lower) wDockLower(dock); @@ -3930,9 +4138,6 @@ static void clipAutoRaise(void *cdata) { WDock *dock = (WDock *) cdata; - if (dock->type != WM_CLIP) - return; - if (dock->auto_raise_lower) wDockRaise(dock); @@ -4015,3 +4220,706 @@ int wClipMakeIconOmnipresent(WAppIcon *aicon, int omnipresent) return status; } + +static void drawerAppendToChain(WScreen *scr, WDock *drawer) +{ + WDrawerChain **where_to_add; + + where_to_add = &scr->drawers; + while ((*where_to_add) != NULL) { + where_to_add = &(*where_to_add)->next; + } + + *where_to_add = wmalloc(sizeof(WDrawerChain)); + (*where_to_add)->adrawer = drawer; + (*where_to_add)->next = NULL; + scr->drawer_count++; +} + + +static void drawerRemoveFromChain(WScreen *scr, WDock *drawer) +{ + WDrawerChain *next, **to_remove; + + to_remove = &scr->drawers; + while (True) { + if (*to_remove == NULL) { + wwarning("The drawer to be removed can not be found."); + return; + } + if ((*to_remove)->adrawer == drawer) + break; + + to_remove = &(*to_remove)->next; + } + next = (*to_remove)->next; + wfree(*to_remove); + *to_remove = next; + scr->drawer_count--; +} + + +/* Don't free the returned string. Duplicate it. */ +static char * findUniqueName(WScreen *scr, char *instance_basename) +{ + static char buffer[128]; + WDrawerChain *dc; + int i; + Bool already_in_use = True; + +#define UNIQUE_NAME_WATCHDOG 128 + for (i = 0; already_in_use && i < UNIQUE_NAME_WATCHDOG; i++) { + snprintf(buffer, sizeof buffer, "%s%d", instance_basename, i); + + already_in_use = False; + + for (dc = scr->drawers; dc != NULL; dc = dc->next) { + if (!strncmp(dc->adrawer->icon_array[0]->wm_instance, buffer, + sizeof buffer)) { + already_in_use = True; + break; + } + } + } + + if (i == UNIQUE_NAME_WATCHDOG) + wwarning("Couldn't find a unique name for drawer in %d attemps.", i); +#undef UNIQUE_NAME_WATCHDOG + + return buffer; +} + + +static void drawerIconExpose(WObjDescriptor *desc, XEvent *event) +{ + wDrawerIconPaint((WAppIcon *) desc->parent); +} + + +static int addADrawer(WScreen *scr) +{ + int i, y, sig, found_y; + WDock *drawer, *dock = scr->dock; + WDrawerChain *dc; + char can_be_here[2 * dock->max_icons - 1]; + + if (dock->icon_count + scr->drawer_count >= dock->max_icons) + return -1; + + for (y = -dock->max_icons + 1; y < dock->max_icons; y++) { + can_be_here[y + dock->max_icons - 1] = True; + } + for (i = 0; i < dock->max_icons; i++) { + if (dock->icon_array[i] != NULL) + can_be_here[dock->icon_array[i]->yindex + dock->max_icons - 1] = False; + } + for (dc = scr->drawers; dc != NULL; dc = dc->next) { + y = (int) ((dc->adrawer->y_pos - dock->y_pos) / ICON_SIZE); + can_be_here[y + dock->max_icons - 1] = False; + } + + found_y = False; + for (sig = 1; !found_y && sig > -2; sig -= 2) // 1, then -1 + { + for (y = sig; sig * y < dock->max_icons; y += sig) + { + if (can_be_here[y + dock->max_icons - 1] && + onScreen(scr, dock->x_pos, dock->y_pos + y * ICON_SIZE)) + { + found_y = True; + break; + } + } + } + + if (!found_y) + /* This can happen even when dock->icon_count + scr->drawer_count + * < dock->max_icons when the dock is not aligned on an + * ICON_SIZE multiple, as some space is lost above and under it */ + return -1; + + drawer = wDockCreate(scr, WM_DRAWER, NULL); + drawer->lowered = scr->dock->lowered; + drawer->auto_raise_lower = scr->dock->auto_raise_lower; + drawer->x_pos = dock->x_pos; + drawer->y_pos = dock->y_pos + ICON_SIZE * y; + drawer->icon_array[0]->xindex = 0; + drawer->icon_array[0]->yindex = 0; + drawer->icon_array[0]->x_pos = drawer->x_pos; + drawer->icon_array[0]->y_pos = drawer->y_pos; + XMoveWindow(dpy, drawer->icon_array[0]->icon->core->window, + drawer->icon_array[0]->x_pos, drawer->icon_array[0]->y_pos); + + return 0; +} + + +static void addADrawerCallback(WMenu *menu, WMenuEntry *entry) +{ + assert(entry->clientdata!=NULL); + addADrawer(((WAppIcon *) entry->clientdata)->dock->screen_ptr); +} + + +static void drawerDestroy(WDock *drawer) +{ + WScreen *scr; + int i; + WAppIcon *aicon; + WMArray *icons; + + if (drawer == NULL) + return; + + scr = drawer->screen_ptr; + + /* Note regarding menus: we can't delete any dock/clip/drawer menu, because + * that would (attempt to) wfree some memory in gettext library (see menu + * entries that have several "versions", such like "Hide" and "Unhide"). */ + + wDefaultPurgeInfo(scr, drawer->icon_array[0]->wm_instance, + drawer->icon_array[0]->wm_class); + icons = WMCreateArray(drawer->icon_count - 1); + for (i = 1; i < drawer->max_icons; i++) { + if (!(aicon = drawer->icon_array[i])) + continue; + WMAddToArray(icons, aicon); + } + removeIcons(icons, drawer); + + if (drawer->auto_collapse_magic) { + WMDeleteTimerHandler(drawer->auto_collapse_magic); + drawer->auto_collapse_magic = NULL; + } + if (drawer->auto_lower_magic) { + WMDeleteTimerHandler(drawer->auto_lower_magic); + drawer->auto_lower_magic = NULL; + } + + wAppIconDestroy(drawer->icon_array[0]); + wfree(drawer->icon_array); + wfree(drawer); + + drawerRemoveFromChain(scr, drawer); + if (scr->last_dock == drawer) + scr->last_dock = NULL; + if (scr->attracting_drawer == drawer) + scr->attracting_drawer = NULL; +} + + +static void removeDrawerCallback(WMenu *menu, WMenuEntry *entry) +{ + WDock *dock = ((WAppIcon*)entry->clientdata)->dock; + assert(dock != NULL); + + if (dock->icon_count > 1) { + if (wMessageDialog(dock->screen_ptr, _("Drawer"), + _("All icons in this drawer will be detached!"), + _("OK"), _("Cancel"), NULL) != WAPRDefault) + return; + } + drawerDestroy(dock); +} + + +void wDrawerIconPaint(WAppIcon *dicon) +{ + Window win = dicon->icon->core->window; + WScreen *scr = dicon->icon->core->screen_ptr; + XPoint p[4]; + GC gc = scr->draw_gc; + WMColor *color; + + wIconPaint(dicon->icon); + + if (!dicon->dock->collapsed) + color = scr->clip_title_color[CLIP_NORMAL]; + else + color = scr->clip_title_color[CLIP_COLLAPSED]; + XSetForeground(dpy, gc, WMColorPixel(color)); + + if (dicon->dock->on_right_side) { + p[0].x = p[3].x = 10; + p[0].y = p[3].y = ICON_SIZE / 2 - 5; + p[1].x = 10; + p[1].y = ICON_SIZE / 2 + 5; + p[2].x = 5; + p[2].y = ICON_SIZE / 2; + } + else { + p[0].x = p[3].x = ICON_SIZE-1 - 10; + p[0].y = p[3].y = ICON_SIZE / 2 - 5; + p[1].x = ICON_SIZE-1 - 10; + p[1].y = ICON_SIZE / 2 + 5; + p[2].x = ICON_SIZE-1 - 5; + p[2].y = ICON_SIZE / 2; + } + XFillPolygon(dpy, win, gc, p,3,Convex,CoordModeOrigin); + XDrawLines(dpy, win, gc, p,4,CoordModeOrigin); +} + + +RImage* wDrawerMakeTile(WScreen *scr, RImage *normalTile) +{ + RImage *tile = RCloneImage(normalTile); + RColor dark; + RColor light; + + dark.alpha = 0; + dark.red = dark.green = dark.blue = 60; + + light.alpha = 0; + light.red = light.green = light.blue = 80; + + /* arrow bevel */ + if (!scr->dock || scr->dock->on_right_side) { + ROperateLine(tile, RSubtractOperation, 11, ICON_SIZE / 2 - 7, + 4, ICON_SIZE / 2, &dark); /* / */ + ROperateLine(tile, RSubtractOperation, 11, ICON_SIZE / 2 + 7, + 4, ICON_SIZE / 2, &dark); /* \ */ + ROperateLine(tile, RAddOperation, 11, ICON_SIZE / 2 - 7, + 11, ICON_SIZE / 2 + 7, &light); /* | */ + } + else { + ROperateLine(tile, RSubtractOperation, ICON_SIZE-1 - 11, ICON_SIZE / 2 - 7, + ICON_SIZE-1 - 4, ICON_SIZE / 2, &dark); /* \ */ + ROperateLine(tile, RAddOperation, ICON_SIZE-1 - 11, ICON_SIZE / 2 + 7, + ICON_SIZE-1 - 4, ICON_SIZE / 2, &light); /* / */ + ROperateLine(tile, RSubtractOperation, ICON_SIZE-1 - 11, ICON_SIZE / 2 - 7, + ICON_SIZE-1 - 11, ICON_SIZE / 2 + 7, &dark); /* | */ + } + return tile; +} + + +static void swapDrawer(WDock *drawer, int new_x) +{ + int i; + + drawer->on_right_side = !drawer->on_right_side; + drawer->x_pos = new_x; + + for (i = 0; i < drawer->max_icons; i++) { + WAppIcon *ai; + if ((ai = drawer->icon_array[i]) == NULL) + continue; + ai->xindex *= -1; /* so A B C becomes C B A */ + ai->x_pos = new_x + ai->xindex * ICON_SIZE; + + /* Update drawer's tile */ + if (i == 0) { + wIconUpdate(ai->icon, NULL); + wDrawerIconPaint(ai); + } + XMoveWindow(dpy, ai->icon->core->window, ai->x_pos, ai->y_pos); + } +} + + +static void swapDrawers(WScreen *scr, int on_right_side, int new_x) +{ + WDrawerChain *dc; + + if (scr->drawer_tile) { + RReleaseImage(scr->drawer_tile); + } + scr->drawer_tile = wDrawerMakeTile(scr, scr->icon_tile); + + for (dc = scr->drawers; dc != NULL; dc = dc->next) { + swapDrawer(dc->adrawer, new_x); + assert(dc->adrawer->on_right_side == on_right_side); + } +} + + +int wIsADrawer(WScreen *scr, WAppIcon *aicon) +{ + return aicon && aicon->dock && + aicon->dock->type == WM_DRAWER && aicon->dock->icon_array[0] == aicon; +} + + +static WDock* getDrawer(WScreen *scr, int y_index) +{ + WDrawerChain *dc; + + for (dc = scr->drawers; dc != NULL; dc = dc->next) { + if (dc->adrawer->y_pos - scr->dock->y_pos == y_index * ICON_SIZE) + return dc->adrawer; + } + return NULL; +} + + + +/* Find the "hole" a moving appicon created when snapped into the + * drawer. redocking is a boolean. If the moving appicon comes from the + * drawer, drawer->icon_count is correct. If not, redocking is then false and + * there are now drawer->icon_count plus one appicons in the drawer. */ +static int indexOfHole(WDock *drawer, WAppIcon *moving_aicon, int redocking) +{ + int index_of_hole, i; + + /* Classic interview question... + * + * We have n-1 (n = drawer->icon_count-1 or drawer->icon_count, see + * redocking) appicons, whose xindex are unique in [1..n]. One is missing: + * that's where the ghost of the moving appicon is, that's what the + * function should return. + * + * We compute 1+2+...+n (this sum is equal to n*(n+1)/2), we substract to + * this sum the xindex of each of the n-1 appicons, and we get the correct + * index! */ + + if (redocking) { + index_of_hole = (drawer->icon_count - 1) * drawer->icon_count / 2; + } else { + index_of_hole = drawer->icon_count * (drawer->icon_count + 1) / 2; + } + index_of_hole *= (drawer->on_right_side ? -1 : 1); + + for (i = 1; i < drawer->max_icons; i++) { + if (drawer->icon_array[i] && drawer->icon_array[i] != moving_aicon) + index_of_hole -= drawer->icon_array[i]->xindex; + } + /* wmessage(" Index of the moving appicon is %d (%sredocking)", index_of_hole, (redocking ? "" : "not ")); */ + if (abs(index_of_hole) > abs(drawer->icon_count) - (redocking ? 1 : 0)) + wwarning(" index_of_hole is too large ! (%d greater than %d)", + index_of_hole, abs(drawer->icon_count) - (redocking ? 1 : 0)); + if (index_of_hole == 0) + wwarning(" index_of_hole == 0 (%sredocking, icon_count == %d)", (redocking ? "" : "not "), drawer->icon_count); + + return index_of_hole; +} + + +void wSlideAppicons(WAppIcon **appicons, int n, int to_the_left) +{ + int i; + int leftmost = -1, min_index = 9999, from_x = -1; // leftmost and from_x initialized to avoid warning + Window *wins[n]; + WAppIcon *aicon; + + if (n < 1) + return; + + for (i = 0; i < n; i++) { + aicon = appicons[i]; + aicon->xindex += (to_the_left ? -1 : +1); + if (aicon->xindex < min_index) { + min_index = aicon->xindex; + leftmost = i; + from_x = aicon->x_pos; + } + aicon->x_pos += (to_the_left ? -ICON_SIZE : +ICON_SIZE); + } + + for (i = 0; i < n; i++) { + aicon = appicons[i]; + wins[aicon->xindex - min_index] = &aicon->icon->core->window; + } + aicon = appicons[leftmost]; + SlideWindows(wins, n, from_x, aicon->y_pos, aicon->x_pos, aicon->y_pos); +} + + +void wDrawerFillTheGap(WDock *drawer, WAppIcon *aicon, Bool redocking) +{ + int i, j; + int index_of_hole = indexOfHole(drawer, aicon, redocking); + WAppIcon *aicons_to_shift[drawer->icon_count]; + + j = 0; + for (i = 0; i < drawer->max_icons; i++) { + WAppIcon *ai = drawer->icon_array[i]; + if (ai && ai != aicon && + abs(ai->xindex) > abs(index_of_hole)) + aicons_to_shift[j++] = ai; + } + if (j != drawer->icon_count - abs(index_of_hole) - (redocking ? 1 : 0)) + wwarning("Removing aicon at index %d from %s: j=%d but should be %d", + index_of_hole, drawer->icon_array[0]->wm_instance, + j, drawer->icon_count - abs(index_of_hole) - (redocking ? 1 : 0)); + wSlideAppicons(aicons_to_shift, j, !drawer->on_right_side); +} + + +static void drawerConsolidateIcons(WDock *drawer) +{ + WAppIcon *aicons_to_shift[drawer->icon_count]; + int maxRemaining = 0; + int sum = 0; + int i; + for (i = 0; i < drawer->max_icons; i++) { + WAppIcon *ai = drawer->icon_array[i]; + if (ai == NULL) + continue; + sum += abs(ai->xindex); + if (abs(ai->xindex) > maxRemaining) + maxRemaining = abs(ai->xindex); + } + while (sum != maxRemaining * (maxRemaining + 1) / 2) { // while there is a hole + WAppIcon *ai; + int n; + // Look up for the hole at max position + int maxDeleted; + for (maxDeleted = maxRemaining - 1; maxDeleted > 0; maxDeleted--) { + Bool foundAppIconThere = False; + for (i = 0; i < drawer->max_icons; i++) { + WAppIcon *ai = drawer->icon_array[i]; + if (ai == NULL) + continue; + if (abs(ai->xindex) == maxDeleted) { + foundAppIconThere = True; + break; + } + } + if (!foundAppIconThere) + break; + } + assert(maxDeleted > 0); // would mean while test is wrong + n = 0; + for (i = 0; i < drawer->max_icons; i++) { + ai = drawer->icon_array[i]; + if (ai != NULL && abs(ai->xindex) > maxDeleted) + aicons_to_shift[n++] = ai; + } + assert(n == maxRemaining - maxDeleted); // for the code review ;-) + wSlideAppicons(aicons_to_shift, n, !drawer->on_right_side); + // Efficient beancounting + maxRemaining -= 1; + sum -= n; + } +} + + + +/* similar to wDockRestoreState, but a lot a specific stuff too... */ +static WDock * drawerRestoreState(WScreen *scr, WMPropList *drawer_state) +{ + WDock *drawer; + WMPropList *apps, *value, *dock_state; + WAppIcon *aicon; + int count, i; + + if (!drawer_state) + return NULL; + + make_keys(); + + WMRetainPropList(drawer_state); + + /* Get the instance name, and create a drawer */ + value = WMGetFromPLDictionary(drawer_state, dName); + drawer = wDockCreate(scr, WM_DRAWER, WMGetFromPLString(value)); + + /* restore DnD command and paste command */ +#ifdef XDND + value = WMGetFromPLDictionary(drawer_state, dDropCommand); + if (value && WMIsPLString(value)) + drawer->icon_array[0]->dnd_command = wstrdup(WMGetFromPLString(value)); +#endif /* XDND */ + + value = WMGetFromPLDictionary(drawer_state, dPasteCommand); + if (value && WMIsPLString(value)) + drawer->icon_array[0]->paste_command = wstrdup(WMGetFromPLString(value)); + + /* restore position */ + value = WMGetFromPLDictionary(drawer_state, dPosition); + if (!value || !WMIsPLString(value)) + COMPLAIN("Position"); + else { + int x, y, y_index; + if (sscanf(WMGetFromPLString(value), "%i,%i", &x, &y) != 2) + COMPLAIN("Position"); + + /* check position sanity */ + if (x != scr->dock->x_pos) { + x = scr->dock->x_pos; + } + y_index = (y - scr->dock->y_pos) / ICON_SIZE; + if (y_index >= scr->dock->max_icons) { + /* Here we should do something more intelligent, since it + * can happen even if the user hasn't hand-edited his + * G/D/State file (but uses a lower resolution). */ + y_index = scr->dock->max_icons - 1; + } + y = scr->dock->y_pos + y_index * ICON_SIZE; + moveDock(drawer, x, y); + } + + /* restore dock properties (applist and others) */ + dock_state = WMGetFromPLDictionary(drawer_state, dDock); + + /* restore lowered/raised state: same as scr->dock, no matter what */ + drawer->lowered = scr->dock->lowered; + if (!drawer->lowered) + ChangeStackingLevel(drawer->icon_array[0]->icon->core, WMDockLevel); + else + ChangeStackingLevel(drawer->icon_array[0]->icon->core, WMNormalLevel); + wRaiseFrame(drawer->icon_array[0]->icon->core); + + /* restore collapsed state */ + drawer->collapsed = 0; + value = WMGetFromPLDictionary(dock_state, dCollapsed); + if (value && strcasecmp(WMGetFromPLString(value), "YES") == 0) { + drawer->collapsed = 1; + } + /* restore auto-collapsed state */ + value = WMGetFromPLDictionary(dock_state, dAutoCollapse); + if (value && strcasecmp(WMGetFromPLString(value), "YES") == 0) { + drawer->auto_collapse = 1; + drawer->collapsed = 1; + } else { + drawer->auto_collapse = 0; // because wDockCreate sets it (drawers only) + } + + /* restore auto-raise/lower state: same as scr->dock, no matter what */ + drawer->auto_raise_lower = scr->dock->auto_raise_lower; + + /* restore attract icons state */ + drawer->attract_icons = 0; + value = WMGetFromPLDictionary(dock_state, dAutoAttractIcons); + if (value && strcasecmp(WMGetFromPLString(value), "YES") == 0) { + drawer->attract_icons = 1; + scr->attracting_drawer = drawer; + } + + /* application list */ + apps = WMGetFromPLDictionary(dock_state, dApplications); + + if (!apps) { + goto finish; + } + + count = WMGetPropListItemCount(apps); + + if (count == 0) + goto finish; + + for (i=0; iicon_count >= drawer->max_icons) { + wwarning(_("there are too many icons stored in drawer. Ignoring what doesn't fit")); + break; + } + + value = WMGetFromPLArray(apps, i); + aicon = restore_icon_state(scr, value, WM_DRAWER, drawer->icon_count); + + drawer->icon_array[drawer->icon_count] = aicon; + + if (aicon) { + aicon->dock = drawer; + aicon->x_pos = drawer->x_pos + (aicon->xindex * ICON_SIZE); + aicon->y_pos = drawer->y_pos + (aicon->yindex * ICON_SIZE); + + if (!drawer->lowered) + ChangeStackingLevel(aicon->icon->core, WMDockLevel); + else + ChangeStackingLevel(aicon->icon->core, WMNormalLevel); + + wCoreConfigure(aicon->icon->core, aicon->x_pos, aicon->y_pos, 0, 0); + + if (!drawer->collapsed) + XMapWindow(dpy, aicon->icon->core->window); + wRaiseFrame(aicon->icon->core); + + drawer->icon_count++; + } + } + +finish: + WMReleasePropList(drawer_state); + + return drawer; +} + + +/* Same kind of comment than for previous function: this function is + * very similar to make_icon_state, but has substential differences as + * well. */ +static WMPropList *drawerSaveState(WScreen *scr, WDock *drawer) +{ + WMPropList *pstr, *drawer_state; + WAppIcon *ai; + char buffer[64]; + + ai = drawer->icon_array[0]; + /* Store its name */ + pstr = WMCreatePLString(ai->wm_instance); + drawer_state = WMCreatePLDictionary(dName, pstr, NULL); /* we need this final NULL */ + WMReleasePropList(pstr); + + /* Store its position */ + snprintf(buffer, sizeof(buffer), "%hi,%hi", ai->x_pos, ai->y_pos); + pstr = WMCreatePLString(buffer); + WMPutInPLDictionary(drawer_state, dPosition, pstr); + WMReleasePropList(pstr); + +#ifdef XDND + /* Store its DnD command */ + if (ai->dnd_command) { + pstr = WMCreatePLString(ai->dnd_command); + WMPutInPLDictionary(drawer_state, dDropCommand, pstr); + WMReleasePropList(pstr); + } +#endif /* XDND */ + + /* Store its paste command */ + if (ai->paste_command) { + pstr = WMCreatePLString(ai->paste_command); + WMPutInPLDictionary(drawer_state, dPasteCommand, pstr); + WMReleasePropList(pstr); + } + + /* Store applications list and other properties */ + pstr = dockSaveState(drawer); + WMPutInPLDictionary(drawer_state, dDock, pstr); + WMReleasePropList(pstr); + + return drawer_state; +} + + +void wDrawersSaveState(WScreen *scr) +{ + WMPropList *all_drawers, *drawer_state; + int i; + WDrawerChain *dc; + + make_keys(); + + all_drawers = WMCreatePLArray(NULL); + for (i=0, dc = scr->drawers; + i < scr->drawer_count; + i++, dc = dc->next) { + drawer_state = drawerSaveState(scr, dc->adrawer); + WMAddToPLArray(all_drawers, drawer_state); + WMReleasePropList(drawer_state); + } + WMPutInPLDictionary(scr->session_state, dDrawers, all_drawers); + WMReleasePropList(all_drawers); +} + + +void wDrawersRestoreState(WScreen *scr) +{ + WMPropList *all_drawers, *drawer_state; + int i; + + make_keys(); + + if (scr->session_state == NULL) + return; + + all_drawers = WMGetFromPLDictionary(scr->session_state, dDrawers); + if (!all_drawers) + return; + + for (i = 0; i < WMGetPropListItemCount(all_drawers); i++) { + drawer_state = WMGetFromPLArray(all_drawers, i); + drawerRestoreState(scr, drawer_state); + // Note: scr->drawers was updated when the the drawer was created + } +} diff --git a/src/dock.h b/src/dock.h index 47d963d4..2c6c4bc4 100644 --- a/src/dock.h +++ b/src/dock.h @@ -39,6 +39,7 @@ typedef struct WDock { #define WM_DOCK 0 #define WM_CLIP 1 +#define WM_DRAWER 2 int type; WMagicNumber auto_expand_magic; @@ -66,7 +67,7 @@ typedef struct WDock { -WDock *wDockCreate(WScreen *scr, int type); +WDock *wDockCreate(WScreen *scr, int type, char *name); WDock *wDockRestoreState(WScreen *scr, WMPropList *dock_state, int type); void wDockDestroy(WDock *dock); @@ -82,6 +83,11 @@ Bool wDockSnapIcon(WDock *dock, WAppIcon *icon, int req_x, int req_y, int *ret_x, int *ret_y, int redocking); Bool wDockFindFreeSlot(WDock *dock, int *req_x, int *req_y); void wDockDetach(WDock *dock, WAppIcon *icon); +Bool wDockMoveIconBetweenDocks(WDock *src, WDock *dest, WAppIcon *icon, int x, int y); +void wDockReattachIcon(WDock *dock, WAppIcon *icon, int x, int y); + +void wSlideAppicons(WAppIcon **appicons, int n, int to_the_left); +void wDrawerFillTheGap(WDock *drawer, WAppIcon *aicon, Bool redocking); void wDockFinishLaunch(WDock *dock, WAppIcon *icon); void wDockTrackWindowLaunch(WDock *dock, Window window); @@ -98,14 +104,27 @@ void wClipSaveState(WScreen *scr); WMPropList *wClipSaveWorkspaceState(WScreen *scr, int workspace); WAppIcon *wClipRestoreState(WScreen *scr, WMPropList *clip_state); +void wDrawerIconPaint(WAppIcon *dicon); +void wDrawersSaveState(WScreen *scr); +void wDrawersRestoreState(WScreen *scr); +int wIsADrawer(WScreen *scr, WAppIcon *aicon); + void wClipUpdateForWorkspaceChange(WScreen *scr, int workspace); RImage *wClipMakeTile(WScreen *scr, RImage *normalTile); +RImage* wDrawerMakeTile(WScreen *scr, RImage *normalTile); #define WO_FAILED 0 #define WO_NOT_APPLICABLE 1 #define WO_SUCCESS 2 +typedef enum +{ + P_NORMAL = 0, + P_AUTO_RAISE_LOWER, + P_KEEP_ON_TOP, +} dockPosition; + int wClipMakeIconOmnipresent(WAppIcon *aicon, int omnipresent); #endif diff --git a/src/dockedapp.c b/src/dockedapp.c index 6c6d1f05..d7fc7b7c 100644 --- a/src/dockedapp.c +++ b/src/dockedapp.c @@ -177,8 +177,11 @@ static void panelBtnCallback(WMWidget * self, void *data) } else { WAppIcon *aicon = panel->editedIcon; + // Cf dock.c:dockIconPaint(WAppIcon *aicon)? if (aicon == aicon->icon->core->screen_ptr->clip_icon) wClipIconPaint(aicon); + else if (wIsADrawer(aicon->icon->core->screen_ptr, aicon)) + wDrawerIconPaint(aicon); else wAppIconPaint(aicon); diff --git a/src/funcs.h b/src/funcs.h index 3902d0b6..7fb69581 100644 --- a/src/funcs.h +++ b/src/funcs.h @@ -73,6 +73,7 @@ WWindow * NextToFocusBefore(WWindow *wwin); void move_window(Window win, int from_x, int from_y, int to_x, int to_y); void SlideWindow(Window win, int from_x, int from_y, int to_x, int to_y); +void SlideWindows(Window *wins[], int n, int from_x, int from_y, int to_x, int to_y); RImage * wGetImageForWindowName(WScreen *scr, char *winstance, char *wclass); diff --git a/src/icon.c b/src/icon.c index 7b71ab7b..2d013415 100644 --- a/src/icon.c +++ b/src/icon.c @@ -246,11 +246,21 @@ static void icon_update_pixmap(WIcon *icon, RImage *image) int theight = 0; WScreen *scr = icon->core->screen_ptr; - if (icon->tile_type == TILE_NORMAL) { + switch (icon->tile_type) { + case TILE_NORMAL: tile = RCloneImage(scr->icon_tile); - } else { + break; + default: + wwarning("Unknown tileType: %d.\n", icon->tile_type); + // fallthrough to TILE_CLIP (emulate previous behaviour) + case TILE_CLIP: assert(scr->clip_tile); tile = RCloneImage(scr->clip_tile); + break; + case TILE_DRAWER: + assert(scr->drawer_tile); + tile = RCloneImage(scr->drawer_tile); + break; } if (image) { diff --git a/src/icon.h b/src/icon.h index 2c81ae70..368696f0 100644 --- a/src/icon.h +++ b/src/icon.h @@ -28,6 +28,7 @@ #define TILE_NORMAL 0 #define TILE_CLIP 1 +#define TILE_DRAWER 2 typedef struct WIcon { WCoreWindow *core; diff --git a/src/main.c b/src/main.c index f63b1a6e..4559f868 100644 --- a/src/main.c +++ b/src/main.c @@ -686,8 +686,11 @@ static int real_main(int argc, char **argv) wPreferences.flags.norestore = 1; } else if (strcmp(argv[i], "-nodock") == 0 || strcmp(argv[i], "--no-dock") == 0) { wPreferences.flags.nodock = 1; + wPreferences.flags.nodrawer = 1; } else if (strcmp(argv[i], "-noclip") == 0 || strcmp(argv[i], "--no-clip") == 0) { wPreferences.flags.noclip = 1; + } else if (strcmp(argv[i], "-nodrawer") == 0 || strcmp(argv[i], "--no-drawer") == 0) { + wPreferences.flags.nodrawer = 1; } else if (strcmp(argv[i], "-version") == 0 || strcmp(argv[i], "--version") == 0) { printf("Window Maker %s\n", VERSION); exit(0); diff --git a/src/misc.c b/src/misc.c index 39f85034..32d5659d 100644 --- a/src/misc.c +++ b/src/misc.c @@ -51,6 +51,7 @@ /**** global variables *****/ extern WPreferences wPreferences; +#define ICON_SIZE wPreferences.icon_size /* XFetchName Wrapper */ Bool wFetchName(Display *dpy, Window win, char **winname) @@ -144,11 +145,21 @@ void move_window(Window win, int from_x, int from_y, int to_x, int to_y) } void SlideWindow(Window win, int from_x, int from_y, int to_x, int to_y) +{ + Window *wins[1] = { &win }; + SlideWindows(wins, 1, from_x, from_y, to_x, to_y); +} + +/* wins is an array of Window, sorted from left to right, the first is + * going to be moved from (from_x,from_y) to (to_x,to_y) and the + * following windows are going to be offset by (ICON_SIZE*i,0) */ +void SlideWindows(Window *wins[], int n, int from_x, int from_y, int to_x, int to_y) { time_t time0 = time(NULL); float dx, dy, x = from_x, y = from_y, sx, sy, px, py; int dx_is_bigger = 0; int slide_delay, slide_steps, slide_slowdown; + int i; /* animation parameters */ static struct { @@ -216,7 +227,9 @@ void SlideWindow(Window win, int from_x, int from_y, int to_x, int to_y) px = (sy == 0 ? 0 : py * dx / dy); } - XMoveWindow(dpy, win, (int)x, (int)y); + for (i = 0; i < n; i++) { + XMoveWindow(dpy, *wins[i], (int)x + i * ICON_SIZE, (int)y); + } XFlush(dpy); if (slide_delay > 0) { wusleep(slide_delay * 1000L); @@ -226,7 +239,9 @@ void SlideWindow(Window win, int from_x, int from_y, int to_x, int to_y) if (time(NULL) - time0 > MAX_ANIMATION_TIME) break; } - XMoveWindow(dpy, win, to_x, to_y); + for (i = 0; i < n; i++) { + XMoveWindow(dpy, *wins[i], to_x + i * ICON_SIZE, to_y); + } XSync(dpy, 0); /* compress expose events */ diff --git a/src/screen.c b/src/screen.c index e8cc5a9a..37e4da31 100644 --- a/src/screen.c +++ b/src/screen.c @@ -94,6 +94,7 @@ static WMPropList *dApplications = NULL; static WMPropList *dWorkspace; static WMPropList *dDock; static WMPropList *dClip; +static WMPropList *dDrawers = NULL; static void make_keys(void) { @@ -104,6 +105,7 @@ static void make_keys(void) dWorkspace = WMCreatePLString("Workspace"); dDock = WMCreatePLString("Dock"); dClip = WMCreatePLString("Clip"); + dDrawers = WMCreatePLString("Drawers"); } /* @@ -842,12 +844,18 @@ void wScreenRestoreState(WScreen * scr) if (!wPreferences.flags.nodock) { state = WMGetFromPLDictionary(scr->session_state, dDock); scr->dock = wDockRestoreState(scr, state, WM_DOCK); + /* If clip_merged_in_dock, setting scr->clip_icon is done by + * wDockRestoreState()->wDockCreate()->mainIconCreate() */ } if (!wPreferences.flags.noclip) { state = WMGetFromPLDictionary(scr->session_state, dClip); scr->clip_icon = wClipRestoreState(scr, state); } + + if (!wPreferences.flags.nodrawer) { + wDrawersRestoreState(scr); + } wWorkspaceRestoreState(scr); @@ -895,6 +903,15 @@ void wScreenSaveState(WScreen * scr) wWorkspaceSaveState(scr, old_state); + if (!wPreferences.flags.nodrawer) { + wDrawersSaveState(scr); + } else { + if ((foo = WMGetFromPLDictionary(old_state, dDrawers)) != NULL) { + WMPutInPLDictionary(scr->session_state, dDrawers, foo); + } + } + + if (wPreferences.save_session_on_exit) { wSessionSaveState(scr); } else { diff --git a/src/screen.h b/src/screen.h index 5bf508eb..861983fc 100644 --- a/src/screen.h +++ b/src/screen.h @@ -62,6 +62,12 @@ typedef struct WAppIconChain { } WAppIconChain; +/* Drawers, which are docks, really */ +typedef struct WDrawerChain { + struct WDock *adrawer; + struct WDrawerChain *next; +} WDrawerChain; + /* * each WScreen is saved into a context associated with it's root window */ @@ -219,16 +225,22 @@ typedef struct _WScreen { struct WMenu *workspace_submenu; /* workspace list for window_menu */ struct WDock *dock; /* the application dock */ + struct WMenu *dock_pos_menu; /* Dock position menu */ struct WPixmap *dock_dots; /* 3 dots for the Dock */ Window dock_shadow; /* shadow for dock buttons */ - struct WAppIcon *clip_icon; /* The clip main icon */ + struct WAppIcon *clip_icon; /* The clip main icon, or the dock's, if they are merged */ struct WMenu *clip_menu; /* Menu for clips */ struct WMenu *clip_submenu; /* Workspace list for clips */ struct WMenu *clip_options; /* Options for Clip */ struct WMenu *clip_ws_menu; /* workspace menu for clip */ + struct WMenu *drawer_menu; /* Menu for drawers */ struct WDock *last_dock; WAppIconChain *global_icons; /* for omnipresent icons chain in clip */ int global_icon_count; /* How many global icons do we have */ + WDrawerChain *drawers; /* Chain of drawers */ + /* Cache the following two informations, as they are used quite a lot */ + int drawer_count; /* Nb of drawers that */ + struct WDock *attracting_drawer; /* The drawer that auto-attracts icons, or NULL */ int keymove_tick; struct RContext *rcontext; /* wrlib context */ @@ -236,8 +248,8 @@ typedef struct _WScreen { WMScreen *wmscreen; /* for widget library */ struct RImage *icon_tile; - struct RImage *clip_tile; - + struct RImage *clip_tile; /* tile with arrows to change workspace */ + struct RImage *drawer_tile; /* tile for a drawer (tile + arrow) */ Pixmap icon_tile_pixmap; /* For app supplied icons */ struct RImage *def_icon_rimage; /* Default RImage icon */ diff --git a/src/session.c b/src/session.c index 4fd11f89..065e769f 100644 --- a/src/session.c +++ b/src/session.c @@ -236,16 +236,28 @@ static WMPropList *makeWindowState(WWindow * wwin, WApplication * wapp) WMReleasePropList(shortcut); if (wapp && wapp->app_icon && wapp->app_icon->dock) { int i; - char *name; + char *name = NULL; if (wapp->app_icon->dock == scr->dock) { name = "Dock"; - } else { + } + if (name == NULL) // Try the clips + { for (i = 0; i < scr->workspace_count; i++) if (scr->workspaces[i]->clip == wapp->app_icon->dock) break; - assert(i < scr->workspace_count); - /*n = i+1; */ - name = scr->workspaces[i]->name; + if (i < scr->workspace_count) + name = scr->workspaces[i]->name; + } + if (name == NULL) // Try the drawers + { + WDrawerChain *dc; + for (dc = scr->drawers; dc != NULL; dc = dc->next) + { + if (dc->adrawer == wapp->app_icon->dock) + break; + } + assert(dc != NULL); + name = dc->adrawer->icon_array[0]->wm_instance; } dock = WMCreatePLString(name); WMPutInPLDictionary(win_state, sDock, dock); @@ -462,7 +474,9 @@ void wSessionRestoreState(WScreen *scr) if (sscanf(tmp, "%i", &n) != 1) { if (!strcasecmp(tmp, "DOCK")) { dock = scr->dock; - } else { + } + if (dock == NULL) // Try the clips + { for (j = 0; j < scr->workspace_count; j++) { if (strcmp(scr->workspaces[j]->name, tmp) == 0) { dock = scr->workspaces[j]->clip; @@ -470,6 +484,18 @@ void wSessionRestoreState(WScreen *scr) } } } + if (dock == NULL) // Try the drawers + { + WDrawerChain *dc; + for (dc = scr->drawers; dc != NULL; dc = dc->next) + { + if (strcmp(dc->adrawer->icon_array[0]->wm_instance, tmp) == 0) + { + dock = dc->adrawer; + break; + } + } + } } else { if (n == 0) { dock = scr->dock; diff --git a/src/startup.c b/src/startup.c index f9d4e52d..d309d98b 100644 --- a/src/startup.c +++ b/src/startup.c @@ -747,6 +747,14 @@ void StartUp(Bool defaultScreenOnly) } } } + /* auto-launch apps in drawers */ + if (!wPreferences.flags.nodrawer) { + WDrawerChain *dc; + for (dc = wScreen[j]->drawers; dc; dc = dc->next) { + wScreen[j]->last_dock = dc->adrawer; + wDockDoAutoLaunch(dc->adrawer, 0); + } + } } /* go to workspace where we were before restart */ diff --git a/src/superfluous.c b/src/superfluous.c index 135ce429..0c95ed4d 100644 --- a/src/superfluous.c +++ b/src/superfluous.c @@ -137,80 +137,6 @@ void DoKaboom(WScreen * scr, Window win, int x, int y) #endif /* NORMAL_ICON_KABOOM */ } -Pixmap MakeGhostDock(WDock * dock, int sx, int dx, int y) -{ - WScreen *scr = dock->screen_ptr; - XImage *img; - RImage *back, *dock_image; - Pixmap pixmap; - int i, virtual_tiles, h, j, n; - unsigned long red_mask, green_mask, blue_mask; - - virtual_tiles = 0; - for (i = 0; i < dock->max_icons; i++) { - if (dock->icon_array[i] != NULL && dock->icon_array[i]->yindex > virtual_tiles) - virtual_tiles = dock->icon_array[i]->yindex; - } - virtual_tiles++; - h = virtual_tiles * wPreferences.icon_size; - h = (y + h > scr->scr_height) ? scr->scr_height - y : h; - virtual_tiles = h / wPreferences.icon_size; /* The visible ones */ - if (h % wPreferences.icon_size) - virtual_tiles++; /* There is one partially visible tile at end */ - - img = XGetImage(dpy, scr->root_win, dx, y, wPreferences.icon_size, h, AllPlanes, ZPixmap); - if (!img) - return None; - - red_mask = img->red_mask; - green_mask = img->green_mask; - blue_mask = img->blue_mask; - - back = RCreateImageFromXImage(scr->rcontext, img, NULL); - XDestroyImage(img); - if (!back) { - return None; - } - - for (i = 0; i < dock->max_icons; i++) { - if (dock->icon_array[i] != NULL && dock->icon_array[i]->yindex < virtual_tiles) { - Pixmap which; - j = dock->icon_array[i]->yindex * wPreferences.icon_size; - n = (h - j < wPreferences.icon_size) ? h - j : wPreferences.icon_size; - if (dock->icon_array[i]->icon->pixmap) - which = dock->icon_array[i]->icon->pixmap; - else - which = dock->icon_array[i]->icon->core->window; - - img = XGetImage(dpy, which, 0, 0, wPreferences.icon_size, n, AllPlanes, ZPixmap); - - if (!img) { - RReleaseImage(back); - return None; - } - img->red_mask = red_mask; - img->green_mask = green_mask; - img->blue_mask = blue_mask; - - dock_image = RCreateImageFromXImage(scr->rcontext, img, NULL); - XDestroyImage(img); - if (!dock_image) { - RReleaseImage(back); - return None; - } - RCombineAreaWithOpaqueness(back, dock_image, 0, 0, - wPreferences.icon_size, n, 0, j, 30 * 256 / 100); - RReleaseImage(dock_image); - } - } - - RConvertImage(scr->rcontext, back, &pixmap); - - RReleaseImage(back); - - return pixmap; -} - Pixmap MakeGhostIcon(WScreen * scr, Drawable drawable) { RImage *back; diff --git a/src/superfluous.h b/src/superfluous.h index b7b70b1a..b3424cd5 100644 --- a/src/superfluous.h +++ b/src/superfluous.h @@ -24,7 +24,6 @@ #include "dock.h" void DoKaboom(WScreen *scr, Window win, int x, int y); -Pixmap MakeGhostDock(WDock *dock, int sx, int dx, int y); Pixmap MakeGhostIcon(WScreen *scr, Drawable drawable); void DoWindowBirth(WWindow *wwin); #endif diff --git a/src/wconfig.h.in b/src/wconfig.h.in index b8063dbe..9201afca 100644 --- a/src/wconfig.h.in +++ b/src/wconfig.h.in @@ -301,20 +301,6 @@ * a docked icon must be dragged out to detach it */ #define DOCK_DETTACH_THRESHOLD 3 -/* Delay (in ms) after which the clip will autocollapse when leaved */ -#define AUTO_COLLAPSE_DELAY 1000 - -/* Delay (in ms) after which the clip will autoexpand when entered. - * Set this to zero if you want instant expanding. */ -#define AUTO_EXPAND_DELAY 600 - -/* Delay (in ms) after which the clip will be lowered when leaved */ -#define AUTO_LOWER_DELAY 1000 - -/* Delay (in ms) after which the clip will be raised when entered. - * Set this to zero if you want instant raise. */ -#define AUTO_RAISE_DELAY 600 - /* Max. number of icons the dock and clip can have */ #define DOCK_MAX_ICONS 32 @@ -329,7 +315,7 @@ #define MAX_WORKSPACENAME_WIDTH 64 /* max width of window title in window list */ -#define MAX_WINDOWLIST_WIDTH 160 +#define MAX_WINDOWLIST_WIDTH 400 #ifndef HAVE_INOTIFY /* Check defaults database for changes every this many milliseconds */ diff --git a/src/wdefaults.c b/src/wdefaults.c index 0e7d6cdb..ce9af5f8 100644 --- a/src/wdefaults.c +++ b/src/wdefaults.c @@ -597,6 +597,37 @@ void wDefaultChangeIcon(WScreen * scr, char *instance, char *class, char *file) WMPLSetCaseSensitive(False); } +void wDefaultPurgeInfo(WScreen *scr, char *instance, char *class) +{ + WMPropList *value, *key, *dict; + char *buffer; + + if (!AIcon) { /* Unnecessary precaution */ + init_wdefaults(); + } + + WMPLSetCaseSensitive(True); + + buffer = wmalloc(strlen(class) + strlen(instance) + 2); + sprintf(buffer, "%s.%s", instance, class); + key = WMCreatePLString(buffer); + + dict = WMGetFromPLDictionary(WDWindowAttributes->dictionary, key); + + if (dict) { + value = WMGetFromPLDictionary(dict, AIcon); + if (value) { + WMRemoveFromPLDictionary(dict, AIcon); + } + WMRemoveFromPLDictionary(WDWindowAttributes->dictionary, key); + UpdateDomainFile(WDWindowAttributes); + } + + wfree(buffer); + WMReleasePropList(key); + WMPLSetCaseSensitive(False); +} + /* --------------------------- Local ----------------------- */ static int getBool(WMPropList * key, WMPropList * value) diff --git a/src/workspace.c b/src/workspace.c index 2f94427f..8b240872 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -103,7 +103,7 @@ int wWorkspaceNew(WScreen *scr) } if (!wPreferences.flags.noclip) - wspace->clip = wDockCreate(scr, WM_CLIP); + wspace->clip = wDockCreate(scr, WM_CLIP, NULL); list = wmalloc(sizeof(WWorkspace *) * scr->workspace_count); @@ -471,6 +471,8 @@ void wWorkspaceRelativeChange(WScreen * scr, int amount) void wWorkspaceForceChange(WScreen * scr, int workspace) { WWindow *tmp, *foc = NULL, *foc2 = NULL; + WWindow **toUnmap; + int toUnmapSize, toUnmapCount; if (workspace >= MAX_WORKSPACES || workspace < 0) return; @@ -490,6 +492,10 @@ void wWorkspaceForceChange(WScreen * scr, int workspace) wWorkspaceMenuUpdate(scr, scr->clip_ws_menu); + toUnmapSize = 16; + toUnmapCount = 0; + toUnmap = wmalloc(toUnmapSize * sizeof(WWindow *)); + if ((tmp = scr->focused_window) != NULL) { if ((IS_OMNIPRESENT(tmp) && (tmp->flags.mapped || tmp->flags.shaded) && !WFLAGP(tmp, no_focusable)) || tmp->flags.changing_workspace) { @@ -504,7 +510,12 @@ void wWorkspaceForceChange(WScreen * scr, int workspace) /* unmap windows not on this workspace */ if ((tmp->flags.mapped || tmp->flags.shaded) && !IS_OMNIPRESENT(tmp) && !tmp->flags.changing_workspace) { - wWindowUnmap(tmp); + if (toUnmapCount == toUnmapSize) + { + toUnmapSize *= 2; + toUnmap = wrealloc(toUnmap, toUnmapSize * sizeof(WWindow *)); + } + toUnmap[toUnmapCount++] = tmp; } /* also unmap miniwindows not on this workspace */ if (!wPreferences.sticky_icons && tmp->flags.miniaturized && @@ -553,6 +564,12 @@ void wWorkspaceForceChange(WScreen * scr, int workspace) tmp = tmp->prev; } + while (toUnmapCount > 0) + { + wWindowUnmap(toUnmap[--toUnmapCount]); + } + wfree(toUnmap); + /* Gobble up events unleashed by our mapping & unmapping. * These may trigger various grab-initiated focus & * crossing events. However, we don't care about them, @@ -606,15 +623,14 @@ void wWorkspaceForceChange(WScreen * scr, int workspace) if (scr->dock) wAppIconPaint(scr->dock->icon_array[0]); - if (scr->clip_icon) { - if (scr->workspaces[workspace]->clip->auto_collapse || - scr->workspaces[workspace]->clip->auto_raise_lower) { - /* to handle enter notify. This will also */ - XUnmapWindow(dpy, scr->clip_icon->icon->core->window); - XMapWindow(dpy, scr->clip_icon->icon->core->window); - } else { - wClipIconPaint(scr->clip_icon); - } + if (!wPreferences.flags.noclip && (scr->workspaces[workspace]->clip->auto_collapse || + scr->workspaces[workspace]->clip->auto_raise_lower)) { + /* to handle enter notify. This will also */ + XUnmapWindow(dpy, scr->clip_icon->icon->core->window); + XMapWindow(dpy, scr->clip_icon->icon->core->window); + } + else if (scr->clip_icon != NULL) { + wClipIconPaint(scr->clip_icon); } wScreenUpdateUsableArea(scr); wNETWMUpdateDesktop(scr);