Home | History | Annotate | Download | only in libvncserver
      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