/apps/desktop/libvncserver/selbox.c

http://ftk.googlecode.com/ · C · 300 lines · 253 code · 35 blank · 12 comment · 78 complexity · 17101e5680848afbf1140410e16a63e2 MD5 · raw file

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