9fe689a6e42c1489f36d0d4afb9299366cee7fda
[alexxy/gromacs.git] / src / ngmx / xdlgitem.c
1 /*
2  * 
3  *                This source code is part of
4  * 
5  *                 G   R   O   M   A   C   S
6  * 
7  *          GROningen MAchine for Chemical Simulations
8  * 
9  *                        VERSION 3.2.0
10  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12  * Copyright (c) 2001-2004, The GROMACS development team,
13  * check out http://www.gromacs.org for more information.
14
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  * 
20  * If you want to redistribute modifications, please consider that
21  * scientific software is very special. Version control is crucial -
22  * bugs must be traceable. We will be happy to consider code for
23  * inclusion in the official distribution, but derived work must not
24  * be called official GROMACS. Details are found in the README & COPYING
25  * files - if they are missing, get the official version at www.gromacs.org.
26  * 
27  * To help us fund GROMACS development, we humbly ask that you cite
28  * the papers on the package - you can find them in the top README file.
29  * 
30  * For more info, check our website at http://www.gromacs.org
31  * 
32  * And Hey:
33  * Gyas ROwers Mature At Cryogenic Speed
34  */
35 #ifdef HAVE_CONFIG_H
36 #include <config.h>
37 #endif
38
39 #include <ctype.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "gmx_fatal.h"
45 #include "string2.h"
46 #include "smalloc.h"
47 #include "macros.h"
48 #include "Xstuff.h"
49 #include "xdlgitem.h"
50
51 #define BUFSIZE 16
52
53 t_dlgitem *newitem(t_x11 *x11)
54 {
55   t_dlgitem *item;
56   
57   snew(item,1);
58   
59   return item;
60 }
61
62 /*****************************
63  *
64  * Window Procedures and helpful functions
65  *
66  ****************************/
67 static void ShowCaret(t_x11 *x11, t_dlgitem *dlgitem)
68 {
69   t_edittext *et;
70
71   if (dlgitem->type == edlgET) {
72     int x,y1,y2;
73     
74     et=&(dlgitem->u.edittext);
75     x=XTextWidth(x11->font,dlgitem->win.text,strlen(dlgitem->win.text))+XCARET+
76       XTextWidth(x11->font,(char*) &(et->buf[et->strbegin]),et->pos);
77     y1=(dlgitem->win.height-XTextHeight(x11->font))/2;
78     y2=(dlgitem->win.height-y1);
79     y1--, y2++;
80     XDrawLine(x11->disp,dlgitem->win.self,x11->gc,x-XCARET,y1,x+XCARET,y1);
81     XDrawLine(x11->disp,dlgitem->win.self,x11->gc,x,y1,x,y2);
82     XDrawLine(x11->disp,dlgitem->win.self,x11->gc,x-XCARET,y2,x+XCARET,y2);
83   }
84 }
85
86 static void HideCaret(t_x11 *x11, t_dlgitem *dlgitem)
87 {
88   XSetForeground(x11->disp,x11->gc,x11->bg);
89   ShowCaret(x11,dlgitem);
90   XSetForeground(x11->disp,x11->gc,x11->fg);
91 }
92
93 static int DefWndProc(t_x11 *x11, t_dlgitem *dlgitem, XEvent *event)
94 {
95   XComposeStatus status;
96   KeySym keysym;
97   char c[BUFSIZE+1];
98
99 #ifdef DEBUG
100   printf("DefWndProc\n");
101 #endif
102   switch(event->type) {
103   case Expose:
104   case ButtonPress:
105   case KeyPress:
106     if (HelpPressed(event))
107       return HELPPRESSED;
108     else {
109       XLookupString(&(event->xkey),c,BUFSIZE,&keysym,&status);
110       if ((keysym==XK_Return) || (keysym==XK_KP_Enter))
111         return ENTERPRESSED;
112     }
113     break;
114   case EnterNotify:
115     dlgitem->win.bFocus=TRUE;
116     ShowCaret(x11,dlgitem);
117     /*    LightBorder(x11->disp,dlgitem->win.self,x11->fg); */
118     break;
119   case LeaveNotify:
120     dlgitem->win.bFocus=FALSE;
121     HideCaret(x11,dlgitem);
122     /*    LightBorder(x11->disp,dlgitem->win.self,x11->bg); */
123     break;
124   default:
125     XBell(x11->disp,50);
126   }
127   return ITEMOK;
128 }
129
130 static int WndProcBN(t_x11 *x11, t_dlgitem *dlgitem, XEvent *event)
131 {
132   t_windata *win;
133   int x,w,th;
134
135   if (dlgitem->type != edlgBN)
136     gmx_incons("button processing");
137   win=&(dlgitem->win);
138   w=XTextWidth(x11->font,win->text,strlen(win->text));
139   x=(win->width-w)/2;
140   th=XTextHeight(x11->font)+OFFS_Y;
141   switch(event->type) {
142   case Expose:
143     RectWin(x11->disp,x11->gc,win,x11->fg);
144     TextInRect(x11,win->self,win->text,0,0,win->width,th,eXCenter,eYCenter);
145     break;
146   case ButtonPress:
147     return BNPRESSED;
148   case EnterNotify:
149     XDrawLine(x11->disp,win->self,x11->gc,x-1,th,x+w,th);
150     break;
151   case LeaveNotify:
152     XSetForeground(x11->disp,x11->gc,x11->bg);
153     XDrawLine(x11->disp,win->self,x11->gc,x-1,th,x+w,th);
154     XSetForeground(x11->disp,x11->gc,x11->fg);
155     break;
156   default: 
157     return DefWndProc(x11,dlgitem,event);
158   }
159   return ITEMOK;
160 }
161
162 static int WndProcRB(t_x11 *x11, t_dlgitem *dlgitem, XEvent *event)
163 {
164   t_radiobutton *rb;
165   t_windata *win;
166   int x,y,rad;
167   
168   if (dlgitem->type != edlgRB)
169     gmx_incons("radiobutton processing");
170   rb=&(dlgitem->u.radiobutton);
171   win=&(dlgitem->win);
172   
173   rad=win->height/3;
174   x=rad;
175   y=win->height/2;
176   switch(event->type) {
177   case Expose:
178     XClearArea(x11->disp,win->self,x-rad,y-rad,x+rad,y+rad,False);
179     if (rb->bSelect)
180       /* Filled */
181       XFillCircle(x11->disp,win->self,x11->gc,x,y,rad);
182     XDrawCircle(x11->disp,win->self,x11->gc,x,y,rad);
183     x+=rad+OFFS_X;
184     TextInRect(x11,win->self,win->text,x,0,win->width-x,win->height,
185                eXLeft,eYCenter);
186     break;
187   case ButtonPress:
188     if (!rb->bSelect)
189       return RBPRESSED;
190     XBell(x11->disp,50);
191     break;
192   case EnterNotify:
193   case LeaveNotify:
194     break;
195   default:
196     return DefWndProc(x11,dlgitem,event);
197   }
198   return ITEMOK;
199 }
200
201 static int WndProcGB(t_x11 *x11, t_dlgitem *dlgitem, XEvent *event)
202 {
203   t_windata *win;
204   int x,y;
205
206   if (dlgitem->type != edlgGB)
207     gmx_incons("gb processing");
208   win=&(dlgitem->win);
209   
210   x=XTextWidth(x11->font,win->text,strlen(win->text));
211   y=XTextHeight(x11->font);
212   switch(event->type) {
213   case Expose:
214     XSetForeground(x11->disp,x11->gc,x11->fg);
215     XDrawRoundRect(x11->disp,win->self,x11->gc,0,y/2,
216                    win->width-1,win->height-y/2-1);
217     XClearArea(x11->disp,win->self,OFFS_X,0,x+OFFS_X,y,False);
218     TextInRect(x11,win->self,win->text,2*OFFS_X,0,x,y,eXCenter,eYCenter);
219     break;
220   case EnterNotify:
221   case LeaveNotify:
222     break;
223   default:
224     return DefWndProc(x11,dlgitem,event);
225   }
226   return ITEMOK;
227 }
228
229 static int WndProcCB(t_x11 *x11, t_dlgitem *dlgitem, XEvent *event)
230 {
231   t_checkbox *cb;
232   t_windata *win;
233   int x,y,w,h;
234   
235   if (dlgitem->type != edlgCB)
236     gmx_incons("check box processing");
237   cb=&(dlgitem->u.checkbox);
238   win=&(dlgitem->win);
239
240   x=0;
241   y=win->height/7;
242   w=5*y;
243   h=5*y;
244   switch(event->type) {
245   case Expose:
246     XSetForeground(x11->disp,x11->gc,x11->fg);
247     XClearArea(x11->disp,win->self,x,y,w,h,False);
248     XDrawRectangle(x11->disp,win->self,x11->gc,x,y,w,h);
249     if (cb->bChecked) {
250       XDrawLine(x11->disp,win->self,x11->gc,x,y,x+w,y+h);
251       XDrawLine(x11->disp,win->self,x11->gc,x+w,y,x,y+h);
252     }
253     x=w+OFFS_X;
254     TextInRect(x11,win->self,win->text,x,0,win->width-x,win->height,
255                eXLeft,eYCenter);
256     break;
257   case ButtonPress:
258     cb->bChecked=!cb->bChecked;
259     return CBPRESSED;
260   case EnterNotify:
261   case LeaveNotify:
262     break;
263   default:
264     return DefWndProc(x11,dlgitem,event);
265   }
266   return ITEMOK;
267 }
268
269 static int WndProcST(t_x11 *x11, t_dlgitem *dlgitem, XEvent *event)
270 {
271   t_statictext *st;
272   t_windata *win;
273   int i,dy;
274   
275   if (dlgitem->type != edlgST)
276     gmx_incons("st processing");
277   st=&(dlgitem->u.statictext);
278   win=&(dlgitem->win);
279
280   switch(event->type) {
281   case Expose:
282     dy=XTextHeight(x11->font)+OFFS_Y;
283     for (i=0; (i<st->nlines); i++)
284       TextInRect(x11,win->self,st->lines[i],
285                  0,OFFS_Y+i*dy,win->width,dy,eXLeft,eYCenter);
286     break;
287   default:
288     return DefWndProc(x11,dlgitem,event);
289   }
290   return ITEMOK;
291 }
292
293 static gmx_bool insert(char *s, char c, int *pos)
294 {
295   int i,sl;
296
297   if (isprint(c)) {
298     sl=strlen(s);
299     /* +1 for zero termination */
300     for(i=sl+1; (i>*pos); i--)
301       s[i+1]=s[i];
302     s[*pos]=c;
303     (*pos)++;
304     return TRUE;
305   }
306   return FALSE;
307 }
308
309 static gmx_bool my_backspace(char *s, int *pos)
310 {
311   int i,sl;
312
313   sl=strlen(s);
314   if ((sl > 0) && ((*pos) > 0)) {
315     for(i=*pos-1; (i<sl); i++)
316       s[i]=s[i+1];
317     (*pos)=max(0,(*pos)-1);
318     return TRUE;
319   }
320   return FALSE;
321 }
322
323 static gmx_bool my_delete(char *s, int *pos)
324 {
325   int i,sl;
326
327   sl=strlen(s);
328   if ((sl > 0) && ((*pos) < sl)) {
329     for(i=*pos; (i<sl); i++)
330       s[i]=s[i+1];
331     return TRUE;
332   }
333   return FALSE;
334 }
335
336 static int WndProcET(t_x11 *x11, t_dlgitem *dlgitem, XEvent *event)
337 {
338   t_edittext *et;
339   t_windata  *win;
340   KeySym     keysym;
341   char       c[BUFSIZE+1],*bp;
342   char       scrbuf[STRLEN];
343   int        i,xp,xtitle,ewidth;
344   
345   if (dlgitem->type != edlgET)
346     gmx_incons("st processing");
347   et=&(dlgitem->u.edittext);
348   win=&(dlgitem->win);
349
350   /* Copy string part that is visible into screen buffer */
351   for(i=0; (i<et->buflen); i++)
352     scrbuf[i]=et->buf[i+et->strbegin];
353   scrbuf[i]='\0';
354
355   switch(event->type) {
356   case Expose:
357     XSetForeground(x11->disp,x11->gc,x11->fg);
358     xtitle=XTextWidth(x11->font,win->text,strlen(win->text));
359     ewidth=win->width-xtitle;
360     TextInRect(x11,win->self,win->text,
361                0,0,xtitle-1,win->height,eXLeft,eYCenter);
362     XClearArea(x11->disp,win->self,xtitle,0,ewidth+XCARET,win->height,False);
363     TextInRect(x11,win->self,scrbuf,
364                xtitle+XCARET,0,ewidth,win->height,eXLeft,eYCenter);
365 #ifdef DEBUG
366     printf("Expose\n");
367 #endif
368     if (win->bFocus)
369       ShowCaret(x11,dlgitem);
370     break;
371   case ButtonPress:
372     /* Calculate new position for caret */
373     et->pos=strlen(et->buf);
374     bp=strdup(et->buf);
375     xp=event->xbutton.x-XTextWidth(x11->font,win->text,strlen(win->text))-
376       XCARET;
377     while ((et->pos > 0) && (XTextWidth(x11->font,bp,strlen(bp)) > xp)) {
378       et->pos--;
379       bp[et->pos]='\0';
380     }
381     sfree(bp);
382     et->bChanged=TRUE;
383     return ETCHANGED;
384   case KeyPress:
385     /* Check for HelpKey */
386     if (HelpPressed(event))
387       return DefWndProc(x11,dlgitem,event);
388     XLookupString(&(event->xkey),c,BUFSIZE,&keysym,NULL);
389 #ifdef DEBUG
390     printf("Keysym: %x\n",keysym);
391 #endif
392     switch(keysym) {
393     case XK_Delete:
394       if (my_delete(et->buf,&(et->pos))){
395         et->bChanged=TRUE;
396         return ETCHANGED;
397       }
398       else
399         XBell(x11->disp,50);
400       break;
401     case XK_BackSpace:
402       if (my_backspace(et->buf,&(et->pos))) {
403         et->bChanged=TRUE;
404         return ETCHANGED;
405       }
406       else
407         XBell(x11->disp,50);
408       break;
409     case XK_KP_Enter:
410     case XK_Return:
411       return ENTERPRESSED;
412     case XK_Home:
413       et->pos=0;
414       et->strbegin=0;
415       et->bChanged=TRUE;
416       return ETCHANGED;
417     case XK_End:
418       if (strlen(et->buf) <= et->buflen)
419         et->pos=strlen(et->buf);
420       else {
421         et->pos=et->buflen;
422         et->strbegin=strlen(et->buf)-et->buflen;
423       }
424       et->bChanged=TRUE;
425       return ETCHANGED;
426     case XK_Left:
427       et->pos=max(0,et->pos-1);
428       et->strbegin=min(et->strbegin,et->pos);
429       et->bChanged=TRUE;
430       return ETCHANGED;
431     case XK_Right:
432       if ((et->pos < et->buflen) && (et->strbegin+et->buflen > strlen(et->buf)))
433         et->pos++;
434       else if ((et->buflen   < strlen(et->buf)) && 
435                (et->strbegin < strlen(et->buf)-et->buflen))
436         et->strbegin++;
437       else
438         break;
439       et->bChanged=TRUE;
440       return ETCHANGED;
441     default:
442       if (keysym < 256) 
443         if (insert(et->buf,c[0],&(et->pos))) {
444           et->bChanged=TRUE;
445           return ETCHANGED;
446         }
447       XBell(x11->disp,50);
448       break;
449     }
450     break;
451   case LeaveNotify:
452     win->bFocus=FALSE;
453     HideCaret(x11,dlgitem);
454     if (et->bChanged)
455       et->bChanged=FALSE;
456     break;
457   default:
458     return DefWndProc(x11,dlgitem,event);
459   }
460   return ITEMOK;
461 }
462
463 /*****************************
464  *
465  * Routines to create dialog items, all items have an id
466  * which you can use to extract info. It is possible to have
467  * multiple items with the same id but it may then not be possible
468  * to extract information.
469  * All routines take the position relative to the parent dlg
470  * and the size and border width.
471  * If the width and height are set to zero initially, they will
472  * be calculated and set by the routine. With the dlgitem manipulation
473  * routines listed below, the application can then move the items around
474  * on the dlg box, and if wished resize them.
475  *
476  ****************************/
477 t_dlgitem *CreateButton(t_x11 *x11,
478                         const char *szLab,gmx_bool bDef,t_id id,t_id groupid,
479                         int x0,int y0,int w,int h,int bw)
480 {
481   t_dlgitem *dlgitem;
482   char *lab;
483   
484   dlgitem=newitem(x11);
485   if (h==0) h=XTextHeight(x11->font)+2*OFFS_Y;
486   if (w==0) w=XTextWidth(x11->font,szLab,strlen(szLab))+2*OFFS_X;
487   if (bDef) {
488     snew(lab,strlen(szLab)+7); /* 6 for >> << and 1 for \0 */
489     sprintf(lab,">> %s <<",szLab);
490   }
491   else
492     lab=strdup(szLab);
493   InitWin(&(dlgitem->win),x0,y0,w,h,bw,szLab);
494   sfree(lab);
495   dlgitem->ID=id;
496   dlgitem->GroupID=groupid;
497   dlgitem->type=edlgBN;
498   dlgitem->u.button.bDefault=bDef;
499   dlgitem->WndProc=WndProcBN;
500   
501   return dlgitem;
502 }
503
504 t_dlgitem *CreateRadioButton(t_x11 *x11,
505                              const char *szLab,gmx_bool bSet,t_id id,
506                              t_id groupid,
507                              int x0,int y0,int w,int h,int bw)
508 {
509   t_dlgitem *dlgitem;
510   
511   dlgitem=newitem(x11);
512   if (h==0) h=XTextHeight(x11->font)+OFFS_Y;
513   if (w==0) w=XTextWidth(x11->font,szLab,strlen(szLab))+OFFS_X+h;
514   InitWin(&(dlgitem->win),x0,y0,w,h,bw,szLab);
515   dlgitem->ID=id;
516   dlgitem->GroupID=groupid;
517   dlgitem->type=edlgRB;
518   dlgitem->u.radiobutton.bSelect=bSet;
519   dlgitem->WndProc=WndProcRB;
520
521   return dlgitem;
522 }
523
524 t_dlgitem *CreateGroupBox(t_x11 *x11,
525                           const char *szLab,t_id id,
526                           int nitems, t_id items[],
527                           int x0,int y0,int w,int h,int bw)
528 {
529   t_dlgitem *dlgitem;
530
531   dlgitem=newitem(x11);
532   if (h==0) h=XTextHeight(x11->font)+OFFS_Y;
533   if (w==0) w=XTextWidth(x11->font,szLab,strlen(szLab))+2*OFFS_X;
534   InitWin(&(dlgitem->win),x0,y0,w,h,bw,szLab);
535   dlgitem->GroupID=id;
536   dlgitem->ID=id;
537   dlgitem->type=edlgGB;
538   dlgitem->u.groupbox.nitems=nitems;
539   snew(dlgitem->u.groupbox.item,nitems);
540   memcpy((char *)dlgitem->u.groupbox.item,(char *)items,
541          nitems*sizeof(items[0]));
542   dlgitem->WndProc=WndProcGB;
543
544   return dlgitem;
545 }
546
547 t_dlgitem *CreateCheckBox(t_x11 *x11,
548                           const char *szLab,gmx_bool bCheckedInitial,t_id id,
549                           t_id groupid,
550                           int x0,int y0,int w,int h,int bw)
551 {
552   t_dlgitem *dlgitem;
553   
554   dlgitem=newitem(x11);
555   if (h==0) h=XTextHeight(x11->font)+OFFS_Y;
556   if (w==0) w=XTextWidth(x11->font,szLab,strlen(szLab))+OFFS_X+h;
557   InitWin(&(dlgitem->win),x0,y0,w,h,bw,szLab);
558   dlgitem->ID=id;
559   dlgitem->GroupID=groupid;
560   dlgitem->type=edlgCB;
561   dlgitem->u.checkbox.bChecked=bCheckedInitial;
562   dlgitem->WndProc=WndProcCB;
563  
564   return dlgitem;
565 }
566
567 t_dlgitem *CreatePixmap(t_x11 *x11,
568                         Pixmap pm,t_id id,
569                         t_id groupid,int x0,int y0,int w,int h,int bw)
570 {
571   t_dlgitem *dlgitem;
572   
573   dlgitem=newitem(x11);
574   InitWin(&(dlgitem->win),x0,y0,w,h,bw,NULL);
575   dlgitem->ID=id;
576   dlgitem->type=edlgPM;
577   dlgitem->u.pixmap.pm=pm;
578   dlgitem->WndProc=DefWndProc;
579   
580   return dlgitem;
581 }
582
583 t_dlgitem *CreateStaticText(t_x11 *x11,
584                             int nlines,char * const * lines,t_id id,
585                             t_id groupid,
586                             int x0,int y0,int w,int h,int bw)
587 {
588   t_dlgitem *dlgitem;
589   int i;
590   
591   dlgitem=newitem(x11);
592   if (h==0) h=(XTextHeight(x11->font)+OFFS_Y)*nlines+OFFS_Y;
593   if (w==0) {
594     for(i=0; (i<nlines); i++)
595       w=max(w,XTextWidth(x11->font,lines[i],strlen(lines[i])));
596     w+=2*OFFS_X;
597   }
598   InitWin(&(dlgitem->win),x0,y0,w,h,bw,NULL);
599   dlgitem->ID=id;
600   dlgitem->GroupID=groupid;
601   dlgitem->type=edlgST;
602   dlgitem->u.statictext.nlines=nlines;
603   snew(dlgitem->u.statictext.lines,nlines);
604   for(i=0; (i<nlines); i++)
605     dlgitem->u.statictext.lines[i]=strdup(lines[i]);
606   dlgitem->WndProc=WndProcST;
607  
608   return dlgitem;
609 }
610
611 t_dlgitem *CreateEditText(t_x11 *x11,
612                           const char *title,
613                           int screenbuf,char *buf, t_id id,t_id groupid,
614                           int x0,int y0,int w,int h,int bw)
615 {
616   t_dlgitem *dlgitem;
617   t_edittext *et;
618   
619   dlgitem=newitem(x11);
620   if (h==0) h=XTextHeight(x11->font)+OFFS_Y;
621   if (w==0) {
622     char *test;
623
624     snew(test,screenbuf);
625     memset(test,'w',screenbuf);
626     w=XTextWidth(x11->font,test,screenbuf)+
627       XTextWidth(x11->font,title,strlen(title))+
628       2*XCARET+2*OFFS_X;
629     sfree(test);
630   }
631   InitWin(&(dlgitem->win),x0,y0,w,h,bw,title);
632   dlgitem->ID=id;
633   dlgitem->GroupID=groupid;
634   dlgitem->type=edlgET;
635   et=&(dlgitem->u.edittext);
636   snew(et->buf,STRLEN);
637   strcpy(et->buf,buf);
638   et->buflen=screenbuf;
639   et->strbegin=0;
640   et->bChanged=FALSE;
641   dlgitem->WndProc=WndProcET;
642
643   return dlgitem;
644 }
645
646 #define SC(src) (strlen(src)?strdup(src):NULL)
647
648 void SetDlgitemOpts(t_dlgitem *dlgitem,gmx_bool bUseMon,
649                     char *set,char *get,char *help)
650 {
651   dlgitem->bUseMon=bUseMon;
652   dlgitem->set=SC(set);
653   dlgitem->get=SC(get);
654   dlgitem->help=SC(help);
655 #ifdef DEBUG
656   printf("Help is: '%s'\n",dlgitem->help);
657 #endif
658 }