\chapter{Dialoge}

Dialoge sind Fenster, die als Kindfenster Kontrollelemente enthalten,
"uber die sie mit dem Anwender kommunizieren. Das Dialogfenster stellt
dabei eine Infrastruktur zur Verf"ugung, die die korrekte Funktion
der Kontrollelemente im Kontext des Dialogs erm"oglicht.

In der libCUI gibt es aus technischer Sicht keinen Unterschied
zwischen Fenstern und Dialogen, da das Standardverhalten eines
libCUI-Fenster das eines Dialogs ist. Der folgende Abschnitt geht
auf ein paar Besonderheiten ein, die es trotzdem zu beachten gilt.


\section{Modale und nichtmodale Dialoge}

Ein Dialog sollte grunds"atzlich ein Popup-Fenster sein. F"ur Popup- Fenster
gilt dabei generell, dass es f"ur sie zwei Ausf"uhrungsmodi gibt: Modal
und nichtmodal.

Ein nichtmodales Popup-Fenster "uberlagert das Anwendungsfenster und wird
immer "uber diesem angeordnet. Allerdings kann nach wie vor auch das
Hauptfenster (bzw. seine Kindfenster) Windows-Nachrichten empfangen
und den Eingabefokus erhalten. Das Popupfenster unterbindet also nicht
den Zugriff auf den Teil der Anwendung der darunter liegt. Diese Form
der Popup-Fenster macht nur in den seltensten F"allen wirklich Sinn.

Ein modales Popup-Fenster liegt ebenfalls immer "uber dem Anwendungsfenster,
sperrt aber zudem den Zugriff darauf, solange bis das Popup-Fenster
geschlossen wird. Dialogfenster sind in der Regel immer modal.

Beispiel f"ur ein nichtmodales Popup-Fenster:

\begin{verbatim}
   dlg = MyWindowNew(win, 10, 2, 40, 10, CWS_POPUP, CWS_NONE);
   WindowCreate(dlg);
   WindowSetFocus(dlg);
   ...
\end{verbatim}

Beispiel f"ur ein modales Popup-Fenster:

\begin{verbatim}
   dlg = LoginDlgNew(win, show_server, CWS_NONE, CWS_NONE);
   if (dlg)
   {
      WindowCreate(dlg);
      if (WindowModal(dlg) == IDOK)
      {
         /* process dialog data */
      }
      WindowDestroy(dlg);
   }
\end{verbatim}

Der entscheidende Unterschied im zweiten Beispiel ist der API Aufruf ''WindowModal''
"uber den das Fenster ''dlg'' in einer modalen Schleife ausgef"uhrt wird. Der
Programmablauf wird scheinbar beim Funktionsaufruf von ''WindowModal'' angehalten
bis der Dialog vom Anwender geschlossen wurde. Der R"uckgabewert der Funktion
ist der Result-Code, der der Funktion ''WindowClose'' innerhalb des Dialogs
"ubergeben wurde.

Obwohl es f"ur den R"uckgabewert eines Dialogs keine funktionale Festlegung gibt,
sollte f"ur Standardf"alle eine der von der libCUI definierten Konstanten verwendet
werden:

\begin{verbatim}
   #define IDOK              0x00000001
   #define IDCANCEL          0x00000002
   #define IDYES             0x00000003
   #define IDNO              0x00000004
   #define IDRETRY           0x00000005
\end{verbatim}

Im Folgenden wird immer von modalen Dialogen ausgegangen, wenn von Dialogen
die Rede ist.


\section{Dialogdaten}

Dialoge sind in der Regel so aufgebaut, dass ihre Datenfelder vom aufrufenden
Programmteil initialisiert werden und nach dem Schlie"sen des Dialogs dort
auch wieder ausgewertet werden. Eine m"ogliche Vorgehensweise dazu ist, die
Instanzdaten des Dialogfensters von "uber einen Zeiger sichtbar zu machen.
Oder die Daten "uber eine Austauschstruktur in den Dialog zu kopieren.
Auch der Zugriff auf einzelne Datenfelder "uber separate Zugriffsfunktionen
ist denkbar, jedoch recht aufw"andig. Die gew"ahlte Vorgehensweise ist Geschmacksache
und bleibt dem Programmierer "uberlassen.

Hier als Beipiel ein Auszug aus der Header-Datei eines Login-Dialogs:

\begin{verbatim}
   typedef struct
   {
      char Host[128 + 1];
      char Port[32 + 1];
      char Username[64 + 1];
      char Password[64 + 1];
      int  ShowServer;
   } LOGINDLGDATA;

   CUIWINDOW* LoginDlgNew(CUIWINDOW* parent,
                          int show_server,
                          int sflags, int cflags);
   LOGINDLGDATA* LoginDlgGetData(CUIWINDOW* win);
\end{verbatim}

In der Funktion ''LoginDlgNew'' wird dabei eine Instanz des Datentyps
''LOGINDLGDATA'' angelegt und dem ''InstData'' Zeiger der Fensterstruktur
zugewiesen. Die Funktion ''LoginDlgGetData'' erlaubt den typsicheren
Zugriff auf das Datenelement.

\begin{verbatim}
    dlg = LoginDlgNew(win, show_server, CWS_NONE, CWS_NONE);
    if (dlg)
    {
       LOGINDLGDATA* dlgdata = LoginDlgGetData(dlg);
       if (dlgdata)
       {
          strcpy(dlgdata->Username, "postgres");
          strcpy(dlgdata->Password, "");

          WindowCreate(dlg);
          if (WindowModal(dlg) == IDOK)
          {
             Login(dlgdata->Username, dlgdata->Password);
          }
       }
       WindowDestroy(dlg);
    }
\end{verbatim}

Hingewiesen werden sollte noch auf die Tatsache, dass der Aufruf von ''WindowCreate''
erst erfolgt, nachdem die Datenfelder initialisiert wurden, da hier im CreateHook
die Kontrollelemente angelegt und mit den Werten aus den Dialogdaten gef"uttert
werden. Sollte nach dem Erzeugen des Dialogs ein Datentransfer in die Kontrollelemente
stattfinden, dann muss der Dialog eine geeignete Update-Funktion bieten.


\section{Dialogfenster anlegen}

Wie bereits bei den Kontrollelementen gezeigt, k"onnen alle zur Anlage und
zur Spezialisierung erforderlichen Schritte auch bei Dialogen in einer gemeinsamen
Funktion zusammengelegt werden, so dass das resultierende Fenster nurnoch
erzeugt werden muss, bevor es an ''WindowModal'' "ubergeben werden kann. Welche
Schritte dabei i.d.R. unternommen werden, zeigt das folgende Beispiel:

\begin{verbatim}
CUIWINDOW*
LoginDlgNew(CUIWINDOW* parent, int show_server,
            int sflags, int cflags)
{
   if (parent)
   {
      CUIWINDOW* dlg;
      int flags = sflags | CWS_POPUP | CWS_BORDER | CWS_CENTERED;
      flags &= ~(cflags);

      if (!show_server)
      {
         dlg = WindowNew(parent, 0, 0, 40, 9, flags);
      }
      else
      {
         dlg = WindowNew(parent, 0, 0, 40, 13, flags);
      }
      dlg->Class = "LOGIN_DLG";

      WindowColScheme(dlg, "DIALOG");
      WindowSetCreateHook(dlg, LoginDlgCreateHook);
      WindowSetDestroyHook(dlg, LoginDlgDestroyHook);

      dlg->InstData = (LOGINDLGDATA*)
          malloc(sizeof(LOGINDLGDATA));
      ((LOGINDLGDATA*)dlg->InstData)->Username[0] = 0;
      ((LOGINDLGDATA*)dlg->InstData)->Password[0] = 0;
      ((LOGINDLGDATA*)dlg->InstData)->Host[0] = 0;
      ((LOGINDLGDATA*)dlg->InstData)->Port[0] = 0;
      ((LOGINDLGDATA*)dlg->InstData)->ShowServer =
          show_server;

      WindowSetText(dlg, "Login");
      return dlg;
   }
   return NULL;
}
\end{verbatim}

Zun"achst wird ein normales libCUI Popup-Fenster angelegt, das es,
abh"angig vom Parameter 'show\_server' in zwei unterschiedlichen Varianten
geben kann. Zudem wird dem neuen Fenster der Klassenname ''LOGIN\_DLG''
zugewiesen.

Anschlie"send wird das Farbschema f"ur Dialoge zugeordnet und zwei Hook-
Funktionen angemeldet - auf die sp"ater noch eingegangen wird. Dann erfolgt
die Instanzierung und Initialisierung des Datentyps ''LOGINDLGDATA'' sowie
die Zuweisung an den Instanzdatenzeiger des Dialogfensters.

Zuletzt wird dem Dialogfenster der Fenstertext ''Login'' zugewiesen, der
in der Titlezeile des Fensters angezeigt werden wird.


\section{Kontrollelemente verwalten}

Wie aus dem vorigen Abschnitt ersichtlich wird, werden die Kontrollelemente
nicht schon bei der Anlage des Dialogs erzeugt. Vielmehr wird dazu der
CreateHook verwendet, der im obigen Beispiel unter dem Namen ''LoginDlgCreateHook''
rangiert. Eine Implementierung kann dabei wie folgt aussehen:

\begin{verbatim}
   static void
   LoginDlgCreateHook(void* w)
   {
      CUIWINDOW* win = (CUIWINDOW*) w;
      CUIWINDOW* ctrl;
      LOGINDLGDATA* data = (LOGINDLGDATA*) win->InstData;

      ctrl = LabelNew(win, "User name:",
                      2, 1, 12, 1, 0,
                      CWS_NONE, CWS_NONE);
      WindowCreate(ctrl);

      ctrl = LabelNew(win, "Password:",
                      2, 3, 12, 1, 0,
                      CWS_NONE, CWS_NONE);
      WindowCreate(ctrl);

      ctrl = EditNew(win, data->Username,
                      14, 1, 20, 1, 64,
                      IDC_EDUSERNAME,
                      CWS_NONE, CWS_NONE);
      WindowCreate(ctrl);

      ctrl = EditNew(win, data->Password,
                      14, 3, 20, 1, 64,
                      IDC_EDPASSWD,
                      EF_PASSWORD, CWS_NONE);
      WindowSetFocus(ctrl);
      WindowCreate(ctrl);

      ctrl = ButtonNew(win, "&OK",
                      7, 5, 10, 1,
                      IDOK,
                      CWS_DEFOK, CWS_NONE);
      ButtonSetClickedHook(ctrl, LoginDlgButtonHook, win);
      WindowCreate(ctrl);

      ctrl = ButtonNew(win, "&Cancel",
                      19, 5, 10, 1,
                      IDCANCEL,
                      CWS_DEFCANCEL, CWS_NONE);
      ButtonSetClickedHook(ctrl, LoginDlgButtonHook, win);
      WindowCreate(ctrl);
   }
\end{verbatim}

Hingewiesen sei darauf, dass die Konstanten ''IDC\_EDUSERNAME'' etc. innerhalb
des Dialogmoduls z.B. per define angelegt sein m"ussen. Zudem ist erw"ahnenswert,
dass der Dialog sich keine Zeiger auf die Kontrollelemente ''merkt''. Er kann
sp"ater jederzeit "uber die ID des Kontrollelements auf dieses zugreifen:

\begin{verbatim}
   ctrl = WindowGetCtrl(win, IDC_EDUSERNAME);
   if (ctrl)
   {
      EditGetText(ctrl, data->Username, 64);
   }
\end{verbatim}

Da die Kontrollelemente Kindfenster des Dialogs sind, m"ussen sie nicht
explizit nach dem Schlie"sen des Dialogs zerst"ort werden. Dies geschieht
automatisch dann, wenn das Dialogfenster selbst entfernt wird. Daher
reduziert sich die Implementierung des DestroyHooks im vorliegenden Fall
auf das Freigeben der Instanzdaten des Dialogs:

\begin{verbatim}
   static void
   LoginDlgDestroyHook(void* w)
   {
      free(((CUIWINDOW*) w)->InstData);
   }
\end{verbatim}

\section{Dialoge schlie"sen}

Ein Dialog wird "uber die Funktion ''WindowClose'' geschlossen, wobei an
an diese Funktion der Result-Code "ubergeben wird, der sp"ater der
R"uckgabewert der Funktion ''WindowModal'' sein wird. "Ublicherweise wird
der Aufruf von ''WindowClose'' aus einem Callback-Hook eines Button-
Kontrollelements heraus aufgerufen:

\begin{verbatim}
static void
LoginDlgButtonHook(void* w, void* c)
{
   CUIWINDOW* win = (CUIWINDOW*) w;
   CUIWINDOW* ctrl = (CUIWINDOW*) c;
   LOGINDLGDATA* data = (LOGINDLGDATA*) win->InstData;

   if (ctrl->Id == IDOK)
   {
      /* update data structures */
      WindowClose(win, IDOK);
   }
   else
   {
      WindowClose(win, IDCANCEL);
   }
}
\end{verbatim}

Zu erw"ahnen ist dabei, dass ''WindowClose'' den Aufruf der Hook-Funktion
''CanClose'' bewirkt. Nur wenn das Dialogfenster und alle seine Kindfenster
(falls sie diesen Hook "uberhaupt implementieren) einen R"uckgabewert von
''TRUE' zur"uckgeben, wird das Dialogfenster auch tats"achlich geschlossen.

Bevor ein Dialog mit ''IDOK'' (oder einem anderen Wert der einen Erfolg
signalisiert) beendet wird, m"ussen die Kontrollelemente ausgelesen und
in die Instanzdaten des Fensters "ubertragen werden. Dabei erfolgt auch
eine logische Pr"ufung der Daten in den Eingabefeldern. Notfalls kann man
den Aufruf von ''WindowClose'' vermeiden, falls die Anwendereingaben
unzufriedenstellend sind. Der Ort an dem eine solche Bearbeitung stattfinden
sollte ist oben mit ''update data structures'' gekennzeichnet.


\section{Vordefinierte Dialoge der libCUI}

Damit h"aufig verwendete Dialoge, wie z.B. der Dialog zum "Offnen und
Speichern von Dateien, nicht immer wieder neu implementiert werden m"ussen,
wird eine Liste von Standarddialogen in der libCUI hinterlegt. Bislang
ist die Auswahl noch recht bescheiden, wird aber in naher Zukunft wachsen.

Die bereits implementierten Dialoge werden hier kurz vorgestellt:

\subsection{MessageBox}

Die MessageBox wird verwendet um dem Anwender kurze Mitteilungen oder
Fehlermeldungen anzuzeigen oder eine Auskunft von ihm zu verlangen, die
sich mit ''Yes'', ''No'' oder mit ''Retry'', ''Cancel'' beantworten l"asst.

Entgegen der "ublichen Praxis wird die MessageBox nicht separat angelegt,
erzeugt und anschlie"send modal ausgef"uhrt. Vielmehr reicht ein einziger
Funktionsaufruf aus, um den Dialog anzuzeigen und den Ergebniswert
zur"uckzuliefern:

\begin{verbatim}
   int MessageBox(CUIWINDOW* parent,
                  const char* text,
                  const char* title,
                  int flags);
\end{verbatim}

Als Vaterfenster kann ein beliebieges Fenster aus dem aktuellen Kontext
angegeben werden, der Parameter ''text'' erh"alt die Textmeldung, im Parameter
''title'' wird der Fenstertitle "ubergeben und der Parameter ''flags'' steuert
das Erscheinungsbild der MessageBox. Dabei k"onnen die folgenden Konstanten
verwendet und mit dem Oder- Operator verkn"upft werden:

\begin{tabularx}{\textwidth}[t]{l|X}
\bf{Flag}& \bf{Bedeutung}\\
\hline
\texttt{ MB\_NORMAL}     &   MessageBox mit grauem Hintergrund (Standard) \\
\texttt{ MB\_INFO}       &   MessageBox mit einem cyanfarbigen Hintergrund (Info-Modus) \\
\texttt{ MB\_ERROR}      &   MessageBox mit einem roten Hintergrund (Fehler-Modus) \\
\texttt{ MB\_OK}         &   Nur eine Schaltfl"ache mit ''Ok' Beschriftung (Standard).
                         Der R"uckgabewert des Dialogs ist immer IDOK.\\
\texttt{ MB\_OKCANCEL}   &   Zwei Schaltfl"achen beschriftet mit ''Ok'' und ''Cancel''.
                         Der R"uckgabewert des Dialogs ist entweder IDOK oder IDCANCEL.\\
\texttt{ MB\_YESNO}      &   Zwei Schaltfl"achen beschriftet mit ''Yes'' und ''No''.
                         Der R"uckgabewert des Dialogs ist entwerder IDYES oder IDNO. \\
\texttt{ MB\_YESNOCANCEL} &  Drei Schalfl"achen beschriftet mit ''Yes'', ''No'' und ''Cancel''.
                         Der R"uckgabewert ist entwerder IDYES, IDNO oder IDCANCEL. \\
\texttt{ MB\_RETRYCANCEL} &  Zwei Schaltfl"achen beschriftet mit ''Retry'' und ''Cancel''.
                         Der R"uckgabewert ist entwerder IDRETRY oder IDCANCEL. \\
\texttt{ MB\_DEFBUTTON1} &   noch nicht implementiert. \\
\texttt{ MB\_DEFBUTTON2} &   noch nicht implementiert. \\
\end{tabularx}

~\\
Beispiele f"ur Message Boxen:

\begin{verbatim}
   MessageBox(win, "File saved successfully!", "Message", MB_OK);

   MessageBox(win, "File not found!", "Error", MB_ERROR);

   MessageBox(win, "I\nam\na\nmultiline\nMessage!",
              "Message", MB_OK);

   if (MessageBox(win, "Really exit?",
                  "Question", MB_YESNO) == IDYES)
   {
      WindowClose(win, IDOK);
   }
\end{verbatim}
