diff --git a/toolkit/crashreporter/client/crashreporter_win.cpp b/toolkit/crashreporter/client/crashreporter_win.cpp index 5b7272e4ec3..3bd43a75538 100644 --- a/toolkit/crashreporter/client/crashreporter_win.cpp +++ b/toolkit/crashreporter/client/crashreporter_win.cpp @@ -49,6 +49,7 @@ #include #include #include +#include #include #include "resource.h" #include "client/windows/sender/crash_report_sender.h" @@ -89,6 +90,7 @@ static vector gRestartArgs; static map gQueryParameters; static wstring gCrashReporterKey(L"Software\\Mozilla\\Crash Reporter"); static wstring gURLParameter; +static int gCheckboxPadding = 0; // When vertically resizing the dialog, these items should move down static set gAttachedBottom; @@ -262,12 +264,48 @@ static string FormatLastError() return message; } +#define TS_DRAW 2 +#define BP_CHECKBOX 3 + +typedef HANDLE (WINAPI*OpenThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList); +typedef HRESULT (WINAPI*CloseThemeDataPtr)(HANDLE hTheme); +typedef HRESULT (WINAPI*GetThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId, + int iStateId, RECT* prc, int ts, + SIZE* psz); + +static void GetThemeSizes(HWND hwnd) +{ + HMODULE themeDLL = LoadLibrary(L"uxtheme.dll"); + + if (!themeDLL) + return; + + OpenThemeDataPtr openTheme = + (OpenThemeDataPtr)GetProcAddress(themeDLL, "OpenThemeData"); + CloseThemeDataPtr closeTheme = + (CloseThemeDataPtr)GetProcAddress(themeDLL, "CloseThemeData"); + GetThemePartSizePtr getThemePartSize = + (GetThemePartSizePtr)GetProcAddress(themeDLL, "GetThemePartSize"); + + if (!openTheme || !closeTheme || !getThemePartSize) { + FreeLibrary(themeDLL); + return; + } + + HANDLE buttonTheme = openTheme(hwnd, L"Button"); + HDC hdc = GetDC(hwnd); + SIZE s; + getThemePartSize(buttonTheme, hdc, BP_CHECKBOX, 0, NULL, TS_DRAW, &s); + gCheckboxPadding = s.cx; + closeTheme(buttonTheme); + FreeLibrary(themeDLL); +} + // Gets the position of a window relative to another window's client area static void GetRelativeRect(HWND hwnd, HWND hwndParent, RECT* r) { GetWindowRect(hwnd, r); - ScreenToClient(hwndParent, (POINT*)&(r->left)); - ScreenToClient(hwndParent, (POINT*)&(r->right)); + MapWindowPoints(NULL, hwndParent, (POINT*)r, 2); } static void SetDlgItemVisible(HWND hwndDlg, UINT item, bool visible) @@ -365,6 +403,50 @@ static void EndCrashReporterDialog(HWND hwndDlg, int code) EndDialog(hwndDlg, code); } +static void MaybeResizeProgressText(HWND hwndDlg) +{ + HWND hwndProgress = GetDlgItem(hwndDlg, IDC_PROGRESSTEXT); + HDC hdc = GetDC(hwndProgress); + HFONT hfont = (HFONT)SendMessage(hwndProgress, WM_GETFONT, 0, 0); + if (hfont) + SelectObject(hdc, hfont); + SIZE size; + RECT rect; + GetRelativeRect(hwndProgress, hwndDlg, &rect); + + wchar_t text[1024]; + GetWindowText(hwndProgress, text, 1024); + + if (!GetTextExtentPoint32(hdc, text, wcslen(text), &size)) + return; + + if (size.cx < (rect.right - rect.left)) + return; + + // Figure out how much we need to resize things vertically + // This is sort of a fudge, but it should be good enough. + int wantedHeight = size.cy * + (int)ceil((float)size.cx / (float)(rect.right - rect.left)); + int diff = wantedHeight - (rect.bottom - rect.top); + if (diff <= 0) + return; + + MoveWindow(hwndProgress, rect.left, rect.top, + rect.right - rect.left, + wantedHeight, + TRUE); + + gAttachedBottom.clear(); + gAttachedBottom.insert(IDC_CLOSEBUTTON); + gAttachedBottom.insert(IDC_RESTARTBUTTON); + + StretchDialog(hwndDlg, diff); + + for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) { + gAttachedBottom.insert(kDefaultAttachedBottom[i]); + } +} + static void MaybeSendReport(HWND hwndDlg) { if (!IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK)) { @@ -383,6 +465,7 @@ static void MaybeSendReport(HWND hwndDlg) EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON), false); SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTDURINGSUBMIT).c_str()); + MaybeResizeProgressText(hwndDlg); // start throbber // play entire AVI, and loop Animate_Play(GetDlgItem(hwndDlg, IDC_THROBBER), 0, -1, -1); @@ -622,6 +705,78 @@ static LRESULT CALLBACK EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, return CallWindowProc(super, hwnd, uMsg, wParam, lParam); } +// Resize a control to fit this text +static int ResizeControl(HWND hwndButton, RECT& rect, wstring text, + bool shiftLeft, int extraPadding) +{ + HDC hdc = GetDC(hwndButton); + HFONT hfont = (HFONT)SendMessage(hwndButton, WM_GETFONT, 0, 0); + if (hfont) + SelectObject(hdc, hfont); + SIZE size, oldSize; + int sizeDiff = 0; + + wchar_t oldText[1024]; + GetWindowText(hwndButton, oldText, 1024); + + if (GetTextExtentPoint32(hdc, text.c_str(), text.length(), &size) + // default text on the button + && GetTextExtentPoint32(hdc, oldText, wcslen(oldText), &oldSize)) { + // We want the change in the text size, minus the existing empty + // space on the control. + sizeDiff = (size.cx - oldSize.cx) - + ((rect.right - rect.left) - extraPadding - oldSize.cx); + if (sizeDiff <= 0) + return 0; + + if (shiftLeft) { + // shift left by the amount the button should grow + rect.left -= sizeDiff; + } + else { + // grow right instead + rect.right += sizeDiff; + } + MoveWindow(hwndButton, rect.left, rect.top, + rect.right - rect.left, + rect.bottom - rect.top, + TRUE); + } + return sizeDiff; +} + +// The window was resized horizontally, so widen some of our +// controls to make use of the space +static void StretchControlsToFit(HWND hwndDlg) +{ + int controls[] = { + IDC_DESCRIPTIONTEXT, + IDC_SUBMITREPORTCHECK, + IDC_COMMENTTEXT, + IDC_INCLUDEURLCHECK, + IDC_EMAILMECHECK, + IDC_EMAILTEXT, + IDC_PROGRESSTEXT + }; + + RECT dlgRect; + GetClientRect(hwndDlg, &dlgRect); + + for (int i=0; i 0) { - viewRect.right += sizeDiff; - MoveWindow(hwndView, viewRect.left, viewRect.top, - viewRect.right - viewRect.left, - viewRect.bottom - viewRect.top, - TRUE); - } - } - SetDlgItemText(hwndDlg, IDC_VIEWREPORTBUTTON, viewButtonText.c_str()); - + RECT rect; + HWND hwnd = GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON); + GetRelativeRect(hwnd, hwndDlg, &rect); + ResizeControl(hwnd, rect, Str(ST_VIEWREPORT), false, 0); + SetDlgItemText(hwndDlg, IDC_VIEWREPORTBUTTON, Str(ST_VIEWREPORT).c_str()); + hwnd = GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK); + GetRelativeRect(hwnd, hwndDlg, &rect); + int maxdiff = ResizeControl(hwnd, rect, Str(ST_CHECKSUBMIT), false, + gCheckboxPadding); SetDlgItemText(hwndDlg, IDC_SUBMITREPORTCHECK, Str(ST_CHECKSUBMIT).c_str()); + if (CheckBoolKey(gCrashReporterKey.c_str(), SUBMIT_REPORT_VALUE, &enabled) && !enabled) { @@ -723,7 +839,13 @@ static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message, wcscpy(hMem, commentGrayText.c_str()); SetProp(hwndComment, L"PROP_GRAYTEXT", hMem); + hwnd = GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK); + GetRelativeRect(hwnd, hwndDlg, &rect); + int diff = ResizeControl(hwnd, rect, Str(ST_CHECKURL), false, + gCheckboxPadding); + maxdiff = max(diff, maxdiff); SetDlgItemText(hwndDlg, IDC_INCLUDEURLCHECK, Str(ST_CHECKURL).c_str()); + // want this on by default if (CheckBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE, &enabled) && !enabled) { @@ -732,7 +854,13 @@ static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message, CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_CHECKED); } + hwnd = GetDlgItem(hwndDlg, IDC_EMAILMECHECK); + GetRelativeRect(hwnd, hwndDlg, &rect); + diff = ResizeControl(hwnd, rect, Str(ST_CHECKEMAIL), false, + gCheckboxPadding); + maxdiff = max(diff, maxdiff); SetDlgItemText(hwndDlg, IDC_EMAILMECHECK, Str(ST_CHECKEMAIL).c_str()); + if (CheckBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE, &enabled) && enabled) { CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED); @@ -757,7 +885,6 @@ static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message, SetProp(hwndEmail, L"PROP_GRAYTEXT", hMem); SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTPRESUBMIT).c_str()); - SetDlgItemText(hwndDlg, IDC_CLOSEBUTTON, Str(ST_QUIT).c_str()); RECT closeRect; HWND hwndClose = GetDlgItem(hwndDlg, IDC_CLOSEBUTTON); @@ -767,24 +894,18 @@ static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message, HWND hwndRestart = GetDlgItem(hwndDlg, IDC_RESTARTBUTTON); GetRelativeRect(hwndRestart, hwndDlg, &restartRect); + // Resize close button to fit text + ResizeControl(hwndClose, closeRect, Str(ST_QUIT), true, 0); + SetDlgItemText(hwndDlg, IDC_CLOSEBUTTON, Str(ST_QUIT).c_str()); + if (gRestartArgs.size() > 0) { // set the restart button text and shift the buttons around // since the size may need to change - hdc = GetDC(hwndRestart); - const wstring restartButtonText = Str(ST_RESTART); - if (GetTextExtentPoint32(hdc, restartButtonText.c_str(), - restartButtonText.length(), &size)) { - // shift left by the amount the button should grow - int sizeDiff = size.cx - (restartRect.right - restartRect.left); - restartRect.left -= sizeDiff; - closeRect.left -= sizeDiff; - closeRect.right -= sizeDiff; - MoveWindow(hwndRestart, restartRect.left, restartRect.top, - restartRect.right - restartRect.left, - restartRect.bottom - restartRect.top, - TRUE); - } - SetDlgItemText(hwndDlg, IDC_RESTARTBUTTON, restartButtonText.c_str()); + int sizeDiff = ResizeControl(hwndRestart, restartRect, Str(ST_RESTART), + true, 0); + closeRect.left -= sizeDiff; + closeRect.right -= sizeDiff; + SetDlgItemText(hwndDlg, IDC_RESTARTBUTTON, Str(ST_RESTART).c_str()); } else { // No restart arguments, move the close button over to the side // and hide the restart button @@ -794,12 +915,63 @@ static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message, closeRect.right = restartRect.right; closeRect.left = closeRect.right - size; } + // See if we need to widen the window + // Leave 6 pixels on either side + 6 pixels between the buttons + int neededSize = closeRect.right - closeRect.left + + restartRect.right - restartRect.left + 6 * 3; + GetClientRect(hwndDlg, &r); + // We may already have resized one of the checkboxes above + maxdiff = max(maxdiff, neededSize - (r.right - r.left)); + + if (maxdiff > 0) { + // widen window + GetWindowRect(hwndDlg, &r); + r.right += maxdiff; + MoveWindow(hwndDlg, r.left, r.top, + r.right - r.left, r.bottom - r.top, TRUE); + // shift both buttons right + if (closeRect.left + maxdiff < 6) + maxdiff += 6; + closeRect.left += maxdiff; + closeRect.right += maxdiff; + restartRect.left += maxdiff; + restartRect.right += maxdiff; + MoveWindow(hwndRestart, restartRect.left, restartRect.top, + restartRect.right - restartRect.left, + restartRect.bottom - restartRect.top, + TRUE); + StretchControlsToFit(hwndDlg); + } // need to move the close button regardless MoveWindow(hwndClose, closeRect.left, closeRect.top, closeRect.right - closeRect.left, closeRect.bottom - closeRect.top, TRUE); + // Resize the description text last, in case the window was resized + // before this. + SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, + EM_SETEVENTMASK, (WPARAM)NULL, + ENM_REQUESTRESIZE); + + wstring description = Str(ST_CRASHREPORTERHEADER); + description += L"\n\n"; + description += Str(ST_CRASHREPORTERDESCRIPTION); + SetDlgItemText(hwndDlg, IDC_DESCRIPTIONTEXT, description.c_str()); + + // Make the title bold. + CHARFORMAT fmt = { 0, }; + fmt.cbSize = sizeof(fmt); + fmt.dwMask = CFM_BOLD; + fmt.dwEffects = CFE_BOLD; + SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL, + 0, Str(ST_CRASHREPORTERHEADER).length()); + SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETCHARFORMAT, + SCF_SELECTION, (LPARAM)&fmt); + SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL, 0, 0); + SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, + EM_SETTARGETDEVICE, (WPARAM)NULL, 0); + // if no URL was given, hide the URL checkbox if (gQueryParameters.find(L"URL") == gQueryParameters.end()) { RECT urlCheckRect, emailCheckRect; @@ -819,6 +991,8 @@ static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message, gAttachedBottom.insert(IDC_COMMENTTEXT); } + MaybeResizeProgressText(hwndDlg); + // Open the AVI resource for the throbber Animate_Open(GetDlgItem(hwndDlg, IDC_THROBBER), MAKEINTRESOURCE(IDR_THROBBER)); @@ -910,6 +1084,7 @@ static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message, success ? Str(ST_REPORTSUBMITSUCCESS).c_str() : Str(ST_SUBMITFAILED).c_str()); + MaybeResizeProgressText(hwndDlg); // close dialog after 5 seconds SetTimer(hwndDlg, 0, 5000, NULL); //