TortoiseGitMerge: Use native ribbons
[TortoiseGit.git] / src / TortoiseMerge / NativeRibbonApp.cpp
blobf5e36710874726b03354b361fe6c5d4847455268
1 // Copyright (C) 2017 - TortoiseGit
2 // Copyright (C) 2017 - TortoiseSVN
4 // This program is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU General Public License
6 // as published by the Free Software Foundation; either version 2
7 // of the License, or (at your option) any later version.
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software Foundation,
16 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 #include "stdafx.h"
20 #include "NativeRibbonApp.h"
22 CNativeRibbonApp::CNativeRibbonApp(CFrameWnd* pFrame, IUIFramework* pFramework)
23 : m_pFrame(pFrame)
24 , m_pFramework(pFramework)
25 , m_cRefCount(0)
29 CNativeRibbonApp::~CNativeRibbonApp()
31 ASSERT(m_cRefCount == 0);
34 STDMETHODIMP CNativeRibbonApp::QueryInterface(REFIID riid, void **ppvObject)
36 if (riid == IID_IUnknown)
38 AddRef();
39 *ppvObject = (IUICommandHandler*)this;
40 return S_OK;
42 else if (riid == __uuidof(IUIApplication))
44 AddRef();
45 *ppvObject = (IUIApplication*)this;
46 return S_OK;
48 else if (riid == __uuidof(IUICommandHandler))
50 AddRef();
51 *ppvObject = (IUICommandHandler*)this;
52 return S_OK;
54 else
56 return E_NOINTERFACE;
60 STDMETHODIMP_(ULONG) CNativeRibbonApp::AddRef(void)
62 return InterlockedIncrement(&m_cRefCount);
65 STDMETHODIMP_(ULONG) CNativeRibbonApp::Release(void)
67 return InterlockedDecrement(&m_cRefCount);
70 STDMETHODIMP CNativeRibbonApp::OnViewChanged(UINT32 /*viewId*/, UI_VIEWTYPE typeID, IUnknown* view, UI_VIEWVERB verb, INT32 /*uReasonCode*/)
72 if (typeID == UI_VIEWTYPE_RIBBON)
74 if (verb == UI_VIEWVERB_CREATE)
76 if (!m_SettingsFileName.IsEmpty())
78 CComQIPtr<IUIRibbon> ribbonView(view);
79 if (ribbonView)
80 LoadRibbonViewSettings(ribbonView, m_SettingsFileName);
83 m_pFrame->RecalcLayout();
85 else if (verb == UI_VIEWVERB_DESTROY)
87 CComQIPtr<IUIRibbon> ribbonView(view);
88 if (ribbonView)
89 SaveRibbonViewSettings(ribbonView, m_SettingsFileName);
91 else if (verb == UI_VIEWVERB_SIZE)
92 m_pFrame->RecalcLayout();
95 return S_OK;
98 STDMETHODIMP CNativeRibbonApp::OnCreateUICommand(UINT32 commandId, UI_COMMANDTYPE typeID, IUICommandHandler** commandHandler)
100 m_commandIds.push_back(commandId);
101 if (typeID == UI_COMMANDTYPE_COLLECTION)
102 m_collectionCommandIds.push_back(commandId);
104 return QueryInterface(IID_PPV_ARGS(commandHandler));
107 STDMETHODIMP CNativeRibbonApp::OnDestroyUICommand(UINT32 commandId, UI_COMMANDTYPE /*typeID*/, IUICommandHandler* /*commandHandler*/)
109 m_commandIds.remove(commandId);
111 return S_OK;
114 STDMETHODIMP CNativeRibbonApp::Execute(UINT32 commandId, UI_EXECUTIONVERB verb, const PROPERTYKEY* key, const PROPVARIANT* currentValue, IUISimplePropertySet* /*commandExecutionProperties*/)
116 if (verb == UI_EXECUTIONVERB_EXECUTE)
118 if (key && IsEqualPropertyKey(*key, UI_PKEY_SelectedItem))
120 CComPtr<IUICollection> items = GetUICommandItemsSource(commandId);
121 UINT32 selectedItemIdx = 0;
122 UIPropertyToUInt32(UI_PKEY_SelectedItem, *currentValue, &selectedItemIdx);
123 UINT32 count = 0;
124 items->GetCount(&count);
126 if (selectedItemIdx < count)
128 CComPtr<IUnknown> selectedItemUnk;
129 items->GetItem(selectedItemIdx, &selectedItemUnk);
130 CComQIPtr<IUISimplePropertySet> selectedItemPropSet(selectedItemUnk);
131 if (selectedItemPropSet)
132 m_pFrame->PostMessage(WM_COMMAND, GetCommandIdProperty(selectedItemPropSet));
135 else
136 m_pFrame->PostMessage(WM_COMMAND, commandId);
138 return S_OK;
141 return S_FALSE;
144 class CRibbonCmdUI : public CCmdUI
146 public:
147 CString m_Text;
148 BOOL m_bOn;
149 int m_nCheck;
150 int m_bCheckChanged;
152 CRibbonCmdUI(int commandId)
153 : m_bOn(FALSE)
154 , m_nCheck(0)
155 , m_bCheckChanged(FALSE)
157 m_nID = commandId;
160 virtual void Enable(BOOL bOn)
162 m_bOn = bOn;
163 m_bEnableChanged = TRUE;
166 virtual void SetCheck(int nCheck)
168 m_nCheck = nCheck;
169 m_bCheckChanged = TRUE;
172 virtual void SetText(LPCTSTR lpszText)
174 m_Text = lpszText;
178 STDMETHODIMP CNativeRibbonApp::UpdateProperty(UINT32 commandId, REFPROPERTYKEY key, const PROPVARIANT* /*currentValue*/, PROPVARIANT* newValue)
180 if (key == UI_PKEY_TooltipTitle)
182 CString str;
183 if (!str.LoadString(commandId))
184 return S_FALSE;
186 int nIndex = str.Find(L'\n');
187 if (nIndex <= 0)
188 return S_FALSE;
190 str = str.Mid(nIndex + 1);
192 CString strLabel;
194 if (m_pFrame != NULL && (CKeyboardManager::FindDefaultAccelerator(commandId, strLabel, m_pFrame, TRUE) ||
195 CKeyboardManager::FindDefaultAccelerator(commandId, strLabel, m_pFrame->GetActiveFrame(), FALSE)))
197 str += _T(" (");
198 str += strLabel;
199 str += _T(')');
202 return UIInitPropertyFromString(UI_PKEY_TooltipTitle, str, newValue);
204 else if (key == UI_PKEY_TooltipDescription)
206 CString str;
207 if (!str.LoadString(commandId))
208 return S_FALSE;
210 int nIndex = str.Find(L'\n');
211 if (nIndex <= 0)
212 return S_FALSE;
214 str = str.Left(nIndex);
216 return UIInitPropertyFromString(UI_PKEY_TooltipDescription, str, newValue);
218 else if (key == UI_PKEY_Enabled)
220 CRibbonCmdUI ui(commandId);
221 ui.DoUpdate(m_pFrame, TRUE);
223 return UIInitPropertyFromBoolean(UI_PKEY_Enabled, ui.m_bOn, newValue);
225 else if (key == UI_PKEY_BooleanValue)
227 CRibbonCmdUI ui(commandId);
228 ui.DoUpdate(m_pFrame, TRUE);
230 return UIInitPropertyFromBoolean(UI_PKEY_BooleanValue, ui.m_nCheck, newValue);
232 else if (key == UI_PKEY_SelectedItem)
234 CComPtr<IUICollection> items = GetUICommandItemsSource(commandId);
235 if (!items)
236 return E_FAIL;
238 UINT32 count;
239 items->GetCount(&count);
240 for (UINT32 idx = 0; idx < count; idx++)
242 CComPtr<IUnknown> item;
243 items->GetItem(idx, &item);
245 CComQIPtr<IUISimplePropertySet> simplePropertySet(item);
246 if (simplePropertySet)
248 PROPVARIANT var = { 0 };
249 UINT32 uiVal;
250 simplePropertySet->GetValue(UI_PKEY_CommandId, &var);
251 UIPropertyToUInt32(UI_PKEY_CommandId, var, &uiVal);
253 CRibbonCmdUI ui(uiVal);
254 ui.DoUpdate(m_pFrame, TRUE);
256 if (ui.m_nCheck)
258 UIInitPropertyFromUInt32(UI_PKEY_SelectedItem, idx, newValue);
259 return S_OK;
264 // No selected item.
265 UIInitPropertyFromUInt32(UI_PKEY_SelectedItem, (UINT)-1, newValue);
267 return S_OK;
270 return E_NOTIMPL;
273 HRESULT CNativeRibbonApp::SaveRibbonViewSettings(IUIRibbon* pRibbonView, const CString& fileName)
275 CComPtr<IStream> stream;
276 HRESULT hr = SHCreateStreamOnFileEx(fileName, STGM_WRITE | STGM_CREATE, FILE_ATTRIBUTE_NORMAL, TRUE, nullptr, &stream);
277 if (FAILED(hr))
278 return hr;
280 hr = pRibbonView->SaveSettingsToStream(stream);
281 if (FAILED(hr))
283 stream->Revert();
284 return hr;
287 hr = stream->Commit(STGC_DEFAULT);
289 return hr;
292 HRESULT CNativeRibbonApp::LoadRibbonViewSettings(IUIRibbon* pRibbonView, const CString& fileName)
294 CComPtr<IStream> stream;
295 HRESULT hr = SHCreateStreamOnFileEx(fileName, STGM_READ, FILE_ATTRIBUTE_NORMAL, FALSE, nullptr, &stream);
296 if (FAILED(hr))
297 return hr;
299 hr = pRibbonView->LoadSettingsFromStream(stream);
301 return hr;
304 CComPtr<IUICollection> CNativeRibbonApp::GetUICommandItemsSource(UINT commandId)
306 PROPVARIANT prop = { 0 };
308 m_pFramework->GetUICommandProperty(commandId, UI_PKEY_ItemsSource, &prop);
310 CComPtr<IUICollection> uiCollection;
311 UIPropertyToInterface(UI_PKEY_ItemsSource, prop, &uiCollection);
312 PropVariantClear(&prop);
314 return uiCollection;
317 void CNativeRibbonApp::SetUICommandItemsSource(UINT commandId, IUICollection* pItems)
319 PROPVARIANT prop = { 0 };
321 UIInitPropertyFromInterface(UI_PKEY_ItemsSource, pItems, &prop);
323 m_pFramework->SetUICommandProperty(commandId, UI_PKEY_ItemsSource, prop);
325 PropVariantClear(&prop);
328 UINT CNativeRibbonApp::GetCommandIdProperty(IUISimplePropertySet* propertySet)
330 PROPVARIANT var = { 0 };
331 UINT32 commandId = 0;
333 if (propertySet->GetValue(UI_PKEY_CommandId, &var) == S_OK)
334 UIPropertyToUInt32(UI_PKEY_CommandId, var, &commandId);
336 return commandId;
339 void CNativeRibbonApp::UpdateCmdUI(BOOL bDisableIfNoHandler)
341 for (auto it = m_commandIds.begin(); it != m_commandIds.end(); ++it)
343 CRibbonCmdUI ui(*it);
344 ui.DoUpdate(m_pFrame, bDisableIfNoHandler);
345 if (ui.m_bEnableChanged)
347 PROPVARIANT val = { 0 };
348 UIInitPropertyFromBoolean(UI_PKEY_Enabled, ui.m_bOn, &val);
349 m_pFramework->SetUICommandProperty(*it, UI_PKEY_Enabled, val);
352 if (ui.m_bCheckChanged)
354 PROPVARIANT val = { 0 };
355 UIInitPropertyFromBoolean(UI_PKEY_BooleanValue, ui.m_nCheck, &val);
356 m_pFramework->SetUICommandProperty(*it, UI_PKEY_BooleanValue, val);
360 for (auto it = m_collectionCommandIds.begin(); it != m_collectionCommandIds.end(); ++it)
362 PROPVARIANT currentValue = { 0 };
363 PROPVARIANT newValue = { 0 };
365 UpdateProperty(*it, UI_PKEY_SelectedItem, &currentValue, &newValue);
366 m_pFramework->SetUICommandProperty(*it, UI_PKEY_SelectedItem, newValue);
370 int CNativeRibbonApp::GetRibbonHeight()
372 CComPtr<IUIRibbon> pRibbon;
373 if (SUCCEEDED(m_pFramework->GetView(0, IID_PPV_ARGS(&pRibbon))))
375 UINT32 cy = 0;
376 pRibbon->GetHeight(&cy);
377 return (int)cy;
380 return 0;
383 class UIDynamicCommandItem : public IUISimplePropertySet
385 private:
386 ULONG m_RefCount;
387 CString m_Label;
388 int m_CommandId;
389 CComPtr<IUIImage> m_Image;
391 public:
392 class UIDynamicCommandItem(int commandId, LPCWSTR label, IUIImage* image)
393 : m_RefCount(0)
394 , m_Label(label)
395 , m_CommandId(commandId)
396 , m_Image(image)
400 STDMETHOD(QueryInterface)(REFIID riid, void** ppvObject)
402 if (riid == __uuidof(IUnknown))
404 AddRef();
405 *ppvObject = (IUnknown*)this;
406 return S_OK;
408 else if (riid == __uuidof(IUISimplePropertySet))
410 AddRef();
411 *ppvObject = (IUISimplePropertySet*)this;
412 return S_OK;
414 return E_NOINTERFACE;
417 STDMETHOD_(ULONG, AddRef)(void)
419 return InterlockedIncrement(&m_RefCount);
422 STDMETHOD_(ULONG, Release)(void)
424 if (InterlockedDecrement(&m_RefCount) == 0)
426 delete this;
427 return 0;
429 return m_RefCount;
432 STDMETHOD(GetValue)(REFPROPERTYKEY key, PROPVARIANT* value)
434 if (key == UI_PKEY_Label)
435 return UIInitPropertyFromString(key, m_Label, value);
436 else if (key == UI_PKEY_CommandId)
437 return UIInitPropertyFromUInt32(UI_PKEY_CommandId, m_CommandId, value);
438 else if (key == UI_PKEY_ItemImage)
439 return UIInitPropertyFromInterface(UI_PKEY_ItemImage, m_Image, value);
440 return E_NOTIMPL;
444 void CNativeRibbonApp::SetItems(UINT cmdId, const std::list<CNativeRibbonDynamicItemInfo>& items)
446 CComPtr<IUICollection> uiCollection = GetUICommandItemsSource(cmdId);
447 if (!uiCollection)
448 return;
450 CComPtr<IUIImageFromBitmap> imageFactory;
451 imageFactory.CoCreateInstance(__uuidof(UIRibbonImageFromBitmapFactory));
453 UINT32 idx = 0;
454 UINT32 count = 0;
455 uiCollection->GetCount(&count);
456 for (const auto & item : items)
458 CComPtr<IUIImage> image;
459 if (imageFactory && item.GetImageId() > 0)
461 HBITMAP hbm = (HBITMAP)LoadImage( AfxGetResourceHandle(), MAKEINTRESOURCE(item.GetImageId()), IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION);
462 imageFactory->CreateImage(hbm, UI_OWNERSHIP_TRANSFER, &image);
465 if (idx < count)
466 uiCollection->Replace(idx, new UIDynamicCommandItem(item.GetCommandId(), item.GetLabel(), image));
467 else
468 uiCollection->Add(new UIDynamicCommandItem(item.GetCommandId(), item.GetLabel(), image));
469 idx++;
472 for (; idx < count; idx++)
474 uiCollection->RemoveAt(idx);
476 SetUICommandItemsSource(cmdId, uiCollection);