home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Enigma Amiga Life 110
/
EnigmaAmiga110CD.iso
/
indispensabili
/
utility
/
apdf
/
xpdf-0.80
/
ltk
/
ltkmenu.cc
< prev
next >
Wrap
C/C++ Source or Header
|
1998-11-27
|
11KB
|
408 lines
//========================================================================
//
// LTKMenu.cc
//
// Menus and menu items.
//
// Copyright 1997 Derek B. Noonburg.
//
//========================================================================
#ifdef __GNUC__
#pragma implementation
#endif
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/cursorfont.h>
#include "gmem.h"
#include "LTKConfig.h"
#include "LTKApp.h"
#include "LTKWindow.h"
#include "LTKBorder.h"
#include "LTKMenu.h"
#ifdef XlibSpecificationRelease
#if XlibSpecificationRelease < 5
typedef char *XPointer;
#endif
#else
typedef char *XPointer;
#endif
//------------------------------------------------------------------------
#define horizBorder 2
#define horizSpace 12
#define arrowSize 10
//------------------------------------------------------------------------
// menu design
// -----------
//
// X coordinates:
//
// /-- main border
// | /-- selection border
// | | /-- shortcut
// | | | /-- submenu arrow
// | | | | /-- selection border
// | | | | | /-- main border
// | | | | | |
// v v v v v v
// | | | |
// | +--------------------------+ |
// | | +----------------------+ | |
// | | | ...text... |> | | |
// | | +----------------------+ | |
// | +--------------------------+ |
// | | +----------------------+ | |
// | | | ...text... shortcut | | |
// | | +----------------------+ | |
// | +--------------------------+ |
// | | | |
// A B C D E F G H I J
//
// A = 0
// B = A + ltkBorderWidth
// C = B + ltkBorderWidth
// D = C + horizBorder
// E = D + max{text widths}
// F = E + horizSpace
// G = F + max{arrowSize, shortcut widths}
// H = G + horizBorder
// I = H + ltkBorderWidth
// J = I + ltkBorderWidth
//
// total width = J
// = 4*ltkBorderWidth + 2*horizBorder + horizSpace
// + max{text widths}
// + max{arrowSize, shortcut widths}
//
//------------------------------------------------------------------------
static Bool isExposeEvent(Display *display, XEvent *e, XPointer w);
//------------------------------------------------------------------------
// LTKMenu
//------------------------------------------------------------------------
LTKMenu::LTKMenu(char *title1, int numItems1, ...) {
va_list args;
int i;
title = title1;
numItems = numItems1;
items = (LTKMenuItem **)gmalloc(numItems * sizeof(LTKMenuItem *));
va_start(args, numItems1);
for (i = 0; i < numItems; ++i)
items[i] = va_arg(args, LTKMenuItem *);
xwin = None;
}
LTKMenu::~LTKMenu() {
int i;
if (xwin != None)
unpost();
for (i = 0; i < numItems; ++i)
delete items[i];
gfree(items);
}
void LTKMenu::post(LTKWindow *win1, int x1, int y1, LTKMenu *parent1) {
XFontStruct *fontStruct;
XCharStruct extents;
XSetWindowAttributes attr;
XEvent event;
int w, h, shortcutW, direction, ascent, descent;
int i;
GBool haveSubmenus;
// parent menu
parent = parent1;
// parent window
win = win1;
display = win->getDisplay();
fgGC = win->getFgGC();
bgGC = win->getBgGC();
brightGC = win->getBrightGC();
darkGC = win->getDarkGC();
fontStruct = win->getXFontStruct();
// compute width and height
width = 0;
height = 0;
itemHeight = fontStruct->ascent + fontStruct->descent;
if (itemHeight < arrowSize)
itemHeight = arrowSize;
if (title) {
XTextExtents(fontStruct, title, strlen(title),
&direction, &ascent, &descent, &extents);
if (extents.width > width)
width = extents.width;
height += itemHeight + ltkBorderWidth + 2 + 2 * ltkBorderWidth;
}
shortcutW = 0;
haveSubmenus = gFalse;
for (i = 0; i < numItems; ++i) {
if (items[i]->text) {
XTextExtents(fontStruct, items[i]->text, strlen(items[i]->text),
&direction, &ascent, &descent, &extents);
if (extents.width > width)
width = extents.width;
height += itemHeight + ltkBorderWidth;
} else {
height += 2 + ltkBorderWidth;
}
if (items[i]->submenu) {
haveSubmenus = gTrue;
} else if (items[i]->shortcut) {
XTextExtents(fontStruct, items[i]->shortcut, strlen(items[i]->shortcut),
&direction, &ascent, &descent, &extents);
if (extents.width > shortcutW)
shortcutW = extents.width;
}
}
width += 2*ltkBorderWidth + horizBorder + horizSpace;
if (!haveSubmenus || shortcutW > arrowSize)
width += shortcutW;
else
width += arrowSize;
width += 2*ltkBorderWidth + horizBorder;
height += 3*ltkBorderWidth;
// compute position
w = win->getApp()->getDisplayWidth();
h = win->getApp()->getDisplayHeight();
x = x1;
if (x + width > w)
x = w - width;
if (x < 0)
x = 0;
y = y1;
if (y + height > h)
y = h - height;
if (y < 0)
y = 0;
// create X window
attr.background_pixel = win->getBgColor();
attr.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask;
attr.override_redirect = True;
xwin = XCreateWindow(display,
RootWindow(display, win->getScreenNum()),
x, y, width, height, 0, CopyFromParent,
InputOutput, CopyFromParent,
CWBackPixel | CWEventMask | CWOverrideRedirect,
&attr);
// map it
XMapWindow(display, xwin);
XPeekIfEvent(display, &event, &isExposeEvent, (XPointer)this);
// grab the pointer
XGrabPointer(display, xwin, False,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
PointerMotionHintMask,
GrabModeAsync, GrabModeAsync, None,
XCreateFontCursor(display, XC_sb_left_arrow), CurrentTime);
// register with app
win->getApp()->setMenu(this);
// no item selected yet
currentItem = -1;
currentSubmenu = NULL;
}
static Bool isExposeEvent(Display *display, XEvent *e, XPointer w) {
return e->type == Expose &&
e->xexpose.window == ((LTKMenu *)w)->getXWindow();
}
void LTKMenu::repost() {
// grab the pointer
XGrabPointer(display, xwin, False,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask |
PointerMotionHintMask,
GrabModeAsync, GrabModeAsync, None,
XCreateFontCursor(display, XC_sb_left_arrow), CurrentTime);
// register with app
win->getApp()->setMenu(this);
}
void LTKMenu::unpost() {
XEvent event;
// unpost any submenus
if (currentSubmenu) {
currentSubmenu->unpost();
currentSubmenu = NULL;
}
// let go of the pointer
XUngrabPointer(display, CurrentTime);
// kill the window
XUnmapWindow(display, xwin);
XDestroyWindow(display, xwin);
// flush any leftover events
XSync(display, False);
while (XCheckWindowEvent(display, xwin, 0xffffffff, &event)) ;
// mark the menu as unposted
xwin = None;
win->getApp()->setMenu(NULL);
}
void LTKMenu::done() {
if (parent)
parent->done();
else
unpost();
}
void LTKMenu::redraw() {
XCharStruct extents;
int direction, ascent, descent;
int textBase, y1, x2, y2;
int i;
// draw menu border
ltkDrawBorder(display, xwin, brightGC, darkGC, bgGC,
0, 0, width, height, ltkBorderRaised);
// draw title
textBase = win->getXFontStruct()->ascent;
y1 = 2 * ltkBorderWidth;
if (title) {
XTextExtents(win->getXFontStruct(), title, strlen(title),
&direction, &ascent, &descent, &extents);
x2 = (width - extents.width) / 2;
XDrawString(display, xwin, fgGC,
x2, y1 + textBase, title, strlen(title));
y1 += itemHeight + ltkBorderWidth;
ltkDrawSplitBorder(display, xwin, brightGC, darkGC, bgGC,
0, y1, width, 0, ltkBorderSunken);
y1 += 2 + 2 * ltkBorderWidth;
}
// draw items
for (i = 0; i < numItems; ++i) {
if (items[i]->text) {
XDrawString(display, xwin, fgGC,
2*ltkBorderWidth + horizBorder, y1 + textBase,
items[i]->text, strlen(items[i]->text));
if (items[i]->submenu) {
x2 = width - 2*ltkBorderWidth - horizBorder - arrowSize;
y2 = y1 + (itemHeight - arrowSize) / 2;
ltkDrawTriBorder(display, xwin, brightGC, darkGC, bgGC,
x2, y2, arrowSize, arrowSize,
ltkTriRight, ltkBorderRaised);
} else if (items[i]->shortcut) {
XTextExtents(win->getXFontStruct(),
items[i]->shortcut, strlen(items[i]->shortcut),
&direction, &ascent, &descent, &extents);
x2 = width - 2*ltkBorderWidth - horizBorder - extents.width;
XDrawString(display, xwin, fgGC, x2, y1 + textBase,
items[i]->shortcut, strlen(items[i]->shortcut));
}
y1 += itemHeight + ltkBorderWidth;
} else {
ltkDrawDivider(display, xwin, brightGC, darkGC, bgGC,
2*ltkBorderWidth + horizBorder - 1, y1,
width - 4*ltkBorderWidth - 2*horizBorder + 2, 0,
ltkBorderRaised);
y1 += 2 + ltkBorderWidth;
}
}
}
void LTKMenu::buttonPress(int mx, int my, int button, GBool dblClick) {
done();
if (currentItem >= 0 && items[currentItem]->cbk)
(*items[currentItem]->cbk)(items[currentItem]);
}
void LTKMenu::buttonRelease(int mx, int my, int button, GBool click) {
if (!click) {
done();
if (currentItem >= 0 && items[currentItem]->cbk)
(*items[currentItem]->cbk)(items[currentItem]);
}
}
void LTKMenu::mouseMove(int mx, int my, int btn) {
int y1, i, j;
y1 = ltkBorderWidth;
if (title)
y1 += itemHeight + ltkBorderWidth + 2 + 2 * ltkBorderWidth;
j = -1;
if (mx >= 0 && mx < width && my >= y1) {
for (i = 0; i < numItems; ++i) {
if (items[i]->text) {
if (my < y1 + itemHeight + ltkBorderWidth) {
j = i;
break;
}
y1 += itemHeight + ltkBorderWidth;
} else {
if (my < y1 + 2 + ltkBorderWidth)
break;
y1 += 2 + ltkBorderWidth;
}
}
}
if (j != currentItem && (j >= 0 || !currentSubmenu)) {
if (currentItem >= 0) {
if (currentSubmenu) {
currentSubmenu->unpost();
currentSubmenu = NULL;
repost();
}
ltkDrawBorder(display, xwin, brightGC, darkGC, bgGC,
ltkBorderWidth, currentY,
width - 2 * ltkBorderWidth,
itemHeight + 2*ltkBorderWidth,
ltkBorderNone);
}
currentItem = j;
currentY = y1;
if (currentItem >= 0) {
ltkDrawBorder(display, xwin, brightGC, darkGC, bgGC,
ltkBorderWidth, currentY,
width - 2 * ltkBorderWidth,
itemHeight + 2*ltkBorderWidth,
ltkBorderRaised);
if (items[currentItem]->submenu) {
currentSubmenu = items[currentItem]->submenu;
currentSubmenu->post(win, x + width - 2*ltkBorderWidth,
y + currentY, this);
}
}
}
if (j < 0 && parent)
parent->mouseMove(x + mx - parent->x, y + my - parent->y, btn);
}
//------------------------------------------------------------------------
// LTKMenuItem
//------------------------------------------------------------------------
LTKMenuItem::LTKMenuItem(char *text1, char *shortcut1, int itemNum1,
LTKMenuCbk cbk1, LTKMenu *submenu1) {
text = text1;
shortcut = shortcut1;
itemNum = itemNum1;
cbk = cbk1;
submenu = submenu1;
}