xref: /freebsd/crypto/krb5/src/windows/leash/LeashUIApplication.cpp (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 // -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*-
2 // leash/LeashUIApplication.cpp - Implement IUIApplication for leash
3 //
4 // Copyright (C) 2014 by the Massachusetts Institute of Technology.
5 // All rights reserved.
6 //
7 // Redistribution and use in source and binary forms, with or without
8 // modification, are permitted provided that the following conditions
9 // are met:
10 //
11 // * Redistributions of source code must retain the above copyright
12 //   notice, this list of conditions and the following disclaimer.
13 //
14 // * Redistributions in binary form must reproduce the above copyright
15 //   notice, this list of conditions and the following disclaimer in
16 //   the documentation and/or other materials provided with the
17 //   distribution.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
24 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
30 // OF THE POSSIBILITY OF SUCH DAMAGE.
31 
32 // Implementation of the LeashUIApplication class.  In addition
33 // to the minimum requirements for the IUIApplication interface,
34 // it also saves and loads the ribbon state across application
35 // sessions, and initiates a redraw of the parent window when
36 // the ribbon size changes.
37 
38 #include <UIRibbon.h>
39 #include <UIRibbonPropertyHelpers.h>
40 #include "kfwribbon.h"
41 #include "LeashUIApplication.h"
42 #include "LeashUICommandHandler.h"
43 
44 HWND LeashUIApplication::mainwin;
45 
46 // The input hwnd is the window to which to bind the ribbon, i.e.,
47 // the Leash CMainFrame.
48 HRESULT
CreateInstance(IUIApplication ** out,HWND hwnd)49 LeashUIApplication::CreateInstance(IUIApplication **out, HWND hwnd)
50 {
51     LeashUIApplication *app = NULL;
52     LeashUICommandHandler *handler;
53     HRESULT ret;
54 
55     if (out == NULL)
56         return E_POINTER;
57     *out = NULL;
58 
59     app = new LeashUIApplication();
60     ret = LeashUICommandHandler::CreateInstance(&app->commandHandler, hwnd);
61     if (FAILED(ret))
62         goto out;
63     ret = app->InitializeRibbon(hwnd);
64     if (FAILED(ret))
65         goto out;
66     mainwin = hwnd;
67     // Only the Leash-specific handler type has the back-pointer.
68     handler = static_cast<LeashUICommandHandler *>(app->commandHandler);
69     handler->app = app;
70     *out = static_cast<IUIApplication *>(app);
71     app = NULL;
72     ret = S_OK;
73 
74 out:
75     if (app != NULL)
76         app->Release();
77     return ret;
78 }
79 
80 // Create a ribbon framework and ribbon for the LeashUIApplication.
81 // CoInitializeEx() is required to be called before calling any COM
82 // functions.  AfxOleInit(), called from CLeashApp::InitInstance(),
83 // makes that call, but it is only scoped to the calling thread,
84 // and the LeashUIApplication is created from CMainFrame, which is
85 // the frame for the MFC document template.  It is unclear if the
86 // Leash main thread will be the same thread which runs the frame
87 // from the document template, so call CoInitializeEx() ourselves
88 // just in case.  It is safe to call multiple times (it will return
89 // S_FALSE on subsequent calls).
90 HRESULT
InitializeRibbon(HWND hwnd)91 LeashUIApplication::InitializeRibbon(HWND hwnd)
92 {
93     HRESULT ret;
94 
95     if (hwnd == NULL)
96         return -1;
97     ret = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
98     if (FAILED(ret))
99         return ret;
100     ret = CoCreateInstance(CLSID_UIRibbonFramework, NULL,
101                            CLSCTX_INPROC_SERVER,
102                            IID_PPV_ARGS(&ribbonFramework));
103     if (FAILED(ret))
104         return ret;
105     ret = ribbonFramework->Initialize(hwnd, this);
106     if (FAILED(ret))
107         return ret;
108     ret = ribbonFramework->LoadUI(GetModuleHandle(NULL),
109                                   L"KFW_RIBBON_RIBBON");
110     if (FAILED(ret))
111         return ret;
112     return S_OK;
113 }
114 
115 // Import ribbon state (minimization state and Quick Access Toolbar
116 // customizations) from a serialized stream stored in the registry.
117 // In particular, the serialized state does not include the state
118 // of checkboxes and other ribbon controls.
119 //
120 // This functionality is not very important, since we do not offer
121 // much in the way of QAT customization.  Paired with SaveRibbonState().
122 HRESULT
LoadRibbonState(IUIRibbon * ribbon)123 LeashUIApplication::LoadRibbonState(IUIRibbon *ribbon)
124 {
125     HRESULT ret;
126     IStream *s;
127 
128     s = SHOpenRegStream2(HKEY_CURRENT_USER, "Software\\MIT\\Kerberos5",
129                          "RibbonState", STGM_READ);
130     if (s == NULL)
131         return E_FAIL;
132     ret = ribbon->LoadSettingsFromStream(s);
133     s->Release();
134     return ret;
135 }
136 
137 // Serialize the ribbon state (minimization state and Quick Access Toolbar
138 // customizations) to the registry.  Paired with LoadRibbonState().
139 HRESULT
SaveRibbonState()140 LeashUIApplication::SaveRibbonState()
141 {
142     HRESULT ret;
143     IStream *s = NULL;
144     IUIRibbon *ribbon = NULL;
145 
146     // No ribbon means no state to save.
147     if (ribbonFramework == NULL)
148         return S_OK;
149     // ViewID of 0 is the ribbon itself.
150     ret = ribbonFramework->GetView(0, IID_PPV_ARGS(&ribbon));
151     if (FAILED(ret))
152         return ret;
153 
154     s = SHOpenRegStream2(HKEY_CURRENT_USER, "Software\\MIT\\Kerberos5",
155                          "RibbonState", STGM_WRITE);
156     if (s == NULL) {
157         ret = E_FAIL;
158         goto out;
159     }
160     ret = ribbon->SaveSettingsToStream(s);
161 
162 out:
163     if (s != NULL)
164         s->Release();
165     if (ribbon != NULL)
166         ribbon->Release();
167     return ret;
168 }
169 
170 UINT
GetRibbonHeight()171 LeashUIApplication::GetRibbonHeight()
172 {
173     return ribbonHeight;
174 }
175 
176 ULONG
AddRef()177 LeashUIApplication::AddRef()
178 {
179     return InterlockedIncrement(&refcnt);
180 }
181 
182 ULONG
Release()183 LeashUIApplication::Release()
184 {
185     LONG tmp;
186 
187     tmp = InterlockedDecrement(&refcnt);
188     if (tmp == 0) {
189         if (commandHandler != NULL)
190             commandHandler->Release();
191         if (ribbonFramework != NULL)
192             ribbonFramework->Release();
193         delete this;
194     }
195     return tmp;
196 }
197 
198 HRESULT
QueryInterface(REFIID iid,void ** ppv)199 LeashUIApplication::QueryInterface(REFIID iid, void **ppv)
200 {
201     if (ppv == NULL)
202         return E_POINTER;
203 
204     if (iid == __uuidof(IUnknown)) {
205         *ppv = static_cast<IUnknown*>(this);
206     } else if (iid == __uuidof(IUIApplication)) {
207         *ppv = static_cast<IUIApplication*>(this);
208     } else {
209         *ppv = NULL;
210         return E_NOINTERFACE;
211     }
212 
213     AddRef();
214     return S_OK;
215 }
216 
217 // This is called by the ribbon framework on events which change the (ribbon)
218 // view, such as creation and resizing.  (There may be other non-ribbon views
219 // in the future, but for now, the ribbon is the only one.)  With the hybrid
220 // COM/MFC setup used by Leash, the destroy event is not always received,
221 // since the main thread is in the MFC half, and that thread gets the
222 // WM_DESTROY message from the system; the MFC code does not know that it
223 // needs to cleanly destroy the IUIFramework.
224 HRESULT
OnViewChanged(UINT32 viewId,UI_VIEWTYPE typeID,IUnknown * view,UI_VIEWVERB verb,INT32 uReasonCode)225 LeashUIApplication::OnViewChanged(UINT32 viewId, UI_VIEWTYPE typeID,
226                                   IUnknown *view, UI_VIEWVERB verb,
227                                   INT32 uReasonCode)
228 {
229     IUIRibbon *ribbon;
230     HRESULT ret;
231 
232     // A viewId means "the ribbon".
233     if (viewId != 0 || typeID != UI_VIEWTYPE_RIBBON)
234         return E_NOTIMPL;
235 
236     switch(verb) {
237         case UI_VIEWVERB_DESTROY:
238             return SaveRibbonState();
239         case UI_VIEWVERB_CREATE:
240             ret = view->QueryInterface(IID_PPV_ARGS(&ribbon));
241             if (FAILED(ret))
242                 return ret;
243             ret = LoadRibbonState(ribbon);
244             ribbon->Release();
245             if (FAILED(ret))
246                 return ret;
247             // FALLTHROUGH
248         case UI_VIEWVERB_SIZE:
249             ret = view->QueryInterface(IID_PPV_ARGS(&ribbon));
250             if (FAILED(ret))
251                 return ret;
252             ret = ribbon->GetHeight(&ribbonHeight);
253             ribbon->Release();
254             if (FAILED(ret))
255                 return ret;
256             // Tell the main frame to recalculate its layout and redraw.
257             SendMessage(mainwin, WM_RIBBON_RESIZE, 0, NULL);
258             return S_OK;
259         case UI_VIEWVERB_ERROR:
260             // FALLTHROUGH
261         default:
262             return E_NOTIMPL;
263     }
264 }
265 
266 // Provide a command handler to which the command with ID commandId will
267 // be bound.  All of our commands get the same handler.
268 //
269 // The typeID argument is just an enum which classifies what type of
270 // command this is, grouping types of buttons together, collections,
271 // etc.  Since we only have one command handler, it can safely be ignored.
272 HRESULT
OnCreateUICommand(UINT32 commandId,UI_COMMANDTYPE typeID,IUICommandHandler ** commandHandler)273 LeashUIApplication::OnCreateUICommand(UINT32 commandId, UI_COMMANDTYPE typeID,
274                                       IUICommandHandler **commandHandler)
275 {
276     return this->commandHandler->QueryInterface(IID_PPV_ARGS(commandHandler));
277 }
278 
279 // It looks like this is called by the framework when the window with the
280 // ribbon is going away, to give the application a chance to free any
281 // application-specific resources (not from the framework) that were bound
282 // to a command in OnCreateUICommand.
283 //
284 // We do not have any such resources, so we do not need to implement this
285 // function other than by returning success.
286 HRESULT
OnDestroyUICommand(UINT32 commandId,UI_COMMANDTYPE typeID,IUICommandHandler * commandHandler)287 LeashUIApplication::OnDestroyUICommand(UINT32 commandId, UI_COMMANDTYPE typeID,
288                                        IUICommandHandler *commandHandler)
289 {
290     return S_OK;
291 }
292