@@ -4,10 +4,6 @@ include_directories(${CMAKE_SOURCE_DIR}/common) | |||
include_directories(${CMAKE_SOURCE_DIR}/common/rfb) | |||
add_library(tx STATIC | |||
TXWindow.cxx | |||
TXScrollbar.cxx | |||
TXViewport.cxx | |||
TXImage.cxx | |||
TXMenu.cxx) | |||
TXWindow.cxx) | |||
target_link_libraries(tx ${X11_LIBRARIES}) |
@@ -1,191 +0,0 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
// | |||
// TXEntry.h | |||
// | |||
// A TXEntry allows you to enter a single line of text in a window. The entry | |||
// must be tall enough to contain a line of text - if not then it will be | |||
// resized appropriately. If the passwd argument to the constructor is true, | |||
// then the text in the entry will be replaced by asterisks on the screen. | |||
// | |||
#ifndef __TXENTRY_H__ | |||
#define __TXENTRY_H__ | |||
#include "TXWindow.h" | |||
#include <X11/keysym.h> | |||
#ifndef XK_ISO_Left_Tab | |||
#define XK_ISO_Left_Tab 0xFE20 | |||
#endif | |||
#ifndef XK_KP_Delete | |||
#define XK_KP_Delete 0xFF9F | |||
#endif | |||
// TXEntryCallback's entryCallback() method is called when one of three special | |||
// key presses have happened: Enter/Return, forward tab, or backward tab. | |||
class TXEntry; | |||
class TXEntryCallback { | |||
public: | |||
enum Detail { ENTER, NEXT_FOCUS, PREV_FOCUS }; | |||
virtual void entryCallback(TXEntry* entry, Detail detail, Time time)=0; | |||
}; | |||
class TXEntry : public TXWindow, public TXEventHandler { | |||
public: | |||
TXEntry(Display* dpy_, TXEntryCallback* cb_=0, | |||
TXWindow* parent_=0, bool passwd_=false, int w=1, int h=1) | |||
: TXWindow(dpy_, w, h, parent_), cb(cb_), | |||
passwd(passwd_), disabled_(false), gotFocus(false) | |||
{ | |||
setEventHandler(this); | |||
gc = XCreateGC(dpy, win(), 0, 0); | |||
addEventMask(ExposureMask | KeyPressMask | FocusChangeMask | |||
| ButtonPressMask); | |||
text[0] = 0; | |||
int textHeight = (defaultFS->ascent + defaultFS->descent); | |||
int newHeight = __rfbmax(height(), textHeight + yPad*2 + bevel*2); | |||
if (height() < newHeight) { | |||
resize(width(), newHeight); | |||
} | |||
} | |||
virtual ~TXEntry() { | |||
XFreeGC(dpy, gc); | |||
// overwrite memory used to store password - not critical, but can avoid | |||
// accidental exposure of a password in uninitialised memory. | |||
if (passwd) | |||
memset(text, 0, maxLen); | |||
} | |||
// getText() gets the text in the entry. | |||
const char* getText() { return text; } | |||
// setText() sets the text in the entry. | |||
void setText(const char* text_) { | |||
strncpy(text, text_, maxLen-1); | |||
text[maxLen-1] = 0; | |||
paint(); | |||
} | |||
// disabled() sets or queries the disabled state of the entry. A disabled | |||
// entry cannot have text entered into it. | |||
void disabled(bool b) { disabled_ = b; paint(); } | |||
bool disabled() { return disabled_; } | |||
private: | |||
void paint() { | |||
if (disabled_) | |||
drawBevel(gc, 0, 0, width(), height(), bevel, disabledBg,darkBg,lightBg); | |||
else | |||
drawBevel(gc, 0, 0, width(), height(), bevel, enabledBg, darkBg,lightBg); | |||
char* str = text; | |||
char stars[maxLen]; | |||
if (passwd) { | |||
int i; | |||
for (i = 0; i < (int)strlen(text); i++) stars[i] = '*'; | |||
stars[i] = 0; | |||
str = stars; | |||
} | |||
int tw = XTextWidth(defaultFS, str, strlen(str)); | |||
int startx = bevel + xPad; | |||
if (startx + tw > width() - 2*bevel) { | |||
startx = width() - 2*bevel - tw; | |||
} | |||
XDrawString(dpy, win(), defaultGC, startx, | |||
(height() + defaultFS->ascent - defaultFS->descent) / 2, | |||
str, strlen(str)); | |||
if (!disabled_ && gotFocus) | |||
XDrawLine(dpy, win(), defaultGC, startx+tw, | |||
(height() - defaultFS->ascent - defaultFS->descent) / 2, | |||
startx+tw, | |||
(height() + defaultFS->ascent + defaultFS->descent) / 2); | |||
} | |||
virtual void handleEvent(TXWindow* w, XEvent* ev) { | |||
switch (ev->type) { | |||
case Expose: | |||
paint(); | |||
break; | |||
case FocusIn: | |||
gotFocus = true; | |||
paint(); | |||
break; | |||
case FocusOut: | |||
gotFocus = false; | |||
paint(); | |||
break; | |||
case ButtonPress: | |||
if (!disabled_) | |||
XSetInputFocus(dpy, win(), RevertToParent, ev->xbutton.time); | |||
break; | |||
case KeyPress: | |||
{ | |||
if (disabled_ || !gotFocus) break; | |||
KeySym keysym; | |||
XComposeStatus compose; | |||
char buf[10]; | |||
int count = XLookupString(&ev->xkey, buf, 10, &keysym, &compose); | |||
if (count >= 1 && buf[0] >= ' ' && buf[0] <= '~') { | |||
if (strlen(text) + count >= maxLen) { | |||
XBell(dpy, 0); | |||
} else { | |||
strncat(text, buf, count); | |||
paint(); | |||
} | |||
} else if (keysym == XK_BackSpace || keysym == XK_Delete || | |||
keysym == XK_KP_Delete) { | |||
if (strlen(text) > 0) { | |||
text[strlen(text)-1] = 0; | |||
paint(); | |||
} | |||
} else if (keysym == XK_Return || keysym == XK_KP_Enter || | |||
keysym == XK_Linefeed) { | |||
if (cb) cb->entryCallback(this, TXEntryCallback::ENTER, | |||
ev->xkey.time); | |||
} else if ((keysym == XK_Tab || keysym == XK_KP_Tab) | |||
&& !(ev->xkey.state & ShiftMask)) | |||
{ | |||
if (cb) cb->entryCallback(this, TXEntryCallback::NEXT_FOCUS, | |||
ev->xkey.time); | |||
} else if (((keysym == XK_Tab || keysym == XK_KP_Tab) | |||
&& (ev->xkey.state & ShiftMask)) | |||
|| keysym == XK_ISO_Left_Tab) | |||
{ | |||
if (cb) cb->entryCallback(this, TXEntryCallback::PREV_FOCUS, | |||
ev->xkey.time); | |||
} | |||
} | |||
} | |||
} | |||
GC gc; | |||
enum { maxLen = 256 }; | |||
char text[maxLen]; | |||
TXEntryCallback* cb; | |||
bool passwd; | |||
bool disabled_; | |||
bool gotFocus; | |||
}; | |||
#endif |
@@ -1,361 +0,0 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
// | |||
// TXImage.cxx | |||
// | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <strings.h> | |||
#include <sys/types.h> | |||
#include <sys/ipc.h> | |||
#include <sys/shm.h> | |||
#include <X11/Xlib.h> | |||
#include <X11/Xutil.h> | |||
#include <list> | |||
#include <rfb/TransImageGetter.h> | |||
#include <rfb/Exception.h> | |||
#include <rfb/LogWriter.h> | |||
#include "TXWindow.h" | |||
#include "TXImage.h" | |||
using namespace rfb; | |||
static rfb::LogWriter vlog("TXImage"); | |||
TXImage::TXImage(Display* d, int width, int height, Visual* vis_, int depth_) | |||
: xim(0), dpy(d), vis(vis_), depth(depth_), tig(0), cube(0) | |||
{ | |||
#ifdef HAVE_MITSHM | |||
shminfo = 0; | |||
#endif | |||
width_ = width; | |||
height_ = height; | |||
for (int i = 0; i < 256; i++) | |||
colourMap[i].r = colourMap[i].g = colourMap[i].b = 0; | |||
if (!vis) | |||
vis = DefaultVisual(dpy,DefaultScreen(dpy)); | |||
if (!depth) | |||
depth = DefaultDepth(dpy,DefaultScreen(dpy)); | |||
createXImage(); | |||
getNativePixelFormat(vis, depth); | |||
colourmap = this; | |||
format.bpp = 0; // just make it different to any valid format, so that... | |||
setPF(nativePF); // ...setPF() always works | |||
} | |||
TXImage::~TXImage() | |||
{ | |||
if (data != (rdr::U8*)xim->data) delete [] data; | |||
destroyXImage(); | |||
delete tig; | |||
delete cube; | |||
} | |||
void TXImage::resize(int w, int h) | |||
{ | |||
if (w == width() && h == height()) return; | |||
int oldStrideBytes = getStride() * (format.bpp/8); | |||
int rowsToCopy = __rfbmin(h, height()); | |||
int bytesPerRow = __rfbmin(w, width()) * (format.bpp/8); | |||
rdr::U8* oldData = 0; | |||
bool allocData = false; | |||
if (data != (rdr::U8*)xim->data) { | |||
oldData = (rdr::U8*)data; | |||
allocData = true; | |||
} else { | |||
oldData = new rdr::U8[xim->bytes_per_line * height()]; | |||
memcpy(oldData, xim->data, xim->bytes_per_line * height()); | |||
} | |||
destroyXImage(); | |||
width_ = w; | |||
height_ = h; | |||
createXImage(); | |||
if (allocData) | |||
data = new rdr::U8[width() * height() * (format.bpp/8)]; | |||
else | |||
data = (rdr::U8*)xim->data; | |||
int newStrideBytes = getStride() * (format.bpp/8); | |||
for (int i = 0; i < rowsToCopy; i++) | |||
memcpy((rdr::U8*)data + newStrideBytes * i, oldData + oldStrideBytes * i, | |||
bytesPerRow); | |||
delete [] oldData; | |||
} | |||
void TXImage::setPF(const PixelFormat& newPF) | |||
{ | |||
if (newPF.equal(format)) return; | |||
format = newPF; | |||
if (data != (rdr::U8*)xim->data) delete [] data; | |||
delete tig; | |||
tig = 0; | |||
if (format.equal(nativePF) && format.trueColour) { | |||
data = (rdr::U8*)xim->data; | |||
} else { | |||
data = new rdr::U8[width() * height() * (format.bpp/8)]; | |||
tig = new TransImageGetter(); | |||
tig->init(this, nativePF, 0, cube); | |||
} | |||
} | |||
int TXImage::getStride() const | |||
{ | |||
if (data == (rdr::U8*)xim->data) | |||
return xim->bytes_per_line / (xim->bits_per_pixel / 8); | |||
else | |||
return width(); | |||
} | |||
void TXImage::put(Window win, GC gc, const rfb::Rect& r) | |||
{ | |||
if (r.is_empty()) return; | |||
int x = r.tl.x; | |||
int y = r.tl.y; | |||
int w = r.width(); | |||
int h = r.height(); | |||
if (data != (rdr::U8*)xim->data) { | |||
rdr::U8* ximDataStart = ((rdr::U8*)xim->data + y * xim->bytes_per_line | |||
+ x * (xim->bits_per_pixel / 8)); | |||
tig->getImage(ximDataStart, r, | |||
xim->bytes_per_line / (xim->bits_per_pixel / 8)); | |||
} | |||
#ifdef HAVE_MITSHM | |||
if (usingShm()) { | |||
XShmPutImage(dpy, win, gc, xim, x, y, x, y, w, h, False); | |||
return; | |||
} | |||
#endif | |||
XPutImage(dpy, win, gc, xim, x, y, x, y, w, h); | |||
} | |||
void TXImage::setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs) | |||
{ | |||
for (int i = 0; i < nColours; i++) { | |||
colourMap[firstColour+i].r = rgbs[i*3]; | |||
colourMap[firstColour+i].g = rgbs[i*3+1]; | |||
colourMap[firstColour+i].b = rgbs[i*3+2]; | |||
} | |||
} | |||
void TXImage::updateColourMap() | |||
{ | |||
if (tig != 0) | |||
tig->setColourMapEntries(0, 0); | |||
} | |||
void TXImage::lookup(int index, int* r, int* g, int* b) | |||
{ | |||
*r = colourMap[index].r; | |||
*g = colourMap[index].g; | |||
*b = colourMap[index].b; | |||
} | |||
#ifdef HAVE_MITSHM | |||
static bool caughtError = false; | |||
static int XShmAttachErrorHandler(Display *dpy, XErrorEvent *error) | |||
{ | |||
caughtError = true; | |||
return 0; | |||
} | |||
class TXImageCleanup { | |||
public: | |||
std::list<TXImage*> images; | |||
~TXImageCleanup() { | |||
while (!images.empty()) | |||
delete images.front(); | |||
} | |||
}; | |||
static TXImageCleanup imageCleanup; | |||
#endif | |||
void TXImage::createXImage() | |||
{ | |||
#ifdef HAVE_MITSHM | |||
int major, minor; | |||
Bool pixmaps; | |||
if (XShmQueryVersion(dpy, &major, &minor, &pixmaps)) { | |||
shminfo = new XShmSegmentInfo; | |||
xim = XShmCreateImage(dpy, vis, depth, ZPixmap, | |||
0, shminfo, width(), height()); | |||
if (xim) { | |||
shminfo->shmid = shmget(IPC_PRIVATE, | |||
xim->bytes_per_line * xim->height, | |||
IPC_CREAT|0777); | |||
if (shminfo->shmid != -1) { | |||
shminfo->shmaddr = xim->data = (char*)shmat(shminfo->shmid, 0, 0); | |||
if (shminfo->shmaddr != (char *)-1) { | |||
shminfo->readOnly = False; | |||
XErrorHandler oldHdlr = XSetErrorHandler(XShmAttachErrorHandler); | |||
XShmAttach(dpy, shminfo); | |||
XSync(dpy, False); | |||
XSetErrorHandler(oldHdlr); | |||
if (!caughtError) { | |||
vlog.debug("Using shared memory XImage"); | |||
imageCleanup.images.push_back(this); | |||
return; | |||
} | |||
shmdt(shminfo->shmaddr); | |||
} else { | |||
vlog.error("shmat failed"); | |||
perror("shmat"); | |||
} | |||
shmctl(shminfo->shmid, IPC_RMID, 0); | |||
} else { | |||
vlog.error("shmget failed"); | |||
perror("shmget"); | |||
} | |||
XDestroyImage(xim); | |||
xim = 0; | |||
} else { | |||
vlog.error("XShmCreateImage failed"); | |||
} | |||
delete shminfo; | |||
shminfo = 0; | |||
} | |||
#endif | |||
xim = XCreateImage(dpy, vis, depth, ZPixmap, | |||
0, 0, width(), height(), BitmapPad(dpy), 0); | |||
xim->data = (char*)malloc(xim->bytes_per_line * xim->height); | |||
if (!xim->data) { | |||
vlog.error("malloc failed"); | |||
exit(1); | |||
} | |||
} | |||
void TXImage::destroyXImage() | |||
{ | |||
#ifdef HAVE_MITSHM | |||
if (shminfo) { | |||
vlog.debug("Freeing shared memory XImage"); | |||
shmdt(shminfo->shmaddr); | |||
shmctl(shminfo->shmid, IPC_RMID, 0); | |||
delete shminfo; | |||
shminfo = 0; | |||
imageCleanup.images.remove(this); | |||
} | |||
#endif | |||
// XDestroyImage() will free(xim->data) if appropriate | |||
if (xim) XDestroyImage(xim); | |||
xim = 0; | |||
} | |||
static bool supportedBPP(int bpp) { | |||
return (bpp == 8 || bpp == 16 || bpp == 32); | |||
} | |||
static int depth2bpp(Display* dpy, int depth) | |||
{ | |||
int nformats; | |||
XPixmapFormatValues* format = XListPixmapFormats(dpy, &nformats); | |||
int i; | |||
for (i = 0; i < nformats; i++) | |||
if (format[i].depth == depth) break; | |||
if (i == nformats || !supportedBPP(format[i].bits_per_pixel)) | |||
throw rfb::Exception("Error: couldn't find suitable pixmap format"); | |||
int bpp = format[i].bits_per_pixel; | |||
XFree(format); | |||
return bpp; | |||
} | |||
void TXImage::getNativePixelFormat(Visual* vis, int depth) | |||
{ | |||
int bpp; | |||
int trueColour, bigEndian; | |||
int redShift, greenShift, blueShift; | |||
int redMax, greenMax, blueMax; | |||
cube = 0; | |||
bpp = depth2bpp(dpy, depth); | |||
bigEndian = (ImageByteOrder(dpy) == MSBFirst); | |||
trueColour = (vis->c_class == TrueColor); | |||
vlog.info("Using default colormap and visual, %sdepth %d.", | |||
(vis->c_class == TrueColor) ? "TrueColor, " : | |||
((vis->c_class == PseudoColor) ? "PseudoColor, " : ""), | |||
depth); | |||
redShift = ffs(vis->red_mask) - 1; | |||
greenShift = ffs(vis->green_mask) - 1; | |||
blueShift = ffs(vis->blue_mask) - 1; | |||
redMax = vis->red_mask >> redShift; | |||
greenMax = vis->green_mask >> greenShift; | |||
blueMax = vis->blue_mask >> blueShift; | |||
nativePF = PixelFormat(bpp, depth, bigEndian, trueColour, | |||
redMax, greenMax, blueMax, | |||
redShift, greenShift, blueShift); | |||
if (!trueColour) { | |||
XColor xc[256]; | |||
cube = new rfb::ColourCube(6,6,6); | |||
int r; | |||
for (r = 0; r < cube->nRed; r++) { | |||
for (int g = 0; g < cube->nGreen; g++) { | |||
for (int b = 0; b < cube->nBlue; b++) { | |||
int i = (r * cube->nGreen + g) * cube->nBlue + b; | |||
xc[i].red = r * 65535 / (cube->nRed-1); | |||
xc[i].green = g * 65535 / (cube->nGreen-1); | |||
xc[i].blue = b * 65535 / (cube->nBlue-1); | |||
} | |||
} | |||
} | |||
TXWindow::getColours(dpy, xc, cube->size()); | |||
for (r = 0; r < cube->nRed; r++) { | |||
for (int g = 0; g < cube->nGreen; g++) { | |||
for (int b = 0; b < cube->nBlue; b++) { | |||
int i = (r * cube->nGreen + g) * cube->nBlue + b; | |||
cube->set(r, g, b, xc[i].pixel); | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -1,97 +0,0 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
// | |||
// TXImage.h | |||
// | |||
// A TXImage represents a rectangular off-screen image in any RFB pixel format. | |||
// By default it will use the "native" pixel format for the screen, which will | |||
// be an 8-bit colourmap unless the X display is TrueColor. The pixel format | |||
// can be changed via the setPF() method. The pixel data is accessible via the | |||
// data member inherited from FullFramePixelBuffer, or can be set via the | |||
// fillRect(), imageRect(), copyRect() and maskRect() methods, also inherited | |||
// from PixelBuffer. A rectangle of the image can be drawn into an X Window | |||
// via the put() method. If using a colourmap, the setColourMapEntries() and | |||
// updateColourMap() methods must be called to set up the colourmap as | |||
// appropriate. | |||
#ifndef __TXIMAGE_H__ | |||
#define __TXIMAGE_H__ | |||
#include <X11/Xlib.h> | |||
#include <rfb/PixelBuffer.h> | |||
#include <rfb/ColourMap.h> | |||
#include <rfb/ColourCube.h> | |||
#ifdef HAVE_MITSHM | |||
#include <X11/extensions/XShm.h> | |||
#endif | |||
namespace rfb { class TransImageGetter; } | |||
class TXImage : public rfb::FullFramePixelBuffer, public rfb::ColourMap { | |||
public: | |||
TXImage(Display* dpy, int width, int height, Visual* vis=0, int depth=0); | |||
~TXImage(); | |||
// resize() resizes the image, preserving the image data where possible. | |||
void resize(int w, int h); | |||
// put causes the given rectangle to be drawn onto the given window. | |||
void put(Window win, GC gc, const rfb::Rect& r); | |||
// setColourMapEntries() changes some of the entries in the colourmap. | |||
// However these settings won't take effect until updateColourMap() is | |||
// called. This is because recalculating the internal translation table can | |||
// be expensive. | |||
void setColourMapEntries(int firstColour, int nColours, rdr::U16* rgbs); | |||
void updateColourMap(); | |||
#ifdef HAVE_MITSHM | |||
bool usingShm() { return shminfo; } | |||
#else | |||
bool usingShm() { return 0; } | |||
#endif | |||
// PixelBuffer methods | |||
// width(), height(), getPF() etc are inherited from PixelBuffer | |||
virtual void setPF(const rfb::PixelFormat& pf); | |||
virtual int getStride() const; | |||
private: | |||
// ColourMap method | |||
virtual void lookup(int index, int* r, int* g, int* b); | |||
void createXImage(); | |||
void destroyXImage(); | |||
void getNativePixelFormat(Visual* vis, int depth); | |||
XImage* xim; | |||
Display* dpy; | |||
Visual* vis; | |||
int depth; | |||
#ifdef HAVE_MITSHM | |||
XShmSegmentInfo* shminfo; | |||
#endif | |||
rfb::TransImageGetter* tig; | |||
rfb::Colour colourMap[256]; | |||
rfb::PixelFormat nativePF; | |||
rfb::ColourCube* cube; | |||
}; | |||
#endif |
@@ -1,186 +0,0 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
// | |||
// TXMenu.cxx | |||
// | |||
#include "TXMenu.h" | |||
#include <stdio.h> | |||
#include <stdlib.h> | |||
#include <string.h> | |||
#include <assert.h> | |||
#include <rfb/util.h> | |||
#include <X11/keysym.h> | |||
TXMenu::TXMenu(Display* dpy_, TXMenuCallback* cb_, int w, int h, | |||
TXWindow* parent_) | |||
: TXWindow(dpy_, w, h, parent_), cb(cb_), nEntries(0), | |||
highlight(-1) | |||
{ | |||
setEventHandler(this); | |||
gc = XCreateGC(dpy, win(), 0, 0); | |||
addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask | | |||
PointerMotionMask | EnterWindowMask | LeaveWindowMask); | |||
} | |||
TXMenu::~TXMenu() | |||
{ | |||
XFreeGC(dpy, gc); | |||
for (int i = 0; i < nEntries; i++) | |||
delete [] text[i]; | |||
} | |||
inline int TXMenu::entryHeight(int i) | |||
{ | |||
if (text[i]) | |||
return defaultFS->ascent + defaultFS->descent + bevel*2 + yPad*2; | |||
else | |||
return yPad*2 + 1; | |||
} | |||
void TXMenu::addEntry(const char* text_, long id_) | |||
{ | |||
assert(nEntries < maxEntries); | |||
text[nEntries] = rfb::strDup(text_); | |||
checked[nEntries] = false; | |||
id[nEntries++] = id_; | |||
int tw = 0; | |||
if (text_) | |||
tw = XTextWidth(defaultFS, text_, strlen(text_)); | |||
int newWidth = width(); | |||
if (tw + bevel*2 + xPad*5 + tickSize > width()) | |||
newWidth = tw + bevel*2 + xPad*5 + tickSize; | |||
int newHeight = 0; | |||
for (int i = 0; i < nEntries; i++) | |||
newHeight += entryHeight(i); | |||
resize(newWidth, newHeight); | |||
} | |||
void TXMenu::check(long id_, bool checked_) | |||
{ | |||
for (int i = 0; i < nEntries; i++) { | |||
if (id[i] == id_) { | |||
checked[i] = checked_; | |||
break; | |||
} | |||
} | |||
} | |||
void TXMenu::paint() | |||
{ | |||
int y = 0; | |||
for (int i = 0; i < nEntries; i++) { | |||
if (text[i]) { | |||
if (i == highlight) | |||
drawBevel(gc, 0, y, width(), entryHeight(i), bevel, | |||
defaultBg, darkBg, lightBg); | |||
else | |||
XClearArea(dpy, win(), 0, y, width(), entryHeight(i), false); | |||
if (checked[i]) | |||
XCopyPlane(dpy, tick, win(), defaultGC, 0, 0, tickSize, tickSize, | |||
bevel + xPad, | |||
y + bevel + yPad + defaultFS->ascent - tickSize, 1); | |||
XDrawImageString(dpy, win(), defaultGC, bevel + xPad*2 + tickSize, | |||
y + bevel + yPad + defaultFS->ascent, | |||
text[i], strlen(text[i])); | |||
} else { | |||
XDrawLine(dpy, win(), defaultGC, bevel + xPad, y + entryHeight(i) / 2, | |||
width() - bevel - xPad, y + entryHeight(i) / 2); | |||
} | |||
y += entryHeight(i); | |||
} | |||
} | |||
void TXMenu::handleEvent(TXWindow* w, XEvent* ev) | |||
{ | |||
switch (ev->type) { | |||
case Expose: | |||
paint(); | |||
break; | |||
case ButtonRelease: | |||
{ | |||
int y = ev->xmotion.y; | |||
int entryY = 0; | |||
for (int i = 0; i < nEntries; i++) { | |||
if (y >= entryY && y <= entryY + entryHeight(i)) { | |||
if (cb && text[i]) | |||
cb->menuSelect(id[i], this); | |||
break; | |||
} | |||
entryY += entryHeight(i); | |||
} | |||
highlight = -1; | |||
paint(); | |||
break; | |||
} | |||
case ButtonPress: | |||
case MotionNotify: | |||
{ | |||
int y = ev->xmotion.y; | |||
int entryY = 0; | |||
for (int i = 0; i < nEntries; i++) { | |||
if (y >= entryY && y <= entryY + entryHeight(i)) { | |||
if (highlight != i) { | |||
highlight = i; | |||
paint(); | |||
} | |||
break; | |||
} | |||
entryY += entryHeight(i); | |||
} | |||
break; | |||
} | |||
case KeyPress: | |||
{ | |||
KeySym ks; | |||
char str[256]; | |||
XLookupString(&ev->xkey, str, 256, &ks, NULL); | |||
if (ks == XK_Escape) { | |||
highlight = -1; | |||
unmap(); | |||
} else if (ks == XK_Down || ks == XK_Up) { | |||
if (nEntries < 1) break; | |||
if (highlight < 0) | |||
highlight = (ks == XK_Down ? nEntries-1 : 0); | |||
int start = highlight; | |||
int inc = (ks == XK_Down ? 1 : nEntries-1); | |||
do { | |||
highlight = (highlight + inc) % nEntries; | |||
} while (highlight != start && !text[highlight]); | |||
paint(); | |||
} else if (ks == XK_space || ks == XK_KP_Space || | |||
ks == XK_Return || ks == XK_KP_Enter) { | |||
if (cb && highlight >= 0 && text[highlight]) | |||
cb->menuSelect(id[highlight], this); | |||
highlight = -1; | |||
paint(); | |||
} | |||
break; | |||
} | |||
case EnterNotify: | |||
case LeaveNotify: | |||
highlight = -1; | |||
paint(); | |||
break; | |||
} | |||
} |
@@ -1,67 +0,0 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
// | |||
// TXMenu.h | |||
// | |||
// A TXMenu consists of multiple entries which can be added one at a time. | |||
// Each entry consists of some text, and has an associated integer identifier. | |||
// A callback is made when a menu entry is selected. | |||
// | |||
#ifndef __TXMENU_H__ | |||
#define __TXMENU_H__ | |||
#include "TXWindow.h" | |||
// TXMenuCallback's menuSelect() method is called when a particular menu entry | |||
// is selected. The id argument identifies the menu entry. | |||
class TXMenu; | |||
class TXMenuCallback { | |||
public: | |||
virtual void menuSelect(long id, TXMenu* menu)=0; | |||
}; | |||
class TXMenu : public TXWindow, public TXEventHandler { | |||
public: | |||
TXMenu(Display* dpy_, TXMenuCallback* cb=0, int width=1, int height=1, | |||
TXWindow* parent_=0); | |||
virtual ~TXMenu(); | |||
// addEntry() adds an entry to the end of the menu with the given text and | |||
// identifier. | |||
void addEntry(const char* text, long id); | |||
// check() sets whether the given menu entry should have a tick next to it. | |||
void check(long id, bool checked); | |||
private: | |||
int entryHeight(int i); | |||
virtual void handleEvent(TXWindow* w, XEvent* ev); | |||
void paint(); | |||
GC gc; | |||
TXMenuCallback* cb; | |||
enum { maxEntries = 64 }; | |||
char* text[maxEntries]; | |||
long id[maxEntries]; | |||
bool checked[maxEntries]; | |||
int nEntries; | |||
int highlight; | |||
}; | |||
#endif |
@@ -1,112 +0,0 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
// | |||
// TXMsgBox.h | |||
// | |||
// A TXMsgBox is a specialised pop-up dialog window, designed to present | |||
// the user with a small amount of textual information, and potentially to | |||
// obtain their response. | |||
// TXMsgBoxes are always modal, and may have an Ok button, Ok+Cancel buttons, | |||
// or Yes+No buttons. | |||
// The MsgBox helper function creates a TXMsgBox on the fly, runs it, and | |||
// returns the result. | |||
// | |||
#ifndef __TXMSGBOX_H__ | |||
#define __TXMSGBOX_H__ | |||
#include "TXDialog.h" | |||
#include "TXLabel.h" | |||
#include "TXButton.h" | |||
enum TXMsgBoxFlags { | |||
MB_OK = 0, | |||
MB_OKCANCEL = 1, | |||
MB_YESNO = 4, | |||
MB_ICONERROR = 0x10, | |||
MB_ICONQUESTION = 0x20, | |||
MB_ICONWARNING = 0x30, | |||
MB_ICONINFORMATION = 0x40, | |||
MB_DEFBUTTON1 = 0, | |||
MB_DEFBUTTON2 = 0x100 | |||
}; | |||
class TXMsgBox : public TXDialog, public TXButtonCallback { | |||
public: | |||
TXMsgBox(Display* dpy, const char* text, unsigned int flags, const char* title=0) | |||
: TXDialog(dpy, 1, 1, "Message", true), | |||
textLabel(dpy, "", this), | |||
okButton(dpy, "OK", this, this, 60), | |||
cancelButton(dpy, "Cancel", this, this, 60) | |||
{ | |||
textLabel.xPad = 8; | |||
textLabel.move(0, yPad*4); | |||
textLabel.setText(text); | |||
resize(textLabel.width(), | |||
textLabel.height() + okButton.height() + yPad*12); | |||
switch (flags & 0x30) { | |||
case MB_ICONERROR: | |||
toplevel("Error", this); break; | |||
case MB_ICONQUESTION: | |||
toplevel("Question", this); break; | |||
case MB_ICONWARNING: | |||
toplevel("Warning", this); break; | |||
case MB_ICONINFORMATION: | |||
toplevel("Information", this); break; | |||
default: | |||
if (title) | |||
toplevel(title, this); | |||
break; | |||
}; | |||
switch (flags & 0x7) { | |||
default: | |||
okButton.move((width() - okButton.width()) / 2, | |||
height() - yPad*4 - okButton.height()); | |||
cancelButton.unmap(); | |||
break; | |||
case MB_OKCANCEL: | |||
case MB_YESNO: | |||
okButton.move(((width()/2) - okButton.width()) / 2, | |||
height() - yPad*4 - okButton.height()); | |||
cancelButton.move(((width()*3/2) - cancelButton.width()) / 2, | |||
height() - yPad*4 - cancelButton.height()); | |||
if ((flags & 0x7) == MB_YESNO) { | |||
okButton.setText("Yes"); | |||
cancelButton.setText("No"); | |||
} | |||
break; | |||
}; | |||
setBorderWidth(1); | |||
} | |||
virtual void buttonActivate(TXButton* b) { | |||
ok = (b == &okButton); | |||
done = true; | |||
unmap(); | |||
} | |||
TXLabel textLabel; | |||
TXButton okButton; | |||
TXButton cancelButton; | |||
}; | |||
#endif |
@@ -1,119 +0,0 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
// | |||
// TXScrollbar.cxx | |||
// | |||
#include "TXScrollbar.h" | |||
#include <stdio.h> | |||
#include <assert.h> | |||
TXScrollbar::TXScrollbar(Display* dpy_, int width, int height, bool vert, | |||
TXScrollbarCallback* cb_, TXWindow* parent_) | |||
: TXWindow(dpy_, width, height, parent_), cb(cb_), vertical(vert), | |||
clickedInThumb(false) | |||
{ | |||
setEventHandler(this); | |||
gc = XCreateGC(dpy, win(), 0, 0); | |||
addEventMask(ExposureMask | ButtonPressMask | ButtonReleaseMask | | |||
ButtonMotionMask); | |||
setBg(scrollbarBg); | |||
limit[0] = len[0] = limit[1] = len[1] = 1; | |||
start[0] = start[1] = 0; | |||
} | |||
TXScrollbar::~TXScrollbar() | |||
{ | |||
XFreeGC(dpy, gc); | |||
} | |||
void TXScrollbar::set(int limit_, int start_, int len_, bool vert) | |||
{ | |||
assert(limit_ > 0 && len_ >= 0 && len_ <= limit_); | |||
if (start_ < 0) start_ = 0; | |||
if (start_ > limit_ - len_) start_ = limit_ - len_; | |||
if (limit[vert] != limit_ || start[vert] != start_ || len[vert] != len_) { | |||
limit[vert] = limit_; | |||
start[vert] = start_; | |||
len[vert] = len_; | |||
paint(); | |||
} | |||
} | |||
void TXScrollbar::paint() | |||
{ | |||
int x = scaleToBarX(start[0]); | |||
int y = scaleToBarY(start[1]); | |||
int w = scaleToBarX(len[0]); | |||
int h = scaleToBarY(len[1]); | |||
if (y > 0) XClearArea(dpy, win(), 0, 0, 0, y, false); | |||
if (x > 0) XClearArea(dpy, win(), 0, y, x, y+h, false); | |||
XClearArea(dpy, win(), x+w, y, 0, y+h, false); | |||
XClearArea(dpy, win(), 0, y+h, 0, 0, false); | |||
drawBevel(gc, x, y, w, h, bevel, defaultBg, lightBg, darkBg); | |||
} | |||
void TXScrollbar::handleEvent(TXWindow* w, XEvent* ev) | |||
{ | |||
switch (ev->type) { | |||
case Expose: | |||
paint(); | |||
break; | |||
case ButtonPress: | |||
{ | |||
xDown = ev->xbutton.x; | |||
yDown = ev->xbutton.y; | |||
xStart = start[0]; | |||
yStart = start[1]; | |||
bool clickedInThumbX = false; | |||
if (xDown < scaleToBarX(start[0])) { | |||
set(limit[0], start[0] - len[0], len[0], false); | |||
} else if (xDown >= scaleToBarX(start[0]+len[0])) { | |||
set(limit[0], start[0] + len[0], len[0], false); | |||
} else { | |||
clickedInThumbX = true; | |||
} | |||
bool clickedInThumbY = false; | |||
if (yDown < scaleToBarY(start[1])) { | |||
set(limit[1], start[1] - len[1], len[1], true); | |||
} else if (yDown >= scaleToBarY(start[1]+len[1])) { | |||
set(limit[1], start[1] + len[1], len[1], true); | |||
} else { | |||
clickedInThumbY = true; | |||
} | |||
clickedInThumb = clickedInThumbX && clickedInThumbY; | |||
if (cb) cb->scrollbarPos(start[0], start[1], this); | |||
} | |||
break; | |||
case ButtonRelease: | |||
case MotionNotify: | |||
while (XCheckTypedWindowEvent(dpy, win(), MotionNotify, ev)); | |||
if (clickedInThumb) { | |||
int dx = ev->xmotion.x - xDown; | |||
int dy = ev->xmotion.y - yDown; | |||
set(limit[0], xStart + barToScaleX(dx), len[0], false); | |||
set(limit[1], yStart + barToScaleY(dy), len[1], true); | |||
if (cb) cb->scrollbarPos(start[0], start[1], this); | |||
} | |||
break; | |||
} | |||
} |
@@ -1,82 +0,0 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
// | |||
// TXScrollbar.h | |||
// | |||
// A TXScrollbar represents a range of values starting at start, of length len, | |||
// between zero and limit. The vertical argument to the constructor says | |||
// whether the scrollbar is horizontal or vertical. | |||
// | |||
// In fact it can represent a range in each dimension but usually one of the | |||
// dimensions is fixed, according to the vertical flag (for a vertical | |||
// scrollbar, the horizontal dimension is fixed, and vice-versa). | |||
// | |||
// The TXScrollbarCallback argument is an object which will be notified when | |||
// the user has attempted to move the scrollbar. The x and y arguments to the | |||
// scrollbarPos() method give the start values in the respective dimensions. | |||
// They are guaranteed to be between 0 and limit-len. | |||
// | |||
#ifndef __TXSCROLLBAR_H__ | |||
#define __TXSCROLLBAR_H__ | |||
#include "TXWindow.h" | |||
class TXScrollbarCallback; | |||
class TXScrollbar : public TXWindow, public TXEventHandler { | |||
public: | |||
TXScrollbar(Display* dpy_, int width=1, int height=1, bool vertical=false, | |||
TXScrollbarCallback* cb=0, TXWindow* parent_=0); | |||
virtual ~TXScrollbar(); | |||
// set() sets the limit, start and length of the range represented by the | |||
// scrollbar. The values of limit and len passed in must be valid | |||
// (i.e. limit > 0 and 0 <= len <= limit). Values of start are clipped to | |||
// the range 0 to limit-len. | |||
void set(int limit, int start, int len) { set(limit, start, len, vertical); } | |||
// set() with an extra argument vert can be used to represent a range in both | |||
// dimensions simultaneously. | |||
void set(int limit, int start, int len, bool vert); | |||
virtual void handleEvent(TXWindow* w, XEvent* ev); | |||
private: | |||
int scaleToBarX(int x) { return (x * width() + limit[0]/2) / limit[0]; } | |||
int scaleToBarY(int y) { return (y * height() + limit[1]/2) / limit[1]; } | |||
int barToScaleX(int x) { return (x * limit[0] + width()/2) / width(); } | |||
int barToScaleY(int y) { return (y * limit[1] + height()/2) / height(); } | |||
void paint(); | |||
GC gc; | |||
TXScrollbarCallback* cb; | |||
int limit[2]; | |||
int start[2]; | |||
int len[2]; | |||
int xDown, yDown; | |||
int xStart, yStart; | |||
bool vertical; | |||
bool clickedInThumb; | |||
}; | |||
class TXScrollbarCallback { | |||
public: | |||
virtual void scrollbarPos(int x, int y, TXScrollbar* sb)=0; | |||
}; | |||
#endif |
@@ -1,189 +0,0 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
// | |||
// TXViewport.cxx | |||
// | |||
#include "TXViewport.h" | |||
#include <stdio.h> | |||
TXViewport::TXViewport(Display* dpy_, int w, int h, TXWindow* parent_) | |||
: TXWindow(dpy_, w, h, parent_), child(0), hScrollbar(0), | |||
vScrollbar(0), scrollbarSize(15), xOff(0), yOff(0), bumpScrollTimer(this), | |||
bumpScroll(false), needXScrollbar(false), needYScrollbar(false), | |||
bumpScrollX(0), bumpScrollY(0) | |||
{ | |||
clipper = new TXWindow(dpy, width()-scrollbarSize, height()-scrollbarSize, | |||
this); | |||
clipper->setBg(black); | |||
hScrollbar = new TXScrollbar(dpy, width()-scrollbarSize, scrollbarSize, | |||
false, this, this); | |||
vScrollbar = new TXScrollbar(dpy, scrollbarSize, height()-scrollbarSize, | |||
true, this, this); | |||
} | |||
TXViewport::~TXViewport() | |||
{ | |||
delete clipper; | |||
delete hScrollbar; | |||
delete vScrollbar; | |||
} | |||
void TXViewport::setChild(TXWindow* child_) | |||
{ | |||
child = child_; | |||
XReparentWindow(dpy, child->win(), clipper->win(), 0, 0); | |||
xOff = yOff = 0; | |||
child->map(); | |||
resizeNotify(); | |||
} | |||
bool TXViewport::setOffset(int x, int y) | |||
{ | |||
if (clipper->width() >= child->width()) { | |||
x = (clipper->width() - child->width()) / 2; | |||
} else { | |||
if (x > 0) x = 0; | |||
if (x + child->width() < clipper->width()) | |||
x = clipper->width() - child->width(); | |||
} | |||
if (clipper->height() >= child->height()) { | |||
y = (clipper->height() - child->height()) / 2; | |||
} else { | |||
if (y > 0) y = 0; | |||
if (y + child->height() < clipper->height()) | |||
y = clipper->height() - child->height(); | |||
} | |||
if (x != xOff || y != yOff) { | |||
xOff = x; | |||
yOff = y; | |||
child->move(xOff, yOff); | |||
return true; | |||
} | |||
return false; | |||
} | |||
void TXViewport::setBumpScroll(bool b) | |||
{ | |||
bumpScroll = b; | |||
resizeNotify(); | |||
} | |||
// Note: bumpScrollEvent() only works if the viewport is positioned at 0,0 and | |||
// is the same width and height as the screen. | |||
bool TXViewport::bumpScrollEvent(XMotionEvent* ev) | |||
{ | |||
if (!bumpScroll) return false; | |||
int bumpScrollPixels = 20; | |||
bumpScrollX = bumpScrollY = 0; | |||
if (ev->x_root == width()-1) bumpScrollX = -bumpScrollPixels; | |||
else if (ev->x_root == 0) bumpScrollX = bumpScrollPixels; | |||
if (ev->y_root == height()-1) bumpScrollY = -bumpScrollPixels; | |||
else if (ev->y_root == 0) bumpScrollY = bumpScrollPixels; | |||
if (bumpScrollX || bumpScrollY) { | |||
if (bumpScrollTimer.isStarted()) return true; | |||
if (setOffset(xOff + bumpScrollX, yOff + bumpScrollY)) { | |||
bumpScrollTimer.start(25); | |||
return true; | |||
} | |||
} | |||
bumpScrollTimer.stop(); | |||
return false; | |||
} | |||
bool TXViewport::handleTimeout(rfb::Timer* timer) { | |||
return setOffset(xOff + bumpScrollX, yOff + bumpScrollY); | |||
} | |||
void TXViewport::resizeNotify() | |||
{ | |||
int winMaxWidth, winMaxHeight; | |||
winMaxWidth = child->width(); | |||
winMaxHeight = child->height(); | |||
needXScrollbar = false; | |||
needYScrollbar = false; | |||
if (!bumpScroll && height() > scrollbarSize && width() > scrollbarSize) { | |||
needXScrollbar = (width() < child->width()); | |||
needYScrollbar = (height() < child->height()); | |||
// Adding an horizontal scrollbar occupies space, which might cause the | |||
// need to add a vertical scrollbar, and vice-versa. These additional | |||
// checks should solve this problem | |||
if (needXScrollbar && (height() - scrollbarSize < child->height())) | |||
needYScrollbar = true; | |||
if (needYScrollbar && (width() - scrollbarSize < child->width())) | |||
needXScrollbar = true; | |||
} | |||
if (needXScrollbar) | |||
winMaxHeight += scrollbarSize; | |||
if (needYScrollbar) | |||
winMaxWidth += scrollbarSize; | |||
setMaxSize(winMaxWidth, winMaxHeight); | |||
if (needXScrollbar && needYScrollbar) { | |||
clipper->resize(width()-scrollbarSize, height()-scrollbarSize); | |||
hScrollbar->map(); | |||
vScrollbar->map(); | |||
} else if (needXScrollbar) { | |||
clipper->resize(width(), height()-scrollbarSize); | |||
hScrollbar->map(); | |||
vScrollbar->unmap(); | |||
} else if (needYScrollbar) { | |||
clipper->resize(width()-scrollbarSize, height()); | |||
hScrollbar->unmap(); | |||
vScrollbar->map(); | |||
} else { | |||
clipper->resize(width(), height()); | |||
hScrollbar->unmap(); | |||
vScrollbar->unmap(); | |||
} | |||
setOffset(xOff, yOff); | |||
if (needXScrollbar) { | |||
hScrollbar->move(0, height()-scrollbarSize); | |||
hScrollbar->resize(width()-scrollbarSize, scrollbarSize); | |||
hScrollbar->set(child->width(), -xOff, width()-scrollbarSize); | |||
} | |||
if (needYScrollbar) { | |||
vScrollbar->move(width()-scrollbarSize, 0); | |||
vScrollbar->resize(scrollbarSize, height()-scrollbarSize); | |||
vScrollbar->set(child->height(), -yOff, height()-scrollbarSize); | |||
} | |||
} | |||
void TXViewport::scrollbarPos(int x, int y, TXScrollbar* sb) | |||
{ | |||
if (sb == hScrollbar) { | |||
x = -x; | |||
y = yOff; | |||
} else { | |||
x = xOff; | |||
y = -y; | |||
} | |||
setOffset(x, y); | |||
} |
@@ -1,78 +0,0 @@ | |||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. | |||
* | |||
* This is free software; you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation; either version 2 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* This software is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with this software; if not, write to the Free Software | |||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, | |||
* USA. | |||
*/ | |||
// | |||
// TXViewport.h | |||
// | |||
// A TXViewport allows a large window to be viewed by adding scrollbars to the | |||
// right and bottom if necessary. It also has a bump-scroll mode where there | |||
// are no scrollbars, and scrolling is achieved by bumping up against the edge | |||
// of the screen instead. Note that this only works when the viewport fills | |||
// the entire screen. If the child window is smaller than the viewport, it is | |||
// always positioned centrally in the viewport. | |||
#ifndef __TXVIEWPORT_H__ | |||
#define __TXVIEWPORT_H__ | |||
#include <rfb/Timer.h> | |||
#include "TXWindow.h" | |||
#include "TXScrollbar.h" | |||
class TXViewport : public TXWindow, public TXScrollbarCallback, | |||
public rfb::Timer::Callback { | |||
public: | |||
TXViewport(Display* dpy_, int width, int height, TXWindow* parent_=0); | |||
virtual ~TXViewport(); | |||
// setChild() sets the child window which is to be viewed in the viewport. | |||
void setChild(TXWindow* child_); | |||
// setOffset() sets the position of the child in the viewport. Note that the | |||
// offsets are negative. For example when the offset is (-100,-30), position | |||
// (100,30) in the child window is at the top-left of the viewport. The | |||
// offsets given are clipped to keep the child window filling the viewport | |||
// (except where the child window is smaller than the viewport, in which case | |||
// it is always positioned centrally in the viewport). It returns true if | |||
// the child was repositioned. | |||
bool setOffset(int x, int y); | |||
// setBumpScroll() puts the viewport in bump-scroll mode. | |||
void setBumpScroll(bool b); | |||
// bumpScrollEvent() can be called with a MotionNotify event which may | |||
// potentially be against the edge of the screen. It returns true if the | |||
// event was used for bump-scrolling, false if it should be processed | |||
// normally. | |||
bool bumpScrollEvent(XMotionEvent* ev); | |||
private: | |||
virtual void resizeNotify(); | |||
virtual void scrollbarPos(int x, int y, TXScrollbar* sb); | |||
virtual bool handleTimeout(rfb::Timer* timer); | |||
TXWindow* clipper; | |||
TXWindow* child; | |||
TXScrollbar* hScrollbar; | |||
TXScrollbar* vScrollbar; | |||
const int scrollbarSize; | |||
int xOff, yOff; | |||
rfb::Timer bumpScrollTimer; | |||
bool bumpScroll; | |||
bool needXScrollbar; | |||
bool needYScrollbar; | |||
int bumpScrollX, bumpScrollY; | |||
}; | |||
#endif |