/* treewm - an X11 window manager.
* Copyright (c) 2001-2003 Thomas Jäger <TheHunter2000 at web dot de>
* This code is released under the terms of the GNU GPL.
* See the included file LICENSE for details.
*/
#include "menu.h"
#include "sceme.h"
#include "uehandler.h"
#include <X11/keysym.h>
inline int min(int x,int y) {
return x < y ? x : y;
}
Menu::Menu(Menu *Parent, const MenuItem* Items,int N,Client *c, char *CMD,
Sceme *sceme,int X,int Y, bool center) {
parent = Parent;
cmd = CMD;
client = c;
Sc = sceme;
window = 0;
int w,width2 = 0;
width = 0;
n = N;
items = Items;
buf = 0;
ignore = 0;
XFontSetExtents *e = XExtentsOfFontSet(Sc->fonts[FO_MENU]);
EHeight = e->max_logical_extent.height*4/5 + e->max_logical_extent.height/5 + 2*MENUSPACE;
height = n*EHeight - 1;
for (int i=0;i!=n;++i) {
w = XmbTextEscapement(Sc->fonts[FO_MENU],items[i].text,strlen(items[i].text));
w += MENUINDENT * (items[i].flags/MF_INDENT);
if (w>width)
width = w;
if (!(Sc->ShowKeys)/* || items[i].key[0] == ':'*/)
continue;
w = XmbTextEscapement(Sc->fonts[FO_MENU],items[i].key,strlen(items[i].key));
if (w>width2)
width2 = w;
}
width += width2 + 4*MENUSPACE;
sel = 0;
x = min(X - (center ? width/2 : 0), DisplayWidth(dpy, screen) - width - 2);
if (x < 0)
x = 0;
y = min(Y, DisplayHeight(dpy, screen) - height - 2);
if (y < 0)
y = 0;
submenu = 0;
}
Menu::~Menu() {
if (submenu)
delete submenu;
}
void Menu::Init() {
XSetWindowAttributes pattr;
pattr.override_redirect = True;
pattr.background_pixel = Sc->colors[C_MBG].pixel;
pattr.border_pixel = Sc->colors[C_MBD].pixel;
pattr.event_mask = ChildMask|MouseMask|ExposureMask|EnterWindowMask|KeyPressMask;
window = XCreateWindow(dpy, root, x, y, width, height, 1,
DefaultDepth(dpy, screen), CopyFromParent, DefaultVisual(dpy, screen),
CWOverrideRedirect|CWBackPixel|CWBorderPixel|CWEventMask, &pattr);
XMapWindow(dpy,window);
if (!parent) {
XGrabPointer(dpy,window,false,MouseMask,GrabModeAsync,GrabModeAsync,
None,None,CurrentTime);
if (!UEH->dialog)
XGrabKeyboard(dpy,window,false,GrabModeAsync,GrabModeAsync,CurrentTime);
}
}
void Menu::DrawItem(int i) {
XFontSetExtents *e = XExtentsOfFontSet(Sc->fonts[FO_MENU]);
XmbDrawString(dpy, window, Sc->fonts[FO_MENU], Sc->menustring_gc,
MENUSPACE + MENUINDENT * (items[i].flags/MF_INDENT),
i*EHeight + MENUSPACE + e->max_logical_extent.height*4/5,
items[i].text, strlen(items[i].text));
if (!(Sc->ShowKeys) /* || items[i].key[0] == ':'*/)
return;
int w = XmbTextEscapement(Sc->fonts[FO_MENU],items[i].key,strlen(items[i].key));
XmbDrawString(dpy, window, Sc->fonts[FO_MENU], Sc->menustring_gc, width -w-MENUSPACE,
i*EHeight + MENUSPACE + e->max_logical_extent.height*4/5,
items[i].key, strlen(items[i].key));
}
void Menu::ReDraw() {
XClearWindow(dpy,window);
for (int i=0;i!=n;++i) {
if (i)
XDrawLine(dpy, window, Sc->menubd_gc, 0, i*EHeight-1, width, i*EHeight-1);
if (i+1 == sel)
XFillRectangle(dpy,window,Sc->hmenu_gc,0,i*EHeight,width,EHeight-1);
DrawItem(i);
}
}
char *Menu::GetCmd() {
if (!sel)
return 0;
char *key = items[sel-1].key;
if (!cmd)
return key;
if (!key)
return cmd;
if (buf)
delete [] buf;
buf = new char[strlen(key) + strlen(cmd) + 1];
strcpy(buf,cmd);
strcat(buf,key);
return buf;
}
void Menu::Mouse(int X,int Y) {
if (submenu) {
submenu->Mouse(X,Y);
if (submenu->sel)
return;
}
int oldsel = sel;
if (x <= X && X <= x+width && y <= Y && Y <= y + height)
sel = (Y-y)/EHeight + 1; else
sel = 0;
if (sel > n)
sel = n;
if (sel == oldsel)
return;
if (oldsel) {
XFillRectangle(dpy,window,Sc->menu_gc,0,(oldsel-1)*EHeight,width,EHeight-1);
DrawItem(oldsel - 1);
}
if (sel) {
XFillRectangle(dpy,window,Sc->hmenu_gc,0,(sel-1)*EHeight,width,EHeight-1);
DrawItem(sel - 1);
}
if (submenu) {
if (sel == selsubmenu)
return;
submenu->Remove(0); // must return true ??
delete submenu;
submenu = 0;
}
if (!sel)
return;
SubMenuInfo *si = items[sel-1].submenu;
if (si) {
submenu = new Menu(this, si->menu, si->num, client, GetCmd(), Sc,
x + width + Sc->BW, y + (sel-1)*EHeight, false);
submenu->Init();
selsubmenu = sel;
}
}
bool Menu::Key(XKeyEvent &e) {
if (submenu && !sel) {
if (submenu->Key(e))
return true;
if (submenu->sel)
return false;
/* selsubmenu must be != 0 */
XWarpPointer(dpy,None,root,0,0,0,0,x+width/2,y+EHeight*(selsubmenu-1)+EHeight/2);
sel = selsubmenu;
submenu->sel = 0;
ReDraw();
submenu->ReDraw();
return true;
}
if (e.keycode == XKeysymToKeycode(dpy,XK_Up)) {
if (sel > 1)
XWarpPointer(dpy,None,root,0,0,0,0,x+width/2,y+EHeight*(sel-2)+EHeight/2);
return true;
}
if (e.keycode == XKeysymToKeycode(dpy,XK_Down)) {
if (sel < n)
XWarpPointer(dpy,None,root,0,0,0,0,x+width/2,y+EHeight*sel+EHeight/2);
return true;
}
if (e.keycode == XKeysymToKeycode(dpy,XK_Return) ||
e.keycode == XKeysymToKeycode(dpy,XK_Right) ) {
if (sel && submenu) {
XWarpPointer(dpy,None,root,0,0,0,0,submenu->x+submenu->width/2,submenu->y+EHeight/2);
sel = 0;
submenu->sel = 1;
ReDraw();
submenu->ReDraw();
return true;
}
return false;
}
if (e.keycode == XKeysymToKeycode(dpy,XK_Escape) ||
e.keycode == XKeysymToKeycode(dpy,XK_Left) ) {
sel = 0;
return false;
}
return true;
}
bool Menu::Remove(int *ret) {
if (ignore) {
--ignore;
return false;
}
if (submenu && !submenu->Remove(0))
return false;
char *Cmd = GetCmd();
if (Cmd && Cmd[0] == '?' && Cmd[1] == '\0')
return false;
if (!parent) {
XUngrabPointer(dpy,CurrentTime);
if (!UEH->dialog)
XUngrabKeyboard(dpy,CurrentTime);
}
XUnmapWindow(dpy,window);
XDestroyWindow(dpy,window);
if (sel) {
Client *c = items[sel-1].client;
if (!c)
c = client;
UEH->ExecCommand(c,0,Cmd,true);
}
if (buf)
delete [] buf;
if (ret)
*ret = sel;
return true;
}
syntax highlighted by Code2HTML, v. 0.9.1