/* dialog.c - draws various useful dialog boxes Copyright (C) 1996-2000 Paul Sheer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #ifdef HAVE_UNISTD_H #include #endif #include #include "lkeysym.h" #include "stringtools.h" #include "app_glob.c" #include "coolwidget.h" #include "coollocal.h" #include "loadfile.h" #include "mad.h" #include "edit.h" #include "editcmddef.h" #define MID_X 20 #define MID_Y 20 extern struct look *look; /* Yuriy Elkin: (from mc/src/util.c) */ char *get_sys_error (const char *s) { char *error_msg; if (errno) { #ifdef HAVE_STRERROR error_msg = _ (strerror (errno)); #else extern int sys_nerr; extern char *sys_errlist[]; if ((0 <= errno) && (errno < sys_nerr)) error_msg = _ (sys_errlist[errno]); else /* The returned value, 'errno' has an unknown meaning */ error_msg = _ ("strange errno"); #endif return catstrs (s, "\n [", error_msg, "] ", NULL); } return (char *) s; } /* error messages displayed before the main window is mapped must be displayed on the root window to be seen */ Window find_mapped_window (Window w) { CWidget *wdt; if (w == CRoot) return CRoot; if (!w) w = CFirstWindow; if ((wdt = CWidgetOfWindow (w))) if (!wdt->mapped) return CRoot; return w; } void CErrorDialog (Window in, int x, int y, const char *heading, const char *fmt,...) { static int inside = 0; va_list pa; char *str; Window win; CEvent cwevent; CState s; if (inside) return; inside = 1; CPushFont ("widget", 0); va_start (pa, fmt); str = vsprintf_alloc (fmt, pa); va_end (pa); if (!in) { x = MID_X; y = MID_Y; } in = find_mapped_window (in); CBackupState (&s); CDisable ("*"); win = CDrawHeadedDialog ("_error", in, x, y, heading); CGetHintPos (&x, &y); (CDrawText ("", win, x, y, "%s", str))->position = POSITION_CENTRE; free (str); CGetHintPos (0, &y); ((*look->draw_exclam_cancel_button) ("_clickhere", win, -50, y))->position = POSITION_CENTRE; CIdent ("_error")->position = WINDOW_UNMOVEABLE | WINDOW_ALWAYS_RAISED; CSetSizeHintPos ("_error"); CMapDialog ("_error"); CFocus (CIdent ("_clickhere")); do { CNextEvent (NULL, &cwevent); if (!CIdent ("_error")) break; } while (strcmp (cwevent.ident, "_clickhere") && cwevent.command != CK_Cancel); CPopFont (); CDestroyWidget ("_error"); CRestoreState (&s); inside = 0; } void CMessageDialog (Window in, int x, int y, unsigned long options, const char *heading, const char *fmt,...) { va_list pa; char *str; Window win; CEvent cwevent; CState s; CPushFont ("widget", 0); va_start (pa, fmt); str = vsprintf_alloc (fmt, pa); va_end (pa); in = find_mapped_window (in); CBackupState (&s); CDisable ("*"); win = CDrawHeadedDialog ("_error", in, x, y, heading); CGetHintPos (&x, &y); (CDrawText ("", win, x, y, "%s", str))->options = options; free (str); CGetHintPos (0, &y); ((*look->draw_tick_cancel_button) ("_clickhere", win, -50, y))->position = POSITION_CENTRE; CCentre ("_clickhere"); CIdent ("_error")->position = WINDOW_UNMOVEABLE | WINDOW_ALWAYS_RAISED; CSetSizeHintPos ("_error"); CMapDialog ("_error"); CFocus (CIdent ("_clickhere")); do { CNextEvent (NULL, &cwevent); if (!CIdent ("_error")) break; } while (strcmp (cwevent.ident, "_clickhere") && cwevent.command != CK_Cancel && cwevent.command != CK_Enter); CPopFont (); CDestroyWidget ("_error"); CRestoreState (&s); } /* draws a scrollable text box with a button to clear. Can be used to give long help messages */ void CTextboxMessageDialog (Window in, int x, int y, int columns, int lines, const char *heading, const char *text, int line) { Window win; CEvent cwevent; CState s; int width, height; CPushFont ("editor", 0); CTextSize (&width, &height, text); width = min (columns * FONT_MEAN_WIDTH, width) + 1 + 6; height = min (lines * FONT_PIX_PER_LINE, height) + 1 + 6; CPopFont (); if (!in) { x = MID_X; y = MID_Y; } in = find_mapped_window (in); CBackupState (&s); CDisable ("*"); win = CDrawHeadedDialog ("_error", in, x, y, heading); CGetHintPos (&x, &y); CDrawTextbox ("_textmessbox", win, x, y, width, height, line, 0, text, 0); CGetHintPos (0, &y); ((*look->draw_tick_cancel_button) ("_clickhere", win, -50, y))->position = POSITION_CENTRE; CCentre ("_clickhere"); CIdent ("_error")->position = WINDOW_UNMOVEABLE | WINDOW_ALWAYS_RAISED; CSetSizeHintPos ("_error"); CMapDialog ("_error"); CFocus (CIdent ("_clickhere")); do { CNextEvent (NULL, &cwevent); if (!CIdent ("_error")) break; } while (strcmp (cwevent.ident, "_clickhere") && cwevent.command != CK_Cancel && cwevent.command != CK_Enter); CDestroyWidget ("_error"); CRestoreState (&s); } /* Draws a scrollable text box with a button to clear. the text box contains lines of text gotten from get_line(). Returns the number of the line the user double clicked on, or pressed enter or space on. get_line() must return a null terminated string without newlines. The string may exist statically within the getline() function and be overwritten with each new call to getline(). Returns -1 on cancel. With heading = 0, this behaves like trivialselection below. */ int CListboxDialog (Window in, int x, int y, int columns, int lines, const char *heading, int start_line, int cursor_line, int num_lines, char *(*get_line) (void *data, int line), void *data) { Window win; CEvent cwevent; CState s; int width, height, len, i; char *text, *p; CPushFont ("editor", 0); width = columns * FONT_MEAN_WIDTH + 1 + 6; height = lines * FONT_PIX_PER_LINE + 1 + 6; CPopFont (); if (!in) { x = MID_X; y = MID_Y; } in = find_mapped_window (in); CBackupState (&s); CDisable ("*"); len = 0; for (i = 0; i < num_lines; i++) len += strlen ((*get_line) (data, i)) + 1; p = text = CMalloc (len + 1); p[0] = '\0'; for (i = 0; i < num_lines; i++) { strcpy (p, (*get_line) (data, i)); p += strlen (p); *p++ = '\n'; } i = -1; if ((unsigned long) p > (unsigned long) text) *(--p) = 0; if (heading) win = CDrawHeadedDialog ("_error", in, x, y, heading); else win = CDrawDialog ("_error", in, x, y); CGetHintPos (&x, &y); (CDrawTextbox ("_textmessbox", win, x, y, width, height, start_line, 0, text, TEXTBOX_MAN_PAGE))->cursor = cursor_line; CGetHintPos (0, &y); if (heading) { ((*look->draw_cross_cancel_button) ("_clickhere", win, -50, y))->position = POSITION_CENTRE; CCentre ("_clickhere"); } CIdent ("_error")->position = WINDOW_ALWAYS_RAISED; CSetSizeHintPos ("_error"); CMapDialog ("_error"); CFocus (CIdent ("_textmessbox")); do { CNextEvent (NULL, &cwevent); if (heading) { if (!strcmp (cwevent.ident, "_clickhere")) break; } else { if (cwevent.key == XK_Tab || cwevent.key == XK_ISO_Left_Tab) break; } if (!strcmp (cwevent.ident, "_textmessbox")) if (cwevent.key == XK_space || cwevent.command == CK_Enter || cwevent.double_click) { i = (CIdent ("_textmessbox"))->cursor; break; } if (!CIdent ("_error")) break; } while (cwevent.command != CK_Cancel); CDestroyWidget ("_error"); CRestoreState (&s); free (text); return i; } /* Draws a scrollable text box. Returns the line you pressed enter on or double-clicked on. Result must not be free'd. Result must be copied ASAP. Returns 0 if cancelled or clicked elsewhere. Result must be less than 1024 bytes long. */ char *CTrivialSelectionDialog (Window in, int x, int y, int columns, int lines, const char *text, int line, int cursor_line) { Window win; CEvent cwevent; XEvent xevent; CState s; int width, height; char *p = 0; CWidget *w; memset (&xevent, 0, sizeof (xevent)); CPushFont ("editor", 0); width = columns * FONT_MEAN_WIDTH + 1 + 6; height = lines * FONT_PIX_PER_LINE + 1 + 6; CPopFont (); CBackupState (&s); CDisable ("*"); win = CDrawDialog ("_select", in, x, y); CGetHintPos (&x, &y); w = CDrawTextbox ("_textmessbox", win, x, y, width, height, line, 0, text, 0); w->cursor = cursor_line; CGetHintPos (0, &y); CIdent ("_select")->position = WINDOW_UNMOVEABLE | WINDOW_ALWAYS_RAISED; CSetSizeHintPos ("_select"); CMapDialog ("_select"); CFocus (CIdent ("_textmessbox")); do { CNextEvent (&xevent, &cwevent); if (xevent.xany.window == w->winid) { if (!strcmp (cwevent.ident, "_textmessbox") && (cwevent.command == CK_Enter || cwevent.double_click)) { p = CGetTextBoxLine (w, w->cursor); break; } } else if (xevent.xany.type == ButtonPress && cwevent.kind != C_VERTSCROLL_WIDGET && cwevent.kind != C_HORISCROLL_WIDGET && cwevent.kind != C_WINDOW_WIDGET) { CSendEvent (&xevent); /* resend this to get processed, here the widget is disabled */ break; } if (!CIdent ("_select")) break; } while (cwevent.command != CK_Cancel && cwevent.key != XK_KP_Tab && cwevent.key != XK_Tab); CDestroyWidget ("_select"); CRestoreState (&s); return p; } void CFatalErrorDialog (int x, int y, const char *fmt,...) { va_list pa; char *str; Window win; CEvent cwevent; CState s; Window in; va_start (pa, fmt); str = vsprintf_alloc (fmt, pa); va_end (pa); fprintf (stderr, "%s: %s\n", CAppName, str); in = find_mapped_window (0); if (CDisplay) { CBackupState (&s); CDisable ("*"); win = CDrawHeadedDialog ("fatalerror", in, x, y," Fatal Error "); CGetHintPos (&x, &y); CDrawText ("fatalerror.text", win, x, y, "%s", str); CCentre ("fatalerror.text"); CGetHintPos (0, &y); ((*look->draw_cross_cancel_button) ("clickhere", win, -50, y))->position = POSITION_CENTRE; CCentre ("clickhere"); CIdent ("fatalerror")->position = WINDOW_UNMOVEABLE | WINDOW_ALWAYS_RAISED; CSetSizeHintPos ("fatalerror"); CMapDialog ("fatalerror"); CFocus (CIdent ("clickhere")); do { CNextEvent (NULL, &cwevent); if (!CIdent ("fatalerror")) abort (); } while (strcmp (cwevent.ident, "clickhere")); } abort (); } /* returns a raw XK_key sym, or 0 on cancel */ XEvent *CRawkeyQuery (Window in, int x, int y, const char *heading, const char *fmt,...) { va_list pa; char *str; XEvent *p = 0; Window win; CEvent cwevent; static XEvent xevent; CState s; va_start (pa, fmt); str = vsprintf_alloc (fmt, pa); va_end (pa); if (!in) { x = MID_X; y = MID_Y; } in = find_mapped_window (in); CBackupState (&s); CDisable ("*"); win = CDrawHeadedDialog ("_rawkeydlg", in, x, y, heading); CGetHintPos (&x, &y); CDrawText ("_rawkeydlg.text", win, x, y, "%s", str); CGetHintPos (&x, 0); free (str); CDrawTextInput ("_rawkeydlg.input", win, x, y, (FONT_MEAN_WIDTH) * 6, AUTO_HEIGHT, 256, ""); CGetHintPos (0, &y); ((*look->draw_cross_cancel_button) ("_rawkeydlg.crosshere", win, -50, y))->position = POSITION_CENTRE; CCentre ("_rawkeydlg.crosshere"); CSetSizeHintPos ("_rawkeydlg"); CMapDialog ("_rawkeydlg"); CFocus (CIdent ("_rawkeydlg.input")); CIdent ("_rawkeydlg")->position = WINDOW_ALWAYS_RAISED; /* handler : */ do { CNextEvent (&xevent, &cwevent); if (!CIdent ("_rawkeydlg")) break; if (cwevent.command == CK_Cancel || !strcmp (cwevent.ident, "_rawkeydlg.crosshere")) break; if (xevent.type == KeyPress) { KeySym k; k = CKeySym (&xevent); if (k && !mod_type_key (k)) p = &xevent; } } while (!p); CDestroyWidget ("_rawkeydlg"); CRestoreState (&s); return p; } /* def is the default string in the textinput widget. Result must be free'd. Returns 0 on cancel. */ char *CInputDialog (const char *ident, Window in, int x, int y, int min_width, const char *def, const char *heading, const char *fmt,...) { va_list pa; char *str, *p = 0; int w, h; Window win; CEvent cwevent; CState s; char inp_name[256]; int browse = 0, xf, yf; min_width &= ~INPUT_DIALOG_BROWSE_MASK; browse = min_width & INPUT_DIALOG_BROWSE_MASK; va_start (pa, fmt); str = vsprintf_alloc (fmt, pa); va_end (pa); if (!in) { x = MID_X; y = MID_Y; } xf = x + 20; yf = y + 20; in = find_mapped_window (in); CTextSize (&w, &h, str); w = max (max (w, min_width), 130); CBackupState (&s); CDisable ("*"); win = CDrawHeadedDialog ("_inputdialog", in, x, y, heading); CGetHintPos (&x, &y); CDrawText ("", win, x, y, "%s", str); CGetHintPos (0, &y); free (str); strcpy (inp_name, ident); inp_name[20] = '\0'; strcat (inp_name, ".inpt_dlg"); CDrawTextInput (inp_name, win, x, y, w, AUTO_HEIGHT, 256, def); if (browse) { CGetHintPos (&x, 0); w += (CDrawButton ("_inputdialog.browse", win, x, y, AUTO_SIZE, _ (" Browse... ")))->width + WIDGET_SPACING; } CGetHintPos (0, &y); (*look->draw_tick_ok_button) ("_inputdialog.clickhere", win, (w + 16) / 4 - 22, y); (*look->draw_cross_cancel_button) ("_inputdialog.crosshere", win, 3 * (w + 16) / 4 - 22, y); CSetSizeHintPos ("_inputdialog"); CMapDialog ("_inputdialog"); CFocus (CIdent (inp_name)); CIdent ("_inputdialog")->position = WINDOW_ALWAYS_RAISED; /* handler : */ do { CNextEvent (NULL, &cwevent); if (cwevent.command == CK_Cancel || !strcmp (cwevent.ident, "_inputdialog.crosshere")) goto fin; if (cwevent.command == CK_Enter) break; if (!strcmp (cwevent.ident, "_inputdialog.browse")) { char *f = 0; /* dialog title */ switch (browse) { case INPUT_DIALOG_BROWSE_DIR: f = CGetDirectory (in, xf, yf, current_dir, "", _ (" Browse ")); break; case INPUT_DIALOG_BROWSE_SAVE: f = CGetSaveFile (in, xf, yf, current_dir, "", _ (" Browse ")); break; case INPUT_DIALOG_BROWSE_LOAD: f = CGetLoadFile (in, xf, yf, current_dir, "", _ (" Browse ")); break; } if (f) if (*f) { if (CIdent (inp_name)->text) free (CIdent (inp_name)->text); CIdent (inp_name)->text = f; CIdent (inp_name)->cursor = strlen (f); CExpose (inp_name); } CFocus (CIdent (inp_name)); } if (!CIdent ("_inputdialog")) goto fin; } while (strcmp (cwevent.ident, "_inputdialog.clickhere")); p = (char *) strdup (CIdent (inp_name)->text); fin: CDestroyWidget ("_inputdialog"); CRestoreState (&s); return p; } static char *id[32] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; void free_last_query_buttons (void) { int i; for (i = 0; i < 32; i++) if (id[i]) { free (id[i]); id[i] = 0; } } /* returns -1 on widget-destroyed-without-a-button-pressed or cancel (i.e. Esc) */ int CQueryDialog (Window in, int x, int y, const char *heading, const char *first,...) { va_list pa; int i, buttons = 0, r = -1; Window win; CEvent cwevent; CState s; char *b[32]; free_last_query_buttons (); va_start (pa, first); while ((b[buttons] = space_string (va_arg (pa, char *)))) buttons++; va_end (pa); if (!buttons) return -1; if (!in) { x = MID_X; y = MID_Y; } in = find_mapped_window (in); CBackupState (&s); CDisable ("*"); win = CDrawHeadedDialog ("_querydialog", in, x, y, heading); CGetHintPos (&x, &y); CDrawText ("_querydialog.text", win, x, y, "%s", first); CGetHintPos (0, &y); for (i = 0; i < buttons; i++) { CDrawButton (id[i] = sprintf_alloc ("_query.%.20s", b[i]), win, x, y, AUTO_WIDTH, AUTO_HEIGHT, b[i]); CGetHintPos (&x, 0); } CSetSizeHintPos ("_querydialog"); CMapDialog ("_querydialog"); CFocus (CIdent (catstrs ("_query.", b[0], NULL))); CIdent ("_querydialog")->position = WINDOW_ALWAYS_RAISED; for (; r < 0;) { CNextEvent (NULL, &cwevent); if (!CIdent ("_querydialog")) break; if (!cwevent.handled && cwevent.command == CK_Cancel) break; for (i = 0; i < buttons; i++) { if (!strcmp (cwevent.ident, id[i])) { r = i; break; } } } for (i = 0; i < buttons; i++) free (b[i]); CDestroyWidget ("_querydialog"); CRestoreState (&s); return r; }