X-Git-Url: https://git.subgeniuskitty.com/xmenu/.git/blobdiff_plain/78fb523f9ff58b3eb22b4e784400f53c36d45366..d90841035dc755668c5751401f3288ad5ceb697f:/xmenu.c diff --git a/xmenu.c b/xmenu.c index f2eaab4..9b3a250 100644 --- a/xmenu.c +++ b/xmenu.c @@ -432,6 +432,10 @@ buildmenutree(unsigned level, const char *label, const char *output, char *file) for (item = prevmenu->list; item->next != NULL; item = item->next) ; + /* a separator is no valid root for a submenu */ + if (!item->label) + errx(1, "a separator is no valid root for a submenu"); + prevmenu = menu; menu->caller = item; item->submenu = menu; @@ -717,8 +721,10 @@ setupmenu(struct Menu *menu, XClassHint *classh) /* set window title (used if wflag is on) */ if (menu->parent == NULL) { title = classh->res_name; - } else { + } else if (menu->caller->output) { title = menu->caller->output; + } else { + title = "\0"; } XStringListToTextProperty(&title, 1, &wintitle); @@ -1129,17 +1135,37 @@ itemcycle(struct Menu *currmenu, int direction) return item; } +/* check if button is used to scroll */ +static int +isscrollbutton(unsigned int button) +{ + if (button == Button4 || button == Button5) + return 1; + return 0; +} + /* check if button is used to open a item on click */ static int isclickbutton(unsigned int button) { - if (button == Button1) + if (button == Button1 || button == Button2) return 1; if (!rflag && button == Button3) return 1; return 0; } +/* warp pointer to center of selected item */ +static void +warppointer(struct Menu *menu, struct Item *item) +{ + if (menu == NULL || item == NULL) + return; + if (menu->selected) { + XWarpPointer(dpy, None, menu->win, 0, 0, 0, 0, menu->w / 2, item->y + item->h / 2); + } +} + /* append buf into text */ static int append(char *text, char *buf, size_t textsize, size_t buflen) @@ -1240,6 +1266,7 @@ run(struct Menu *currmenu) KeySym ksym; Status status; XEvent ev; + int warped = 0; int action; int len; int i; @@ -1256,38 +1283,50 @@ run(struct Menu *currmenu) action = ACTION_DRAW; break; case MotionNotify: - menu = getmenu(currmenu, ev.xbutton.window); - item = getitem(menu, ev.xbutton.y); - if (menu == NULL || item == NULL || previtem == item) - break; - previtem = item; - select = menu->selected = item; - if (item->submenu != NULL) { - currmenu = item->submenu; - select = NULL; - } else { - currmenu = menu; + if (!warped) { + menu = getmenu(currmenu, ev.xbutton.window); + item = getitem(menu, ev.xbutton.y); + if (menu == NULL || item == NULL || previtem == item) + break; + previtem = item; + select = menu->selected = item; + if (item->submenu != NULL) { + currmenu = item->submenu; + select = NULL; + } else { + currmenu = menu; + } + action = ACTION_CLEAR | ACTION_SELECT | ACTION_MAP | ACTION_DRAW; } - action = ACTION_CLEAR | ACTION_SELECT | ACTION_MAP | ACTION_DRAW; + warped = 0; break; case ButtonRelease: - if (!isclickbutton(ev.xbutton.button)) - break; - menu = getmenu(currmenu, ev.xbutton.window); - item = getitem(menu, ev.xbutton.y); - if (menu == NULL || item == NULL) - break; + if (isscrollbutton(ev.xbutton.button)) { + if (ev.xbutton.button == Button4) + select = itemcycle(currmenu, ITEMPREV); + else + select = itemcycle(currmenu, ITEMNEXT); + action = ACTION_CLEAR | ACTION_SELECT | ACTION_DRAW | ACTION_WARP; + } else if (isclickbutton(ev.xbutton.button)) { + menu = getmenu(currmenu, ev.xbutton.window); + item = getitem(menu, ev.xbutton.y); + if (menu == NULL || item == NULL) + break; enteritem: - if (item->label == NULL) - break; /* ignore separators */ - if (item->submenu != NULL) { - currmenu = item->submenu; - } else { - printf("%s\n", item->output); - return; + if (item->label == NULL) + break; /* ignore separators */ + if (item->submenu != NULL) { + currmenu = item->submenu; + } else { + printf("%s\n", item->output); + return; + } + select = currmenu->list; + action = ACTION_CLEAR | ACTION_SELECT | ACTION_MAP | ACTION_DRAW; + if (ev.xbutton.button == Button2) { + action |= ACTION_WARP; + } } - select = currmenu->list; - action = ACTION_CLEAR | ACTION_SELECT | ACTION_MAP | ACTION_DRAW; break; case ButtonPress: menu = getmenu(currmenu, ev.xbutton.window); @@ -1315,36 +1354,38 @@ enteritem: ksym = XK_ISO_Left_Tab; /* cycle through menu */ - item = NULL; + select = NULL; ksym = normalizeksym(ksym); switch (ksym) { case XK_Home: - item = itemcycle(currmenu, ITEMFIRST); - action = ACTION_CLEAR; + select = itemcycle(currmenu, ITEMFIRST); + action = ACTION_CLEAR | ACTION_SELECT | ACTION_DRAW; break; case XK_End: - item = itemcycle(currmenu, ITEMLAST); - action = ACTION_CLEAR; + select = itemcycle(currmenu, ITEMLAST); + action = ACTION_CLEAR | ACTION_SELECT | ACTION_DRAW; break; case XK_ISO_Left_Tab: if (*text) { - item = matchitem(currmenu, text, -1); + select = matchitem(currmenu, text, -1); + action = ACTION_SELECT | ACTION_DRAW; break; } /* FALLTHROUGH */ case XK_Up: - item = itemcycle(currmenu, ITEMPREV); - action = ACTION_CLEAR; + select = itemcycle(currmenu, ITEMPREV); + action = ACTION_CLEAR | ACTION_SELECT | ACTION_DRAW; break; case XK_Tab: if (*text) { - item = matchitem(currmenu, text, 1); + select = matchitem(currmenu, text, 1); + action = ACTION_SELECT | ACTION_DRAW; break; } /* FALLTHROUGH */ case XK_Down: - item = itemcycle(currmenu, ITEMNEXT); - action = ACTION_CLEAR; + select = itemcycle(currmenu, ITEMNEXT); + action = ACTION_CLEAR | ACTION_SELECT | ACTION_DRAW; break; case XK_1: case XK_2: case XK_3: case XK_4: case XK_5: case XK_6: case XK_7: case XK_8: case XK_9: item = itemcycle(currmenu, ITEMFIRST); @@ -1353,7 +1394,8 @@ enteritem: currmenu->selected = item; item = itemcycle(currmenu, ITEMNEXT); } - action = ACTION_CLEAR; + select = item; + action = ACTION_CLEAR | ACTION_SELECT | ACTION_DRAW; break; case XK_Return: case XK_Right: if (currmenu->selected) { @@ -1363,26 +1405,27 @@ enteritem: break; case XK_Escape: case XK_Left: if (currmenu->parent) { - item = currmenu->parent->selected; + select = currmenu->parent->selected; currmenu = currmenu->parent; - action = ACTION_CLEAR | ACTION_MAP; + action = ACTION_CLEAR | ACTION_MAP | ACTION_SELECT | ACTION_DRAW; } break; case XK_BackSpace: case XK_Clear: case XK_Delete: - action = ACTION_CLEAR; + action = ACTION_CLEAR | ACTION_SELECT | ACTION_DRAW; break; default: append: + if (*buf == '\0' || iscntrl(*buf)) + break; for (i = 0; i < 2; i++) { append(text, buf, sizeof text, len); - if ((item = matchitem(currmenu, text, 0))) + if ((select = matchitem(currmenu, text, 0))) break; text[0] = '\0'; } + action = ACTION_SELECT | ACTION_DRAW; break; } - select = item; - action |= ACTION_SELECT | ACTION_DRAW; break; case LeaveNotify: previtem = NULL; @@ -1415,6 +1458,10 @@ append: mapmenu(currmenu); if (action & ACTION_DRAW) drawmenus(currmenu); + if (action & ACTION_WARP) { + warppointer(currmenu, select); + warped = 1; + } } }