Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 3527 lines (2887 sloc) 57.1 KB
#ifndef com_sleepless_gui_gui_cpp
#define com_sleepless_gui_gui_cpp
/* Copyright 1998-2004 Sleepless Software Inc. All Rights Reserved */
// uh, this is here to get round a dumb thing with cygwin ... not
// sure what it's about ... with the timespec_t thing
#include <sys/types.h>
#if defined(linux) //|| (defined(__APPLE__) && defined(__GNUC__))
# include "sdl.cpp"
#endif
#ifdef WIN32
# define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff
# include "windows.h"
#endif
#include "misc.cpp"
#include "system.cpp"
#include "image.cpp"
#include "font.cpp"
#include "list.cpp"
#include "queue.cpp"
extern "C" {
extern void ext_draw_start();
extern void ext_draw_end();
}
// Generic events types
enum
{
E_UNKNOWN = 0,
E_BACKSPACE = 8,
E_DELETE = 127,
// Key events 1 thru 65535 correspond with unicode chars
E_QUIT = 65536,
E_REFRESH_DISPLAY,
E_TICK,
E_POINTER_MOTION,
E_POINTER_LEFT_BUTTON_PRESS,
E_POINTER_MIDDLE_BUTTON_PRESS,
E_POINTER_RIGHT_BUTTON_PRESS,
E_POINTER_LEFT_BUTTON_RELEASE,
E_POINTER_MIDDLE_BUTTON_RELEASE,
E_POINTER_RIGHT_BUTTON_RELEASE,
E_WIN_INACTIVE,
E_WIN_ACTIVE,
// modifier keys
E_CONTROL,
E_SHIFT,
// arrows
E_LEFT,
E_RIGHT,
E_UP,
E_DOWN,
// function keys
E_F1,
E_F2,
E_F3,
E_F4,
E_F5,
E_F6,
E_F7,
E_F8,
E_F9,
E_F10,
E_F11,
E_F12,
// All numbers from here up can be used by your app for whatever you want.
E_FIRST_USER_EVENT,
};
// Interface for scroller/scrollable stuff
struct WScrollable
{
virtual void unitLess() = 0;
virtual void unitMore() = 0;
virtual void posSet(float p) = 0;
};
// Base widget class. Does nothing much more than exist.
struct Widget
{
int x, y, w, h;
float alpha;
int havePointer;
int haveFocus;
int clean;
int id;
Image *i_bg;
unsigned long long cdata1; // client use. completely ignored by gui code
unsigned long long cdata2; // client use. completely ignored by gui code
Widget *tabTo;
int itran;
long int clrBG;
float bgAlpha;
Widget()
{
x = 0;
y = 0;
w = 1;
h = 1;
alpha = 1.0f;
havePointer = 0;
haveFocus = 0;
clean = 0;
id = 0;
i_bg = 0;
cdata1 = 0;
cdata2 = 0;
tabTo = 0;
clrBG = 0x000000;
bgAlpha = 0;
svf = 0;
itran = 1;
}
virtual ~Widget()
{
if(svf)
free(svf);
}
char *svf;
virtual void load(const char *s)
{
if(svf)
free(svf);
svf = dupstr(s);
if(svf)
{
File f(svf);
const char *b = (const char *)f.getBytes();
if(b)
{
int sx, sy, sw, sh;
char imfl[256];
if(sscanf(b, "%d %d %d %d '%[^']'", &sx, &sy, &sw, &sh, imfl) == 5)
{
setLocation(sx, sy);
setSize(sw, sh);
Image *img = new Image(imfl);
if(img && img->width > 0)
setImage(img);
}
}
}
}
virtual void save()
{
if(svf)
{
FILE *fp = fopen(svf, "wb");
if(fp)
{
const char *imfl = "";
if(i_bg && i_bg->fromFile)
imfl = i_bg->fromFile;
fprintf(fp, "%d %d %d %d '%s'", x, y, w, h, imfl);
fclose(fp);
}
}
}
void setImage(Image *i)
{
soil();
i_bg = i;
if(i_bg)
{
w = i_bg->width;
h = i_bg->height;
}
soil();
}
void setID(int i)
{
id = i;
}
void setLocation(int nx, int ny)
{
soil();
x = nx;
y = ny;
soil();
}
void setLocationCentered(int pw, int ph)
{
setLocation((pw - w) / 2, (ph - h) / 2);
}
// set this widget's location so it's centered over 'ow'.
void setLocationCentered(Widget *ow)
{
setLocation(ow->x + ((ow->w - w) / 2), ow->y + ((ow->h - h) / 2));
}
void setSize(int nw, int nh)
{
soil();
w = nw;
h = nh;
soil();
}
void setSize(Widget *_w)
{
setSize(_w->w, _w->h);
}
void setAlpha(float a)
{
alpha = a;
soil();
}
void modAlpha(float am)
{
float na = alpha + am;
if(na < 0)
na = 0;
else
if(na > 1)
na = 1;
setAlpha(na);
}
void setImgStencil(bool b)
{
itran = b ? 1 : 0;
}
void setBGColor(int c)
{
clrBG = c;
soil();
}
void setBGAlpha(float a)
{
bgAlpha = a;
soil();
}
///////////////////////////////////////////////////
int contains(int px, int py)
{
if(px < x)
return 0;
if(px >= x + w)
return 0;
if(py < y)
return 0;
if(py >= (y + h))
return 0;
return 1;
}
virtual void soil()
{
clean = 0;
}
////////////////////////////////////////////////////////
virtual void character(int ch)
{
}
virtual void keyDown(int k)
{
}
virtual void keyUp(int k)
{
}
virtual void gainPointer()
{
havePointer = 1;
}
virtual void losePointer()
{
havePointer = 0;
}
virtual void pointer(int lx, int ly)
{
}
virtual void leftButtonDown(int lx, int ly)
{
}
virtual void leftButtonUp(int lx, int ly)
{
}
virtual void rightButtonDown(int lx, int ly)
{
}
virtual void rightButtonUp(int lx, int ly)
{
}
virtual void paint(Image *img)
{
if(bgAlpha > 0)
img->fillRect(x, y, w, h, clrBG, alpha * bgAlpha);
if(i_bg)
img->blit(i_bg, x, y, alpha, itran);
}
virtual void gainFocus()
{
haveFocus = 1;
}
virtual void loseFocus()
{
haveFocus = 0;
}
};
struct WDisplay
{
char title[300];
int width;
int height;
int pointerx;
int pointery;
int bboffx;
int bboffy;
int quit;
Image *backBuf;
Font *defFont;
int dirty;
struct Widget *over; // widget that is currently under pointer
int overoffx;
int overoffy;
struct Widget *focus; // widget that currently has "focus"
struct List widgets;
int showDebug;
int mod_ctrl;
int mod_shft;
struct Widget *dragged;
int draggedOffX;
int draggedOffY;
int draggedSizing;
int rframe;
// OS dependent vars
#ifdef __APPLE__
// XXX
#endif
#if defined(linux) //|| (defined(__APPLE__) && defined(__GNUC__))
SleepySDL sdl;
#endif
#ifdef WIN32
HWND win;
int winmsg;
#endif
// Creates an OS display (probably a window) within which the
// sleepless widgets will be rendered.
WDisplay(const char *t, int _w, int _h, int fs)
{
title[0] = 0;
width = 0;
height = 0;
pointerx = 0;
pointery = 0;
bboffx = 0;
bboffy = 0;
quit = 0;
// OS dependent display initialization
#ifdef __APPLE__
// XXX
#endif
#if defined(linux) //|| (defined(__APPLE__) && defined(__GNUC__))
/*XXX handle fullscreen mode in linux here
if(fs) {
}
else
*/
{
sdl.init_video(_w, _h, t, t);
}
#endif
backBuf = new Image(_w, _h, 0);
defFont = new Font(12, 0, 0, 0, "courier");
dirty = 1;
over = 0;
overoffx = 0;
overoffy = 0;
focus = 0;
showDebug = 0;
mod_ctrl = 0;
mod_shft = 0;
dragged = 0;
draggedOffX = 0;
draggedOffY = 0;
draggedSizing = 0;
rframe = 0;
Str::copy(title, t, sizeof(title));
#ifdef __APPLE__
// XXX
width = _w;
height = _h;
#endif
#if defined(linux) //|| (defined(__APPLE__) && defined(__GNUC__))
if(sdl.bbSurf)
{
width = _w;
height = _h;
}
#endif
#ifdef WIN32
if(fs)
{
int fsw = (WORD)GetSystemMetrics(SM_CXSCREEN);
int fsh = (WORD)GetSystemMetrics(SM_CYSCREEN);
win = Windows::createBlandWindow(t, fsw, fsh, WDisplay::swproc);
bboffx = (fsw - _w) / 2;
bboffy = (fsh - _h) / 2;
}
else
{
win = Windows::createStandardWindow(t, _w, _h, WDisplay::swproc);
}
if(win)
{
SetProp(win, "sleepylib-gui-Window", this);
width = _w;
height = _h;
}
#endif
}
virtual ~WDisplay()
{
#ifdef WIN32
if(win)
{
DestroyWindow(win);
}
if(backBuf)
delete backBuf;
if(defFont)
delete defFont;
#endif
}
int show()
{
#ifdef __APPLE__
// XXX
#endif
#if defined(linux) //|| (defined(__APPLE__) && defined(__GNUC__))
// XXX
#endif
#ifdef WIN32
if(win)
{
ShowWindow(win, SW_SHOW);
SetForegroundWindow(win);
SetActiveWindow(win);
return 1;
}
#endif
return 0;
}
int hide()
{
#ifdef __APPLE__
// XXX
#endif
#if defined(linux) //|| (defined(__APPLE__) && defined(__GNUC__))
// XXX
#endif
#ifdef WIN32
if(win)
{
ShowWindow(win, SW_HIDE);
return 1;
}
#endif
return 0;
}
// Sleeps until an OS event arrives
void waitEvent()
{
#ifdef __APPLE__
// XXX
#endif
#if defined(linux) //|| (defined(__APPLE__) && defined(__GNUC__))
SDL_WaitEvent(0);
#endif
#ifdef WIN32
WaitMessage();
#endif
}
/* Here is a system for posting your own events so that they come through
the normal event dispatching mechanism */
Queue userEvents;
void postUserEvent(unsigned long long s)
{
#ifdef __APPLE__
userEvents.queue( (void *)s );
#endif
#if defined(linux) //|| (defined(__APPLE__) && defined(__GNUC__))
# error "sorry ... "
#endif
#ifdef WIN32
userEvents.queue((void *)s);
PostMessage(win, WM_USER, 0, 0); // just to wake up any waiters
#endif
}
// Reads any pending OS events, translates them to widget events
// and dispatches them to the appropriate widget.
// Then returns when the event queue has been drained.
void dispatch()
{
#ifdef __APPLE__
// XXX
#endif
#if defined(linux) //|| (defined(__APPLE__) && defined(__GNUC__))
SDL_Event se;
while(SDL_PollEvent(&se))
{
sdl_event(&se);
}
#endif
#ifdef WIN32
if(win)
{
// SetProp(win, "Sleepless-Client-Data");
MSG wmsg;
while(PeekMessage(&wmsg, win, 0, 0, PM_REMOVE))
{
TranslateMessage(&wmsg);
DispatchMessage(&wmsg);
}
}
#endif
/* dispatch any user posted events ... see postUserEvent() */
while(userEvents.length() > 0)
{
userEvent((unsigned long long)userEvents.next());
}
// Check for dirtiness
int n = widgets.length();
for(int i = 0; i < n; i++)
{
Widget *w = (Widget *)widgets.get((n - 1) - i);
if(!w->clean)
{
soil(); // XXX
w->clean = 1;
break;
}
}
}
#if defined(linux) //|| (defined(__APPLE__) && defined(__GNUC__))
/* translate SDL events into our own sleepy events */
void sdl_event(SDL_Event *event)
{
switch(event->type)
{
case SDL_KEYUP:
{
mod_shft = (event->key.keysym.mod & KMOD_SHIFT) ? 1 : 0;
mod_ctrl = (event->key.keysym.mod & KMOD_CTRL) ? 1 : 0;
switch(event->key.keysym.sym)
{
case SDLK_UP: keyUp(E_UP); break;
case SDLK_DOWN: keyUp(E_DOWN); break;
case SDLK_RIGHT: keyUp(E_RIGHT); break;
case SDLK_LEFT: keyUp(E_LEFT); break;
case SDLK_RCTRL:
case SDLK_LCTRL: keyUp(E_CONTROL); break;
case SDLK_RSHIFT:
case SDLK_LSHIFT: keyUp(E_SHIFT); break;
case SDLK_F1: keyUp(E_F1); break;
case SDLK_F2: keyUp(E_F2); break;
case SDLK_F3: keyUp(E_F3); break;
case SDLK_F4: keyUp(E_F4); break;
case SDLK_F5: keyUp(E_F5); break;
case SDLK_F6: keyUp(E_F6); break;
case SDLK_F7: keyUp(E_F7); break;
case SDLK_F8: keyUp(E_F8); break;
case SDLK_F9: keyUp(E_F9); break;
case SDLK_F10: keyUp(E_F10); break;
case SDLK_F11: keyUp(E_F11); break;
case SDLK_F12: keyUp(E_F12); break;
default: break;
}
if((event->key.keysym.sym & 0xffffff80) == 0)
character(event->key.keysym.sym);
}
break;
case SDL_KEYDOWN:
{
mod_shft = (event->key.keysym.mod & KMOD_SHIFT) ? 1 : 0;
mod_ctrl = (event->key.keysym.mod & KMOD_CTRL) ? 1 : 0;
switch(event->key.keysym.sym)
{
case SDLK_UP: keyDown(E_UP); break;
case SDLK_DOWN: keyDown(E_DOWN); break;
case SDLK_RIGHT: keyDown(E_RIGHT); break;
case SDLK_LEFT: keyDown(E_LEFT); break;
case SDLK_RCTRL:
case SDLK_LCTRL: keyDown(E_CONTROL); break;
case SDLK_RSHIFT:
case SDLK_LSHIFT: keyDown(E_SHIFT); break;
case SDLK_F1: keyDown(E_F1); break;
case SDLK_F2: keyDown(E_F2); break;
case SDLK_F3: keyDown(E_F3); break;
case SDLK_F4: keyDown(E_F4); break;
case SDLK_F5: keyDown(E_F5); break;
case SDLK_F6: keyDown(E_F6); break;
case SDLK_F7: keyDown(E_F7); break;
case SDLK_F8: keyDown(E_F8); break;
case SDLK_F9: keyDown(E_F9); break;
case SDLK_F10: keyDown(E_F10); break;
case SDLK_F11: keyDown(E_F11); break;
case SDLK_F12: keyDown(E_F12); break;
default: break;
}
}
break;
case SDL_MOUSEBUTTONDOWN:
pointerx = event->button.x;
pointery = event->button.y;
switch(event->button.button)
{
case SDL_BUTTON_LEFT:
leftButtonDown(pointerx, pointery);
break;
case SDL_BUTTON_RIGHT:
rightButtonDown(pointerx, pointery);
break;
}
break;
case SDL_MOUSEBUTTONUP:
pointerx = event->button.x;
pointery = event->button.y;
switch(event->button.button)
{
case SDL_BUTTON_LEFT:
leftButtonUp(pointerx, pointery);
break;
case SDL_BUTTON_RIGHT:
rightButtonUp(pointerx, pointery);
break;
}
break;
case SDL_MOUSEMOTION:
pointerx = event->button.x;
pointery = event->button.y;
pointer(pointerx, pointery);
break;
case SDL_VIDEOEXPOSE:
{
SDL_LockSurface(backBuf->surf);
paint();
SDL_UnlockSurface(backBuf->surf);
sdl.bbSurf = SDL_GetVideoSurface();
SDL_Rect srcRect;
srcRect.x = 0;
srcRect.y = 0;
srcRect.w = width;
srcRect.h = height;
SDL_Rect dstRect;
dstRect.x = bboffx;
dstRect.y = bboffy;
dstRect.w = width; // docs say this is ignored, but it isn't
dstRect.h = height;
//int alpha = 255;
//SDL_SetAlpha(backBuf->surf, SDL_SRCALPHA | SDL_RLEACCEL, alpha);
if(SDL_BlitSurface(backBuf->surf, &srcRect, sdl.bbSurf, &dstRect) < 0)
{
TRACE("blit: %s\n", SDL_GetError());
}
SDL_Flip(sdl.bbSurf);
}
break;
case SDL_ACTIVEEVENT:
switch(event->active.state)
{
case SDL_APPINPUTFOCUS:
{
if(event->active.gain)
gainFocus();
else
loseFocus();
}
break;
}
break;
case SDL_QUIT:
TRACE("**QUIT**\n");
close();
break;
default:
break;
}
}
#endif
#ifdef WIN32
/* translate win32 events into our own sleepy events */
LRESULT CALLBACK wproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch(uMsg)
{
case WM_CHAR:
// XXX pass along other stuff, like whether the key is a repeat
// key, etc.
// if((lParam & 0x40000000) == 0)
/* I don't know why I was only calling character() if the bit (above) was
clear. There was probably a reason, but I can't think of it now, and I
need the repeat keys, so I'm taking out the if() ... It'll probably come
back and bite me in the ass somewhere along the way. */
{
//TRACE("KEY: %x 0-15=%d 31=%d 30=%d\n", lParam, (lParam & 0xffff), lParam & 0x80000000, lParam & 0x40000000);
// XXX only doing this on key release ...
// thus, ignoring repeat keys
character((int)wParam);
}
return 0;
#if 0
// Windows virtual key codes.
#define VK_LBUTTON 0x01
#define VK_RBUTTON 0x02
#define VK_CANCEL 0x03
#define VK_MBUTTON 0x04
#define VK_BACK 0x08
#define VK_TAB 0x09
#define VK_CLEAR 0x0C
#define VK_RETURN 0x0D
#define VK_SHIFT 0x10
#define VK_CONTROL 0x11
#define VK_MENU 0x12
#define VK_PAUSE 0x13
#define VK_CAPITAL 0x14
#define VK_KANA 0x15
#define VK_HANGEUL 0x15
#define VK_HANGUL 0x15
#define VK_JUNJA 0x17
#define VK_FINAL 0x18
#define VK_HANJA 0x19
#define VK_KANJI 0x19
#define VK_ESCAPE 0x1B
#define VK_CONVERT 0x1C
#define VK_NONCONVERT 0x1D
#define VK_ACCEPT 0x1E
#define VK_MODECHANGE 0x1F
#define VK_SPACE 0x20
#define VK_PRIOR 0x21
#define VK_NEXT 0x22
#define VK_END 0x23
#define VK_HOME 0x24
#define VK_LEFT 0x25
#define VK_UP 0x26
#define VK_RIGHT 0x27
#define VK_DOWN 0x28
#define VK_SELECT 0x29
#define VK_PRINT 0x2A
#define VK_EXECUTE 0x2B
#define VK_SNAPSHOT 0x2C
#define VK_INSERT 0x2D
#define VK_DELETE 0x2E
#define VK_HELP 0x2F
#define VK_LWIN 0x5B
#define VK_RWIN 0x5C
#define VK_APPS 0x5D
#define VK_NUMPAD0 0x60
#define VK_NUMPAD1 0x61
#define VK_NUMPAD2 0x62
#define VK_NUMPAD3 0x63
#define VK_NUMPAD4 0x64
#define VK_NUMPAD5 0x65
#define VK_NUMPAD6 0x66
#define VK_NUMPAD7 0x67
#define VK_NUMPAD8 0x68
#define VK_NUMPAD9 0x69
#define VK_MULTIPLY 0x6A
#define VK_ADD 0x6B
#define VK_SEPARATOR 0x6C
#define VK_SUBTRACT 0x6D
#define VK_DECIMAL 0x6E
#define VK_DIVIDE 0x6F
#define VK_F1 0x70
#define VK_F2 0x71
#define VK_F3 0x72
#define VK_F4 0x73
#define VK_F5 0x74
#define VK_F6 0x75
#define VK_F7 0x76
#define VK_F8 0x77
#define VK_F9 0x78
#define VK_F10 0x79
#define VK_F11 0x7A
#define VK_F12 0x7B
#define VK_F13 0x7C
#define VK_F14 0x7D
#define VK_F15 0x7E
#define VK_F16 0x7F
#define VK_F17 0x80
#define VK_F18 0x81
#define VK_F19 0x82
#define VK_F20 0x83
#define VK_F21 0x84
#define VK_F22 0x85
#define VK_F23 0x86
#define VK_F24 0x87
#define VK_NUMLOCK 0x90
#define VK_SCROLL 0x91
#define VK_LSHIFT 0xA0
#define VK_RSHIFT 0xA1
#define VK_LCONTROL 0xA2
#define VK_RCONTROL 0xA3
#define VK_LMENU 0xA4
#define VK_RMENU 0xA5
#define VK_ATTN 0xF6
#define VK_CRSEL 0xF7
#define VK_EXSEL 0xF8
#define VK_EREOF 0xF9
#define VK_PLAY 0xFA
#define VK_ZOOM 0xFB
#define VK_NONAME 0xFC
#define VK_PA1 0xFD
#define VK_OEM_CLEAR 0xFE
// Values for lParam in KEYDOWN and KEYUP msgs.
// 0–15 Specifies the repeat count for the current message.
// The value is the number of times the keystroke is auto-repeated
// as a result of the user holding down the key. If the keystroke
// is held long enough, multiple messages are sent. However, the
// repeat count is not cumulative.
//
// 16–23 Specifies the scan code. The value depends on the
// original equipment manufacturer (OEM).
// 24 Specifies whether the key is an extended key, such as the
// right-hand alt and ctrl keys that appear on an enhanced
// 101- or 102-key keyboard. The value is 1 if it is an extended
// key; otherwise, it is 0.
// 25–28 Reserved; do not use.
// 29 Specifies the context code. The value is always 0 for a
// key-down message.
// 30 Specifies the previous key state. The value is 1 if the
// key is down before the message is sent, or it is 0 if the
// key is up.
// 31 Specifies the transition state. The value is always 0 for
// a key-down message.
#endif
case WM_KEYDOWN:
switch(wParam)
{
case VK_BACK:
keyDown(E_BACKSPACE); break;
case VK_DELETE:
keyDown(E_DELETE); break;
case VK_LEFT:
keyDown(E_LEFT); break;
case VK_RIGHT:
keyDown(E_RIGHT); break;
case VK_UP:
keyDown(E_UP); break;
case VK_DOWN:
keyDown(E_DOWN); break;
case VK_CONTROL:
keyDown(E_CONTROL); break;
case VK_SHIFT:
keyDown(E_SHIFT); break;
case VK_F1:
keyDown(E_F1); break;
case VK_F2:
keyDown(E_F2); break;
case VK_F3:
keyDown(E_F3); break;
case VK_F4:
keyDown(E_F4); break;
case VK_F5:
keyDown(E_F5); break;
case VK_F6:
keyDown(E_F6); break;
case VK_F7:
keyDown(E_F7); break;
case VK_F8:
keyDown(E_F8); break;
case VK_F9:
keyDown(E_F9); break;
case VK_F10:
keyDown(E_F10); break;
case VK_F11:
keyDown(E_F11); break;
case VK_F12:
keyDown(E_F12); break;
}
return 0;
case WM_KEYUP:
switch(wParam)
{
case VK_BACK:
keyUp(E_BACKSPACE); break;
case VK_DELETE:
keyUp(E_DELETE); break;
case VK_LEFT:
keyUp(E_LEFT); break;
case VK_RIGHT:
keyUp(E_RIGHT); break;
case VK_UP:
keyUp(E_UP); break;
case VK_DOWN:
keyUp(E_DOWN); break;
case VK_CONTROL:
keyUp(E_CONTROL); break;
case VK_SHIFT:
keyUp(E_SHIFT); break;
case VK_F1:
keyUp(E_F1); break;
case VK_F2:
keyUp(E_F2); break;
case VK_F3:
keyUp(E_F3); break;
case VK_F4:
keyUp(E_F4); break;
case VK_F5:
keyUp(E_F5); break;
case VK_F6:
keyUp(E_F6); break;
case VK_F7:
keyUp(E_F7); break;
case VK_F8:
keyUp(E_F8); break;
case VK_F9:
keyUp(E_F9); break;
case VK_F10:
keyUp(E_F10); break;
case VK_F11:
keyUp(E_F11); break;
case VK_F12:
keyUp(E_F12); break;
}
return 0;
case WM_MOUSEMOVE:
pointerx = LOWORD(lParam) - bboffx;
pointery = HIWORD(lParam) - bboffy;
pointer(pointerx, pointery);
return 0;
case WM_LBUTTONDOWN:
pointerx = LOWORD(lParam) - bboffx;
pointery = HIWORD(lParam) - bboffy;
leftButtonDown(pointerx, pointery);
return 0;
case WM_LBUTTONUP:
pointerx = LOWORD(lParam) - bboffx;
pointery = HIWORD(lParam) - bboffy;
leftButtonUp(pointerx, pointery);
return 0;
case WM_RBUTTONDOWN:
pointerx = LOWORD(lParam) - bboffx;
pointery = HIWORD(lParam) - bboffy;
rightButtonDown(pointerx, pointery);
return 0;
case WM_RBUTTONUP:
pointerx = LOWORD(lParam) - bboffx;
pointery = HIWORD(lParam) - bboffy;
rightButtonUp(pointerx, pointery);
return 0;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
paint();
// Blit backbuffer to real window
RECT rc;
GetClientRect(hwnd, &rc);
BitBlt(hdc, bboffx, bboffy, rc.right-rc.left, rc.bottom-rc.top, backBuf->dc, 0, 0, SRCCOPY);
EndPaint(hwnd, &ps);
}
return 0;
case WM_ACTIVATE:
{
int a = LOWORD(wParam);
switch(a)
{
case WA_CLICKACTIVE: // activated by mouse click
case WA_ACTIVE: // activated (not by mouse click)
gainFocus();
break;
case WA_INACTIVE:
loseFocus();
break;
}
}
return 0;
case WM_DESTROY:
close();
return 0;
// default:
//TRACE("winmsg=%x\n", uMsg);
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
#endif
#ifdef WIN32
static LRESULT CALLBACK swproc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
WDisplay *display = (WDisplay *)GetProp(hwnd, "sleepylib-gui-Window");
if(display)
return display->wproc(hwnd, uMsg, wParam, lParam);
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
#endif
////////////////////////////////////////////////////////////////////
/* force a redraw */
void soil()
{
#ifdef __APPLE__
postUserEvent( E_REFRESH_DISPLAY );
#endif
#if defined(linux) //|| (defined(__APPLE__) && defined(__GNUC__))
SDL_Event e;
e.expose.type = SDL_VIDEOEXPOSE;
SDL_PushEvent(&e);
#endif
#ifdef WIN32
InvalidateRect(win, 0, 0);
#endif
}
void findOver()
{
Widget *w = findWidget(pointerx, pointery);
if(w != over)
{
if(over)
over->losePointer();
over = w;
if(over)
over->gainPointer();
}
}
/* returns true if the widget 'vw' is present on the list of
active widgets. */
int isPresent(Widget *vw)
{
return widgets.locate(vw) != -1;
}
/* Adds a widget to the list of active widgets on this display */
void add(struct Widget *w)
{
widgets.append(w);
findOver();
soil();
}
/* Same as add(), but doesn't add the widget if it's already present */
void addOnce(struct Widget *w)
{
if(!isPresent(w))
add(w);
}
/* Add a widget immediately before some other one.
If the other widget isn't present, just append it with add(). */
void addBefore(struct Widget *aw, struct Widget *ow)
{
int n = widgets.locate(ow);
if(n == -1)
widgets.append(aw);
else
widgets.insert(aw, n);
findOver();
soil();
}
/* Remove widget 'tw' from the display */
Widget *remove(struct Widget *tw)
{
Widget *w = (Widget *)widgets.rem(tw);
if(w)
{
if(w == focus)
setFocus(0);
if(w->havePointer)
w->losePointer();
if(over == w)
over = 0;
soil();
}
findOver();
return w;
}
/* Remove all the widgets from the display */
void removeAll()
{
while(true)
{
Widget *w = (Widget *)widgets.get(0);
if(w == 0)
break;
remove(w);
}
}
/* find the topmost widget on the display that is under x,y */
Widget *findWidget(int px, int py)
{
int n = widgets.length();
for(int i = 0; i < n; i++)
{
int t = (n - 1) - i;
Widget *w = (Widget *)widgets.get(t);
if(w)
{
if(w->contains(px, py))
return w;
}
}
return 0;
}
virtual void paint()
{
int n = widgets.length();
for(int i = 0; i < n; i++)
{
Widget *w = (Widget *)widgets.get(i);
if(w)
w->paint(backBuf);
}
if(showDebug)
{
char buf[200];
int px = pointerx;
int py = pointery;
float pctx = (float)px / width;
float pcty = (float)py / height;
int lx = -1;
int ly = -1;
if(over)
{
lx = pointerx - over->x;
ly = pointery - over->y;
}
backBuf->fillRect(4, 4, 450, 70, 0x000000, 0.6f);
sprintf(buf, "Pointer: %d,%d (local %d,%d) %.3f%%,%.3f%%", px, py, lx, ly, pctx, pcty);
defFont->draw(backBuf, buf, 10, 12, 0x00dddd);
sprintf(buf, "ctrl=%d shft=%d rframe=%d", mod_ctrl, mod_shft, rframe);
defFont->draw(backBuf, buf, 10, 24, 0x00dddd);
if(over)
{
backBuf->drawRect(over->x, over->y, over->w, over->h, 0xff0000, 0.7f);
sprintf(buf, "over=%p focus=%p", over, focus);
defFont->draw(backBuf, buf, 10, 36, 0x00dddd);
sprintf(buf, "loc=%d,%d size=%d,%d", over->x, over->y, over->w, over->h);
defFont->draw(backBuf, buf, 10, 48, 0x00dddd);
}
}
rframe++;
}
void setFocus(Widget *w)
{
if(w != focus)
{
if(focus)
focus->loseFocus();
focus = w;
if(focus)
focus->gainFocus();
}
}
void iconify()
{
#ifdef WIN32
ShowWindow(win, SW_MINIMIZE);
#endif
}
///////////////////////////////////////////////////////////////
// The display (window) has gained focus
virtual void gainFocus()
{
}
// The display (window) has lost focus
virtual void loseFocus()
{
}
virtual void close()
{
quit = 1;
}
// XXX experimental widget saving/loading stuff
void saveWidgets()
{
int n = widgets.length();
for(int i = 0; i < n; i++)
{
Widget *w = (Widget *)widgets.get(i);
if(w)
w->save();
}
}
virtual void character(int ch)
{
#if defined(_DEBUG) || defined(DEBUG)
if(ch == '`')
{
showDebug = !showDebug;
}
else
if(ch == 'S')
{
if(showDebug)
saveWidgets();
}
#endif
if(ch == 9) // HT (TAB)
{
if(focus)
{
if(focus->tabTo)
{
setFocus(focus->tabTo);
return;
}
// act same as ENTER
ch = 10;
}
}
if(focus)
{
focus->character(ch);
if((ch == 10) || (ch == 13)) // LF / CR
{
setFocus(0);
soil();
}
}
}
virtual void keyDown(long int k)
{
#if defined(_DEBUG) || defined(DEBUG)
if(showDebug)
{
if(over)
{
if(k == E_LEFT)
over->setLocation(over->x - 1, over->y );
if(k == E_RIGHT)
over->setLocation(over->x + 1, over->y );
if(k == E_UP)
over->setLocation(over->x , over->y - 1);
if(k == E_DOWN)
over->setLocation(over->x , over->y + 1);
}
}
#endif
if(k == E_CONTROL)
mod_ctrl = 1;
else
if(k == E_SHIFT)
mod_shft = 1;
if(focus)
focus->keyDown(k);
}
virtual void keyUp(long int k)
{
if(k == E_CONTROL)
mod_ctrl = 0;
else
if(k == E_SHIFT)
mod_shft = 0;
if(focus)
focus->keyUp(k);
}
virtual void pointer(int px, int py)
{
findOver();
if(over)
over->pointer(px - over->x, py - over->y);
if(dragged)
{
if(draggedSizing)
dragged->setSize(px - dragged->x + 4, py - dragged->y + 4);
else
dragged->setLocation(px - draggedOffX, py - draggedOffY);
}
if(showDebug)
soil();
}
virtual void leftButtonDown(int px, int py)
{
if(showDebug && mod_ctrl)
{
dragged = over;
if(dragged)
{
draggedOffX = px - dragged->x;
draggedOffY = py - dragged->y;
draggedSizing = 0;
if(((draggedOffX + 10) > dragged->w) && ((draggedOffY + 10) > dragged->h))
draggedSizing = 1;
}
}
else
{
setFocus(over);
if(focus)
focus->leftButtonDown(px - focus->x, py - focus->y);
}
}
virtual void leftButtonUp(int px, int py)
{
if(focus)
focus->leftButtonUp(px - focus->x, py - focus->y);
dragged = 0;
}
virtual void rightButtonDown(int px, int py)
{
setFocus(over);
if(focus)
focus->rightButtonDown(px - focus->x, py - focus->y);
}
virtual void rightButtonUp(int px, int py)
{
if(focus)
focus->rightButtonUp(px - focus->x, py - focus->y);
}
/* Any events posted with postUserEvent() will be passed back into
this method */
virtual void userEvent(unsigned long long ue)
{
#ifdef __APPLE__
if( ue == E_REFRESH_DISPLAY ) {
ext_draw_start();
paint();
ext_draw_end();
}
#endif
}
};
struct WButton : Widget
{
Font *defFont;
Font *font;
char label[128];
int clrTxt;
int down;
Image *i_normal;
Image *i_normalRoll;
Image *i_clicked;
WButton()
{
defFont = new Font(12, 0, 0, 0, "helvetica");
font = defFont;
label[0] = 0;
clrTxt = 0x000000;
down = 0;
i_normal = 0;
i_normalRoll = 0;
i_clicked = 0;
setSize(60, 14);
}
~WButton()
{
if(defFont)
delete defFont;
}
// If 'n' is non-null, size is changed to size of image.
// If 'n' is null, then all bitmap imagery is disabled.
void setImagery(Image *n, Image *r, Image *c)
{
soil();
int nw = 0;
int nh = 0;
if(n && (n->width > nw))
nw = n->width;
if(r && (r->width > nw))
nw = r->width;
if(c && (c->width > nw))
nw = c->width;
if(n && (n->height > nh))
nh = n->height;
if(r && (r->height > nh))
nh = r->height;
if(c && (c->height > nh))
nh = c->height;
i_normal = n;
i_normalRoll = r;
i_clicked = c;
if(nw && nh)
{
setSize(nw, nh);
}
soil();
}
void setTextColor(int c)
{
clrTxt = c;
soil();
}
void setFont(Font *f)
{
soil();
font = f;
if(font == 0)
font = defFont;
soil();
}
void setLabel(const char *l)
{
soil();
strcpy(label, l);
soil();
}
//////////////////////////////////////////////////////////////////
virtual void click()
{
}
//////////////////////////////////////////////////////////////////
virtual void paint(Image *img)
{
if(i_normal || i_normalRoll || i_clicked)
{
if(down)
{
if(havePointer)
{
if(i_clicked)
img->blit(i_clicked, x, y, alpha, itran);
}
else
{
if(i_normal)
img->blit(i_normal, x, y, alpha, itran);
}
}
else
{
if(havePointer)
{
if(i_normalRoll)
img->blit(i_normalRoll, x, y, alpha, itran);
}
else
{
if(i_normal)
img->blit(i_normal, x, y, alpha, itran);
}
}
}
else
{
// fill bg
long int bgc = 0xcccccc;
img->fillRect(x, y, w, h, bgc, alpha);
long int cc = down ? 0x000000 : 0xffffff;
if(havePointer)
img->fillRect(x, y, w, h, cc, alpha * 0.3f);
// Draw raised 3d border
img->raisedRect(x, y, w, h, alpha);
}
// draw label
int tw, th;
font->textSize(label, &tw, &th);
int dx = x + ((w - tw) / 2);
int dy = y + ((h - th) / 2);
font->draw(img, label, dx, dy, clrTxt);
Widget::paint(img);
}
virtual void gainPointer()
{
soil();
Widget::gainPointer();
}
virtual void losePointer()
{
soil();
Widget::losePointer();
}
virtual void leftButtonDown(int lx, int ly)
{
Widget::leftButtonDown(lx, ly);
down = 1;
soil();
}
virtual void leftButtonUp(int lx, int ly)
{
Widget::leftButtonUp(lx, ly);
down = 0;
soil();
click();
}
////////////////////////////////////////////////////////////////////////
virtual void load(const char *s)
{
if(svf)
free(svf);
svf = dupstr(s);
if(svf)
{
File f(svf);
const char *b = (const char *)f.getBytes();
if(b)
{
int sx, sy, sw, sh;
char ifn[256];
char ifr[256];
char ifc[256];
char lab[80];
if(sscanf(b, "%d %d %d %d '%79[^']' '%255[^']' '%255[^']' '%255[^']'", &sx, &sy, &sw, &sh, lab, ifn, ifr, ifc) == 8)
{
setLocation(sx, sy);
setSize(sw, sh);
Image *img_n = new Image(ifn);
Image *img_r = new Image(ifr);
Image *img_c = new Image(ifc);
setImagery(img_n, img_r, img_c);
setLabel(lab);
}
}
}
}
virtual void save()
{
if(svf)
{
FILE *fp = fopen(svf, "wb");
if(fp)
{
const char *ifn = "";
if(i_normal && i_normal->fromFile)
ifn = i_normal->fromFile;
const char *ifr = "";
if(i_normalRoll && i_normalRoll->fromFile)
ifr = i_normalRoll->fromFile;
const char *ifc = "";
if(i_clicked && i_clicked->fromFile)
ifc = i_clicked->fromFile;
fprintf(fp, "%d %d %d %d '%s' '%s' '%s' '%s'", x, y, w, h, label, ifn, ifr, ifc);
fclose(fp);
}
}
}
};
enum WAlignment
{
ALIGN_LEFT,
ALIGN_CENTER,
ALIGN_RIGHT,
ALIGN_TOP,
ALIGN_MIDDLE,
ALIGN_BOTTOM,
};
struct WLabel : Widget
{
Font *defFont;
Font *font;
char label[128];
long int clrTxt;
Image *i_normal;
int halign; // 0 = left, 1 = center 2 = right
int valign; // 0 = top, 1 = center 2 = bottom
WLabel()
{
defFont = 0;
font = 0;
label[0] = 0;
clrTxt = 0x000000;
i_normal = 0;
halign = ALIGN_LEFT;
valign = ALIGN_TOP;
setBGColor(0xaaaaaa);
setBGAlpha(1.0f);
setSize(60, 14);
setLocation(0, 0);
defFont = new Font(12, 0, 0, 0, "helvetica");
font = defFont;
}
~WLabel()
{
if(defFont)
delete defFont;
}
void setFont(Font *f)
{
soil();
font = f;
if(font == 0)
font = defFont;
soil();
}
void setTextColor(int c)
{
clrTxt = c;
soil();
}
void setLabel(const char *l)
{
soil();
strcpy(label, l);
soil();
}
void setLabel(int n)
{
char buf[32];
sprintf(buf, "%d", n);
setLabel(buf);
}
void setSizeToLabel()
{
int tw, th;
font->textSize(label, &tw, &th);
setSize(tw, th);
}
void setImagery(Image *n)
{
soil();
i_normal = n;
if(i_normal)
{
setSize(i_normal->width, i_normal->height);
}
else
{
i_normal = 0;
}
soil();
}
void setAlignment(int ha, int va)
{
halign = ha;
valign = va;
soil();
}
void setAlignLeft()
{
setAlignment(ALIGN_LEFT, valign);
}
void setAlignCenter()
{
setAlignment(ALIGN_CENTER, valign);
}
void setAlignRight()
{
setAlignment(ALIGN_RIGHT, valign);
}
//////////////////////////////////////////////////////////////////
virtual void paint(Image *img)
{
Widget::paint(img);
if(i_normal)
img->blit(i_normal, x, y, alpha, itran);
int tw, th;
font->textSize(label, &tw, &th);
// Default center align
int dx = x;
int dy = y;
if(halign == ALIGN_CENTER)
dx = x + ((w - tw) / 2);
if(valign == ALIGN_MIDDLE)
dy = y + ((h - th) / 2);
if(halign == ALIGN_RIGHT)
dx = x + w - tw;
if(valign == ALIGN_BOTTOM)
dy = y + h - th;
font->draw(img, label, dx, dy, clrTxt, 0, 0, alpha);
}
////////////////////////////////////////////////////////////////////////
virtual void load(const char *s)
{
if(svf)
free(svf);
svf = dupstr(s);
if(svf)
{
File f(svf);
const char *b = (const char *)f.getBytes();
if(b)
{
int sx, sy, sw, sh;
char lab[80];
if(sscanf(b, "%d %d %d %d '%79[^']' ", &sx, &sy, &sw, &sh, lab) == 5)
{
setLocation(sx, sy);
setSize(sw, sh);
setLabel(lab);
}
}
}
}
virtual void save()
{
if(svf)
{
FILE *fp = fopen(svf, "wb");
if(fp)
{
fprintf(fp, "%d %d %d %d '%s'", x, y, w, h, label);
fclose(fp);
}
}
}
};
struct WTextField : Widget
{
Font *defFont;
Font *font;
int capacity;
char *text;
int clrTxt;
int caret;
int len;
Image *i_normal;
Image *i_normalRoll;
int halign; // 0 = left, 1 = center 2 = right
WTextField()
{
defFont = new Font(12, 0, 0, 0, "helvetica");
font = defFont;
capacity = 200;
text = new char[capacity + 1];
text[0] = 0;
clrTxt = 0x000000;
caret = 0;
len = 0;
i_normal = 0;
i_normalRoll = 0;
halign = ALIGN_LEFT;
setBGColor(0xffffff);
setBGAlpha(1.0f);
setSize(60, 14);
}
virtual ~WTextField()
{
if(text)
delete text;
if(defFont)
delete defFont;
}
void setCapacity(int nc)
{
if(nc > capacity)
{
char *ntext = new char[nc + 1];
if(!ntext)
return;
strcpy(ntext, text);
if(text)
delete text;
text = ntext;
}
capacity = nc;
text[capacity] = 0;
if(caret > capacity)
caret = capacity;
}
void setTextColor(int c)
{
clrTxt = c;
soil();
}
void setFont(Font *f)
{
soil();
font = f;
if(font == 0)
font = defFont;
soil();
}
void setText(const char *t)
{
soil();
strncpy(text, t, capacity);
text[capacity] = 0;
len = (int)strlen(text);
caret = len;
soil();
}
const char *getText()
{
return text;
}
void setImagery(Image *n, Image *nr)
{
soil();
i_normal = n;
if(i_normal)
{
setSize(i_normal->width, i_normal->height);
i_normalRoll = nr;
if(!i_normalRoll)
i_normalRoll = i_normal;
}
else
{
i_normal = 0;
i_normalRoll = 0;
}
soil();
}
void setAlignment(int ha)
{
halign = ha;
soil();
}
void setTabTo(Widget *w)
{
tabTo = w;
}
//////////////////////////////////////////////////////////////////
virtual void paint(Image *img)
{
if(i_normal)
{
if(havePointer)
img->blit(i_normalRoll, x, y, alpha, itran);
else
img->blit(i_normal, x, y, alpha, itran);
}
else
{
// fill bg
img->fillRect(x, y, w, h, clrBG, alpha * bgAlpha);
// Draw 3d border
// img->sunkenRect(x, y, w, h, alpha * bgAlpha);
}
// draw text
int tw, th;
font->textSize(text, &tw, &th);
int dx = x + 4;
if(halign == ALIGN_CENTER)
dx = x + ((w - tw) / 2);
if(halign == ALIGN_RIGHT)
dx = x + w - tw;
int dy = y + ((h - th) / 2);
font->draw(img, text, dx, dy, clrTxt);
// draw caret
if(haveFocus)
{
font->textSize(text, caret, &tw, &th);
dx += tw;
// I have to do " " cause textSize() on "" gives 0 height.
font->textSize("I", 1, &tw, &th);
dy = y + ((h - th) / 2);
img->fillRect(dx , dy, 1, th, clrTxt, alpha);
img->fillRect(dx - 1, dy, 3, 1, clrTxt, alpha);
img->fillRect(dx - 1, dy + th - 1, 3, 1, clrTxt, alpha);
}
}
virtual void character(int ch)
{
if(ch == 8) // BS
{
if(len > 0)
{
soil();
text[--len] = 0;
caret--;
soil();
}
return;
}
if(ch == 127) // DEL
{
return;
}
if((ch == 10) || (ch == 13)) // LF / CR
{
enter();
return;
}
if((ch < 32) || (ch > 126))
return;
if(len < capacity)
{
soil();
text[len++] = ch;
text[len] = 0;
caret++;
soil();
}
}
/* Called when enter key is pressed on this text field */
virtual void enter()
{
}
void trim()
{
Str::trim(text);
}
virtual void gainPointer()
{
soil();
Widget::gainPointer();
}
virtual void losePointer()
{
soil();
Widget::losePointer();
}
virtual void gainFocus()
{
soil();
Widget::gainFocus();
}
virtual void loseFocus()
{
soil();
Widget::loseFocus();
}
};
struct WCheckBox : Widget
{
int checked;
Image *i_normal;
Image *i_normalRoll;
Image *i_checked;
Image *i_checkedRoll;
WCheckBox()
{
checked = 0;
i_normal = 0;
i_normalRoll = 0;
i_checked = 0;
i_checkedRoll = 0;
setSize(12, 12);
}
// size is changed to size of largest image.
virtual void setImagery(Image *n, Image *nr, Image *c, Image *cr)
{
soil();
int nw = 0;
int nh = 0;
if(n && (n->width > nw))
nw = n->width;
if(nr && (nr->width > nw))
nw = nr->width;
if(c && (c->width > nw))
nw = c->width;
if(cr && (cr->width > nw))
nw = cr->width;
if(n && (n->height > nh))
nh = n->height;
if(nr && (nr->height > nh))
nh = nr->height;
if(c && (c->height > nh))
nh = c->height;
if(cr && (cr->height > nh))
nh = cr->height;
i_normal = n;
i_normalRoll = nr;
i_checked = c;
i_checkedRoll = cr;
if(nw && nh) //i_normal)
{
setSize(nw, nh); //i_normal->width, i_normal->height);
}
soil();
}
virtual void check()
{
}
virtual void uncheck()
{
}
virtual int setChecked(int tf)
{
int was = checked;
soil();
checked = tf;
if(checked)
check();
else
uncheck();
soil();
return was;
}
//////////////////////////////////////////////////////////////////
virtual void paint(Image *img)
{
if(i_normal || i_normalRoll || i_checked || i_checkedRoll)
{
if(checked)
{
if(havePointer)
{
if(i_checkedRoll)
img->blit(i_checkedRoll, x, y, alpha, itran);
}
else
{
if(i_checked)
img->blit(i_checked, x, y, alpha, itran);
}
}
else
{
if(havePointer)
{
if(i_normalRoll)
img->blit(i_normalRoll, x, y, alpha, itran);
}
else
{
if(i_normal)
img->blit(i_normal, x, y, alpha, itran);
}
}
}
else
{
// fill bg
img->fillRect(x, y, w, h, 0xffffff, alpha);
// Draw 3d border
img->sunkenRect(x, y, w, h, alpha);
// Draw black inner box if checked
if(checked)
img->fillRect(x+3, y+3, w-6, h-6, 0x000000, alpha);
}
Widget::paint(img);
}
virtual void leftButtonDown(int lx, int ly)
{
if(havePointer)
setChecked(!checked);
soil();
}
virtual void gainPointer()
{
soil();
Widget::gainPointer();
}
virtual void losePointer()
{
soil();
Widget::losePointer();
}
};
struct WRadioButton : WCheckBox
{
WRadioButton()
{
}
virtual void check()
{
}
virtual void uncheck()
{
}
virtual void leftButtonDown(int lx, int ly)
{
WCheckBox::leftButtonDown(lx, ly);
if(checked)
{
WRadioButton *rb = this;
while(true)
{
rb = (WRadioButton *)rb->cdata1;
if(!rb)
break;
if(rb == this)
break;
rb->setChecked(false);
rb->uncheck();
}
check();
}
}
};
struct WMenu : Widget
{
Font *defFont;
Font *font;
int maxItems;
int maxItemLength;
char *popBuf;
char **popItems;
int numItems;
// long int clrBG;
long int clrTxt;
long int clrBrdr;
long int clrHilite;
int lnsp;
int brdr;
int pad;
int shad;
int overItem;
WMenu()
{
// XXX Need to support defFont, and add a setFont() routine.
defFont = new Font(12, 0, 0, 0, "helvetica");
font = defFont;
maxItems = 100;
maxItemLength = 100;
popBuf = new char [maxItems * (maxItemLength + 1)];
popItems = new char *[maxItems];
for(int i = 0; i < maxItems; i++)
{
popItems[i] = popBuf + ((maxItemLength + 1) * i);
}
numItems = 0;
brdr = 1;
pad = 4;
lnsp = 4;
shad = 8;
clrTxt = 0x00000000;
clrBrdr = 0x00444444;
clrHilite = 0x00eeeeee;
overItem = -1;
w = (brdr * 2) + (pad * 2);
h = (brdr * 2) + (pad * 2);
setBGColor(0xcccccc);
}
virtual ~WMenu()
{
if(popItems)
delete popItems;
if(popBuf)
delete popBuf;
if(defFont)
delete defFont;
}
void setFont(Font *f)
{
soil();
font = f;
if(font == 0)
font = defFont;
soil();
}
void setTextColor(int c)
{
clrTxt = c;
soil();
}
void setHiliteColor(int c)
{
clrHilite = c;
soil();
}
//////////////////////
void clear()
{
overItem = -1;
numItems = 0;
}
void addItem(const char *t)
{
Str::copy(popItems[numItems++], t, maxItemLength);
reckonSize();
}
// XXX this is duplicated code (see paint() below)
void reckonSize()
{
int i;
int th = 0;
int dw = 0;
int dh = 0;
for(i = 0; i < numItems; i++)
{
int ww, hh;
font->textSize(popItems[i], &ww, &hh);
dh += hh + lnsp;
if(ww > dw)
dw = ww;
if(hh > th)
th = hh;
}
dh += (brdr * 2) + (pad * 2);
dw += (brdr * 2) + (pad * 2);
dw += shad;
dh += shad;
w = dw;
h = dh;
}
// Returns null if click is outside of menu
// Returns "" if click is inside menu, but NOT on an actual item.
// Returns "item" if click was on an item.
const char *itemAt(int _x, int _y)
{
if(!contains(x + _x, y + _y))
return 0;
if(numItems >= 1)
return popItems[ _y / (h / numItems) ];
return "";
}
const char *getItem(int n)
{
if(numItems < 1)
return 0;
if(n < 0)
return 0;
if(n >= numItems)
return 0;
return popItems[ n ];
}
///////////////////////////////////////////////////////////////////////
virtual void paint(Image *img)
{
int i;
int th = 0;
int dw = 0;
int dh = 0;
for(i = 0; i < numItems; i++)
{
int ww, hh;
font->textSize(popItems[i], &ww, &hh);
dh += hh + lnsp;
if(ww > dw)
dw = ww;
if(hh > th)
th = hh;
}
dh += (brdr * 2) + (pad * 2);
dw += (brdr * 2) + (pad * 2);
img->fillRect(x + shad, y + shad, dw, dh, 0x00000000, alpha * 0.3f);
img->fillRect(x + brdr, y + brdr, dw - (brdr * 2), dh - (brdr * 2), clrBG, alpha);
img->raisedRect(x, y, dw, dh, alpha);
int dx = x + brdr + pad;
int dy = y + brdr + pad;
int hw = dw - ((brdr + pad) * 2);
int hh = font->getHeight();
for(i = 0; i < numItems; i++)
{
if(i == overItem)
img->fillRect(dx-1, dy, hw+2, hh, clrHilite, alpha);
font->draw(img, popItems[i], dx, dy, clrTxt);
dy += th + lnsp;
}
}
virtual void pointer(int px, int py)
{
Widget::pointer(px, py);
int noi = -1;
if(numItems > 0)
{
if((px >= (brdr + pad)) && (py >= (brdr + pad)))
{
int ph = numItems * (font->getHeight() + lnsp);
{
noi = py / (ph / numItems);
}
}
}
if(noi != overItem)
{
if(noi != -1)
roll();
overItem = noi;
soil();
}
}
virtual void losePointer()
{
Widget::losePointer();
overItem = -1;
soil();
}
virtual void roll()
{
}
};
struct WScroller : Widget
{
int horvert; // 0 = horizontal, 1 = vertical
float knobPos; // Ranges from 0.0 (top or left) to 1.0 (bottom or right)
float knobSize; // Ranges from 0.0 (no knob) thru 1.0 (full size knob)
int knobDown; // True if knob is being dragged
int knobDragOffset; // offset of knob corner to click-to-drag spot
int overKnob; // True if mouse is over knob portion of widget.
int mdown; // True if mouse is mouse is down on this widget.
int downly;
float downKnobPos;
WScrollable *scrollable;
int pageClick;
WScroller()
{
setSize(20, 100);
horvert = 0;
knobPos = 0.3f; //0.0f;
knobSize = 0.3f; //1.0f;
knobDown = 0;
knobDragOffset = 0;
overKnob = 0;
mdown = 0;
downly = 0;
downKnobPos = 0.0f;
scrollable = 0;
pageClick = 0;
}
void setScrollable(WScrollable *sl)
{
scrollable = sl;
}
virtual void setKnobSize(float f)
{
if(f > 1.0f)
f = 1.0f;
if(f < 0.0f)
f = 0.0f;
knobSize = f;
if((knobPos + knobSize) >= 1.0f)
knobPos = 1.0f - knobSize;
soil();
}
virtual void setKnobPos(float f)
{
if(f > 1.0f)
f = 1.0f;
if(f < 0.0f)
f = 0.0f;
knobPos = f;
if((knobPos + knobSize) >= 1.0f)
knobPos = 1.0f - knobSize;
if(scrollable)
scrollable->posSet(knobPos);
soil();
}
virtual float getKnobSize()
{
return knobSize;
}
virtual float getKnobPos()
{
return knobPos;
}
////////////////////////////////////////////////////////////////
virtual void pointer(int lx, int ly)
{
if(!pageClick)
{
if(mdown)
{
if(ly != downly)
{
int pxoff = ly - downly;
int ky = (int)(h * downKnobPos);
int vy = ky + pxoff;
float frac = (float)vy / (float)(h - 1);
setKnobPos(frac);
}
else
{
setKnobPos(downKnobPos);
}
return;
}
int nok = 0;
int kx = 0;
int kw = w;
int ky = (int)(h * knobPos);
int kh = (int)(h * knobSize);
if((lx >= kx) && (ly >= ky))
{
if((lx < (kx + kw)) && (ly < (ky + kh)))
nok = 1;
}
if(nok != overKnob)
{
overKnob = nok;
soil();
}
}
Widget::pointer(lx, ly);
}
virtual void paint(Image *img)
{
if((w < 6) || (h < 6))
return;
// fill bg
long int bgc = 0x666666;
img->fillRect(x, y, w, h, bgc, alpha);
if(havePointer && !overKnob)
{
int cc = mdown ? 0x000000 : 0xffffff;
img->fillRect(x, y, w, h, cc, alpha * 0.20f);
}
// draw knob
if(knobSize > 0.0f)
{
int kx = x;
int kw = w;
int ky = (int)(y + (h * knobPos));
int kh = (int)(h * knobSize);
if(kh < 6)
kh = 6;
if(kw < 6)
kw = 6;
long int kc = 0xaaaaaa;
img->fillRect(kx, ky, kw, kh, kc, alpha);
img->raisedRect(kx, ky, kw, kh, alpha);
if(havePointer && overKnob)
{
int cc = mdown ? 0x000000 : 0xffffff;
img->fillRect(kx, ky, kw, kh, cc, alpha * 0.3f);
}
}
Widget::paint(img);
}
virtual void gainPointer()
{
soil();
Widget::gainPointer();
}
virtual void losePointer()
{
soil();
Widget::losePointer();
}
virtual void leftButtonDown(int lx, int ly)
{
mdown = 1;
if(overKnob)
{
downly = ly;
downKnobPos = knobPos;
}
else
{
pageClick = 1;
int ky = (int)(h * knobPos);
if(ly < ky)
{
setKnobPos(knobPos - knobSize);
}
else
{
setKnobPos(knobPos + knobSize);
}
}
soil();
}
virtual void leftButtonUp(int lx, int ly)
{
mdown = 0;
pageClick = 0;
soil();
}
};
struct WList : Widget, WScrollable
{
List list;
Font *font;
Font *defFont;
int first;
long int clrTxt;
long int clrHilite;
float hiliteAlpha;
WScroller *scroller;
int overItem;
WList()
{
defFont = new Font(12, 0, 0, 0, "helvetica");
font = defFont;
first = 0;
clrTxt = 0x000000;
clrHilite = 0xcccccc;
hiliteAlpha = 1.0f;
scroller = 0;
overItem = -1;
}
~WList()
{
if(defFont)
delete defFont;
}
void setScroller(WScroller *sl)
{
scroller = sl;
sync();
}
void setFont(Font *f)
{
soil();
font = f;
if(font == 0)
font = defFont;
soil();
}
void setTextColor(int c)
{
clrTxt = c;
soil();
}
void setHiliteColor(int c)
{
clrHilite = c;
soil();
}
void setHiliteAlpha(float a)
{
hiliteAlpha = a;
soil();
}
char *dupstr(const char *s)
{
char *n = new char[strlen(s) + 1];
if(n)
strcpy(n, s);
return n;
}
virtual void append(const char *s)
{
char *ns = dupstr(s);
if(!ns)
return;
soil();
list.append(ns);
sync();
soil();
}
virtual void remove(int n)
{
char *s = (char *)list.rem((int)n);
if(s)
{
soil();
free(s);
sync();
soil();
}
}
int length()
{
return list.length();
}
void clear()
{
soil();
while(list.length() > 0)
remove(0);
soil();
}
const char *get(int n)
{
return (const char *)list.get(n);
}
int find(const char *s)
{
int len = list.length();
for(int i = 0; i < len; i++)
{
const char *ms = (const char *)list.get(i);
if(Str::equals(ms, s))
return i;
}
return -1;
}
int findWithCase(const char *s)
{
int len = list.length();
for(int i = 0; i < len; i++)
{
const char *ms = (const char *)list.get(i);
if(Str::equalsCase(ms, s))
return i;
}
return -1;
}
const char *getSelected()
{
return (overItem == -1) ? 0 : (const char *)list.get(overItem);
}
static int cb_compare(const void *s1, const void *s2)
{
const char *p1 = *((const char **)s1);
const char *p2 = *((const char **)s2);
return Str::compare(p1, p2);
}
static int cb_compareCase(const void *s1, const void *s2)
{
const char *p1 = *((const char **)s1);
const char *p2 = *((const char **)s2);
return Str::compareCase(p1, p2);
}
virtual void sort()
{
soil();
list.sort(cb_compare);
soil();
}
virtual void sortCase()
{
soil();
list.sort(cb_compareCase);
soil();
}
////////////////////////////////////////////////////////////////////
void sync()
{
if(scroller)
{
int visible = (h - 4) / font->getHeight();
int len = list.length();
if(len > 0)
scroller->setKnobSize((float)visible / (float)len);
else
scroller->setKnobSize(0.0f);
scroller->setKnobPos((float)first / (float)len);
}
}
////////////////////////////////////////////////////////////////////
// This is the "Scrollable" interface. If there is a WScroller widget
// associated this WList (done by calling setScroller()), it will call
// posSet().
// If there are up/down/left/right buttons that have had this WList
// widget associated with THEM, then those buttons will call
// unitLess() and unitMore().
virtual void unitLess()
{
if(first > 0)
{
first--;
soil();
}
}
virtual void unitMore()
{
int visible = (h - 4) / font->getHeight();
if(first < (list.length() - visible))
{
first++;
soil();
}
}
virtual void posSet(float p)
{
first = (int)((list.length() + 1) * p);
soil();
}
////////////////////////////////////////////////////////////////////
virtual void paint(Image *img)
{
Widget::paint(img);
int fh = font->getHeight();
int dx = x + 2;
int dy = y + 2;
int n = (h - 4) / fh;
for(int i = 0; i < n; i++)
{
int m = first + i;
if(m < list.length())
{
if(m == overItem)
{
img->fillRect(x, 2 + y + (fh * i), w, fh, clrHilite, alpha * hiliteAlpha);
}
const char *s = (const char *)list.get(m);
font->draw(img, s, dx, dy, clrTxt);
dy += fh;
}
}
}
virtual void pointer(int lx, int ly)
{
int noi = -1;
if(list.length() > 0)
{
int visible = (h - 4) / font->getHeight();
if(visible > 0)
{
noi = first + (ly / font->getHeight());
if(noi >= (first + visible))
noi = (first + visible) - 1;
}
}
if(noi != overItem)
{
overItem = noi;
soil();
}
Widget::pointer(lx, ly);
}
virtual void losePointer()
{
Widget::losePointer();
overItem = -1;
soil();
}
};
#if 0
struct WSet
{
ImageBoss imgBoss;
TagShelf shelf;
char *filename;
WSet(const char *sf)
{
filename = dupstr(sf);
}
~WSet()
{
// dispose of widgets
while(true)
{
Widget *w = (Widget *)shelf.get(0);
if(!w)
break;
delete w;
}
if(filename)
free(filename);
}
// scan a single line and create a widget from it.
void grokLine(const char *line)
{
char name[32];
char fa[256];
char fb[256];
char fc[256];
int x, y, w, h;
if(sscanf(line, "%31[^ ] widget %d %d %d %d \"%255[^\"]\"\n", &x, &y, &w, &h, fa) == n)
{
Widget *w = new Widget();
if(w)
{
w->setLocation(x, y);
w->setSize(w, h);
w->setImage(imgBoss.getorload(fa));
shelf.put(name, w);
}
}
}
void load(const char *s)
{
char line[256];
FILE *fp = fopen(filename, "rb");
if(fp)
{
while(fgets(line, sizeof(line), fp))
{
grokLine(line);
}
fclose(fp);
}
#if 0
if(skfile)
free(skfile);
skfile = dupstr(s);
tl.readFile("data", skfile)
const char *val;
val = tl.get("i_bg")
if(val)
{
Image *img = imgBoss.load(val);
if(img)
setImage(img);
}
x = tl.getInt("x");
y = tl.getInt("y");
w = tl.getInt("w");
if(w < 1)
w = 1;
h = tl.getInt("h");
if(h < 1)
h = 1;
#endif
}
void save()
{
if(!filename)
return;
FILE *fp = fopen(filename, "wb");
if(fp)
{
int l = shelf.length();
for(int i = 0; i < l; i++)
{
const char *tag = shelf.getTag(i);
Widget *w = shelf.get(i);
}
fclose(fp);
}
}
};
#endif
//////////////////////////////////////////////////////////////////////////
#ifdef TEST_GUI // For testing the lib
#include <stdio.h>
struct MyButton : WButton
{
void leftButtonUp(int lx, int ly)
{
WButton::leftButtonUp(lx, ly);
if(havePointer)
{
char buf[128];
sprintf(buf, "ID=%d", id);
System::alert("Test Button", buf);
}
}
};
struct MyCheckBox : WCheckBox
{
void leftButtonDown(int lx, int ly)
{
WCheckBox::leftButtonDown(lx, ly);
char buf[128];
sprintf(buf, "ID=%d checked=%s", id, checked ? "yes" : "no");
System::alert("MyCheckBox", buf);
}
};
struct MyDisplay : WDisplay
{
WMenu *popMenu;
MyDisplay(const char *t, int _w, int _h, int fs) : WDisplay(t, _w, _h, fs)
{
popMenu = 0;
}
void paint()
{
backBuf->fillRect(0, 0, width, height, 0x00888888, 1.0f);
WDisplay::paint();
}
void rightButtonDown(int px, int py)
{
if(!popMenu)
popMenu = new WMenu();
remove(popMenu);
popMenu->clear();
popMenu->addItem("Menu item 1");
popMenu->addItem("Menu item 2");
popMenu->addItem("Menu item 3");
popMenu->setLocation(px, py);
add(popMenu);
WDisplay::rightButtonDown(px, py);
}
void leftButtonDown(int px, int py)
{
WDisplay::leftButtonDown(px, py);
if(popMenu)
{
int n = popMenu->overItem;
if(n != -1)
{
const char *mi = popMenu->getItem(n);
System::alert("Menu Item", mi);
}
remove(popMenu);
}
}
};
int main(int argc, char **argv)
{
TRACEON();
MyDisplay display("title", 400, 400, 0);
WButton *bt = new MyButton();
bt->setSize(80, 16);
bt->setLabel("MyButton");
bt->setLocation(10, 40);
bt->setID(13);
display.add(bt);
Image bt_n("bt1.bmp");
Image bt_nr("bt2.bmp");
Image bt_c("bt3.bmp");
bt = new MyButton();
bt->setLabel("Button +gfx");
bt->setLocation(120, 30);
bt->setImagery(&bt_n, &bt_nr, &bt_c);
bt->setID(77);
display.add(bt);
WLabel *lb = new WLabel();
lb->setLabel("Label");
lb->setSize(70, 16);
lb->setLocation(10, 70);
display.add(lb);
Image lb_n("lb1.bmp");
lb = new WLabel();
lb->setLabel("Label with graphic");
lb->setImagery(&lb_n);
lb->setLocation(90, 70);
display.add(lb);
WTextField *tf = new WTextField();
tf->setSize(100, 16);
tf->setLocation(10, 100);
tf->setText("Edit me!");
display.add(tf);
Image tf_n("tf1.bmp");
Image tf_nr("tf2.bmp");
tf = new WTextField();
tf->setSize(100, 16);
tf->setLocation(120, 100);
tf->setText("Edit me! Ain't I pretty?");
tf->setImagery(&tf_n, &tf_nr);
display.add(tf);
WCheckBox *cb = new MyCheckBox();
cb->setLocation(10, 130);
cb->setID(22);
display.add(cb);
Image cb_n("cb1.bmp");
Image cb_nr("cb2.bmp");
Image cb_c("cb3.bmp");
Image cb_cr("cb4.bmp");
cb = new MyCheckBox();
cb->setLocation(40, 130);
cb->setID(23);
cb->setImagery(&cb_n, &cb_nr, &cb_c, &cb_cr);
display.add(cb);
WScroller *sl = new WScroller();
sl->setLocation(10, 170);
display.add(sl);
Widget sv_widget;
sv_widget.load("sv_widget.txt");
display.add(&sv_widget);
WButton sv_button;
sv_button.load("sv_button.txt");
display.add(&sv_button);
WLabel sv_label;
sv_label.load("sv_label.txt");
display.add(&sv_label);
display.show();
while(!display.quit)
{
display.waitEvent();
display.dispatch();
}
return 0;
}
// Windows 95/98/ME/NT/2K/XP
#ifdef WIN32
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
return main(__argc, __argv);
}
#endif
#endif
#endif // com_sleepless_gui_gui_cpp