Generic_Window_Manager/menu.c

460 lines
13 KiB
C

/* Copyright 1989 GROUPE BULL -- See license conditions in file COPYRIGHT
* Copyright 1989 Massachusetts Institute of Technology
*/
/*****************************************************\
* *
* BULL WINDOW MANAGER for X11 . *
* *
* MODULE defining the Menu Wob Class *
* *
\*****************************************************/
/* include */
#include <string.h>
#include "EXTERN.h"
#include "wool.h"
#include "wl_atom.h"
#include "wl_number.h"
#include "wl_string.h"
#include "wl_list.h"
#include "gwm.h"
#include "wl_fsm.h"
#include "wl_pixmap.h"
#include "wl_cursor.h"
#include "wl_bar.h"
#include "wl_menu.h"
/* local constants */
/* external */
extern Wob NewWob();
extern Menu MenuOpen();
extern MenuClose(), MenuEventHandler(), ReconfigureMenu();
#ifdef SHAPE /* compile with -I/usr/include/X11 AND
-I/usr/include/X11/extensions to work on
machines having shapes.h in either place */
#include <shape.h>
extern MenuIsShaped(), UpdateMenuShape();
#endif /* SHAPE */
WOB_METHOD MenuClass[] = {
0, /* METHODS_ARRAY */
WobEval,
WobPrint,
WobRelease,
WobExecute,
WobSet,
WobGetCValue,
(WOB_METHOD) MenuOpen,
MenuClose,
MenuEventHandler,
(WOB_METHOD) wool_undefined_method_1,
WobGetDimensions,
(WOB_METHOD) wool_undefined_method_2,
(WOB_METHOD) wool_undefined_method_2,
ReconfigureMenu,
(WOB_METHOD) wool_undefined_method_2,
(WOB_METHOD) wool_undefined_method_1,
(WOB_METHOD) wool_undefined_method_1,
(WOB_METHOD) wool_undefined_method_1,
(WOB_METHOD) wool_undefined_method_1,
(WOB_METHOD) wool_undefined_method_1
};
unsigned int MenuMask = 0;
/* routines */
Menu NewMenu(), MenuOpen();
/*
* Set up a menu
*/
Menu
SetUpMenu(wl_menu)
WOOL_Menu wl_menu;
{
Menu menu = NewMenu(Context->rootWob, wl_menu);
UpdateMenuGeometry(menu);
MenuOpen(menu);
return menu;
}
Menu
MenuOpen(menu)
Menu menu;
{
int i;
check_window_size(menu);
menu -> hook =
XCreateSimpleWindow(dpy, Context->root,
menu -> box.x, menu -> box.y,
menu -> box.width, menu -> box.height,
menu -> box.borderwidth,
menu -> box.borderpixel, menu -> box.background);
menu -> status = MenuStatus | TopLevelXWindowStatus;
WobRecordHook(menu);
for (i = 0; i < menu -> nbars; i++)
WOOL_send(WOOL_open, menu -> bars[i], (menu -> bars[i]));
menu -> curstate = (int) WOOL_send(WOOL_open, menu -> fsm, (menu -> fsm));
menu -> input_mask = MenuMask | ((WOOL_Fsm) menu -> fsm) -> mask;
XSelectInput(dpy, menu -> hook, menu -> input_mask);
if (menu -> cursor != NIL)
XDefineCursor(dpy, menu -> hook,
((WOOL_Cursor) menu -> cursor) -> cursor);
if (menu -> bordertile != NIL)
XSetWindowBorderPixmap(dpy, menu -> hook,
((WOOL_Pixmap) menu -> bordertile) -> pixmap);
{
XSetWindowAttributes wa; /* menus are OverrideRedirect */
wa.override_redirect = 1;
XChangeWindowAttributes(dpy, menu -> hook, CWOverrideRedirect, &wa);
}
#ifdef SHAPE
if (MenuIsShaped(menu)) {
UpdateMenuShape(menu);
}
#endif /* SHAPE */
XMapSubwindows(dpy, menu -> hook);
return menu;
}
Menu
NewMenu(parent, wl_menu)
Wob parent;
WOOL_Menu wl_menu;
{
int i;
Menu menu;
int dir =
(wl_menu -> direction == HORIZONTAL ? VERTICAL : HORIZONTAL);
WOOL_OBJECT object;
menu = (Menu) NewWob(sizeof(struct _Menu)
+ sizeof(Bar) * Max(0, (wl_menu -> bars_size - 1)));
wl_menu = (WOOL_Menu) wool_type_or_evaluate(wl_menu, WLMenu);
menu -> type = MenuClass;
menu -> parent = parent;
menu -> screen = ((ClientWindow) parent) -> screen;
menu -> box.borderwidth = wl_menu -> borderwidth;
menu -> box.borderpixel = wl_menu -> borderpixel;
menu -> box.background = wl_menu -> background;
menu -> direction = wl_menu -> direction;
menu -> bar_separator = wl_menu -> bar_separator;
menu -> min_width = DefaultMenuMinWidth;
menu -> max_width = DefaultMenuMaxWidth;
increase_reference(menu -> menu =
wool_type_or_evaluate(wl_menu -> menu, WLMenu));
increase_reference(menu -> property = (WOOL_OBJECT) wl_menu -> property);
increase_reference(menu -> bordertile =
wool_type_or_evaluate(wl_menu -> bordertile, WLPixmap));
increase_reference(menu -> fsm =
wool_type_or_evaluate(wl_menu -> fsm, WLFsm));
increase_reference(menu -> cursor =
wool_type_or_evaluate(wl_menu -> cursor, WLCursor));
menu -> nbars = wl_menu -> bars_size;
for (i = 0; i < wl_menu -> bars_size; i++) {
object = wool_type_or_evaluate(wl_menu -> bars[i], WLBar);
UpdateClientWindowBar(menu, &(menu -> bars[i]), object, dir);
}
return menu;
}
MenuClose(menu)
Menu menu;
{
int i;
for (i = 0; i < menu -> nbars; i++) {
BarClose(menu -> bars[i]);
}
WobRelease(menu);
}
/*
* Set here the dimensions of a menu
*/
UpdateMenuGeometry(menu)
Menu menu;
{
int i, width = 0, length = 0;
int dir = menu -> direction;
for (i = 0; i < menu -> nbars; i++) {
if (dir == HORIZONTAL) {
menu -> bars[i] -> box.x = length;
menu -> bars[i] -> box.y = 0;
} else {
menu -> bars[i] -> box.x = 0;
menu -> bars[i] -> box.y = length;
}
length += UpdateBarWidth(menu -> bars[i]) +
menu -> bar_separator;
width = Max(NaturalBarLength(menu -> bars[i]), width);
}
width = Min(Max(width, menu -> min_width), menu -> max_width);
length = Max(length - menu -> bar_separator, 1);
if (dir == HORIZONTAL) {
menu -> box.width = length;
menu -> box.height = width;
} else {
menu -> box.width = width;
menu -> box.height = length;
}
for (i = 0; i < menu -> nbars; i++) {
if (dir == HORIZONTAL)
menu -> bars[i] -> box.height = width
- 2 * menu -> bars[i] -> box.borderwidth;
else
menu -> bars[i] -> box.width = width
- 2 * menu -> bars[i] -> box.borderwidth;
UpdateBarLength(menu -> bars[i]);
}
}
/*
* Since a menu will receive events for its sons, we must transmit them!
*/
MenuEventHandler(menu, evt)
Menu menu;
XEvent *evt;
{
int i;
switch (evt -> type) {
case GWMUserEvent:
WLFsm_action(menu -> fsm, menu, evt);
if (GWM_Propagate_user_events)
for (i = 0; i < menu -> nbars; i++)
if (menu -> bars[i])
WOOL_send(WOOL_process_event, menu -> bars[i],
(menu -> bars[i], evt));
break;
case ClientMessage:
if (evt -> xclient.message_type == XA_WM_PROTOCOLS
&& evt -> xclient.data.l[0] == (long) XA_WM_DELETE_WINDOW) {
/* to not be sent messages later */
if (((ClientWindow) menu -> parent) -> client_wob == (Wob) menu)
((ClientWindow) menu -> parent) -> client_wob = 0;
MenuClose(menu);
} else {
WLFsm_action(menu -> fsm, menu, evt);
}
break;
case PropertyNotify: /* HACK for placed menus */
WOOL_send(WOOL_process_event, menu -> parent, (menu -> parent, evt));
break;
default:
WLFsm_action(menu -> fsm, menu, evt);
}
}
ReconfigureMenu(menu, culprit)
Menu menu;
Bar culprit; /* only bar */
{
int i, width = menu -> box.width, height = menu ->box.height;
int shaped = 0;
UpdateMenuGeometry(menu);
for (i = 0; i < menu -> nbars; i++)
if (menu -> bars[i])
WOOL_send(WOOL_reconfigure, menu -> bars[i],
(menu -> bars[i], menu));
#ifdef SHAPE
if ((shaped = MenuIsShaped(menu))) {
UpdateMenuShape(menu);
}
#endif /* SHAPE */
if ((width != menu -> box.width) || (height != menu -> box.height) || shaped) {
XResizeWindow(dpy, menu -> hook,
menu -> box.width, menu -> box.height);
if (menu -> parent && menu -> parent -> type == ClientWindowClass &&
((ClientWindow) menu -> parent) -> client_wob == (Wob) menu) {
WOOL_send(WOOL_reconfigure, menu -> parent,
(menu -> parent, menu));
UpdateMenuNormalHints(menu, 0, 0, 0,
menu -> box.width, menu -> box.height);
}
}
}
/*
* Place a fixed menu on the screen
* usage (place-menu "name-of-this-menu" menu [x y])
*/
WOOL_OBJECT
PlaceFixedMenu(argc, argv)
int argc;
WOOL_String *argv;
{
int x = 0, y = 0;
WOOL_Menu wl_menu;
Window w;
XClassHint classhints;
XWMHints wm_hints;
ClientWindow wob;
XSetWindowAttributes wa;
WOOL_String icon_name;
WOOL_OBJECT starts_iconic;
WOOL_String wl_string;
if (argc < 2 || argc == 3 || argc > 4)
return wool_error(BAD_NUMBER_OF_ARGS, argc);
must_be_string(argv[0], 0);
if (argc == 4) {
must_be_number(argv[2], 2);
must_be_number(argv[3], 3);
x = ((WOOL_Number) argv[2]) -> number;
y = ((WOOL_Number) argv[3]) -> number;
}
wl_menu = (WOOL_Menu) wool_type_or_evaluate(argv[1], WLMenu);
w = wl_menu -> wob_menu -> hook;
/* Now, set the names */
XStoreName(dpy, w, argv[0] -> string);
get_val_from_context(icon_name, WA_icon_name);
get_val_from_context(starts_iconic, WA_starts_iconic);
if(icon_name != (WOOL_String) NIL)
XSetIconName(dpy, w, icon_name -> string);
else
XSetIconName(dpy, w, argv[0] -> string);
wl_string = (WOOL_String) WOOL_send(WOOL_eval, WA_class_name,
(WA_class_name));
if (wl_string->type == WLString)
classhints.res_class = wl_string->string;
else
classhints.res_class = "Gwm";
wl_string = (WOOL_String) WOOL_send(WOOL_eval, WA_client_name,
(WA_client_name));
if (wl_string->type == WLString)
classhints.res_name = wl_string->string;
else
classhints.res_name = "menu";
XSetClassHint(dpy, w, &classhints);
/* machine_name */
XChangeProperty(dpy, w, XA_WM_CLIENT_MACHINE, XA_STRING, 8, PropModeReplace,
((WOOL_String) wool_host_name)->string,
strlen(((WOOL_String) wool_host_name)->string));
/* normal_hints */
UpdateMenuNormalHints(wl_menu -> wob_menu, (argc == 4 ? 1 : 0), x, y,
wl_menu -> wob_menu -> box.width,
wl_menu -> wob_menu -> box.height);
/* WM_hints */
if (starts_iconic == NIL) {
wm_hints.flags = 0;
} else {
wm_hints.flags = StateHint;
wm_hints.initial_state = WM_STATE_Iconified;
}
XSetWMHints(dpy, w, &wm_hints);
/* participate in the delete_window protocol */
{
#define GWM_menus_number_of_protocols 1
Window protocols[GWM_menus_number_of_protocols];
protocols[0] = XA_WM_DELETE_WINDOW;
XChangeProperty(dpy, w, XA_WM_PROTOCOLS, XA_ATOM, 32, PropModeReplace,
(unsigned char *)protocols, GWM_menus_number_of_protocols);
}
/* a placed menu is no more OverrideRedirect */
wa.override_redirect = 0;
XChangeWindowAttributes(dpy, w, CWOverrideRedirect, &wa);
/* now decorate it and map it */
wob = (ClientWindow) DecorateWindow(w, Context -> rootWob, 0, 1);
ClientWindowInitialMap(wob);
return (WOOL_OBJECT) WLNumber_make(wob);
}
UpdateMenuNormalHints(menu, pos, x, y, width, height)
Menu menu;
int pos, x, y, width, height;
{
XSizeHints normal_hints;
normal_hints.min_width = normal_hints.max_width = width;
normal_hints.min_height = normal_hints.max_height = height;
if (pos) {
normal_hints.x = x;
normal_hints.y = y;
XMoveWindow(dpy, menu -> hook, x, y);
}
#ifdef NOBASEDIMS
normal_hints.flags = (pos ? USPosition : 0) | PMinSize | PMaxSize;
XSetNormalHints(dpy, menu -> hook, &normal_hints);
#else /* NOBASEDIMS */
normal_hints.win_gravity = StaticGravity;
normal_hints.flags = (pos ? USPosition : 0) | PMinSize | PMaxSize | PWinGravity;
XSetWMNormalHints(dpy, menu -> hook, &normal_hints);
#endif /* NOBASEDIMS */
}
#ifdef SHAPE
/* non-rectangular extension */
int
MenuIsShaped(menu)
Menu menu;
{
int i;
for (i = 0; i < menu -> nbars; i++)
if (menu -> bars[i] && ((Bar) menu -> bars[i]) -> shaped)
return 1;
return 0;
}
UpdateMenuShape(menu)
Menu menu;
{
XRectangle rect, rect2;
int i;
rect.x = - menu -> box.borderwidth;
rect.y = - menu -> box.borderwidth;
rect.width = menu -> box.width + 2 * menu -> box.borderwidth;
rect.height = menu -> box.height + 2 * menu -> box.borderwidth;
XShapeCombineRectangles(dpy, menu -> hook, ShapeBounding,
0, 0,
&rect, 1, ShapeSet, 0);
for (i = 0; i < menu -> nbars; i++)
if (menu -> bars[i]) {
rect2.x = menu -> bars[i] -> box.x + menu -> bars[i] -> box.borderwidth;
rect2.y = menu -> bars[i] -> box.y + menu -> bars[i] -> box.borderwidth;
rect2.width = menu -> bars[i] -> box.width;
rect2.height = menu -> bars[i] -> box.height;
XShapeCombineRectangles(dpy, menu -> hook, ShapeBounding,
0, 0,
&rect2, 1, ShapeSubtract, 0);
XShapeCombineShape(dpy, menu -> hook, ShapeBounding,
rect2.x,
rect2.y,
menu -> bars[i] -> hook, ShapeBounding,
ShapeUnion);
}
}
#endif /* SHAPE */