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