This patch add ability to assing kyboard shortcut to docked
appicons.

Original-patch-by: Alexey Voinov <[email protected]>
Signed-off-by: Alexey I. Froloff <[email protected]>
---
 src/appicon.c   |    2 +
 src/appicon.h   |    4 ++
 src/dock.c      |  143 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/dockedapp.c |  137 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 src/event.c     |    2 +-
 src/rootmenu.c  |    2 +-
 src/screen.h    |    1 +
 src/window.c    |    1 +
 8 files changed, 289 insertions(+), 3 deletions(-)

diff --git a/src/appicon.c b/src/appicon.c
index 75fc044..ae72922 100644
--- a/src/appicon.c
+++ b/src/appicon.c
@@ -168,6 +168,8 @@ void wAppIconDestroy(WAppIcon * aicon)
        if (aicon->dnd_command)
                wfree(aicon->dnd_command);
 #endif
+       if (aicon->keyboard_shortcut)
+               wfree(aicon->keyboard_shortcut);
        if (aicon->wm_instance)
                wfree(aicon->wm_instance);
        if (aicon->wm_class)
diff --git a/src/appicon.h b/src/appicon.h
index f3e9796..8611527 100644
--- a/src/appicon.h
+++ b/src/appicon.h
@@ -51,6 +51,10 @@ typedef struct WAppIcon {
 
     char *paste_command;              /* command to run when something is 
pasted */
 
+    char *keyboard_shortcut;          /* keyboard shortcut to launch app */
+    int modifier;
+    KeyCode keycode;
+
     char *wm_class;
     char *wm_instance;
     pid_t pid;                        /* for apps launched from the dock */
diff --git a/src/dock.c b/src/dock.c
index 7e6d926..1892043 100644
--- a/src/dock.c
+++ b/src/dock.c
@@ -79,6 +79,7 @@ static WMPropList *dPasteCommand = NULL;
 #ifdef XDND                    /* XXX was OFFIX */
 static WMPropList *dDropCommand = NULL;
 #endif
+static WMPropList *dKeyboardShortcut=NULL;
 static WMPropList *dAutoLaunch, *dLock;
 static WMPropList *dName, *dForced, *dBuggyApplication, *dYes, *dNo;
 static WMPropList *dHost, *dDock, *dClip;
@@ -131,6 +132,7 @@ static void make_keys(void)
 #ifdef XDND
        dDropCommand = WMRetainPropList(WMCreatePLString("DropCommand"));
 #endif
+       dKeyboardShortcut = WMRetainPropList(WMCreatePLString("Shortcut"));
        dLock = WMRetainPropList(WMCreatePLString("Lock"));
        dAutoLaunch = WMRetainPropList(WMCreatePLString("AutoLaunch"));
        dName = WMRetainPropList(WMCreatePLString("Name"));
@@ -1272,6 +1274,12 @@ static WMPropList *make_icon_state(WAppIcon * btn)
                        WMReleasePropList(command);
                }
 
+               if (btn->keyboard_shortcut) {
+                       command = WMCreatePLString(btn->keyboard_shortcut);
+                       WMPutInPLDictionary(node, dKeyboardShortcut, command);
+                       WMReleasePropList(command);
+               }
+
                if (btn->client_machine && btn->remote_start) {
                        host = WMCreatePLString(btn->client_machine);
                        WMPutInPLDictionary(node, dHost, host);
@@ -1466,6 +1474,12 @@ static WAppIcon *restore_icon_state(WScreen * scr, 
WMPropList * info, int type,
                aicon->dnd_command = wstrdup(WMGetFromPLString(cmd));
 #endif
 
+       cmd = WMGetFromPLDictionary(info, dKeyboardShortcut);
+       if (cmd) {
+               if(addDockShortcut(WMGetFromPLString(cmd), aicon))
+                       aicon->keyboard_shortcut = 
wstrdup(WMGetFromPLString(cmd));
+       }
+
        cmd = WMGetFromPLDictionary(info, dPasteCommand);
        if (cmd)
                aicon->paste_command = wstrdup(WMGetFromPLString(cmd));
@@ -1752,6 +1766,8 @@ WDock *wDockRestoreState(WScreen * scr, WMPropList * 
dock_state, int type)
        if (type == WM_DOCK)
                dock->icon_count = 0;
 
+       dock->screen_ptr->flags.dock_changed_shortcuts = 0;
+
        for (i = 0; i < count; i++) {
                if (dock->icon_count >= dock->max_icons) {
                        wwarning(_("there are too many icons stored in dock. 
Ignoring what doesn't fit"));
@@ -1783,6 +1799,11 @@ WDock *wDockRestoreState(WScreen * scr, WMPropList * 
dock_state, int type)
                } else if (dock->icon_count == 0 && type == WM_DOCK)
                        dock->icon_count++;
        }
+       if(dock->screen_ptr->flags.dock_changed_shortcuts)
+       {
+               rebindKeygrabs(dock->screen_ptr);
+               dock->screen_ptr->flags.dock_changed_shortcuts = 0;
+       }
 
        /* if the first icon is not defined, use the default */
        if (dock->icon_array[0] == NULL) {
@@ -4139,3 +4160,125 @@ int wClipMakeIconOmnipresent(WAppIcon * aicon, int 
omnipresent)
 
        return status;
 }
+
+Bool
+addDockShortcut(char *shortcutDefinition, WAppIcon *icon)
+{
+       int modifier = 0;
+       KeyCode keycode;
+       KeySym ksym;
+       char *k;
+       char buf[128], *b;
+
+       strcpy(buf, shortcutDefinition);
+       b = (char*)buf;
+
+       /* get modifiers */
+       while((k = strchr(b, '+'))!=NULL) {
+               int mod;
+
+               *k = 0;
+               mod = wXModifierFromKey(b);
+               if(mod < 0) {
+                       wwarning(_("invalid key modifier \"%s\""), b);
+                       return False;
+               }
+               modifier |= mod;
+
+               b = k+1;
+       }
+
+       /* get key */
+       ksym = XStringToKeysym(b);
+
+       if (ksym==NoSymbol) {
+               wwarning(_("invalid kbd shortcut specification \"%s\""), 
shortcutDefinition);
+               return False;
+       }
+
+       keycode = XKeysymToKeycode(dpy, ksym);
+       if (keycode==0) {
+               wwarning(_("invalid key in shortcut \"%s\""), 
shortcutDefinition);
+               return False;
+       }
+       icon->modifier = modifier;
+       icon->keycode = keycode;
+       if(icon->dock && icon->dock->screen_ptr)
+               icon->dock->screen_ptr->flags.dock_changed_shortcuts = 1;
+       return True;
+}
+
+static Bool
+wDockPerformShortcut(WDock *dock, XEvent *event)
+{
+       int i;
+       int modifiers;
+       int done = 0;
+
+       if(!dock) return done;
+       modifiers = event->xkey.state & ValidModMask;
+       for(i=(dock->type==WM_DOCK ? 0 : 1); i<dock->max_icons; i++) {
+               WAppIcon *btn = dock->icon_array[i];
+
+               if(!btn || btn->attracted)
+                       continue;
+
+               if(btn->keycode==event->xkey.keycode && 
(btn->modifier==modifiers)) {
+                       launchDockedApplication(btn, False);
+                       done = True;
+                       break;
+               }
+
+       }
+       return done;
+}
+
+Bool
+wDockAndClipPerformShortcut(WScreen *scr, XEvent *event)
+{
+       int done = 0;
+       int i;
+       if(!(done = wDockPerformShortcut(scr->dock, event))) {
+               for(i=0; i < scr->workspace_count; i++) {
+                       if(done = 
wDockPerformShortcut(scr->workspaces[i]->clip, event)) break;
+               }
+       }
+       return done;
+}
+
+static void
+wDockBindShortcuts(Window window, WDock* dock)
+{
+       int i;
+       if(!dock) return;
+       for(i=(dock->type==WM_DOCK ? 0 : 1); i<dock->max_icons; i++) {
+               WAppIcon *btn = dock->icon_array[i];
+
+               if(!btn || btn->attracted)
+                       continue;
+
+               if(btn->keyboard_shortcut)
+               {
+                       if(btn->keyboard_shortcut && 
btn->modifier!=AnyModifier) {
+                               XGrabKey(dpy, btn->keycode, 
btn->modifier|LockMask,
+                                        window, True, GrabModeAsync, 
GrabModeAsync);
+#ifdef NUMLOCK_HACK
+                               wHackedGrabKey(btn->keycode, btn->modifier,
+                                              window, True, GrabModeAsync, 
GrabModeAsync);
+#endif
+                       }
+                       XGrabKey(dpy, btn->keycode, btn->modifier, window, True,
+                                GrabModeAsync, GrabModeAsync);
+               }
+       }
+}
+
+void
+wDockAndClipBindShortcuts(Window window, WScreen *scr)
+{
+       int i;
+       wDockBindShortcuts(window, scr->dock);
+       for(i=0; i < scr->workspace_count; i++ ) {
+               wDockBindShortcuts(window, scr->workspaces[i]->clip);
+       }
+}
diff --git a/src/dockedapp.c b/src/dockedapp.c
index 9c90387..0741f48 100644
--- a/src/dockedapp.c
+++ b/src/dockedapp.c
@@ -24,6 +24,7 @@
 
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
+#include <X11/keysym.h>
 #include <stdlib.h>
 #include <string.h>
 
@@ -61,6 +62,10 @@ typedef struct _AppSettingsPanel {
        WMTextField *pasteCommandField;
        WMLabel *pasteCommandLabel;
 
+       WMFrame *keyboardShortcutFrame;
+       WMTextField *keyboardShortcutField;
+       WMButton *keyboardShortcutCaptureBtn;
+
        WMFrame *iconFrame;
        WMTextField *iconField;
        WMButton *browseBtn;
@@ -76,6 +81,7 @@ typedef struct _AppSettingsPanel {
        /* kluge */
        unsigned int destroyed:1;
        unsigned int choosingIcon:1;
+       unsigned int capturing:1;
 } AppSettingsPanel;
 
 void DestroyDockAppSettingsPanel(AppSettingsPanel * panel);
@@ -106,6 +112,43 @@ static void updatePasteCommand(WAppIcon * icon, char 
*command)
        icon->paste_command = command;
 }
 
+static char*
+trimstr(char *str)
+{
+       char *p = str;
+       int i;
+
+       while (isspace(*p)) p++;
+       p = wstrdup(p);
+       i = strlen(p);
+       while (isspace(p[i]) && i>0) {
+               p[i]=0;
+               i--;
+       }
+
+       return p;
+}
+
+static void
+updateKeyboardShortcut(WAppIcon *icon, char *shortcut)
+{
+       char *str = NULL;
+       if(icon->keyboard_shortcut)
+               wfree(icon->keyboard_shortcut);
+       if(shortcut) {
+               str = trimstr(shortcut);
+               if(!strlen(str)) {
+                       wfree(str);
+                       str = NULL;
+               }
+       }
+       icon->keyboard_shortcut = str;
+       icon->modifier = 0;
+       icon->keycode = 0;
+       if(str) addDockShortcut(str, icon);
+       rebindKeygrabs(icon->dock->screen_ptr);
+}
+
 #ifdef XDND
 static void updateDNDCommand(WAppIcon * icon, char *command)
 {
@@ -243,6 +286,9 @@ static void panelBtnCallback(WMWidget * self, void *data)
                text = WMGetTextFieldText(panel->pasteCommandField);
                updatePasteCommand(panel->editedIcon, text);
 
+               text = WMGetTextFieldText(panel->keyboardShortcutField);
+               updateKeyboardShortcut(panel->editedIcon, text);
+
                panel->editedIcon->auto_launch = 
WMGetButtonSelected(panel->autoLaunchBtn);
 
                panel->editedIcon->lock = WMGetButtonSelected(panel->lockBtn);
@@ -252,8 +298,82 @@ static void panelBtnCallback(WMWidget * self, void *data)
                DestroyDockAppSettingsPanel(panel);
 }
 
+static char*
+captureShortcut(Display *dpy, AppSettingsPanel *panel)
+{
+       XEvent ev;
+       KeySym ksym, lksym, uksym;
+       char buffer[64];
+       char *key = NULL;
+
+       while (panel->capturing) {
+               XAllowEvents(dpy, AsyncKeyboard, CurrentTime);
+               WMNextEvent(dpy, &ev);
+               if (ev.type==KeyPress && ev.xkey.keycode!=0) {
+                       ksym = XKeycodeToKeysym(dpy, ev.xkey.keycode, 0);
+                       if (!IsModifierKey(ksym)) {
+                               XConvertCase(ksym, &lksym, &uksym);
+                               key=XKeysymToString(uksym);
+                               panel->capturing = 0;
+                               break;
+                       }
+               }
+               WMHandleEvent(&ev);
+       }
+       if (!key)
+               return NULL;
+
+       buffer[0] = 0;
+       if (ev.xkey.state & ControlMask) {
+               strcat(buffer, "Control+");
+       }
+       if (ev.xkey.state & ShiftMask) {
+               strcat(buffer, "Shift+");
+       }
+       if (ev.xkey.state & Mod1Mask) {
+               strcat(buffer, "Mod1+");
+       }
+       if (ev.xkey.state & Mod2Mask) {
+               strcat(buffer, "Mod2+");
+       }
+       if (ev.xkey.state & Mod3Mask) {
+               strcat(buffer, "Mod3+");
+       }
+       if (ev.xkey.state & Mod4Mask) {
+               strcat(buffer, "Mod4+");
+       }
+       if (ev.xkey.state & Mod5Mask) {
+               strcat(buffer, "Mod5+");
+       }
+       strcat(buffer, key);
+
+       return wstrdup(buffer);
+}
+
+static void
+captureClick(WMWidget *w, void *data)
+{
+       AppSettingsPanel *panel = (AppSettingsPanel*)data;
+       char *shortcut;
+
+       if(!panel->capturing) {
+               panel->capturing = 1;
+               WMSetButtonText(w, _("Cancel"));
+               XGrabKeyboard(dpy, WMWidgetXID(panel->win), True, GrabModeAsync,
+                                         GrabModeAsync, CurrentTime);
+               shortcut = captureShortcut(dpy, panel);
+               if (shortcut) {
+                       WMSetTextFieldText(panel->keyboardShortcutField, 
shortcut);
+                       wfree(shortcut);
+               }
+       }
+       panel->capturing = 0;
+       WMSetButtonText(w, _("Capture"));
+       XUngrabKeyboard(dpy, CurrentTime);
+}
+
 #define PWIDTH 295
-#define PHEIGHT        430
+#define PHEIGHT        490
 
 void ShowDockAppSettingsPanel(WAppIcon * aicon)
 {
@@ -353,6 +473,21 @@ void ShowDockAppSettingsPanel(WAppIcon * aicon)
 #endif
        WMMapSubwidgets(panel->dndCommandFrame);
 
+       panel->keyboardShortcutFrame = WMCreateFrame(vbox);
+       WMSetFrameTitle(panel->keyboardShortcutFrame, _("Keyboard shortcut"));
+       WMAddBoxSubview(vbox, WMWidgetView(panel->keyboardShortcutFrame), 
False, True,
+                       50, 50, 10);
+       panel->keyboardShortcutField = 
WMCreateTextField(panel->keyboardShortcutFrame);
+       WMResizeWidget(panel->keyboardShortcutField, 176, 20);
+       WMMoveWidget(panel->keyboardShortcutField, 10, 20);
+       WMSetTextFieldText(panel->keyboardShortcutField, 
aicon->keyboard_shortcut);
+       panel->keyboardShortcutCaptureBtn = 
WMCreateCommandButton(panel->keyboardShortcutFrame);
+       WMSetButtonText(panel->keyboardShortcutCaptureBtn, _("Capture"));
+       WMResizeWidget(panel->keyboardShortcutCaptureBtn, 70, 24);
+       WMMoveWidget(panel->keyboardShortcutCaptureBtn, 195, 18);
+       WMSetButtonAction(panel->keyboardShortcutCaptureBtn, captureClick, 
panel);
+       WMMapSubwidgets(panel->keyboardShortcutFrame);
+
        panel->iconFrame = WMCreateFrame(vbox);
        WMSetFrameTitle(panel->iconFrame, _("Icon Image"));
        WMAddBoxSubview(vbox, WMWidgetView(panel->iconFrame), False, True, 50, 
50, 10);
diff --git a/src/event.c b/src/event.c
index 5d80fbe..4bc5d4f 100644
--- a/src/event.c
+++ b/src/event.c
@@ -1358,7 +1358,7 @@ static void handleKeyPress(XEvent * event)
 
        if (command < 0) {
 
-               if (!wRootMenuPerformShortcut(event)) {
+               if (!wRootMenuPerformShortcut(event) && 
!wDockAndClipPerformShortcut(scr, event)) {
                        static int dontLoop = 0;
 
                        if (dontLoop > 10) {
diff --git a/src/rootmenu.c b/src/rootmenu.c
index 51a5791..8d56a7d 100644
--- a/src/rootmenu.c
+++ b/src/rootmenu.c
@@ -378,7 +378,7 @@ void wRootMenuBindShortcuts(Window window)
        }
 }
 
-static void rebindKeygrabs(WScreen * scr)
+void rebindKeygrabs(WScreen * scr)
 {
        WWindow *wwin;
 
diff --git a/src/screen.h b/src/screen.h
index 51edd53..fc2c783 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -285,6 +285,7 @@ typedef struct _WScreen {
         unsigned int regenerate_icon_textures:1;
         unsigned int dnd_data_convertion_status:1;
         unsigned int root_menu_changed_shortcuts:1;
+        unsigned int dock_changed_shortcuts:1;
         unsigned int added_workspace_menu:1;
         unsigned int added_windows_menu:1;
         unsigned int startup2:1;       /* startup phase 2 */
diff --git a/src/window.c b/src/window.c
index a7ed37e..e1a3778 100644
--- a/src/window.c
+++ b/src/window.c
@@ -2507,6 +2507,7 @@ void wWindowSetKeyGrabs(WWindow * wwin)
        }
 
        wRootMenuBindShortcuts(wwin->frame->core->window);
+       wDockAndClipBindShortcuts(wwin->frame->core->window, wwin->screen_ptr);
 }
 
 void wWindowResetMouseGrabs(WWindow * wwin)
-- 
1.7.2.3


-- 
To unsubscribe, send mail to [email protected].

Reply via email to