root/Mulberry/branches/users/kenneth_porter/FromTrunk/Win32/Sources/Support/Toolbars/CToolbar.cp @ 186

Revision 186, 34.6 kB (checked in by svnusers, 20 months ago)

Safe cross-thread CWnd usage.

Line 
1/*
2    Copyright (c) 2007 Cyrus Daboo. All rights reserved.
3   
4    Licensed under the Apache License, Version 2.0 (the "License");
5    you may not use this file except in compliance with the License.
6    You may obtain a copy of the License at
7   
8        http://www.apache.org/licenses/LICENSE-2.0
9   
10    Unless required by applicable law or agreed to in writing, software
11    distributed under the License is distributed on an "AS IS" BASIS,
12    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13    See the License for the specific language governing permissions and
14    limitations under the License.
15*/
16
17
18// Source for CToolbar class
19
20#include "CToolbar.h"
21
22#include "CBaseView.h"
23#include "CCommander.h"
24#include "CDrawUtils.h"
25#include "CFilterManager.h"
26#include "CIconMenu.h"
27#include "CIconWnd.h"
28#include "CMailboxToolbarPopup.h"
29#include "CMulberryApp.h"
30#include "CMulberryCommon.h"
31#include "CPopupButton.h"
32#include "CPreferences.h"
33#include "CSelectPopup.h"
34#include "CServerViewPopup.h"
35#include "CSMTPAccountPopup.h"
36#include "CTextButton.h"
37#include "CToolbarButton.h"
38#include "CToolbarPopupButton.h"
39#include "CToolbarView.h"
40#include "CXstringResources.h"
41
42#include "StValueChanger.h"
43
44#include <algorithm>
45
46// Static members
47
48CToolbar::CToolbarList CToolbar::sToolbars;
49
50BEGIN_MESSAGE_MAP(CToolbar, CContainerWnd)
51        ON_WM_CREATE()
52        ON_WM_SIZE()
53        ON_WM_KEYDOWN()
54        ON_WM_LBUTTONDOWN()
55        ON_WM_RBUTTONDOWN()
56        ON_WM_CONTEXTMENU()
57        ON_WM_MOUSEMOVE()
58        ON_WM_PAINT()
59END_MESSAGE_MAP()
60
61const int cToolbarWidth = 128;
62const int cToolbarHeight = 45;
63const int cBtnStart = 8;
64const int cLargeIconBtnSize = 32;
65const int cSmallIconBtnSize = 20;
66const int cCaptionXtraBtnSize = 14;
67const int cClickAndPopupXtraBtnSize = 12;
68const int cPopupXtraBtnSize = 8;
69const int cTextBtnHeight = 18;
70
71const int cDropCursorWidth = 4;
72
73const int cSpaceWidth = 16;
74const int cExpandSpaceMinWidth = 4;
75const int cSeparatorWidth = 8;
76const int cPopupWidth = 200;
77const int cPopupTitleWidth = 64;
78
79enum
80{
81        ePopup_RemoveButton = 0,
82        ePopup_MoveButton,
83        //ePopup_Separator1,
84        ePopup_AddButton,
85        //ePopup_Separator2,
86        ePopup_ResetButtons
87};
88
89// C O N S T R U C T I O N / D E S T R U C T I O N  M E T H O D S
90
91// Default constructor
92CToolbar::CToolbar()
93{
94        mIs3Pane = false;
95        mShowIt = true;
96        mSmallIcons = CPreferences::sPrefs->mToolbarSmallIcons.GetValue();
97        mShowIcons = CPreferences::sPrefs->mToolbarShowIcons.GetValue();
98        mShowCaptions = CPreferences::sPrefs->mToolbarShowCaptions.GetValue();
99        mLeftJustOffset = cBtnStart;
100        mLastCommander = NULL;
101        mDragIndex = 0xFFFFFFFF;
102       
103        sToolbars.push_back(this);
104}
105
106// Default destructor
107CToolbar::~CToolbar()
108{
109        sToolbars.erase(remove(sToolbars.begin(), sToolbars.end(), this), sToolbars.end());
110}
111
112// O T H E R  M E T H O D S ____________________________________________________________________________
113
114void CToolbar::PrefsChanged()
115{
116        // Rebuild each toolbar
117        for(CToolbarList::iterator iter = sToolbars.begin(); iter != sToolbars.end(); iter++)
118        {
119                (*iter)->ResetButtons(false);
120        }
121}
122
123bool CToolbar::RulesChanged()
124{
125        bool result = false;
126
127        // Rebuild each toolbar
128        for(CToolbarList::iterator iter = sToolbars.begin(); iter != sToolbars.end(); iter++)
129        {
130                result |= (*iter)->UpdateRulesButtons();
131        }
132       
133        return result;
134}
135
136void CToolbar::InitToolbar(bool is_3pane, CToolbarView* parent)
137{
138        mIs3Pane = is_3pane;
139       
140        DWORD dwStyle = AFX_WS_DEFAULT_VIEW;
141        dwStyle &= ~WS_BORDER;
142       
143        // Create this
144        CRect rect(0, 0, cToolbarWidth, GetBtnSize().cy + 1);
145        Create(NULL, NULL, dwStyle, rect, parent, IDC_STATIC);
146}
147
148int CToolbar::OnCreate(LPCREATESTRUCT lpCreateStruct)
149{
150        if (CContainerWnd::OnCreate(lpCreateStruct) == -1)
151                return -1;
152
153        return 0;
154}
155
156
157void CToolbar::OnPaint()
158{
159        // Fill the middle
160        CPaintDC dc(this);
161        CRect client;
162        GetClientRect(client);
163        dc.FillSolidRect(client, afxData.clrBtnFace);
164}
165
166void CToolbar::BuildToolbar()
167{
168        // Get the toolbar items
169        const CToolbarItem::CToolbarPtrItems& items = CToolbarManager::sToolbarManager.GetToolbarItems(GetType());
170       
171        // Add each one
172        UINT paneid = IDC_TOOLBAR_BTN1; // Double this for every button as click-and-popup buttons use up two message ids
173        for(CToolbarItem::CToolbarPtrItems::const_iterator iter = items.begin(); iter != items.end(); iter++, paneid += 2)
174        {
175                // Double-check we have something valid
176                if ((*iter).GetItem() == NULL)
177                {
178                        // Force entire reset
179                        ResetButtons(true);
180                        return;
181                };
182
183                // Separator is special cased as its not derived from toolbar button class
184                if ((*iter).GetItem()->GetType() == CToolbarItem::eSeparator)
185                {
186                        CRect rect1(0, 0, cSeparatorWidth, 44);
187                        CGrayBackground* sep_container = new CGrayBackground;
188                        sep_container->Create(_T(""), WS_CHILD | WS_VISIBLE, rect1, this, paneid);
189
190                        CRect rect2(cSeparatorWidth/2 - 2, 2, cSeparatorWidth/2, 40);
191                        CStatic* sep = new CStatic;
192                        sep->Create(_T(""), WS_CHILD | WS_VISIBLE | SS_ETCHEDVERT, rect2, sep_container, IDC_STATIC);
193                        sep_container->AddAlignment(new CWndAlignment(sep, CWndAlignment::eAlign_LeftHeight));
194
195                        AddItem(sep_container, *iter);
196                }
197                else if (((*iter).GetItem()->GetType() == CToolbarItem::eSpace) ||
198                                        ((*iter).GetItem()->GetType() == CToolbarItem::eExpandSpace))
199                {
200                        CRect rect(0, 0, cSpaceWidth, 44);
201                        CStatic* sep = new CStatic;
202                        sep->Create(_T(""), WS_CHILD | WS_VISIBLE, rect, this, paneid);
203                        AddItem(sep, *iter);
204                }
205                // Popup menu is special cased as its not derived from toolbar button class
206                else if ((*iter).GetItem()->GetType() == CToolbarItem::ePopupMenu)
207                {
208                        CRect rect1(0, 10, cPopupWidth, 20);
209                        CGrayBackground* popup_container = new CGrayBackground;
210                        popup_container->Create(_T(""), WS_CHILD | WS_VISIBLE, rect1, this, paneid);
211
212                        CRect rect2(0, 0, cPopupTitleWidth, 2);
213                        CStatic* title = new CStatic;
214                        CString s = cdustring(CToolbarManager::sToolbarManager.GetTitle((*iter).GetItem()->GetTitleID()));
215                        title->Create(s, WS_CHILD | WS_VISIBLE, rect2, popup_container, IDC_STATIC);
216                        title->SetFont(CMulberryApp::sAppFont);
217
218                        CRect rect3(cPopupTitleWidth, 0, cPopupWidth - cPopupTitleWidth, 20);
219                        CPopupButton* popup = new CPopupButton;
220                        popup->Create(_T(""), rect3, popup_container, paneid, IDC_STATIC, IDI_POPUPBTN);
221                        popup->SetMenu((*iter).GetItem()->GetPopupMenu());
222                        popup->SetFont(CMulberryApp::sAppFont);
223
224                        AddItem(popup, *iter);
225                }
226                // Popup menu is special cased as its not derived from toolbar button class
227                else if ((*iter).GetItem()->GetType() == CToolbarItem::eSMTPAccountPopup)
228                {
229                        const int small_offset = CMulberryApp::sLargeFont ? 4 : 0;
230                        CRect rect(0, 0, 212, 22 + small_offset);
231                        CString s = cdustring(CToolbarManager::sToolbarManager.GetTitle((*iter).GetItem()->GetTitleID()));
232                        CSMTPAccountPopup* popup = new CSMTPAccountPopup(paneid, s);
233                        popup->Create(_T(""), WS_CHILD | WS_VISIBLE, rect, this, paneid);
234                        AddItem(popup, *iter);
235                }
236
237                // Icon is special cased as its not derived from toolbar button class
238                else if ((*iter).GetItem()->GetType() == CToolbarItem::eStaticIcon)
239                {
240                        CRect rect(6, 0, 32, 32);
241                        CIconWnd* icon = new CIconWnd;
242                        icon->Create(NULL, WS_CHILD | WS_VISIBLE | SS_ICON, rect, this, paneid);
243                        icon->SetIconID((*iter).GetItem()->GetIconID());
244                        AddItem(icon, *iter);
245                }
246                else
247                {
248                        CRect rect(0, 0, 44, 44);
249
250                        CString title = cdustring(CToolbarManager::sToolbarManager.GetTitle((*iter).GetItem()->GetTitleID()));
251
252                        // Apply rules titles depend on the rule
253                        if ((*iter).GetItem()->GetTitleID() == CToolbarManager::eToolbar_ApplyRules)
254                        {
255                                // The name is actual the uid encoded as a string
256                                unsigned long uid = ::strtoul((*iter).GetExtraInfo(), NULL, 10);
257                               
258                                if (uid > 0)
259                                {
260                                        const CFilterItem* filter = CPreferences::sPrefs->GetFilterManager()->GetManualFilter(uid);
261                                        if (filter)
262                                                title = filter->GetName();
263                                }
264                                else
265                                        title = rsrc::GetString("CToolbarManager::AllRules");
266                        }
267
268                        CToolbarButton* tbtn = NULL;
269                        CToolbarPopupButton* ptbtn = NULL;
270                       
271                        switch((*iter).GetItem()->GetType())
272                        {
273                        case CToolbarItem::ePushButton:
274                        case CToolbarItem::eToggleButton:
275                                tbtn = new CToolbarButton;
276                                break;
277                        case CToolbarItem::ePopupButton:
278                        case CToolbarItem::ePopupPushButton:
279                        case CToolbarItem::ePopupToggleButton:
280                                tbtn = ptbtn = new CToolbarPopupButton;
281                                break;
282                        case CToolbarItem::eCabinetButton:
283                                tbtn = new CServerViewPopup;
284                                break;
285                        case CToolbarItem::eCopyToButton:
286                                tbtn = new CMailboxToolbarPopup;
287                                static_cast<CMailboxToolbarPopup*>(tbtn)->SetClickAndPopup(true);
288                                static_cast<CMailboxToolbarPopup*>(tbtn)->SetCopyTo(true);
289                                break;
290                        case CToolbarItem::eSelectButton:
291                                tbtn = ptbtn = new CSelectPopup;
292                                break;
293                        }
294
295                        tbtn->Create(title, rect, this, paneid, IDC_STATIC, (*iter).GetItem()->GetIconID(), 0, (*iter).GetItem()->IsToggleIcon() ? (*iter).GetItem()->GetIconID() + 1 : 0, 0);
296                        if ((ptbtn != 0) && ((*iter).GetItem()->GetPopupMenu() != 0))
297                                ptbtn->SetMenu((*iter).GetItem()->GetPopupMenu());
298
299                        // Now add to the display
300                        AddButton(tbtn, *iter);
301                }
302        }
303       
304        // We have to reset the toolbar to get the expand item width calculated properly
305        ResetLayout();
306}
307
308// Resize sub-views
309void CToolbar::OnSize(UINT nType, int cx, int cy)
310{
311        CContainerWnd::OnSize(nType, cx, cy);
312        ResetLayout();
313}
314
315unsigned long CToolbar::GetMinimumWidth() const
316{
317        // Size of left area + size of right area plus some space between them
318        return mLeftJustOffset;
319}
320
321unsigned long CToolbar::GetActualWidth() const
322{
323        // Take into account collapsed state
324        return GetMinimumWidth();
325}
326
327CSize CToolbar::GetBtnSize(const CToolbarButton* tb) const
328{
329        const int small_offset = CMulberryApp::sLargeFont ? 4 : 0;
330        const int large_offset = CMulberryApp::sLargeFont ? 12 : 0;
331
332        CSize result;
333
334        // If captions visible, horizontal size is always full size
335        if (mShowCaptions)
336                result.cx = cLargeIconBtnSize + cCaptionXtraBtnSize + large_offset;
337        else
338                result.cx = mSmallIcons ? cSmallIconBtnSize : cLargeIconBtnSize;
339       
340        // No icons => use text button height
341        if (!mShowIcons)
342                result.cy = cTextBtnHeight;
343        else
344        {
345                // Vertical size depends on icon
346                result.cy = mSmallIcons ? cSmallIconBtnSize : cLargeIconBtnSize;
347
348                // If caption visible, vertical size always contains caption extra space
349                if (mShowCaptions)
350                        result.cy += cCaptionXtraBtnSize + small_offset;
351
352        }
353
354        // Special toolbar button sizes
355        if (tb)
356        {
357                // Click-and-popup always has extra space
358                if (tb->GetClickAndPopup())
359                        result.cx += cClickAndPopupXtraBtnSize;
360                // If popup without captions or icons add extra width for popup glyph
361                else if ((!mShowCaptions || !mShowIcons) && tb->HasPopup())
362                        result.cx += cPopupXtraBtnSize;
363        }
364
365        return result;
366}
367
368// Resize after change to buttons size/captions
369void CToolbar::Reset()
370{
371        // First resize the toolbar itself
372        CRect rect;
373        GetWindowRect(rect);
374        GetParent()->ScreenToClient(rect);
375        rect.bottom = rect.top + GetBtnSize().cy + 1;
376        MoveWindow(rect);
377}
378
379// Resize after change to buttons size/captions
380void CToolbar::ResetLayout()
381{
382        CRect rect;
383        GetWindowRect(rect);
384
385        // Resize each button and accumulate total size (excluding additional expand item)
386        unsigned long total_width = 0;
387
388        // Loop over each item
389        for(CItemArray::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
390        {
391                // Check for space
392                if ((*iter).mWnd)
393                {
394                        CRect btnrect;
395                        (*iter).mWnd->GetWindowRect(btnrect);
396
397                        switch((*iter).mDetails.GetItem()->GetType())
398                        {
399                        case CToolbarItem::eSeparator:
400                        case CToolbarItem::eSpace:
401                                // Resize height - keep width the same
402                                btnrect.bottom = btnrect.top + GetBtnSize().cy;
403                                (*iter).mWnd->MoveWindow(btnrect);
404                                break;
405                        case CToolbarItem::eExpandSpace:
406                                // Resize height and width to standards - width will be adjusted to expanded size later
407                                btnrect.right = btnrect.left + cExpandSpaceMinWidth;
408                                btnrect.bottom = btnrect.top + GetBtnSize().cy;
409                                (*iter).mWnd->MoveWindow(btnrect);
410                                break;
411                        case CToolbarItem::ePushButton:
412                        case CToolbarItem::eToggleButton:
413                        case CToolbarItem::ePopupButton:
414                        case CToolbarItem::ePopupPushButton:
415                        case CToolbarItem::ePopupToggleButton:
416                        case CToolbarItem::eCabinetButton:
417                        case CToolbarItem::eCopyToButton:
418                        case CToolbarItem::eSelectButton:
419                        // These are all toolbar buttons
420                        {
421                                // See if it is a toolbar button
422                                CToolbarButton* btn = dynamic_cast<CToolbarButton*>((*iter).mWnd);
423                                if (btn)
424                                {
425                                        // Set its size/caption visibility
426                                        btn->SetSmallIcon(mSmallIcons);
427                                        btn->SetShowIcon(mShowIcons);
428                                        btn->SetShowCaption(mShowCaptions);
429                                       
430                                        btnrect.right = btnrect.left + GetBtnSize(btn).cx;
431                                        btnrect.bottom = btnrect.top + GetBtnSize().cy;
432                                        btn->MoveWindow(btnrect);
433                                }
434                                break;
435                        }
436                        default:;
437                        }
438
439                        // Accumalate size
440                        total_width += btnrect.Width();
441                }
442        }
443
444        // Now reposition each button from the start accounting for expansion
445        mLeftJustOffset = cBtnStart;
446        long expand_by = rect.Width() - 2 * cBtnStart - total_width;
447
448        // Loop over each item
449        for(CItemArray::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
450        {
451                // Check for space
452                if ((*iter).mWnd)
453                {
454                        // Get current item rect
455                        CRect btnrect;
456                        (*iter).mWnd->GetWindowRect(btnrect);
457
458                        switch((*iter).mDetails.GetItem()->GetType())
459                        {
460                        case CToolbarItem::eExpandSpace:
461                                // Resize width by expand amount if positive
462                                if (expand_by > 0)
463                                {
464                                        btnrect.right = btnrect.left + expand_by;
465                                        (*iter).mWnd->MoveWindow(btnrect);
466                                }
467                                break;
468                        default:;
469                        }
470
471                        // Now reposition it
472                        btnrect.OffsetRect(-btnrect.left, -btnrect.top);
473                        PositionButton(btnrect);
474                        (*iter).mWnd->MoveWindow(btnrect);
475                }
476        }
477       
478        // Tell parent to adjust itself if we are visible
479        if (IsVisible())
480                static_cast<CToolbarView*>(GetParent())->AdjustSize();
481}
482
483void CToolbar::PositionButton(CRect& btnrect)
484{
485        // Center vertically
486        CRect client;
487        GetClientRect(client);
488        int top_offset = (client.Height() - btnrect.Height()) / 2;
489
490        btnrect.OffsetRect(mLeftJustOffset, top_offset);
491        mLeftJustOffset += btnrect.Width();
492}
493
494void CToolbar::AddItem(CWnd* wnd, const CToolbarItem::CToolbarItemInfo& details)
495{
496        // Position appropriately
497        CRect btnrect;
498        wnd->GetWindowRect(btnrect);
499        btnrect.OffsetRect(-btnrect.left, -btnrect.top);
500        PositionButton(btnrect);
501        wnd->MoveWindow(btnrect);
502
503        // Add item to list
504        mItemList.push_back(SItemSpec(wnd, details));
505}
506
507void CToolbar::AddButton(CIconButton* btn, const CToolbarItem::CToolbarItemInfo& details)
508{
509        CRect btnrect;
510        btn->GetWindowRect(btnrect);
511
512        // Set up toolbar button styles
513        CToolbarButton* tbtn = dynamic_cast<CToolbarButton*>(btn);
514        if (tbtn)
515        {
516                tbtn->SetSmallIcon(mSmallIcons);
517                tbtn->SetShowIcon(mShowIcons);
518                tbtn->SetShowCaption(mShowCaptions);
519                               
520                btnrect.right = btnrect.left + GetBtnSize(tbtn).cx;
521                btnrect.bottom = btnrect.top + GetBtnSize().cy;
522        }
523
524        // Position appropriately
525        btnrect.OffsetRect(-btnrect.left, -btnrect.top);
526        PositionButton(btnrect);
527        btn->MoveWindow(btnrect);
528
529        // Add item to list
530        mItemList.push_back(SItemSpec(btn, details));
531}
532
533BOOL CToolbar::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
534{
535        // Handle button message ourselves - we map it to the button's command
536        if ((nCode == CN_COMMAND) && (nID >= IDC_TOOLBAR_BTN1) && (nID <= IDC_TOOLBAR_BTN1 + 100))
537        {
538                // Find the button for this message
539                for(CItemArray::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
540                {
541                        if ((*iter).mWnd)
542                        {
543                                if ((*iter).mWnd->GetDlgCtrlID() == nID)
544                                {
545                                        // Adjust pExtra to extra info if present
546                                        if (!(*iter).mDetails.GetExtraInfo().empty())
547                                                pExtra = const_cast<char*>((*iter).mDetails.GetExtraInfo().c_str());
548
549                                        // Determine nature of command (ignore Alt key command - the handler will look for the key down
550                                        // Let super commander handle the command if it wants to
551                                        if (GetCommander() && GetCommander()->OnCmdMsg((*iter).mDetails.GetItem()->GetCommand(), nCode, pExtra, pHandlerInfo))
552                                                return true;
553                                }
554                                else if ((*iter).mWnd->GetDlgCtrlID() + 1 == nID)
555                                {
556                                        // Adjust ioParam to extra info if present
557                                        if (!(*iter).mDetails.GetExtraInfo().empty())
558                                                pExtra = const_cast<char*>((*iter).mDetails.GetExtraInfo().c_str());
559
560                                        // Determine nature of command (ignore Alt key command - the handler will look for the key down
561                                        // Let super commander handle the command if it wants to
562                                        if (GetCommander() && GetCommander()->OnCmdMsg((*iter).mDetails.GetItem()->GetMenuCommand(), nCode, pExtra, pHandlerInfo))
563                                                return true;
564                                }
565                        }
566                }
567        }
568
569        // Let super commander handle the unmapped command if it wants to
570        if (GetCommander() && GetCommander()->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
571                return true;
572       
573        // If the object(s) in the extended command route don't handle
574        // the command, then let the base class OnCmdMsg handle it.
575        BOOL result = CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
576        return result;
577}
578
579void CToolbar::ListenTo_Message(long msg, void* param)
580{
581        switch(msg)
582        {
583        case CBaseView::eBroadcast_ViewActivate:
584                // Broadcast activation to parent
585                Broadcast_Message(eBroadcast_ToolbarActivate, this);
586
587                // Always update state after activation - selection change may not be triggered
588                UpdateToolbarState();
589                break;
590        case CBaseView::eBroadcast_ViewDeactivate:
591                // Broadcast activation to parent
592                Broadcast_Message(eBroadcast_ToolbarDeactivate, this);
593                break;
594        case CBaseView::eBroadcast_ViewSelectionChanged:
595                // Update state
596                UpdateToolbarState();
597                break;
598        }
599}               
600
601// Set the commander to send commands to
602void CToolbar::SetCommander(CCommander* cmdr)
603{
604        // Only if valid
605        if (!cmdr)
606                return;
607
608        // See if already in the list
609        CCommanderArray::const_iterator found = ::find(mCmdrs.begin(), mCmdrs.end(), cmdr);
610        if (found == mCmdrs.end())
611        {
612                // Erase all
613                mCmdrs.clear();
614
615                mCmdrs.push_back(cmdr);
616                // Update state if it changes
617                UpdateToolbarState();
618        }
619}
620
621// Add a commander to send commands to
622void CToolbar::AddCommander(CCommander* cmdr)
623{
624        // Only if valid
625        if (!cmdr)
626                return;
627
628        // See if already in the list
629        CCommanderArray::const_iterator found = ::find(mCmdrs.begin(), mCmdrs.end(), cmdr);
630        if (found == mCmdrs.end())
631        {
632                mCmdrs.push_back(cmdr);
633                // Update state if it changes
634                UpdateToolbarState();
635        }
636}
637
638// Remove a commander to send commands to
639void CToolbar::RemoveCommander(CCommander* cmdr)
640{
641        // Only if valid
642        if (!cmdr)
643                return;
644
645        // See if already in the list
646        mCmdrs.erase(std::remove(mCmdrs.begin(), mCmdrs.end(), cmdr), mCmdrs.end());
647
648        // Update state if it changes
649        UpdateToolbarState();
650}
651
652CWnd* CToolbar::GetCommander() const
653{
654        CCommander* found = NULL;
655        for(CCommanderArray::const_iterator iter = mCmdrs.begin(); iter != mCmdrs.end(); iter++)
656        {
657                // Always use the one which is the current target
658                if ((*iter)->IsTarget())
659                {
660                        found = *iter;
661                        break;
662                }
663
664                // Cache the one on duty and return that if none are targets
665                // Also use the last one if that is found in the array
666                if ((*iter)->IsOnDuty() || (*iter == mLastCommander))
667                        found = *iter;
668        }
669       
670        mLastCommander = found;
671        return dynamic_cast<CWnd*>(found);
672}
673
674void CToolbar::UpdateControl(CWnd* ctrl, UINT cmd, bool enable, bool show)
675{
676        CCmdUI cmdui;
677        cmdui.m_pOther = ctrl;
678        cmdui.m_nID = cmd;
679       
680        // Only if commander available
681        CWnd* commander = GetCommander();
682        if (commander)
683                // we recreate the CWnd as we got it from another thread
684                cmdui.DoUpdate(CWnd::FromHandle(commander->GetSafeHwnd()), true);
685       
686        // Check for visibility change tied to enable state
687        if (show)
688                ctrl->ShowWindow(ctrl->IsWindowEnabled() ? SW_SHOW : SW_HIDE);
689}
690
691void CToolbar::UpdatePopupControl(CWnd* ctrl)
692{
693        // Must be a toolbar popup button
694        CToolbarPopupButton* popup = dynamic_cast<CToolbarPopupButton*>(ctrl);
695        if (!popup)
696                return;
697       
698        // Do command UI processing for each message in the menu
699        for(UINT i = 0; i < popup->GetPopupMenu()->GetMenuItemCount(); i++)
700        {
701                UINT cmd = popup->GetPopupMenu()->GetMenuItemID(i);
702
703                if ((cmd != 0) && (cmd != 0xFFFFFFFF))
704                {
705                        CCmdUI cmdui;
706                        cmdui.m_pOther = ctrl;
707                        cmdui.m_nID = cmd;
708                        cmdui.m_nIndex = i;
709                        cmdui.m_pMenu = popup->GetPopupMenu();
710                       
711                        // Only if commander available
712                        if (GetCommander())
713                                cmdui.DoUpdate(GetCommander(), true);
714                }
715        }       
716       
717}
718
719bool CToolbar::UpdateRulesButtons()
720{
721        bool result = false;
722
723        // Update each button
724        for(CItemArray::const_iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
725        {
726                CToolbarButton* tbtn = dynamic_cast<CToolbarButton*>((*iter).mWnd);
727                if ((tbtn != NULL) && ((*iter).mDetails.GetItem()->GetTitleID() == CToolbarManager::eToolbar_ApplyRules))
728                {
729                        // Reset button title
730                        cdstring title;
731
732                        // The name is actual the uid encoded as a string
733                        unsigned long uid = ::strtoul((*iter).mDetails.GetExtraInfo(), NULL, 10);
734                       
735                        if (uid > 0)
736                        {
737                                const CFilterItem* filter = CPreferences::sPrefs->GetFilterManager()->GetManualFilter(uid);
738                                if (filter)
739                                        title = filter->GetName();
740                        }
741                        else
742                                title = rsrc::GetString("CToolbarManager::AllRules");
743                       
744                        cdstring newtitle(title);
745                        cdstring oldtitle;
746                        tbtn->GetTitle(oldtitle);
747                        if (newtitle != oldtitle)
748                        {
749                                tbtn->SetTitle(newtitle);
750                                result = true;
751                        }
752                       
753                }
754        }
755       
756        return result;
757}
758
759// Display and track context menu
760void CToolbar::OnContextMenu(CWnd* wnd, CPoint point)
761{
762        // Convert event point to a local point
763        CPoint client_point(point);
764        ScreenToClient(&client_point);
765
766        // Find the item under the mouse
767        CWnd* pane = ChildWindowFromPoint(client_point, CWP_ALL);
768        if (pane == this)
769                pane = NULL;
770       
771        // Always make sure fly-over acti vation is disabled when doing context
772        if (pane != NULL)
773                pane->SendMessage(WM_MOUSELEAVE);
774
775        // Create popup menu
776        CMenu popup;
777        popup.LoadMenu(IDR_POPUP_CONTEXT_TOOLBAR);
778        CMenu* pPopup = popup.GetSubMenu(0);
779       
780        // Fill add menu with items
781        CIconMenu* submenu = new CIconMenu;
782        submenu->CreatePopupMenu();
783        CString old_str;
784        pPopup->GetMenuString(3, old_str, MF_BYPOSITION);
785        pPopup->ModifyMenu(3, MF_STRING | MF_BYPOSITION | MF_POPUP, (UINT) submenu->m_hMenu, old_str);
786       
787        // Place holder for apply rules popup if present
788        CIconMenu* apply_rules = NULL;
789       
790        // Get the allowed toolbar items
791        const CToolbarItem::CToolbarPtrItems& items = CToolbarManager::sToolbarManager.GetAllowedToolbarItems(GetType());
792       
793        // Add each one
794        UINT pos = IDM_TOOLBAR_ADD_Start;
795        unsigned long apply_rules_index = 0;
796        for(CToolbarItem::CToolbarPtrItems::const_iterator iter = items.begin(); iter != items.end(); iter++, pos++)
797        {
798                // Insert menu item
799                cdstring temp = CToolbarManager::sToolbarManager.GetDescriptor((*iter).GetItem()->GetTitleID());
800
801                // Look for apply rules sub menu and treat differently
802                if ((*iter).GetItem()->GetTitleID() == CToolbarManager::eToolbar_ApplyRules)
803                {
804                        apply_rules_index = pos;
805
806                        if (apply_rules == NULL)
807                        {
808                                apply_rules = new CIconMenu;
809                                apply_rules->CreatePopupMenu();
810                                submenu->AppendMenu(MF_BYPOSITION | MF_POPUP | MF_OWNERDRAW, (UINT) apply_rules->m_hMenu,
811                                                                                (const TCHAR*)submenu->AddData(new CIconMenu::SIconMenuData(temp, IDI_RULESICON)));
812                        }
813                       
814                        // Add All item
815                        apply_rules->AppendMenu(MF_BYPOSITION | MF_OWNERDRAW, IDM_TOOLBAR_RULES_All, (const TCHAR*)apply_rules->AddData(new CIconMenu::SIconMenuData(rsrc::GetString("CToolbarManager::AllRules"), IDI_RULESICON)));
816                        apply_rules->AppendMenu(MF_SEPARATOR, 0, (TCHAR*) NULL);
817
818                        // Disable All item if already in the toolbar
819                        cdstring temp(0UL);
820                        if (GetButton(CToolbarManager::eToolbar_ApplyRules, temp) != NULL)
821                                apply_rules->EnableMenuItem(IDM_TOOLBAR_RULES_All, MF_BYCOMMAND | MF_GRAYED);
822                       
823                        // Now fill the menu with apply rules items
824
825                        // Get all manual filters
826                        cdstrvect items;
827                        CPreferences::sPrefs->GetFilterManager()->GetManualFilters(items);
828                       
829                        // Add each one to menu
830                        UINT rules_pos = IDM_TOOLBAR_RULES_Start;
831                        for(cdstrvect::const_iterator iter2 = items.begin(); iter2 != items.end(); iter2++, rules_pos++)
832                        {
833                               
834                                apply_rules->AppendMenu(MF_BYPOSITION | MF_OWNERDRAW, rules_pos, (const TCHAR*)apply_rules->AddData(new CIconMenu::SIconMenuData(*iter2, IDI_RULESICON)));
835                               
836                                // Get filter UID for rule
837                                const CFilterItem* filter = CPreferences::sPrefs->GetFilterManager()->GetFilter(CFilterItem::eLocal, *iter2);
838                                if (filter != NULL)
839                                {
840                                        cdstring temp(filter->GetUID());
841
842                                        // Disable ones already in the toolbar
843                                        if (GetButton(CToolbarManager::eToolbar_ApplyRules, temp) != NULL)
844                                                apply_rules->EnableMenuItem(rules_pos, MF_BYCOMMAND | MF_GRAYED);
845                                }
846                        }
847                }
848                else
849                {
850                        submenu->AppendMenu(MF_STRING | MF_BYPOSITION | MF_OWNERDRAW, pos,
851                                                                        (const TCHAR*)submenu->AddData(new CIconMenu::SIconMenuData(temp, (*iter).GetItem()->GetIconID())));
852                       
853                        // See whether item is already present (allow multiple separators and ordinary space)
854                        if (((*iter).GetItem()->GetType() != CToolbarItem::eSeparator) &&
855                                ((*iter).GetItem()->GetType() != CToolbarItem::eSpace) &&
856                                (GetItem(static_cast<CToolbarManager::EToolbarItem>((*iter).GetItem()->GetTitleID())) != NULL))
857                                submenu->EnableMenuItem(pos, MF_BYCOMMAND | MF_GRAYED);
858                }
859        }
860
861        // Remove button specific items if not over a button
862        CRect portRect;
863        bool drawing = false;
864        if (pane == NULL)
865        {
866                pPopup->DeleteMenu(0, MF_BYPOSITION);
867                pPopup->DeleteMenu(0, MF_BYPOSITION);
868                pPopup->DeleteMenu(0, MF_BYPOSITION);
869        }
870        else
871        {
872                // Adjust titles of menu items
873                const CToolbarItem* details = mItemList.at(GetItemIndex(pane)).mDetails.GetItem();
874                cdstring btnname = CToolbarManager::sToolbarManager.GetTitle(details->GetTitleID());
875                {
876                        CString old_str;
877                        pPopup->GetMenuString(IDM_TOOLBAR_REMOVE, old_str, MF_BYCOMMAND);
878                        cdstring temp(old_str);
879                        temp.Substitute(btnname);
880                        pPopup->ModifyMenu(IDM_TOOLBAR_REMOVE, MF_BYCOMMAND | MF_STRING, IDM_TOOLBAR_REMOVE, temp.win_str());
881                }
882                {
883                        CString old_str;
884                        pPopup->GetMenuString(IDM_TOOLBAR_MOVE, old_str, MF_BYCOMMAND);
885                        cdstring temp(old_str);
886                        temp.Substitute(btnname);
887                        pPopup->ModifyMenu(IDM_TOOLBAR_MOVE, MF_BYCOMMAND | MF_STRING, IDM_TOOLBAR_MOVE, temp.win_str());
888                }
889
890                // Draw insert marker
891               
892                // Is click to left or right side of current item
893                bool left = true;
894                pane->GetWindowRect(portRect);
895                left = (point.x < (portRect.left + portRect.right) / 2);
896               
897                if (left)
898                {
899                        portRect.left -= cDropCursorWidth/2;
900                        portRect.right = portRect.left + cDropCursorWidth;
901                }
902                else
903                {
904                        portRect.right += cDropCursorWidth/2;
905                        portRect.left = portRect.right - cDropCursorWidth;
906                }
907               
908                ScreenToClient(portRect);
909                CRect myPort;
910                GetClientRect(myPort);
911                portRect.top = myPort.top;
912                portRect.bottom = myPort.bottom;
913               
914                // Now draw the rect
915                CDC* pDC = GetDC();
916                StDCState save(pDC);
917
918                CPen pen;
919                pen.CreatePen(PS_SOLID | PS_INSIDEFRAME, 3, CDrawUtils::sDkGrayColor);
920
921                // Hilite outline
922                pDC->SelectObject(&pen);
923                pDC->Rectangle(portRect);
924
925                drawing = true;
926        }
927
928        // Do popup menu of suggestions
929        UINT popup_result = pPopup->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, point.x, point.y, this);
930
931        // Remove any drawn cursor
932        if (drawing)
933        {
934                RedrawWindow(portRect);
935        }
936
937        if (popup_result != 0)
938        {
939                bool handled = false;
940                switch(popup_result)
941                {
942                case IDM_TOOLBAR_REMOVE:
943                        RemoveButton(pane);
944                        break;
945                case IDM_TOOLBAR_MOVE:
946                        MoveButton(pane);
947                        break;
948                case IDM_TOOLBAR_RESET:
949                        ResetButtons(true);
950                        break;
951                default:
952                        if ((popup_result >= IDM_TOOLBAR_ADD_Start) && (popup_result <= IDM_TOOLBAR_ADD_End))
953                        {
954                                // Get the item
955                                CToolbarManager::sToolbarManager.AddItemAt(GetType(), popup_result - IDM_TOOLBAR_ADD_Start, GetItemIndex(pane));
956
957                                // Rebuild the toolbar
958                                ResetButtons(false);
959                        }
960                        else if ((popup_result >= IDM_TOOLBAR_RULES_All) && (popup_result <= IDM_TOOLBAR_RULES_End))
961                        {
962                                // Get uid for item
963                                unsigned long uid = (popup_result == IDM_TOOLBAR_RULES_All) ? 0 : CPreferences::sPrefs->GetFilterManager()->GetManualUID(popup_result - IDM_TOOLBAR_RULES_Start);
964                                cdstring uid_txt(uid);
965
966                                // Get the item
967                                CToolbarManager::sToolbarManager.AddItemAt(GetType(), apply_rules_index - IDM_TOOLBAR_ADD_Start, GetItemIndex(pane), uid_txt);
968
969                                // Rebuild the toolbar
970                                ResetButtons(false);
971                        }
972                        break;
973                }
974
975        }
976}
977
978CToolbarButton* CToolbar::GetButton(CToolbarManager::EToolbarItem item) const
979{
980        // Find the button of this type
981        for(CItemArray::const_iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
982        {
983                CToolbarButton* tbtn = dynamic_cast<CToolbarButton*>((*iter).mWnd);
984                if ((tbtn != NULL) && static_cast<CToolbarManager::EToolbarItem>((*iter).mDetails.GetItem()->GetTitleID()) == item)
985                        return tbtn;
986        }
987       
988        return NULL;
989}
990
991CToolbarButton* CToolbar::GetButton(CToolbarManager::EToolbarItem item, const cdstring& extra) const
992{
993        // Find the button of this type with the same extra data
994        for(CItemArray::const_iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
995        {
996                CToolbarButton* tbtn = dynamic_cast<CToolbarButton*>((*iter).mWnd);
997                if ((tbtn != NULL) && (static_cast<CToolbarManager::EToolbarItem>((*iter).mDetails.GetItem()->GetTitleID()) == item) &&
998                        ((*iter).mDetails.GetExtraInfo() == extra))
999                        return tbtn;
1000        }
1001       
1002        return NULL;
1003}
1004
1005CWnd* CToolbar::GetItem(CToolbarManager::EToolbarItem item) const
1006{
1007        // Find the button for this message
1008        for(CItemArray::const_iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
1009        {
1010                if (((*iter).mDetails.GetItem() != NULL) && (static_cast<CToolbarManager::EToolbarItem>((*iter).mDetails.GetItem()->GetTitleID()) == item))
1011                        return (*iter).mWnd;
1012        }
1013       
1014        return NULL;
1015}
1016
1017unsigned long CToolbar::GetItemIndex(CWnd* item) const
1018{
1019        // Find index of pane in item list
1020        unsigned long index = 0;
1021        for(CItemArray::const_iterator iter = mItemList.begin(); iter != mItemList.end(); iter++, index++)
1022        {
1023                if ((*iter).mWnd == item)
1024                {
1025                        break;
1026                }
1027        }
1028       
1029        return index;
1030}
1031
1032void CToolbar::RemoveButton(CWnd* pane)
1033{
1034        // Find index of pane in item list
1035        unsigned long index = 0;
1036        for(CItemArray::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++, index++)
1037        {
1038                if ((*iter).mWnd == pane)
1039                {
1040                        // Remove from toolbar manager prefs
1041                        CToolbarManager::sToolbarManager.RemoveItemAt(GetType(), index);
1042
1043                        // Delete the button object
1044                        delete (*iter).mWnd;
1045                        mItemList.erase(iter);
1046                       
1047                        // Reset the toolbar
1048                        ResetLayout();
1049                        return;
1050                }
1051        }
1052}
1053
1054void CToolbar::MoveButton(CWnd* pane)
1055{
1056        TrackStart(pane);
1057}
1058
1059void CToolbar::TrackStart(CWnd* pane)
1060{
1061        mDragIndex = GetItemIndex(pane);
1062
1063        SetCapture();
1064
1065        // Tell each control to be active
1066        for(CItemArray::const_iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
1067        {
1068                if ((*iter).mWnd != NULL)
1069                {
1070                        (*iter).mWnd->EnableWindow(true);
1071                        CToolbarButton* tbtn = dynamic_cast<CToolbarButton*>((*iter).mWnd);
1072                        if (tbtn != NULL)
1073                                tbtn->SetDragMode(true);
1074                }
1075        }
1076       
1077        TrackHighlight();
1078}
1079
1080void CToolbar::TrackStop()
1081{
1082        // Tell each control no longer dragging
1083        for(CItemArray::const_iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
1084        {
1085                if ((*iter).mWnd != NULL)
1086                {
1087                        CToolbarButton* tbtn = dynamic_cast<CToolbarButton*>((*iter).mWnd);
1088                        if (tbtn != NULL)
1089                                tbtn->SetDragMode(false);
1090                }
1091        }
1092
1093        mDragIndex = 0xFFFFFFFF;
1094
1095        // Always reset state
1096        UpdateToolbarState();
1097        RedrawWindow();
1098
1099        ReleaseCapture();
1100}
1101
1102void CToolbar::OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags)
1103{
1104        if (mDragIndex != 0xFFFFFFFF)
1105        {
1106                TrackStop();
1107        }
1108        else
1109                CContainerWnd::OnKeyDown(nChar, nRepCnt, nFlags);
1110}
1111
1112void CToolbar::OnLButtonDown(UINT nFlags, CPoint point)
1113{
1114        if (mDragIndex != 0xFFFFFFFF)
1115        {
1116                TrackStop();
1117        }
1118        else
1119                CContainerWnd::OnLButtonDown(nFlags, point);
1120}
1121
1122// Clicked somewhere
1123void CToolbar::OnRButtonDown(UINT nFlags, CPoint point)
1124{
1125        if (mDragIndex != 0xFFFFFFFF)
1126        {
1127                TrackStop();
1128        }
1129        else
1130        {
1131                // Do context menu
1132                ClientToScreen(&point);
1133                OnContextMenu(NULL, point);     
1134        }
1135}
1136
1137void CToolbar::OnMouseMove(UINT nFlags, CPoint point)
1138{
1139        if (mDragIndex != 0xFFFFFFFF)
1140        {
1141                TrackMouseMove(point);
1142        }
1143        else
1144                CContainerWnd::OnMouseMove(nFlags, point);
1145}
1146
1147void CToolbar::TrackMouseMove(CPoint point)
1148{
1149        // Find the item under the mouse
1150        unsigned long new_index = mDragIndex;
1151        CWnd* new_pane = ChildWindowFromPoint(point, CWP_ALL);
1152        if (new_pane != NULL)
1153        {
1154                new_index = GetItemIndex(new_pane);
1155
1156                // Apply hysteresis to movement to ensure panes with different sizes do not cause
1157                // rapid switches
1158                if (new_index != mDragIndex)
1159                {
1160                        CRect old_portRect;
1161                        mItemList.at(mDragIndex).mWnd->GetWindowRect(old_portRect);
1162                        ScreenToClient(old_portRect);
1163                               
1164                        CRect new_portRect;
1165                        mItemList.at(new_index).mWnd->GetWindowRect(new_portRect);
1166                        ScreenToClient(new_portRect);
1167                       
1168                        if (new_index < mDragIndex)
1169                        {
1170                                // Only use new index if the mouse is within the old button size of the left edge of the new button
1171                                if (point.x >= new_portRect.left + (old_portRect.right - old_portRect.left))
1172                                        new_index = mDragIndex;
1173                        }
1174                        else
1175                        {
1176                                // Only use new index if the mouse is within the old button size of the right edge of the new button
1177                                if (point.x < new_portRect.right - (old_portRect.right - old_portRect.left))
1178                                        new_index = mDragIndex;
1179                        }
1180                }
1181        }
1182
1183        if (new_index != mDragIndex)
1184        {
1185                // Determine index of start/end panes
1186                CToolbarManager::sToolbarManager.MoveItemAt(GetType(), mDragIndex, new_index);
1187               
1188                SItemSpec temp = mItemList.at(mDragIndex);
1189                mItemList.erase(mItemList.begin() + mDragIndex);
1190                mItemList.insert(mItemList.begin() + new_index, temp);
1191                ResetLayout();
1192                RedrawWindow();
1193               
1194                mDragIndex = new_index;
1195        }
1196               
1197        // Draw highlight around moved button
1198        TrackHighlight();
1199}
1200
1201void CToolbar::TrackHighlight()
1202{
1203        CRect portRect;
1204        mItemList.at(mDragIndex).mWnd->GetWindowRect(portRect);
1205        ScreenToClient(portRect);
1206
1207        // Now draw the rect
1208        CDC* pDC = GetDC();
1209        StDCState save(pDC);
1210
1211        // Hilite outline
1212        for(int i = 0; i < 4; i++)
1213        {
1214                pDC->FrameRect(portRect, &CDrawUtils::sDkGrayBrush);
1215                portRect.DeflateRect(1, 1);
1216        }
1217}
1218
1219void CToolbar::ResetButtons(bool use_default)
1220{
1221        // Remove all buttons
1222        for(CItemArray::iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
1223        {
1224                // Delete the button object
1225                delete (*iter).mWnd;
1226        }
1227        mItemList.clear();
1228       
1229        // Reset to default state
1230        if (use_default)
1231                CToolbarManager::sToolbarManager.ResetToolbar(GetType());
1232       
1233        // Clear positions
1234        mLeftJustOffset = cBtnStart;
1235       
1236        // Rebuild the toolbar
1237        BuildToolbar();
1238        UpdateToolbarState();
1239        RedrawWindow();
1240}
1241
1242// Force update of state if items visible
1243void CToolbar::UpdateToolbarState()
1244{
1245        // Do actual update only if items are visible
1246        DoUpdateToolbarState();
1247}
1248
1249// Force update of state
1250void CToolbar::DoUpdateToolbarState()
1251{
1252        // Update each button
1253        for(CItemArray::const_iterator iter = mItemList.begin(); iter != mItemList.end(); iter++)
1254        {
1255                CToolbarButton* tbtn = dynamic_cast<CToolbarButton*>((*iter).mWnd);
1256                if (tbtn)
1257                        UpdateControlState(tbtn, (*iter).mDetails.GetItem()->GetCommand());
1258        }
1259}
1260
1261void CToolbar::ShowToolbar(bool show)
1262{
1263        // Only do if different
1264        if (show ^ mShowIt)
1265        {
1266                mShowIt = show;
1267                ShowWindow(show ? SW_SHOW : SW_HIDE);
1268        }
1269}
1270
1271void CToolbar::SmallIcons(bool small_icon)
1272{
1273        if (mSmallIcons ^ small_icon)
1274        {
1275                mSmallIcons = small_icon;
1276                Reset();
1277        }
1278}
1279
1280void CToolbar::ShowIcons(bool show)
1281{
1282        if (mShowIcons ^ show)
1283        {
1284                mShowIcons = show;
1285                Reset();
1286        }
1287}
1288
1289void CToolbar::ShowCaptions(bool show)
1290{
1291        if (mShowCaptions ^ show)
1292        {
1293                mShowCaptions = show;
1294                Reset();
1295        }
1296}
Note: See TracBrowser for help on using the browser.