1 #include <ctype.h> 2 #include <rfb/rfb.h> 3 #include <rfb/keysym.h> 4 5 typedef struct { 6 rfbScreenInfoPtr screen; 7 rfbFontDataPtr font; 8 char** list; 9 int listSize; 10 int selected; 11 int displayStart; 12 int x1,y1,x2,y2,textH,pageH; 13 int xhot,yhot; 14 int buttonWidth,okBX,cancelBX,okX,cancelX,okY; 15 rfbBool okInverted,cancelInverted; 16 int lastButtons; 17 rfbPixel colour,backColour; 18 SelectionChangedHookPtr selChangedHook; 19 enum { SELECTING, OK, CANCEL } state; 20 } rfbSelectData; 21 22 static const char* okStr="OK"; 23 static const char* cancelStr="Cancel"; 24 25 static void selPaintButtons(rfbSelectData* m,rfbBool invertOk,rfbBool invertCancel) 26 { 27 rfbScreenInfoPtr s = m->screen; 28 rfbPixel bcolour = m->backColour; 29 rfbPixel colour = m->colour; 30 31 rfbFillRect(s,m->x1,m->okY-m->textH,m->x2,m->okY,bcolour); 32 33 if(invertOk) { 34 rfbFillRect(s,m->okBX,m->okY-m->textH,m->okBX+m->buttonWidth,m->okY,colour); 35 rfbDrawStringWithClip(s,m->font,m->okX+m->xhot,m->okY-1+m->yhot,okStr, 36 m->x1,m->okY-m->textH,m->x2,m->okY, 37 bcolour,colour); 38 } else 39 rfbDrawString(s,m->font,m->okX+m->xhot,m->okY-1+m->yhot,okStr,colour); 40 41 if(invertCancel) { 42 rfbFillRect(s,m->cancelBX,m->okY-m->textH, 43 m->cancelBX+m->buttonWidth,m->okY,colour); 44 rfbDrawStringWithClip(s,m->font,m->cancelX+m->xhot,m->okY-1+m->yhot, 45 cancelStr,m->x1,m->okY-m->textH,m->x2,m->okY, 46 bcolour,colour); 47 } else 48 rfbDrawString(s,m->font,m->cancelX+m->xhot,m->okY-1+m->yhot,cancelStr,colour); 49 50 m->okInverted = invertOk; 51 m->cancelInverted = invertCancel; 52 } 53 54 /* line is relative to displayStart */ 55 static void selPaintLine(rfbSelectData* m,int line,rfbBool invert) 56 { 57 int y1 = m->y1+line*m->textH, y2 = y1+m->textH; 58 if(y2>m->y2) 59 y2=m->y2; 60 rfbFillRect(m->screen,m->x1,y1,m->x2,y2,invert?m->colour:m->backColour); 61 if(m->displayStart+line<m->listSize) 62 rfbDrawStringWithClip(m->screen,m->font,m->x1+m->xhot,y2-1+m->yhot, 63 m->list[m->displayStart+line], 64 m->x1,y1,m->x2,y2, 65 invert?m->backColour:m->colour, 66 invert?m->backColour:m->colour); 67 } 68 69 static void selSelect(rfbSelectData* m,int _index) 70 { 71 int delta; 72 73 if(_index==m->selected || _index<0 || _index>=m->listSize) 74 return; 75 76 if(m->selected>=0) 77 selPaintLine(m,m->selected-m->displayStart,FALSE); 78 79 if(_index<m->displayStart || _index>=m->displayStart+m->pageH) { 80 /* targetLine is the screen line in which the selected line will 81 be displayed. 82 targetLine = m->pageH/2 doesn't look so nice */ 83 int targetLine = m->selected-m->displayStart; 84 int lineStart,lineEnd; 85 86 /* scroll */ 87 if(_index<targetLine) 88 targetLine = _index; 89 else if(_index+m->pageH-targetLine>=m->listSize) 90 targetLine = _index+m->pageH-m->listSize; 91 delta = _index-(m->displayStart+targetLine); 92 93 if(delta>-m->pageH && delta<m->pageH) { 94 if(delta>0) { 95 lineStart = m->pageH-delta; 96 lineEnd = m->pageH; 97 rfbDoCopyRect(m->screen,m->x1,m->y1,m->x2,m->y1+lineStart*m->textH, 98 0,-delta*m->textH); 99 } else { 100 lineStart = 0; 101 lineEnd = -delta; 102 rfbDoCopyRect(m->screen, 103 m->x1,m->y1+lineEnd*m->textH,m->x2,m->y2, 104 0,-delta*m->textH); 105 } 106 } else { 107 lineStart = 0; 108 lineEnd = m->pageH; 109 } 110 m->displayStart += delta; 111 for(delta=lineStart;delta<lineEnd;delta++) 112 if(delta!=_index) 113 selPaintLine(m,delta,FALSE); 114 } 115 116 m->selected = _index; 117 selPaintLine(m,m->selected-m->displayStart,TRUE); 118 119 if(m->selChangedHook) 120 m->selChangedHook(_index); 121 122 /* todo: scrollbars */ 123 } 124 125 static void selKbdAddEvent(rfbBool down,rfbKeySym keySym,rfbClientPtr cl) 126 { 127 if(down) { 128 if(keySym>' ' && keySym<0xff) { 129 int i; 130 rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; 131 char c = tolower(keySym); 132 133 for(i=m->selected+1;m->list[i] && tolower(m->list[i][0])!=c;i++); 134 if(!m->list[i]) 135 for(i=0;i<m->selected && tolower(m->list[i][0])!=c;i++); 136 selSelect(m,i); 137 } else if(keySym==XK_Escape) { 138 rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; 139 m->state = CANCEL; 140 } else if(keySym==XK_Return) { 141 rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; 142 m->state = OK; 143 } else { 144 rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; 145 int curSel=m->selected; 146 if(keySym==XK_Up) { 147 if(curSel>0) 148 selSelect(m,curSel-1); 149 } else if(keySym==XK_Down) { 150 if(curSel+1<m->listSize) 151 selSelect(m,curSel+1); 152 } else { 153 if(keySym==XK_Page_Down) { 154 if(curSel+m->pageH<m->listSize) 155 selSelect(m,curSel+m->pageH); 156 else 157 selSelect(m,m->listSize-1); 158 } else if(keySym==XK_Page_Up) { 159 if(curSel-m->pageH>=0) 160 selSelect(m,curSel-m->pageH); 161 else 162 selSelect(m,0); 163 } 164 } 165 } 166 } 167 } 168 169 static void selPtrAddEvent(int buttonMask,int x,int y,rfbClientPtr cl) 170 { 171 rfbSelectData* m = (rfbSelectData*)cl->screen->screenData; 172 if(y<m->okY && y>=m->okY-m->textH) { 173 if(x>=m->okBX && x<m->okBX+m->buttonWidth) { 174 if(!m->okInverted) 175 selPaintButtons(m,TRUE,FALSE); 176 if(buttonMask) 177 m->state = OK; 178 } else if(x>=m->cancelBX && x<m->cancelBX+m->buttonWidth) { 179 if(!m->cancelInverted) 180 selPaintButtons(m,FALSE,TRUE); 181 if(buttonMask) 182 m->state = CANCEL; 183 } else if(m->okInverted || m->cancelInverted) 184 selPaintButtons(m,FALSE,FALSE); 185 } else { 186 if(m->okInverted || m->cancelInverted) 187 selPaintButtons(m,FALSE,FALSE); 188 if(!m->lastButtons && buttonMask) { 189 if(x>=m->x1 && x<m->x2 && y>=m->y1 && y<m->y2) 190 selSelect(m,m->displayStart+(y-m->y1)/m->textH); 191 } 192 } 193 m->lastButtons = buttonMask; 194 195 /* todo: scrollbars */ 196 } 197 198 static rfbCursorPtr selGetCursorPtr(rfbClientPtr cl) 199 { 200 return NULL; 201 } 202 203 int rfbSelectBox(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font, 204 char** list, 205 int x1,int y1,int x2,int y2, 206 rfbPixel colour,rfbPixel backColour, 207 int border,SelectionChangedHookPtr selChangedHook) 208 { 209 int bpp = rfbScreen->bitsPerPixel/8; 210 char* frameBufferBackup; 211 void* screenDataBackup = rfbScreen->screenData; 212 rfbKbdAddEventProcPtr kbdAddEventBackup = rfbScreen->kbdAddEvent; 213 rfbPtrAddEventProcPtr ptrAddEventBackup = rfbScreen->ptrAddEvent; 214 rfbGetCursorProcPtr getCursorPtrBackup = rfbScreen->getCursorPtr; 215 rfbDisplayHookPtr displayHookBackup = rfbScreen->displayHook; 216 rfbSelectData selData; 217 int i,j,k; 218 int fx1,fy1,fx2,fy2; /* for font bbox */ 219 220 if(list==0 || *list==0) 221 return(-1); 222 223 rfbWholeFontBBox(font, &fx1, &fy1, &fx2, &fy2); 224 selData.textH = fy2-fy1; 225 /* I need at least one line for the choice and one for the buttons */ 226 if(y2-y1<selData.textH*2+3*border) 227 return(-1); 228 selData.xhot = -fx1; 229 selData.yhot = -fy2; 230 selData.x1 = x1+border; 231 selData.y1 = y1+border; 232 selData.y2 = y2-selData.textH-3*border; 233 selData.x2 = x2-2*border; 234 selData.pageH = (selData.y2-selData.y1)/selData.textH; 235 236 i = rfbWidthOfString(font,okStr); 237 j = rfbWidthOfString(font,cancelStr); 238 selData.buttonWidth= k = 4*border+(i<j?j:i); 239 selData.okBX = x1+(x2-x1-2*k)/3; 240 if(selData.okBX<x1+border) /* too narrow! */ 241 return(-1); 242 selData.cancelBX = x1+k+(x2-x1-2*k)*2/3; 243 selData.okX = selData.okBX+(k-i)/2; 244 selData.cancelX = selData.cancelBX+(k-j)/2; 245 selData.okY = y2-border; 246 247 frameBufferBackup = (char*)malloc(bpp*(x2-x1)*(y2-y1)); 248 249 selData.state = SELECTING; 250 selData.screen = rfbScreen; 251 selData.font = font; 252 selData.list = list; 253 selData.colour = colour; 254 selData.backColour = backColour; 255 for(i=0;list[i];i++); 256 selData.selected = i; 257 selData.listSize = i; 258 selData.displayStart = i; 259 selData.lastButtons = 0; 260 selData.selChangedHook = selChangedHook; 261 262 rfbScreen->screenData = &selData; 263 rfbScreen->kbdAddEvent = selKbdAddEvent; 264 rfbScreen->ptrAddEvent = selPtrAddEvent; 265 rfbScreen->getCursorPtr = selGetCursorPtr; 266 rfbScreen->displayHook = NULL; 267 268 /* backup screen */ 269 for(j=0;j<y2-y1;j++) 270 memcpy(frameBufferBackup+j*(x2-x1)*bpp, 271 rfbScreen->frameBuffer+j*rfbScreen->paddedWidthInBytes+x1*bpp, 272 (x2-x1)*bpp); 273 274 /* paint list and buttons */ 275 rfbFillRect(rfbScreen,x1,y1,x2,y2,colour); 276 selPaintButtons(&selData,FALSE,FALSE); 277 selSelect(&selData,0); 278 279 /* modal loop */ 280 while(selData.state == SELECTING) 281 rfbProcessEvents(rfbScreen,20000); 282 283 /* copy back screen data */ 284 for(j=0;j<y2-y1;j++) 285 memcpy(rfbScreen->frameBuffer+j*rfbScreen->paddedWidthInBytes+x1*bpp, 286 frameBufferBackup+j*(x2-x1)*bpp, 287 (x2-x1)*bpp); 288 free(frameBufferBackup); 289 rfbMarkRectAsModified(rfbScreen,x1,y1,x2,y2); 290 rfbScreen->screenData = screenDataBackup; 291 rfbScreen->kbdAddEvent = kbdAddEventBackup; 292 rfbScreen->ptrAddEvent = ptrAddEventBackup; 293 rfbScreen->getCursorPtr = getCursorPtrBackup; 294 rfbScreen->displayHook = displayHookBackup; 295 296 if(selData.state==CANCEL) 297 selData.selected=-1; 298 return(selData.selected); 299 } 300 301