#include #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+linelistSize) 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(_indexdisplayStart || _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(_indexpageH-targetLine>=m->listSize) targetLine = _index+m->pageH-m->listSize; delta = _index-(m->displayStart+targetLine); if(delta>-m->pageH && deltapageH) { 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;deltaselected = _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;iselected && 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+1listSize) selSelect(m,curSel+1); } else { if(keySym==XK_Page_Down) { if(curSel+m->pageHlistSize) 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(yokY && y>=m->okY-m->textH) { if(x>=m->okBX && xokBX+m->buttonWidth) { if(!m->okInverted) selPaintButtons(m,TRUE,FALSE); if(buttonMask) m->state = OK; } else if(x>=m->cancelBX && xcancelBX+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 && xx2 && y>=m->y1 && yy2) 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-y1screenData = &selData; rfbScreen->kbdAddEvent = selKbdAddEvent; rfbScreen->ptrAddEvent = selPtrAddEvent; rfbScreen->getCursorPtr = selGetCursorPtr; rfbScreen->displayHook = 0; /* backup screen */ for(j=0;jframeBuffer+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;jframeBuffer+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); }