669 lines
16 KiB
C
669 lines
16 KiB
C
/*
|
|
* Copyright (c) 2002 Alban G. Hertroys
|
|
*
|
|
* libDockapp example - Usage of action rectangles
|
|
*
|
|
* Some clickable and draggable areas for you to play
|
|
* with.
|
|
*
|
|
* There's a bit much in here...
|
|
*/
|
|
|
|
/* The cursor font - stamdard cursor glyphs. */
|
|
#include <X11/cursorfont.h>
|
|
|
|
/* Required because we don't use a pixmap for the shape (using a DASetShape
|
|
* variation). Instead we use the XLib call "XShapeCombineRegion".
|
|
* Window shapes are an extension (since X11R5).
|
|
*/
|
|
#include <X11/extensions/shape.h>
|
|
|
|
#include <libdockapp/dockapp.h>
|
|
|
|
/* already includes Xlib, Xresources, XPM, stdlib and stdio */
|
|
|
|
/*
|
|
* Type definitions
|
|
*/
|
|
|
|
/* I like to keep my graphic contexts (GCs) together */
|
|
struct Colors {
|
|
GC white; /* foreground color from X-resource, or default */
|
|
GC black; /* background color, idem */
|
|
GC lightGray; /* Some GC's we'll have to define for colors */
|
|
GC darkGray;
|
|
GC slider; /* draw-color when not highlighted,
|
|
* dark-color when highlighted
|
|
*/
|
|
GC sliderLight; /* draw-color when highlighted */
|
|
GC sliderDark; /* dark-color when not highlighted */
|
|
};
|
|
|
|
|
|
/*
|
|
* Global variables
|
|
*/
|
|
Pixmap pixmap; /* pixmap pixmap */
|
|
DARect *buttonDown = NULL;
|
|
struct Colors *colors; /* our colors */
|
|
DAActionRect **actionRects;
|
|
float sliderPos = 0.7;
|
|
int mouseIn = 0;
|
|
Cursor pointer;
|
|
|
|
/*
|
|
* Prototypes for local functions
|
|
*/
|
|
struct Colors *setGCs(Drawable d);
|
|
unsigned long adjustColor(unsigned long color, signed int adjustment);
|
|
|
|
/* drawing routines */
|
|
void createBtn(DARect rect);
|
|
void drawRaisedFrame(DARect rect);
|
|
void drawSunkenFrame(DARect rect);
|
|
void createSquare(DARect rect);
|
|
void drawSquare(DARect rect, int button);
|
|
void createSlider(DARect rect);
|
|
void drawSlider(DARect rect);
|
|
void setPointerColor(GC);
|
|
|
|
/* utility functions */
|
|
DAActionRect setRectAction(DARect rect, DARectCallback action);
|
|
|
|
|
|
/* event handlers functions */
|
|
void destroy(void);
|
|
void buttonPress(int button, int state, int x, int y);
|
|
void buttonRelease(int button, int state, int x, int y);
|
|
void mouseMove(int x, int y);
|
|
void mouseEnter(void);
|
|
void mouseLeave(void);
|
|
|
|
/* what to do for a specific event for every 'item' in the dockapp */
|
|
/* Button that can be pressed "down" and jumps back "up" again */
|
|
void btnDown(int x, int y, DARect rect, void *data);
|
|
void btnUp(int x, int y, DARect rect, void *data);
|
|
void btnLeave(int x, int y, DARect rect, void *data);
|
|
|
|
/* Square that tells which button was pressed (number) */
|
|
void squareDown(int x, int y, DARect rect, void *data);
|
|
|
|
/* A draggable slider that highlights when the mouse is over it */
|
|
void sliderDown(int x, int y, DARect rect, void *data);
|
|
void sliderUp(int x, int y, DARect rect, void *data);
|
|
void sliderMove(int x, int y, DARect rect, void *data);
|
|
void sliderEnter(int x, int y, DARect rect, void *data);
|
|
void sliderLeave(int x, int y, DARect rect, void *data);
|
|
|
|
|
|
/*
|
|
* M A I N
|
|
*/
|
|
|
|
int
|
|
main(int argc, char **argv)
|
|
{
|
|
/* define the event handlers for the events */
|
|
DACallbacks eventCallbacks = {
|
|
destroy, /* destroy */
|
|
buttonPress, /* buttonPress */
|
|
buttonRelease, /* buttonRelease */
|
|
mouseMove, /* motion (mouse) */
|
|
mouseEnter, /* mouse enters window */
|
|
mouseLeave, /* mouse leaves window */
|
|
NULL /* timeout */
|
|
};
|
|
|
|
/* define regions (x, y, width, height) that need event-handling */
|
|
Region clipRegion = XCreateRegion();
|
|
|
|
DARect btn = {0, 0, 16, 16},
|
|
square = {0, 25, 22, 22},
|
|
slider = {24, 0, 23, 48};
|
|
|
|
/* define what to do if an event occurs in a rectangle */
|
|
DAActionRect *buttonPressRects, *buttonReleaseRects,
|
|
*mouseMoveRects, *mouseEnterRects, *mouseLeaveRects;
|
|
|
|
buttonPressRects = malloc(3 * sizeof(DAActionRect));
|
|
buttonPressRects[0] = setRectAction(btn, btnDown);
|
|
buttonPressRects[1] = setRectAction(square, squareDown);
|
|
buttonPressRects[2] = setRectAction(slider, sliderDown);
|
|
|
|
buttonReleaseRects = malloc(2 * sizeof(DAActionRect));
|
|
buttonReleaseRects[0] = setRectAction(btn, btnUp);
|
|
buttonReleaseRects[1] = setRectAction(slider, sliderUp);
|
|
|
|
mouseMoveRects = malloc(sizeof(DAActionRect));
|
|
mouseMoveRects[0] = setRectAction(slider, sliderMove);
|
|
|
|
mouseEnterRects = malloc(sizeof(DAActionRect));
|
|
mouseEnterRects[0] = setRectAction(slider, sliderEnter);
|
|
|
|
mouseLeaveRects = malloc(2 * sizeof(DAActionRect));
|
|
mouseLeaveRects[0] = setRectAction(btn, btnLeave);
|
|
mouseLeaveRects[1] = setRectAction(slider, sliderLeave);
|
|
|
|
|
|
/* XXX: make action rectangles available outside main()
|
|
* ...libDockapp should be able to do this... (reminder)
|
|
*/
|
|
actionRects = malloc(6 * sizeof(DAActionRect *));
|
|
actionRects[0] = buttonPressRects;
|
|
actionRects[1] = buttonReleaseRects;
|
|
actionRects[2] = mouseMoveRects;
|
|
actionRects[3] = mouseEnterRects;
|
|
actionRects[4] = mouseLeaveRects;
|
|
actionRects[5] = NULL;
|
|
|
|
/* provide standard command-line options */
|
|
DAParseArguments(
|
|
argc, argv, /* Where the options come from */
|
|
NULL, 0, /* Our list with options - none as you can see */
|
|
"This is the help text for the rectangle example of how to "
|
|
"use libDockapp.\n",
|
|
"Rectangle example version 1.0");
|
|
|
|
/* Tell libdockapp what version we expect it to be, so that you can use
|
|
* older programs with newer versions of libdockapp with less risc for
|
|
* compatibility problems.
|
|
*/
|
|
DASetExpectedVersion(20030126);
|
|
|
|
/* Initialize a dockapp */
|
|
DAInitialize(
|
|
"", /* Use default display */
|
|
"daRectangleExample", /* WM_CLASS hint; don't use chars in [.?*: ] */
|
|
48, 48, /* geometry of dockapp internals */
|
|
argc, argv /* (needed internally) */
|
|
);
|
|
|
|
/* Create a pixmap to draw on, and to display */
|
|
pixmap = DAMakePixmap(); /* size == dockapp geometry (48,48) */
|
|
|
|
colors = setGCs(pixmap);
|
|
|
|
XFillRectangle(DADisplay, pixmap, DAClearGC, 0, 0, 48, 48);
|
|
XClearWindow(DADisplay, DAWindow);
|
|
|
|
/* Make a "Region" from the shapes we have */
|
|
XUnionRectWithRegion(&btn, clipRegion, clipRegion);
|
|
XUnionRectWithRegion(&square, clipRegion, clipRegion);
|
|
XUnionRectWithRegion(&slider, clipRegion, clipRegion);
|
|
|
|
/* Make this region a window shape mask */
|
|
XShapeCombineRegion(DADisplay, DAWindow, ShapeBounding,
|
|
0, 0, clipRegion, ShapeSet);
|
|
|
|
/* We don't need the region anymore (it is copied by XShapeCombineRegion).
|
|
* XXX: That's not certain, it is not documented. XSetRegion does so,
|
|
* though, so it is a likely assumption that it does copy.
|
|
*/
|
|
XDestroyRegion(clipRegion);
|
|
|
|
/* The cursor we want to use.
|
|
* Specify 'None' for the default,
|
|
* or one from X11/cursorfont.h
|
|
*/
|
|
pointer = XCreateFontCursor(DADisplay, XC_based_arrow_up);
|
|
XDefineCursor(DADisplay, DAWindow, pointer);
|
|
|
|
/* a square with an image that changes when clicked (A button). */
|
|
createBtn(btn);
|
|
|
|
/* a square that shows the number of the mouse-button pressed on click. */
|
|
createSquare(square);
|
|
|
|
/* a slider a using two dashed line GC's. */
|
|
createSlider(slider);
|
|
|
|
/* Tell libdockapp this is the pixmap that we want to show */
|
|
DASetPixmap(pixmap);
|
|
|
|
/* Process events every 100ms */
|
|
DASetTimeout(100);
|
|
|
|
/* set event callbacks */
|
|
DASetCallbacks(&eventCallbacks);
|
|
|
|
/* Display the pixmap we said it to show */
|
|
DAShow();
|
|
|
|
/* Process events and keep the dockapp running */
|
|
DAEventLoop();
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* Create our GC's to draw colored lines and such */
|
|
struct Colors *
|
|
setGCs(Drawable d)
|
|
{
|
|
struct Colors *colors;
|
|
XGCValues gcv;
|
|
unsigned long origColor;
|
|
char dashList[2] = {3, 1};
|
|
|
|
colors = malloc(sizeof(struct Colors));
|
|
if (colors == NULL)
|
|
return NULL;
|
|
|
|
/* Get the GC-values of the default GC */
|
|
XGetGCValues(DADisplay, DAGC,
|
|
GCForeground | GCBackground | GCGraphicsExposures, &gcv);
|
|
|
|
origColor = gcv.foreground;
|
|
|
|
/* don't send expose events */
|
|
gcv.graphics_exposures = False;
|
|
|
|
/* GC for white color */
|
|
gcv.foreground = WhitePixel(DADisplay, DefaultScreen(DADisplay));
|
|
colors->white = XCreateGC(DADisplay, d,
|
|
GCForeground | GCGraphicsExposures, &gcv);
|
|
|
|
/* GC for dark blue color */
|
|
#if 1
|
|
gcv.foreground = DAGetColor("navy");
|
|
#else
|
|
gcv.foreground = 0;
|
|
#endif
|
|
colors->black = XCreateGC(DADisplay, d,
|
|
GCForeground | GCGraphicsExposures, &gcv);
|
|
|
|
/* GC for light borders */
|
|
gcv.foreground = DAGetColor("lightGray");
|
|
colors->lightGray = XCreateGC(DADisplay, d,
|
|
GCForeground | GCGraphicsExposures, &gcv);
|
|
|
|
/* GC for dark borders (note re-use of gcv-values) */
|
|
gcv.foreground = DAGetColor("#222222");
|
|
colors->darkGray = XCreateGC(DADisplay, d,
|
|
GCForeground | GCGraphicsExposures, &gcv);
|
|
|
|
/* GC for the un-/highlighted colors and dashed line of the slider */
|
|
gcv.foreground = origColor;
|
|
gcv.line_width = 9;
|
|
gcv.line_style = LineOnOffDash;
|
|
|
|
colors->slider = XCreateGC(DADisplay, d,
|
|
GCForeground | GCBackground | GCGraphicsExposures |
|
|
GCLineWidth | GCLineStyle, &gcv);
|
|
|
|
XSetDashes(DADisplay, colors->slider, 1, dashList, 2);
|
|
|
|
/* light slider GC */
|
|
gcv.foreground = adjustColor(origColor, +0x40);
|
|
|
|
colors->sliderLight = XCreateGC(DADisplay, d,
|
|
GCForeground | GCBackground | GCGraphicsExposures |
|
|
GCLineWidth | GCLineStyle, &gcv);
|
|
|
|
XSetDashes(DADisplay, colors->sliderLight, 1, dashList, 2);
|
|
|
|
/* dark slider GC */
|
|
gcv.foreground = adjustColor(origColor, -0x40);
|
|
|
|
colors->sliderDark = XCreateGC(DADisplay, d,
|
|
GCForeground | GCBackground | GCGraphicsExposures |
|
|
GCLineWidth | GCLineStyle, &gcv);
|
|
|
|
XSetDashes(DADisplay, colors->sliderDark, 1, dashList, 2);
|
|
|
|
return colors;
|
|
}
|
|
|
|
|
|
/* Make a (GC) color lighter or darker */
|
|
unsigned long
|
|
adjustColor(unsigned long color, signed int adjustment)
|
|
{
|
|
signed long r, g, b;
|
|
|
|
r = color >> 16;
|
|
g = (color - (r << 16)) >> 8;
|
|
b = (color - (g << 8) - (r << 16));
|
|
|
|
r += adjustment;
|
|
g += adjustment;
|
|
b += adjustment;
|
|
|
|
if (r > 0xff)
|
|
r = 0xff;
|
|
|
|
if (g > 0xff)
|
|
g = 0xff;
|
|
|
|
if (b > 0xff)
|
|
b = 0xff;
|
|
|
|
if (r < 0)
|
|
r = 0;
|
|
|
|
if (g < 0)
|
|
g = 0;
|
|
|
|
if (b < 0)
|
|
b = 0;
|
|
|
|
return ((unsigned short)r << 16) +
|
|
((unsigned short)g << 8) +
|
|
(unsigned short)b;
|
|
}
|
|
|
|
|
|
void
|
|
setPointerColor(GC color)
|
|
{
|
|
XGCValues gcv;
|
|
XColor fcolor, bcolor;
|
|
|
|
XGetGCValues(DADisplay, color, GCForeground, &gcv);
|
|
|
|
fcolor.pixel = gcv.foreground;
|
|
fcolor.flags = DoRed | DoGreen | DoBlue;
|
|
|
|
bcolor.red = 0;
|
|
bcolor.green = 0;
|
|
bcolor.blue = 0;
|
|
|
|
XRecolorCursor(DADisplay, pointer, &fcolor, &bcolor);
|
|
}
|
|
|
|
|
|
/* event handlers functions */
|
|
void destroy(void)
|
|
{
|
|
}
|
|
|
|
void buttonPress(int button, int state, int x, int y)
|
|
{
|
|
int *data = malloc(sizeof(int *));
|
|
|
|
*data = button;
|
|
|
|
DAProcessActionRects(x, y, actionRects[0], 3, (void *)data);
|
|
|
|
free(data);
|
|
}
|
|
|
|
void buttonRelease(int button, int state, int x, int y)
|
|
{
|
|
DAProcessActionRects(x, y, actionRects[1], 2, NULL);
|
|
|
|
}
|
|
|
|
void
|
|
mouseMove(int x, int y)
|
|
{
|
|
DAProcessActionRects(x, y, actionRects[2], 1, NULL);
|
|
}
|
|
|
|
void
|
|
mouseEnter(void)
|
|
{
|
|
mouseIn = 1;
|
|
|
|
drawSlider(actionRects[3][0].rect);
|
|
}
|
|
|
|
|
|
void
|
|
mouseLeave(void)
|
|
{
|
|
mouseIn = 0;
|
|
|
|
/* mouse pointer left the dockapp window */
|
|
DAProcessActionRects(0, 0, actionRects[4], 2, NULL);
|
|
|
|
/* if the button is still depressed, make it go up again. */
|
|
/* TODO: Use data in actionRects[4] here instead of below check */
|
|
if (buttonDown != NULL)
|
|
btnUp(0, 0, *buttonDown, NULL);
|
|
|
|
drawSlider(actionRects[4][1].rect);
|
|
}
|
|
|
|
/* what to do for a specific event for every 'item' in the dockapp */
|
|
/* Button that can be pressed "down" and jumps back "up" again */
|
|
void
|
|
btnDown(int x, int y, DARect rect, void *data)
|
|
{
|
|
buttonDown = ▭
|
|
drawSunkenFrame(rect);
|
|
}
|
|
|
|
|
|
void
|
|
btnUp(int x, int y, DARect rect, void *data)
|
|
{
|
|
buttonDown = NULL;
|
|
drawRaisedFrame(rect);
|
|
}
|
|
|
|
|
|
void
|
|
btnLeave(int x, int y, DARect rect, void *data)
|
|
{
|
|
if (buttonDown == NULL)
|
|
return;
|
|
|
|
drawRaisedFrame(rect);
|
|
}
|
|
|
|
|
|
/* Square that tells which button was pressed (number) */
|
|
void
|
|
squareDown(int x, int y, DARect rect, void *data)
|
|
{
|
|
int button;
|
|
|
|
if (data) {
|
|
int *tmp = (int *)data;
|
|
|
|
button = *tmp;
|
|
} else
|
|
button = 0;
|
|
|
|
drawSquare(rect, button);
|
|
}
|
|
|
|
|
|
/* A draggable slider that highlights when the mouse is over it */
|
|
void
|
|
sliderDown(int x, int y, DARect rect, void *data)
|
|
{
|
|
buttonDown = ▭
|
|
setPointerColor(colors->sliderDark);
|
|
}
|
|
|
|
|
|
void
|
|
sliderUp(int x, int y, DARect rect, void *data)
|
|
{
|
|
buttonDown = NULL;
|
|
setPointerColor(colors->black);
|
|
}
|
|
|
|
|
|
void
|
|
sliderMove(int x, int y, DARect rect, void *data)
|
|
{
|
|
if (buttonDown == NULL /* ||
|
|
rect.x != buttonDown->x ||
|
|
rect.y != buttonDown->y ||
|
|
rect.width != buttonDown->width ||
|
|
rect.height != buttonDown->height */)
|
|
return;
|
|
|
|
sliderPos = (float)(rect.height - y) / (float)rect.height;
|
|
if (sliderPos > 1.0)
|
|
sliderPos = 1.0;
|
|
|
|
if (sliderPos < 0.0)
|
|
sliderPos = 0.0;
|
|
|
|
drawSlider(rect);
|
|
}
|
|
|
|
void sliderEnter(int x, int y, DARect rect, void *data)
|
|
{
|
|
}
|
|
void sliderLeave(int x, int y, DARect rect, void *data)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Drawing functions
|
|
*/
|
|
void
|
|
createBtn(DARect rect)
|
|
{
|
|
/* fill square excluding borders */
|
|
XFillRectangle(DADisplay, pixmap, colors->lightGray,
|
|
rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2);
|
|
|
|
drawRaisedFrame(rect);
|
|
}
|
|
|
|
|
|
void
|
|
drawRaisedFrame(DARect rect)
|
|
{
|
|
/* left border */
|
|
XDrawLine(DADisplay, pixmap, colors->white,
|
|
rect.x, rect.y, rect.x, rect.y + rect.height - 2);
|
|
/* top border */
|
|
XDrawLine(DADisplay, pixmap, colors->white,
|
|
rect.x + 1, rect.y, rect.width - 1, rect.y);
|
|
/* bottom border */
|
|
XDrawLine(DADisplay, pixmap, colors->darkGray,
|
|
rect.x, rect.y + rect.height - 1,
|
|
rect.x + rect.width - 1, rect.y + rect.height - 1);
|
|
/* right border */
|
|
XDrawLine(DADisplay, pixmap, colors->darkGray,
|
|
rect.x + rect.width - 1, rect.y + 1,
|
|
rect.x + rect.width - 1, rect.y + rect.height - 2);
|
|
|
|
DASetPixmap(pixmap);
|
|
}
|
|
|
|
|
|
void
|
|
drawSunkenFrame(DARect rect)
|
|
{
|
|
/* left border */
|
|
XDrawLine(DADisplay, pixmap, colors->darkGray,
|
|
rect.x, rect.y, rect.x, rect.y + rect.height - 2);
|
|
/* top border */
|
|
XDrawLine(DADisplay, pixmap, colors->darkGray,
|
|
rect.x + 1, rect.y, rect.width - 1, rect.y);
|
|
/* bottom border */
|
|
XDrawLine(DADisplay, pixmap, colors->white,
|
|
rect.x, rect.y + rect.height - 1,
|
|
rect.x + rect.width - 1, rect.y + rect.height - 1);
|
|
/* right border */
|
|
XDrawLine(DADisplay, pixmap, colors->white,
|
|
rect.x + rect.width - 1, rect.y + 1,
|
|
rect.x + rect.width - 1, rect.y + rect.height - 2);
|
|
|
|
DASetPixmap(pixmap);
|
|
}
|
|
|
|
|
|
void
|
|
createSquare(DARect rect)
|
|
{
|
|
/* fill square excluding borders */
|
|
XFillRectangle(DADisplay, pixmap, colors->black,
|
|
rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2);
|
|
|
|
XDrawRectangle(DADisplay, pixmap, colors->lightGray,
|
|
rect.x, rect.y, rect.width - 1, rect.height - 1);
|
|
|
|
drawSquare(rect, 0);
|
|
}
|
|
|
|
|
|
void
|
|
drawSquare(DARect rect, int button)
|
|
{
|
|
char label[3];
|
|
|
|
XFillRectangle(DADisplay, pixmap, colors->black,
|
|
rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2);
|
|
|
|
snprintf(label, 3, "%2d", button);
|
|
XDrawString(DADisplay, pixmap, colors->white,
|
|
rect.x + 3, rect.y + rect.height - 5, label, 2);
|
|
|
|
DASetPixmap(pixmap);
|
|
}
|
|
|
|
|
|
void
|
|
createSlider(DARect rect)
|
|
{
|
|
/* fill square excluding borders */
|
|
XFillRectangle(DADisplay, pixmap, colors->black,
|
|
rect.x + 1, rect.y + 1, rect.width - 2, rect.height - 2);
|
|
|
|
drawSunkenFrame(rect);
|
|
|
|
drawSlider(rect);
|
|
}
|
|
|
|
|
|
void
|
|
drawSlider(DARect rect)
|
|
{
|
|
GC highColor, lowColor; /* determine colors to use */
|
|
|
|
if (mouseIn) {
|
|
highColor = colors->sliderLight;
|
|
lowColor = colors->slider;
|
|
} else {
|
|
highColor = colors->slider;
|
|
lowColor = colors->sliderDark;
|
|
}
|
|
|
|
/* Draw two lines from bottom to sliderPos fraction of height */
|
|
if (sliderPos < 1.0) {
|
|
XDrawLine(DADisplay, pixmap, highColor,
|
|
rect.x + 6, rect.y + rect.height - 2,
|
|
rect.x + 6, rect.y + (1.0 - sliderPos) * (rect.height - 2));
|
|
|
|
XDrawLine(DADisplay, pixmap, highColor,
|
|
rect.x + 17, rect.y + rect.height - 2,
|
|
rect.x + 17, rect.y + (1.0 - sliderPos) * (rect.height - 2));
|
|
}
|
|
|
|
if (sliderPos > 0.0) {
|
|
XDrawLine(DADisplay, pixmap, lowColor,
|
|
rect.x + 6, rect.y + 1,
|
|
rect.x + 6, rect.y + (1.0 - sliderPos) * (rect.height - 2));
|
|
|
|
XDrawLine(DADisplay, pixmap, lowColor,
|
|
rect.x + 17, rect.y + 1,
|
|
rect.x + 17, rect.y + (1.0 - sliderPos) * (rect.height - 2));
|
|
}
|
|
|
|
DASetPixmap(pixmap);
|
|
}
|
|
|
|
|
|
DAActionRect
|
|
setRectAction(DARect rect, DARectCallback action)
|
|
{
|
|
DAActionRect ar;
|
|
|
|
ar.rect = rect;
|
|
ar.action = action;
|
|
|
|
return ar;
|
|
}
|
|
|