Update instructions in containers.rst
[gromacs.git] / src / programs / view / xdlg.cpp
blobb403a9e71b5ac96c7850108df398b05d3f3ee87d
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
5 * Copyright (c) 2001-2004, The GROMACS development team.
6 * Copyright (c) 2013,2014,2015,2017,2019, by the GROMACS development team, led by
7 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
8 * and including many others, as listed in the AUTHORS file in the
9 * top-level source directory and at http://www.gromacs.org.
11 * GROMACS is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation; either version 2.1
14 * of the License, or (at your option) any later version.
16 * GROMACS is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with GROMACS; if not, see
23 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
24 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
26 * If you want to redistribute modifications to GROMACS, please
27 * consider that scientific software is very special. Version
28 * control is crucial - bugs must be traceable. We will be happy to
29 * consider code for inclusion in the official distribution, but
30 * derived work must not be called official GROMACS. Details are found
31 * in the README & COPYING files - if they are missing, get the
32 * official version at http://www.gromacs.org.
34 * To help us fund GROMACS development, we humbly ask that you cite
35 * the research papers on the package. Check out http://www.gromacs.org.
37 #include "gmxpre.h"
39 #include "xdlg.h"
41 #include <cstdio>
42 #include <cstdlib>
43 #include <cstring>
45 #include "gromacs/utility/cstringutil.h"
46 #include "gromacs/utility/fatalerror.h"
47 #include "gromacs/utility/smalloc.h"
49 #include "Xstuff.h"
50 #include "xmb.h"
51 #include "xutil.h"
52 /*****************************
54 * Helpful routines
56 ****************************/
57 t_dlgitem* FindItem(t_dlg* dlg, t_id id)
59 int i;
61 for (i = 0; (i < dlg->nitem); i++)
63 if (dlg->dlgitem[i]->ID == id)
65 return dlg->dlgitem[i];
68 return nullptr;
71 t_dlgitem* FindWin(t_dlg* dlg, Window win)
73 int i;
75 for (i = 0; (i < dlg->nitem); i++)
77 if (dlg->dlgitem[i]->win.self == win)
79 return dlg->dlgitem[i];
82 return nullptr;
85 /*****************************
87 * Routines to manipulate items on a dialog box
89 ****************************/
90 bool QueryDlgItemSize(t_dlg* dlg, t_id id, int* w, int* h)
92 t_dlgitem* dlgitem;
94 if ((dlgitem = FindItem(dlg, id)) != nullptr)
96 *w = dlgitem->win.width;
97 *h = dlgitem->win.height;
98 return true;
100 return false;
103 bool QueryDlgItemPos(t_dlg* dlg, t_id id, int* x0, int* y0)
105 t_dlgitem* dlgitem;
107 if ((dlgitem = FindItem(dlg, id)) != nullptr)
109 *x0 = dlgitem->win.x;
110 *y0 = dlgitem->win.y;
111 return true;
113 return false;
116 int QueryDlgItemX(t_dlg* dlg, t_id id)
118 t_dlgitem* dlgitem;
120 if ((dlgitem = FindItem(dlg, id)) != nullptr)
122 return dlgitem->win.x;
124 return 0;
127 int QueryDlgItemY(t_dlg* dlg, t_id id)
129 t_dlgitem* dlgitem;
131 if ((dlgitem = FindItem(dlg, id)) != nullptr)
133 return dlgitem->win.y;
135 return 0;
138 int QueryDlgItemW(t_dlg* dlg, t_id id)
140 t_dlgitem* dlgitem;
142 if ((dlgitem = FindItem(dlg, id)) != nullptr)
144 return dlgitem->win.width;
146 return 0;
149 int QueryDlgItemH(t_dlg* dlg, t_id id)
151 t_dlgitem* dlgitem;
153 if ((dlgitem = FindItem(dlg, id)) != nullptr)
155 return dlgitem->win.height;
157 return 0;
160 bool SetDlgItemSize(t_dlg* dlg, t_id id, int w, int h)
162 t_dlgitem* dlgitem;
163 #ifdef DEBUG
164 int old_w, old_h;
165 #endif
167 if ((dlgitem = FindItem(dlg, id)) != nullptr)
169 #ifdef DEBUG
170 old_w = dlgitem->win.width;
171 old_h = dlgitem->win.height;
172 #endif
173 if (w)
175 dlgitem->win.width = w;
177 if (h)
179 dlgitem->win.height = h;
181 #ifdef DEBUG
182 std::fprintf(dlg->x11->console, "Size window from: %dx%d to %dx%d\n", old_w, old_h,
183 dlgitem->win.width, dlgitem->win.height);
184 dlg->x11->Flush(dlg->x11);
185 #endif
186 if (dlgitem->win.self)
188 XResizeWindow(dlg->x11->disp, dlgitem->win.self, dlgitem->win.width, dlgitem->win.height);
190 if ((w) && (dlgitem->type == edlgGB))
192 int i;
193 t_id gid = dlgitem->GroupID;
194 t_id id = dlgitem->ID;
195 for (i = 0; (i < dlg->nitem); i++)
197 t_dlgitem* child = dlg->dlgitem[i];
198 if ((child->GroupID == gid) && (child->ID != id))
200 SetDlgItemSize(dlg, child->ID, w - 4 * OFFS_X, 0);
204 return true;
206 return false;
209 bool SetDlgItemPos(t_dlg* dlg, t_id id, int x0, int y0)
211 t_dlgitem* dlgitem;
212 int old_x, old_y;
214 if ((dlgitem = FindItem(dlg, id)) != nullptr)
216 old_x = dlgitem->win.x;
217 old_y = dlgitem->win.y;
218 dlgitem->win.x = x0;
219 dlgitem->win.y = y0;
220 #ifdef DEBUG
221 std::fprintf(dlg->x11->console, "Move window from: %d,%d to %d,%d\n", old_x, old_y, x0, y0);
222 dlg->x11->Flush(dlg->x11);
223 #endif
224 if (dlgitem->win.self)
226 XMoveWindow(dlg->x11->disp, dlgitem->win.self, x0, y0);
228 if (dlgitem->type == edlgGB)
230 int i, x, y;
231 t_id gid = dlgitem->GroupID;
232 t_id id = dlgitem->ID;
233 x = dlgitem->win.x + 2 * OFFS_X - old_x;
234 y = dlgitem->win.y + 2 * OFFS_Y - old_y;
235 for (i = 0; (i < dlg->nitem); i++)
237 t_dlgitem* child = dlg->dlgitem[i];
238 if ((child->GroupID == gid) && (child->ID != id))
240 SetDlgItemPos(dlg, child->ID, child->win.x + x, child->win.y + y);
244 return true;
246 return false;
249 /*****************************
251 * Routines to extract information from the dlg proc
252 * after dlg is exec'ed
254 ****************************/
255 bool IsCBChecked(t_dlg* dlg, t_id id)
257 t_dlgitem* dlgitem;
259 if ((dlgitem = FindItem(dlg, id)) != nullptr)
261 if (dlgitem->type == edlgCB)
263 return dlgitem->u.checkbox.bChecked;
267 return false;
270 t_id RBSelected(t_dlg* dlg, int gid)
272 int i;
274 for (i = 0; (i < dlg->nitem); i++)
276 if ((dlg->dlgitem[i]->type == edlgRB) && (dlg->dlgitem[i]->u.radiobutton.bSelect)
277 && (dlg->dlgitem[i]->GroupID == gid))
279 return dlg->dlgitem[i]->ID;
283 return -1;
286 int EditTextLen(t_dlg* dlg, t_id id)
288 t_dlgitem* dlgitem;
290 if ((dlgitem = FindItem(dlg, id)) != nullptr)
292 if (dlgitem->type == edlgET)
294 return std::strlen(dlgitem->u.edittext.buf);
298 return 0;
301 char* EditText(t_dlg* dlg, t_id id)
303 t_dlgitem* dlgitem;
305 if ((dlgitem = FindItem(dlg, id)) != nullptr)
307 if (dlgitem->type == edlgET)
309 return dlgitem->u.edittext.buf;
313 return nullptr;
316 /*****************************
318 * Exececute the dialog box procedure
319 * Returns when a button is pushed.
320 * return value is the ID of the button
322 ****************************/
323 void ShowDlg(t_dlg* dlg)
325 int i;
326 t_dlgitem* dlgitem;
328 XMapWindow(dlg->x11->disp, dlg->win.self);
329 XMapSubwindows(dlg->x11->disp, dlg->win.self);
330 for (i = 0; (i < dlg->nitem); i++)
332 LightBorder(dlg->x11->disp, dlg->dlgitem[i]->win.self, dlg->bg);
334 XSetForeground(dlg->x11->disp, dlg->x11->gc, dlg->x11->fg);
335 for (i = 0; (i < dlg->nitem); i++)
337 dlgitem = dlg->dlgitem[i];
338 if ((dlgitem->type == edlgBN) && (dlgitem->u.button.bDefault))
340 PushMouse(dlg->x11->disp, dlgitem->win.self, dlgitem->win.width / 2, dlgitem->win.height / 2);
341 dlg->bPop = true;
342 break;
345 dlg->bGrab = false;
348 void HideDlg(t_dlg* dlg)
350 if (dlg->bPop)
352 PopMouse(dlg->x11->disp);
355 XUnmapSubwindows(dlg->x11->disp, dlg->win.self);
356 XUnmapWindow(dlg->x11->disp, dlg->win.self);
359 void NoHelp(t_dlg* dlg)
361 const char* lines[2] = { "Error", "No help for this item" };
362 MessageBox(dlg->x11, dlg->wDad, "No Help", 2, lines, MB_OK | MB_ICONSTOP | MB_APPLMODAL,
363 nullptr, nullptr);
366 void HelpDlg(t_dlg* dlg)
368 const char* lines[] = { "Place the cursor over one of the items",
369 "and press the F1 key to get more help.", "First press the OK button." };
370 MessageBox(dlg->x11, dlg->win.self, "Help Dialogbox", 3, lines,
371 MB_OK | MB_ICONINFORMATION | MB_APPLMODAL, nullptr, nullptr);
374 void HelpNow(t_dlg* dlg, t_dlgitem* dlgitem)
376 char buf[80];
377 bool bCont = true;
378 int i, nlines = 0;
379 char** lines = nullptr;
381 if (!dlgitem->help)
383 NoHelp(dlg);
384 return;
387 std::printf("%s\n", dlgitem->help);
390 fgets2(buf, 79, stdin);
391 #ifdef DEBUG
392 std::fprintf(dlg->x11->console, "buffer: '%s'\n", buf);
393 dlg->x11->Flush(dlg->x11);
394 #endif
395 if (gmx_strcasecmp(buf, "nok") == 0)
397 /* An error occurred */
398 if (lines)
400 for (i = 0; (i < nlines); i++)
402 sfree(lines[i]);
404 sfree(lines);
406 NoHelp(dlg);
407 return;
409 else
411 bCont = (gmx_strcasecmp(buf, "ok") != 0);
412 if (bCont)
414 srenew(lines, ++nlines);
415 lines[nlines - 1] = gmx_strdup(buf);
418 } while (bCont);
419 MessageBox(dlg->x11, dlg->wDad, "Help", nlines, lines,
420 MB_OK | MB_ICONINFORMATION | MB_APPLMODAL, nullptr, nullptr);
421 for (i = 0; (i < nlines); i++)
423 sfree(lines[i]);
425 sfree(lines);
428 static void EnterDlg(t_dlg* dlg)
430 if (dlg->flags & DLG_APPLMODAL)
432 dlg->bGrab = GrabOK(dlg->x11->console,
433 XGrabPointer(dlg->x11->disp, dlg->win.self, True, 0, GrabModeAsync,
434 GrabModeAsync, dlg->win.self, None, CurrentTime));
436 dlg->x11->Flush(dlg->x11);
439 static void ExitDlg(t_dlg* dlg)
441 if (dlg->bGrab)
443 XUngrabPointer(dlg->x11->disp, CurrentTime);
444 dlg->bGrab = false;
446 HideDlg(dlg);
447 if (dlg->flags & DLG_FREEONBUTTON)
449 FreeDlg(dlg);
453 static bool DlgCB(t_x11* x11, XEvent* event, Window w, void* data)
455 t_dlg* dlg = (t_dlg*)data;
456 int i, nWndProc;
457 t_dlgitem* dlgitem;
459 if ((dlgitem = FindWin(dlg, w)) != nullptr)
461 nWndProc = (dlgitem->WndProc)(x11, dlgitem, event);
462 #ifdef DEBUG
463 std::fprintf(x11->console, "window: %s, nWndProc: %d\n", dlgitem->win.text, nWndProc);
464 x11->Flush(x11);
465 #endif
466 switch (nWndProc)
468 case ENTERPRESSED:
469 if ((dlgitem->type == edlgBN) && (dlgitem->u.button.bDefault))
471 if (dlg->cb)
473 dlg->cb(x11, DLG_EXIT, dlgitem->ID, dlgitem->win.text, dlg->data);
475 else
477 ExitDlg(dlg);
480 else
482 for (i = 0; (i < dlg->nitem); i++)
484 if ((dlg->dlgitem[i]->type == edlgBN) && (dlg->dlgitem[i]->u.button.bDefault))
486 PushMouse(x11->disp, dlg->dlgitem[i]->win.self,
487 dlg->dlgitem[i]->win.width / 2, dlg->dlgitem[i]->win.height / 2);
488 break;
492 break;
493 case BNPRESSED:
494 if (dlg->cb)
496 dlg->cb(x11, DLG_EXIT, dlgitem->ID, dlgitem->win.text, dlg->data);
498 else
500 ExitDlg(dlg);
502 break;
503 case RBPRESSED:
505 int gid = dlgitem->GroupID;
506 t_id tid = RBSelected(dlg, gid);
507 #ifdef DEBUG
508 std::fprintf(stderr, "RBPRESSED\n");
509 #endif
510 if (tid != -1)
512 t_dlgitem* dit = FindItem(dlg, tid);
513 dit->u.radiobutton.bSelect = false;
514 ExposeWin(x11->disp, dit->win.self);
516 else
518 gmx_fatal(FARGS, "No RB Selected initially!\n");
520 dlgitem->u.radiobutton.bSelect = true;
521 ExposeWin(x11->disp, dlgitem->win.self);
522 if (dlg->cb)
524 dlg->cb(x11, DLG_SET, dlgitem->ID, dlgitem->win.text, dlg->data);
526 break;
528 case CBPRESSED:
529 ExposeWin(x11->disp, dlgitem->win.self);
530 if (dlg->cb)
532 dlg->cb(x11, DLG_SET, dlgitem->ID, dlgitem->set, dlg->data);
534 break;
535 case ETCHANGED:
536 ExposeWin(x11->disp, dlgitem->win.self);
537 if (dlg->cb)
539 dlg->cb(x11, DLG_SET, dlgitem->ID, dlgitem->u.edittext.buf, dlg->data);
541 break;
542 case HELPPRESSED: HelpNow(dlg, dlgitem); break;
543 case ITEMOK: break;
544 default: gmx_fatal(FARGS, "Invalid return code (%d) from wndproc\n", nWndProc);
547 else if (w == dlg->win.self)
549 switch (event->type)
551 case Expose: EnterDlg(dlg); break;
552 case ButtonPress:
553 case KeyPress:
554 if (HelpPressed(event))
556 HelpDlg(dlg);
558 else
560 XBell(x11->disp, 50);
562 break;
563 default: break;
566 return false;
569 /*****************************
571 * Routine to add an item to the dialog box
572 * The pointer to the item is copied to the dlg struct,
573 * the item itself may not be freed until the dlg is done with
575 ****************************/
576 static void DoCreateDlg(t_dlg* dlg)
578 XSizeHints hints;
579 XSetWindowAttributes attr;
580 unsigned long Val;
582 attr.border_pixel = dlg->x11->fg;
583 attr.background_pixel = dlg->bg;
584 attr.override_redirect = False;
585 attr.save_under = True;
586 attr.cursor = XCreateFontCursor(dlg->x11->disp, XC_hand2);
587 Val = CWBackPixel | CWBorderPixel | CWOverrideRedirect | CWSaveUnder | CWCursor;
588 dlg->win.self = XCreateWindow(dlg->x11->disp, dlg->wDad, dlg->win.x, dlg->win.y, dlg->win.width,
589 dlg->win.height, dlg->win.bwidth, CopyFromParent, InputOutput,
590 CopyFromParent, Val, &attr);
591 dlg->x11->RegisterCallback(dlg->x11, dlg->win.self, dlg->wDad, DlgCB, dlg);
592 dlg->x11->SetInputMask(dlg->x11, dlg->win.self, ExposureMask | ButtonPressMask | KeyPressMask);
594 if (!CheckWindow(dlg->win.self))
596 std::exit(1);
598 hints.x = dlg->win.x;
599 hints.y = dlg->win.y;
600 hints.flags = PPosition;
601 XSetStandardProperties(dlg->x11->disp, dlg->win.self, dlg->title, dlg->title, None, nullptr, 0, &hints);
604 void AddDlgItem(t_dlg* dlg, t_dlgitem* item)
606 #define EnterLeaveMask (EnterWindowMask | LeaveWindowMask)
607 #define UserMask (ButtonPressMask | KeyPressMask)
608 static unsigned long InputMask[edlgNR] = {
609 ExposureMask | UserMask | EnterLeaveMask, /* edlgBN */
610 ExposureMask | UserMask | EnterLeaveMask, /* edlgRB */
611 ExposureMask, /* edlgGB */
612 ExposureMask | UserMask | EnterLeaveMask, /* edlgCB */
613 0, /* edlgPM */
614 ExposureMask, /* edlgST */
615 ExposureMask | UserMask | EnterLeaveMask /* edlgET */
618 if (!dlg->win.self)
620 DoCreateDlg(dlg);
622 srenew(dlg->dlgitem, dlg->nitem + 1);
623 if (!item)
625 gmx_fatal(FARGS, "dlgitem not allocated");
627 item->win.self = XCreateSimpleWindow(dlg->x11->disp, dlg->win.self, item->win.x, item->win.y,
628 item->win.width, item->win.height, item->win.bwidth,
629 dlg->x11->fg, dlg->x11->bg);
630 CheckWindow(item->win.self);
632 dlg->x11->RegisterCallback(dlg->x11, item->win.self, dlg->win.self, DlgCB, dlg);
633 dlg->x11->SetInputMask(dlg->x11, item->win.self, InputMask[item->type]);
635 switch (item->type)
637 case edlgPM:
638 XSetWindowBackgroundPixmap(dlg->x11->disp, item->win.self, item->u.pixmap.pm);
639 break;
640 default: break;
642 dlg->dlgitem[dlg->nitem] = item;
644 dlg->nitem++;
647 void AddDlgItems(t_dlg* dlg, int nitem, t_dlgitem* item[])
649 int i;
651 for (i = 0; (i < nitem); i++)
653 #ifdef DEBUG
654 std::fprintf(dlg->x11->console, "Adding item: %d from group %d\n", item[i]->ID, item[i]->GroupID);
655 dlg->x11->Flush(dlg->x11);
656 #endif
657 AddDlgItem(dlg, item[i]);
661 void FreeDlgItem(t_dlg* dlg, t_id id)
663 t_dlgitem* dlgitem;
664 int i;
666 if ((dlgitem = FindItem(dlg, id)) != nullptr)
668 dlg->x11->UnRegisterCallback(dlg->x11, dlgitem->win.self);
669 if (dlgitem->win.self)
671 XDestroyWindow(dlg->x11->disp, dlgitem->win.self);
673 FreeWin(dlg->x11->disp, &(dlgitem->win));
674 switch (dlgitem->type)
676 case edlgBN:
677 case edlgRB: break;
678 case edlgGB: sfree(dlgitem->u.groupbox.item); break;
679 case edlgCB: break;
680 case edlgPM: XFreePixmap(dlg->x11->disp, dlgitem->u.pixmap.pm); break;
681 case edlgST:
682 for (i = 0; (i < dlgitem->u.statictext.nlines); i++)
684 sfree(dlgitem->u.statictext.lines[i]);
686 sfree(dlgitem->u.statictext.lines);
687 break;
688 case edlgET: sfree(dlgitem->u.edittext.buf); break;
689 default: break;
694 void FreeDlg(t_dlg* dlg)
696 int i;
698 if (dlg->dlgitem)
700 HideDlg(dlg);
701 dlg->x11->UnRegisterCallback(dlg->x11, dlg->win.self);
702 for (i = 0; (i < dlg->nitem); i++)
704 FreeDlgItem(dlg, dlg->dlgitem[i]->ID);
705 if (dlg->dlgitem[i])
707 sfree(dlg->dlgitem[i]);
710 sfree(dlg->dlgitem);
711 if (dlg->win.self)
713 XDestroyWindow(dlg->x11->disp, dlg->win.self);
715 dlg->dlgitem = nullptr;
719 /*****************************
721 * Routine to create the DLG structure, returns NULL on failure
723 ****************************/
724 t_dlg* CreateDlg(t_x11* x11, Window Parent, const char* title, int x0, int y0, int w, int h, int bw, DlgCallback* cb, void* data)
726 t_dlg* dlg;
727 int x = 0, y = 0;
729 snew(dlg, 1);
730 dlg->x11 = x11;
731 dlg->cb = cb;
732 dlg->data = data;
733 if (title)
735 dlg->title = gmx_strdup(title);
737 else
739 dlg->title = nullptr;
741 if (w == 0)
743 w = 1;
745 if (h == 0)
747 h = 1;
749 if (!Parent)
751 Parent = x11->root;
752 dlg->xmax = DisplayWidth(x11->disp, x11->screen);
753 dlg->ymax = DisplayHeight(x11->disp, x11->screen);
755 else
757 Window root;
758 unsigned int dum;
760 XGetGeometry(x11->disp, Parent, &root, &x, &y, &(dlg->xmax), &(dlg->ymax), &dum, &dum);
761 #ifdef DEBUG
762 std::fprintf(x11->console, "Daddy is %d x %d at %d, %d\n", dlg->xmax, dlg->ymax, x, y);
763 dlg->x11->Flush(dlg->x11);
764 #endif
766 if (x0)
768 x = x0;
770 if (y0)
772 y = y0;
774 InitWin(&(dlg->win), x, y, w, h, bw, nullptr);
775 SetDlgSize(dlg, w, h, x0 || y0);
777 dlg->wDad = Parent;
778 dlg->fg = x11->fg;
779 dlg->bg = x11->bg;
780 dlg->nitem = 0;
781 dlg->dlgitem = nullptr;
783 DoCreateDlg(dlg);
784 return dlg;
787 void SetDlgSize(t_dlg* dlg, int w, int h, bool bAutoPosition)
789 if (bAutoPosition)
791 int x, y;
793 x = (dlg->xmax - w) / 2;
794 y = (dlg->ymax - h) / 2;
795 dlg->win.x = x;
796 dlg->win.y = y;
798 dlg->win.width = w;
799 dlg->win.height = h;
801 #ifdef DEBUG
802 std::fprintf(dlg->x11->console, "SetDlgSize: Dialog is %dx%d, at %d,%d\n", dlg->win.width,
803 dlg->win.height, dlg->win.x, dlg->win.y);
804 dlg->x11->Flush(dlg->x11);
805 #endif
806 if (dlg->win.self)
808 XMoveWindow(dlg->x11->disp, dlg->win.self, dlg->win.x, dlg->win.y);
809 XResizeWindow(dlg->x11->disp, dlg->win.self, w, h);