Files
amiwm-neo/menu.c
Adrian Chadd 18e858ae80 [amiwm] MVP for initial battery stuff
Totally not ready for prod, but at least I'm making progress
figuring out how to add extra bits to this thing.

* add a second widget to the menu bar, next to the title bar
* add a new module command to update the battery information
* add a freebsd specific Battery module, not linked into the
  build right now.
* amiwm will print out whenever we get battery information from the
  module.

Right now I'm trying to figure out how to get some kind of periodic
background event into the module main loop.  Launcher does it
using cx_broker(), but I dunno if I can adapt that pattern here.
2022-04-25 21:28:49 -07:00

998 lines
28 KiB
C

#include <X11/Xlib.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <time.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "alloc.h"
#include "drawinfo.h"
#include "prefs.h"
#include "screen.h"
#include "client.h"
#include "icon.h"
#include "version.h"
#define ABOUT_STRING(LF) \
" " LF "version "VERSION LF "by Marcus Comstedt" LF \
"<marcus@lysator.liu.se>" LF " " LF \
"Contributions by Scott Lawrence" LF "<amiwm10@umlautllama.com>" LF \
"and Nicolas Sipieter " LF "<freeeaks@gmail.com>" LF " " LF \
"Some small contributions by redsPL" LF "<reds@sakamoto.pl>" LF " " LF
#ifdef AMIGAOS
#include <pragmas/xlib_pragmas.h>
extern struct Library *XLibBase;
#endif
#ifdef AMIGAOS
#define BIN_PREFIX AMIWM_HOME
#else
#define BIN_PREFIX AMIWM_HOME"/"
#endif
#define CHECKIT 1
#define CHECKED 2
#define DISABLED 4
extern Display *dpy;
extern Cursor wm_curs;
extern XContext screen_context, client_context;
extern Client *activeclient;
extern void select_all_icons(Scrn *i);
extern void mod_menuselect(struct module *, int, int, int);
extern void setfocus(Window);
extern void flushmodules();
extern void wberror(Scrn *, char *);
Scrn *mbdclick=NULL, *mbdscr=NULL;
static struct ToolItem {
struct ToolItem *next;
const char *name, *cmd;
char hotkey;
int level;
struct Menu *submenu;
} *firsttoolitem=NULL, *lasttoolitem=NULL;
static struct Item {
struct Item *next;
Window win;
const char *text;
int textlen;
char hotkey, flags;
struct Menu *menu, *sub;
struct module *owner;
struct Item *mod_chain;
} *activeitem=NULL, *activesubitem=NULL;
static struct Menu {
struct Menu *next;
struct Item *item;
Window win, parent;
const char *title;
int titlelen;
int width, height;
char flags;
struct Item *firstitem, *lastitem;
} *activemenu=NULL, *activesubmenu=NULL;
#ifdef AMIGAOS
void spawn(const char *cmd)
{
char *line=malloc(strlen(cmd)+12);
if(line) {
sprintf(line, "RUN <>NIL: %s", cmd);
system(line);
free(line);
}
}
#else
void spawn(const char *cmd)
{
extern char *x_server;
#ifdef HAVE_ALLOCA
char *line=alloca(strlen(x_server)+strlen(cmd)+28);
#else
char *line=malloc(strlen(x_server)+strlen(cmd)+28);
if(line) {
#endif
char *dot;
sprintf(line, "DISPLAY='%s", x_server);
if(!(dot=strrchr(line, '.')) || strchr(dot, ':'))
dot=line+strlen(line);
sprintf(dot, ".%d' %s &", scr->number, cmd);
#ifdef __ultrix
{
int pid, status;
if ((pid = fork ()) == 0) {
(void) setsid();
execl ("/bin/sh", "sh", "-c", line, 0);
} else
waitpid (pid, &status, 0);
}
#else
system(line);
#endif
#ifndef HAVE_ALLOCA
free(line);
}
#endif
}
#endif
void add_toolitem(const char *n, const char *c, const char *k, int l)
{
struct ToolItem *ti;
if((ti=malloc(sizeof(struct ToolItem)))) {
ti->name=n;
ti->cmd=c;
if(k)
ti->hotkey=k[0];
else
ti->hotkey=0;
ti->level=l;
ti->next=NULL;
if(lasttoolitem)
lasttoolitem->next=ti;
else
firsttoolitem=ti;
lasttoolitem=ti;
}
}
static void menu_layout(struct Menu *menu)
{
XSetWindowAttributes attr;
XWindowAttributes attr2;
int w, x, y;
struct Item *item;
if(menu->win) {
XGetWindowAttributes(dpy, menu->win, &attr2);
x=attr2.x; y=scr->bh-2;
} else {
XGetWindowAttributes(dpy, menu->item->win, &attr2);
x=attr2.x+attr2.width-scr->hotkeyspace+9; y=attr2.y-2;
XGetWindowAttributes(dpy, menu->item->menu->parent, &attr2);
x+=attr2.x; y+=attr2.y;
}
menu->width=menu->height=0;
for(item=menu->firstitem; item; item=item->next) {
if(item->text)
menu->height+=scr->dri.dri_Ascent+scr->dri.dri_Descent+1;
else
menu->height+=6;
#ifdef USE_FONTSETS
w=XmbTextEscapement(scr->dri.dri_FontSet, item->text, item->textlen)+2;
#else
w=XTextWidth(scr->dri.dri_Font, item->text, item->textlen)+2;
#endif
if(item->hotkey)
w+=scr->hotkeyspace;
if(item->flags&CHECKIT)
w+=scr->checkmarkspace;
if(item->sub)
w+=scr->subspace;
if(w>menu->width)
menu->width=w;
}
menu->width+=6;
menu->height+=2;
attr.override_redirect=True;
attr.background_pixel=scr->dri.dri_Pens[BARBLOCKPEN];
attr.border_pixel=scr->dri.dri_Pens[BARDETAILPEN];
menu->parent=XCreateWindow(dpy, scr->back, x, y,
menu->width, menu->height, 1,
CopyFromParent, InputOutput, CopyFromParent,
CWOverrideRedirect|CWBackPixel|CWBorderPixel,
&attr);
XSaveContext(dpy, menu->parent, screen_context, (XPointer)scr);
XSelectInput(dpy, menu->parent, ExposureMask);
w=1;
for(item=menu->firstitem; item; item=item->next) {
int h=(item->text? scr->dri.dri_Ascent+scr->dri.dri_Descent+1:6);
item->win=XCreateWindow(dpy, menu->parent, 3, w, menu->width-6, h, 0,
CopyFromParent, InputOutput, CopyFromParent,
CWOverrideRedirect|CWBackPixel, &attr);
XSaveContext(dpy, item->win, screen_context, (XPointer)scr);
w+=h;
XSelectInput(dpy, item->win, ExposureMask|ButtonReleaseMask|
EnterWindowMask|LeaveWindowMask);
}
XMapSubwindows(dpy, menu->parent);
}
static struct Menu *add_menu(const char *name, char flags)
{
struct Menu *menu=calloc(1, sizeof(struct Menu));
XSetWindowAttributes attr;
int w;
if(menu) {
attr.override_redirect=True;
attr.background_pixel=scr->dri.dri_Pens[BARBLOCKPEN];
#ifdef USE_FONTSETS
w=XmbTextEscapement(scr->dri.dri_FontSet, name, menu->titlelen=strlen(name))+8;
#else
w=XTextWidth(scr->dri.dri_Font, name, menu->titlelen=strlen(name))+8;
#endif
menu->win=XCreateWindow(dpy, scr->menubarparent, scr->menuleft, 0, w, scr->bh-1, 0,
CopyFromParent, InputOutput, CopyFromParent,
CWOverrideRedirect|CWBackPixel, &attr);
XSaveContext(dpy, menu->win, screen_context, (XPointer)scr);
XMapWindow(dpy, menu->win);
XSelectInput(dpy, menu->win, ExposureMask|EnterWindowMask|LeaveWindowMask);
scr->menuleft+=w+6;
menu->flags=flags;
menu->title=name;
menu->item=NULL;
menu->firstitem=menu->lastitem=NULL;
menu->next=scr->firstmenu;
scr->firstmenu=menu;
}
return menu;
}
static struct Item *add_item(struct Menu *m, const char *name, char key,
char flags)
{
struct Item *item=calloc(1, sizeof(struct Item));
if(item) {
if(name) {
item->text=name;
item->textlen=strlen(name);
} else item->text=NULL;
item->hotkey=key;
item->flags=flags;
item->menu=m;
item->sub=NULL;
item->next=NULL;
if(m->lastitem)
m->lastitem->next=item;
else
m->firstitem=item;
m->lastitem=item;
}
return item;
}
static struct Menu *sub_menu(struct Item *i, char flags)
{
struct Menu *menu=calloc(1, sizeof(struct Menu));
if(menu) {
menu->flags=flags;
menu->title=NULL;
menu->firstitem=menu->lastitem=NULL;
menu->next=NULL;
menu->item=i;
i->sub=menu;
}
return menu;
}
void redraw_menu(struct Menu *m, Window w)
{
int active=(m==activemenu && !(m->flags & DISABLED));
XSetForeground(dpy, scr->menubargc, scr->dri.dri_Pens[active?BARBLOCKPEN:
BARDETAILPEN]);
XSetBackground(dpy, scr->menubargc, scr->dri.dri_Pens[active?BARDETAILPEN:
BARBLOCKPEN]);
#ifdef USE_FONTSETS
XmbDrawImageString(dpy, w, scr->dri.dri_FontSet,
scr->menubargc, 4, 1+scr->dri.dri_Ascent,
m->title, m->titlelen);
#else
XDrawImageString(dpy, w, scr->menubargc, 4, 1+scr->dri.dri_Ascent,
m->title, m->titlelen);
#endif
}
void redraw_item(struct Item *i, Window w)
{
struct Menu *m=i->menu;
int s=scr->dri.dri_Ascent>>1;
if((i==activeitem || i==activesubitem) && !(i->flags&DISABLED)) {
XSetForeground(dpy, scr->menubargc, scr->dri.dri_Pens[BARBLOCKPEN]);
XSetBackground(dpy, scr->menubargc, scr->dri.dri_Pens[BARDETAILPEN]);
} else {
XSetForeground(dpy, scr->menubargc, scr->dri.dri_Pens[BARDETAILPEN]);
XSetBackground(dpy, scr->menubargc, scr->dri.dri_Pens[BARBLOCKPEN]);
}
if(i->text)
#ifdef USE_FONTSETS
XmbDrawImageString(dpy, w, scr->dri.dri_FontSet,
scr->menubargc, (i->flags&CHECKIT)?1+scr->checkmarkspace:1,
scr->dri.dri_Ascent+1, i->text, i->textlen);
#else
XDrawImageString(dpy, w, scr->menubargc, (i->flags&CHECKIT)?1+scr->checkmarkspace:1,
scr->dri.dri_Ascent+1, i->text, i->textlen);
#endif
else
XFillRectangle(dpy, w, scr->menubargc, 2, 2, m->width-10, 2);
if(i->sub) {
int x=m->width-6-scr->hotkeyspace-1+8;
#ifdef USE_FONTSETS
#ifdef HAVE_XUTF8DRAWIMAGESTRING
Xutf8DrawImageString(dpy, w, scr->dri.dri_FontSet,
scr->menubargc, x+scr->dri.dri_Ascent+1,
1+scr->dri.dri_Ascent, "\302\273", 2);
#else
XmbDrawImageString(dpy, w, scr->dri.dri_FontSet,
scr->menubargc, x+scr->dri.dri_Ascent+1,
1+scr->dri.dri_Ascent, "»", 1);
#endif
#else
XDrawImageString(dpy, w, scr->menubargc, x+scr->dri.dri_Ascent+1,
1+scr->dri.dri_Ascent, "»", 1);
#endif
} else if(i->hotkey) {
int x=m->width-6-scr->hotkeyspace-1+8;
XDrawLine(dpy, w, scr->menubargc, x, 1+s, x+s, 1);
XDrawLine(dpy, w, scr->menubargc, x+s, 1, x+s+s, 1+s);
XDrawLine(dpy, w, scr->menubargc, x+s+s, 1+s, x+s, 1+s+s);
XDrawLine(dpy, w, scr->menubargc, x+s, 1+s+s, x, 1+s);
#ifdef USE_FONTSETS
XmbDrawImageString(dpy, w, scr->dri.dri_FontSet,
scr->menubargc, x+scr->dri.dri_Ascent+1,
1+scr->dri.dri_Ascent, &i->hotkey, 1);
#else
XDrawImageString(dpy, w, scr->menubargc, x+scr->dri.dri_Ascent+1,
1+scr->dri.dri_Ascent, &i->hotkey, 1);
#endif
}
if(i->flags&CHECKED) {
XDrawLine(dpy, w, scr->menubargc, 0, s, s, scr->dri.dri_Ascent);
XDrawLine(dpy, w, scr->menubargc, s, scr->dri.dri_Ascent, s+s, 0);
}
if(i->flags&DISABLED) {
XSetStipple(dpy, scr->menubargc, scr->disabled_stipple);
XSetFillStyle(dpy, scr->menubargc, FillStippled);
XSetForeground(dpy, scr->menubargc, scr->dri.dri_Pens[BARBLOCKPEN]);
XFillRectangle(dpy, w, scr->menubargc, 0, 0,
m->width-6, scr->dri.dri_Ascent+scr->dri.dri_Descent+1);
XSetFillStyle(dpy, scr->menubargc, FillSolid);
}
}
void createmenubar()
{
XSetWindowAttributes attr;
struct Menu *m, *sm1;
// struct Menu *sm2, *sm3;
struct ToolItem *ti;
GC gc;
scr->firstmenu = NULL;
attr.override_redirect=True;
attr.background_pixel=scr->dri.dri_Pens[BARBLOCKPEN];
scr->menubar=XCreateWindow(dpy, scr->back, 0, 0, scr->width, scr->bh, 0,
CopyFromParent,
InputOutput, CopyFromParent,
CWOverrideRedirect|CWBackPixel,
&attr);
XSaveContext(dpy, scr->menubar, screen_context, (XPointer)scr);
scr->menubarparent=XCreateWindow(dpy, scr->menubar, 0, 0, scr->width,
scr->bh-1, 0,
CopyFromParent, InputOutput, CopyFromParent,
CWOverrideRedirect|CWBackPixel, &attr);
XSaveContext(dpy, scr->menubarparent, screen_context, (XPointer)scr);
attr.background_pixel=scr->dri.dri_Pens[BACKGROUNDPEN];
scr->menubardepth=XCreateWindow(dpy, scr->menubar, scr->width-23,
0, 23, scr->bh, 0,
CopyFromParent, InputOutput, CopyFromParent,
CWOverrideRedirect|CWBackPixel, &attr);
XSaveContext(dpy, scr->menubardepth, screen_context, (XPointer)scr);
scr->disabled_stipple=XCreatePixmap(dpy, scr->back, 6, 2, 1);
gc=XCreateGC(dpy, scr->disabled_stipple, 0, NULL);
XSetForeground(dpy, gc, 0);
XFillRectangle(dpy, scr->disabled_stipple, gc, 0, 0, 6, 2);
XSetForeground(dpy, gc, 1);
XDrawPoint(dpy, scr->disabled_stipple, gc, 0, 0);
XDrawPoint(dpy, scr->disabled_stipple, gc, 3, 1);
XFreeGC(dpy, gc);
scr->menubargc=XCreateGC(dpy, scr->menubar, 0, NULL);
#ifndef USE_FONTSETS
XSetFont(dpy, scr->menubargc, scr->dri.dri_Font->fid);
#endif
XSetBackground(dpy, scr->menubargc, scr->dri.dri_Pens[BARBLOCKPEN]);
XSelectInput(dpy, scr->menubar, ExposureMask|ButtonPressMask|ButtonReleaseMask);
XSelectInput(dpy, scr->menubardepth, ExposureMask|ButtonPressMask|
ButtonReleaseMask|EnterWindowMask|LeaveWindowMask);
XMapWindow(dpy, scr->menubardepth);
XMapWindow(dpy, scr->menubar);
scr->hotkeyspace=8+1+scr->dri.dri_MaxBoundsWidth+
scr->dri.dri_Ascent;
scr->checkmarkspace=4+scr->dri.dri_Ascent;
scr->subspace=scr->hotkeyspace-scr->dri.dri_Ascent;
scr->menuleft=4;
m=add_menu("Workbench", 0);
//add_item(m,"Backdrop",'B',CHECKIT|CHECKED|DISABLED);
add_item(m,"Execute Command...",'E',0);
add_item(m,"Configuration",'C',0);
add_item(m,"Redraw All",0,0);
//add_item(m,"Update All",0,DISABLED);
//add_item(m,"Last Message",0,DISABLED);
add_item(m,NULL,0,DISABLED);
add_item(m,"About...",'?',0);
add_item(m,"Quit...",'Q',0);
menu_layout(m);
/*m=add_menu("Window", 0);
add_item(m,"New Drawer",'N',DISABLED);
add_item(m,"Open Parent",0,DISABLED);
add_item(m,"Close",'K',DISABLED);
add_item(m,"Update",0,DISABLED);
add_item(m,"Select Contents",'A',0);
add_item(m,"Clean Up",'.',0);
sm1=sub_menu(add_item(m,"Snapshot",0,DISABLED),0);
add_item(sm1, "Window",0,DISABLED);
add_item(sm1, "All",0,DISABLED);
sm2=sub_menu(add_item(m,"Show",0,DISABLED),0);
add_item(sm2, "Only Icons",0,CHECKIT|CHECKED|DISABLED);
add_item(sm2, "All Files",'V',CHECKIT|DISABLED);
sm3=sub_menu(add_item(m,"View By",0,DISABLED),0);
add_item(sm3, "Icon",0,CHECKIT|CHECKED|DISABLED);
add_item(sm3, "Name",0,CHECKIT|DISABLED);
add_item(sm3, "Date",0,CHECKIT|DISABLED);
add_item(sm3, "Size",0,CHECKIT|DISABLED);
menu_layout(m);
menu_layout(sm1);
menu_layout(sm2);
menu_layout(sm3);*/
/*m=add_menu("Icons", DISABLED);
add_item(m,"Open",'O',DISABLED);
add_item(m,"Copy",'C',DISABLED);
add_item(m,"Rename...",'R',DISABLED);
add_item(m,"Information...",'I',DISABLED);
add_item(m,"Snapshot",'S',DISABLED);
add_item(m,"UnSnapshot",'U',DISABLED);
add_item(m,"Leave Out",'L',DISABLED);
add_item(m,"Put Away",'P',DISABLED);
add_item(m,NULL,0,DISABLED);
add_item(m,"Delete...",'D',DISABLED);
add_item(m,"Format Disk...",0,DISABLED);
add_item(m,"Empty Trash",0,DISABLED);
menu_layout(m);*/
m=add_menu("Tools", 0);
#ifdef AMIGAOS
add_item(m,"ResetWB",0,DISABLED);
#else
add_item(m,"ResetWB",0,0);
#endif
for(ti=firsttoolitem; ti; ti=ti->next)
if(ti->level<0)
ti->submenu=sm1=sub_menu(add_item(m,ti->name,ti->hotkey,0),0);
else {
ti->submenu=NULL;
add_item((ti->level? sm1:m), ti->name,ti->hotkey,(ti->cmd? 0:DISABLED));
}
menu_layout(m);
for(ti=firsttoolitem; ti; ti=ti->next)
if(ti->submenu) {
menu_layout(ti->submenu);
ti->submenu=NULL;
}
if(prefs.screenmenu) {
m=add_menu("Screens",0);
add_item(m,"New Screen",0,0);
add_item(m,"Delete Screen",0,0);
menu_layout(m);
}
}
/*
* Redraw the menu bar and its components.
*
* This takes in the target window, which may be the basic menubar,
* a clicked-on menu, or the depth widget.
*/
void redrawmenubar(Window w)
{
static const char defaultTimeFormat[] = "%c";
int widget_rhs;
struct Menu *m;
struct Item *item;
if(!w)
return;
if(w==scr->menubar) {
/* Menubar itself */
XSetForeground(dpy, scr->menubargc, scr->dri.dri_Pens[BARDETAILPEN]);
XSetBackground(dpy, scr->menubargc, scr->dri.dri_Pens[BARBLOCKPEN]);
#ifdef USE_FONTSETS
XmbDrawImageString(dpy, w, scr->dri.dri_FontSet,
scr->menubargc, 4, 1+scr->dri.dri_Ascent,
scr->title, strlen(scr->title));
#else
XDrawImageString(dpy, w, scr->menubargc, 4, 1+scr->dri.dri_Ascent,
scr->title, strlen(scr->title));
#endif
XSetForeground(dpy, scr->menubargc, scr->dri.dri_Pens[BARTRIMPEN]);
XDrawLine(dpy, w, scr->menubargc, 0, scr->bh-1, scr->width-1, scr->bh-1);
/* Widgets start here and move to the left */
widget_rhs = (scr->width - 30);
/*
* Update the title bar clock if it's enabled.
*/
if( prefs.titlebarclock )
{
char clockbuf[512];
const char * fmt = defaultTimeFormat;
time_t the_time;
int l;
if( prefs.titleclockformat ) fmt = prefs.titleclockformat;
time( &the_time );
strftime( clockbuf, 512, fmt, localtime( &the_time ) );
#ifdef USE_FONTSETS
l = XmbTextEscapement(scr->dri.dri_FontSet, clockbuf, strlen(clockbuf));
XmbDrawImageString(dpy, w, scr->dri.dri_FontSet, scr->menubargc,
widget_rhs - l, 1+scr->dri.dri_Ascent,
clockbuf, strlen(clockbuf));
#else
l = XTextWidth(scr->dri.dri_Font, clockbuf, strlen(clockbuf));
XDrawImageString( dpy, w, scr->menubargc, widget_rhs - l,
1+scr->dri.dri_Ascent, clockbuf, strlen(clockbuf));
#endif
widget_rhs = widget_rhs - l - 8; // 8 = padding
}
/*
* Update the battery indicator if it's enabled.
*/
if (1) {
char battery_buf[512];
int l;
sprintf(battery_buf, "| Battery |");
#ifdef USE_FONTSETS
l = XmbTextEscapement(scr->dri.dri_FontSet, battery_buf, strlen(battery_buf));
XmbDrawImageString(dpy, w, scr->dri.dri_FontSet, scr->menubargc,
widget_rhs - l, 1+scr->dri.dri_Ascent,
battery_buf, strlen(battery_buf));
#else
l = XTextWidth(scr->dri.dri_Font, battery_buf, strlen(battery_buf));
XDrawImageString( dpy, w, scr->menubargc, widget_rhs - l,
1+scr->dri.dri_Ascent, battery_buf, strlen(battery_buf));
#endif
widget_rhs = widget_rhs - l - 8; // 8 = padding
}
} else if(w==scr->menubardepth) {
/* Menubar depth widget */
if(!mbdclick) {
XSetForeground(dpy, scr->menubargc, scr->dri.dri_Pens[SHADOWPEN]);
XDrawRectangle(dpy, w, scr->menubargc, 4, scr->h2, 10, scr->h6-scr->h2);
}
XSetForeground(dpy, scr->menubargc, scr->dri.dri_Pens[SHINEPEN]);
XFillRectangle(dpy, w, scr->menubargc, 8, scr->h4, 10, scr->h8-scr->h4);
XSetForeground(dpy, scr->menubargc, scr->dri.dri_Pens[SHADOWPEN]);
XDrawRectangle(dpy, w, scr->menubargc, 8, scr->h4, 10, scr->h8-scr->h4);
if(mbdclick)
XDrawRectangle(dpy, w, scr->menubargc, 4, scr->h2, 10, scr->h6-scr->h2);
XSetForeground(dpy, scr->menubargc, scr->dri.dri_Pens[mbdclick?SHADOWPEN:SHINEPEN]);
XDrawLine(dpy, w, scr->menubargc, 0, 0, 22, 0);
XDrawLine(dpy, w, scr->menubargc, 0, 0, 0, scr->bh-2);
XSetForeground(dpy, scr->menubargc, scr->dri.dri_Pens[mbdclick?SHINEPEN:SHADOWPEN]);
XDrawLine(dpy, w, scr->menubargc, 0, scr->bh-1, 22, scr->bh-1);
XDrawLine(dpy, w, scr->menubargc, 22, 0, 22, scr->bh-1);
} else {
/* One of the menus is being displayed */
for(m=scr->firstmenu; m; m=m->next)
if(m->win==w)
redraw_menu(m, w);
if(activemenu) {
for(item=activemenu->firstitem; item; item=item->next)
if(item->win==w)
redraw_item(item, w);
if(w==activemenu->parent) {
XSetForeground(dpy, scr->menubargc, scr->dri.dri_Pens[BARDETAILPEN]);
XDrawLine(dpy, w, scr->menubargc, 0, 0, 0, activemenu->height-1);
XDrawLine(dpy, w, scr->menubargc, activemenu->width-1, 0,
activemenu->width-1, activemenu->height-1);
}
}
if(activesubmenu) {
for(item=activesubmenu->firstitem; item; item=item->next)
if(item->win==w)
redraw_item(item, w);
if(w==activesubmenu->parent) {
XSetForeground(dpy, scr->menubargc, scr->dri.dri_Pens[BARDETAILPEN]);
XDrawLine(dpy, w, scr->menubargc, 0, 0, 0, activesubmenu->height-1);
XDrawLine(dpy, w, scr->menubargc, activesubmenu->width-1, 0,
activesubmenu->width-1, activesubmenu->height-1);
}
}
}
}
static void leave_item(struct Item *i, Window w)
{
if(i==activesubitem)
activesubitem=NULL;
if(i==activeitem) {
if(activesubmenu && i->sub==activesubmenu) {
return;
} else {
activeitem=NULL;
}
}
XSetWindowBackground(dpy, i->win, scr->dri.dri_Pens[BARBLOCKPEN]);
XClearWindow(dpy, i->win);
redraw_item(i, i->win);
}
static void enter_item(struct Item *i, Window w)
{
if(activesubitem)
leave_item(activesubitem, activesubitem->win);
if(activesubmenu!=i->sub && i->menu!=activesubmenu) {
if(activesubmenu)
XUnmapWindow(dpy, activesubmenu->parent);
if(i->sub)
XMapRaised(dpy, i->sub->parent);
activesubmenu=i->sub;
}
if(activeitem && !(activeitem->sub && i->menu==activeitem->sub))
leave_item(activeitem, activeitem->win);
if(!(i->flags&DISABLED)) {
if(activeitem)
activesubitem=i;
else
activeitem=i;
XSetWindowBackground(dpy, i->win, scr->dri.dri_Pens[BARDETAILPEN]);
XClearWindow(dpy, i->win);
redraw_item(i, i->win);
}
}
static void enter_menu(struct Menu *m, Window w)
{
if(m!=activemenu) {
struct Menu *oa=activemenu;
if(activesubitem)
leave_item(activeitem, activesubitem->win);
if(activesubmenu) {
XUnmapWindow(dpy, activesubmenu->parent);
activesubmenu=NULL;
}
if(activeitem)
leave_item(activeitem, activeitem->win);
if(!(m->flags & DISABLED))
XSetWindowBackground(dpy, w, scr->dri.dri_Pens[BARDETAILPEN]);
XClearWindow(dpy, w);
redraw_menu(activemenu=m, w);
if(m->parent)
XMapRaised(dpy, m->parent);
if(oa) {
if(oa->parent)
XUnmapWindow(dpy, oa->parent);
XSetWindowBackground(dpy, oa->win, scr->dri.dri_Pens[BARBLOCKPEN]);
XClearWindow(dpy, oa->win);
redraw_menu(oa, oa->win);
}
}
}
void menubar_enter(Window w)
{
struct Menu *m;
struct Item *i;
for(m=scr->firstmenu; m; m=m->next)
if(m->win==w) {
enter_menu(m, w);
return;
}
if((m=activemenu))
for(i=m->firstitem; i; i=i->next)
if(w==i->win) {
enter_item(i, w);
return;
}
if((m=activesubmenu))
for(i=m->firstitem; i; i=i->next)
if(w==i->win) {
enter_item(i, w);
return;
}
}
void menubar_leave(Window w)
{
if(activesubitem && activesubitem->win==w)
leave_item(activesubitem, w);
if(activeitem && activeitem->win==w)
leave_item(activeitem, w);
}
void menu_on()
{
Window r, c;
int rx, ry, x, y;
unsigned int m;
if(scr->menubarparent) {
XMapRaised(dpy, scr->menubarparent);
XRaiseWindow(dpy, scr->menubar);
XGrabPointer(dpy, scr->back, True, ButtonPressMask|ButtonReleaseMask|
EnterWindowMask|LeaveWindowMask, GrabModeAsync, GrabModeAsync,
scr->back, wm_curs, CurrentTime);
XSetInputFocus(dpy, scr->menubar, RevertToParent, CurrentTime);
if(XQueryPointer(dpy, scr->menubarparent, &r, &c, &rx, &ry, &x, &y, &m))
menubar_enter(c);
}
}
void menuaction(struct Item *i, struct Item *si)
{
extern void restart_amiwm(void);
extern int screen_has_clients(void);
struct Menu *m;
struct Item *mi;
struct ToolItem *ti;
int menu=0, item=0, sub=0;
for(m=i->menu->next; m; m=m->next) menu++;
for(mi=i->menu->firstitem; mi&&mi!=i; mi=mi->next) item++;
if(i->sub) {
for(mi=i->sub->firstitem; mi&&mi!=si; mi=mi->next) sub++;
if(!mi)
sub=-1;
} else
--sub;
mi=(sub>=0? si:i);
if(mi->flags & DISABLED)
return;
if(mi->owner) {
mod_menuselect(mi->owner, menu, item, sub);
return;
}
switch(menu) {
case 0: /* Workbench */
switch(item) {
case 0:
spawn(BIN_PREFIX"executecmd");
break;
case 1:
spawn(BIN_PREFIX"config_util");
case 2:
{
XSetWindowAttributes xswa;
unsigned long mask;
Window win;
xswa.background_pixmap = None;
xswa.override_redirect = True;
xswa.backing_store = NotUseful;
xswa.save_under = False;
mask = CWBackPixmap|CWOverrideRedirect|CWBackingStore|CWSaveUnder;
win = XCreateWindow(dpy, scr->back, 0, 0, scr->width, scr->height,
0, scr->depth, InputOutput,
scr->visual, mask, &xswa);
XMapWindow(dpy, win);
XDestroyWindow(dpy, win);
}
break;
case 4:
#ifdef AMIGAOS
spawn(BIN_PREFIX"requestchoice >NIL: amiwm \""
ABOUT_STRING("*N") "\" Ok");
#else
spawn(BIN_PREFIX"requestchoice >/dev/null amiwm '"
ABOUT_STRING("\n") "' Ok");
#endif
break;
case 5:
#ifndef AMIGAOS
if(prefs.fastquit) {
#endif
exit(0);
#ifndef AMIGAOS
} else {
#ifdef HAVE_ALLOCA
char *buf=alloca(256);
#else
char buf[256];
#endif
sprintf(buf, "; export DISPLAY; ( if [ `"BIN_PREFIX"requestchoice "
"Workbench 'Do you really want\nto quit workbench?' Ok Cancel`"
" = 1 ]; then kill %d; fi; )", (int)getpid());
spawn(buf);
}
#endif
break;
}
break;
/* case 1: // Window
switch(item) {
case 4:
select_all_icons(scr);
break;
case 5:
cleanupicons();
break;
}
break; */
// case 2: /* Icons */
// break;
case 1: /* Tools */
#ifndef AMIGAOS
if(item==0)
restart_amiwm();
#endif
if(item>0) {
int it=0, si=-1;
for(ti=firsttoolitem; ti; ti=ti->next) {
if(ti->level>0)
si++;
else {
it++;
si=-1;
}
if(it==item && si==sub) break;
}
if(ti && ti->cmd) spawn(ti->cmd);
}
break;
case 2: /* Screens */
if(item==0) {
openscreen("New Screen", DefaultRootWindow(dpy));
realizescreens();
scr = get_front_scr()->upfront;
screentoback();
}
if(item==1) {
if(scr->behind == scr)
wberror(scr,"Cannot close last Screen");
else if(screen_has_clients())
wberror(scr,"Can't close a screen with running programs on it");
else
closescreen();
}
break;
}
}
void menu_off()
{
struct Menu *oa;
struct Item *oi, *osi;
if(scr->menubarparent) {
Window r,p,*children;
unsigned int nchildren;
XUngrabPointer(dpy, CurrentTime);
setfocus((activeclient && activeclient->state==NormalState?
activeclient->window:None));
XUnmapWindow(dpy, scr->menubarparent);
if(XQueryTree(dpy, scr->back, &r, &p, &children, &nchildren)) {
int n;
Client *c2;
for(n=0; n<nchildren; n++)
if((!XFindContext(dpy, children[n], client_context, (XPointer*)&c2)) &&
children[n]==c2->parent)
break;
if(n<nchildren) {
Window ws[2];
ws[0]=children[n];
ws[1]=scr->menubar;
XRestackWindows(dpy, ws, 2);
}
if(children) XFree(children);
}
}
if((osi=activesubitem))
leave_item(osi, osi->win);
if((oi=activeitem))
leave_item(oi, oi->win);
if((oa=activesubmenu)) {
activesubmenu=NULL;
if(oa->parent)
XUnmapWindow(dpy, oa->parent);
}
if((oa=activemenu)) {
activemenu=NULL;
if(oa->parent)
XUnmapWindow(dpy, oa->parent);
XSetWindowBackground(dpy, oa->win, scr->dri.dri_Pens[BARBLOCKPEN]);
XClearWindow(dpy, oa->win);
redraw_menu(oa, oa->win);
}
if(oi) {
XSync(dpy, False);
menuaction(oi, osi);
}
}
struct Item *getitembyhotkey(KeySym key)
{
struct Menu *m;
struct Item *i;
if(key) {
if(key>='a' && key<='z')
key-=0x20;
for(m=scr->firstmenu; m; m=m->next)
for(i=m->firstitem; i; i=i->next)
if(i->hotkey==key)
return i;
}
return NULL;
}
static struct Item *own_item(struct module *m, struct Item *i, struct Item *c)
{
if(i->owner)
return NULL;
i->owner = m;
i->mod_chain = c;
i->flags &= ~DISABLED;
redraw_item(i, i->win);
return i;
}
void disown_item_chain(struct module *m, struct Item *i)
{
while(i)
if(i->owner == m) {
struct Item *c = i->mod_chain;
i->owner = NULL;
i->mod_chain = NULL;
i->flags |= DISABLED;
redraw_item(i, i->win);
i = c;
} else
i=i->mod_chain;
}
struct Item *own_items(struct module *m, Scrn *s,
int menu, int item, int sub, struct Item *c)
{
struct Item *cl, *chain = NULL, *endlink = NULL;
int m0, m1, mn;
struct Menu *mm;
if(!s) return NULL;
if(menu<0) {
m0 = 0; m1 = 65535;
} else m0 = m1 = menu;
for(mn=0, mm=s->firstmenu; mm && mn <= m1; mm=mm->next, mn++)
if(mn>=m0) {
int i0, i1, in;
struct Item *ii;
if(item<0) {
i0 = 0; i1 = 65535;
} else i0 = i1 = item;
for(in=0, ii=mm->firstitem; ii && in <= i1; ii=ii->next, in++)
if(in>=i0) {
int s0, s1, sn;
struct Item *ss;
if(!(cl=own_item(m, ii, chain))) {
disown_item_chain(m, chain);
return NULL;
} else chain=cl;
if(!endlink) endlink=chain;
if(ii->sub) {
if(sub<0) {
s0 = 0; s1 = 65535;
} else s0 = s1 = sub;
for(sn=0, ss=ii->sub->firstitem; ss && sn <= i1; ss=ss->next, sn++)
if(sn>=s0) {
if(!(cl=own_item(m, ss, chain))) {
disown_item_chain(m, chain);
return NULL;
} else chain=cl;
}
}
}
}
if(endlink)
endlink->next = c;
return chain;
}