From 92e6e8eaeffd4f7b615b2c916023d017ac6398e9 Mon Sep 17 00:00:00 2001 From: Sven Strickroth Date: Fri, 16 Dec 2011 02:20:14 +0100 Subject: [PATCH] replace own balloon implementation with mfc one Signed-off-by: Sven Strickroth --- src/TortoiseGitBlame/TortoiseGitBlameView.cpp | 17 +- src/TortoiseGitBlame/TortoiseGitBlameView.h | 4 +- src/TortoiseProc/BranchCombox.h | 6 +- src/TortoiseProc/CommitDlg.cpp | 2 +- src/TortoiseProc/ExportDlg.cpp | 2 +- src/TortoiseProc/ImportPatchDlg.h | 1 - src/TortoiseProc/RebaseDlg.h | 4 +- src/TortoiseProc/Settings/SetBugTraqAdv.cpp | 13 +- src/TortoiseProc/Settings/SetBugTraqAdv.h | 5 +- src/TortoiseProc/Settings/SetHooksAdv.cpp | 15 +- src/TortoiseProc/Settings/SetHooksAdv.h | 5 +- src/TortoiseProc/SyncDlg.cpp | 1 + src/TortoiseProc/SyncDlg.h | 3 +- src/Utils/MiscUI/Balloon.cpp | 1504 ------------------------- src/Utils/MiscUI/Balloon.h | 624 ---------- src/Utils/MiscUI/StandAloneDlg.h | 18 +- src/Utils/MiscUI/Tooltip.cpp | 128 ++- src/Utils/MiscUI/Tooltip.h | 33 +- src/Utils/MiscUI/balloon_box.jpg | Bin 20561 -> 0 bytes src/Utils/MiscUI/balloon_tooltip.jpg | Bin 32181 -> 0 bytes src/Utils/Utils.vcproj | 4 - 21 files changed, 187 insertions(+), 2202 deletions(-) delete mode 100644 src/Utils/MiscUI/Balloon.cpp delete mode 100644 src/Utils/MiscUI/Balloon.h delete mode 100644 src/Utils/MiscUI/balloon_box.jpg delete mode 100644 src/Utils/MiscUI/balloon_tooltip.jpg diff --git a/src/TortoiseGitBlame/TortoiseGitBlameView.cpp b/src/TortoiseGitBlame/TortoiseGitBlameView.cpp index d61f617fc..10beda19b 100644 --- a/src/TortoiseGitBlame/TortoiseGitBlameView.cpp +++ b/src/TortoiseGitBlame/TortoiseGitBlameView.cpp @@ -29,7 +29,6 @@ #include "TortoiseGitBlameDoc.h" #include "TortoiseGitBlameView.h" #include "MainFrm.h" -#include "Balloon.h" #include "EditGotoDlg.h" #include "TortoiseGitBlameAppUtils.h" #include "FileTextLines.h" @@ -239,7 +238,6 @@ int CTortoiseGitBlameView::OnCreate(LPCREATESTRUCT lpcs) CreateFont(); InitialiseEditor(); m_ToolTip.Create(this->GetParent()); - m_ToolTip.AddTool(this,_T("Test")); ::AfxGetApp()->GetMainWnd(); return CView::OnCreate(lpcs); @@ -2757,7 +2755,6 @@ void CTortoiseGitBlameView::FocusOn(GitRev *pRev) void CTortoiseGitBlameView::OnMouseHover(UINT nFlags, CPoint point) { - LONG_PTR line = SendEditor(SCI_GETFIRSTVISIBLELINE); LONG_PTR height = SendEditor(SCI_TEXTHEIGHT); line = line + (point.y/height); @@ -2778,29 +2775,21 @@ void CTortoiseGitBlameView::OnMouseHover(UINT nFlags, CPoint point) pRev=&this->GetLogData()->GetGitRevAt(this->GetLogList()->GetItemCount()-m_ID[line]); } - this->ClientToScreen(&point); - CString str; - str.Format(_T("%s\n%s\n%s %s\n%s"),pRev->m_CommitHash.ToString(), + str.Format(_T("%s\n%s\n%s %s\n%s"),pRev->m_CommitHash.ToString(), pRev->GetSubject(), pRev->GetAuthorName(), CAppUtils::FormatDateAndTime( pRev->GetAuthorDate(), m_DateFormat ), pRev->GetBody()); - m_ToolTip.AddTool(this,str); - m_ToolTip.DisplayToolTip(&point); + m_ToolTip.Pop(); + m_ToolTip.AddTool(this, str); CRect rect; - this->ScreenToClient(&point); rect.left=LOCATOR_WIDTH; rect.right=this->m_blamewidth+rect.left; rect.top=point.y-height; rect.bottom=point.y+height; this->InvalidateRect(rect); - - } - else - { - m_MouseLine=-1; } } } diff --git a/src/TortoiseGitBlame/TortoiseGitBlameView.h b/src/TortoiseGitBlame/TortoiseGitBlameView.h index 6d63d106c..7f9b0ae62 100644 --- a/src/TortoiseGitBlame/TortoiseGitBlameView.h +++ b/src/TortoiseGitBlame/TortoiseGitBlameView.h @@ -31,7 +31,7 @@ #include "SciEdit.h" #include "GitBlameLogList.h" -#include "Balloon.h" +#include "Tooltip.h" const COLORREF black = RGB(0,0,0); const COLORREF white = RGB(0xff,0xff,0xff); @@ -160,7 +160,7 @@ public: void FocusOn(GitRev *pRev); CSciEditBlame m_TextView; - CBalloon m_ToolTip; + CToolTips m_ToolTip; HINSTANCE hInstance; HINSTANCE hResource; diff --git a/src/TortoiseProc/BranchCombox.h b/src/TortoiseProc/BranchCombox.h index a19ca1551..64af286a4 100644 --- a/src/TortoiseProc/BranchCombox.h +++ b/src/TortoiseProc/BranchCombox.h @@ -3,10 +3,10 @@ #include "LogDlg.h" #include "BrowseRefsDlg.h" #include "HistoryCombo.h" -#include "Balloon.h" #include "HistoryCombo.h" #include "AppUtils.h" #include "UnicodeUtils.h" +#include "Tooltip.h" class CBranchCombox { @@ -24,7 +24,7 @@ protected: int m_LocalBranchFilter; int m_RemoteBranchFilter; - CBalloon *m_pTooltip; + CToolTips *m_pTooltip; CString m_DialogName; @@ -121,7 +121,7 @@ protected: virtual void RemoteBranchChange(){}; virtual void SetRemote(CString remote){}; - void AddBranchToolTips(CHistoryCombo *pBranch,CBalloon *tip) + void AddBranchToolTips(CHistoryCombo *pBranch, CToolTips *tip) { if(pBranch&&tip) { diff --git a/src/TortoiseProc/CommitDlg.cpp b/src/TortoiseProc/CommitDlg.cpp index b02c5452a..24b82872d 100644 --- a/src/TortoiseProc/CommitDlg.cpp +++ b/src/TortoiseProc/CommitDlg.cpp @@ -409,7 +409,7 @@ void CCommitDlg::OnOK() GetDlgItemText(IDC_BUGID, id); if (!m_ProjectProperties.CheckBugID(id)) { - ShowBalloon(IDC_BUGID, IDS_COMMITDLG_ONLYNUMBERS, IDI_EXCLAMATION); + ShowEditBalloon(IDC_BUGID, IDS_COMMITDLG_ONLYNUMBERS, TTI_ERROR); return; } m_sLogMessage = m_cLogMessage.GetText(); diff --git a/src/TortoiseProc/ExportDlg.cpp b/src/TortoiseProc/ExportDlg.cpp index 12060da16..94e3b9a5d 100644 --- a/src/TortoiseProc/ExportDlg.cpp +++ b/src/TortoiseProc/ExportDlg.cpp @@ -120,7 +120,7 @@ void CExportDlg::OnOK() if (m_VersionName.IsEmpty()) { - ShowBalloon(IDC_COMBOBOXEX_VERSION, IDS_ERR_INVALIDREV); + m_tooltips.ShowBalloon(IDC_COMBOBOXEX_VERSION, IDS_ERR_INVALIDREV, IDS_ERR_ERROR, TTI_ERROR); return; } diff --git a/src/TortoiseProc/ImportPatchDlg.h b/src/TortoiseProc/ImportPatchDlg.h index e2be4f85a..b68deeaa8 100644 --- a/src/TortoiseProc/ImportPatchDlg.h +++ b/src/TortoiseProc/ImportPatchDlg.h @@ -26,7 +26,6 @@ #include "SciEdit.h" #include "SplitterControl.h" #include "HistoryCombo.h" -#include "Balloon.h" #include "GitLogList.h" #include "MenuButton.h" #include "Win7.h" diff --git a/src/TortoiseProc/RebaseDlg.h b/src/TortoiseProc/RebaseDlg.h index f1722af07..32ab42845 100644 --- a/src/TortoiseProc/RebaseDlg.h +++ b/src/TortoiseProc/RebaseDlg.h @@ -25,10 +25,10 @@ #include "SciEdit.h" #include "SplitterControl.h" #include "HistoryCombo.h" -#include "Balloon.h" #include "GitLogList.h" #include "MenuButton.h" #include "Win7.h" +#include "Tooltip.h" // CRebaseDlg dialog #define IDC_REBASE_TAB 0x1000000 @@ -131,7 +131,7 @@ protected: CProgressCtrl m_ProgressBar; CStatic m_CtrlStatusText; - CBalloon m_tooltips; + CToolTips m_tooltips; CString m_PreCmd; diff --git a/src/TortoiseProc/Settings/SetBugTraqAdv.cpp b/src/TortoiseProc/Settings/SetBugTraqAdv.cpp index f373c937f..3cf9cf32a 100644 --- a/src/TortoiseProc/Settings/SetBugTraqAdv.cpp +++ b/src/TortoiseProc/Settings/SetBugTraqAdv.cpp @@ -1,6 +1,6 @@ // TortoiseGit - a Windows shell extension for easy version control -// Copyright (C) 2008 - TortoiseSVN +// Copyright (C) 2008,2010 - TortoiseSVN // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -91,6 +91,7 @@ BOOL CSetBugTraqAdv::OnInitDialog() } } + m_tooltips.Create(this); UpdateData(FALSE); CheckHasOptions(); @@ -135,14 +136,14 @@ void CSetBugTraqAdv::OnOK() if (FAILED(hr)) { - ShowBalloon(IDC_BUGTRAQPROVIDERCOMBO, IDS_ERR_MISSING_PROVIDER); + m_tooltips.ShowBalloon(IDC_BUGTRAQPROVIDERCOMBO, IDS_ERR_MISSING_PROVIDER, IDS_ERR_ERROR, TTI_ERROR); return; } VARIANT_BOOL valid; if (FAILED(hr = pProvider->ValidateParameters(GetSafeHwnd(), m_sParameters.AllocSysString(), &valid))) { - ShowBalloon(IDC_BUGTRAQPARAMETERS, IDS_ERR_PROVIDER_VALIDATE_FAILED); + ShowEditBalloon(IDC_BUGTRAQPARAMETERS, IDS_ERR_PROVIDER_VALIDATE_FAILED, IDS_ERR_ERROR, TTI_ERROR); return; } @@ -237,3 +238,9 @@ void CSetBugTraqAdv::OnBnClickedOptions() SysFreeString(temp); } } + +BOOL CSetBugTraqAdv::PreTranslateMessage(MSG* pMsg) +{ + m_tooltips.RelayEvent(pMsg); + return CResizableStandAloneDialog::PreTranslateMessage(pMsg); +} diff --git a/src/TortoiseProc/Settings/SetBugTraqAdv.h b/src/TortoiseProc/Settings/SetBugTraqAdv.h index 1b6e8ad6b..a2354aa4d 100644 --- a/src/TortoiseProc/Settings/SetBugTraqAdv.h +++ b/src/TortoiseProc/Settings/SetBugTraqAdv.h @@ -1,6 +1,6 @@ // TortoiseGit - a Windows shell extension for easy version control -// Copyright (C) 2008 - TortoiseSVN +// Copyright (C) 2008,2010 - TortoiseSVN // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -19,6 +19,7 @@ #pragma once #include "resource.h" #include "StandAloneDlg.h" +#include "Tooltip.h" class CBugTraqAssociation; @@ -43,6 +44,7 @@ public: protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support virtual BOOL OnInitDialog(); + virtual BOOL PreTranslateMessage(MSG* pMsg); virtual void OnOK(); afx_msg void OnDestroy(); afx_msg void OnBnClickedBugTraqbrowse(); @@ -58,4 +60,5 @@ protected: CLSID m_provider_clsid; CString m_sParameters; CComboBox m_cProviderCombo; + CToolTips m_tooltips; }; diff --git a/src/TortoiseProc/Settings/SetHooksAdv.cpp b/src/TortoiseProc/Settings/SetHooksAdv.cpp index 2f81a6b0a..ff55eaa44 100644 --- a/src/TortoiseProc/Settings/SetHooksAdv.cpp +++ b/src/TortoiseProc/Settings/SetHooksAdv.cpp @@ -1,6 +1,6 @@ // TortoiseGit - a Windows shell extension for easy version control -// Copyright (C) 2003-2008 - TortoiseSVN +// Copyright (C) 2003-2008,2010 - TortoiseSVN // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -97,6 +97,7 @@ BOOL CSetHooksAdv::OnInitDialog() m_sCommandLine = cmd.commandline; m_bWait = cmd.bWait; m_bHide = !cmd.bShow; + m_tooltips.Create(this); UpdateData(FALSE); AddAnchor(IDC_HOOKTYPELABEL, TOP_LEFT, TOP_RIGHT); @@ -132,17 +133,17 @@ void CSetHooksAdv::OnOK() } if (key.htype == unknown_hook) { - ShowBalloon(IDC_HOOKTYPECOMBO, IDS_ERR_NOHOOKTYPESPECIFIED); + m_tooltips.ShowBalloon(IDC_HOOKTYPECOMBO, IDS_ERR_NOHOOKTYPESPECIFIED, IDS_ERR_ERROR, TTI_ERROR); return; } if (key.path.IsEmpty()) { - ShowBalloon(IDC_HOOKPATH, IDS_ERR_NOHOOKPATHSPECIFIED); + ShowEditBalloon(IDC_HOOKPATH, IDS_ERR_NOHOOKPATHSPECIFIED, IDS_ERR_ERROR, TTI_ERROR); return; } if (cmd.commandline.IsEmpty()) { - ShowBalloon(IDC_HOOKCOMMANDLINE, IDS_ERR_NOHOOKCOMMANDPECIFIED); + ShowEditBalloon(IDC_HOOKCOMMANDLINE, IDS_ERR_NOHOOKCOMMANDPECIFIED, IDS_ERR_ERROR, TTI_ERROR); return; } @@ -183,3 +184,9 @@ void CSetHooksAdv::OnBnClickedHelp() { OnHelp(); } + +BOOL CSetHooksAdv::PreTranslateMessage(MSG* pMsg) +{ + m_tooltips.RelayEvent(pMsg); + return CResizableStandAloneDialog::PreTranslateMessage(pMsg); +} diff --git a/src/TortoiseProc/Settings/SetHooksAdv.h b/src/TortoiseProc/Settings/SetHooksAdv.h index 751389cfe..f6d85ad03 100644 --- a/src/TortoiseProc/Settings/SetHooksAdv.h +++ b/src/TortoiseProc/Settings/SetHooksAdv.h @@ -1,6 +1,6 @@ // TortoiseGit - a Windows shell extension for easy version control -// Copyright (C) 2003-2008 - TortoiseSVN +// Copyright (C) 2003-2008,2010 - TortoiseSVN // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -19,6 +19,7 @@ #pragma once #include "Hooks.h" #include "StandAloneDlg.h" +#include "Tooltip.h" /** * \ingroup TortoiseProc @@ -41,6 +42,7 @@ public: protected: virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support virtual BOOL OnInitDialog(); + virtual BOOL PreTranslateMessage(MSG* pMsg); virtual void OnOK(); afx_msg void OnBnClickedHookbrowse(); afx_msg void OnBnClickedHookcommandbrowse(); @@ -53,4 +55,5 @@ protected: BOOL m_bWait; BOOL m_bHide; CComboBox m_cHookTypeCombo; + CToolTips m_tooltips; }; diff --git a/src/TortoiseProc/SyncDlg.cpp b/src/TortoiseProc/SyncDlg.cpp index 8d2333ec7..fb26aa5cb 100644 --- a/src/TortoiseProc/SyncDlg.cpp +++ b/src/TortoiseProc/SyncDlg.cpp @@ -747,6 +747,7 @@ BOOL CSyncDlg::OnInitDialog() this->m_regSubmoduleButton = CRegDWORD(regkey+_T("\\Submodule")); this->m_regAutoLoadPutty = CRegDWORD(regkey + _T("\\AutoLoadPutty"), CAppUtils::IsSSHPutty()); + m_tooltips.Create(this); this->UpdateData(); this->m_bAutoLoadPuttyKey = m_regAutoLoadPutty; if(!CAppUtils::IsSSHPutty()) diff --git a/src/TortoiseProc/SyncDlg.h b/src/TortoiseProc/SyncDlg.h index edb60d045..413bfda11 100644 --- a/src/TortoiseProc/SyncDlg.h +++ b/src/TortoiseProc/SyncDlg.h @@ -25,7 +25,6 @@ #include "HistoryCombo.h" #include "MenuButton.h" #include "registry.h" -#include "Balloon.h" #include "BranchCombox.h" #include "GitLoglist.h" #include "Win7.h" @@ -86,7 +85,7 @@ protected: CMFCTabCtrl m_ctrlTabCtrl; - CBalloon m_tooltips; + CToolTips m_tooltips; BOOL m_bInited; diff --git a/src/Utils/MiscUI/Balloon.cpp b/src/Utils/MiscUI/Balloon.cpp deleted file mode 100644 index 5796e6302..000000000 --- a/src/Utils/MiscUI/Balloon.cpp +++ /dev/null @@ -1,1504 +0,0 @@ -// TortoiseGit - a Windows shell extension for easy version control - -// Copyright (C) 2003-2008 - TortoiseSVN - -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. - -// This program 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 General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// -#include "stdafx.h" -#include "Balloon.h" - -tagBALLOON_INFO::tagBALLOON_INFO() - : hIcon(NULL), - sBalloonTip(), - nMask(0), - nStyles(0), - nDirection(0), - nEffect(0), - nBehaviour(0), - crBegin(0), - crMid(0), - crEnd(0) -{ -} - -CBalloon::CBalloon() - : m_nStyles (0) -{ - m_pParentWnd = NULL; - m_hCurrentWnd = NULL; - m_hDisplayedWnd = NULL; - - m_rgnShadow.CreateRectRgn(0, 0, 1, 1); - m_rgnBalloon.CreateRectRgn(0, 0, 1, 1); - - m_ptOriginal.x = -1; - m_ptOriginal.y = -1; - - SetDelayTime(TTDT_INITIAL, 500); - SetDelayTime(TTDT_AUTOPOP, 30000); - SetNotify(FALSE); - SetDirection(); - SetBehaviour(); - SetDefaultStyles(); - SetDefaultColors(); - SetDefaultSizes(); - SetEffectBk(BALLOON_EFFECT_SOLID); - RemoveAllTools(); - m_bButtonPushed = FALSE; - - // Register the window class if it has not already been registered. - WNDCLASS wndcls; - HINSTANCE hInst = AfxGetInstanceHandle(); - if(!(::GetClassInfo(hInst, BALLOON_CLASSNAME, &wndcls))) - { - // otherwise we need to register a new class - wndcls.style = CS_SAVEBITS; - wndcls.lpfnWndProc = ::DefWindowProc; - wndcls.cbClsExtra = wndcls.cbWndExtra = 0; - wndcls.hInstance = hInst; - wndcls.hIcon = NULL; - wndcls.hCursor = LoadCursor(hInst, IDC_ARROW ); - wndcls.hbrBackground = NULL; - wndcls.lpszMenuName = NULL; - wndcls.lpszClassName = BALLOON_CLASSNAME; - - if (!AfxRegisterClass(&wndcls)) - AfxThrowResourceException(); - } -} - -CBalloon::~CBalloon() -{ - RemoveAllTools(); - - m_rgnBalloon.DeleteObject(); - m_rgnShadow.DeleteObject(); - - if (IsWindow(m_hWnd)) - DestroyWindow(); -} - - -BEGIN_MESSAGE_MAP(CBalloon, CWnd) - //{{AFX_MSG_MAP(CBalloon) - ON_WM_PAINT() - ON_WM_TIMER() - ON_WM_DESTROY() - ON_WM_KILLFOCUS() - //}}AFX_MSG_MAP - ON_WM_MOUSEMOVE() - ON_WM_LBUTTONDOWN() - ON_WM_LBUTTONUP() -END_MESSAGE_MAP() - - -///////////////////////////////////////////////////////////////////////////// -// CBalloon message handlers - -BOOL CBalloon::Create(CWnd* pParentWnd) -{ - DWORD dwStyle = WS_POPUP; - DWORD dwExStyle = WS_EX_TOOLWINDOW | WS_EX_TOPMOST; - - m_pParentWnd = pParentWnd; - - if (!CreateEx(dwExStyle, BALLOON_CLASSNAME, NULL, dwStyle, 0, 0, 0, 0, m_pParentWnd->GetSafeHwnd(), NULL, NULL)) - { - return FALSE; - } - SetDefaultFont(); - - return TRUE; -} - -void CBalloon::OnDestroy() -{ - TRACE("OnDestroy()\n"); - KillTimers(); - - CWnd::OnDestroy(); -} - - - -void CBalloon::OnKillFocus(CWnd* pNewWnd) -{ - CWnd::OnKillFocus(pNewWnd); - Pop(); -} - - -BOOL CBalloon::PreTranslateMessage(MSG* pMsg) -{ - RelayEvent(pMsg); - - return CWnd::PreTranslateMessage(pMsg); -} - -LRESULT CBalloon::SendNotify(CWnd * pWnd, CPoint * pt, BALLOON_INFO & bi) -{ - //make sure this is a valid window - if (!IsWindow(GetSafeHwnd())) - return 0L; - - //see if the user wants to be notified - if (!GetNotify()) - return 0L; - - NM_BALLOON_DISPLAY lpnm; - - lpnm.pWnd = pWnd; - lpnm.pt = pt; - lpnm.bi = &bi; - lpnm.hdr.hwndFrom = m_hWnd; - lpnm.hdr.idFrom = GetDlgCtrlID(); - lpnm.hdr.code = UDM_TOOLTIP_DISPLAY; - - ::SendMessage(m_hNotifyWnd, WM_NOTIFY, lpnm.hdr.idFrom, (LPARAM)&lpnm); - - return 0L; -} - -void CBalloon::OnPaint() -{ - //if (!m_pCurrentWnd) - // return; - - m_hDisplayedWnd = m_hCurrentWnd; - - CPaintDC dc(this); // device context for painting - - CRect rect; - GetClientRect(&rect); - rect.DeflateRect(0, 0, 1, 1); - - //create a memory device-context. This is done to help reduce - //screen flicker, since we will paint the entire control to the - //off screen device context first.CDC memDC; - CDC memDC; - CBitmap bitmap; - memDC.CreateCompatibleDC(&dc); - bitmap.CreateCompatibleBitmap(&dc, rect.Width(), rect.Height()); - CBitmap* pOldBitmap = memDC.SelectObject(&bitmap); - - memDC.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &dc, 0, 0, SRCCOPY); - - //draw the tooltip - OnDraw(&memDC, rect); - - //Copy the memory device context back into the original DC. - dc.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(), &memDC, 0,0, SRCCOPY); - - //Cleanup resources. - memDC.SelectObject(pOldBitmap); - memDC.DeleteDC(); - bitmap.DeleteObject(); -} - -void CBalloon::OnDraw(CDC * pDC, CRect rect) -{ - CBrush brBackground(m_crColor [BALLOON_COLOR_BK_BEGIN]); - CBrush brShadow(m_crColor [BALLOON_COLOR_SHADOW]); - CBrush brBorder(m_crColor [BALLOON_COLOR_BORDER]); - - pDC->SetBkMode(TRANSPARENT); - pDC->SetTextColor(m_crColor [BALLOON_COLOR_FG]); - //set clip region of the tooltip and draw the shadow if needed - if (m_pToolInfo.nStyles & BALLOON_SHADOW) - { - //draw the shadow for the tooltip - int nRop2Mode = pDC->SetROP2(R2_MASKPEN); - pDC->FillRgn(&m_rgnShadow, &brShadow); - pDC->SetROP2(nRop2Mode); - rect.DeflateRect(0, 0, m_nSizes[XBLSZ_SHADOW_CX], m_nSizes[XBLSZ_SHADOW_CY]); - } - pDC->SelectClipRgn(&m_rgnBalloon); - - OnDrawBackground(pDC, &rect); - - //draw the main region's border of the tooltip - pDC->FrameRgn(&m_rgnBalloon, &brBorder, m_nSizes[XBLSZ_BORDER_CX], m_nSizes[XBLSZ_BORDER_CY]); - - if ((m_nLastDirection == BALLOON_RIGHT_BOTTOM) || (m_nLastDirection == BALLOON_LEFT_BOTTOM)) - rect.top += m_nSizes[XBLSZ_HEIGHT_ANCHOR]; - else - rect.bottom -= m_nSizes[XBLSZ_HEIGHT_ANCHOR]; - - if (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON) - { - m_rtCloseButton = CRect( - rect.right - m_szCloseButton.cx - m_nSizes[XBLSZ_BUTTON_MARGIN_CX] , rect.top + m_nSizes[XBLSZ_BUTTON_MARGIN_CY], - rect.right - m_nSizes[XBLSZ_BUTTON_MARGIN_CX], rect.top + m_szCloseButton.cy + m_nSizes[XBLSZ_BUTTON_MARGIN_CY]); - pDC->DrawFrameControl(m_rtCloseButton, DFC_CAPTION, DFCS_CAPTIONCLOSE|DFCS_FLAT|DFCS_TRANSPARENT); - rect.right -= (m_szCloseButton.cx + m_nSizes[XBLSZ_BUTTON_MARGIN_CX]); - } - - //get the rectangle to draw the tooltip text - rect.DeflateRect(m_nSizes[XBLSZ_MARGIN_CX], m_nSizes[XBLSZ_MARGIN_CY]); - - //draw the icon - if (m_pToolInfo.hIcon != NULL) - { - DrawIconEx(pDC->m_hDC, m_nSizes[XBLSZ_MARGIN_CX], rect.top + (rect.Height() - m_szBalloonIcon.cy) / 2, - m_pToolInfo.hIcon, m_szBalloonIcon.cx, m_szBalloonIcon.cy, 0, NULL, DI_NORMAL); - - rect.left += m_szBalloonIcon.cx + m_nSizes[XBLSZ_MARGIN_CX]; - } - - - //aligns tool tip's text - if (m_pToolInfo.nStyles & BALLOON_BOTTOM_ALIGN) - rect.top = rect.bottom - m_szBalloonText.cy; - else if (m_pToolInfo.nStyles & BALLOON_VCENTER_ALIGN) - rect.top += (rect.Height() - m_szBalloonText.cy) / 2; - - //prints the tool tip's text - DrawHTML(pDC, rect, m_pToolInfo.sBalloonTip, m_LogFont, FALSE); - - //free resources - brBackground.DeleteObject(); - brShadow.DeleteObject(); - brBorder.DeleteObject(); -} - -void CBalloon::OnDrawBackground(CDC * pDC, CRect * pRect) -{ -#ifdef USE_GDI_GRADIENT - #define DRAW CGradient::DrawGDI -#else - #define DRAW CGradient::Draw -#endif - switch (m_pToolInfo.nEffect) - { - case BALLOON_EFFECT_HGRADIENT: - DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END]); - break; - case BALLOON_EFFECT_VGRADIENT: - DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END], FALSE); - break; - case BALLOON_EFFECT_HCGRADIENT: - DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END], m_crColor[BALLOON_COLOR_BK_BEGIN]); - break; - case BALLOON_EFFECT_VCGRADIENT: - DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_END], m_crColor[BALLOON_COLOR_BK_BEGIN], FALSE); - break; - case BALLOON_EFFECT_3HGRADIENT: - DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_MID], m_crColor[BALLOON_COLOR_BK_END]); - break; - case BALLOON_EFFECT_3VGRADIENT: - DRAW(pDC, pRect, m_crColor[BALLOON_COLOR_BK_BEGIN], m_crColor[BALLOON_COLOR_BK_MID], m_crColor[BALLOON_COLOR_BK_END], FALSE); - break; -#undef DRAW - default: - pDC->FillSolidRect(pRect, m_crColor[BALLOON_COLOR_BK_BEGIN]); - break; - } -} - -CRect CBalloon::GetWindowRegion(CRgn * rgn, CSize sz, CPoint pt) const -{ - CRect rect; - rect.SetRect(0, 0, sz.cx, sz.cy); - CRgn rgnRect; - CRgn rgnAnchor; - CPoint ptAnchor [3]; - ptAnchor [0] = pt; - ScreenToClient(&ptAnchor [0]); - - switch (m_nLastDirection) - { - case BALLOON_LEFT_TOP: - case BALLOON_RIGHT_TOP: - rect.bottom -= m_nSizes[XBLSZ_HEIGHT_ANCHOR]; - ptAnchor [1].y = ptAnchor [2].y = rect.bottom; - break; - case BALLOON_LEFT_BOTTOM: - case BALLOON_RIGHT_BOTTOM: - rect.top += m_nSizes[XBLSZ_HEIGHT_ANCHOR]; - ptAnchor [1].y = ptAnchor [2].y = rect.top; - break; - } - - //get the region for rectangle with the text - if (m_pToolInfo.nStyles & BALLOON_ROUNDED) - rgnRect.CreateRoundRectRgn(rect.left, rect.top, rect.right + 1, rect.bottom + 1, - m_nSizes[XBLSZ_ROUNDED_CX], m_nSizes[XBLSZ_ROUNDED_CY]); - else rgnRect.CreateRectRgn(rect.left, rect.top, rect.right + 1, rect.bottom + 1); - - //gets the region for the anchor - if (m_pToolInfo.nStyles & BALLOON_ANCHOR) - { - switch (m_nLastDirection) - { - case BALLOON_LEFT_TOP: - case BALLOON_LEFT_BOTTOM: - ptAnchor [1].x = rect.right - m_nSizes[XBLSZ_MARGIN_ANCHOR]; - ptAnchor [2].x = ptAnchor [1].x - m_nSizes[XBLSZ_WIDTH_ANCHOR]; - break; - case BALLOON_RIGHT_TOP: - case BALLOON_RIGHT_BOTTOM: - ptAnchor [1].x = rect.left + m_nSizes[XBLSZ_MARGIN_ANCHOR]; - ptAnchor [2].x = ptAnchor [1].x + m_nSizes[XBLSZ_WIDTH_ANCHOR]; - break; - } - rgnAnchor.CreatePolygonRgn(ptAnchor, 3, ALTERNATE); - } - else - rgnAnchor.CreateRectRgn(0, 0, 0, 0); - - rgn->CreateRectRgn(0, 0, 1, 1); - rgn->CombineRgn(&rgnRect, &rgnAnchor, RGN_OR); - - rgnAnchor.DeleteObject(); - rgnRect.DeleteObject(); - - return rect; -} - -void CBalloon::RelayEvent(MSG* pMsg) -{ - HWND hWnd = NULL; - CPoint pt; - CString str; - CRect rect; - - BALLOON_INFO Info; - - switch(pMsg->message) - { - case WM_MOUSEMOVE: - if (m_ptOriginal == pMsg->pt) - return; - - m_ptOriginal = pMsg->pt; - - //get the real window under the mouse pointer - pt = pMsg->pt; - if (m_pParentWnd) - m_pParentWnd->ScreenToClient(&pt); - hWnd = GetChildWindowFromPoint(pt); - - if (!hWnd) - { - if (!(GetBehaviour() & BALLOON_DIALOG)) - { - Pop(); - m_hCurrentWnd = NULL; - m_hDisplayedWnd = NULL; - return; - } - } - else - { - UINT behaviour = GetBehaviour(CWnd::FromHandle(hWnd)); - if (hWnd == m_hDisplayedWnd) - { - if (IsWindowVisible()) - { - if ((behaviour & BALLOON_TRACK_MOUSE)&&(!(behaviour & BALLOON_DIALOG))) - { - //mouse moved, so move the tooltip too - CRect rect; - GetWindowRect(rect); - CalculateInfoBoxRect(&m_ptOriginal, &rect); - SetWindowPos(NULL, rect.left, rect.top, 0, 0, SWP_SHOWWINDOW|SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE); - } - else - return; - } - else if ((behaviour & BALLOON_MULTIPLE_SHOW)&&(!(behaviour & BALLOON_DIALOG))) - { - SetNewToolTip(CWnd::FromHandle(hWnd)); - } - else - Pop(); - } - else - { - SetNewToolTip(CWnd::FromHandle(hWnd)); - } - } - break; - } -} - -void CBalloon::SetNewToolTip(CWnd * pWnd) -{ - m_hDisplayedWnd = NULL; - Pop(); - - if (!pWnd->IsWindowEnabled()) - return; - - if (!GetTool(pWnd, m_pToolInfo)) - return; - - m_hCurrentWnd = pWnd->GetSafeHwnd(); - - SetTimer(BALLOON_SHOW, m_nTimeInitial, NULL); -} - -void CBalloon::OnTimer(UINT_PTR nIDEvent) -{ - CPoint pt, point; - CString str; - HWND hWnd; - - switch (nIDEvent) - { - case BALLOON_SHOW: - KillTimers(BALLOON_SHOW); - //check if mouse pointer is still over the right window - GetCursorPos(&pt); - point = pt; - if (m_pParentWnd) - m_pParentWnd->ScreenToClient(&point); - hWnd = GetChildWindowFromPoint(point); - if (hWnd == m_hCurrentWnd) - { - DisplayToolTip(&pt); - SetTimer(BALLOON_HIDE, m_nTimeAutoPop, NULL); - } - break; - case BALLOON_HIDE: - KillTimers(BALLOON_HIDE); - Pop(); - if (GetBehaviour() & BALLOON_DIALOG_DESTROY) - { - CWnd::OnTimer(nIDEvent); - DestroyWindow(); - return; - } - break; - } - - CWnd::OnTimer(nIDEvent); -} - -HWND CBalloon::GetChildWindowFromPoint(CPoint & point) const -{ - if (!m_pParentWnd) - return NULL; - CPoint pt = point; - m_pParentWnd->ClientToScreen(&pt); - HWND hWnd = ::WindowFromPoint(pt); - - //::WindowFromPoint misses disabled windows and such - go for a more - //comprehensive search in this case. - if (hWnd == m_pParentWnd->GetSafeHwnd()) - hWnd = m_pParentWnd->ChildWindowFromPoint(point, CWP_ALL)->GetSafeHwnd(); - - //check that we aren't over the parent or out of client area - if (!hWnd || hWnd == m_pParentWnd->GetSafeHwnd()) - return NULL; - - //if it's not part of the main parent window hierarchy, then we are - //not interested - if (!::IsChild(m_pParentWnd->GetSafeHwnd(), hWnd)) - return NULL; - - return hWnd; -} - -BOOL CBalloon::IsCursorInToolTip() const -{ - if (!IsVisible() || !IsWindow(m_hWnd)) - return FALSE; - - CPoint pt; - GetCursorPos(&pt); - - CBalloon * pWnd = (CBalloon*)WindowFromPoint(pt); - - return (pWnd == this); -} - -void CBalloon::KillTimers(UINT nIDTimer /* = NULL */) -{ - if (nIDTimer == NULL) - { - KillTimer(BALLOON_SHOW); - KillTimer(BALLOON_HIDE); - } - else if (nIDTimer == BALLOON_SHOW) - KillTimer(BALLOON_SHOW); - else if (nIDTimer == BALLOON_HIDE) - KillTimer(BALLOON_HIDE); -} - -void CBalloon::DisplayToolTip(CPoint * pt /* = NULL */) -{ - if(!GetTool(CWnd::FromHandle(m_hCurrentWnd), m_pToolInfo) || m_pToolInfo.sBalloonTip.IsEmpty()) - return; - //if a mask is set then use the default values for the tooltip - if (!(m_pToolInfo.nMask & BALLOON_MASK_STYLES)) - m_pToolInfo.nStyles = m_nStyles; - if (!(m_pToolInfo.nMask & BALLOON_MASK_EFFECT)) - { - m_pToolInfo.nEffect = m_nEffect; - } - if (!(m_pToolInfo.nMask & BALLOON_MASK_COLORS)) - { - m_pToolInfo.crBegin = m_crColor[BALLOON_COLOR_BK_BEGIN]; - m_pToolInfo.crMid = m_crColor[BALLOON_COLOR_BK_MID]; - m_pToolInfo.crEnd = m_crColor[BALLOON_COLOR_BK_END]; - } - if (!(m_pToolInfo.nMask & BALLOON_MASK_DIRECTION)) - m_pToolInfo.nDirection = m_nDirection; - - //send notification - SendNotify(CWnd::FromHandle(m_hCurrentWnd), pt, m_pToolInfo); - - //calculate the width and height of the box dynamically - CSize sz = GetTooltipSize(m_pToolInfo.sBalloonTip); - m_szBalloonText = sz; - - //get the size of the current icon - m_szBalloonIcon = GetSizeIcon(m_pToolInfo.hIcon); - if (m_szBalloonIcon.cx || m_szBalloonIcon.cy) - { - sz.cx += m_szBalloonIcon.cx + m_nSizes[XBLSZ_MARGIN_CX]; - sz.cy = max(m_szBalloonIcon.cy, sz.cy); - } - - //get the size of the close button - m_szCloseButton = CSize(::GetSystemMetrics(SM_CXMENUSIZE), ::GetSystemMetrics(SM_CYMENUSIZE)); - if (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON) - { - sz.cx += m_szCloseButton.cx + m_nSizes[XBLSZ_BUTTON_MARGIN_CX]; - } - - //get size of the tooltip with margins - sz.cx += m_nSizes[XBLSZ_MARGIN_CX] * 2; - sz.cy += m_nSizes[XBLSZ_MARGIN_CY] * 2 + m_nSizes[XBLSZ_HEIGHT_ANCHOR]; - if (m_pToolInfo.nStyles & BALLOON_SHADOW) - { - sz.cx += m_nSizes[XBLSZ_SHADOW_CX]; - sz.cy += m_nSizes[XBLSZ_SHADOW_CY]; - } - - CRect rect (0, 0, sz.cx, sz.cy); - - DisplayToolTip(pt, &rect); -} - -void CBalloon::DisplayToolTip(CPoint * pt, CRect * rect) -{ - CalculateInfoBoxRect(pt, rect); - - SetWindowPos( - NULL, rect->left, rect->top, rect->Width() + 2, rect->Height() + 2, - SWP_SHOWWINDOW|SWP_NOCOPYBITS|SWP_NOACTIVATE|SWP_NOZORDER); - - CRgn rgn; - rgn.CreateRectRgn(0, 0, 1, 1); - if (m_pToolInfo.nStyles & BALLOON_SHADOW) - { - rect->right -= m_nSizes[XBLSZ_SHADOW_CX]; - rect->bottom -= m_nSizes[XBLSZ_SHADOW_CY]; - } - - m_rgnBalloon.DeleteObject(); - GetWindowRegion(&m_rgnBalloon, CSize (rect->Width(), rect->Height()), *pt); - rgn.CopyRgn(&m_rgnBalloon); - if (m_pToolInfo.nStyles & BALLOON_SHADOW) - { - m_rgnShadow.DeleteObject(); - m_rgnShadow.CreateRectRgn(0, 0, 1, 1); - m_rgnShadow.CopyRgn(&m_rgnBalloon); - m_rgnShadow.OffsetRgn(m_nSizes[XBLSZ_SHADOW_CX], m_nSizes[XBLSZ_SHADOW_CY]); - rgn.CombineRgn(&rgn, &m_rgnShadow, RGN_OR); - } - SetWindowRgn(rgn, FALSE); - - rgn.DeleteObject(); -} - -void CBalloon::Pop() -{ - KillTimers(); - ShowWindow(SW_HIDE); - m_bButtonPushed = FALSE; -} - -CSize CBalloon::GetTooltipSize(const CString& str) -{ - CRect rect; - GetWindowRect(&rect); - - CDC * pDC = GetDC(); - - CDC memDC; - CBitmap bitmap; - memDC.CreateCompatibleDC(pDC); - bitmap.CreateCompatibleBitmap(pDC, rect.Width(), rect.Height()); - CBitmap* pOldBitmap = memDC.SelectObject(&bitmap); - - //get the minimum size of the rectangle of the tooltip - CSize sz = DrawHTML(&memDC, rect, str, m_LogFont, TRUE); - - memDC.SelectObject(pOldBitmap); - memDC.DeleteDC(); - bitmap.DeleteObject(); - - ReleaseDC(pDC); - - return sz; -} - -CSize CBalloon::GetSizeIcon(HICON hIcon) const -{ - ICONINFO ii; - CSize sz (0, 0); - - if (hIcon != NULL) - { - //get icon dimensions - ::SecureZeroMemory(&ii, sizeof(ICONINFO)); - if (::GetIconInfo(hIcon, &ii)) - { - sz.cx = (DWORD)(ii.xHotspot * 2); - sz.cy = (DWORD)(ii.yHotspot * 2); - //release icon mask bitmaps - if(ii.hbmMask) - ::DeleteObject(ii.hbmMask); - if(ii.hbmColor) - ::DeleteObject(ii.hbmColor); - } - } - return sz; -} - -void CBalloon::CalculateInfoBoxRect(CPoint * pt, CRect * rect) -{ - CRect monitorRect; - GetMonitorWorkArea(*pt, monitorRect); - - CPoint ptEnd; - m_nLastDirection = m_pToolInfo.nDirection; - BOOL horzAdjusted = TestHorizDirection(pt->x, rect->Width(), monitorRect, m_nLastDirection, rect); - if (!horzAdjusted) - { - m_nLastDirection = GetNextHorizDirection(m_nLastDirection); - horzAdjusted = TestHorizDirection(pt->x, rect->Width(), monitorRect, m_nLastDirection, rect); - } - BOOL vertAdjusted = TestVertDirection(pt->y, rect->Height(), monitorRect, m_nLastDirection, rect); - if (!vertAdjusted) - { - m_nLastDirection = GetNextVertDirection(m_nLastDirection); - vertAdjusted = TestVertDirection(pt->y, rect->Height(), monitorRect, m_nLastDirection, rect); - } - // in case the rectangle wasn't adjusted which can happen if the tooltip is - // larger than half the monitor size, we center the tooltip around the mouse pointer - if (!horzAdjusted) - { - int cx = rect->Width() / 2; - rect->right = pt->x + cx; - rect->left = pt->x - cx; - } - if (!vertAdjusted) - { - int cy = rect->Height() / 2; - rect->bottom = pt->y + cy; - rect->top = pt->y - cy; - } - if ((m_pToolInfo.nStyles & BALLOON_SHADOW) && - ((m_nLastDirection == BALLOON_LEFT_TOP) || (m_nLastDirection == BALLOON_LEFT_BOTTOM))) - rect->OffsetRect(m_nSizes[XBLSZ_SHADOW_CX], m_nSizes[XBLSZ_SHADOW_CY]); -} - - -int CBalloon::GetNextHorizDirection(int nDirection) const -{ - switch (nDirection) - { - case BALLOON_LEFT_TOP: - nDirection = BALLOON_RIGHT_TOP; - break; - case BALLOON_RIGHT_TOP: - nDirection = BALLOON_LEFT_TOP; - break; - case BALLOON_LEFT_BOTTOM: - nDirection = BALLOON_RIGHT_BOTTOM; - break; - case BALLOON_RIGHT_BOTTOM: - nDirection = BALLOON_LEFT_BOTTOM; - break; - } - return nDirection; -} - -int CBalloon::GetNextVertDirection(int nDirection) const -{ - switch (nDirection) - { - case BALLOON_LEFT_TOP: - nDirection = BALLOON_LEFT_BOTTOM; - break; - case BALLOON_LEFT_BOTTOM: - nDirection = BALLOON_LEFT_TOP; - break; - case BALLOON_RIGHT_TOP: - nDirection = BALLOON_RIGHT_BOTTOM; - break; - case BALLOON_RIGHT_BOTTOM: - nDirection = BALLOON_RIGHT_TOP; - break; - } - return nDirection; -} - -BOOL CBalloon::TestHorizDirection(int x, int cx, const CRect& monitorRect, - int nDirection, LPRECT rect) -{ - int left = 0; - int right = 0; - int anchorMarginSize = (int)m_nSizes[XBLSZ_MARGIN_ANCHOR]; - - switch (nDirection) - { - case BALLOON_LEFT_TOP: - case BALLOON_LEFT_BOTTOM: - right = ((x + anchorMarginSize) > monitorRect.right) ? monitorRect.right : (x + anchorMarginSize); - left = right - cx; - break; - case BALLOON_RIGHT_TOP: - case BALLOON_RIGHT_BOTTOM: - left = (x - anchorMarginSize)= monitorRect.left) && (right <= monitorRect.right)) ? TRUE : FALSE; - if (bTestOk) - { - rect->left = left; - rect->right = right; - } - - return bTestOk; -} - -BOOL CBalloon::TestVertDirection(int y, int cy, const CRect& monitorRect, - int nDirection, LPRECT rect) -{ - int top = 0; - int bottom = 0; - - switch (nDirection) - { - case BALLOON_LEFT_TOP: - case BALLOON_RIGHT_TOP: - bottom = y; - top = bottom - cy; - break; - case BALLOON_LEFT_BOTTOM: - case BALLOON_RIGHT_BOTTOM: - top = y; - bottom = top + cy; - break; - } - - BOOL bTestOk = ((top >= monitorRect.top) && (bottom <= monitorRect.bottom)) ? TRUE : FALSE; - if (bTestOk) - { - rect->top = top; - rect->bottom = bottom; - } - - return bTestOk; -} - -LPLOGFONT CBalloon::GetSystemToolTipFont() const -{ - static LOGFONT LogFont; - - NONCLIENTMETRICS ncm; - OSVERSIONINFO vers; - - memset(&vers, 0, sizeof(OSVERSIONINFO)); - ncm.cbSize = sizeof(NONCLIENTMETRICS); - vers.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); - - if(!GetVersionEx(&vers)) - return NULL; - - if(vers.dwMajorVersion < 6) - { - ncm.cbSize -= sizeof(ncm.iPaddedBorderWidth); - } - - if (!SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(NONCLIENTMETRICS), &ncm, 0)) - return NULL; - - memcpy(&LogFont, &(ncm.lfStatusFont), sizeof(LOGFONT)); - - return &LogFont; -} - - -void CBalloon::Redraw(BOOL /*bRedraw*/ /* = TRUE */) -{ -} - -void CBalloon::SetStyles(DWORD nStyles, CWnd * pWnd /* = NULL */) -{ - ModifyStyles(nStyles, (DWORD)-1, pWnd); -} - -void CBalloon::ModifyStyles(DWORD nAddStyles, DWORD nRemoveStyles, CWnd * pWnd /* = NULL */) -{ - if (!pWnd) - { - m_nStyles &= ~nRemoveStyles; - m_nStyles |= nAddStyles; - } - else - { - BALLOON_INFO bi; - if (GetTool(pWnd, bi)) - { - if (!(bi.nMask & BALLOON_MASK_STYLES)) - bi.nStyles = m_nStyles; - bi.nStyles &= ~nRemoveStyles; - bi.nStyles |= nAddStyles; - bi.nMask |= BALLOON_MASK_STYLES; - AddTool(pWnd, bi); - } - } -} - -DWORD CBalloon::GetStyles(CWnd * pWnd /* = NULL */) const -{ - if (pWnd) - { - BALLOON_INFO bi; - if (GetTool(pWnd, bi)) - { - if (bi.nMask & BALLOON_MASK_STYLES) - return bi.nStyles; - } - } - return m_nStyles; -} - -void CBalloon::SetDefaultStyles(CWnd * pWnd /* = NULL */) -{ - SetStyles(BALLOON_RSA, pWnd); -} - -void CBalloon::SetColor(int nIndex, COLORREF crColor) -{ - if (nIndex >= BALLOON_MAX_COLORS) - return; - - m_crColor [nIndex] = crColor; -} - -COLORREF CBalloon::GetColor(int nIndex) const -{ - if (nIndex >= BALLOON_MAX_COLORS) - nIndex = BALLOON_COLOR_FG; - - return m_crColor [nIndex]; -} - -void CBalloon::SetDefaultColors() -{ - SetColor(BALLOON_COLOR_FG, ::GetSysColor(COLOR_INFOTEXT)); - SetColor(BALLOON_COLOR_BK_BEGIN, ::GetSysColor(COLOR_INFOBK)); - SetColor(BALLOON_COLOR_BK_MID, ::GetSysColor(COLOR_INFOBK)); - SetColor(BALLOON_COLOR_BK_END, ::GetSysColor(COLOR_INFOBK)); - SetColor(BALLOON_COLOR_SHADOW, ::GetSysColor(COLOR_3DSHADOW)); - SetColor(BALLOON_COLOR_BORDER, ::GetSysColor(COLOR_WINDOWFRAME)); -} - -void CBalloon::SetGradientColors(COLORREF crBegin, COLORREF crMid, COLORREF crEnd, CWnd * pWnd /* = NULL */) -{ - if (!pWnd) - { - SetColor(BALLOON_COLOR_BK_BEGIN, crBegin); - SetColor(BALLOON_COLOR_BK_MID, crMid); - SetColor(BALLOON_COLOR_BK_END, crEnd); - } - else - { - BALLOON_INFO bi; - if (GetTool(pWnd, bi)) - { - bi.crBegin = crBegin; - bi.crMid = crMid; - bi.crEnd = crEnd; - bi.nMask |= BALLOON_MASK_COLORS; - AddTool(pWnd, bi); - } - } -} - -void CBalloon::GetGradientColors(COLORREF & crBegin, COLORREF & crMid, COLORREF & crEnd, CWnd * pWnd /* = NULL */) const -{ - if (pWnd) - { - BALLOON_INFO bi; - if (GetTool(pWnd, bi)) - { - if (bi.nMask & BALLOON_MASK_COLORS) - { - crBegin = bi.crBegin; - crMid = bi.crMid; - crEnd = bi.crEnd; - return; - } - } - } - crBegin = GetColor(BALLOON_COLOR_BK_BEGIN); - crMid = GetColor(BALLOON_COLOR_BK_MID); - crEnd = GetColor(BALLOON_COLOR_BK_END); -} - -void CBalloon::ShowBalloon(CWnd * pWnd, CPoint pt, UINT nIdText, BOOL showCloseButton, LPCTSTR szIcon) -{ - CString str; - str.LoadString(nIdText); - HICON hIcon = (HICON)::LoadImage(NULL, szIcon, IMAGE_ICON, 0, 0, LR_SHARED | LR_DEFAULTSIZE); - ShowBalloon(pWnd, pt, str, showCloseButton, hIcon); -} - -void CBalloon::ShowBalloon( - CWnd * pWnd, CPoint pt, const CString& sText, BOOL showCloseButton, HICON hIcon, - UINT nDirection, UINT nEffect, COLORREF crStart, COLORREF crMid, COLORREF crEnd) -{ - BALLOON_INFO Info; - Info.hIcon = hIcon; - Info.sBalloonTip = sText; - Info.nMask = 0; - - CBalloon * pSB = new CBalloon(); - if (pWnd == NULL) - pWnd = GetDesktopWindow(); - pSB->Create(pWnd); - pSB->AddTool(pWnd, Info); - pSB->m_hCurrentWnd = pWnd->GetSafeHwnd(); - pSB->SetDirection(nDirection); - pSB->SetEffectBk(nEffect); - if (crStart == NULL) - crStart = ::GetSysColor(COLOR_INFOBK); - if (crMid == NULL) - crMid = ::GetSysColor(COLOR_INFOBK); - if (crEnd == NULL) - crEnd = ::GetSysColor(COLOR_INFOBK); - pSB->SetGradientColors(crStart, crMid, crEnd); - pSB->SetBehaviour(BALLOON_DIALOG | BALLOON_DIALOG_DESTROY); - if (showCloseButton) - { - pSB->ModifyStyles(BALLOON_CLOSEBUTTON, 0); - } - pSB->DisplayToolTip(&pt); - if (!showCloseButton) - pSB->SetTimer(BALLOON_HIDE, 5000, NULL); //auto close the dialog if no close button is shown -} - -void CBalloon::AddTool(int nIdWnd, UINT nIdText, HICON hIcon/* = NULL*/) -{ - AddTool(m_pParentWnd->GetDlgItem(nIdWnd), nIdText, hIcon); -} -void CBalloon::AddTool(int nIdWnd, UINT nIdText, UINT nIdIcon) -{ - AddTool(m_pParentWnd->GetDlgItem(nIdWnd), nIdText, nIdIcon); -} -void CBalloon::AddTool(int nIdWnd, const CString& sBalloonTipText, HICON hIcon/* = NULL*/) -{ - AddTool(m_pParentWnd->GetDlgItem(nIdWnd), sBalloonTipText, hIcon); -} -void CBalloon::AddTool(int nIdWnd, const CString& sBalloonTipText, UINT nIdIcon) -{ - AddTool(m_pParentWnd->GetDlgItem(nIdWnd), sBalloonTipText, nIdIcon); -} - -void CBalloon::AddTool(CWnd * pWnd, UINT nIdText, HICON hIcon /* = NULL */) -{ - CString str; - str.LoadString(nIdText); - AddTool(pWnd, str, hIcon); -} - -void CBalloon::AddTool(CWnd * pWnd, UINT nIdText, UINT nIdIcon) -{ - CString str; - str.LoadString(nIdText); - AddTool(pWnd, str, nIdIcon); -} - -void CBalloon::AddTool(CWnd * pWnd, const CString& sBalloonTipText, UINT nIdIcon) -{ - HICON hIcon = NULL; - HINSTANCE hInstResource = NULL; - - if (nIdIcon >= 0) - { - hInstResource = AfxFindResourceHandle(MAKEINTRESOURCE(nIdIcon), RT_GROUP_ICON); - - hIcon = (HICON)::LoadImage(hInstResource, MAKEINTRESOURCE(nIdIcon), IMAGE_ICON, 0, 0, 0); - } - - AddTool(pWnd, sBalloonTipText, hIcon); -} - -void CBalloon::AddTool(CWnd * pWnd, const CString& sBalloonTipText, HICON hIcon /* = NULL */) -{ - //store the tool information - BALLOON_INFO Info; - Info.hIcon = hIcon; - Info.sBalloonTip = sBalloonTipText; - Info.nMask = 0; - - AddTool(pWnd, Info); -} - -void CBalloon::AddTool(CWnd * pWnd, BALLOON_INFO & bi) -{ - if (pWnd) - m_ToolMap.SetAt(pWnd->m_hWnd, bi); - else - m_ToolMap.SetAt(NULL, bi); -} - -BOOL CBalloon::GetTool(CWnd * pWnd, CString & sBalloonTipText, HICON & hIcon) const -{ - BALLOON_INFO bi; - BOOL bFound = GetTool(pWnd, bi); - if (bFound) - { - sBalloonTipText = bi.sBalloonTip; - hIcon = bi.hIcon; - } - - return bFound; -} - -BOOL CBalloon::GetTool(CWnd * pWnd, BALLOON_INFO & bi) const -{ - if (pWnd) - return m_ToolMap.Lookup(pWnd->m_hWnd, bi); - return m_ToolMap.Lookup(NULL, bi); -} - -void CBalloon::RemoveTool(CWnd * pWnd) -{ - if (pWnd) - m_ToolMap.RemoveKey(pWnd->m_hWnd); - m_ToolMap.RemoveKey(NULL); -} - -void CBalloon::RemoveAllTools() -{ - m_ToolMap.RemoveAll(); -} - -void CBalloon::SetMaskTool(CWnd * pWnd, UINT nMask /* = 0 */) -{ - ModifyMaskTool(pWnd, nMask, (UINT)-1); -} - -void CBalloon::ModifyMaskTool(CWnd * pWnd, UINT nAddMask, UINT nRemoveMask) -{ - ASSERT(pWnd); - - BALLOON_INFO bi; - if (GetTool(pWnd, bi)) - { - bi.nMask &= ~nRemoveMask; - bi.nMask |= nAddMask; - AddTool(pWnd, bi); - } -} - -UINT CBalloon::GetMaskTool(CWnd * pWnd) const -{ - ASSERT(pWnd); - - UINT nMask = 0; - BALLOON_INFO bi; - if (GetTool(pWnd, bi)) - nMask = bi.nMask; - return nMask; -} - -void CBalloon::SetEffectBk(UINT nEffect, CWnd * pWnd /* = NULL */) -{ - if (!pWnd) - { - m_nEffect = nEffect; - } - else - { - BALLOON_INFO bi; - if (GetTool(pWnd, bi)) - { - bi.nEffect = nEffect; - bi.nMask |= BALLOON_MASK_EFFECT; - AddTool(pWnd, bi); - } - } -} - -UINT CBalloon::GetEffectBk(CWnd * pWnd /* = NULL */) const -{ - if (pWnd) - { - BALLOON_INFO bi; - if (GetTool(pWnd, bi)) - { - if (bi.nMask & BALLOON_MASK_EFFECT) - { - return bi.nEffect; - } - } - } - return m_nEffect; -} - -void CBalloon::SetNotify(BOOL bParentNotify /* = TRUE */) -{ - HWND hWnd = NULL; - - if (bParentNotify) - hWnd = m_pParentWnd->GetSafeHwnd(); - - SetNotify(hWnd); -} - -void CBalloon::SetNotify(HWND hWnd) -{ - m_hNotifyWnd = hWnd; -} - -BOOL CBalloon::GetNotify() const -{ - return (m_hNotifyWnd != NULL); -} - -void CBalloon::SetDelayTime(DWORD dwDuration, UINT nTime) -{ - switch (dwDuration) - { - case TTDT_AUTOPOP: - m_nTimeAutoPop = nTime; - break; - case TTDT_INITIAL : - m_nTimeInitial = nTime; - break; - } -} - -UINT CBalloon::GetDelayTime(DWORD dwDuration) const -{ - UINT nTime = 0; - switch (dwDuration) - { - case TTDT_AUTOPOP: - nTime = m_nTimeAutoPop; - break; - case TTDT_INITIAL: - nTime = m_nTimeInitial; - break; - } - - return nTime; -} - -void CBalloon::SetSize(int nSizeIndex, UINT nValue) -{ - if (nSizeIndex >= XBLSZ_MAX_SIZES) - return; - - m_nSizes [nSizeIndex] = nValue; -} - -UINT CBalloon::GetSize(int nSizeIndex) const -{ - if (nSizeIndex >= XBLSZ_MAX_SIZES) - return 0; - - return m_nSizes [nSizeIndex]; -} - -void CBalloon::SetDefaultSizes() -{ - SetSize(XBLSZ_ROUNDED_CX, 16); - SetSize(XBLSZ_ROUNDED_CY, 16); - SetSize(XBLSZ_MARGIN_CX, 12); - SetSize(XBLSZ_MARGIN_CY, 12); - SetSize(XBLSZ_SHADOW_CX, 4); - SetSize(XBLSZ_SHADOW_CY, 4); - SetSize(XBLSZ_WIDTH_ANCHOR, 12); - SetSize(XBLSZ_HEIGHT_ANCHOR, 16); - SetSize(XBLSZ_MARGIN_ANCHOR, 16); - SetSize(XBLSZ_BORDER_CX, 1); - SetSize(XBLSZ_BORDER_CY, 1); - SetSize(XBLSZ_BUTTON_MARGIN_CX, 5); - SetSize(XBLSZ_BUTTON_MARGIN_CY, 5); -} - -void CBalloon::SetDirection(UINT nDirection /* = BALLOON_RIGHT_TOP */, CWnd * pWnd /* = NULL */) -{ - if (nDirection >= BALLOON_MAX_DIRECTIONS) - return; - - if (!pWnd) - { - m_nDirection = nDirection; - } - else - { - BALLOON_INFO bi; - if (GetTool(pWnd, bi)) - { - bi.nDirection = nDirection; - bi.nMask |= BALLOON_MASK_DIRECTION; - AddTool(pWnd, bi); - } - } -} - -UINT CBalloon::GetDirection(CWnd * pWnd /* = NULL */) const -{ - if (pWnd) - { - BALLOON_INFO bi; - if (GetTool(pWnd, bi)) - { - if (bi.nMask & BALLOON_MASK_DIRECTION) - return bi.nDirection; - } - } - return m_nDirection; -} - -void CBalloon::SetBehaviour(UINT nBehaviour /* = 0 */, CWnd * pWnd /* = NULL */) -{ - if (!pWnd) - { - m_nBehaviour = nBehaviour; - } - else - { - BALLOON_INFO bi; - if (GetTool(pWnd, bi)) - { - bi.nBehaviour = nBehaviour; - bi.nMask |= BALLOON_MASK_BEHAVIOUR; - AddTool(pWnd, bi); - } - } -} - -UINT CBalloon::GetBehaviour(CWnd * pWnd /* = NULL */) const -{ - if (pWnd) - { - BALLOON_INFO bi; - if (GetTool(pWnd, bi)) - { - if (bi.nMask & BALLOON_MASK_BEHAVIOUR) - return bi.nBehaviour; - } - } - return m_nBehaviour; -} - -BOOL CBalloon::SetFont(CFont & font) -{ - LOGFONT lf; - font.GetLogFont (&lf); - - return SetFont(&lf); -} - -BOOL CBalloon::SetFont(LPLOGFONT lf) -{ - memcpy(&m_LogFont, lf, sizeof(LOGFONT)); - - return TRUE; -} - -BOOL CBalloon::SetFont(LPCTSTR lpszFaceName, int nSizePoints /* = 8 */, - BOOL bUnderline /* = FALSE */, BOOL bBold /* = FALSE */, - BOOL bStrikeOut /* = FALSE */, BOOL bItalic /* = FALSE */) -{ - CDC* pDC = GetDC(); - LOGFONT lf; - memset (&lf, 0, sizeof(LOGFONT)); - - _tcscpy_s (lf.lfFaceName, 32, lpszFaceName); - lf.lfHeight = -MulDiv (nSizePoints, GetDeviceCaps (pDC->m_hDC, LOGPIXELSY), 72); - lf.lfUnderline = (BYTE)bUnderline; - lf.lfWeight = bBold ? FW_BOLD : FW_NORMAL; - lf.lfStrikeOut = (BYTE)bStrikeOut; - lf.lfItalic = (BYTE)bItalic; - - if (pDC) - ReleaseDC(pDC); - - return SetFont(&lf); -} - -void CBalloon::SetDefaultFont() -{ - LPLOGFONT lpSysFont = GetSystemToolTipFont(); - if(lpSysFont) - SetFont(lpSysFont); -} - -void CBalloon::GetFont(CFont & font) const -{ - font.CreateFontIndirect(&m_LogFont); -} - -void CBalloon::GetFont(LPLOGFONT lf) const -{ - memcpy(lf, &m_LogFont, sizeof(LOGFONT)); -} - -void CBalloon::OnMouseMove(UINT nFlags, CPoint point) -{ - if (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON) - { - UINT nState = DFCS_CAPTIONCLOSE | DFCS_FLAT | DFCS_TRANSPARENT; - if (m_rtCloseButton.PtInRect(point)) - { - nState |= DFCS_HOT; - if (m_bButtonPushed) - nState |= DFCS_PUSHED; - } - CClientDC dc(this); - dc.DrawFrameControl(m_rtCloseButton, DFC_CAPTION, nState); - - if (IsPointOverALink(point)) - m_Cursor.SetCursor(IDC_HAND); - else - m_Cursor.Restore(); - } - - CWnd::OnMouseMove(nFlags, point); -} - -void CBalloon::OnLButtonDown(UINT nFlags, CPoint point) -{ - if ((m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON) && m_rtCloseButton.PtInRect(point)) - { - m_bButtonPushed = TRUE; - OnMouseMove(0, point); - } - - CWnd::OnLButtonDown(nFlags, point); -} - -void CBalloon::OnLButtonUp(UINT nFlags, CPoint point) -{ - if (IsPointOverALink(point)) - { - CString url = GetLinkForPoint(point); - ShellExecute(NULL, _T("open"), url, NULL,NULL, 0); - } - else if ( - // Dialog has close button, but user has clicked somewhere else. - (m_pToolInfo.nStyles & BALLOON_CLOSEBUTTON) && - (!m_rtCloseButton.PtInRect(point) || !m_bButtonPushed)) - { - m_bButtonPushed = FALSE; - } - else - { - Pop(); - if (GetBehaviour() & BALLOON_DIALOG_DESTROY) - { - CWnd::OnLButtonUp(nFlags, point); - DestroyWindow(); - return; - } - } - CWnd::OnLButtonUp(nFlags, point); -} - -void CBalloon::PostNcDestroy() -{ - CWnd::PostNcDestroy(); - TRACE("CBalloon: PostNcDestroy()\n"); - - if (GetBehaviour() & BALLOON_DIALOG_DESTROY) - { - TRACE("CBalloon: object deleted\n"); - delete this; - } -} - -void CBalloon::GetMonitorWorkArea(const CPoint& sourcePoint, CRect& monitorRect) const -{ - // identify the monitor that contains the sourcePoint - // and return the work area (the portion of the screen - // not obscured by the system task bar or by application - // desktop tool bars) of that monitor - OSVERSIONINFOEX VersionInformation; - SecureZeroMemory(&VersionInformation, sizeof(OSVERSIONINFOEX)); - VersionInformation.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - GetVersionEx((OSVERSIONINFO *)&VersionInformation); - - ::GetWindowRect(GetDesktopWindow()->m_hWnd, &monitorRect); - - if (VersionInformation.dwMajorVersion >= 5) - { - MONITORINFO mi; - - // - // get the work area - // - mi.cbSize = sizeof(mi); - HMODULE hUser32 = ::GetModuleHandle (_T("USER32.DLL")); - if (hUser32 != NULL) - { - typedef HMONITOR (WINAPI *FN_MonitorFromPoint) (POINT pt, DWORD dwFlags); - typedef BOOL (WINAPI *FN_GetMonitorInfo) (HMONITOR hMonitor, LPMONITORINFO lpmi); - FN_MonitorFromPoint pfnMonitorFromPoint = (FN_MonitorFromPoint) - ::GetProcAddress (hUser32, "MonitorFromPoint"); - FN_GetMonitorInfo pfnGetMonitorInfo = (FN_GetMonitorInfo) - ::GetProcAddress (hUser32, "GetMonitorInfoW"); - if (pfnMonitorFromPoint != NULL && pfnGetMonitorInfo != NULL) - { - MONITORINFO mi; - HMONITOR hMonitor = pfnMonitorFromPoint (sourcePoint, - MONITOR_DEFAULTTONEAREST); - mi.cbSize = sizeof (mi); - pfnGetMonitorInfo (hMonitor, &mi); - monitorRect = mi.rcWork; - } - } - } - -} - -CPoint -CBalloon::GetCtrlCentre(CWnd* pDlgWnd, UINT ctrlId) -{ - CWnd* pCtrl = pDlgWnd->GetDlgItem(ctrlId); - if(pCtrl == NULL) - { - ASSERT(FALSE); - return CPoint(200,200); - } - CRect ctrlRect; - pCtrl->GetWindowRect(ctrlRect); - return ctrlRect.CenterPoint(); -} diff --git a/src/Utils/MiscUI/Balloon.h b/src/Utils/MiscUI/Balloon.h deleted file mode 100644 index d3d02cf87..000000000 --- a/src/Utils/MiscUI/Balloon.h +++ /dev/null @@ -1,624 +0,0 @@ -// TortoiseGit - a Windows shell extension for easy version control - -// Copyright (C) 2003-2008 - TortoiseSVN - -// This program is free software; you can redistribute it and/or -// modify it under the terms of the GNU General Public License -// as published by the Free Software Foundation; either version 2 -// of the License, or (at your option) any later version. - -// This program 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 General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -// -#pragma once - -#include "gradient.h" -#include "htmlformatter.h" -#include "cursor.h" - -//The styles -#define BALLOON_ANCHOR 0x0001 -#define BALLOON_SHADOW 0x0002 -#define BALLOON_ROUNDED 0x0004 -#define BALLOON_RSA 0x0007 -#define BALLOON_VCENTER_ALIGN 0x0008 -#define BALLOON_BOTTOM_ALIGN 0x0010 -#define BALLOON_CLOSEBUTTON 0x0020 - -//The behaviors -#define BALLOON_MULTIPLE_SHOW 0x0001 //Multiple show for single control -#define BALLOON_TRACK_MOUSE 0x0002 //ToolTip follows the mouse cursor -#define BALLOON_DIALOG 0x0004 //Shown as a dialog instead of a tooltip -#define BALLOON_DIALOG_DESTROY 0x0008 //delete the object after window is destroyed. Use carefully! - -//The masks -#define BALLOON_MASK_STYLES 0x0001 //The styles for the tooltip gets from the structures -#define BALLOON_MASK_EFFECT 0x0002 //The background's type for the tooltip gets from the structures -#define BALLOON_MASK_COLORS 0x0004 //The background's colors for the tooltip gets from the structures -#define BALLOON_MASK_DIRECTION 0x0008 //The align for the tooltip gets from the structures -#define BALLOON_MASK_BEHAVIOUR 0x0010 //The behavior for the tooltip gets from the structures - -/** - * \ingroup Utils - * BALLOON_INFO structure. - */ -typedef struct tagBALLOON_INFO -{ - HICON hIcon; ///GetWindowRect(rt); - * CPoint point = CPoint((rt.left+rt.right)/2, (rt.top+rt.bottom)/2); - * CBalloon::ShowBalloon(NULL, point, - * "this is a Message Balloon\n\nWarning! Warning!\nSomething unexpected happened", - * TRUE, IDI_EXCLAMATION); - * \endcode - * - * To use the tooltips, declare an object of CBalloon as a member of your dialog class: - * \code - * CBalloon m_tooltips; - * \code - * In your OnInitDialog() method add the tooltips and modify them as you like: - * \code - * m_tooltips.Create(this); //initializes the tooltips - * m_tooltips.AddTool(IDC_BUTTON, "this button does nothing"); - * m_tooltips.AddTool(IDC_EDITBOX, "enter a value here", IDI_ICON); - * m_tooltips.SetEffectBk(GetDlgItem(IDC_EDITBOX), CBalloon::BALLOON_EFFECT_HGRADIENT); //only affects the edit box tooltip - * m_tooltips.SetGradientColors(0x80ffff, 0x000000, 0xffff80); - * \endcode - * and last you have to override the PreTranslateMessage() method of your dialog box: - * \code - * BOOL CMyDialog::PreTranslateMessage(MSG* pMsg) - * { - * m_tooltips.RelayEvent(pMsg); - * return CDialog::PreTranslateMessage(pMsg); - * } - * \endcode - */ -class CBalloon : public CWnd, public CHTMLFormatter -{ -// Construction -public: - virtual BOOL Create(CWnd* pParentWnd); - CBalloon(); - virtual ~CBalloon(); - -// Attributes -public: - enum { XBLSZ_ROUNDED_CX = 0, - XBLSZ_ROUNDED_CY, - XBLSZ_MARGIN_CX, - XBLSZ_MARGIN_CY, - XBLSZ_SHADOW_CX, - XBLSZ_SHADOW_CY, - XBLSZ_WIDTH_ANCHOR, - XBLSZ_HEIGHT_ANCHOR, - XBLSZ_MARGIN_ANCHOR, - XBLSZ_BORDER_CX, - XBLSZ_BORDER_CY, - XBLSZ_BUTTON_MARGIN_CX, - XBLSZ_BUTTON_MARGIN_CY, - - XBLSZ_MAX_SIZES - }; - - enum { BALLOON_COLOR_FG = 0, - BALLOON_COLOR_BK_BEGIN, - BALLOON_COLOR_BK_MID, - BALLOON_COLOR_BK_END, - BALLOON_COLOR_SHADOW, // Color for the shadow - BALLOON_COLOR_BORDER, // Color for border of the tooltip - - BALLOON_MAX_COLORS - }; - - enum { BALLOON_LEFT_TOP = 0, - BALLOON_RIGHT_TOP, - BALLOON_LEFT_BOTTOM, - BALLOON_RIGHT_BOTTOM, - - BALLOON_MAX_DIRECTIONS - }; - - enum { BALLOON_EFFECT_SOLID = 0, - BALLOON_EFFECT_HGRADIENT, - BALLOON_EFFECT_VGRADIENT, - BALLOON_EFFECT_HCGRADIENT, - BALLOON_EFFECT_VCGRADIENT, - BALLOON_EFFECT_3HGRADIENT, - BALLOON_EFFECT_3VGRADIENT, - - BALLOON_MAX_EFFECTS - }; - - -// Operations -public: - -// Overrides - // ClassWizard generated virtual function overrides - //{{AFX_VIRTUAL(XToolTip) - public: - virtual BOOL PreTranslateMessage(MSG* pMsg); - //}}AFX_VIRTUAL - -// Implementation -public: - /** \name Balloon Dialogs - * static methods to show a balloon like a modeless dialog - */ - //@{ - /** - * Pops up a balloon like a modeless dialog to inform the user in - * a non disturbing way like with a normal MessageBox(). - * \image html "balloon_box.jpg" - * An example of when to use such balloons is in a dialog box where - * the user can enter values. If one or several values are outside - * of valid ranges then just pop up a balloon. That way the user - * knows exactly \b where the wrong value is (if the balloon is - * placed so that the anchor points to the edit box) and also - * doesn't have to press "OK" to close the box. - * - * \param pWnd the parent window. Or NULL if no parent window is available. - * \param pt the point where the anchor should point to. For example if you - * want to point to an edit box the point would be: - * \code - * CWnd* ctrl = GetDlgItem(IDC_EDITBOX); - * CRect rt; - * ctrl->GetWindowRect(rt); - * CPoint point = CPoint((rt.left+rt.right)/2, (rt.top+rt.bottom)/2); - * \endcode - * \param nIdText the string ID of the text to show. The ID has to be in your resources. - * \param sText the string to show. - * \param showCloseButton If TRUE, then the balloon has a close button in the upper right corner. That also - * makes the balloon to show up until the user presses the close button.\n - * If FALSE, then the balloon will be closed after a timeout of 5 seconds or as soon as the user clicks anywhere - * on the balloon. - * \param hIcon a handle of an icon. - * \param nIdIcon an ID of an icon which has to be in your resources. - * \param szIcon a name of an icon. Either a path to an icon file or one of - * the following system icons:\n - * - IDI_APPLICATION - * - IDI_ERROR - * - IDI_HAND - * - IDI_EXCLAMATION - * - IDI_WARNING - * - IDI_QUESTION - * - IDI_WINLOGO - * - IDI_INFORMATION - * - IDI_ASTERISK - * - IDI_QUESTION - * - * \param nDirection the direction to where the dialog should be drawn. Defaults to BALLOON_RIGHT_TOP. - * - BALLOON_LEFT_TOP - * - BALLOON_RIGHT_TOP - * - BALLOON_LEFT_BOTTOM - * - BALLOON_RIGHT_BOTTOM - * - * \param nEffect specifies how to draw the background. Defaults to BALLOON_EFFECT_SOLID. - * - BALLOON_EFFECT_SOLID one color for the background. The default is the standard windows color for tooltip backgrounds. - * - BALLOON_EFFECT_HGRADIENT draws a horizontal gradient from crStart to crEnd - * - BALLOON_EFFECT_VGRADIENT draws a vertical gradient from crStart to crEnd - * - BALLOON_EFFECT_HCGRADIENT draws a horizontal gradient from crStart to crEnd to crStart - * - BALLOON_EFFECT_VCGRADIENT draws a vertical gradient from crStart to crEnd to crStart - * - BALLOON_EFFECT_3HGRADIENT draws a horizontal gradient from crStart to crMid to crEnd - * - BALLOON_EFFECT_3VGRADIENT draws a vertical gradient from crStart to crMid to crEnd - * - * \param crStart the starting color for gradients - * \param crMid the middle color for three colored gradients - * \param crEnd the end color for gradients - */ - /** - * \overload ShowBalloon(CWnd * pWnd, CPoint pt, UINT nIdText, BOOL showCloseButton, UINT nIdIcon, UINT nDirection = BALLOON_RIGHT_TOP, UINT nEffect = BALLOON_EFFECT_SOLID, COLORREF crStart = NULL, COLORREF crMid = NULL, COLORREF crEnd = NULL); - */ - static void ShowBalloon( - CWnd * pWnd, CPoint pt, const CString& sText, BOOL showCloseButton, HICON hIcon, - UINT nDirection = BALLOON_RIGHT_TOP, UINT nEffect = BALLOON_EFFECT_SOLID, - COLORREF crStart = NULL, COLORREF crMid = NULL, COLORREF crEnd = NULL); - /** - * \overload ShowBalloon(CWnd * pWnd, CPoint pt, UINT nIdText, BOOL showCloseButton, LPCTSTR szIcon); - */ - static void ShowBalloon(CWnd * pWnd, CPoint pt, UINT nIdText, BOOL showCloseButton, LPCTSTR szIcon); - //@} - - /** - * Helper function to return the center point of a dialog control - * Useful for passing to ShowBalloon - */ - static CPoint GetCtrlCentre(CWnd* pDlgWnd, UINT ctrlId); - - /** \name ToolTips - * handling of tooltips. - */ - //@{ - //@{ - /** - * Adds a tooltip for a windows element to the internal list. - * \param pWnd pointer to a windows element. - * \param nIdWnd an ID of a dialog resource. - * \param nIdText an ID of a string dialog resource to use as the tooltip text. - * \param sBalloontipText string for the tooltip. - * \param hIcon handle for an icon to show on the tooltip. - * \param nIdIcon a resource ID for an icon to show on the tooltip. - * \param bi pointer to a BALLOON_IFNO structure. - */ - void AddTool(CWnd * pWnd, UINT nIdText, HICON hIcon = NULL); //Adds tool - void AddTool(CWnd * pWnd, UINT nIdText, UINT nIdIcon); //Adds tool - void AddTool(CWnd * pWnd, const CString& sBalloonTipText, HICON hIcon = NULL); //Adds tool - void AddTool(CWnd * pWnd, const CString& sBalloonTipText, UINT nIdIcon); //Adds tool - void AddTool(int nIdWnd, UINT nIdText, HICON hIcon = NULL); //Adds tool - void AddTool(int nIdWnd, UINT nIdText, UINT nIdIcon); //Adds tool - void AddTool(int nIdWnd, const CString& sBalloonTipText, HICON hIcon = NULL); //Adds tool - void AddTool(int nIdWnd, const CString& sBalloonTipText, UINT nIdIcon); //Adds tool - void AddTool(CWnd * pWnd, BALLOON_INFO & bi); //Adds tool - //@} - - /** - * Gets the text and the icon handle of a specific tooltip. - * \param pWnd pointer to the tooltip window - * \param sBalloonTipText the returned tooltip text - * \param hIcon the returned icon handle - * \param bi pointer to the returned BALLOON_INFO structure. - * \return TRUE if the tooltip exists. - */ - BOOL GetTool(CWnd * pWnd, CString & sBalloonTipText, HICON & hIcon) const; //Gets the tool tip's text - BOOL GetTool(CWnd * pWnd, BALLOON_INFO & bi) const; //Gets tool - - /** - * Removes a specific tooltip from the internal list. - * \param pWnd pointer to the tooltip window - */ - void RemoveTool(CWnd * pWnd); //Removes specified tool - - /** - * Removes all tooltips from the internal list. - */ - void RemoveAllTools(); // Removes all tools - //@} - - /** \name Styles - * handling of tooltip appearance styles. - * The following styles are available: - * - BALLOON_ANCHOR the balloon is drawn with an anchor - * - BALLOON_SHADOW the balloon is drawn with a SE shadow - * - BALLOON_ROUNDED the balloon has round corners. For tooltips like the standard windows ones disable this style. - * - BALLOON_RSA combines BALLOON_ANCHOR, BALLOON_SHADOW and BALLOON_ROUNDED. This is the default. - * - BALLOON_VCENTER_ALIGN - * - BALLOON_BOTTOM_ALIGN - * - BALLOON_CLOSEBUTTON the balloon has a close button in the upper right corner. - */ - //@{ - /** - * sets styles for either all tooltips or specific ones. - * \param nStyles the styles to set. - * \param pWnd pointer to the tooltip window or NULL if the styles should affect all tooltips. - */ - void SetStyles(DWORD nStyles, CWnd * pWnd = NULL); //Sets New Style - /** - * Modifies existing styles. - * \param nAddStyles the styles to add. - * \param nRemoveStyles the styles to remove - * \param pWnd pointer to the tooltip window or NULL if the styles should affect all tooltips. - */ - void ModifyStyles(DWORD nAddStyles, DWORD nRemoveStyles, CWnd * pWnd = NULL); //Modifies styles - /** - * returns the current styles for the tooltip. - * \param pWnd pointer to the tooltip window or NULL if the global styles are needed. - */ - DWORD GetStyles(CWnd * pWnd = NULL) const; //Gets current Styles - /** - * Resets the styles to the default values. - * \param pWnd pointer to the tooltip window or NULL if the styles should affect all tooltips. - */ - void SetDefaultStyles(CWnd * pWnd = NULL); //Sets default styles - //@} - - /** \name Colors - * different color settings. The following elements have colors: - * - BALLOON_COLOR_FG the foreground text color. Default is black. - * - BALLOON_COLOR_BK_BEGIN the background color and the first color in gradients. - * - BALLOON_COLOR_BK_MID the middle color for gradients. - * - BALLOON_COLOR_BK_END the end color for gradients. - * - BALLOON_COLOR_SHADOW the color of the shadow - * - BALLOON_COLOR_BORDER the color for the balloon border - */ - //@{ - /** - * Sets the color for a balloon element. - * \param nIndex the element to set the color. - * \param crColor the color. - */ - void SetColor(int nIndex, COLORREF crColor); //Sets the color - /** - * Returns the color of a balloon element. - */ - COLORREF GetColor(int nIndex) const; //Gets the color - /** - * Resets all colors to default values. - */ - void SetDefaultColors(); //Sets default colors - /** - * Sets the colors used in the background gradients. - * \param crBegin first color - * \param crMid middle color - * \param crEnd end color - * \param pWnd pointer to the tooltip window or NULL if the settings are global. - */ - void SetGradientColors(COLORREF crBegin, COLORREF crMid, COLORREF crEnd, CWnd * pWnd = NULL); //Sets the gradient's colors - /** - * Returns the colors used in the background gradients. - * \param pWnd pointer to the tooltip window or NULL if the global settings are needed. - */ - void GetGradientColors(COLORREF & crBegin, COLORREF & crMid, COLORREF & crEnd, CWnd * pWnd = NULL) const; //Gets the gradient's colors - //@} - - - /** \name Masks - * Manipulate masks of tooltips. Masks are used to define styles, effects, colors and the like for single - * tooltips and not only for all tooltips. - * Whatever mask is set for a specific tooltip means that this tooltip has its own version of those settings - * and ignores the global settings.\n - * The following masks are available: - * - BALLOON_MASK_STYLES masks out the styles - * - BALLOON_MASK_EFFECT masks out the effects - * - BALLOON_MASK_COLORS masks out the colors - * - BALLOON_MASK_DIRECTION masks out the direction - * - BALLOON_MASK_BEHAVIOUR masks out the behavior - * - * The functions either set, modify or read out the masks for specific tooltip windows. - */ - //@{ - void SetMaskTool(CWnd * pWnd, UINT nMask = 0); - void ModifyMaskTool(CWnd * pWnd, UINT nAddMask, UINT nRemoveMask); - UINT GetMaskTool(CWnd * pWnd) const; - //@} - - /** \name Effects - * Use these methods to manipulate background effects of the tooltip. The following - * effects are available. - * - BALLOON_EFFECT_SOLID one color for the background. The default is the standard windows color for tooltip backgrounds. - * - BALLOON_EFFECT_HGRADIENT draws a horizontal gradient from crStart to crEnd - * - BALLOON_EFFECT_VGRADIENT draws a vertical gradient from crStart to crEnd - * - BALLOON_EFFECT_HCGRADIENT draws a horizontal gradient from crStart to crEnd to crStart - * - BALLOON_EFFECT_VCGRADIENT draws a vertical gradient from crStart to crEnd to crStart - * - BALLOON_EFFECT_3HGRADIENT draws a horizontal gradient from crStart to crMid to crEnd - * - BALLOON_EFFECT_3VGRADIENT draws a vertical gradient from crStart to crMid to crEnd - */ - //@{ - void SetEffectBk(UINT nEffect, CWnd * pWnd = NULL); - UINT GetEffectBk(CWnd * pWnd = NULL) const; - //@} - - /** \name Notification - * Gets or sets if the parent or any other window should get notification messages from - * the tooltips. - */ - //@{ - void SetNotify(HWND hWnd); - void SetNotify(BOOL bParentNotify = TRUE); - BOOL GetNotify() const; //Is enabled notification - //@} - - /** \name Delaytimes - * Gets or sets the delay times for the tooltips. - * - TTDT_AUTOPOP time in milliseconds until the tooltip automatically closes. - * - TTDT_INITIAL time in milliseconds until the tooltip appears when the mouse pointer is over a control. - */ - //@{ - void SetDelayTime(DWORD dwDuration, UINT nTime); - UINT GetDelayTime(DWORD dwDuration) const; - //@} - - - /** \name Direction - * Gets or sets the direction of the balloons. - * - BALLOON_LEFT_TOP - * - BALLOON_RIGHT_TOP - * - BALLOON_LEFT_BOTTOM - * - BALLOON_RIGHT_BOTTOM - */ - //@{ - void SetDirection(UINT nDirection = BALLOON_RIGHT_TOP, CWnd * pWnd = NULL); - UINT GetDirection(CWnd * pWnd = NULL) const; - //@} - - /** \name Behavior - * Gets or sets the behavior of the balloons. - * - BALLOON_MULTIPLE_SHOW if this is set then the tooltip will appear again if the mouse pointer is still over the same control. - * - BALLOON_TRACK_MOUSE if set then the tooltip will follow the mouse pointer - * - BALLOON_DIALOG the balloon is shown as a dialog instead of a tooltip, i.e. it won't close when the mouse pointer leaves the control. - * - BALLOON_DIALOG_DESTROY the object itself is destroyed when the balloon is closed. Use this \b very carefully! - */ - //@{ - void SetBehaviour(UINT nBehaviour = 0, CWnd * pWnd = NULL); - UINT GetBehaviour(CWnd * pWnd = NULL) const; - //@} - - /** \name Fonts - * Font settings for the balloon text. - */ - //@{ - BOOL SetFont(CFont & font); //set font - BOOL SetFont(LPLOGFONT lf); //set font - BOOL SetFont(LPCTSTR lpszFaceName, int nSizePoints = 8, - BOOL bUnderline = FALSE, BOOL bBold = FALSE, - BOOL bStrikeOut = FALSE, BOOL bItalic = FALSE); //set font - void SetDefaultFont(); //set default fonts - void GetFont(CFont & font) const; - void GetFont(LPLOGFONT lf) const; - //@} - - /** - * Call this method from CDialog::PreTranslateMessage(pMsg). - */ - void RelayEvent(MSG* pMsg); - - /** - * Hide tooltip immediately. - */ - void Pop(); - - /** - * Shows a tooltip immediately. - */ - void DisplayToolTip(CPoint * pt = NULL); - void DisplayToolTip(CPoint * pt, CRect * rect); - - // Generated message map functions -protected: - void SetSize(int nSizeIndex, UINT nValue); - UINT GetSize(int nSizeIndex) const; - void SetDefaultSizes(); - - void Redraw(BOOL bRedraw = TRUE); - void KillTimers(UINT nIDTimer = NULL); - - void SetNewToolTip(CWnd * pWnd); - void GetMonitorWorkArea(const CPoint& sourcePoint, CRect& monitorRect) const; - - /** - * Finds the child window to which the point belongs - * \param point the point to look for the child window - * \return the pointer to the child window, or NULL if there is now window - */ - HWND GetChildWindowFromPoint(CPoint & point) const; - BOOL IsCursorInToolTip() const; - inline BOOL IsVisible() const { return ((GetStyle() & WS_VISIBLE) == WS_VISIBLE); } - - CSize GetTooltipSize(const CString& str); //Gets max rectangle for display tooltip text - CSize GetSizeIcon(HICON hIcon) const; - void CalculateInfoBoxRect(CPoint * pt, CRect * rect); - - LPLOGFONT GetSystemToolTipFont() const; - - int GetNextHorizDirection(int nDirection) const; - int GetNextVertDirection(int nDirection) const; - BOOL TestHorizDirection(int x, int cx, const CRect& monitorRect, int nDirection, LPRECT rect); - BOOL TestVertDirection(int y, int cy, const CRect& monitorRect, int nDirection, LPRECT rect); - - CRect GetWindowRegion(CRgn * rgn, CSize sz, CPoint pt) const; - - LRESULT SendNotify(CWnd * pWnd, CPoint * pt, BALLOON_INFO & bi); - - void OnDraw(CDC * pDC, CRect rect); - void OnDrawBackground(CDC * pDC, CRect * pRect); - - virtual void PostNcDestroy(); - afx_msg void OnPaint(); - afx_msg void OnTimer(UINT_PTR nIDEvent); - afx_msg void OnDestroy(); - afx_msg void OnKillFocus(CWnd* pNewWnd); - afx_msg void OnMouseMove(UINT nFlags, CPoint point); - afx_msg void OnLButtonDown(UINT nFlags, CPoint point); - afx_msg void OnLButtonUp(UINT nFlags, CPoint point); - DECLARE_MESSAGE_MAP() -protected: - enum { BALLOON_SHOW = 0x100, //the identifier of the timer for show the tooltip - BALLOON_HIDE = 0x101 //the identifier of the timer for hide the tooltip - }; - - CMap m_ToolMap; //Tool Maps - - HWND m_hNotifyWnd; // Handle to window for notification about change data - CWnd * m_pParentWnd; // The pointer to the parent window - HWND m_hCurrentWnd; - HWND m_hDisplayedWnd; - UINT m_nLastDirection; - - - LOGFONT m_LogFont; // Current font in use - - //Default setting - COLORREF m_crColor [BALLOON_MAX_COLORS]; //The indexing colors - UINT m_nSizes [XBLSZ_MAX_SIZES]; //All sizes - UINT m_nStyles; - UINT m_nDirection; - UINT m_nEffect; - UINT m_nBehaviour; //The tool tip's behavior - - UINT m_nTimeAutoPop; - UINT m_nTimeInitial; - - //The properties of the current tooltip - CPoint m_ptOriginal; - - CRgn m_rgnBalloon; - CRgn m_rgnShadow; - - CSize m_szBalloonIcon; //the size of the current icon - CSize m_szBalloonText; //the size of the tool tip's text - CSize m_szCloseButton; - - CRect m_rtCloseButton; //the rect for the close button - BOOL m_bButtonPushed; - - CCursor m_Cursor; - - BALLOON_INFO m_pToolInfo; //info of the current tooltip - - -}; - - - - - - - - - - - - - - - - - - diff --git a/src/Utils/MiscUI/StandAloneDlg.h b/src/Utils/MiscUI/StandAloneDlg.h index 4818f5e29..4421926ee 100644 --- a/src/Utils/MiscUI/StandAloneDlg.h +++ b/src/Utils/MiscUI/StandAloneDlg.h @@ -19,7 +19,6 @@ #pragma once #include "ResizableDialog.h" -#include "Balloon.h" #pragma comment(lib, "htmlhelp.lib") @@ -158,11 +157,22 @@ protected: } /** - * Display a balloon with close button, anchored at a given control on this dialog. + * Display a balloon with close button, anchored at a given edit control on this dialog. */ - void ShowBalloon(UINT nIdControl, UINT nIdText, LPCTSTR szIcon = IDI_EXCLAMATION) + void ShowEditBalloon(UINT nIdControl, UINT nIdText, UINT nIdTitle, int nIcon = TTI_WARNING) { - CBalloon::ShowBalloon(this, CBalloon::GetCtrlCentre(this, nIdControl), nIdText, TRUE, szIcon); + CString text(MAKEINTRESOURCE(nIdText)); + CString title(MAKEINTRESOURCE(nIdTitle)); + ShowEditBalloon(nIdControl, text, title, nIcon); + } + void ShowEditBalloon(UINT nIdControl, const CString& text, const CString& title, int nIcon = TTI_WARNING) + { + EDITBALLOONTIP bt; + bt.cbStruct = sizeof(bt); + bt.pszText = text; + bt.pszTitle = title; + bt.ttiIcon = nIcon; + SendDlgItemMessage(nIdControl, EM_SHOWBALLOONTIP, 0, (LPARAM)&bt); } /** diff --git a/src/Utils/MiscUI/Tooltip.cpp b/src/Utils/MiscUI/Tooltip.cpp index 415d1b3d4..0b4bff334 100644 --- a/src/Utils/MiscUI/Tooltip.cpp +++ b/src/Utils/MiscUI/Tooltip.cpp @@ -1,6 +1,7 @@ // TortoiseGit - a Windows shell extension for easy version control -// Copyright (C) 2008 - TortoiseSVN +// Copyright (C) 2011 - Sven Strickroth +// Copyright (C) 2008-2011 - TortoiseSVN // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -36,12 +37,12 @@ BOOL CToolTips::OnTtnNeedText(NMHDR *pNMHDR, LRESULT *pResult) if (lpnmtdi->uFlags & TTF_IDISHWND) { - // idFrom is actually the HWND of the tool + // idFrom is actually the HWND of the tool nID = ::GetDlgCtrlID((HWND)nID); } - if (toolTextMap.find(nID) != toolTextMap.end()) + if (toolTextMap.find((unsigned int)nID) != toolTextMap.end()) { - lpnmtdi->lpszText = (LPTSTR)(LPCTSTR)(CString)toolTextMap[nID]; + lpnmtdi->lpszText = (LPTSTR)(LPCTSTR)(CString)toolTextMap[(unsigned int)nID]; lpnmtdi->hinst = AfxGetResourceHandle(); *pResult = 0; return TRUE; @@ -52,12 +53,7 @@ BOOL CToolTips::OnTtnNeedText(NMHDR *pNMHDR, LRESULT *pResult) BOOL CToolTips::AddTool(CWnd* pWnd, UINT nIDText, LPCRECT lpRectTool /* = NULL */, UINT_PTR nIDTool /* = 0 */) { - CString sTemp; - sTemp.LoadString(nIDText); - // tooltips can't handle \t and single \n, only spaces and \r\n - sTemp.Replace('\t', ' '); - sTemp.Replace(_T("\r\n"), _T("\n")); - sTemp.Replace(_T("\n"), _T("\r\n")); + CString sTemp = LoadTooltip(nIDText); toolTextMap[::GetDlgCtrlID(pWnd->GetSafeHwnd())] = sTemp; return CToolTipCtrl::AddTool(pWnd, LPSTR_TEXTCALLBACK, lpRectTool, nIDTool); } @@ -79,4 +75,116 @@ void CToolTips::AddTool(int nIdWnd, CString sBalloonTipText, LPCRECT lpRectTool AddTool(((CDialog*)m_pParentWnd)->GetDlgItem(nIdWnd), sBalloonTipText, lpRectTool, nIDTool); } +void CToolTips::DelTool( CWnd* pWnd, UINT_PTR nIDTool /* = 0 */) +{ + toolTextMap.erase(::GetDlgCtrlID(pWnd->GetSafeHwnd())); + return CToolTipCtrl::DelTool(pWnd, nIDTool); +} + +void CToolTips::DelTool( int nIdWnd, UINT_PTR nIDTool /* = 0 */) +{ + return DelTool(((CDialog*)m_pParentWnd)->GetDlgItem(nIdWnd), nIDTool); +} + +BOOL CToolTips::ShowBalloon(CWnd *pWnd, UINT nIDText, UINT nIDTitle, UINT icon /* = 0 */) +{ + CString sTemp = LoadTooltip(nIDText); + + const HWND hwndTT = CreateWindow + ( + TOOLTIPS_CLASS, + TEXT(""), + TTS_NOPREFIX|TTS_BALLOON|TTS_ALWAYSTIP|TTS_CLOSE, + CW_USEDEFAULT, CW_USEDEFAULT, + CW_USEDEFAULT, CW_USEDEFAULT, + pWnd->GetSafeHwnd(), + NULL, + NULL, + NULL + ); + if (hwndTT == NULL) + return FALSE; + + TOOLINFO ti = { 0 }; + ti.cbSize = sizeof(ti); + ti.uFlags = TTF_TRACK | TTF_IDISHWND | TTF_PARSELINKS; + ti.hwnd = pWnd->GetSafeHwnd(); + ti.lpszText = sTemp.GetBuffer(); + ::SendMessage(hwndTT, TTM_ADDTOOL, 0, (LPARAM)&ti); + ::SendMessage(hwndTT, TTM_SETTITLE, icon, (LPARAM)(LPCTSTR)CString(MAKEINTRESOURCE(nIDTitle))); + ::SendMessage(hwndTT, TTM_SETMAXTIPWIDTH, 0, 800); + + // Position the tooltip below the control + RECT rc; + ::GetWindowRect(pWnd->GetSafeHwnd(), &rc); + ::SendMessage(hwndTT, TTM_TRACKPOSITION, 0, MAKELONG(rc.left + 10, rc.bottom)); + + // Show the tooltip + ::SendMessage(hwndTT, TTM_TRACKACTIVATE, TRUE, (LPARAM)&ti); + + return TRUE; +} +void CToolTips::ShowBalloon(int nIdWnd, UINT nIdText, UINT nIDTitle, UINT icon /* = 0 */) +{ + ShowBalloon(((CDialog*)m_pParentWnd)->GetDlgItem(nIdWnd), nIdText, nIDTitle, icon); +} + +CString CToolTips::LoadTooltip( UINT nIDText ) +{ + CString sTemp; + sTemp.LoadString(nIDText); + // tooltips can't handle \t and single \n, only spaces and \r\n + sTemp.Replace('\t', ' '); + sTemp.Replace(_T("\r\n"), _T("\n")); + sTemp.Replace(_T("\n"), _T("\r\n")); + return sTemp; +} + +void CToolTips::RelayEvent(LPMSG lpMsg, CWnd * dlgWnd) +{ + if(dlgWnd && ((lpMsg->message == WM_MOUSEMOVE) || (lpMsg->message == WM_NCMOUSEMOVE)) && (lpMsg->hwnd == dlgWnd->m_hWnd)) + { + // allow tooltips for disabled controls + CRect rect; + POINT pt; + + pt.x = LOWORD(lpMsg->lParam); + pt.y = HIWORD(lpMsg->lParam); + + for (std::map::iterator it = toolTextMap.begin(); it != toolTextMap.end(); ++it) + { + CWnd * pWndCtrl = dlgWnd->GetDlgItem(it->first); + pWndCtrl->GetWindowRect(&rect); + if (lpMsg->message == WM_MOUSEMOVE) + dlgWnd->ScreenToClient(&rect); + + if(rect.PtInRect(pt) ) + { + // save the original parameters + HWND origHwnd = lpMsg->hwnd; + LPARAM origLParam = lpMsg->lParam; + + // translate and relay the mouse move message to + // the tooltip control as if they were sent + // by the disabled control + lpMsg->hwnd = pWndCtrl->m_hWnd; + + if (lpMsg->message == WM_MOUSEMOVE) + dlgWnd->ClientToScreen(&pt); + pWndCtrl->ScreenToClient(&pt); + lpMsg->lParam = MAKELPARAM(pt.x, pt.y); + + __super::RelayEvent(lpMsg); + + // restore the original parameters + lpMsg->hwnd = origHwnd; + lpMsg->lParam = origLParam; + return; + } + } + } + + // Let the ToolTip process this message. + __super::RelayEvent(lpMsg); +} diff --git a/src/Utils/MiscUI/Tooltip.h b/src/Utils/MiscUI/Tooltip.h index a0d1ea9e9..c72397735 100644 --- a/src/Utils/MiscUI/Tooltip.h +++ b/src/Utils/MiscUI/Tooltip.h @@ -1,6 +1,6 @@ // TortoiseGit - a Windows shell extension for easy version control -// Copyright (C) 2008 - TortoiseSVN +// Copyright (C) 2008, 2010-2011 - TortoiseSVN // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -31,12 +31,13 @@ class CToolTips : public CToolTipCtrl { // Construction public: - virtual BOOL Create(CWnd* pParentWnd, DWORD dwStyle = 0) - { + virtual BOOL Create(CWnd* pParentWnd, DWORD dwStyle = 0) + { m_pParentWnd = pParentWnd; m_pParentWnd->EnableToolTips(); BOOL bRet = CToolTipCtrl::Create(pParentWnd, dwStyle); SetMaxTipWidth(600); + SetDelayTime (TTDT_AUTOPOP, 30000); return bRet; } CToolTips() : CToolTipCtrl(), m_pParentWnd(NULL) {} @@ -46,6 +47,12 @@ public: BOOL AddTool(CWnd* pWnd, LPCTSTR lpszText = LPSTR_TEXTCALLBACK, LPCRECT lpRectTool = NULL, UINT_PTR nIDTool = 0); void AddTool(int nIdWnd, UINT nIdText, LPCRECT lpRectTool = NULL, UINT_PTR nIDTool = 0); void AddTool(int nIdWnd, CString sBalloonTipText, LPCRECT lpRectTool = NULL, UINT_PTR nIDTool = 0); + void DelTool(CWnd* pWnd, UINT_PTR nIDTool = 0); + void DelTool(int nIdWnd, UINT_PTR nIDTool = 0); + + static BOOL ShowBalloon(CWnd* pWnd, UINT nIDText, UINT nIDTitle, UINT icon = 0); + void ShowBalloon(int nIdWnd, UINT nIdText, UINT nIDTitle, UINT icon = 0); + void RelayEvent(LPMSG lpMsg, CWnd * dlgWnd = NULL); DECLARE_MESSAGE_MAP() afx_msg BOOL OnTtnNeedText(NMHDR *pNMHDR, LRESULT *pResult); @@ -53,22 +60,6 @@ public: private: CWnd * m_pParentWnd; std::map toolTextMap; -}; - - - - - - - - - - - - - - - - - + static CString LoadTooltip( UINT nIDText ); +}; diff --git a/src/Utils/MiscUI/balloon_box.jpg b/src/Utils/MiscUI/balloon_box.jpg deleted file mode 100644 index 8aa50d83902c3b2af97cde6cf925bc0d281ddfd5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcwPel00001 literal 20561 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 diff --git a/src/Utils/MiscUI/balloon_tooltip.jpg b/src/Utils/MiscUI/balloon_tooltip.jpg deleted file mode 100644 index 558b9af7797bfbf2510df858a7f7132c0d65d96b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcwPel00001 literal 32181 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�mIlW33JsT^N=^<&qZEGp%hv<^w6yRcG%7%M%Sh(NT+R{IQC4K}` zUe{c8x{3e6nQ${$*sXr*ZRyY3W%K5#`!CiVInngw&%@$Pjde$R_(GsPt@i5%`3BivKRx31#p2!Afcn$lC||{MZa&9{hNk zA7Eqt0ewq;7$g48sO({P5$czCy-H=*-hi`=3e@9LllH4|3&P{TA1QR!UaG9@yy2jn z=^96KZ66xSeofa_gjtAHRCtST{X;%lQOdK7QZ&ShkJ1HT-+*cD>t%>UxTQH(Qu&cG3U- zm=xCX^A)aMY0#|WqJzvyYMhfem0w8gAl2&+oT%Eo=|Qpkx-A?= zQ}G6AJ2xAK;%C@dna?$q3|`|19v;*@IH@6tO-=*4NlAs;lA}$=bZXSItP6$l0(hAj zZ?njXY;r!Ij>zSsL`-~Z*W$-A9rCN_qPD^sS-x|sFrcPk-tQcU8j^UBpo55%7xWLQ z1u@}9x`B8>`6_jGC@?+xutYd9T%M~|SCl7GOB@fmK}SASJHBL-mVS$9=_A7TYPQ=V zEfX|h%1pXz4ZM^sYjbUlpn!10r8>M8mBuiT-5W}!SY>801i5U^D0REMSXQ<0iIF*h zKj#&b+)2R^g{HLeg~K!=#AfBer^V1-y=3qT_=W_U2<3jK^ymiR5NFkESe=CsM_H86 zq@l;2)YxI7EQ6zCIVL2#&)b;FZXc%QWIBLV@BcLqlUZ~&P{hsUJ}tq=(#kAriIl_| zRwc=RT9Ub3s}Dkh;KaOHvNIwo%AO`aK57}68mJcH7_?!SC?3i;QVvH5B_lOAeON)t z3k@q5Q$`#11P|tUWIMTy5`4;t99VX9iSXu~Eo)riMYdIvyL$x=NvU%hKGB_Wc%pir zG;e$HJkclIJH{i1juP9`*l;-!ne8C-k#D-}(KR*0Rd`L2Yio`R zyQHUU0Zux7HR>};@h7DGJm=9yKe6J*K#kEpvA`8Op^0#Ugc`aBwrz3x>R$BUU0y6D zG`)c)%#@AT#`BX2$a28q=(lWUTUg67Bzx`Fomdq60CgZVfO8A@j7n^$WJS?{!f1iS@J7%dw{Vo`#i|tEZs!z`_sW67 z=HCE`)2}h+E^oRz1ae!Q&JCpSNZDOuzR`mqogbP__#qK?H@?B-f0Lp5x_vjKCTYJ- zz3#uM#4XP*1=6i}&Czeb>ym;#e#jrv0?PN{ho8O50EU#{W2mI#`zo%BlD&ppTNhVx zmhnyU2ytS}u(J*ByNlzQr56QNu+kH{DwUI|c!GPzT;=r$$>(&T+9~RUjTCzqi*yg3 z_Zo(Z^2Tf2*w5mkGE#p3C2wuN0X}~h$L6}d4{_A{yEyI(yF&vm2|rv^^&ED2Xm)cM z|Dm3?-P?bh6N}#eP(JTk>W_mY2c-M-#P(r*goSZy7!i0t4(veMRfEpRn*k#8eU2GDucsPWZ{Tt zrWH-JbYXbtnDmJ(VoZ3@JEYLI+#>p}tJ4D%z9-VrzD1{)vb@8;0r<2C^#<*rl>I)x z0oO&tUus{k6xv3K-5%)Llf*ai?~|4c7(0Q2zk1zXCxeR#@(xj67F7r{F?}aiEDcVD zoV1JJoc{7dl!e$|uhmP%%gxVAoO%wsjoZPrTd*wsBL^olsgM_9$k(90zI@MN# zMD@0Do!NUQ4XuTsOcbb9%{v}4y%xB|Xg8ZGb1M2v;gc^(po@%$p+hLkqA$d3EZdAs z3ai+1IL=(1;yCwt`gkhUG|Of8ri#1Yxh#}T_IbT(p70GE!QzMDU~Y@8mQx=N{l;3ZPF;Jj})gH z^#Ebh+$9JOSnRfVhc)jdl%MFfHu0Y*6C&?-Fzx zZg>yzRjD6A?y|s{Rg6V#3Q7qMEd(WY-qS8wl&-dAN)!z$H8J}YJJ*?^QuZ0haCuL+ zahCevI_FCZA0Cm)4OsI|nqx*AUH z366Dg$)h<7d|y*?R#>mF^lxMrnv;oxNpoI|G8Wa2ErW9r6i)S18R$9}Zq|vOUB8h63m+wk# zwrLyf5am*=sW3=Rl2Vuuv^uv8qt}6x0OWwH5I_`(5{1s%qBaL(8G+O=Sc~900&35~ zgjmpUn}TOrR!~#qH{LZne@QejA6kp+n}?sTo$1jU25v=kF5#6{#aFzqSSE zxArzRK4M4uH}#%b7g+?|d7DGxQ`LMT1JQL#a2~i~5~a{2A!s|m>9Sum?R@OGrsa;m z#F7>lNANaH(Oak-A>Mh=dGoP$-6m}M#VW8!Nz&7u%FN-UNKRn64nZr+^uc3RJx_%P zZEBXiEz+r8eqbTZAhy*>R5QY_A|21=nxD*SWP( zucWv~Jqjnehm{j8vN6y}gR0urWfah}ncIXI24M^oOo-myf+RDto;az}cqstCdd-+H zR>o>6fP^dE$^3GrnUAXa0V=||Bbsfse+x$nwzv$dr*J4SC4Zc0v0#)s`C)fnUgB1W zm@Fxpo}gE?mkw-diIhQOZz{d%@vs1=!4d~HA`9m^T`aOX*fQ1n*h<~mW13{}H-IYj zE_vXnhD%W`H0a3c5~+*Ohd8}ohQG{K`{AUtRvx$H3LW^VkxzLutpXSJw77I{$?V5L z22Y}5X`2RW{adv0RBqzP+qrN+g|-Lx2vZ&hp6XUESPgp8Wbc5f(t78rOG9yJXB^gx z$$3!w$a;v->WbE#D_V0RIGeM#@szElO9fW&H-1OrtbX)%cU^%ya62B+WqTLV049+r zJh{(7ZEpK1suSw)zGc=2->a)#2vxLOaU;Cx8-mO*roO=6NYG+TVFymIRCH=cCISzF z1qwwm5W$tyRlvxTuPA8u5JkBQxArl()gw-C{edo}YHIPbqAI6~X6i~TL;7Y_$rybO zwtlHfC^xXwSKB?f?GfWK7+|E-$!MxV>g$5u$17>hr&w)Lx9jSs`79iQYzq?6N|wJM z;zD!ZrW?eg9yoSU+R+-BAmmVH+w$;?ZM zB9jJRt%K?s6%s|pT+pFZv{uHVW=(0R_0ts5)p<1&U4eVEQMUC&nbC}Vj>a->gf6}f zjI0hn7%pVp@|d$v=3EWyYAz}h5o!xfEi`C$g(%i591FIKAMWc9Z!X5dK4a`apg4?S zLHyF@WPIwt1yxT74^?%^8WP5?eZUQIIL>J;>Z?(zcYsK*p`MbgBY=(bL}+!BX{E59 zKODy!wPzFP;7y={l*HAso}^(7ZgysxOfOPuWE+{Y@t7@EiM|%x4)4eza5*%lA;$tY0)xStCVJ>&72}mywq!!XH`|Eoc*^eTr88-N)O=d^ z#OQ3}bRD3(Jd9tP4&vp>Jdgxmbe<2^RHD#;jK**mDjnR5uQol@$nOE7qbtWlpZ{3> zu@#rmd~wYRub5jd6_%x^s0h9L#-<+j%haM(v|yR8H-@AlQQ7@EtlSZ1L`@n*1Vq>_ z9$mJyF=?{H+q}ZcPGCtjUkQOnS84^Q5+DidD#Xl^SXQsfZalGaj8;elDkdWV5V}On zU93-Vyvf@P9z*}i2|0ksZ8XgQ13jyumBgi^Uuat;m*n4;>@oRcbYZqhWfPZLeqoBD z?5P>H90pZ|fn1weG$To0O<*Dl$B12a6&-Vm8x>iMFes&flR=i&mB3^%-p&qNOi*m0 zDZbN4UO?Gg?IJ7pd`b(21*fwz5^p`$GMDdjgsr^>NfO6Ngb_{QQb4_HaB6T7^_uPC zdbnp!9@*C9%(a5EG)Ls|a1Hbcr!7`~x+tlOD(O*?!Uws{4X0B^ESglKjEzC2v@XWTXtwOHMH8)Z9 z$&$GBkh8AfOj0TVI4_Bchp|(uXrRlO41F>Db(_Da3M>eh>so!DCw25`8sY8M+MgU5 z@NlNI4T&;<4Y%Dr#n--#oh83rLFGh-A!_pi^ zx|}UZkz7`2o(6}0W}+fCImS6|9FYvBxx~tDZV3_&x&8t)Ik;s1{v`*U5yDP^hE_yt zGzYR!X!f(=%UaXTk-DURLs zwrt*BbaDutdHSLqON@-M&jHDDs623#9Yq9SvC@~qqm=|#Q1<0Q4au!6Sl=S1tLYxd z8bP@AK7VJDl9lPH!2c0sr1sU+zby)Z@CAMZYd0<9?O1yBB})U;77p)hvUDh0Qqdlv zdpTdpuHKERt{Y?2*(S@&%%R#fecX%GkbpI|@PLM#aFV->Xf7cEB7J%YuG~8F&HRF z1r5mD#S@OCp>zL^{?)|A}3DsoBtx3eAf{I-No4V3{W)cqD1zRrZp0nWbxx>v8BgKxfDX`Ot0^az#eSq`jF z$R$+|rH3)q2;_>Sjwk7odkyK{GZ^NxXHH^d7e;ZAsAARXXrzpUB)(wbE0UbPPft1c4;liXO6MDO@$ zI@+JgeLB~hX^)mFY%;%#VK|xLqbh>)_Kge#iL0-dAwh5;;GSVtoY!NTR5o{<%K*&#v^2LwtltGL=OQG0Oq2WuWW!R1-wk{C{sPsc}S z0>0=ms+2OHqHjE8b~B&jFuXm9R>ZqnrxBT`IyY1I34QVdrC>_!0f2Ofh%2+2aVYMw zC-;r>%Wbq*$PRB%aVpuztJcvgODy`|EDGIF98)5Gle-a>okHfM5DDC%*xhVl?XbC1WZEt96ry7DW?9TW=_@ z^N`I>uEuhFb8@T|x+sPS?C3yX2un_!XMyW+sY_&rsTG~Rcn}NL5CFfm|7g30{&B}v zYHhnPgb}?KPPoE(0k$PBCjWle48vH92hZ`R$+syqL^OU0LYF8z@{yABi;`AM&59Iy zep#(_61j$~EOwqlZQ`|h+x%1d37q#)mmVdURx}Yb<&eO9$WQoWXFAzhqO@=Y*pcj2 zaHjOYn^FcDa4)H;bM^azwO(aZv?>dhfFx>n+6YD1YE&0V$laG};S18nsy+*WjpO0bssb>bi-VVS|qGRlB&etn>zV3qh*H?-(< z-$-Fh95M)PK#27ME17L<(nCx7$Y&^iebOK>U;QF0CnqP7DZ25Q1u?+qAaVBun`$Pv zahKLgfqLJvorGRmbmHd4*MxKM2)xEPt^02EV1#y3!eCpQFv*e0h4n;vbD6wx zV-bg+3;r{X~AkC$ic-LjvLuO>-v;J`Stx&iN z*K?G&riQr(unfI8nKdj38{f4A2Yt#3TaFkR-_(pPi|W=POX@b=&qYaG zu%no?97+)e{|;-}%(kF7jDhR`)_>T1VT?lA9&a)Z6%cOnsf%o#Y93x8Vxt#q1qf$( zx6^;4S0~z(VE*o9j6PnxZv#myET=(Z5Y=Sm^~+!7t_F;tuqfA31BNNXLwiVySu|Yv zhw*BBTs&sjp!dZr;=R<^{-v`Gj)V2}VpL~SljGiYWKX?g8r2lb@|tNpJpJb9zAq`L z{QebCc9_Wqis>wye-Pif;>B;C^I4!Nx zD)3=X4OfQ6Q-}4J$WNnW+R3z1kI<9__Gt_qP(a+yI$E)sBb(#5l`)d9_NpSH4{^4Y)#cA%S+~GQd$Ouk^{4}y!NOp8qKX*i( zsFq<5q`c6jYw1|MnnER90{Wv`=UW`z^|7$|!#JkXN{HA*nI4i^-Q=B@TA@L{=Gs z;z1n^i-q)w!B+HEC-0JD1n{Bm844N^UnKYHoSflBe(Esj;*a6_Ldytt9bx>C1GDdc zj`2J!oleHGm`Z%wN`k1Kt~`yku|7SEoJTV*gZ0lCUe5x~-y^h?YD)8jwXq-)!=a6i zhZV)kE*K>jyG^Tz3h~MIYTk*NN@r8gtkL}jtfjcoH7s71TtCJ-NNi@ffoSdpR{A$s zdr_Iy)icw>V5JweULvVftN)^`Ryv}d)80}4BG9v{0ZOsB&0e=;lVA$Js_A7uS!nFd zi;lBc1v|>Nx7Nd8rXW$QAnl+WqkRO-1GDGgo-l5KC*kRvy2gbtCfqntThR!sF|IME z!ps&uPywtKthk$&dUxONLO?81Qwij5H3Cu95On+xnsM$1o0Wt#%#v9ewM}edECiH$~;- zTde6H%X~ryR7&jF;ApgJFynC9A_oWSj(&zbZI<(EV+@j;O5UDib>2p?IFqhknLXslnzrmIA+y3v z8aIa)0cMMq5;DwJn`h zXmCwpDay4TE{{fO;qjn*gb5~(MeX6fBwEHr_uyB{}``|7Cf z21SKMbXNv#ux3(Hvej0+Hgs*Y@|2Z>c$rp+h=jIB8>G9z0w%j<_% zM>bNFJnaUJ{-X$DqEzy}6V{f5c=Z7lcx7s$<6TgbL{#G#UdB)UBhG4>(j8L-!j8gp zn)~R4SUGP~p` zYPg7t>^DI2U~0BCvoyBOc`_ltMz*2WjUu`M8`+VH*Bc6S%=q?(85OByMpNp*T^Ajr zs(4-@u2+6CZPjpcv~K{$pGS7Nb zE}CBuMQpClv@KebGuR&fmlzC5-8fAeZW>GMY33k+IZ&KzXzw8?&;$e@!MKV{#+%x% zke;5dk7YwVH;P?~pVg=<$UF@hw1D8p#2U70nIB1RqllcaqseCCwDio^)bN`X-x7h+ zKHQiW53@l}E%UWj3qhsHY7<$>CE%_ib#iIBxhD4@A2Qcj1JD9*=hc5Q;S@+~-=hDg?p#s9oZd+v$R7E`iB=aWu_&q=@Y@xqbZq_pZ;{I;fI?upap*R)uPzS)eK zA5%%whHRNmA6P`FCb&3>;(-9jDirv&(93MFlaIOukvVpbro4M>;xN z*^SRd@5IyjO(BjJ_+C#xo2^pR%x?b=7fw5=4v2dfVCTt6x!x|`Gdbe^T%%m)ZvaJ7 zGj${GR1JtXzKffuCbw+qmWw;w@mWRt+`LNn0QbEV{%Dcqw<4P{8?n2J9zF4ooXOG3 zmYg3Do_S6$?;92wZxFpvKGO44C)NDNScR7$7XW-UspZ7As3SGMuIRS*PI|oJ1v{QU z@GWkhSo{-T!@pGVXAgGiSK%Cyyp41Tm&f3kl$z0YsrCOH@oO7T+jEDf>c*dD%?~&V zJU;j@sII<}mR0%<_;fT&{LG1V$(rpX z2PDvoJJK#8A1fK|?H`hT6>Likd3J3qJXq8Pp(keMBYf~>h)uc}wJBY)t%xc9a)kw_ z2>0wwz2!B-Q)MygP}!!9N9a7rdAZMdWA}l%zQTRyGV4XL3xf&sTvW|1rb@6jTh9d=v!#{<;}hG)mqebtzWkxO?!toJDvQeT~Bh@ zRo<7c&+Jt(a&M)*i%%LodbQ{}TVJC89KGC9J>*R|=3WZD&fLYtZoBYJUlGd6q7HCnhY2;w-h`+v@BhXA{{lu$Y2fAqeG%*`rpRC>3@R$IqZ9DzSN}i=V|c|inigf;2udT+K1-+|=8kB{UdIWYo z0HKZ`w(mG_Qax9aMii`=${e*LO6upP$AQh7$$7S0#uG|U*qu3@KnO5OpcreT8;1jA z_*ie336Tn@5Cwa0JgI-|k!cDR7MN>&ccWLtc2eu)iA?90<}=KF%st_{_LE6vpP=f? z^vA}f#05#mnPFw5QFQ_urJKhZ2d~>^?oR5IlBG9-#Y;vX)g#U#miy=JV%!e?faV&3 zP&9B6H-ME^rP0r^BAS$xKZig5i@TK2_KRbM;}=uUB_(5cGDf5CKDJIEDghssj(zl zM0eb#?z-Xu(?N;MLg1;eD}gj@Es@=Tl6=U@m{t8+)WLwiNlnh3@Xt zfvuCLGNXLeGhfAq?rYVQos`MRL$?gC3jRC8c8aVC;SiAO-!a2fa9ZAtOf zL@CVu<i)4FwfkZ`g9MGK2K8!<_GPE z1=#7f54GD~;%ifDR~O*1`jMh$OK<#fT}yd+Giw;KVeACfzitbCJbmIyREqEtbWYK1 zuSscXRKxMVBCCp8mnyL7Y#L99Q&VyhN>9A-rfRR@y}R<`oW*V%qjcC*V?M)yuY1hN z8ko0TZH3FM6749eDAdYnU6kF;UQ*D)UDZF&OH0U&aaq|?R{|?8aV>?IbZdSb9+sh3 zmfcA*!1tC`w+J&6RhsyPeuGs;S`59eDY-+ns9Mb_r!*zcyWDIX)Kto`4Z@U_1yh_IY{TOeT zMmn@?8USID(stBapZZg0uc_##m2yL7_mN9Lr|-QxkniYpwaRqP4{7KOijM$Y-s4%H z^2Xb!0@l^rM@N9{ow%Wx8$FW$TPG%{sYVr>p`WnTV?v0nj{Z@>P}vo~w$GuK2nZRdL16Jq75EbV%90{>Y!f1tCbWRsXbjUb3XPHWSgFRwNJy~iTok(J+i zR$*@kkLHgnyTPMBeD^BA*7(0P*LBuu&E7Z*zS18TIB|3g5EhNW;A~z{BHcCgZgoHN zS!UlgS}(m=a0~lNf5s<#+iH~eSj#d)Sntx=Td$xze|{iIx{z9lc=+rKecYg|DanmQ zS;~A4Wx?@r`-a2AKmXbJM+mzWia9%8c!GPc4=|RF8Q*sI>GF(kBsx!#|4M>KX|BK zr_EI>>GB=%(I{|uEvOKGjBOD7^0hiGyW&(GieHn$PtyqK-z8joF87*TSp36TfBP{+ zNm7S6HR1uehaP;|5@X_X{w+Ql(<605BEYw=0;>HBV_Qkbrq&~UL&j7IR3~AQyhmF) z*a|;KrKLEvxk$u=vj5D)W4oO&m)JfJmC3o41!I;R64LwCX-UVV)+1j-%2W>28{y)I zTN=-=2eL$jaApv53SO%@Dl_QU5K$xqkcQZT_96zPH$R%}+lQW+3`w~5S8ArOZtvGP zB^{kIsE==&-%BEtG^}{QW!4&4bvjPi_>1{(qr-{ezsw1M5G1+_K7y2cjV~jIt%(8n zcZ37+v-Mr%Jv>t=UXd5SofBp#QS0C*L_Dxw7ToE5dRBuwNhR##p-T8MS?J@;CHM$l z?lrcIY|s!0E?oHjDdyRKk?+7#{j}#XX1WZoI7DOiOX=6K8JSn9ldqqP#jl(5!zs)f z0{N%hpv8Ulc9B$`k}74+cG-Bm(P~5sm7utgblimc zm|YfIyiph#fpy}_K)O-7D5?x(sZ`0tlQ1fF{N|T$n9AvQLD9}tU!2P&WV5Uc2Ar5> z41c4DZODTYA_{|<0_kZ06LKJmDmOHrD_SLuWnbJI)WXY}FNZ9xB6rvgYx#oVfTN-< znYC{OB!jmV2^0uRa+yP+P)dY`3zNW0gH>5u>Nr5oT90sJEWO?$Y2dO^gd+0A!B5(j zv&^`scXM8DD0PIo$y?RAKP>AOT6U9l7WP$r+orOTwDy*{e@!x~7rX50+2HGDNQs_{ z2vSwRkjhd@j)OEVt+*9Zu}-p0FI`~Xk4*eZw@f`^&jL#`2~f!hEH8$V6yTAf68=8B zy}aH&*k2UVD3Mu|++6nPuo;cNOZXPRSKV-d@>x;~#pF6uI6xM9(iPKq&fcscUJAZn zI9+!wAP!wR?9g({3{xn%j&*qoB5R^5ce(T=U1mW5W`Cs+E=^QWiEUpBfS`8D+#{Yq zkhg_S?3hi%r(o&4g#HUO*XGB~_v<7<&TU@4 zkxQ9%v7(A~v+R~PU9I5hP1$uV+$Y7vi0IM|G>=e;lmr%($-$v3M^W`gXW-{?58zPWM;L8c zG}TaF8@zW;B92bDH889VuY{DHrnO|#wZe>_A3wp1> z*Q?xIZMVBcj8|gEE~i`eYc)5c;0$fHKW9pnXgVg;E2*qoNSNQx==5DJV4s902a7U^ zz#3BgI15Xb|3>PEH!zhmUIXP^=JwFoRAc_LD4jE*-28;clg|SJUIbZZ+yt385hZa5 z6eJpy1Oj=p+a9ryIs4d#!MqF??F+(s-=h@LUi5qfrgv>JEAu4;tC~SI8xZ-%@g99+ z!ty=5*sx-#W7lA|kp225Y^lDYn zo*Aw%u#^gIS_G$)fX{J(@jh>9k*chAyXtA>l65p4Z7f!FHM|mKP0Rvgm@|8ii&{>x z7~2Gl*vyZ=G%A2e=z%W8MrT?#4)ldEwBOuU&k~od2a>|4mo_J&1Y*Ed+OGa1HJr z+zG**!7cbOFc4&L3GTrmI0V-q!69s(|NCy$^KR9?TeY9|!+z^Nr@OjO_dRvHyMDKB zrZ3$SX(XS6Z?uBU3TZ`l7DExbK0e)wL{BV`7hOYMP~3AoVT2+!yw^B^pVqJSyGT`| z$GSn#@mjg#o9$UM-N8ZGaXx(#0v|!3>&9TRR7ka^qsEH@-w_(XyUa67n%Kg*Qm(z=NvSB85FDk>Phr(Im!O-<>X@UkH~{NR#t93;@2II`~^?K z8^Kauso>f5aN$rrt6!T9&wzskOM{1WUpW1H(U$+Su8A*ZE2-5zuDr@O)A`*fu_ z8@WnG<~%_b$*j|>AM&RAq))r?eTr6@I5zxo7R`l!U42#MFO)pTFV?T+4FiqWDD+M@ zTvlXfZ(Z)3c5_z6<~Sf^i5D$*h6!YV5U?wAF+&+dF?YmfT)2aBRySXJxmvN zL_Cu6qm&+xX2&mtv8!XI#|DEuSt+|tg)wCdKk#g;3xeZ{bvnbRuM)vqQZ&L`R}ICy z;$ee@Sx|e#L<#qKC*BGF#!j*=ISaGiWx^^4WY%wLDMyv5qq?=}MQb2LZTaU>IXEf!x73rojBB84!&;Aw`2l)nXp zoN|4tF{$#G+nWdUnf6PObSCoZ{uU9nIX|H;(D3<6&dP&N&W%mu54-@)*;)*d&y~J2 zP|T6B&&_Sr=li72pUUHs1~1~?VoIq)Akfs*d0)n2g4mhgIFB_5Uue}2_PN52M?A8Y zGo+F9#c9z^UL?;I^j9%{Jine?^D7&lZvrJ>D}XJY0t6(d6mA2L#LU5Ai`HR#sm^tF z%?~4o8L6(?1UH7a-XZ9!ysDE&3K91iEGR2qK{t|ahpwYW-lYA2?e5-Y(PsmSwH{ER zK|0hw3HH{NpH7_kpr*JLcsrE6g?$`nsan=jQ&fGm2JGRLe+Y zlpAKF{W4U1vtX`zfo2G{}`Wr?oWY zWoe?@S!PV5!4n;LSQPa8;gsuRqqdrnQOz_#iEuuI=Qm?5R=Dlylx=`W8#m@nOoVeq zwxqa{KPF*l`^;r46~z5~^HV6?|NVG=ozZ}4gp0cFg7a!QeZBka*w}D);KsVzAXeLa zr{07gkA8sm4}=EK5_0N^&iHcK&*{~YJ+uAn!agtq`8WrM-tahBZ3p{6hJ=AhWY2j7 zP&guq8Z6Ss!H1CW8(uPE!lOt%;{nb$hNQr)%4<%S(0AF=*dW5^0mu zVKz|d(--~T6-mt__>zVGgDjpsR4_+Iqj}urAP2lLB8Lqtb#QcxzV_yBS~*ma6jnZR zhrQ#jzDYYu+VJs=zFGTn!?;RZ*JO!^A3FS*)vPskC1nL0%<`7q3H%K`07ZE8cz+%j zA-bTfjb-2H1=y_2ANX>Us0V_n3f9C%k$r*`D8S)D)Y(FXJXf0p^Swh`G#FV)`Hgp1O3ZSbolaSCA=p z2Mu?<^!)HIrOP1nzYPM;ctt==^aLx}SJC3j!#`CxwvYE2L&q8lUA?5$eK^frR))>`g#JO zCQ7T?wk%pS_#6|O*GSZwqq$zH{rXD5k)9zY>>*rPV&_h*=WzA(zR3%_@Lr9MeNk>P z5^x?(S|ggU20uZYZt9!}?_peH@oRqkveo}qID_fhOMAhjpc8S$2K4lmpTe<4#ok3# z(7P%$W;lPtZX}0EH`R?T_dR3icOr_JMh%wu} z(C=a|_Kj@1ej+$--0tJl3#METx{MU*B0$2)L%8=qZtPbl_G?Td-@64~Mi=cj8|BIz zKt87|3Ct5C1@4#KbTl9UajIY>soLp?3(XMV={OpP}k+H{bO%7v})``Ovk8n@* zV6u|o@d(R+a9o@hgp0qX6G7dGn2a$gtF~=dY4#>RjntWStwc#~bzt*c)h#n0wy7yB zeQh3A9<*4g`>O102k>zf1a-a+8 zc~|VFGD;wTMt5L>?M{&$As^aYMCbHpZwl<{c(97N<-_mKv1VVMy;A6I)56RGs5u^9 zqKAqDgl~@Kge?}aJ{Jo!vmIy}S(yPA%)nmpcTgS>(EJ+VV3*f!ZT(}CzC8trDSUQP z+sUm;%8x_a#=*|^J}hEzxg!OC;>~n~hm?MFwtid{*rKR4A^?^~xFSO9EX9y53vyO% z&YPChiWAp5*qJ&3thS>yX)i?TNvX)ge!mFmczZz&4TL$~|KV~h@D)aTdqLsX@t4JU zD%O90!S~+p#`F3y`&?V5zZg3F5U4bp@&`M&HH|0x{U?htV#D4#X1R_uZ7SXwF8ob9 zigQ&)WywVP9#n$NQYyVztVNzIt@7;1z_%CVO-rQ{1zSYJ-l<=B2<-arhc;0(`gwH) zIiLI2YxPb~wHVWmGl_L+IV2*eHM4wF9+)}Jxr9fb%&~BmHgQYOL_3(?2PE)M~lNj_dy{0AbdL2>Qqf4NIWD>p)Drs zr_;Pw5f=DuXu1}BSx`?tljK{Drz&la6uNiLGobOwJnp(Z0A3C>N@U56H3sMGo9@~kc$}C&dY;McBa*si=KYn;ryu4F#m0YgUh;Y}WjsX%6{OVF zGlsg3uJ_tK17@nxehb*~iK^X7L4gq_f5_GTv; zmW~<{J-<|Ejl=AcRS8P6l_2p4kUGl8s@PIIJ#&Tz)yS`vc7}oWTY>% zKc#PMqkE~1ymY>!-Q6$~eqs5vC>7-$5;yn5=jrH(C9U7NT2=!#Z|2Y(*xdPotS^F#*d=R~On`?Qcb^qq#&P-gPd+6-uveloJETSmYIo?D*v+EO#aVI;o7iLJ z_^myta)jAprxYI^t@T^}Y#LzyLRh+~J^&I=GOgEBp8+%{Xx>XF**5dsLg7%Wu#y%e zdX)*;8P>_cL4$8;PKpQj94qzj`1rYyI5}3W&o7k9c14TBvBNEPLM2p+_SE)cCn$8X z(-bM=N0R7!4CkCXq)XBo^DLK^!3RD@#J(EM`q6k{Akeo_np!LD+5HwvuS3GvXfSV` zkpb?RhkJFmT3x(Ks}E+xoh8pk`(mx;{jaC#wHpK=mqrnGo;O=WcR+l4$wuPBj`jjLlKJqu=+RC8GoF}lFs`Nfs`PM9M6*}Q~ z-`7u>+&x+e!T)R-aQwP*R%7+_JR+dUWumHw5SJrAX}1?iem#nnfS6_XEPYLubh zRGSQRn5Pnl#TIvp>xW5h_e=Y~5xR*vDP|!}(dGLXXKaXb9BuPeM-iIL*pj$cZ4q|} z+os3n9bxnukU*{w7>kji+r~6QvfN5ZQ$YXqg+3*5?ERcuC27|ay|q8F_LTWwmq;uB z{9F=K=d&mpzqlQ)2S4>U1buN}TRtwSYf|0%do9BAVN8eiMpi@D>hq1Br2=dv;J)Hg z`~;}3xqLLxKtwNog=u_gP)1!nhOkU|L(fk4grnr3ZKDI`DPtsPyClH0%_i!=TpCrm z3#=k%lof&|X(H#yKoY4bHwmOk>-R<}>jE}PwsRROthnO>cWZC)i>sp@lshJWa5-TR z|9w_-be!L_t+PJ^T9vXb=KjW$8cJ3dVAx69c0F_{9ehPL?H5`-wdVhGgsUNL#B-^XpHBN22&vdsa4x<(%G?r$`J(qekV#kn7%K8)YDtzaF$z_tVOw!GX}z+-f`8>> z7b5+5D4%VQ`K3%ZTjCkO2MJ`@92)JBY>;VVN!1>~o@6PZu7T2At-ZP-+>hyMy zN8o9&VXe@9atq~jO{|eG%il-kEh*2=o+oYT)!zE^Y-0H+!7aWjGoEc zvvoJf5HQ9HcTa_v_WX;=x#ccSLtWSN{K+*32O?Omqf)|YQ25Fg`cffnVuJ5? zyns`oHZCwEz}>?#Ltj;eFgG~HHS(Y;C!6Q}jH=n6qc_HPY z4queea>QUg*$P*72CF^F`TYmj z_%lGK2-!$7wIxR`YiwcO%x`3f)p}&y?mi)XFG-3)x(`f~yF+-Vj(2!|m5|>!<&-(R z_j0wl`TG90YfxmtB}MeTVVEMjeOgr?(#U?#q+W{byZvgJT&TH_!neQ^Zx&egw~ukF z?x@}G>Nw6h=AvKt3c97M3j|;0Bb-A_&aRWwmuh&Ijq{hjH!_uNisO;uZGKxvW@=Cm z6Zo8TooH~)Yx2D^k65LLkr+dzp{&Tf=%>EBtu(i!OpINbB%uHvH|}Qu;1B?qHf_FL z|9HWJSysf3#cy9-Uh1yjVSb8%#z>!q%3g?>JhXx}>nVMjyrQz%E|BmC&I|kWP|#jl zv9W!ymsR6xkyA1Lx$+4Thy24#!M^W+L{TIT;7%d{5ePYA>^qhlxd#{27xHJz zO`SP!Ly{ChoP?%DB4a2l%3q817-WDObXMHCSw79kPyhgVhBC}6&HCDBxND)W)trcE zk{Bob%At>)v1T!?#bIgkgntxK2*+v*por!h9rwC6xUdu*Zu!ket!Cyl)}&>=t9{a6 zQ&ZBb2Qkyf)9GWeNVVn^nn?x9j1PmDiQOem@B8@TSkYx@8qdr?m>7b_s#UEZ^hyr60p*iKWBH=7mev(9B4|3KbdkvM zRKX1*ud;{ZZQQpujtsNxK5!@>cjU#Zpt&f~;ig|m@>iy+=wcaiabq!%m8oF>z0ri3 zDS;$wP^7kzysuj^cX9yTUP+PyOJ2Fs0yBt@+6xQE!CMAVh0F z)HCqcB`o?tf?K}Ta4}c*3obY2VcyJ2dQh<@iu=KV{Yy%*DmqF<0iLk7y*KhzgtC^F ztvy06aR%DZVZk4;Y5E>Euta={C=0e_Y2@^(5DW$Hqh?uw)1n>wKHPakpQ|B zc}0w@DzxtM$$Fz0hAQEmP2o_S#=4Mh)K(+?oDGe|&p}Ffo0-d=V6xPVSX_5@3Y1h* zGTEJll*%X?8JM&u*X)AsuV#U?YOA))x1Z^Y9~nl(r)7(XatYI7f~MV#@w0lISBCKF zQyd42jXpVK^P7JZ_e3QX!mZ11Je)((x$n83&!SMU}?Z%LIY%; zb2}~FUt6ASDOZ;3)6(#4_>(D-wX+VyXKF`PUmmTH=AfMDD?F8&Jh0nz~*Oe+*rYbCLG$HflMIi#o z21V`uAPSr8R7R4X0i%2u^ocT60ePRsSP5c@IK-$yMuUmaqX6tOm1H)UfF!#tW6Z^TXbuY4dO3FF0gw^17x4F?q z5Ox6FI4qyuD42p@M2~U7(PPV*2W9C#9)yv-FWs)VJ|3mh87d13Kt?eC2dH>1I76@0 z$pZAyU6g5d;QtJ0V)O+Ps}*nyYp}6@Y5alE*8c?Denb^l=`nar{rIQSH^Jj7MU^Ea zSZRXW^5jz3%3Ypgu*Y8JOI=t>Mad^bcMxh!8=g+)rpnHT<;ut1M@x<%1rvqX*ItOL zqNe6(UzSB^4h(;)km^rvC6m3rJpPFuBBnvd*SdviU*GEvd|| z{M1gt8qei`>20-Q>+i5gojmzieg#b?-I^ZVRAYrCtnhEbAwg=JbK(NQ5c^Q{l6L_b^XT>spa+X2P5vAv!&qQN1;zxODbyMAlyR~vy85Y znP)(`5zfBzw7p-!S)?gfn|TziUDa}4l7H~exi)rA2*CNUVX;rq)ru`&T5RJd>KsBo zGdDn7&l>e2pmo%c(oyrlY`qQX_wdD5jbU9v=90q*yXqm$*h>sy*wq@B+ zSQMm^shO$hz}>!Jamu-I!_cR%&{p|nksXQU0#Azc3()w#_Z6P4BWoY$Pe>jI4E1uI_QL<)sn-)DvN$Oq{bBv5~{UGfapt3l6 zR^#o9(q_T94;&>cG0R%J5J!YW-F1A0j(JkixP40Pmvze8m)_CxpgC(?B&u4#LdOIC zNu?|s7SO7IM+_se{XXm-7vtMD0+*L#il}6EYsb``g}0ZC%$o49vB`PP(m1b+d){8s z1pc|`z;}qz9&%t~D?O_!|7lj4D^W`xDyEFRvH=nh4oAZOMY+3L#rSn_O_#khhuieR z3a`qa{Nr^`w`K~Vj99&OcDY>z$)1LN|2JJ;R_P>_ZdL8i-1t>5AiV=->un9*huV%Q zgRDCV>2#;`NraYx&2Z;Ga%0AsM?q_F2mT94^qDdRy}6kCq?XNhoin-wAM)xJ^wb7S zDi&xa z$kW+kw&6sIYRzn(hxG!oF(&ukHx*sMX+HLWruRsiyjvK^IyKyyGChJs*n}edQ>AY? zL01~us(RZZX5juxwB!4$4R-2=Eqx9DA*M)1y3EXgW&y9#3Z3J}U_NmRxB!Zug4&$V zgqbh$lx z>sm6(@?j2DjVWmv(4!H>X%;k~Z1r`1Ovr{Een~MxHO49qR0>KA-x@h1mB7D<$nX6J zHR{CbARG7nWT5EFnTFj#`6Pb}V`{pgP{nZ}+{B2*?m_zv$FL&h6|0P?qo0H8ekr9m zqjrNHlbNpH57VMegx&@i#6bpH>?IlnTD=;G?i^vYDP<*aaDkUsP#YN7r*$PKn z)JTr;i7v8A)akQJJ=djsLDw3Ds!eqkB*P%9C{}(Asf&;eO_gN(?lzUnLkoJxF%n5U zFOo~XceC#9bBQ1MuVrBvW@{8&{X+()l>M9$Zi+f%TNbAB=CUMjY_#%aDkxF|x;3*? zSa-xMb2agi-HSjqJsLZK1AK|S%E_U{Co+u45U;`(u82|###r}hlE(9%vt;;|m0}XkyAiZhyQ+0r>zJ<%z7jEgfj;)tgRDg~ zS;TQ;*SLp6qLL5kC3aS~9F=qTT^rx!>A${}Q7$<_uSI7#Zad~@o^_(l4*9v;7W{4& zy;jc~y!85W3vtExXyIg;gO&Xo41;AJyLI*RIi6vC-h#Le^~qG{!ks;Qg_njeTe*}? zsxnE4HO$@ZN`E8zvf=rPaNl8Xv-zgTjuFdO4z@oRDXbR1!)2-BU8AjI>9js4C$oAp zC{O*Y0D}5~2{P#D-va}l0XNr&+N^K)`>WlCrT0!2GiyAC`$9x>Qq+JE!-w|PU6NaP zM#?SqDJVG>n!KX{v<@C*4c4a>1r^!1xV_xnWA@u*(aSbyz!kb}avpVD@!=duCh;#| z*tOAvocoJbb~Zyj9cgqr&6TtI4{H^rFq2Z>o`58r+G>|uwABe;)lQ{Qh47&D%6c8b zb9*yJ;B0F^3-fAeNosO+hS&j0@#Jpd@VO9>BRhj}{3EM?A$>{BWsDC?Au~)4B4pbu z=aJynE+~$&z}dafm9pLI&00g8kQu0}Kk_}Mq8hjx-FiMRIV3pvF2IJ=K}DsjtU?#N zIK!2vT2f*fzYdLo0Ti&=ruSQET=%U2l3R<=zDqZy9#!`pg0;R=HDDx4&FQ!_TXn3srDP66s77n|+yZD5S~Qd6Bys~qunxSJVm?K6NRR$-dg%C8|T zwmCx+*J>rED$TN}YJx(J#qBkuIN8XExnFPB;U9>7$^6G0(x(Tw(R{vvvW?fmB9(PNEj9_Jr)DA z2Yrn>gvd+Ucw9dj+~X`?IqZqK%vCg0I``5KRkug;-`(B1nOiIQsx!X%uc(To>%MiL zIGcMsje@B&g8;DzmEcHj!&qTME$|*r=Q2qRdMfeE?Ug~k9 z_{E;p*w>y6(TYQ-&zK$5->G3A|l6DKrH*>FwJbbY~W%geyT8nAO%qtv{8E7RRJ>A9iD0MIx;? zVo0&+n8&$TVnnM99jKh<1T)M(IPFA{RIq&?^;PhvjjrXihn6uGm5vggAKmv~;K*+n z$%r>fQ&vl7wgGclNXCXUdQj9`hZ^-Qb$adKzkZ!nd0~_vhLPVlo!&_qq#IP}xA8&z z4g9i(o{Da;HM7F(!%sB$8mf1A-8c#rwOU4t%Qg6|wdc81-RM{tB&fit%Hd2ucTPjI zD9>sNOwwK@9(lD0=Y4txq^30U!t=bn68@|cr)tmFiXwB1Y?{9B01;EqpHTig!zU6D zkaA!sV*B36-1)Mse=`>nNKxTyT4@{9u=a%NVNqC)%uRx=>O=tezP}i2!CTwG@T^dLZt`l@wU04!9I2hc?$eobneF(2&^4QHCGpv5^ zzz8&B{}gfRqs$(Ofp|%GuQb=x#^2@;yA5WzIXxYk+bfn+ zG%c4DO~CMk_~0+MH;sMG(ojAjC)oM@dS;g0?V7RSOYnr0V0%|AqT%Y5;i7YQ`Nke) z!T6M&HLu@0M`(e2KD(Zn%|ZzWZ^X3Q$@uG^4WDtulak`)th+w3!*eH5srd`T z+Lj^IGM?a^qM0$g#)sPnzt0`u9+p?+J(oOZ{tk%z(U&ClGX9>X&Eb%$;+Wj_#znb(^FegkR2|Wl zKB+h95he87K<^1M(La2$H}$Ujgt|Zn>ltACF+nzbNzl1=tZMV&y`hWxRi7pNao?8hVUuJ7IcjX@j$PvFsOJE#`4j zOyzUedmY&Lr^vyd(*|9r?X=96z!doXI@WjVugd-MeIzXKdt;36 zPaCvU--jT$9=N}xihs*C>3WyS73aQdIZ=ae@YJb6 z+TY`qAlj4Z$sn|KU1|9(&2GaC^=q#&$6}tpnk@f`kevQNZr330{cDVEW92 z-a8DkG!|Hs?Z}L3p9dI)PtyjMxV`vhqfd7ReHPtklGrV>qD1{Fn@Z)&WWI)W<2P#! zDDY}&3tICx*~(wtUxzgNYbC_VM!n^02yGpy9|UWaUysf^MZ61jQU>iVu}!GZFoCS_+W~yW458h}qTCFYA-9Ww z!dssvH*&9Y_w4xQ1*?K3VP}=G%TCSm%9hG3pIK~hA-c4LssgTH5LxIbb=Ugc3SEVoeFD43Q296U(KbBMe^IAadM7sUB7J&H5_G#|=J8>k3v)4`+r_QWmbKr&2-ftY z{&rBC;O?G|>KzPf{i@l^b7a$%+fo_Eq;bOtP#2sd2#v9fF`Li{p}^)XM+AdLzhtLD z*!XH~9GFRlb~0(=!Jt(kP{#onTiaW9&HRf+=yweeiWrj#vMV}d4}he;e5uQ9O4}9U zsj3`M_(=a`AjEYMd6szPfc=W5o1#MOe*HM1B{W2;GT0^2=s)-DLoy@dL;{MR3@T_U z#mnaWW#MP!eNO9o`x^rH!6U+rF*_czQ1WjA=)dMX|Cc{V{qG4G7YGhr!7G3 zbWsr!NVm%vJ{FavI?W>Vrm|XoD5}hnUY~vx7p02csZV(i|A<~U^HtJK(>o8ct<_3@ zAZr}2%4Ep}`74$tAe|!PM~;v2)Dkn?2-f+&EYyQa)%v3RhD3&N8xmGsc_HT-eEHmX zU0_vve_cpHCfuA%PKWPxNCUlw>*`Xn1{6Ka3MG}L8O0(RB7|NMW1d`ti!?-|*B@Ji z%OZhfa$mB@ zNaaNBy(JV(@W8P>n!Nb@43JT~M!QE0VTuC)%m6g&%4V@CEQNWQ-ne%$!C%GZiXoi$ zpx`>OxoZE|KT-A(%+8eG$bZYI - - -- 2.11.4.GIT