| /* | |
| * 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, 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, | |
| 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, | |
| 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); | |
| } | |
| static INT_PTR CALLBACK | |
| AskEmailProc (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) | |
| { | |
| int len; | |
| switch (msg) | |
| { | |
| case WM_COMMAND: | |
| switch (LOWORD (wParam)) { | |
| case IDOK: | |
| len = GetWindowTextLengthA (GetDlgItem (hwnd, IDC_EMAIL)); | |
| if(!len) { | |
| report (R_WARNING, "You must enter an email address to continue"); | |
| return FALSE; | |
| } | |
| email = heap_alloc (len+1); | |
| GetDlgItemTextA (hwnd, IDC_EMAIL, email, len+1); | |
| EndDialog (hwnd, IDOK); | |
| return TRUE; | |
| case IDABORT: | |
| EndDialog (hwnd, IDABORT); | |
| return TRUE; | |
| } | |
| } | |
| return FALSE; | |
| } | |
| int | |
| guiAskEmail (void) | |
| { | |
| return DialogBox (GetModuleHandle (NULL), MAKEINTRESOURCE (IDD_EMAIL), dialog, AskEmailProc); | |
| } | |
| /* 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; | |
| } |