3 #include <rfb/keysym.h>
6 rfbScreenInfoPtr screen;
12 int x1,y1,x2,y2,textH,pageH;
14 int buttonWidth,okBX,cancelBX,okX,cancelX,okY;
15 rfbBool okInverted,cancelInverted;
17 rfbPixel colour,backColour;
18 SelectionChangedHookPtr selChangedHook;
19 enum { SELECTING, OK, CANCEL } state;
22 static const char* okStr="OK";
23 static const char* cancelStr="Cancel";
25 static void selPaintButtons(rfbSelectData* m,rfbBool invertOk,rfbBool invertCancel)
27 rfbScreenInfoPtr s = m->screen;
28 rfbPixel bcolour = m->backColour;
29 rfbPixel colour = m->colour;
31 rfbFillRect(s,m->x1,m->okY-m->textH,m->x2,m->okY,bcolour);
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,
39 rfbDrawString(s,m->font,m->okX+m->xhot,m->okY-1+m->yhot,okStr,colour);
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,
48 rfbDrawString(s,m->font,m->cancelX+m->xhot,m->okY-1+m->yhot,cancelStr,colour);
50 m->okInverted = invertOk;
51 m->cancelInverted = invertCancel;
54 /* line is relative to displayStart */
55 static void selPaintLine(rfbSelectData* m,int line,rfbBool invert)
57 int y1 = m->y1+line*m->textH, y2 = y1+m->textH;
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],
65 invert?m->backColour:m->colour,
66 invert?m->backColour:m->colour);
69 static void selSelect(rfbSelectData* m,int _index)
73 if(_index==m->selected || _index<0 || _index>=m->listSize)
77 selPaintLine(m,m->selected-m->displayStart,FALSE);
79 if(_index<m->displayStart || _index>=m->displayStart+m->pageH) {
80 /* targetLine is the screen line in which the selected line will
82 targetLine = m->pageH/2 doesn't look so nice */
83 int targetLine = m->selected-m->displayStart;
84 int lineStart,lineEnd;
89 else if(_index+m->pageH-targetLine>=m->listSize)
90 targetLine = _index+m->pageH-m->listSize;
91 delta = _index-(m->displayStart+targetLine);
93 if(delta>-m->pageH && delta<m->pageH) {
95 lineStart = m->pageH-delta;
97 rfbDoCopyRect(m->screen,m->x1,m->y1,m->x2,m->y1+lineStart*m->textH,
102 rfbDoCopyRect(m->screen,
103 m->x1,m->y1+lineEnd*m->textH,m->x2,m->y2,
110 m->displayStart += delta;
111 for(delta=lineStart;delta<lineEnd;delta++)
113 selPaintLine(m,delta,FALSE);
116 m->selected = _index;
117 selPaintLine(m,m->selected-m->displayStart,TRUE);
119 if(m->selChangedHook)
120 m->selChangedHook(_index);
122 /* todo: scrollbars */
125 static void selKbdAddEvent(rfbBool down,rfbKeySym keySym,rfbClientPtr cl)
128 if(keySym>' ' && keySym<0xff) {
130 rfbSelectData* m = (rfbSelectData*)cl->screen->screenData;
131 char c = tolower(keySym);
133 for(i=m->selected+1;m->list[i] && tolower(m->list[i][0])!=c;i++);
135 for(i=0;i<m->selected && tolower(m->list[i][0])!=c;i++);
137 } else if(keySym==XK_Escape) {
138 rfbSelectData* m = (rfbSelectData*)cl->screen->screenData;
140 } else if(keySym==XK_Return) {
141 rfbSelectData* m = (rfbSelectData*)cl->screen->screenData;
144 rfbSelectData* m = (rfbSelectData*)cl->screen->screenData;
145 int curSel=m->selected;
148 selSelect(m,curSel-1);
149 } else if(keySym==XK_Down) {
150 if(curSel+1<m->listSize)
151 selSelect(m,curSel+1);
153 if(keySym==XK_Page_Down) {
154 if(curSel+m->pageH<m->listSize)
155 selSelect(m,curSel+m->pageH);
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);
169 static void selPtrAddEvent(int buttonMask,int x,int y,rfbClientPtr cl)
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) {
175 selPaintButtons(m,TRUE,FALSE);
178 } else if(x>=m->cancelBX && x<m->cancelBX+m->buttonWidth) {
179 if(!m->cancelInverted)
180 selPaintButtons(m,FALSE,TRUE);
183 } else if(m->okInverted || m->cancelInverted)
184 selPaintButtons(m,FALSE,FALSE);
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);
193 m->lastButtons = buttonMask;
195 /* todo: scrollbars */
198 static rfbCursorPtr selGetCursorPtr(rfbClientPtr cl)
203 int rfbSelectBox(rfbScreenInfoPtr rfbScreen,rfbFontDataPtr font,
205 int x1,int y1,int x2,int y2,
206 rfbPixel colour,rfbPixel backColour,
207 int border,SelectionChangedHookPtr selChangedHook)
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;
218 int fx1,fy1,fx2,fy2; /* for font bbox */
220 if(list==0 || *list==0)
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)
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;
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! */
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;
247 frameBufferBackup = (char*)malloc(bpp*(x2-x1)*(y2-y1));
249 selData.state = SELECTING;
250 selData.screen = rfbScreen;
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;
262 rfbScreen->screenData = &selData;
263 rfbScreen->kbdAddEvent = selKbdAddEvent;
264 rfbScreen->ptrAddEvent = selPtrAddEvent;
265 rfbScreen->getCursorPtr = selGetCursorPtr;
266 rfbScreen->displayHook = NULL;
270 memcpy(frameBufferBackup+j*(x2-x1)*bpp,
271 rfbScreen->frameBuffer+j*rfbScreen->paddedWidthInBytes+x1*bpp,
274 /* paint list and buttons */
275 rfbFillRect(rfbScreen,x1,y1,x2,y2,colour);
276 selPaintButtons(&selData,FALSE,FALSE);
277 selSelect(&selData,0);
280 while(selData.state == SELECTING)
281 rfbProcessEvents(rfbScreen,20000);
283 /* copy back screen data */
285 memcpy(rfbScreen->frameBuffer+j*rfbScreen->paddedWidthInBytes+x1*bpp,
286 frameBufferBackup+j*(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;
296 if(selData.state==CANCEL)
298 return(selData.selected);