diff --git a/src/GdiImageDrawer.cpp b/src/GdiImageDrawer.cpp index e12c8825..834d8915 100644 --- a/src/GdiImageDrawer.cpp +++ b/src/GdiImageDrawer.cpp @@ -95,7 +95,7 @@ void CGdiImageDrawer::Draw(CDC* pScreenDC, CDPI &dpi, CWnd *pWnd, CRect rc, bool Draw(pScreenDC, dpi, pWnd, x, y, mouseHover, mouseDown); } -void CGdiImageDrawer::Draw(CDC* pScreenDC, CDPI &dpi, CWnd *pWnd, int posX, int posY, bool mouseHover, bool mouseDown, int forceWidth, int forceHeight) +void CGdiImageDrawer::Draw(CDC* pScreenDC, CDPI &dpi, CWnd *pWnd, int posX, int posY, bool mouseHover, bool mouseDown, int forceWidth, int forceHeight, int opacity) { int width = m_pStdImage->m_pBitmap->GetWidth(); if (forceWidth != INT_MAX) @@ -135,7 +135,29 @@ void CGdiImageDrawer::Draw(CDC* pScreenDC, CDPI &dpi, CWnd *pWnd, int posX, int //ia.SetRemapTable(1, &blackToRed); Gdiplus::Graphics graphics(pScreenDC->m_hDC); - graphics.DrawImage(*m_pStdImage, posX, posY, width, height); + if (opacity >= 255) + { + graphics.DrawImage(*m_pStdImage, posX, posY, width, height); + } + else + { + // draw the png faded out (the hover only pin button) + if (opacity < 0) + opacity = 0; + + Gdiplus::ColorMatrix cm = { 0 }; + cm.m[0][0] = 1.0f; + cm.m[1][1] = 1.0f; + cm.m[2][2] = 1.0f; + cm.m[3][3] = (Gdiplus::REAL)opacity / 255.0f; + cm.m[4][4] = 1.0f; + + Gdiplus::ImageAttributes ia; + ia.SetColorMatrix(&cm); + + Gdiplus::RectF grect((Gdiplus::REAL)posX, (Gdiplus::REAL)posY, (Gdiplus::REAL)width, (Gdiplus::REAL)height); + graphics.DrawImage(*m_pStdImage, grect, 0, 0, (Gdiplus::REAL)width, (Gdiplus::REAL)height, Gdiplus::UnitPixel, &ia); + } //RectF grect; grect.X = posX, grect.Y = posY; grect.Width = width; grect.Height = height; //graphics.DrawImage(*m_pStdImage, grect, 0, 0, width, height, UnitPixel, &ia); diff --git a/src/GdiImageDrawer.h b/src/GdiImageDrawer.h index 6e5ebe6c..65cdb3be 100644 --- a/src/GdiImageDrawer.h +++ b/src/GdiImageDrawer.h @@ -11,12 +11,12 @@ class CGdiImageDrawer BOOL LoadStdImage(UINT id, LPCTSTR pType); BOOL LoadStdImageDPI(int dpi, UINT id96, UINT id120, UINT id144, UINT id168, UINT id192, LPCTSTR pType, UINT id225 = 0, UINT id250 = 0, UINT id275 = 0, UINT id300 = 0, UINT id325 = 0, UINT id350 = 0); - void Draw(CDC* pScreenDC, CDPI &dpi, CWnd *pWnd, int posX, int posY, bool mouseHover, bool mouseDown, int forceWidth = INT_MAX, int forceHeight = INT_MAX); + void Draw(CDC* pScreenDC, CDPI &dpi, CWnd *pWnd, int posX, int posY, bool mouseHover, bool mouseDown, int forceWidth = INT_MAX, int forceHeight = INT_MAX, int opacity = 255); void Draw(CDC* pScreenDC, CDPI &dpi, CWnd *pWnd, CRect rc, bool mouseHover, bool mouseDown); BOOL LoadRaw(unsigned char* bitmapData, int imageSize); - UINT ImageWidth() { return m_pStdImage->m_pBitmap->GetWidth(); } - UINT ImageHeight() { return m_pStdImage->m_pBitmap->GetHeight(); } + UINT ImageWidth() { return (m_pStdImage && m_pStdImage->m_pBitmap) ? m_pStdImage->m_pBitmap->GetWidth() : 0; } + UINT ImageHeight() { return (m_pStdImage && m_pStdImage->m_pBitmap) ? m_pStdImage->m_pBitmap->GetHeight() : 0; } void Reset(); diff --git a/src/QListCtrl.cpp b/src/QListCtrl.cpp index 3c9cf307..39f086be 100644 --- a/src/QListCtrl.cpp +++ b/src/QListCtrl.cpp @@ -26,6 +26,9 @@ static char THIS_FILE[] = __FILE__; #define ROW_BOTTOM_BORDER 4 #define ROW_LEFT_BORDER 3 +// opacity of the pin button while it is only shown because the row is hovered. +// keep it far below full so a hovered clip is clearly not a pinned one +#define PIN_BUTTON_HOVER_ALPHA 60 #define COLOR_SHADOW RGB(245, 245, 245) #define DUMMY_COL_WIDTH 2 @@ -209,6 +212,9 @@ CQListCtrl::CQListCtrl() m_pFormatter = NULL; m_allSelected = false; m_rowHeight = 50; + m_hoverRow = -1; + m_pinButtonItem = -1; + m_trackingMouseLeave = FALSE; m_mouseOverScrollAreaStart = 0; m_showIfClipWasPasted = TRUE; m_bShowTextForFirstTenHotKeys = true; @@ -259,6 +265,7 @@ BEGIN_MESSAGE_MAP(CQListCtrl, CListCtrl) ON_NOTIFY_REFLECT(LVN_KEYDOWN, OnKeydown) ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomdrawList) ON_WM_MOUSEMOVE() + ON_WM_MOUSELEAVE() ON_WM_SYSKEYDOWN() ON_WM_ERASEBKGND() ON_WM_CREATE() @@ -420,6 +427,9 @@ void CQListCtrl::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) GetTextMetrics(hDC, &tm); if (m_windowDpi != NULL) { + // windows list views only support one height for all rows, every row gets + // the room of "lines per clip" lines and the wrapped text is drawn into + // it. a clip needing more lines is cut off at the bottom of the row. lpMeasureItemStruct->itemHeight = ((tm.tmHeight + tm.tmExternalLeading) * m_linesPerRow) + m_windowDpi->Scale(ROW_BOTTOM_BORDER); m_rowHeight = lpMeasureItemStruct->itemHeight; } @@ -427,6 +437,55 @@ void CQListCtrl::MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct) ::ReleaseDC(NULL, hDC); } +// the right end of the row area that really belongs to the list. The strip the +// list keeps for its scroll bar is inside the client rect but is not part of +// the list for the mouse, a click there goes to the window behind it +int CQListCtrl::GetVisibleRight() +{ + CRect rcListClient; + GetClientRect(&rcListClient); + + CRect rcWindow; + GetWindowRect(&rcWindow); + + int nExtra = rcWindow.Width() - rcListClient.Width(); + if (nExtra > 0) + rcListClient.right -= nExtra; + + return rcListClient.right; +} + +BOOL CQListCtrl::HitTestPinButton(CPoint point, int& nItem) +{ + int nRow = HitTest(point); + // a pin button only exists for the row the mouse is over, use the rect that + // row was last painted with + if (nRow < 0 || nRow != m_pinButtonItem) + return FALSE; + + CRect rcBtn = m_pinButtonRect; + rcBtn.InflateRect(m_windowDpi->Scale(3), m_windowDpi->Scale(2)); + if (rcBtn.PtInRect(point) == FALSE) + return FALSE; + + nItem = nRow; + + return TRUE; +} + +void CQListCtrl::OnMouseLeave() +{ + m_trackingMouseLeave = FALSE; + m_pinButtonItem = -1; + + if (m_hoverRow >= 0) + { + int nRow = m_hoverRow; + m_hoverRow = -1; + RefreshRow(nRow); + } +} + void CQListCtrl::OnCustomdrawList(NMHDR* pNMHDR, LRESULT* pResult) { NMLVCUSTOMDRAW* pLVCD = reinterpret_cast(pNMHDR); @@ -515,6 +574,10 @@ void CQListCtrl::OnCustomdrawList(NMHDR* pNMHDR, LRESULT* pResult) rcText.left += m_windowDpi->Scale(ROW_LEFT_BORDER); rcText.top += m_windowDpi->Scale(1); rcText.bottom -= m_windowDpi->Scale(1); + // a row can be wider than the control, keep the text inside the part of + // the row that is really visible + if (rcText.right > GetVisibleRight()) + rcText.right = GetVisibleRight(); if (m_showIfClipWasPasted && strSymbols.GetLength() > 0 && @@ -555,37 +618,57 @@ void CQListCtrl::OnCustomdrawList(NMHDR* pNMHDR, LRESULT* pResult) DrawBitMap(nItem, rcText, pDC, csText); // draw the symbol box - if (strSymbols.GetLength() > 0) + if (strSymbols.Find(_T("")) >= 0) //group { - if (strSymbols.Find(_T("")) >= 0) //group - { - m_groupFolder.Draw(pDC, *m_windowDpi, this, rcText.left, rcText.top, false, false); - rcText.left += m_groupFolder.ImageWidth() + m_windowDpi->Scale(2); - } - if (strSymbols.Find(_T("")) >= 0) //don't auto delete - { - m_dontDeleteImage.Draw(pDC, *m_windowDpi, this, rcText.left, rcText.top, false, false); - rcText.left += m_dontDeleteImage.ImageWidth() + m_windowDpi->Scale(2); - } - if (strSymbols.Find(_T("")) >= 0) // has shortcut - { - m_shortCutImage.Draw(pDC, *m_windowDpi, this, rcText.left, rcText.top, false, false); - rcText.left += m_shortCutImage.ImageWidth() + m_windowDpi->Scale(2); - } - if (drawInGroupIcon && - strSymbols.Find(_T("")) >= 0) // in group - { - m_inFolderImage.Draw(pDC, *m_windowDpi, this, rcText.left, rcText.top, false, false); - rcText.left += m_inFolderImage.ImageWidth() + m_windowDpi->Scale(2); - } - if (strSymbols.Find(_T("")) >= 0) // has quick paste text + m_groupFolder.Draw(pDC, *m_windowDpi, this, rcText.left, rcText.top, false, false); + rcText.left += m_groupFolder.ImageWidth() + m_windowDpi->Scale(2); + } + + // the pin button is the "never auto delete" star, so it sits in the slot + // that star has always been drawn in and a pinned clip shows one star + // and not two. A pinned clip shows it solid, a hovered clip shows it + // faint so it can be pinned. The slot is kept free for every row so the + // text does not move when the star shows up. + int nStarWidth = m_dontDeleteImage.ImageWidth(); + if (nStarWidth > 0) + { + CRect rcStar(rcText.left, rcText.top, rcText.left + nStarWidth, + rcText.top + m_dontDeleteImage.ImageHeight()); + bool bPinned = (strSymbols.Find(_T("")) >= 0); + bool bHover = (nItem == m_hoverRow); + + // remember where the star of the hovered row is, the click on it is + // handled outside this paint + if (bHover) { + m_pinButtonRect = rcStar; + m_pinButtonItem = nItem; } - if (strSymbols.Find(_T("")) >= 0) //sticky clip + + if (bPinned || bHover) { - m_stickyImage.Draw(pDC, *m_windowDpi, this, rcText.left, rcText.top, false, false); - rcText.left += m_stickyImage.ImageWidth() + m_windowDpi->Scale(2); + m_dontDeleteImage.Draw(pDC, *m_windowDpi, this, rcStar.left, rcStar.top, false, false, + rcStar.Width(), rcStar.Height(), bPinned ? 255 : PIN_BUTTON_HOVER_ALPHA); } + + rcText.left += nStarWidth + m_windowDpi->Scale(2); + } + + if (strSymbols.Find(_T("")) >= 0) // has shortcut + { + m_shortCutImage.Draw(pDC, *m_windowDpi, this, rcText.left, rcText.top, false, false); + rcText.left += m_shortCutImage.ImageWidth() + m_windowDpi->Scale(2); + } + if (drawInGroupIcon && + strSymbols.Find(_T("")) >= 0) // in group + { + m_inFolderImage.Draw(pDC, *m_windowDpi, this, rcText.left, rcText.top, false, false); + rcText.left += m_inFolderImage.ImageWidth() + m_windowDpi->Scale(2); + } + if (strSymbols.Find(_T("")) >= 0) //sticky clip + { + m_stickyImage.Draw(pDC, *m_windowDpi, this, rcText.left, rcText.top, false, false); + rcText.left += m_stickyImage.ImageWidth() + m_windowDpi->Scale(2); } if (DrawRtfText(nItem, rcText, pDC) == FALSE) @@ -595,11 +678,21 @@ void CQListCtrl::OnCustomdrawList(NMHDR* pNMHDR, LRESULT* pResult) if (m_searchText.GetLength() > 0 && FindNoCaseAndInsert(csText, m_searchText, StrF(_T("\x01\x04 color='#%02x%02x%02x'\x02"), GetRValue(highlightColor), GetGValue(highlightColor), GetBValue(highlightColor)), _T("\x01\x03\x04\x02"), m_linesPerRow) > 0) { - DrawHTML(pDC->m_hDC, csText, csText.GetLength(), rcText, DT_VCENTER | DT_EXPANDTABS | DT_NOPREFIX); + DrawHTML(pDC->m_hDC, csText, csText.GetLength(), rcText, DT_WORDBREAK | DT_EXPANDTABS | DT_NOPREFIX); } else { - pDC->DrawText(csText, rcText, DT_VCENTER | DT_EXPANDTABS | DT_NOPREFIX); + // keep a clip that needs less room than the row has vertically + // centered, like it was before the text could wrap + CRect rcMeasure = rcText; + int nTextHeight = pDC->DrawText(csText, rcMeasure, DT_CALCRECT | DT_WORDBREAK | DT_EXPANDTABS | DT_NOPREFIX); + if (nTextHeight > 0 && nTextHeight < rcText.Height()) + { + rcText.top += (rcText.Height() - nTextHeight) / 2; + rcText.bottom = rcText.top + nTextHeight; + } + + pDC->DrawText(csText, rcText, DT_WORDBREAK | DT_EXPANDTABS | DT_NOPREFIX); } } @@ -2100,6 +2193,32 @@ BOOL CQListCtrl::OnItemDeleted(long lID) void CQListCtrl::OnMouseMove(UINT nFlags, CPoint point) { + // highlight the row under the mouse so its pin button shows up + int nHover = HitTest(point); + if (nHover != m_hoverRow) + { + int nPrev = m_hoverRow; + m_hoverRow = nHover; + // the star of the previous row is not a button any more + m_pinButtonItem = -1; + + if (nPrev >= 0) + RefreshRow(nPrev); + if (nHover >= 0) + RefreshRow(nHover); + } + + if (m_trackingMouseLeave == FALSE) + { + TRACKMOUSEEVENT tme = { 0 }; + tme.cbSize = sizeof(TRACKMOUSEEVENT); + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = m_hWnd; + + if (::TrackMouseEvent(&tme)) + m_trackingMouseLeave = TRUE; + } + if (CGetSetOptions::m_showScrollBar == FALSE) { CRect crWindow; diff --git a/src/QListCtrl.h b/src/QListCtrl.h index 01ef076d..55bfebd7 100644 --- a/src/QListCtrl.h +++ b/src/QListCtrl.h @@ -154,6 +154,8 @@ class CQListCtrl : public CListCtrl int GetRowHeight() { return m_rowHeight; } + BOOL HitTestPinButton(CPoint point, int& nItem); + void SetSearchText(CString text); void SetDpiInfo(CDPI *dpi); @@ -195,6 +197,13 @@ class CQListCtrl : public CListCtrl CGdiImageDrawer m_shortCutImage; CGdiImageDrawer m_stickyImage; int m_rowHeight; + int m_hoverRow; + BOOL m_trackingMouseLeave; + // rect of the pin star of the hovered row, kept up to date while that row + // is painted + CRect m_pinButtonRect; + int m_pinButtonItem; + int GetVisibleRight(); CString m_searchText; BOOL m_showIfClipWasPasted; CAccels *m_pToolTipActions; @@ -219,6 +228,7 @@ class CQListCtrl : public CListCtrl afx_msg BOOL OnToolTipText(UINT id, NMHDR * pNMHDR, LRESULT * pResult); afx_msg void MeasureItem(LPMEASUREITEMSTRUCT lpMeasureItemStruct); afx_msg void OnMouseMove(UINT nFlags, CPoint point); + afx_msg void OnMouseLeave(); DECLARE_MESSAGE_MAP() public: afx_msg void OnKillFocus(CWnd* pNewWnd); diff --git a/src/QPasteWnd.cpp b/src/QPasteWnd.cpp index 08fedfc6..662d297b 100644 --- a/src/QPasteWnd.cpp +++ b/src/QPasteWnd.cpp @@ -49,6 +49,16 @@ static char THIS_FILE[] = __FILE__; #define ID_SYSTEM_BUTTON 0x210 #define ID_NO_SEARCH_RESULTS 0x211 +// The entry of the system menu that deletes clips is drawn in red to warn +// about it. Windows draws its menus itself and cannot color a single entry, +// so this entry is switched to owner draw and painted in OnDrawItem with the +// size and the colors windows uses for the entries around it (a menu that +// holds an owner drawn entry is drawn by windows the classic way). +#define MENU_DANGER_TEXT_COLOR RGB(200, 0, 0) +#define MENU_ITEM_HEIGHT 17 +#define MENU_ITEM_TEXT_LEFT 17 +#define MENU_ITEM_RIGHT_MARGIN 20 + #define QPASTE_WIDTH 200 #define QPASTE_HEIGHT 200 @@ -87,6 +97,7 @@ CQPasteWnd::CQPasteWnd() m_lastDbWrite = 0; m_pendingRefresh = false; m_lastNonActiveMouseMove = 0; + m_bSwallowPinUp = FALSE; } CQPasteWnd::~CQPasteWnd() @@ -208,6 +219,8 @@ BEGIN_MESSAGE_MAP(CQPasteWnd, CWndEx) ON_MESSAGE(NM_GROUP_TREE_MESSAGE, OnGroupTreeMessage) ON_COMMAND(ID_BACK_BUTTON, OnBackButton) ON_COMMAND(ID_SYSTEM_BUTTON, OnSystemButton) + ON_WM_DRAWITEM() + ON_WM_MEASUREITEM() ON_MESSAGE(CB_UPDOWN, OnUpDown) ON_MESSAGE(NM_INACTIVE_TOOLTIPWND, OnToolTipWndInactive) ON_MESSAGE(NM_SET_LIST_COUNT, OnSetListCount) @@ -2396,6 +2409,126 @@ void CQPasteWnd::OnMenuQuickpropertiesAutodelete() m_lstHeader.RefreshVisibleRows(); } +// the symbol part of a row, "..." - the same text GetDispInfo +// puts in front of the clip text +CString CQPasteWnd::GetRowSymbols(int iItem) +{ + CString cs; + + if (m_listItems[iItem].m_bDontAutoDelete) + { + cs += _T(""); + } + + if (m_listItems[iItem].m_bHasShortCut) + { + cs += _T(""); + } + + if (m_listItems[iItem].m_bIsGroup) + { + cs += _T(""); + } + + if (theApp.m_GroupID > 0) + { + if (m_listItems[iItem].m_stickyClipGroupOrder != INVALID_STICKY) + { + cs += _T(""); + } + } + else + { + if (m_listItems[iItem].m_stickyClipOrder != INVALID_STICKY) + { + cs += _T(""); + } + } + + // attached to a group + if (m_listItems[iItem].m_bHasParent) + { + cs += _T(""); + } + + if (m_listItems[iItem].m_QuickPaste.IsEmpty() == FALSE) + { + cs += _T(""); + } + + if (m_listItems[iItem].m_dateCopied != m_listItems[iItem].m_datePasted) + { + cs += ""; + } + + return cs; +} + +// the pin button of a row was clicked, toggle it +void CQPasteWnd::TogglePin(int nRow) +{ + bool bPinned = false; + + { + ATL::CCritSecLock csLock(m_CritSection.m_sect); + + if (nRow < 0 || nRow >= (int)m_listItems.size()) + { + return; + } + + bPinned = m_listItems[nRow].m_bDontAutoDelete; + } + + SetClipPinned(nRow, bPinned == false); +} + +// set or clear the "never auto delete" flag of a single row (the pin button) +void CQPasteWnd::SetClipPinned(int nRow, bool bPinned) +{ + int nID = -1; + + { + ATL::CCritSecLock csLock(m_CritSection.m_sect); + + if (nRow < 0 || nRow >= (int)m_listItems.size()) + { + return; + } + + nID = m_listItems[nRow].m_lID; + } + + if (nID <= 0) + { + return; + } + + try + { + if (bPinned) + { + theApp.m_db.execDMLEx(_T("UPDATE Main SET lDontAutoDelete = %d where lID = %d;"), (int)CTime::GetCurrentTime().GetTime(), nID); + } + else + { + theApp.m_db.execDMLEx(_T("UPDATE Main SET lDontAutoDelete = 0 where lID = %d;"), nID); + } + } + CATCH_SQLITE_EXCEPTION + + { + ATL::CCritSecLock csLock(m_CritSection.m_sect); + + if (nRow < (int)m_listItems.size()) + { + m_listItems[nRow].m_bDontAutoDelete = bPinned; + } + } + + m_lstHeader.RefreshRow(nRow); +} + void CQPasteWnd::OnMenuQuickpropertiesRemovehotkey() { CWaitCursor wait; @@ -3045,6 +3178,31 @@ void CQPasteWnd::SetKeyModiferState(bool bActive) BOOL CQPasteWnd::PreTranslateMessage(MSG* pMsg) { + if (pMsg->message == WM_LBUTTONDOWN || pMsg->message == WM_LBUTTONUP) + { + // a click on the pin button of a row pins/unpins the row, the action + // that is configured for clicking a clip must not run for it + if (pMsg->message == WM_LBUTTONDOWN && pMsg->hwnd == m_lstHeader.m_hWnd) + { + CPoint point(pMsg->pt); + m_lstHeader.ScreenToClient(&point); + + int nPinRow = -1; + if (m_lstHeader.HitTestPinButton(point, nPinRow)) + { + TogglePin(nPinRow); + m_bSwallowPinUp = TRUE; + return TRUE; + } + } + else if (pMsg->message == WM_LBUTTONUP && m_bSwallowPinUp) + { + // the button up that belongs to the swallowed pin down + m_bSwallowPinUp = FALSE; + return TRUE; + } + } + switch (pMsg->message) { case WM_MBUTTONUP: @@ -5690,52 +5848,7 @@ void CQPasteWnd::GetDispInfo(NMHDR* pNMHDR, LRESULT* pResult) if ((int)m_listItems.size() > pItem->iItem && m_listItems[pItem->iItem].m_lID > 0) { - CString cs; - if (m_listItems[pItem->iItem].m_bDontAutoDelete) - { - cs += _T(""); - } - - if (m_listItems[pItem->iItem].m_bHasShortCut) - { - cs += _T(""); - } - - if (m_listItems[pItem->iItem].m_bIsGroup) - { - cs += _T(""); - } - - if (theApp.m_GroupID > 0) - { - if (m_listItems[pItem->iItem].m_stickyClipGroupOrder != INVALID_STICKY) - { - cs += _T(""); - } - } - else - { - if (m_listItems[pItem->iItem].m_stickyClipOrder != INVALID_STICKY) - { - cs += _T(""); - } - } - - // attached to a group - if (m_listItems[pItem->iItem].m_bHasParent) - { - cs += _T(""); - } - - if (m_listItems[pItem->iItem].m_QuickPaste.IsEmpty() == FALSE) - { - cs += _T(""); - } - - if (m_listItems[pItem->iItem].m_dateCopied != m_listItems[pItem->iItem].m_datePasted) - { - cs += ""; - } + CString cs = GetRowSymbols(pItem->iItem); // pipe is the "end of symbols" marker cs += "|" + CMainTableFunctions::GetDisplayText(CGetSetOptions::m_nLinesPerRow, m_listItems[pItem->iItem].m_Desc); @@ -7278,10 +7391,116 @@ void CQPasteWnd::OnSystemButton() SetMenuChecks(cmSubMenu); + // the entry that throws clips away for good is drawn in red. windows + // draws menus itself and cannot color one entry, so that entry is made + // owner draw and painted in OnDrawItem/OnMeasureItem + int nDeleteAllPos = -1; + for (int i = 0; i < ::GetMenuItemCount(cmSubMenu->GetSafeHmenu()); i++) + { + if (::GetMenuItemID(cmSubMenu->GetSafeHmenu(), i) == ID_MENU_DELETEALLNONUSEDCLIPS) + { + nDeleteAllPos = i; + break; + } + } + + if (nDeleteAllPos >= 0) + { + TCHAR szDeleteAllText[128] = { 0 }; + ::GetMenuString(cmSubMenu->GetSafeHmenu(), nDeleteAllPos, szDeleteAllText, _countof(szDeleteAllText), MF_BYPOSITION); + m_strDeleteAllClipsText = szDeleteAllText; + + MENUITEMINFO mii = { 0 }; + mii.cbSize = sizeof(MENUITEMINFO); + mii.fMask = MIIM_FTYPE | MIIM_DATA; + mii.fType = MFT_OWNERDRAW; + mii.dwItemData = ID_MENU_DELETEALLNONUSEDCLIPS; + ::SetMenuItemInfo(cmSubMenu->GetSafeHmenu(), nDeleteAllPos, TRUE, &mii); + } + cmSubMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_TOPALIGN | TPM_RIGHTBUTTON, pp.x, pp.y, this, NULL); } } +void CQPasteWnd::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct) +{ + if (lpMeasureItemStruct->CtlType == ODT_MENU && + lpMeasureItemStruct->itemID == ID_MENU_DELETEALLNONUSEDCLIPS) + { + // keep the size of the entries windows draws itself. the text is + // translated, so the width comes from the text and not from a constant + lpMeasureItemStruct->itemHeight = m_DittoWindow.m_dpi.Scale(MENU_ITEM_HEIGHT); + lpMeasureItemStruct->itemWidth = m_DittoWindow.m_dpi.Scale(MENU_ITEM_TEXT_LEFT + MENU_ITEM_RIGHT_MARGIN); + + NONCLIENTMETRICS ncm; + memset(&ncm, 0, sizeof(ncm)); + ncm.cbSize = sizeof(ncm); + if (SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)) + { + CDC* pDC = GetDC(); + if (pDC != NULL) + { + CFont font; + font.CreateFontIndirect(&ncm.lfMenuFont); + CFont* pOldFont = pDC->SelectObject(&font); + lpMeasureItemStruct->itemWidth += pDC->GetTextExtent(m_strDeleteAllClipsText).cx; + pDC->SelectObject(pOldFont); + ReleaseDC(pDC); + } + } + return; + } + + CWnd::OnMeasureItem(nIDCtl, lpMeasureItemStruct); +} + +void CQPasteWnd::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct) +{ + if (lpDrawItemStruct->CtlType != ODT_MENU || + (lpDrawItemStruct->itemID != ID_MENU_DELETEALLNONUSEDCLIPS && + lpDrawItemStruct->itemData != ID_MENU_DELETEALLNONUSEDCLIPS)) + { + CWnd::OnDrawItem(nIDCtl, lpDrawItemStruct); + return; + } + + CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); + CRect rcItem(lpDrawItemStruct->rcItem); + const bool bSelected = (lpDrawItemStruct->itemState & ODS_SELECTED) != 0; + const bool bDisabled = (lpDrawItemStruct->itemState & (ODS_DISABLED | ODS_GRAYED)) != 0; + + pDC->FillSolidRect(rcItem, bSelected ? ::GetSysColor(COLOR_HIGHLIGHT) : ::GetSysColor(COLOR_MENU)); + + // the font windows uses for its own menu entries + NONCLIENTMETRICS ncm; + memset(&ncm, 0, sizeof(ncm)); + ncm.cbSize = sizeof(ncm); + if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0)) + { + return; + } + + CFont font; + font.CreateFontIndirect(&ncm.lfMenuFont); + CFont* pOldFont = pDC->SelectObject(&font); + + CRect rcText(rcItem); + rcText.left += m_DittoWindow.m_dpi.Scale(MENU_ITEM_TEXT_LEFT); + + pDC->SetBkMode(TRANSPARENT); + if (bDisabled) + { + pDC->SetTextColor(::GetSysColor(COLOR_GRAYTEXT)); + } + else + { + pDC->SetTextColor(bSelected ? ::GetSysColor(COLOR_HIGHLIGHTTEXT) : MENU_DANGER_TEXT_COLOR); + } + pDC->DrawText(m_strDeleteAllClipsText, &rcText, DT_SINGLELINE | DT_VCENTER | DT_NOPREFIX); + + pDC->SelectObject(pOldFont); +} + void CQPasteWnd::OnSpecialpasteRemovelinefeeds() { DoAction(ActionEnums::PASTE_REMOVE_LINE_FEEDS); diff --git a/src/QPasteWnd.h b/src/QPasteWnd.h index 2a42b5ec..af3696d6 100644 --- a/src/QPasteWnd.h +++ b/src/QPasteWnd.h @@ -178,6 +178,8 @@ class CQPasteWnd: public CWndEx __int64 m_lastDbWrite; bool m_pendingRefresh; DWORD m_lastNonActiveMouseMove; + bool m_bSwallowPinUp; + CString m_strDeleteAllClipsText; void RefreshNc(); void UpdateStatus(bool bRepaintImmediately = false); // regenerates the status (caption) text @@ -347,6 +349,11 @@ class CQPasteWnd: public CWndEx void SetCurrentTransparency(); + // pin (never auto delete) helpers + CString GetRowSymbols(int iItem); + void SetClipPinned(int nRow, bool bPinned); + void TogglePin(int nRow); + // Generated message map functions protected: //{{AFX_MSG(CQPasteWnd) @@ -460,6 +467,8 @@ class CQPasteWnd: public CWndEx afx_msg void OnMenuNewGroupSelection(); afx_msg void OnBackButton(); afx_msg void OnSystemButton(); + afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct); + afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct); afx_msg LRESULT OnUpDown(WPARAM wParam, LPARAM lParam); afx_msg LRESULT OnItemDeleted(WPARAM wParam, LPARAM lParam); LRESULT OnToolTipWndInactive(WPARAM wParam, LPARAM lParam);