From aaf09848824471c7407340487e63a94eed822d65 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Sat, 16 Jan 2010 01:22:44 -0800 Subject: [PATCH] Replace standard message boxes with my own, which will word-wrap consistently across multiple versionf of Windows, and perhaps not be immediately ignored by the user. [git-p4: depot-paths = "//depot/solvespace/": change = 2108] --- export.cpp | 2 +- solvespace.cpp | 3 +- solvespace.h | 5 +- util.cpp | 75 ++++++++++++++++++++++ win32/w32main.cpp | 155 +++++++++++++++++++++++++++++++++++++--------- wishlist.txt | 2 +- 6 files changed, 209 insertions(+), 33 deletions(-) diff --git a/export.cpp b/export.cpp index fb75d4e..e168bdc 100644 --- a/export.cpp +++ b/export.cpp @@ -176,7 +176,7 @@ void SolveSpace::ExportViewOrWireframeTo(char *filename, bool wireframe) { out); } - if(!out->HasCanvasSize()) { + if(out && !out->HasCanvasSize()) { // These file formats don't have a canvas size, so they just // get exported in the raw coordinate system. So indicate what // that was on-screen. diff --git a/solvespace.cpp b/solvespace.cpp index 0167a06..72d5707 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -789,7 +789,8 @@ void SolveSpace::MenuHelp(int id) { Message("This is SolveSpace version 1.6.\r\n\r\n" "For more information, see http://solvespace.com/\r\n\r\n" "Built " __TIME__ " " __DATE__ ".\r\n\r\n" - "Copyright 2008-2010 Useful Subset, LLC. All Rights Reserved."); + "Copyright 2008-2010 Useful Subset, LLC.\r\n" + "All Rights Reserved."); break; case GraphicsWindow::MNU_LICENSE: { diff --git a/solvespace.h b/solvespace.h index 8887365..5f1eed2 100644 --- a/solvespace.h +++ b/solvespace.h @@ -152,8 +152,7 @@ void dbp(char *str, ...); CO((tri).a), CO((tri).b), CO((tri).c)) void SetWindowTitle(char *str); -void Message(char *str, ...); -void Error(char *str, ...); +void DoMessageBox(char *str, int rows, int cols, BOOL error); void SetTimerFor(int milliseconds); void ExitNow(void); @@ -235,6 +234,8 @@ void MakePathRelative(char *base, char *path); void MakePathAbsolute(char *base, char *path); bool StringAllPrintable(char *str); bool StringEndsIn(char *str, char *ending); +void Message(char *str, ...); +void Error(char *str, ...); class System { public: diff --git a/util.cpp b/util.cpp index 0fa0d57..1d4b473 100644 --- a/util.cpp +++ b/util.cpp @@ -125,6 +125,81 @@ void MakeMatrix(double *mat, double a11, double a12, double a13, double a14, mat[15] = a44; } +//----------------------------------------------------------------------------- +// Word-wrap the string for our message box appropriately, and then display +// that string. +//----------------------------------------------------------------------------- +static void DoStringForMessageBox(char *str, va_list f, bool error) +{ + char inBuf[1024*50]; + vsprintf(inBuf, str, f); + + char outBuf[1024*50]; + int i = 0, j = 0, len = 0, longestLen = 47; + int rows = 0, cols = 0; + + // Count the width of the longest line that starts with spaces; those + // are list items, that should not be split in the middle. + bool listLine = false; + while(inBuf[i]) { + if(inBuf[i] == '\r') { + // ignore these + } else if(inBuf[i] == ' ' && len == 0) { + listLine = true; + } else if(inBuf[i] == '\n') { + if(listLine) longestLen = max(longestLen, len); + len = 0; + } else { + len++; + } + i++; + } + if(listLine) longestLen = max(longestLen, len); + + // Word wrap according to our target line length longestLen. + len = 0; + i = 0; + while(inBuf[i]) { + if(inBuf[i] == '\r') { + // ignore these + } else if(inBuf[i] == '\n') { + outBuf[j++] = '\n'; + if(len == 0) rows++; + len = 0; + } else if(inBuf[i] == ' ' && len > longestLen) { + outBuf[j++] = '\n'; + len = 0; + } else { + outBuf[j++] = inBuf[i]; + // Count rows when we draw the first character; so an empty + // row doesn't end up counting. + if(len == 0) rows++; + len++; + } + cols = max(cols, len); + i++; + } + outBuf[j++] = '\0'; + + // And then display the text with our actual longest line length. + DoMessageBox(outBuf, rows, cols, error); +} +void Error(char *str, ...) +{ + va_list f; + va_start(f, str); + DoStringForMessageBox(str, f, true); + va_end(f); +} +void Message(char *str, ...) +{ + va_list f; + va_start(f, str); + DoStringForMessageBox(str, f, false); + va_end(f); +} + + //----------------------------------------------------------------------------- // Solve a mostly banded matrix. In a given row, there are LEFT_OF_DIAG // elements to the left of the diagonal element, and RIGHT_OF_DIAG elements to diff --git a/win32/w32main.cpp b/win32/w32main.cpp index e9e6605..7d6a7a0 100644 --- a/win32/w32main.cpp +++ b/win32/w32main.cpp @@ -58,42 +58,141 @@ HFONT FixedFont, LinkFont; // The 6-DOF input device. SiHdl SpaceNavigator = SI_NO_HANDLE; -static void DoMessageBox(char *str, va_list f, BOOL error) -{ - char buf[1024*50]; - vsprintf(buf, str, f); +//----------------------------------------------------------------------------- +// Routines to display message boxes on screen. Do our own, instead of using +// MessageBox, because that is not consistent from version to version and +// there's word wrap problems. +//----------------------------------------------------------------------------- +HWND MessageWnd, OkButton; +BOOL MessageDone; +char *MessageString; + +static LRESULT CALLBACK MessageProc(HWND hwnd, UINT msg, WPARAM wParam, + LPARAM lParam) +{ + switch (msg) { + case WM_COMMAND: + if((HWND)lParam == OkButton && wParam == BN_CLICKED) { + MessageDone = TRUE; + } + break; + + case WM_CLOSE: + case WM_DESTROY: + MessageDone = TRUE; + break; + + case WM_PAINT: { + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + int row = 0, col = 0, i; + SelectObject(hdc, FixedFont); + SetTextColor(hdc, RGB(0, 0, 0)); + SetBkMode(hdc, TRANSPARENT); + for(i = 0; MessageString[i]; i++) { + if(MessageString[i] == '\n') { + col = 0; + row++; + } else { + TextOut(hdc, col*TEXT_WIDTH + 10, row*TEXT_HEIGHT + 10, + &(MessageString[i]), 1); + col++; + } + } + EndPaint(hwnd, &ps); + break; + } + + default: + return DefWindowProc(hwnd, msg, wParam, lParam); + } + + return 1; +} + +HWND CreateWindowClient(DWORD exStyle, char *className, char *windowName, + DWORD style, int x, int y, int width, int height, HWND parent, + HMENU menu, HINSTANCE instance, void *param) +{ + HWND h = CreateWindowEx(exStyle, className, windowName, style, x, y, + width, height, parent, menu, instance, param); + + RECT r; + GetClientRect(h, &r); + width = width - (r.right - width); + height = height - (r.bottom - height); + + SetWindowPos(h, HWND_TOP, x, y, width, height, 0); + + return h; +} + +void DoMessageBox(char *str, int rows, int cols, BOOL error) +{ EnableWindow(GraphicsWnd, FALSE); EnableWindow(TextWnd, FALSE); - - int flags; - if(error) { - flags = MB_OK | MB_ICONERROR; - } else { - flags = MB_OK | MB_ICONINFORMATION; - } HWND h = GetForegroundWindow(); - MessageBox(h, buf, "SolveSpace", flags); + // Register the window class for our dialog. + WNDCLASSEX wc; + memset(&wc, 0, sizeof(wc)); + wc.cbSize = sizeof(wc); + wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC; + wc.lpfnWndProc = (WNDPROC)MessageProc; + wc.hInstance = Instance; + wc.hbrBackground = (HBRUSH)COLOR_BTNSHADOW; + wc.lpszClassName = "MessageWnd"; + wc.lpszMenuName = NULL; + wc.hCursor = LoadCursor(NULL, IDC_ARROW); + wc.hIcon = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000), + IMAGE_ICON, 32, 32, 0); + wc.hIconSm = (HICON)LoadImage(Instance, MAKEINTRESOURCE(4000), + IMAGE_ICON, 16, 16, 0); + RegisterClassEx(&wc); + + // Create the window. + MessageString = str; + RECT r; + GetWindowRect(GraphicsWnd, &r); + char *title = error ? "SolveSpace - Error" : "SolveSpace - Message"; + int width = cols*TEXT_WIDTH + 20, height = rows*TEXT_HEIGHT + 60; + MessageWnd = CreateWindowClient(0, "MessageWnd", title, + WS_OVERLAPPED | WS_SYSMENU, + r.left + 100, r.top + 100, width, height, NULL, NULL, Instance, NULL); + + OkButton = CreateWindowEx(0, WC_BUTTON, "OK", + WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_VISIBLE | BS_DEFPUSHBUTTON, + (width - 70)/2, rows*TEXT_HEIGHT + 20, + 70, 25, MessageWnd, NULL, Instance, NULL); + SendMessage(OkButton, WM_SETFONT, (WPARAM)FixedFont, TRUE); + + ShowWindow(MessageWnd, TRUE); + SetFocus(OkButton); + + MSG msg; + DWORD ret; + MessageDone = FALSE; + while((ret = GetMessage(&msg, NULL, 0, 0)) && !MessageDone) { + if((msg.message == WM_KEYDOWN && + (msg.wParam == VK_RETURN || + msg.wParam == VK_ESCAPE)) || + (msg.message == WM_KEYUP && + (msg.wParam == VK_SPACE))) + { + MessageDone = TRUE; + break; + } + + TranslateMessage(&msg); + DispatchMessage(&msg); + } + + MessageString = NULL; EnableWindow(TextWnd, TRUE); EnableWindow(GraphicsWnd, TRUE); SetForegroundWindow(GraphicsWnd); -} - -void Error(char *str, ...) -{ - va_list f; - va_start(f, str); - DoMessageBox(str, f, TRUE); - va_end(f); -} - -void Message(char *str, ...) -{ - va_list f; - va_start(f, str); - DoMessageBox(str, f, FALSE); - va_end(f); + DestroyWindow(MessageWnd); } void AddContextMenuItem(char *label, int id) diff --git a/wishlist.txt b/wishlist.txt index 5467e98..5a43562 100644 --- a/wishlist.txt +++ b/wishlist.txt @@ -1,4 +1,4 @@ -better Error() and Message() dialog boxes +replace show/hide links with icons? lock point where dragged constraint projected and signed distance constraints