362 lines
10 KiB
C
362 lines
10 KiB
C
/*
|
|
* menu.c
|
|
* Copyright © 2008, 2009 Martin Duquesnoy <xorg62@gmail.com>
|
|
* All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are
|
|
* met:
|
|
*
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above
|
|
* copyright notice, this list of conditions and the following disclaimer
|
|
* in the documentation and/or other materials provided with the
|
|
* distribution.
|
|
* * Neither the name of the nor the names of its
|
|
* contributors may be used to endorse or promote products derived from
|
|
* this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "wmfs.h"
|
|
|
|
static int
|
|
menu_get_longer_string(MenuItem *mi, int nitem)
|
|
{
|
|
int i, w, l = 0;
|
|
|
|
for(i = 0; i < nitem; ++i)
|
|
if((w = textw(mi[i].name)) > l)
|
|
l = w;
|
|
|
|
return l;
|
|
}
|
|
|
|
static bool
|
|
menu_get_checkstring_needed(MenuItem *mi, int nitem)
|
|
{
|
|
(void)mi;
|
|
(void)nitem;
|
|
return True;
|
|
}
|
|
|
|
static void
|
|
menu_draw_item_name(Menu *menu, int item, BarWindow *winitem[], int chcklen)
|
|
{
|
|
int x;
|
|
int width = menu_get_longer_string(menu->item, menu->nitem) + chcklen + PAD / 3;
|
|
|
|
switch(menu->align)
|
|
{
|
|
case MA_Left:
|
|
x = chcklen + PAD / 2;
|
|
break;
|
|
case MA_Right:
|
|
x = width - textw(menu->item[item].name) + PAD * 3 / 2;
|
|
break;
|
|
default:
|
|
case MA_Center:
|
|
x = (width - (chcklen + PAD / 3)) / 2 - textw(menu->item[item].name) / 2 + chcklen + PAD / 3;
|
|
break;
|
|
}
|
|
barwin_draw_image_ofset_text(winitem[item], x, FHINFOBAR, menu->item[item].name, chcklen + PAD / 2, 0);
|
|
|
|
if(menu->item[item].check)
|
|
if(menu->item[item].check(menu->item[item].cmd))
|
|
barwin_draw_image_ofset_text(winitem[item], PAD / 3, FHINFOBAR, conf.selected_layout_symbol, PAD / 3, 0);
|
|
|
|
if(menu->item[item].submenu)
|
|
barwin_draw_text(winitem[item], width + PAD * 2, FHINFOBAR, ">");
|
|
|
|
return;
|
|
}
|
|
|
|
static bool
|
|
menu_activate_item(Menu *menu, int i)
|
|
{
|
|
int j, x, y;
|
|
int chcklen = 0;
|
|
if(menu_get_checkstring_needed(menu->item, menu->nitem))
|
|
chcklen = textw(conf.selected_layout_symbol) + PAD / 3;
|
|
|
|
if(menu->item[i].submenu)
|
|
{
|
|
for(j = 0; j < conf.nmenu; ++j)
|
|
if(!strcmp(menu->item[i].submenu, conf.menu[j].name))
|
|
{
|
|
y = menu->y + ((i - 1) * INFOBARH + PAD) - SHADH * 2;
|
|
x = menu->x + menu_get_longer_string(menu->item, menu->nitem) + chcklen + textw(">") + PAD * 3;
|
|
|
|
menu_draw(conf.menu[j], x, y);
|
|
|
|
return True;
|
|
}
|
|
}
|
|
else if(menu->item[i].func)
|
|
{
|
|
menu->item[i].func(menu->item[i].cmd);
|
|
|
|
return True;
|
|
}
|
|
|
|
return False;
|
|
}
|
|
|
|
static void
|
|
menu_focus_item(Menu *menu, int item, BarWindow *winitem[])
|
|
{
|
|
int i;
|
|
|
|
int chcklen = 0;
|
|
if(menu_get_checkstring_needed(menu->item, menu->nitem))
|
|
chcklen = textw(conf.selected_layout_symbol) + PAD / 3;
|
|
|
|
menu->focus_item = item;
|
|
|
|
if(menu->focus_item > menu->nitem - 1)
|
|
menu->focus_item = 0;
|
|
else if(menu->focus_item < 0)
|
|
menu->focus_item = menu->nitem - 1;
|
|
|
|
for(i = 0; i < menu->nitem; ++i)
|
|
{
|
|
winitem[i]->fg = ((i == menu->focus_item) ? menu->colors.focus.fg : menu->colors.normal.fg);
|
|
winitem[i]->bg = ((i == menu->focus_item) ? menu->colors.focus.bg : menu->colors.normal.bg);
|
|
|
|
barwin_refresh_color(winitem[i]);
|
|
menu_draw_item_name(menu, i, winitem, chcklen);
|
|
barwin_refresh(winitem[i]);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
static bool
|
|
menu_manage_event(XEvent *ev, Menu *menu, BarWindow *winitem[])
|
|
{
|
|
int i, c = 0;
|
|
KeySym ks;
|
|
bool quit = False;
|
|
char acc = 0;
|
|
|
|
switch(ev->type)
|
|
{
|
|
/* Mouse events */
|
|
case ButtonPress:
|
|
/* Execute the function linked with the item */
|
|
for(i = 0; i < menu->nitem; ++i)
|
|
{
|
|
if(ev->xbutton.window == winitem[i]->win
|
|
&& (ev->xbutton.button == Button1 || ev->xbutton.button == Button2))
|
|
quit = menu_activate_item(menu, i);
|
|
else if(ev->xbutton.window != winitem[i]->win)
|
|
++c;
|
|
else if(ev->xbutton.button == Button4)
|
|
menu_focus_item(menu, menu->focus_item - 1, winitem);
|
|
else if(ev->xbutton.button == Button5)
|
|
menu_focus_item(menu, menu->focus_item + 1, winitem);
|
|
}
|
|
|
|
/* If the clicked window is not one of menu wins (items), quit. */
|
|
if(c == i)
|
|
quit = True;
|
|
|
|
break;
|
|
|
|
/* Keys */
|
|
case KeyPress:
|
|
XLookupString(&ev->xkey, NULL, 0, &ks, 0);
|
|
switch(ks)
|
|
{
|
|
case XK_Up:
|
|
menu_focus_item(menu, menu->focus_item - 1, winitem);
|
|
break;
|
|
|
|
case XK_Down:
|
|
menu_focus_item(menu, menu->focus_item + 1, winitem);
|
|
break;
|
|
|
|
case XK_Return:
|
|
quit = menu_activate_item(menu, menu->focus_item);
|
|
break;
|
|
|
|
case XK_Escape:
|
|
quit = True;
|
|
break;
|
|
|
|
}
|
|
break;
|
|
|
|
/* Focus (with mouse) management */
|
|
case EnterNotify:
|
|
/* For focus an item with the mouse */
|
|
for(i = 0; i < menu->nitem; ++i)
|
|
if(ev->xcrossing.window == winitem[i]->win)
|
|
{
|
|
acc = 1;
|
|
menu_focus_item(menu, i, winitem);
|
|
if(menu->item[i].submenu)
|
|
menu_activate_item(menu, menu->focus_item);
|
|
}
|
|
|
|
if(!acc)
|
|
{
|
|
if(ev->xcrossing.window)
|
|
XSendEvent(dpy, ev->xcrossing.window, False, StructureNotifyMask, ev);
|
|
|
|
return True;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if(ev->type < nevent && ev->type > 0)
|
|
HANDLE_EVENT(ev);
|
|
break;
|
|
}
|
|
|
|
XNextEvent(dpy, ev);
|
|
|
|
return quit;
|
|
}
|
|
|
|
void
|
|
uicb_menu(uicb_t cmd)
|
|
{
|
|
int i, d, u, x, y;
|
|
Window w;
|
|
|
|
if(!strcmp(cmd, "menulayout"))
|
|
menu_draw(menulayout, menulayout.x, menulayout.y);
|
|
|
|
for(i = 0; i < conf.nmenu; ++i)
|
|
if(!strcmp(cmd, conf.menu[i].name))
|
|
{
|
|
if(conf.menu[i].place_at_mouse)
|
|
{
|
|
XQueryPointer(dpy, ROOT, &w, &w, &x, &y, &d, &d, (uint *)&u);
|
|
conf.menu[i].x = x;
|
|
conf.menu[i].y = y;
|
|
}
|
|
else
|
|
{
|
|
screen_get_sel();
|
|
x = conf.menu[i].x + spgeo[selscreen].x;
|
|
y = conf.menu[i].y + spgeo[selscreen].y;
|
|
}
|
|
menu_draw(conf.menu[i], x, y);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
menu_clear(Menu *menu)
|
|
{
|
|
free(menu->item);
|
|
menu->nitem = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
menu_init(Menu *menu, char *name, int nitem, uint bg_f, char *fg_f, uint bg_n, char *fg_n)
|
|
{
|
|
/* Item */
|
|
menu->nitem = nitem;
|
|
menu->item = xcalloc(nitem, sizeof(*menu->item));
|
|
menu->name = name;
|
|
|
|
/* Colors */
|
|
menu->colors.focus.bg = bg_f;
|
|
menu->colors.focus.fg = fg_f;
|
|
menu->colors.normal.bg = bg_n;
|
|
menu->colors.normal.fg = fg_n;
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
menu_new_item(MenuItem *mi, char *name, void *func, char *cmd)
|
|
{
|
|
mi->name = name;
|
|
mi->func = func;
|
|
mi->cmd = cmd;
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
menu_draw(Menu menu, int x, int y)
|
|
{
|
|
int i, width, height, out;
|
|
XEvent ev;
|
|
BarWindow *item[menu.nitem];
|
|
BarWindow *frame;
|
|
|
|
int chcklen = 0;
|
|
if(menu_get_checkstring_needed(menu.item, menu.nitem))
|
|
chcklen = textw(conf.selected_layout_symbol) + PAD / 3;
|
|
|
|
width = menu_get_longer_string(menu.item, menu.nitem) + chcklen + textw(">") + PAD * 3;
|
|
height = menu.nitem * (INFOBARH - SHADH);
|
|
|
|
/* Frame barwin */
|
|
screen_get_sel();
|
|
|
|
if((out = x + width - MAXW) > 0)
|
|
x -= out;
|
|
if((out = y + height - MAXH) > 0)
|
|
y -= out;
|
|
|
|
frame = barwin_create(ROOT, x, y, width + SHADH, height + SHADH * 3,
|
|
menu.colors.normal.bg, menu.colors.normal.fg, False, False, True);
|
|
|
|
barwin_map(frame);
|
|
barwin_map_subwin(frame);
|
|
barwin_refresh_color(frame);
|
|
|
|
for(i = 0; i < menu.nitem; ++i)
|
|
{
|
|
item[i] = barwin_create(frame->win,
|
|
SHADH,
|
|
(i * (INFOBARH - SHADH) + SHADH),
|
|
width - SHADH,
|
|
INFOBARH,
|
|
menu.colors.normal.bg,
|
|
menu.colors.normal.fg,
|
|
True, False, False);
|
|
|
|
barwin_map(item[i]);
|
|
barwin_refresh_color(item[i]);
|
|
menu_draw_item_name(&menu, i, item, chcklen);
|
|
barwin_refresh(item[i]);
|
|
}
|
|
|
|
/* Select the first item */
|
|
menu_focus_item(&menu, 0, item);
|
|
|
|
XGrabKeyboard(dpy, ROOT, True, GrabModeAsync, GrabModeAsync, CurrentTime);
|
|
|
|
while(!menu_manage_event(&ev, &menu, item));
|
|
|
|
XUngrabKeyboard(dpy, CurrentTime);
|
|
|
|
for(i = 0; i < menu.nitem; ++i)
|
|
barwin_delete(item[i]);
|
|
barwin_delete(frame);
|
|
|
|
return;
|
|
}
|