static void PropertiesOnCommand(HWND hwnd, WPARAM wParam) { switch (LOWORD(wParam)) { case IDM_COPY_SELECTION: CopyPropertiesToClipboard(hwnd); break; case IDM_PROPERTIES: #if defined(DEBUG) || defined(ENABLE_EXTENDED_PROPERTIES) // make a repeated Ctrl+D display some extended properties // TODO: expose this through a UI button or similar PropertiesLayout *pl = FindPropertyWindowByHwnd(hwnd); if (pl) { WindowInfo *win = FindWindowInfoByHwnd(pl->hwndParent); if (win && !pl->HasProperty(_TR("Fonts:"))) { DestroyWindow(hwnd); ShowProperties(win->hwndFrame, GetDocForWindow(SumatraWindow::Make(win)), win->dm, true); } } #endif break; } }
static LRESULT CALLBACK WndProcFavTree(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { WindowInfo* win = FindWindowInfoByHwnd(hwnd); if (!win) return CallWindowProc(DefWndProcFavTree, hwnd, msg, wParam, lParam); switch (msg) { case WM_ERASEBKGND: return FALSE; case WM_CHAR: if (VK_ESCAPE == wParam && gGlobalPrefs->escToExit && MayCloseWindow(win)) CloseWindow(win, true); break; case WM_MOUSEWHEEL: case WM_MOUSEHWHEEL: // scroll the canvas if the cursor isn't over the ToC tree if (!IsCursorOverWindow(win->hwndFavTree)) return SendMessage(win->hwndCanvas, msg, wParam, lParam); break; } return CallWindowProc(DefWndProcFavTree, hwnd, msg, wParam, lParam); }
static void RelayoutTocItem(LPNMTVCUSTOMDRAW ntvcd) { // code inspired by http://www.codeguru.com/cpp/controls/treeview/multiview/article.php/c3985/ LPNMCUSTOMDRAW ncd = &ntvcd->nmcd; HWND hTV = ncd->hdr.hwndFrom; HTREEITEM hItem = (HTREEITEM)ncd->dwItemSpec; RECT rcItem; if (0 == ncd->rc.right - ncd->rc.left || 0 == ncd->rc.bottom - ncd->rc.top) return; if (!TreeView_GetItemRect(hTV, hItem, &rcItem, TRUE)) return; if (rcItem.right > ncd->rc.right) rcItem.right = ncd->rc.right; // Clear the label RECT rcFullWidth = rcItem; rcFullWidth.right = ncd->rc.right; FillRect(ncd->hdc, &rcFullWidth, GetSysColorBrush(COLOR_WINDOW)); // Get the label's text WCHAR szText[MAX_PATH]; TVITEM item; item.hItem = hItem; item.mask = TVIF_TEXT | TVIF_PARAM; item.pszText = szText; item.cchTextMax = MAX_PATH; TreeView_GetItem(hTV, &item); // Draw the page number right-aligned (if there is one) WindowInfo *win = FindWindowInfoByHwnd(hTV); DocTocItem *tocItem = (DocTocItem *)item.lParam; ScopedMem<WCHAR> label; if (tocItem->pageNo && win && win->IsDocLoaded()) { label.Set(win->ctrl->GetPageLabel(tocItem->pageNo)); label.Set(str::Join(L" ", label)); } if (label && str::EndsWith(item.pszText, label)) { RECT rcPageNo = rcFullWidth; InflateRect(&rcPageNo, -2, -1); SIZE txtSize; GetTextExtentPoint32(ncd->hdc, label, str::Len(label), &txtSize); rcPageNo.left = rcPageNo.right - txtSize.cx; SetTextColor(ncd->hdc, GetSysColor(COLOR_WINDOWTEXT)); SetBkColor(ncd->hdc, GetSysColor(COLOR_WINDOW)); DrawText(ncd->hdc, label, -1, &rcPageNo, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX); // Reduce the size of the label and cut off the page number rcItem.right = std::max(rcItem.right - txtSize.cx, 0); szText[str::Len(szText) - str::Len(label)] = '\0'; } SetTextColor(ncd->hdc, ntvcd->clrText); SetBkColor(ncd->hdc, ntvcd->clrTextBk); // Draw the focus rectangle (including proper background color) HBRUSH brushBg = CreateSolidBrush(ntvcd->clrTextBk); FillRect(ncd->hdc, &rcItem, brushBg); DeleteObject(brushBg); if ((ncd->uItemState & CDIS_FOCUS)) DrawFocusRect(ncd->hdc, &rcItem); InflateRect(&rcItem, -2, -1); DrawText(ncd->hdc, szText, -1, &rcItem, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX | DT_WORD_ELLIPSIS); }
void WindowInfo::FocusFrame(bool always) { if (always || !FindWindowInfoByHwnd(GetFocus())) SetFocus(hwndFrame); }
static LRESULT CALLBACK WndProcTabBar(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; int index; LPTCITEM tcs; TabPainter *tab = (TabPainter *)GetWindowLongPtr(hwnd, GWLP_USERDATA); switch (msg) { case WM_DESTROY: delete tab; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)0); break; case TCM_INSERTITEM: index = (int)wParam; tcs = (LPTCITEM)lParam; CrashIf(!(TCIF_TEXT & tcs->mask)); tab->Insert(index, tcs->pszText); if ((int)index <= tab->current) tab->current++; tab->xClicked = -1; if (tab->isMouseInClientArea) PostMessage(hwnd, WM_MOUSEMOVE, 0, tab->mouseCoordinates); InvalidateRgn(hwnd, nullptr, FALSE); UpdateWindow(hwnd); break; case TCM_SETITEM: index = (int)wParam; tcs = (LPTCITEM)lParam; if (TCIF_TEXT & tcs->mask) { if (tab->Set(index, tcs->pszText)) tab->Invalidate(index); } break; case TCM_DELETEITEM: index = (int)wParam; if (tab->Delete(index)) { if ((int)index < tab->current) tab->current--; else if ((int)index == tab->current) tab->current = -1; tab->xClicked = -1; if (tab->isMouseInClientArea) PostMessage(hwnd, WM_MOUSEMOVE, 0, tab->mouseCoordinates); if (tab->Count()) { InvalidateRgn(hwnd, nullptr, FALSE); UpdateWindow(hwnd); } } break; case TCM_DELETEALLITEMS: tab->DeleteAll(); tab->current = tab->highlighted = tab->xClicked = tab->xHighlighted = -1; break; case TCM_SETITEMSIZE: if (tab->Reshape(LOWORD(lParam), HIWORD(lParam))) { tab->xClicked = -1; if (tab->isMouseInClientArea) PostMessage(hwnd, WM_MOUSEMOVE, 0, tab->mouseCoordinates); if (tab->Count()) { InvalidateRgn(hwnd, nullptr, FALSE); UpdateWindow(hwnd); } } break; case TCM_GETCURSEL: return tab->current; case TCM_SETCURSEL: { index = (int)wParam; if (index >= tab->Count()) return -1; int previous = tab->current; if ((int)index != tab->current) { tab->Invalidate(tab->current); tab->Invalidate(index); tab->current = index; UpdateWindow(hwnd); } return previous; } case WM_NCHITTEST: { if (!tab->inTitlebar || hwnd == GetCapture()) return HTCLIENT; POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)}; ScreenToClient(hwnd, &pt); if (-1 != tab->IndexFromPoint(pt.x, pt.y)) return HTCLIENT; } return HTTRANSPARENT; case WM_MOUSELEAVE: PostMessage(hwnd, WM_MOUSEMOVE, 0xFF, 0); return 0; case WM_MOUSEMOVE: { tab->mouseCoordinates = lParam; if (!tab->isMouseInClientArea) { // Track the mouse for leaving the client area. TRACKMOUSEEVENT tme; tme.cbSize = sizeof(TRACKMOUSEEVENT); tme.dwFlags = TME_LEAVE; tme.hwndTrack = hwnd; if (TrackMouseEvent(&tme)) tab->isMouseInClientArea = true; } if (wParam == 0xFF) // The mouse left the client area. tab->isMouseInClientArea = false; bool inX = false; int hl = wParam == 0xFF ? -1 : tab->IndexFromPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), &inX); if (tab->isDragging && hl == -1) { // preserve the highlighted tab if it's dragged outside the tabs' area hl = tab->highlighted; } if (tab->highlighted != hl) { if (tab->isDragging) { // send notification if the highlighted tab is dragged over another WindowInfo *win = FindWindowInfoByHwnd(hwnd); int tabNo = tab->highlighted; uitask::Post([=] { TabNotification(win, T_DRAG, tabNo, hl); }); } tab->Invalidate(hl); tab->Invalidate(tab->highlighted); tab->highlighted = hl; } int xHl = inX && !tab->isDragging ? hl : -1; if (tab->xHighlighted != xHl) { tab->Invalidate(xHl); tab->Invalidate(tab->xHighlighted); tab->xHighlighted = xHl; } if (!inX) tab->xClicked = -1; } return 0; case WM_LBUTTONDOWN: bool inX; tab->nextTab = tab->IndexFromPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam), &inX); if (inX) { // send request to close the tab WindowInfo *win = FindWindowInfoByHwnd(hwnd); int next = tab->nextTab; uitask::Post([=] { TabNotification(win, T_CLOSING, next, -1); }); } else if (tab->nextTab != -1) { if (tab->nextTab != tab->current) { // send request to select tab WindowInfo *win = FindWindowInfoByHwnd(hwnd); uitask::Post([=] { TabNotification(win, TCN_SELCHANGING, -1, -1); }); } tab->isDragging = true; SetCapture(hwnd); } return 0; case WM_LBUTTONUP: if (tab->xClicked != -1) { // send notification that the tab is closed WindowInfo *win = FindWindowInfoByHwnd(hwnd); int clicked = tab->xClicked; uitask::Post([=] { TabNotification(win, T_CLOSE, clicked, -1); }); tab->Invalidate(clicked); tab->xClicked = -1; } if (tab->isDragging) { tab->isDragging = false; ReleaseCapture(); } return 0; case WM_MBUTTONDOWN: // middle-clicking unconditionally closes the tab { tab->nextTab = tab->IndexFromPoint(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)); // send request to close the tab WindowInfo *win = FindWindowInfoByHwnd(hwnd); int next = tab->nextTab; uitask::Post([=] { TabNotification(win, T_CLOSING, next, -1); }); } return 0; case WM_MBUTTONUP: if (tab->xClicked != -1) { // send notification that the tab is closed WindowInfo *win = FindWindowInfoByHwnd(hwnd); int clicked = tab->xClicked; uitask::Post([=] { TabNotification(win, T_CLOSE, clicked, -1); }); tab->Invalidate(clicked); tab->xClicked = -1; } return 0; case WM_ERASEBKGND: return TRUE; case WM_PAINT: { RECT rc; GetUpdateRect(hwnd, &rc, FALSE); // TODO: when is wParam != nullptr? hdc = wParam ? (HDC)wParam : BeginPaint(hwnd, &ps); DoubleBuffer buffer(hwnd, RectI::FromRECT(rc)); tab->EvaluateColors(); tab->Paint(buffer.GetDC(), rc); buffer.Flush(hdc); ValidateRect(hwnd, nullptr); if (!wParam) EndPaint(hwnd, &ps); return 0; } case WM_SIZE: { WindowInfo *win = FindWindowInfoByHwnd(hwnd); if (win) UpdateTabWidth(win); } break; } return CallWindowProc(DefWndProcTabBar, hwnd, msg, wParam, lParam); }
static LRESULT CALLBACK TabBarProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp, UINT_PTR uIdSubclass, DWORD_PTR dwRefData) { PAINTSTRUCT ps; HDC hdc; int index; LPTCITEM tcs; UNUSED(uIdSubclass); UNUSED(dwRefData); TabPainter* tab = (TabPainter*)GetWindowLongPtr(hwnd, GWLP_USERDATA); if (WM_NCDESTROY == msg) { RemoveWindowSubclass(GetParent(hwnd), TabBarParentProc, 0); RemoveWindowSubclass(hwnd, TabBarProc, 0); return DefSubclassProc(hwnd, msg, wp, lp); } switch (msg) { case WM_DESTROY: delete tab; SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)0); break; case TCM_INSERTITEM: index = (int)wp; tcs = (LPTCITEM)lp; CrashIf(!(TCIF_TEXT & tcs->mask)); tab->Insert(index, tcs->pszText); if (index <= tab->selectedTabIdx) tab->selectedTabIdx++; tab->xClicked = -1; InvalidateRgn(hwnd, nullptr, FALSE); UpdateWindow(hwnd); break; case TCM_SETITEM: index = (int)wp; tcs = (LPTCITEM)lp; if (TCIF_TEXT & tcs->mask) { if (tab->Set(index, tcs->pszText)) { tab->Invalidate(index); } } break; case TCM_DELETEITEM: index = (int)wp; if (tab->Delete(index)) { if (index < tab->selectedTabIdx) { tab->selectedTabIdx--; } else if (index == tab->selectedTabIdx) { tab->selectedTabIdx = -1; } tab->xClicked = -1; if (tab->Count()) { InvalidateRgn(hwnd, nullptr, FALSE); UpdateWindow(hwnd); } } break; case TCM_DELETEALLITEMS: tab->DeleteAll(); tab->selectedTabIdx = -1; tab->highlighted = -1; tab->xClicked = -1; tab->xHighlighted = -1; break; case TCM_SETITEMSIZE: if (tab->Reshape(LOWORD(lp), HIWORD(lp))) { tab->xClicked = -1; if (tab->Count()) { InvalidateRgn(hwnd, nullptr, FALSE); UpdateWindow(hwnd); } } break; case TCM_GETCURSEL: return tab->selectedTabIdx; case TCM_SETCURSEL: { index = (int)wp; if (index >= tab->Count()) { return -1; } int previous = tab->selectedTabIdx; if (index != tab->selectedTabIdx) { tab->Invalidate(tab->selectedTabIdx); tab->Invalidate(index); tab->selectedTabIdx = index; UpdateWindow(hwnd); } return previous; } case WM_NCHITTEST: { if (!tab->inTitlebar || hwnd == GetCapture()) { return HTCLIENT; } POINT pt = {GET_X_LPARAM(lp), GET_Y_LPARAM(lp)}; ScreenToClient(hwnd, &pt); if (-1 != tab->IndexFromPoint(pt.x, pt.y)) { return HTCLIENT; } } return HTTRANSPARENT; case WM_MOUSELEAVE: PostMessage(hwnd, WM_MOUSEMOVE, 0xFF, 0); return 0; case WM_MOUSEMOVE: { tab->mouseCoordinates = lp; if (0xff != wp) { TrackMouseLeave(hwnd); } bool inX = false; int hl = wp == 0xFF ? -1 : tab->IndexFromPoint(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), &inX); if (tab->isDragging && hl == -1) { // preserve the highlighted tab if it's dragged outside the tabs' area hl = tab->highlighted; } if (tab->highlighted != hl) { if (tab->isDragging) { // send notification if the highlighted tab is dragged over another WindowInfo* win = FindWindowInfoByHwnd(hwnd); int tabNo = tab->highlighted; uitask::Post([=] { TabNotification(win, T_DRAG, tabNo, hl); }); } tab->Invalidate(hl); tab->Invalidate(tab->highlighted); tab->highlighted = hl; } int xHl = inX && !tab->isDragging ? hl : -1; if (tab->xHighlighted != xHl) { tab->Invalidate(xHl); tab->Invalidate(tab->xHighlighted); tab->xHighlighted = xHl; } if (!inX) tab->xClicked = -1; } return 0; case WM_LBUTTONDOWN: bool inX; tab->nextTab = tab->IndexFromPoint(GET_X_LPARAM(lp), GET_Y_LPARAM(lp), &inX); if (inX) { // send request to close the tab WindowInfo* win = FindWindowInfoByHwnd(hwnd); int next = tab->nextTab; uitask::Post([=] { TabNotification(win, T_CLOSING, next, -1); }); } else if (tab->nextTab != -1) { if (tab->nextTab != tab->selectedTabIdx) { // send request to select tab WindowInfo* win = FindWindowInfoByHwnd(hwnd); uitask::Post([=] { TabNotification(win, TCN_SELCHANGING, -1, -1); }); } tab->isDragging = true; SetCapture(hwnd); } return 0; case WM_LBUTTONUP: if (tab->xClicked != -1) { // send notification that the tab is closed WindowInfo* win = FindWindowInfoByHwnd(hwnd); int clicked = tab->xClicked; uitask::Post([=] { TabNotification(win, T_CLOSE, clicked, -1); }); tab->Invalidate(clicked); tab->xClicked = -1; } if (tab->isDragging) { tab->isDragging = false; ReleaseCapture(); } return 0; case WM_MBUTTONDOWN: // middle-clicking unconditionally closes the tab { tab->nextTab = tab->IndexFromPoint(GET_X_LPARAM(lp), GET_Y_LPARAM(lp)); // send request to close the tab WindowInfo* win = FindWindowInfoByHwnd(hwnd); int next = tab->nextTab; uitask::Post([=] { TabNotification(win, T_CLOSING, next, -1); }); } return 0; case WM_MBUTTONUP: if (tab->xClicked != -1) { // send notification that the tab is closed WindowInfo* win = FindWindowInfoByHwnd(hwnd); int clicked = tab->xClicked; uitask::Post([=] { TabNotification(win, T_CLOSE, clicked, -1); }); tab->Invalidate(clicked); tab->xClicked = -1; } return 0; case WM_ERASEBKGND: return TRUE; case WM_PAINT: { RECT rc; GetUpdateRect(hwnd, &rc, FALSE); // TODO: when is wp != nullptr? hdc = wp ? (HDC)wp : BeginPaint(hwnd, &ps); DoubleBuffer buffer(hwnd, RectI::FromRECT(rc)); tab->Paint(buffer.GetDC(), rc); buffer.Flush(hdc); ValidateRect(hwnd, nullptr); if (!wp) EndPaint(hwnd, &ps); return 0; } case WM_SIZE: { WindowInfo* win = FindWindowInfoByHwnd(hwnd); if (win) UpdateTabWidth(win); } break; } return DefSubclassProc(hwnd, msg, wp, lp); }