| /* | 
 |  * GUI support | 
 |  * | 
 |  * Copyright 2004 Ferenc Wagner | 
 |  * | 
 |  * This library is free software; you can redistribute it and/or | 
 |  * modify it under the terms of the GNU Lesser General Public | 
 |  * License as published by the Free Software Foundation; either | 
 |  * version 2.1 of the License, or (at your option) any later version. | 
 |  * | 
 |  * This library 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 | 
 |  * Lesser General Public License for more details. | 
 |  * | 
 |  * You should have received a copy of the GNU Lesser General Public | 
 |  * License along with this library; if not, write to the Free Software | 
 |  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
 |  */ | 
 |  | 
 | #include <windows.h> | 
 | #include <commctrl.h> | 
 |  | 
 | #include "resource.h" | 
 | #include "winetest.h" | 
 |  | 
 | /* Event object to signal successful window creation to main thread. | 
 |  */ | 
 | static HANDLE initEvent; | 
 |  | 
 | /* Dialog handle | 
 |  */ | 
 | static HWND dialog; | 
 |  | 
 | /* Progress data for the text* functions and for scaling. | 
 |  */ | 
 | static unsigned int progressMax, progressCurr; | 
 | static double progressScale; | 
 |  | 
 | /* Progress group counter for the gui* functions. | 
 |  */ | 
 | static int progressGroup; | 
 |  | 
 | static WNDPROC DefEditProc; | 
 |  | 
 | static int | 
 | MBdefault (int uType) | 
 | { | 
 |     static const int matrix[][4] = {{IDOK,    0,        0,        0}, | 
 |                                     {IDOK,    IDCANCEL, 0,        0}, | 
 |                                     {IDABORT, IDRETRY,  IDIGNORE, 0}, | 
 |                                     {IDYES,   IDNO,     IDCANCEL, 0}, | 
 |                                     {IDYES,   IDNO,     0,        0}, | 
 |                                     {IDRETRY, IDCANCEL, 0,        0}}; | 
 |     int type = uType & MB_TYPEMASK; | 
 |     int def  = (uType & MB_DEFMASK) / MB_DEFBUTTON2; | 
 |  | 
 |     return matrix[type][def]; | 
 | } | 
 |  | 
 | /* report (R_STATUS, fmt, ...) */ | 
 | static int | 
 | textStatus (va_list ap) | 
 | { | 
 |     char *str = vstrmake (NULL, ap); | 
 |  | 
 |     fputs (str, stderr); | 
 |     fputc ('\n', stderr); | 
 |     heap_free (str); | 
 |     return 0; | 
 | } | 
 |  | 
 | static int | 
 | guiStatus (va_list ap) | 
 | { | 
 |     size_t len; | 
 |     char *str = vstrmake (&len, ap); | 
 |  | 
 |     if (len > 128) str[129] = 0; | 
 |     SetDlgItemText (dialog, IDC_SB, str); | 
 |     heap_free (str); | 
 |     return 0; | 
 | } | 
 |  | 
 | /* report (R_PROGRESS, barnum, steps) */ | 
 | static int | 
 | textProgress (va_list ap) | 
 | { | 
 |     progressGroup = va_arg (ap, int); | 
 |     progressMax = va_arg (ap, int); | 
 |     progressCurr = 0; | 
 |     return 0; | 
 | } | 
 |  | 
 | static int | 
 | guiProgress (va_list ap) | 
 | { | 
 |     unsigned int max; | 
 |     HWND pb; | 
 |  | 
 |     progressGroup = va_arg (ap, int); | 
 |     progressMax = max = va_arg (ap, int); | 
 |     progressCurr = 0; | 
 |     if (max > 0xffff) { | 
 |         progressScale = (double)0xffff / max; | 
 |         max = 0xffff; | 
 |     } | 
 |     else progressScale = 1; | 
 |     pb = GetDlgItem (dialog, IDC_PB0 + progressGroup * 2); | 
 |     SendMessage (pb, PBM_SETRANGE, 0, MAKELPARAM (0, max)); | 
 |     SendMessage (pb, PBM_SETSTEP, (WPARAM)1, 0); | 
 |     return 0; | 
 | } | 
 |  | 
 | /* report (R_STEP, fmt, ...) */ | 
 | static int | 
 | textStep (va_list ap) | 
 | { | 
 |     char *str = vstrmake (NULL, ap); | 
 |  | 
 |     progressCurr++; | 
 |     fputs (str, stderr); | 
 |     fprintf (stderr, " (%d of %d)\n", progressCurr, progressMax); | 
 |     heap_free (str); | 
 |     return 0; | 
 | } | 
 |  | 
 | static int | 
 | guiStep (va_list ap) | 
 | { | 
 |     const int pgID = IDC_ST0 + progressGroup * 2; | 
 |     char *str = vstrmake (NULL, ap); | 
 |      | 
 |     progressCurr++; | 
 |     SetDlgItemText (dialog, pgID, str); | 
 |     SendDlgItemMessage (dialog, pgID+1, PBM_SETPOS, | 
 |                         (WPARAM)(progressScale * progressCurr), 0); | 
 |     heap_free (str); | 
 |     return 0; | 
 | } | 
 |  | 
 | /* report (R_DELTA, inc, fmt, ...) */ | 
 | static int | 
 | textDelta (va_list ap) | 
 | { | 
 |     const int inc = va_arg (ap, int); | 
 |     char *str = vstrmake (NULL, ap); | 
 |  | 
 |     progressCurr += inc; | 
 |     fputs (str, stderr); | 
 |     fprintf (stderr, " (%d of %d)\n", progressCurr, progressMax); | 
 |     heap_free (str); | 
 |     return 0; | 
 | } | 
 |  | 
 | static int | 
 | guiDelta (va_list ap) | 
 | { | 
 |     const int inc = va_arg (ap, int); | 
 |     const int pgID = IDC_ST0 + progressGroup * 2; | 
 |     char *str = vstrmake (NULL, ap); | 
 |  | 
 |     progressCurr += inc; | 
 |     SetDlgItemText (dialog, pgID, str); | 
 |     SendDlgItemMessage (dialog, pgID+1, PBM_SETPOS, | 
 |                         (WPARAM)(progressScale * progressCurr), 0); | 
 |     heap_free (str); | 
 |     return 0; | 
 | } | 
 |  | 
 | /* report (R_TAG) */ | 
 | static int | 
 | textTag (va_list ap) | 
 | { | 
 |     fputs ("Tag: ", stderr); | 
 |     fputs (tag, stderr); | 
 |     fputc ('\n', stderr); | 
 |     return 0; | 
 | } | 
 |  | 
 | static int | 
 | guiTag (va_list ap) | 
 | { | 
 |     SetDlgItemText (dialog, IDC_TAG, tag); | 
 |     return 0; | 
 | } | 
 |  | 
 | /* report (R_DIR, fmt, ...) */ | 
 | static int | 
 | textDir (va_list ap) | 
 | { | 
 |     char *str = vstrmake (NULL, ap); | 
 |  | 
 |     fputs ("Temporary directory: ", stderr); | 
 |     fputs (str, stderr); | 
 |     fputc ('\n', stderr); | 
 |     heap_free (str); | 
 |     return 0; | 
 | } | 
 |  | 
 | static int | 
 | guiDir (va_list ap) | 
 | { | 
 |     char *str = vstrmake (NULL, ap); | 
 |  | 
 |     SetDlgItemText (dialog, IDC_DIR, str); | 
 |     heap_free (str); | 
 |     return 0; | 
 | } | 
 |  | 
 | /* report (R_OUT, fmt, ...) */ | 
 | static int | 
 | textOut (va_list ap) | 
 | { | 
 |     char *str = vstrmake (NULL, ap); | 
 |  | 
 |     fputs ("Log file: ", stderr); | 
 |     fputs (str, stderr); | 
 |     fputc ('\n', stderr); | 
 |     heap_free (str); | 
 |     return 0; | 
 | } | 
 |  | 
 | static int | 
 | guiOut (va_list ap) | 
 | { | 
 |     char *str = vstrmake (NULL, ap); | 
 |  | 
 |     SetDlgItemText (dialog, IDC_OUT, str); | 
 |     heap_free (str); | 
 |     return 0; | 
 | } | 
 |  | 
 | /* report (R_WARNING, fmt, ...) */ | 
 | static int | 
 | textWarning (va_list ap) | 
 | { | 
 |     fputs ("Warning: ", stderr); | 
 |     textStatus (ap); | 
 |     return 0; | 
 | } | 
 |  | 
 | static int | 
 | guiWarning (va_list ap) | 
 | { | 
 |     char *str = vstrmake (NULL, ap); | 
 |  | 
 |     MessageBox (dialog, str, "Warning", MB_ICONWARNING | MB_OK); | 
 |     heap_free (str); | 
 |     return 0; | 
 | } | 
 |  | 
 | /* report (R_ERROR, fmt, ...) */ | 
 | static int | 
 | textError (va_list ap) | 
 | { | 
 |     fputs ("Error: ", stderr); | 
 |     textStatus (ap); | 
 |     return 0; | 
 | } | 
 |  | 
 | static int | 
 | guiError (va_list ap) | 
 | { | 
 |     char *str = vstrmake (NULL, ap); | 
 |  | 
 |     MessageBox (dialog, str, "Error", MB_ICONERROR | MB_OK); | 
 |     heap_free (str); | 
 |     return 0; | 
 | } | 
 |  | 
 | /* report (R_FATAL, fmt, ...) */ | 
 | static int | 
 | textFatal (va_list ap) | 
 | { | 
 |     textError (ap); | 
 |     exit (1); | 
 | } | 
 |  | 
 | static int | 
 | guiFatal (va_list ap) | 
 | { | 
 |     guiError (ap); | 
 |     exit (1); | 
 | } | 
 |  | 
 | /* report (R_ASK, type, fmt, ...) */ | 
 | static int | 
 | textAsk (va_list ap) | 
 | { | 
 |     int uType = va_arg (ap, int); | 
 |     int ret = MBdefault (uType); | 
 |     char *str = vstrmake (NULL, ap); | 
 |  | 
 |     fprintf (stderr, "Question of type %d: %s\n" | 
 |              "Returning default: %d\n", uType, str, ret); | 
 |     heap_free (str); | 
 |     return ret; | 
 | } | 
 |  | 
 | static int | 
 | guiAsk (va_list ap) | 
 | { | 
 |     int uType = va_arg (ap, int); | 
 |     char *str = vstrmake (NULL, ap); | 
 |     int ret = MessageBox (dialog, str, "Question", | 
 |                           MB_ICONQUESTION | uType); | 
 |  | 
 |     heap_free (str); | 
 |     return ret; | 
 | } | 
 |  | 
 | static BOOL CALLBACK | 
 | EditTagProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | 
 | { | 
 |     switch (msg) { | 
 |     case WM_CHAR: | 
 |         if (wParam == 8) break; /* backspace is OK */ | 
 |         if (GetWindowTextLengthA (hwnd) == MAXTAGLEN || | 
 |             !goodtagchar (wParam)) return TRUE; | 
 |         break; | 
 |     } | 
 |     return CallWindowProcA (DefEditProc, hwnd, msg, wParam, lParam); | 
 | } | 
 |  | 
 | static INT_PTR CALLBACK | 
 | AskTagProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | 
 | { | 
 |     int len; | 
 |  | 
 |     switch (msg) { | 
 |     case WM_INITDIALOG: | 
 |         DefEditProc = (WNDPROC)SetWindowLongPtr | 
 |             (GetDlgItem (hwnd, IDC_TAG), GWLP_WNDPROC, (LONG_PTR)EditTagProc); | 
 |         return TRUE; | 
 |     case WM_COMMAND: | 
 |         switch (LOWORD (wParam)) { | 
 |         case IDOK: | 
 |             len = GetWindowTextLengthA (GetDlgItem (hwnd, IDC_TAG)); | 
 | 	    if(!len) { | 
 |                report (R_WARNING, "You must enter a tag to continue"); | 
 |                return FALSE; | 
 |             } | 
 |             tag = heap_alloc (len+1); | 
 |             GetDlgItemTextA (hwnd, IDC_TAG, tag, len+1); | 
 |             EndDialog (hwnd, IDOK); | 
 |             return TRUE; | 
 |         case IDABORT: | 
 |             EndDialog (hwnd, IDABORT); | 
 |             return TRUE; | 
 |         } | 
 |     } | 
 |     return FALSE; | 
 | } | 
 |  | 
 | int | 
 | guiAskTag (void) | 
 | { | 
 |     return DialogBox (GetModuleHandle (NULL), | 
 |                       MAKEINTRESOURCE (IDD_TAG), | 
 |                       dialog, AskTagProc); | 
 | } | 
 |  | 
 | /* Quiet functions */ | 
 | static int | 
 | qNoOp (va_list ap) | 
 | { | 
 |     return 0; | 
 | } | 
 |  | 
 | static int | 
 | qFatal (va_list ap) | 
 | { | 
 |     exit (1); | 
 | } | 
 |  | 
 | static int | 
 | qAsk (va_list ap) | 
 | { | 
 |     return MBdefault (va_arg (ap, int)); | 
 | } | 
 |  | 
 | static INT_PTR CALLBACK | 
 | AboutProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | 
 | { | 
 |     switch (msg) { | 
 |     case WM_COMMAND: | 
 |         switch (LOWORD (wParam)) { | 
 |         case IDCANCEL: | 
 |             EndDialog (hwnd, IDCANCEL); | 
 |             return TRUE; | 
 |         } | 
 |     } | 
 |     return FALSE; | 
 | } | 
 |  | 
 | static INT_PTR CALLBACK | 
 | DlgProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | 
 | { | 
 |     switch (msg) { | 
 |     case WM_INITDIALOG: | 
 |         SendMessage (hwnd, WM_SETICON, ICON_SMALL, | 
 |                      (LPARAM)LoadIcon (GetModuleHandle (NULL), | 
 |                                        MAKEINTRESOURCE (IDI_WINE))); | 
 |         SendMessage (hwnd, WM_SETICON, ICON_BIG, | 
 |                      (LPARAM)LoadIcon (GetModuleHandle (NULL), | 
 |                                        MAKEINTRESOURCE (IDI_WINE))); | 
 |         dialog = hwnd; | 
 |         if (!SetEvent (initEvent)) { | 
 |             report (R_STATUS, "Can't signal main thread: %d", | 
 |                     GetLastError ()); | 
 |             EndDialog (hwnd, 2); | 
 |         } | 
 |         return TRUE; | 
 |     case WM_CLOSE: | 
 |         EndDialog (hwnd, 3); | 
 |         return TRUE; | 
 |     case WM_COMMAND: | 
 |         switch (LOWORD (wParam)) { | 
 |         case IDHELP: | 
 |             DialogBox (GetModuleHandle (NULL), | 
 |                        MAKEINTRESOURCE (IDD_ABOUT), hwnd, AboutProc); | 
 |             return TRUE; | 
 |         case IDABORT: | 
 |             report (R_WARNING, "Not implemented"); | 
 |             return TRUE; | 
 |         } | 
 |     } | 
 |     return FALSE; | 
 | } | 
 |  | 
 | static DWORD WINAPI | 
 | DlgThreadProc (LPVOID param) | 
 | { | 
 |     int ret; | 
 |  | 
 |     InitCommonControls (); | 
 |     ret = DialogBox (GetModuleHandle (NULL), | 
 |                      MAKEINTRESOURCE (IDD_STATUS), | 
 |                      NULL, DlgProc); | 
 |     switch (ret) { | 
 |     case 0: | 
 |         report (R_WARNING, "Invalid parent handle"); | 
 |         break; | 
 |     case 1: | 
 |         report (R_WARNING, "DialogBox failed: %d", | 
 |                 GetLastError ()); | 
 |         break; | 
 |     case 3: | 
 |         exit (0); | 
 |     default: | 
 |         report (R_STATUS, "Dialog exited: %d", ret); | 
 |     } | 
 |     return 0; | 
 | } | 
 |  | 
 | int | 
 | report (enum report_type t, ...) | 
 | { | 
 |     typedef int r_fun_t (va_list); | 
 |  | 
 |     va_list ap; | 
 |     int ret = 0; | 
 |     static r_fun_t * const text_funcs[] = | 
 |         {textStatus, textProgress, textStep, textDelta, | 
 |          textTag, textDir, textOut, | 
 |          textWarning, textError, textFatal, textAsk}; | 
 |     static r_fun_t * const GUI_funcs[] = | 
 |         {guiStatus, guiProgress, guiStep, guiDelta, | 
 |          guiTag, guiDir, guiOut, | 
 |          guiWarning, guiError, guiFatal, guiAsk}; | 
 |     static r_fun_t * const quiet_funcs[] = | 
 |         {qNoOp, qNoOp, qNoOp, qNoOp, | 
 |          qNoOp, qNoOp, qNoOp, | 
 |          qNoOp, qNoOp, qFatal, qAsk}; | 
 |     static r_fun_t * const * funcs = NULL; | 
 |  | 
 |     switch (t) { | 
 |     case R_TEXTMODE: | 
 |         funcs = text_funcs; | 
 |         return 0; | 
 |     case R_QUIET: | 
 |         funcs = quiet_funcs; | 
 |         return 0; | 
 |     default: | 
 |         break; | 
 |     } | 
 |  | 
 |     if (!funcs) { | 
 |         HANDLE DlgThread; | 
 |         DWORD DlgThreadID; | 
 |  | 
 |         funcs = text_funcs; | 
 |         initEvent = CreateEvent (NULL, FALSE, FALSE, NULL); | 
 |         if (!initEvent) | 
 |             report (R_STATUS, "Can't create event object: %d", | 
 |                     GetLastError ()); | 
 |         else { | 
 |             DlgThread = CreateThread (NULL, 0, DlgThreadProc, | 
 |                                       NULL, 0, &DlgThreadID); | 
 |             if (!DlgThread) | 
 |                 report (R_STATUS, "Can't create GUI thread: %d", | 
 |                         GetLastError ()); | 
 |             else { | 
 |                 DWORD ret = WaitForSingleObject (initEvent, INFINITE); | 
 |                 switch (ret) { | 
 |                 case WAIT_OBJECT_0: | 
 |                     funcs = GUI_funcs; | 
 |                     break; | 
 |                 case WAIT_TIMEOUT: | 
 |                     report (R_STATUS, "GUI creation timed out"); | 
 |                     break; | 
 |                 case WAIT_FAILED: | 
 |                     report (R_STATUS, "Wait for GUI failed: %d", | 
 |                             GetLastError ()); | 
 |                     break; | 
 |                 default: | 
 |                     report (R_STATUS, "Wait returned %d", | 
 |                             ret); | 
 |                     break; | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 |          | 
 |     va_start (ap, t); | 
 |     if (t < sizeof text_funcs / sizeof text_funcs[0] && | 
 |         t < sizeof GUI_funcs / sizeof GUI_funcs[0]) ret = funcs[t](ap); | 
 |     else report (R_WARNING, "unimplemented report type: %d", t); | 
 |     va_end (ap); | 
 |     return ret; | 
 | } |