Update for 0.51.2-pre2
[wmaker-crm.git] / WPrefs.app / editmenu.c
blob039ac6113d538f2de08395a1012a2e2065aa2b70
1 /* editmenu.c - editable menus
2 *
3 * WPrefs - Window Maker Preferences Program
4 *
5 * Copyright (c) 1999 Alfredo K. Kojima
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20 * USA.
24 #include <WINGsP.h>
25 #include <WUtil.h>
26 #include <stdlib.h>
27 #include <assert.h>
28 #include <ctype.h>
30 #include "editmenu.h"
32 typedef struct W_EditMenuItem {
33 W_Class widgetClass;
34 WMView *view;
36 struct W_EditMenu *menu;
38 char *label;
40 WMTextField *textField;
42 struct W_EditMenu *submenu; /* if it's a cascade, NULL otherwise */
43 } EditMenuItem;
46 typedef struct W_EditMenu {
47 W_Class widgetClass;
48 WMView *view;
50 struct W_EditMenu *parent;
52 char *label;
54 int itemCount;
55 int itemsAlloced;
56 struct W_EditMenuItem **items;
58 int titleHeight;
59 int itemHeight;
61 struct W_EditMenu *next;
62 struct W_EditMenu *prev;
64 /* item dragging */
65 int draggedItem;
66 int dragX, dragY;
67 } EditMenu;
71 /******************** WEditMenuItem ********************/
73 static void destroyEditMenuItem(WEditMenuItem *iPtr);
74 static void paintEditMenuItem(WEditMenuItem *iPtr);
76 static void handleItemEvents(XEvent *event, void *data);
77 static void handleItemActionEvents(XEvent *event, void *data);
80 static W_ViewProcedureTable WEditMenuItemViewProcedures = {
81 NULL,
82 NULL,
83 NULL
87 static W_Class EditMenuItemClass = 0;
90 W_Class
91 InitEditMenuItem(WMScreen *scr)
93 /* register our widget with WINGs and get our widget class ID */
94 if (!EditMenuItemClass) {
95 EditMenuItemClass = W_RegisterUserWidget(&WEditMenuItemViewProcedures);
98 return EditMenuItemClass;
102 WEditMenuItem*
103 WCreateEditMenuItem(WMWidget *parent, char *title)
105 WEditMenuItem *iPtr;
107 if (!EditMenuItemClass)
108 InitEditMenuItem(WMWidgetScreen(parent));
111 iPtr = wmalloc(sizeof(WEditMenuItem));
113 memset(iPtr, 0, sizeof(WEditMenuItem));
115 iPtr->widgetClass = EditMenuItemClass;
117 iPtr->view = W_CreateView(W_VIEW(parent));
118 if (!iPtr->view) {
119 free(iPtr);
120 return NULL;
122 iPtr->view->self = iPtr;
124 iPtr->label = wstrdup(title);
126 WMCreateEventHandler(iPtr->view, ExposureMask|StructureNotifyMask,
127 handleItemEvents, iPtr);
129 WMCreateEventHandler(iPtr->view, ButtonPressMask, handleItemActionEvents,
130 iPtr);
132 return iPtr;
137 static void
138 paintEditMenuItem(WEditMenuItem *iPtr)
140 WMScreen *scr = WMWidgetScreen(iPtr);
141 WMColor *black = scr->black;
142 Window win = W_VIEW(iPtr)->window;
143 int w = W_VIEW(iPtr)->size.width;
144 int h = WMFontHeight(scr->normalFont) + 6;
146 if (!iPtr->view->flags.realized)
147 return;
149 XClearWindow(scr->display, win);
151 W_DrawRelief(scr, win, 0, 0, w+1, h, WRRaised);
153 WMDrawString(scr, win, WMColorGC(black), scr->normalFont, 5, 3, iPtr->label,
154 strlen(iPtr->label));
158 static void
159 handleItemEvents(XEvent *event, void *data)
161 WEditMenuItem *iPtr = (WEditMenuItem*)data;
164 switch (event->type) {
165 case Expose:
166 if (event->xexpose.count!=0)
167 break;
168 paintEditMenuItem(iPtr);
169 break;
171 case DestroyNotify:
172 destroyEditMenuItem(iPtr);
173 break;
179 static void
180 handleItemActionEvents(XEvent *event, void *data)
182 WEditMenuItem *iPtr = (WEditMenuItem*)data;
184 switch (event->type) {
185 case ButtonPress:
186 break;
192 static void
193 destroyEditMenuItem(WEditMenuItem *iPtr)
195 if (iPtr->label)
196 free(iPtr->label);
198 free(iPtr);
203 /******************** WEditMenu *******************/
206 static WEditMenu *EditMenuList = NULL;
208 static void destroyEditMenu(WEditMenu *mPtr);
209 static void paintEditMenu(WEditMenu *mPtr, int y);
211 static void updateMenuContents(WEditMenu *mPtr);
213 static void handleEvents(XEvent *event, void *data);
214 static void handleActionEvents(XEvent *event, void *data);
216 static void handleItemDrag(XEvent *event, void *data);
219 static W_ViewProcedureTable WEditMenuViewProcedures = {
220 NULL,
221 NULL,
222 NULL
226 static W_Class EditMenuClass = 0;
229 W_Class
230 InitEditMenu(WMScreen *scr)
232 /* register our widget with WINGs and get our widget class ID */
233 if (!EditMenuClass) {
235 EditMenuClass = W_RegisterUserWidget(&WEditMenuViewProcedures);
238 return EditMenuClass;
243 typedef struct {
244 int flags;
245 int window_style;
246 int window_level;
247 int reserved;
248 Pixmap miniaturize_pixmap; /* pixmap for miniaturize button */
249 Pixmap close_pixmap; /* pixmap for close button */
250 Pixmap miniaturize_mask; /* miniaturize pixmap mask */
251 Pixmap close_mask; /* close pixmap mask */
252 int extra_flags;
253 } GNUstepWMAttributes;
256 #define GSWindowStyleAttr (1<<0)
257 #define GSWindowLevelAttr (1<<1)
260 static void
261 writeGNUstepWMAttr(WMScreen *scr, Window window, GNUstepWMAttributes *attr)
263 unsigned long data[9];
265 /* handle idiot compilers where array of CARD32 != struct of CARD32 */
266 data[0] = attr->flags;
267 data[1] = attr->window_style;
268 data[2] = attr->window_level;
269 data[3] = 0; /* reserved */
270 /* The X protocol says XIDs are 32bit */
271 data[4] = attr->miniaturize_pixmap;
272 data[5] = attr->close_pixmap;
273 data[6] = attr->miniaturize_mask;
274 data[7] = attr->close_mask;
275 data[8] = attr->extra_flags;
276 XChangeProperty(scr->display, window, scr->attribsAtom, scr->attribsAtom,
277 32, PropModeReplace, (unsigned char *)data, 9);
281 static void
282 realizeObserver(void *self, WMNotification *not)
284 WEditMenu *menu = (WEditMenu*)self;
285 GNUstepWMAttributes attribs;
287 memset(&attribs, 0, sizeof(GNUstepWMAttributes));
288 attribs.flags = GSWindowStyleAttr|GSWindowLevelAttr;
289 attribs.window_style = WMBorderlessWindowMask;
290 attribs.window_level = WMSubmenuWindowLevel;
292 writeGNUstepWMAttr(WMWidgetScreen(menu), menu->view->window, &attribs);
296 WEditMenu*
297 WCreateEditMenu(WMScreen *scr, char *title)
299 WEditMenu *mPtr;
301 if (!EditMenuClass)
302 InitEditMenu(scr);
305 mPtr = wmalloc(sizeof(WEditMenu));
307 memset(mPtr, 0, sizeof(WEditMenu));
309 mPtr->widgetClass = EditMenuClass;
311 mPtr->view = W_CreateTopView(scr);
312 if (!mPtr->view) {
313 free(mPtr);
314 return NULL;
316 mPtr->view->self = mPtr;
318 WMAddNotificationObserver(realizeObserver, mPtr,
319 WMViewRealizedNotification, mPtr->view);
321 W_SetViewBackgroundColor(mPtr->view, mPtr->view->screen->darkGray);
323 mPtr->label = wstrdup(title);
325 mPtr->itemsAlloced = 10;
326 mPtr->items = wmalloc(sizeof(WEditMenuItem*)*mPtr->itemsAlloced);
328 WMCreateEventHandler(mPtr->view, ExposureMask|StructureNotifyMask,
329 handleEvents, mPtr);
331 WMCreateEventHandler(mPtr->view, ButtonPressMask,handleActionEvents, mPtr);
333 updateMenuContents(mPtr);
336 mPtr->itemHeight = WMFontHeight(scr->normalFont) + 6;
337 mPtr->titleHeight = WMFontHeight(scr->boldFont) + 8;
339 mPtr->draggedItem = -1;
341 mPtr->next = EditMenuList;
342 if (EditMenuList)
343 EditMenuList->prev = mPtr;
344 EditMenuList = mPtr;
346 return mPtr;
350 WEditMenuItem*
351 WInsertMenuItemWithTitle(WEditMenu *mPtr, char *title, int index)
353 WEditMenuItem *item;
355 item = WCreateEditMenuItem(mPtr, title);
356 item->menu = mPtr;
358 WMCreateEventHandler(item->view, ButtonPressMask|ButtonReleaseMask
359 |Button1MotionMask, handleItemDrag, item);
361 WMMapWidget(item);
363 if (index < 0)
364 index = 0;
365 else if (index > mPtr->itemCount)
366 index = mPtr->itemCount;
368 if (mPtr->itemCount == mPtr->itemsAlloced) {
369 WEditMenuItem *newList;
371 newList = wmalloc(sizeof(WEditMenuItem*)*(mPtr->itemsAlloced+10));
372 memset(newList, 0, sizeof(WEditMenuItem*)*(mPtr->itemsAlloced+10));
374 memcpy(newList, mPtr->items, mPtr->itemsAlloced*sizeof(WEditMenuItem*));
376 mPtr->itemsAlloced += 10;
378 free(mPtr->items);
380 mPtr->items = newList;
383 if (index < mPtr->itemCount) {
384 memmove(&mPtr->items[index+1], &mPtr->items[index],
385 sizeof(WEditMenuItem*));
386 mPtr->items[index] = item;
387 mPtr->itemCount++;
388 } else {
389 mPtr->items[mPtr->itemCount++] = item;
392 updateMenuContents(mPtr);
394 return item;
398 void
399 WSetMenuSubmenu(WEditMenu *mPtr, WEditMenu *submenu, WEditMenuItem *item)
401 item->submenu = submenu;
402 submenu->parent = mPtr;
404 paintEditMenuItem(item);
408 void
409 WRemoveMenuItem(WEditMenu *mPtr, WEditMenuItem *item)
415 static void
416 updateMenuContents(WEditMenu *mPtr)
418 WMScreen *scr = WMWidgetScreen(mPtr);
419 int i;
420 int newW, newH;
421 int w;
422 int iheight = mPtr->itemHeight;
424 newW = WMWidthOfString(scr->boldFont, mPtr->label,
425 strlen(mPtr->label)) + 12 + iheight;
427 newH = mPtr->titleHeight;
429 for (i = 0; i < mPtr->itemCount; i++) {
430 w = WMWidthOfString(scr->normalFont, mPtr->items[i]->label,
431 strlen(mPtr->items[i]->label)) + 5;
432 if (w > newW)
433 newW = w;
435 W_MoveView(mPtr->items[i]->view, 0, newH);
436 newH += iheight;
439 newH--;
441 W_ResizeView(mPtr->view, newW, newH);
443 for (i = 0; i < mPtr->itemCount; i++) {
444 W_ResizeView(mPtr->items[i]->view, newW, iheight);
447 paintEditMenu(mPtr, -1);
451 static void
452 paintMenuTitle(WEditMenu *mPtr)
454 WMScreen *scr = WMWidgetScreen(mPtr);
455 WMColor *black = scr->black;
456 WMColor *white = scr->white;
457 Window win = W_VIEW(mPtr)->window;
458 int w = W_VIEW(mPtr)->size.width;
459 int h = mPtr->titleHeight;
461 XFillRectangle(scr->display, win, WMColorGC(black), 0, 0, w, h);
463 W_DrawRelief(scr, win, 0, 0, w+1, h, WRRaised);
465 WMDrawString(scr, win, WMColorGC(white), scr->boldFont, 5, 4, mPtr->label,
466 strlen(mPtr->label));
470 static void
471 paintEditMenu(WEditMenu *mPtr, int y)
473 if (!mPtr->view->flags.realized)
474 return;
476 if (y < mPtr->titleHeight || y < 0)
477 paintMenuTitle(mPtr);
482 static void
483 handleEvents(XEvent *event, void *data)
485 WEditMenu *mPtr = (WEditMenu*)data;
488 switch (event->type) {
489 case Expose:
490 paintEditMenu(mPtr, event->xexpose.y);
491 break;
493 case DestroyNotify:
494 destroyEditMenu(mPtr);
495 break;
503 static void
504 handleActionEvents(XEvent *event, void *data)
506 WEditMenu *mPtr = (WEditMenu*)data;
508 switch (event->type) {
509 case ButtonPress:
510 break;
515 static void
516 editItemLabel(WEditMenuItem *iPtr)
518 WMTextField *tPtr;
520 tPtr = WMCreateTextField(iPtr);
521 WMResizeWidget(tPtr, iPtr->view->size.width - 20,
522 iPtr->view->size.height - 3);
523 WMMoveWidget(tPtr, 4, 1);
524 WMSetTextFieldBeveled(tPtr, False);
525 WMMapWidget(tPtr);
527 WMRealizeWidget(tPtr);
529 iPtr->textField = tPtr;
533 static void
534 handleItemDrag(XEvent *event, void *data)
536 WEditMenuItem *iPtr = (WEditMenuItem*)data;
537 WEditMenu *mPtr = iPtr->menu;
538 WMScreen *scr = WMWidgetScreen(mPtr);
539 Bool done = False;
540 int y;
541 int i;
542 int newIdx, oldIdx;
543 int newY;
545 switch (event->type) {
546 case ButtonPress:
547 if (WMIsDoubleClick(event)) {
549 editItemLabel(iPtr);
551 } else if (event->xbutton.button == Button1) {
552 mPtr->draggedItem = 1;
553 mPtr->dragX = event->xbutton.x;
554 mPtr->dragY = event->xbutton.y;
556 return;
557 case ButtonRelease:
558 if (event->xbutton.button == Button1) {
559 mPtr->draggedItem = -1;
561 return;
562 case MotionNotify:
563 if (mPtr->draggedItem >= 0) {
564 if (abs(event->xmotion.y - mPtr->dragY) > 3
565 || abs(event->xmotion.x - mPtr->dragX) > 3) {
566 mPtr->draggedItem = -1;
567 } else {
568 return;
570 } else {
571 return;
573 break;
574 default:
575 return;
578 XRaiseWindow(scr->display, iPtr->view->window);
580 XGrabPointer(scr->display, mPtr->view->window, False,
581 PointerMotionMask|ButtonReleaseMask|ButtonPressMask
582 |ButtonPressMask,
583 GrabModeAsync, GrabModeAsync, None, None, CurrentTime);
585 y = iPtr->view->pos.y;
587 while (!done) {
588 XEvent ev;
590 WMMaskEvent(scr->display, ButtonReleaseMask|PointerMotionMask
591 |ExposureMask, &ev);
593 switch (ev.type) {
594 case ButtonRelease:
595 if (ev.xbutton.button == Button1)
596 done = True;
597 break;
599 case MotionNotify:
600 y = ev.xbutton.y - mPtr->dragY;
602 if (y < mPtr->titleHeight)
603 y = mPtr->titleHeight;
604 else if (y > mPtr->view->size.height - mPtr->itemHeight + 1)
605 y = mPtr->view->size.height - mPtr->itemHeight + 1;
607 W_MoveView(iPtr->view, 0, y);
608 break;
610 default:
611 WMHandleEvent(&ev);
612 break;
615 XUngrabPointer(scr->display, CurrentTime);
617 for (oldIdx = 0; oldIdx < mPtr->itemCount; oldIdx++) {
618 if (mPtr->items[oldIdx] == iPtr) {
619 break;
622 assert(oldIdx < mPtr->itemCount);
624 newIdx = (y - mPtr->titleHeight + mPtr->itemHeight/2) / mPtr->itemHeight;
626 if (newIdx < 0)
627 newIdx = 0;
628 else if (newIdx >= mPtr->itemCount)
629 newIdx = mPtr->itemCount - 1;
631 newY = mPtr->titleHeight + newIdx * mPtr->itemHeight;
632 for (i = 0; i <= 15; i++) {
633 W_MoveView(iPtr->view, 0, ((newY*i)/15 + (y - (y*i)/15)));
634 XFlush(scr->display);
637 if (oldIdx != newIdx) {
638 WEditMenuItem *item;
640 item = mPtr->items[oldIdx];
641 mPtr->items[oldIdx] = mPtr->items[newIdx];
642 mPtr->items[newIdx] = item;
644 updateMenuContents(mPtr);
649 static void
650 destroyEditMenu(WEditMenu *mPtr)
652 WMRemoveNotificationObserver(mPtr);
654 if (mPtr->next)
655 mPtr->next->prev = mPtr->prev;
656 if (mPtr->prev)
657 mPtr->prev->next = mPtr->next;
658 if (EditMenuList == mPtr)
659 EditMenuList = mPtr->next;
661 if (mPtr->label)
662 free(mPtr->label);
664 if (mPtr->items)
665 free(mPtr->items);
667 free(mPtr);