diff options
Diffstat (limited to 'krfb/libvncserver/selbox.c')
-rw-r--r-- | krfb/libvncserver/selbox.c | 301 |
1 files changed, 301 insertions, 0 deletions
diff --git a/krfb/libvncserver/selbox.c b/krfb/libvncserver/selbox.c new file mode 100644 index 00000000..6cdb4590 --- /dev/null +++ b/krfb/libvncserver/selbox.c @@ -0,0 +1,301 @@ +#include <ctype.h> +#include "rfb.h" +#include "keysym.h" + +typedef struct { + rfbScreenInfoPtr screen; + rfbFontDataPtr font; + char** list; + int listSize; + int selected; + int displayStart; + int x1,y1,x2,y2,textH,pageH; + int xhot,yhot; + int buttonWidth,okBX,cancelBX,okX,cancelX,okY; + Bool okInverted,cancelInverted; + int lastButtons; + Pixel colour,backColour; + SelectionChangedHookPtr selChangedHook; + enum { SELECTING, OK, CANCEL } state; +} rfbSelectData; + +static const char okStr[] = "OK"; +static const char cancelStr[] = "Cancel"; + +static void selPaintButtons(rfbSelectData* m,Bool invertOk,Bool invertCancel) +{ + rfbScreenInfoPtr s = m->screen; + Pixel bcolour = m->backColour; + Pixel colour = m->colour; + + rfbFillRect(s,m->x1,m->okY-m->textH,m->x2,m->okY,bcolour); + + if(invertOk) { + rfbFillRect(s,m->okBX,m->okY-m->textH,m->okBX+m->buttonWidth,m->okY,colour); + rfbDrawStringWithClip(s,m->font,m->okX+m->xhot,m->okY-1+m->yhot,okStr, + m->x1,m->okY-m->textH,m->x2,m->okY, + bcolour,colour); + } else + rfbDrawString(s,m->font,m->okX+m->xhot,m->okY-1+m->yhot,okStr,colour); + + if(invertCancel) { + rfbFillRect(s,m->cancelBX,m->okY-m->textH, + m->cancelBX+m->buttonWidth,m->okY,colour); + rfbDrawStringWithClip(s,m->font,m->cancelX+m->xhot,m->okY-1+m->yhot, + cancelStr,m->x1,m->okY-m->textH,m->x2,m->okY, + bcolour,colour); + } else + rfbDrawString(s,m->font,m->cancelX+m->xhot,m->okY-1+m->yhot,cancelStr,colour); + + m->okInverted = invertOk; + m->cancelInverted = invertCancel; +} + +/* line is relative to displayStart */ +static void selPaintLine(rfbSelectData* m,int line,Bool invert) +{ + int y1 = m->y1+line*m->textH, y2 = y1+m->textH; + if(y2>m->y2) + y2=m->y2; + rfbFillRect(m->screen,m->x1,y1,m->x2,y2,invert?m->colour:m->backColour); + if(m->displayStart+line<m->listSize) + rfbDrawStringWithClip(m->screen,m->font,m->x1+m->xhot,y2-1+m->yhot, + m->list[m->displayStart+line], + m->x1,y1,m->x2,y2, + invert?m->backColour:m->colour, + invert?m->backColour:m->colour); +} + +static void selSelect(rfbSelectData* m,int _index) +{ + int delta; + + if(_index==m->selected || _index<0 || _index>=m->listSize) + return; + + if(m->selected>=0) + selPaintLine(m,m->selected-m->displayStart,FALSE); + + if(_index<m->displayStart || _index>=m->displayStart+m->pageH) { + /* targetLine is the screen line in which the selected line will + be displayed. + targetLine = m->pageH/2 doesn't look so nice */ + int targetLine = m->selected-m->displayStart; + int lineStart,lineEnd; + + /* scroll */ + if(_index<targetLine) + targetLine = _index; + else if(_index+m->pageH-targetLine>=m->listSize) + targetLine = _index+m->pageH-m->listSize; + delta = _index-(m->displayStart+targetLine); + + if(delta>-m->pageH && delta<m->pageH) { + if(delta>0) { + lineStart = m->pageH-delta; + lineEnd = m->pageH; + rfbDoCopyRect(m->screen,m->x1,m->y1,m->x2,m->y1+lineStart*m->textH, + 0,-delta*m->textH); + } else { + lineStart = 0; + lineEnd = -delta; + rfbDoCopyRect(m->screen, + m->x1,m->y1+lineEnd*m->textH,m->x2,m->y2, + 0,-delta*m->textH); + } + } else { + lineStart = 0; + lineEnd = m->pageH; + } + m->displayStart += delta; + for(delta=lineStart;delta<lineEnd;delta++) + if(delta!=_index) + selPaintLine(m,delta,FALSE); + } + + m->selected = _index; + selPaintLine(m,m->selected-m->displayStart,TRUE); + + if(m->selChangedHook) + m->selChangedHook(_index); + + /* todo: scrollbars */ +} + +static void selKbdAddEvent(Bool down,KeySym keySym,rfbClientPtr cl) +{ + if(down) { + if(keySym>' ' && keySym<0xff) { + int i; + rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; + char c = tolower(keySym); + + for(i=m->selected+1;m->list[i] && tolower(m->list[i][0])!=c;i++); + if(!m->list[i]) + for(i=0;i<m->selected && tolower(m->list[i][0])!=c;i++); + selSelect(m,i); + } else if(keySym==XK_Escape) { + rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; + m->state = CANCEL; + } else if(keySym==XK_Return) { + rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; + m->state = OK; + } else { + rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; + int curSel=m->selected; + if(keySym==XK_Up) { + if(curSel>0) + selSelect(m,curSel-1); + } else if(keySym==XK_Down) { + if(curSel+1<m->listSize) + selSelect(m,curSel+1); + } else { + if(keySym==XK_Page_Down) { + if(curSel+m->pageH<m->listSize) + selSelect(m,curSel+m->pageH); + else + selSelect(m,m->listSize-1); + } else if(keySym==XK_Page_Up) { + if(curSel-m->pageH>=0) + selSelect(m,curSel-m->pageH); + else + selSelect(m,0); + } + } + } + } +} + +static void selPtrAddEvent(int buttonMask,int x,int y,rfbClientPtr cl) +{ + rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; + if(y<m->okY && y>=m->okY-m->textH) { + if(x>=m->okBX && x<m->okBX+m->buttonWidth) { + if(!m->okInverted) + selPaintButtons(m,TRUE,FALSE); + if(buttonMask) + m->state = OK; + } else if(x>=m->cancelBX && x<m->cancelBX+m->buttonWidth) { + if(!m->cancelInverted) + selPaintButtons(m,FALSE,TRUE); + if(buttonMask) + m->state = CANCEL; + } else if(m->okInverted || m->cancelInverted) + selPaintButtons(m,FALSE,FALSE); + } else { + if(m->okInverted || m->cancelInverted) + selPaintButtons(m,FALSE,FALSE); + if(!m->lastButtons && buttonMask) { + if(x>=m->x1 && x<m->x2 && y>=m->y1 && y<m->y2) + selSelect(m,m->displayStart+(y-m->y1)/m->textH); + } + } + m->lastButtons = buttonMask; + + /* todo: scrollbars */ +} + +static rfbCursorPtr selGetCursorPtr(rfbClientPtr cl) +{ + return(0); +} + +int rfbSelectBox(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font, + char** list, + int x1,int y1,int x2,int y2, + Pixel colour,Pixel backColour, + int border,SelectionChangedHookPtr selChangedHook) +{ + int bpp = rfbScreen->bitsPerPixel/8; + char* frameBufferBackup; + void* screenDataBackup = rfbScreen->screenData; + KbdAddEventProcPtr kbdAddEventBackup = rfbScreen->kbdAddEvent; + PtrAddEventProcPtr ptrAddEventBackup = rfbScreen->ptrAddEvent; + GetCursorProcPtr getCursorPtrBackup = rfbScreen->getCursorPtr; + DisplayHookPtr displayHookBackup = rfbScreen->displayHook; + rfbSelectData selData; + int i,j,k; + int fx1,fy1,fx2,fy2; /* for font bbox */ + + if(list==0 || *list==0) + return(-1); + + rfbWholeFontBBox(font, &fx1, &fy1, &fx2, &fy2); + selData.textH = fy2-fy1; + /* I need at least one line for the choice and one for the buttons */ + if(y2-y1<selData.textH*2+3*border) + return(-1); + selData.xhot = -fx1; + selData.yhot = -fy2; + selData.x1 = x1+border; + selData.y1 = y1+border; + selData.y2 = y2-selData.textH-3*border; + selData.x2 = x2-2*border; + selData.pageH = (selData.y2-selData.y1)/selData.textH; + + i = rfbWidthOfString(font,okStr); + j = rfbWidthOfString(font,cancelStr); + selData.buttonWidth= k = 4*border+(i<j)?j:i; + selData.okBX = x1+(x2-x1-2*k)/3; + if(selData.okBX<x1+border) /* too narrow! */ + return(-1); + selData.cancelBX = x1+k+(x2-x1-2*k)*2/3; + selData.okX = selData.okBX+(k-i)/2; + selData.cancelX = selData.cancelBX+(k-j)/2; + selData.okY = y2-border; + + rfbUndrawCursor(rfbScreen); + frameBufferBackup = (char*)malloc(bpp*(x2-x1)*(y2-y1)); + + selData.state = SELECTING; + selData.screen = rfbScreen; + selData.font = font; + selData.list = list; + selData.colour = colour; + selData.backColour = backColour; + for(i=0;list[i];i++); + selData.selected = i; + selData.listSize = i; + selData.displayStart = i; + selData.lastButtons = 0; + selData.selChangedHook = selChangedHook; + + rfbScreen->screenData = &selData; + rfbScreen->kbdAddEvent = selKbdAddEvent; + rfbScreen->ptrAddEvent = selPtrAddEvent; + rfbScreen->getCursorPtr = selGetCursorPtr; + rfbScreen->displayHook = 0; + + /* backup screen */ + for(j=0;j<y2-y1;j++) + memcpy(frameBufferBackup+j*(x2-x1)*bpp, + rfbScreen->frameBuffer+j*rfbScreen->paddedWidthInBytes+x1*bpp, + (x2-x1)*bpp); + + /* paint list and buttons */ + rfbFillRect(rfbScreen,x1,y1,x2,y2,colour); + selPaintButtons(&selData,FALSE,FALSE); + selSelect(&selData,0); + + /* modal loop */ + while(selData.state == SELECTING) + rfbProcessEvents(rfbScreen,20000); + + /* copy back screen data */ + for(j=0;j<y2-y1;j++) + memcpy(rfbScreen->frameBuffer+j*rfbScreen->paddedWidthInBytes+x1*bpp, + frameBufferBackup+j*(x2-x1)*bpp, + (x2-x1)*bpp); + free(frameBufferBackup); + rfbMarkRectAsModified(rfbScreen,x1,y1,x2,y2); + rfbScreen->screenData = screenDataBackup; + rfbScreen->kbdAddEvent = kbdAddEventBackup; + rfbScreen->ptrAddEvent = ptrAddEventBackup; + rfbScreen->getCursorPtr = getCursorPtrBackup; + rfbScreen->displayHook = displayHookBackup; + + if(selData.state==CANCEL) + selData.selected=-1; + return(selData.selected); +} + |