1 /* editmenu.c - editable menus
3 * WPrefs - Window Maker Preferences Program
5 * Copyright (c) 1999 Alfredo K. Kojima
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,
32 typedef struct W_EditMenuItem
{
36 struct W_EditMenu
*menu
;
40 WMTextField
*textField
;
42 struct W_EditMenu
*submenu
; /* if it's a cascade, NULL otherwise */
46 typedef struct W_EditMenu
{
50 struct W_EditMenu
*parent
;
56 struct W_EditMenuItem
**items
;
61 struct W_EditMenu
*next
;
62 struct W_EditMenu
*prev
;
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
= {
87 static W_Class EditMenuItemClass
= 0;
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
;
103 WCreateEditMenuItem(WMWidget
*parent
, char *title
)
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
));
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
,
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
)
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
));
159 handleItemEvents(XEvent
*event
, void *data
)
161 WEditMenuItem
*iPtr
= (WEditMenuItem
*)data
;
164 switch (event
->type
) {
166 if (event
->xexpose
.count
!=0)
168 paintEditMenuItem(iPtr
);
172 destroyEditMenuItem(iPtr
);
180 handleItemActionEvents(XEvent
*event
, void *data
)
182 WEditMenuItem
*iPtr
= (WEditMenuItem
*)data
;
184 switch (event
->type
) {
193 destroyEditMenuItem(WEditMenuItem
*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
= {
226 static W_Class EditMenuClass
= 0;
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
;
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 */
253 } GNUstepWMAttributes
;
256 #define GSWindowStyleAttr (1<<0)
257 #define GSWindowLevelAttr (1<<1)
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);
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
);
297 WCreateEditMenu(WMScreen
*scr
, char *title
)
305 mPtr
= wmalloc(sizeof(WEditMenu
));
307 memset(mPtr
, 0, sizeof(WEditMenu
));
309 mPtr
->widgetClass
= EditMenuClass
;
311 mPtr
->view
= W_CreateTopView(scr
);
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
,
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
;
343 EditMenuList
->prev
= mPtr
;
351 WInsertMenuItemWithTitle(WEditMenu
*mPtr
, char *title
, int index
)
355 item
= WCreateEditMenuItem(mPtr
, title
);
358 WMCreateEventHandler(item
->view
, ButtonPressMask
|ButtonReleaseMask
359 |Button1MotionMask
, handleItemDrag
, item
);
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;
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
;
389 mPtr
->items
[mPtr
->itemCount
++] = item
;
392 updateMenuContents(mPtr
);
399 WSetMenuSubmenu(WEditMenu
*mPtr
, WEditMenu
*submenu
, WEditMenuItem
*item
)
401 item
->submenu
= submenu
;
402 submenu
->parent
= mPtr
;
404 paintEditMenuItem(item
);
409 WRemoveMenuItem(WEditMenu
*mPtr
, WEditMenuItem
*item
)
416 updateMenuContents(WEditMenu
*mPtr
)
418 WMScreen
*scr
= WMWidgetScreen(mPtr
);
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;
435 W_MoveView(mPtr
->items
[i
]->view
, 0, 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);
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
));
471 paintEditMenu(WEditMenu
*mPtr
, int y
)
473 if (!mPtr
->view
->flags
.realized
)
476 if (y
< mPtr
->titleHeight
|| y
< 0)
477 paintMenuTitle(mPtr
);
483 handleEvents(XEvent
*event
, void *data
)
485 WEditMenu
*mPtr
= (WEditMenu
*)data
;
488 switch (event
->type
) {
490 paintEditMenu(mPtr
, event
->xexpose
.y
);
494 destroyEditMenu(mPtr
);
504 handleActionEvents(XEvent
*event
, void *data
)
506 WEditMenu
*mPtr
= (WEditMenu
*)data
;
508 switch (event
->type
) {
516 editItemLabel(WEditMenuItem
*iPtr
)
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
);
527 WMRealizeWidget(tPtr
);
529 iPtr
->textField
= tPtr
;
534 handleItemDrag(XEvent
*event
, void *data
)
536 WEditMenuItem
*iPtr
= (WEditMenuItem
*)data
;
537 WEditMenu
*mPtr
= iPtr
->menu
;
538 WMScreen
*scr
= WMWidgetScreen(mPtr
);
545 switch (event
->type
) {
547 if (WMIsDoubleClick(event
)) {
551 } else if (event
->xbutton
.button
== Button1
) {
552 mPtr
->draggedItem
= 1;
553 mPtr
->dragX
= event
->xbutton
.x
;
554 mPtr
->dragY
= event
->xbutton
.y
;
558 if (event
->xbutton
.button
== Button1
) {
559 mPtr
->draggedItem
= -1;
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;
578 XRaiseWindow(scr
->display
, iPtr
->view
->window
);
580 XGrabPointer(scr
->display
, mPtr
->view
->window
, False
,
581 PointerMotionMask
|ButtonReleaseMask
|ButtonPressMask
583 GrabModeAsync
, GrabModeAsync
, None
, None
, CurrentTime
);
585 y
= iPtr
->view
->pos
.y
;
590 WMMaskEvent(scr
->display
, ButtonReleaseMask
|PointerMotionMask
595 if (ev
.xbutton
.button
== Button1
)
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
);
615 XUngrabPointer(scr
->display
, CurrentTime
);
617 for (oldIdx
= 0; oldIdx
< mPtr
->itemCount
; oldIdx
++) {
618 if (mPtr
->items
[oldIdx
] == iPtr
) {
622 assert(oldIdx
< mPtr
->itemCount
);
624 newIdx
= (y
- mPtr
->titleHeight
+ mPtr
->itemHeight
/2) / mPtr
->itemHeight
;
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
) {
640 item
= mPtr
->items
[oldIdx
];
641 mPtr
->items
[oldIdx
] = mPtr
->items
[newIdx
];
642 mPtr
->items
[newIdx
] = item
;
644 updateMenuContents(mPtr
);
650 destroyEditMenu(WEditMenu
*mPtr
)
652 WMRemoveNotificationObserver(mPtr
);
655 mPtr
->next
->prev
= mPtr
->prev
;
657 mPtr
->prev
->next
= mPtr
->next
;
658 if (EditMenuList
== mPtr
)
659 EditMenuList
= mPtr
->next
;