mirror of
https://github.com/gryf/wmaker.git
synced 2025-12-19 04:20:27 +01:00
669 lines
14 KiB
C
669 lines
14 KiB
C
/* editmenu.c - editable menus
|
|
*
|
|
* WPrefs - Window Maker Preferences Program
|
|
*
|
|
* Copyright (c) 1999 Alfredo K. Kojima
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
|
* USA.
|
|
*/
|
|
|
|
|
|
#include <WINGsP.h>
|
|
#include <WUtil.h>
|
|
#include <stdlib.h>
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
|
|
#include "editmenu.h"
|
|
|
|
typedef struct W_EditMenuItem {
|
|
W_Class widgetClass;
|
|
WMView *view;
|
|
|
|
struct W_EditMenu *menu;
|
|
|
|
char *label;
|
|
|
|
WMTextField *textField;
|
|
|
|
struct W_EditMenu *submenu; /* if it's a cascade, NULL otherwise */
|
|
} EditMenuItem;
|
|
|
|
|
|
typedef struct W_EditMenu {
|
|
W_Class widgetClass;
|
|
WMView *view;
|
|
|
|
struct W_EditMenu *parent;
|
|
|
|
char *label;
|
|
|
|
int itemCount;
|
|
int itemsAlloced;
|
|
struct W_EditMenuItem **items;
|
|
|
|
int titleHeight;
|
|
int itemHeight;
|
|
|
|
struct W_EditMenu *next;
|
|
struct W_EditMenu *prev;
|
|
|
|
/* item dragging */
|
|
int draggedItem;
|
|
int dragX, dragY;
|
|
} EditMenu;
|
|
|
|
|
|
|
|
/******************** WEditMenuItem ********************/
|
|
|
|
static void destroyEditMenuItem(WEditMenuItem *iPtr);
|
|
static void paintEditMenuItem(WEditMenuItem *iPtr);
|
|
|
|
static void handleItemEvents(XEvent *event, void *data);
|
|
static void handleItemActionEvents(XEvent *event, void *data);
|
|
|
|
|
|
static W_ViewProcedureTable WEditMenuItemViewProcedures = {
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
static W_Class EditMenuItemClass = 0;
|
|
|
|
|
|
W_Class
|
|
InitEditMenuItem(WMScreen *scr)
|
|
{
|
|
/* register our widget with WINGs and get our widget class ID */
|
|
if (!EditMenuItemClass) {
|
|
EditMenuItemClass = W_RegisterUserWidget(&WEditMenuItemViewProcedures);
|
|
}
|
|
|
|
return EditMenuItemClass;
|
|
}
|
|
|
|
|
|
WEditMenuItem*
|
|
WCreateEditMenuItem(WMWidget *parent, char *title)
|
|
{
|
|
WEditMenuItem *iPtr;
|
|
|
|
if (!EditMenuItemClass)
|
|
InitEditMenuItem(WMWidgetScreen(parent));
|
|
|
|
|
|
iPtr = wmalloc(sizeof(WEditMenuItem));
|
|
|
|
memset(iPtr, 0, sizeof(WEditMenuItem));
|
|
|
|
iPtr->widgetClass = EditMenuItemClass;
|
|
|
|
iPtr->view = W_CreateView(W_VIEW(parent));
|
|
if (!iPtr->view) {
|
|
free(iPtr);
|
|
return NULL;
|
|
}
|
|
iPtr->view->self = iPtr;
|
|
|
|
iPtr->label = wstrdup(title);
|
|
|
|
WMCreateEventHandler(iPtr->view, ExposureMask|StructureNotifyMask,
|
|
handleItemEvents, iPtr);
|
|
|
|
WMCreateEventHandler(iPtr->view, ButtonPressMask, handleItemActionEvents,
|
|
iPtr);
|
|
|
|
return iPtr;
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
paintEditMenuItem(WEditMenuItem *iPtr)
|
|
{
|
|
WMScreen *scr = WMWidgetScreen(iPtr);
|
|
WMColor *black = scr->black;
|
|
Window win = W_VIEW(iPtr)->window;
|
|
int w = W_VIEW(iPtr)->size.width;
|
|
int h = WMFontHeight(scr->normalFont) + 6;
|
|
|
|
if (!iPtr->view->flags.realized)
|
|
return;
|
|
|
|
XClearWindow(scr->display, win);
|
|
|
|
W_DrawRelief(scr, win, 0, 0, w+1, h, WRRaised);
|
|
|
|
WMDrawString(scr, win, W_GC(black), scr->normalFont, 5, 3, iPtr->label,
|
|
strlen(iPtr->label));
|
|
}
|
|
|
|
|
|
static void
|
|
handleItemEvents(XEvent *event, void *data)
|
|
{
|
|
WEditMenuItem *iPtr = (WEditMenuItem*)data;
|
|
|
|
|
|
switch (event->type) {
|
|
case Expose:
|
|
if (event->xexpose.count!=0)
|
|
break;
|
|
paintEditMenuItem(iPtr);
|
|
break;
|
|
|
|
case DestroyNotify:
|
|
destroyEditMenuItem(iPtr);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
handleItemActionEvents(XEvent *event, void *data)
|
|
{
|
|
WEditMenuItem *iPtr = (WEditMenuItem*)data;
|
|
|
|
switch (event->type) {
|
|
case ButtonPress:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
destroyEditMenuItem(WEditMenuItem *iPtr)
|
|
{
|
|
if (iPtr->label)
|
|
free(iPtr->label);
|
|
|
|
free(iPtr);
|
|
}
|
|
|
|
|
|
|
|
/******************** WEditMenu *******************/
|
|
|
|
|
|
static WEditMenu *EditMenuList = NULL;
|
|
|
|
static void destroyEditMenu(WEditMenu *mPtr);
|
|
static void paintEditMenu(WEditMenu *mPtr, int y);
|
|
|
|
static void updateMenuContents(WEditMenu *mPtr);
|
|
|
|
static void handleEvents(XEvent *event, void *data);
|
|
static void handleActionEvents(XEvent *event, void *data);
|
|
|
|
static void handleItemDrag(XEvent *event, void *data);
|
|
|
|
|
|
static W_ViewProcedureTable WEditMenuViewProcedures = {
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
|
|
static W_Class EditMenuClass = 0;
|
|
|
|
|
|
W_Class
|
|
InitEditMenu(WMScreen *scr)
|
|
{
|
|
/* register our widget with WINGs and get our widget class ID */
|
|
if (!EditMenuClass) {
|
|
|
|
EditMenuClass = W_RegisterUserWidget(&WEditMenuViewProcedures);
|
|
}
|
|
|
|
return EditMenuClass;
|
|
}
|
|
|
|
|
|
|
|
typedef struct {
|
|
int flags;
|
|
int window_style;
|
|
int window_level;
|
|
int reserved;
|
|
Pixmap miniaturize_pixmap; /* pixmap for miniaturize button */
|
|
Pixmap close_pixmap; /* pixmap for close button */
|
|
Pixmap miniaturize_mask; /* miniaturize pixmap mask */
|
|
Pixmap close_mask; /* close pixmap mask */
|
|
int extra_flags;
|
|
} GNUstepWMAttributes;
|
|
|
|
|
|
#define GSWindowStyleAttr (1<<0)
|
|
#define GSWindowLevelAttr (1<<1)
|
|
|
|
|
|
static void
|
|
writeGNUstepWMAttr(WMScreen *scr, Window window, GNUstepWMAttributes *attr)
|
|
{
|
|
unsigned long data[9];
|
|
|
|
/* handle idiot compilers where array of CARD32 != struct of CARD32 */
|
|
data[0] = attr->flags;
|
|
data[1] = attr->window_style;
|
|
data[2] = attr->window_level;
|
|
data[3] = 0; /* reserved */
|
|
/* The X protocol says XIDs are 32bit */
|
|
data[4] = attr->miniaturize_pixmap;
|
|
data[5] = attr->close_pixmap;
|
|
data[6] = attr->miniaturize_mask;
|
|
data[7] = attr->close_mask;
|
|
data[8] = attr->extra_flags;
|
|
XChangeProperty(scr->display, window, scr->attribsAtom, scr->attribsAtom,
|
|
32, PropModeReplace, (unsigned char *)data, 9);
|
|
}
|
|
|
|
|
|
static void
|
|
realizeObserver(void *self, WMNotification *not)
|
|
{
|
|
WEditMenu *menu = (WEditMenu*)self;
|
|
GNUstepWMAttributes attribs;
|
|
|
|
memset(&attribs, 0, sizeof(GNUstepWMAttributes));
|
|
attribs.flags = GSWindowStyleAttr|GSWindowLevelAttr;
|
|
attribs.window_style = WMBorderlessWindowMask;
|
|
attribs.window_level = WMSubmenuWindowLevel;
|
|
|
|
writeGNUstepWMAttr(WMWidgetScreen(menu), menu->view->window, &attribs);
|
|
}
|
|
|
|
|
|
WEditMenu*
|
|
WCreateEditMenu(WMScreen *scr, char *title)
|
|
{
|
|
WEditMenu *mPtr;
|
|
|
|
if (!EditMenuClass)
|
|
InitEditMenu(scr);
|
|
|
|
|
|
mPtr = wmalloc(sizeof(WEditMenu));
|
|
|
|
memset(mPtr, 0, sizeof(WEditMenu));
|
|
|
|
mPtr->widgetClass = EditMenuClass;
|
|
|
|
mPtr->view = W_CreateTopView(scr);
|
|
if (!mPtr->view) {
|
|
free(mPtr);
|
|
return NULL;
|
|
}
|
|
mPtr->view->self = mPtr;
|
|
|
|
WMAddNotificationObserver(realizeObserver, mPtr,
|
|
WMViewRealizedNotification, mPtr->view);
|
|
|
|
W_SetViewBackgroundColor(mPtr->view, mPtr->view->screen->darkGray);
|
|
|
|
mPtr->label = wstrdup(title);
|
|
|
|
mPtr->itemsAlloced = 10;
|
|
mPtr->items = wmalloc(sizeof(WEditMenuItem*)*mPtr->itemsAlloced);
|
|
|
|
WMCreateEventHandler(mPtr->view, ExposureMask|StructureNotifyMask,
|
|
handleEvents, mPtr);
|
|
|
|
WMCreateEventHandler(mPtr->view, ButtonPressMask,handleActionEvents, mPtr);
|
|
|
|
updateMenuContents(mPtr);
|
|
|
|
|
|
mPtr->itemHeight = WMFontHeight(scr->normalFont) + 6;
|
|
mPtr->titleHeight = WMFontHeight(scr->boldFont) + 8;
|
|
|
|
mPtr->draggedItem = -1;
|
|
|
|
mPtr->next = EditMenuList;
|
|
if (EditMenuList)
|
|
EditMenuList->prev = mPtr;
|
|
EditMenuList = mPtr;
|
|
|
|
return mPtr;
|
|
}
|
|
|
|
|
|
WEditMenuItem*
|
|
WInsertMenuItemWithTitle(WEditMenu *mPtr, char *title, int index)
|
|
{
|
|
WEditMenuItem *item;
|
|
|
|
item = WCreateEditMenuItem(mPtr, title);
|
|
item->menu = mPtr;
|
|
|
|
WMCreateEventHandler(item->view, ButtonPressMask|ButtonReleaseMask
|
|
|Button1MotionMask, handleItemDrag, item);
|
|
|
|
WMMapWidget(item);
|
|
|
|
if (index < 0)
|
|
index = 0;
|
|
else if (index > mPtr->itemCount)
|
|
index = mPtr->itemCount;
|
|
|
|
if (mPtr->itemCount == mPtr->itemsAlloced) {
|
|
WEditMenuItem **newList;
|
|
|
|
newList = wmalloc(sizeof(WEditMenuItem*)*(mPtr->itemsAlloced+10));
|
|
memset(newList, 0, sizeof(WEditMenuItem*)*(mPtr->itemsAlloced+10));
|
|
|
|
memcpy(newList, mPtr->items, mPtr->itemsAlloced*sizeof(WEditMenuItem*));
|
|
|
|
mPtr->itemsAlloced += 10;
|
|
|
|
free(mPtr->items);
|
|
|
|
mPtr->items = newList;
|
|
}
|
|
|
|
if (index < mPtr->itemCount) {
|
|
memmove(&mPtr->items[index+1], &mPtr->items[index],
|
|
sizeof(WEditMenuItem*));
|
|
mPtr->items[index] = item;
|
|
mPtr->itemCount++;
|
|
} else {
|
|
mPtr->items[mPtr->itemCount++] = item;
|
|
}
|
|
|
|
updateMenuContents(mPtr);
|
|
|
|
return item;
|
|
}
|
|
|
|
|
|
void
|
|
WSetMenuSubmenu(WEditMenu *mPtr, WEditMenu *submenu, WEditMenuItem *item)
|
|
{
|
|
item->submenu = submenu;
|
|
submenu->parent = mPtr;
|
|
|
|
paintEditMenuItem(item);
|
|
}
|
|
|
|
|
|
void
|
|
WRemoveMenuItem(WEditMenu *mPtr, WEditMenuItem *item)
|
|
{
|
|
|
|
}
|
|
|
|
|
|
static void
|
|
updateMenuContents(WEditMenu *mPtr)
|
|
{
|
|
WMScreen *scr = WMWidgetScreen(mPtr);
|
|
int i;
|
|
int newW, newH;
|
|
int w;
|
|
int iheight = mPtr->itemHeight;
|
|
|
|
newW = WMWidthOfString(scr->boldFont, mPtr->label,
|
|
strlen(mPtr->label)) + 12 + iheight;
|
|
|
|
newH = mPtr->titleHeight;
|
|
|
|
for (i = 0; i < mPtr->itemCount; i++) {
|
|
w = WMWidthOfString(scr->normalFont, mPtr->items[i]->label,
|
|
strlen(mPtr->items[i]->label)) + 5;
|
|
if (w > newW)
|
|
newW = w;
|
|
|
|
W_MoveView(mPtr->items[i]->view, 0, newH);
|
|
newH += iheight;
|
|
}
|
|
|
|
newH--;
|
|
|
|
W_ResizeView(mPtr->view, newW, newH);
|
|
|
|
for (i = 0; i < mPtr->itemCount; i++) {
|
|
W_ResizeView(mPtr->items[i]->view, newW, iheight);
|
|
}
|
|
|
|
paintEditMenu(mPtr, -1);
|
|
}
|
|
|
|
|
|
static void
|
|
paintMenuTitle(WEditMenu *mPtr)
|
|
{
|
|
WMScreen *scr = WMWidgetScreen(mPtr);
|
|
WMColor *black = scr->black;
|
|
WMColor *white = scr->white;
|
|
Window win = W_VIEW(mPtr)->window;
|
|
int w = W_VIEW(mPtr)->size.width;
|
|
int h = mPtr->titleHeight;
|
|
|
|
XFillRectangle(scr->display, win, W_GC(black), 0, 0, w, h);
|
|
|
|
W_DrawRelief(scr, win, 0, 0, w+1, h, WRRaised);
|
|
|
|
WMDrawString(scr, win, W_GC(white), scr->boldFont, 5, 4, mPtr->label,
|
|
strlen(mPtr->label));
|
|
}
|
|
|
|
|
|
static void
|
|
paintEditMenu(WEditMenu *mPtr, int y)
|
|
{
|
|
if (!mPtr->view->flags.realized)
|
|
return;
|
|
|
|
if (y < mPtr->titleHeight || y < 0)
|
|
paintMenuTitle(mPtr);
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
handleEvents(XEvent *event, void *data)
|
|
{
|
|
WEditMenu *mPtr = (WEditMenu*)data;
|
|
|
|
|
|
switch (event->type) {
|
|
case Expose:
|
|
paintEditMenu(mPtr, event->xexpose.y);
|
|
break;
|
|
|
|
case DestroyNotify:
|
|
destroyEditMenu(mPtr);
|
|
break;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
static void
|
|
handleActionEvents(XEvent *event, void *data)
|
|
{
|
|
WEditMenu *mPtr = (WEditMenu*)data;
|
|
|
|
switch (event->type) {
|
|
case ButtonPress:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
editItemLabel(WEditMenuItem *iPtr)
|
|
{
|
|
WMTextField *tPtr;
|
|
|
|
tPtr = WMCreateTextField(iPtr);
|
|
WMResizeWidget(tPtr, iPtr->view->size.width - 20,
|
|
iPtr->view->size.height - 3);
|
|
WMMoveWidget(tPtr, 4, 1);
|
|
WMSetTextFieldBeveled(tPtr, False);
|
|
WMMapWidget(tPtr);
|
|
|
|
WMRealizeWidget(tPtr);
|
|
|
|
iPtr->textField = tPtr;
|
|
}
|
|
|
|
|
|
static void
|
|
handleItemDrag(XEvent *event, void *data)
|
|
{
|
|
WEditMenuItem *iPtr = (WEditMenuItem*)data;
|
|
WEditMenu *mPtr = iPtr->menu;
|
|
WMScreen *scr = WMWidgetScreen(mPtr);
|
|
Bool done = False;
|
|
int y;
|
|
int i;
|
|
int newIdx, oldIdx;
|
|
int newY;
|
|
|
|
switch (event->type) {
|
|
case ButtonPress:
|
|
if (WMIsDoubleClick(event)) {
|
|
|
|
editItemLabel(iPtr);
|
|
|
|
} else if (event->xbutton.button == Button1) {
|
|
mPtr->draggedItem = 1;
|
|
mPtr->dragX = event->xbutton.x;
|
|
mPtr->dragY = event->xbutton.y;
|
|
}
|
|
return;
|
|
case ButtonRelease:
|
|
if (event->xbutton.button == Button1) {
|
|
mPtr->draggedItem = -1;
|
|
}
|
|
return;
|
|
case MotionNotify:
|
|
if (mPtr->draggedItem >= 0) {
|
|
if (abs(event->xmotion.y - mPtr->dragY) > 3
|
|
|| abs(event->xmotion.x - mPtr->dragX) > 3) {
|
|
mPtr->draggedItem = -1;
|
|
} else {
|
|
return;
|
|
}
|
|
} else {
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
XRaiseWindow(scr->display, iPtr->view->window);
|
|
|
|
XGrabPointer(scr->display, mPtr->view->window, False,
|
|
PointerMotionMask|ButtonReleaseMask|ButtonPressMask
|
|
|ButtonPressMask,
|
|
GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
|
|
|
|
y = iPtr->view->pos.y;
|
|
|
|
while (!done) {
|
|
XEvent ev;
|
|
|
|
WMMaskEvent(scr->display, ButtonReleaseMask|PointerMotionMask
|
|
|ExposureMask, &ev);
|
|
|
|
switch (ev.type) {
|
|
case ButtonRelease:
|
|
if (ev.xbutton.button == Button1)
|
|
done = True;
|
|
break;
|
|
|
|
case MotionNotify:
|
|
y = ev.xbutton.y - mPtr->dragY;
|
|
|
|
if (y < mPtr->titleHeight)
|
|
y = mPtr->titleHeight;
|
|
else if (y > mPtr->view->size.height - mPtr->itemHeight + 1)
|
|
y = mPtr->view->size.height - mPtr->itemHeight + 1;
|
|
|
|
W_MoveView(iPtr->view, 0, y);
|
|
break;
|
|
|
|
default:
|
|
WMHandleEvent(&ev);
|
|
break;
|
|
}
|
|
}
|
|
XUngrabPointer(scr->display, CurrentTime);
|
|
|
|
for (oldIdx = 0; oldIdx < mPtr->itemCount; oldIdx++) {
|
|
if (mPtr->items[oldIdx] == iPtr) {
|
|
break;
|
|
}
|
|
}
|
|
assert(oldIdx < mPtr->itemCount);
|
|
|
|
newIdx = (y - mPtr->titleHeight + mPtr->itemHeight/2) / mPtr->itemHeight;
|
|
|
|
if (newIdx < 0)
|
|
newIdx = 0;
|
|
else if (newIdx >= mPtr->itemCount)
|
|
newIdx = mPtr->itemCount - 1;
|
|
|
|
newY = mPtr->titleHeight + newIdx * mPtr->itemHeight;
|
|
for (i = 0; i <= 15; i++) {
|
|
W_MoveView(iPtr->view, 0, ((newY*i)/15 + (y - (y*i)/15)));
|
|
XFlush(scr->display);
|
|
}
|
|
|
|
if (oldIdx != newIdx) {
|
|
WEditMenuItem *item;
|
|
|
|
item = mPtr->items[oldIdx];
|
|
mPtr->items[oldIdx] = mPtr->items[newIdx];
|
|
mPtr->items[newIdx] = item;
|
|
|
|
updateMenuContents(mPtr);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
destroyEditMenu(WEditMenu *mPtr)
|
|
{
|
|
WMRemoveNotificationObserver(mPtr);
|
|
|
|
if (mPtr->next)
|
|
mPtr->next->prev = mPtr->prev;
|
|
if (mPtr->prev)
|
|
mPtr->prev->next = mPtr->next;
|
|
if (EditMenuList == mPtr)
|
|
EditMenuList = mPtr->next;
|
|
|
|
if (mPtr->label)
|
|
free(mPtr->label);
|
|
|
|
if (mPtr->items)
|
|
free(mPtr->items);
|
|
|
|
free(mPtr);
|
|
}
|