/* 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 #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 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 */