// -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- // leash/LeashUIApplication.cpp - Implement IUIApplication for leash // // Copyright (C) 2014 by the Massachusetts Institute of Technology. // All rights reserved. // // Redistribution and use in source and binary forms, with or without // modification, are permitted provided that the following conditions // are met: // // * Redistributions of source code must retain the above copyright // notice, this list of conditions and the following disclaimer. // // * Redistributions in binary form must reproduce the above copyright // notice, this list of conditions and the following disclaimer in // the documentation and/or other materials provided with the // distribution. // // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED // OF THE POSSIBILITY OF SUCH DAMAGE. // Implementation of the LeashUIApplication class. In addition // to the minimum requirements for the IUIApplication interface, // it also saves and loads the ribbon state across application // sessions, and initiates a redraw of the parent window when // the ribbon size changes. #include #include #include "kfwribbon.h" #include "LeashUIApplication.h" #include "LeashUICommandHandler.h" HWND LeashUIApplication::mainwin; // The input hwnd is the window to which to bind the ribbon, i.e., // the Leash CMainFrame. HRESULT LeashUIApplication::CreateInstance(IUIApplication **out, HWND hwnd) { LeashUIApplication *app = NULL; LeashUICommandHandler *handler; HRESULT ret; if (out == NULL) return E_POINTER; *out = NULL; app = new LeashUIApplication(); ret = LeashUICommandHandler::CreateInstance(&app->commandHandler, hwnd); if (FAILED(ret)) goto out; ret = app->InitializeRibbon(hwnd); if (FAILED(ret)) goto out; mainwin = hwnd; // Only the Leash-specific handler type has the back-pointer. handler = static_cast(app->commandHandler); handler->app = app; *out = static_cast(app); app = NULL; ret = S_OK; out: if (app != NULL) app->Release(); return ret; } // Create a ribbon framework and ribbon for the LeashUIApplication. // CoInitializeEx() is required to be called before calling any COM // functions. AfxOleInit(), called from CLeashApp::InitInstance(), // makes that call, but it is only scoped to the calling thread, // and the LeashUIApplication is created from CMainFrame, which is // the frame for the MFC document template. It is unclear if the // Leash main thread will be the same thread which runs the frame // from the document template, so call CoInitializeEx() ourselves // just in case. It is safe to call multiple times (it will return // S_FALSE on subsequent calls). HRESULT LeashUIApplication::InitializeRibbon(HWND hwnd) { HRESULT ret; if (hwnd == NULL) return -1; ret = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); if (FAILED(ret)) return ret; ret = CoCreateInstance(CLSID_UIRibbonFramework, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&ribbonFramework)); if (FAILED(ret)) return ret; ret = ribbonFramework->Initialize(hwnd, this); if (FAILED(ret)) return ret; ret = ribbonFramework->LoadUI(GetModuleHandle(NULL), L"KFW_RIBBON_RIBBON"); if (FAILED(ret)) return ret; return S_OK; } // Import ribbon state (minimization state and Quick Access Toolbar // customizations) from a serialized stream stored in the registry. // In particular, the serialized state does not include the state // of checkboxes and other ribbon controls. // // This functionality is not very important, since we do not offer // much in the way of QAT customization. Paired with SaveRibbonState(). HRESULT LeashUIApplication::LoadRibbonState(IUIRibbon *ribbon) { HRESULT ret; IStream *s; s = SHOpenRegStream2(HKEY_CURRENT_USER, "Software\\MIT\\Kerberos5", "RibbonState", STGM_READ); if (s == NULL) return E_FAIL; ret = ribbon->LoadSettingsFromStream(s); s->Release(); return ret; } // Serialize the ribbon state (minimization state and Quick Access Toolbar // customizations) to the registry. Paired with LoadRibbonState(). HRESULT LeashUIApplication::SaveRibbonState() { HRESULT ret; IStream *s = NULL; IUIRibbon *ribbon = NULL; // No ribbon means no state to save. if (ribbonFramework == NULL) return S_OK; // ViewID of 0 is the ribbon itself. ret = ribbonFramework->GetView(0, IID_PPV_ARGS(&ribbon)); if (FAILED(ret)) return ret; s = SHOpenRegStream2(HKEY_CURRENT_USER, "Software\\MIT\\Kerberos5", "RibbonState", STGM_WRITE); if (s == NULL) { ret = E_FAIL; goto out; } ret = ribbon->SaveSettingsToStream(s); out: if (s != NULL) s->Release(); if (ribbon != NULL) ribbon->Release(); return ret; } UINT LeashUIApplication::GetRibbonHeight() { return ribbonHeight; } ULONG LeashUIApplication::AddRef() { return InterlockedIncrement(&refcnt); } ULONG LeashUIApplication::Release() { LONG tmp; tmp = InterlockedDecrement(&refcnt); if (tmp == 0) { if (commandHandler != NULL) commandHandler->Release(); if (ribbonFramework != NULL) ribbonFramework->Release(); delete this; } return tmp; } HRESULT LeashUIApplication::QueryInterface(REFIID iid, void **ppv) { if (ppv == NULL) return E_POINTER; if (iid == __uuidof(IUnknown)) { *ppv = static_cast(this); } else if (iid == __uuidof(IUIApplication)) { *ppv = static_cast(this); } else { *ppv = NULL; return E_NOINTERFACE; } AddRef(); return S_OK; } // This is called by the ribbon framework on events which change the (ribbon) // view, such as creation and resizing. (There may be other non-ribbon views // in the future, but for now, the ribbon is the only one.) With the hybrid // COM/MFC setup used by Leash, the destroy event is not always received, // since the main thread is in the MFC half, and that thread gets the // WM_DESTROY message from the system; the MFC code does not know that it // needs to cleanly destroy the IUIFramework. HRESULT LeashUIApplication::OnViewChanged(UINT32 viewId, UI_VIEWTYPE typeID, IUnknown *view, UI_VIEWVERB verb, INT32 uReasonCode) { IUIRibbon *ribbon; HRESULT ret; // A viewId means "the ribbon". if (viewId != 0 || typeID != UI_VIEWTYPE_RIBBON) return E_NOTIMPL; switch(verb) { case UI_VIEWVERB_DESTROY: return SaveRibbonState(); case UI_VIEWVERB_CREATE: ret = view->QueryInterface(IID_PPV_ARGS(&ribbon)); if (FAILED(ret)) return ret; ret = LoadRibbonState(ribbon); ribbon->Release(); if (FAILED(ret)) return ret; // FALLTHROUGH case UI_VIEWVERB_SIZE: ret = view->QueryInterface(IID_PPV_ARGS(&ribbon)); if (FAILED(ret)) return ret; ret = ribbon->GetHeight(&ribbonHeight); ribbon->Release(); if (FAILED(ret)) return ret; // Tell the main frame to recalculate its layout and redraw. SendMessage(mainwin, WM_RIBBON_RESIZE, 0, NULL); return S_OK; case UI_VIEWVERB_ERROR: // FALLTHROUGH default: return E_NOTIMPL; } } // Provide a command handler to which the command with ID commandId will // be bound. All of our commands get the same handler. // // The typeID argument is just an enum which classifies what type of // command this is, grouping types of buttons together, collections, // etc. Since we only have one command handler, it can safely be ignored. HRESULT LeashUIApplication::OnCreateUICommand(UINT32 commandId, UI_COMMANDTYPE typeID, IUICommandHandler **commandHandler) { return this->commandHandler->QueryInterface(IID_PPV_ARGS(commandHandler)); } // It looks like this is called by the framework when the window with the // ribbon is going away, to give the application a chance to free any // application-specific resources (not from the framework) that were bound // to a command in OnCreateUICommand. // // We do not have any such resources, so we do not need to implement this // function other than by returning success. HRESULT LeashUIApplication::OnDestroyUICommand(UINT32 commandId, UI_COMMANDTYPE typeID, IUICommandHandler *commandHandler) { return S_OK; }