From 5cd7d22192a8627d33af4f52232697b52f392c88 Mon Sep 17 00:00:00 2001 From: Robert Knight Date: Tue, 23 Aug 2011 19:53:03 +0100 Subject: [PATCH] Add Windows implementation of updater dialog The Windows implementation uses Win++, a thin header-only wrapper around the Windows API --- src/CMakeLists.txt | 8 +- src/UpdateDialogWin32.cpp | 187 ++++++++++++++++++++++++++++++++++++++ src/UpdateDialogWin32.h | 53 ++++++++++- src/main.cpp | 13 ++- 4 files changed, 256 insertions(+), 5 deletions(-) create mode 100644 src/UpdateDialogWin32.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 09d50a2..3e43bc4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -32,6 +32,9 @@ endif() if (APPLE) set(SOURCES ${SOURCES} UpdateDialogCocoa.mm) endif() +if (WIN32) + set(SOURCES ${SOURCES} UpdateDialogWin32.cpp) +endif() set (HEADERS Dir.h @@ -47,7 +50,10 @@ if (ENABLE_GTK) set(HEADERS ${HEADERS} UpdateDialogGtk.h) endif() if (APPLE) - set(SOURCES ${SOURCES} UpdateDialogCocoa.h) + set(HEADERS ${HEADERS} UpdateDialogCocoa.h) +endif() +if (WIN32) + set(HEADERS ${HEADERS} UpdateDialogWin32.h) endif() add_library(updatershared diff --git a/src/UpdateDialogWin32.cpp b/src/UpdateDialogWin32.cpp new file mode 100644 index 0000000..407020b --- /dev/null +++ b/src/UpdateDialogWin32.cpp @@ -0,0 +1,187 @@ +#include "UpdateDialogWin32.h" + +#include "Log.h" + +// enable themed controls +// see http://msdn.microsoft.com/en-us/library/bb773175%28v=vs.85%29.aspx +// for details +#pragma comment(linker,"\"/manifestdependency:type='win32' \ +name='Microsoft.Windows.Common-Controls' version='6.0.0.0' \ +processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"") + +static const char* updateDialogClassName = "UPDATEDIALOG"; + +static std::map windowDialogMap; + +// enable the standard Windows font for a widget +// (typically Tahoma or Segoe UI) +void setDefaultFont(HWND window) +{ + SendMessage(window, WM_SETFONT,(WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(TRUE, 0)); +} + +LRESULT WINAPI updateDialogWindowProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) +{ + std::map::const_iterator iter = windowDialogMap.find(window); + if (iter != windowDialogMap.end()) + { + return iter->second->windowProc(window,message,wParam,lParam); + } + else + { + return DefWindowProc(window,message,wParam,lParam); + } +}; + +void registerWindowClass() +{ + WNDCLASSEX wcex; + ZeroMemory(&wcex,sizeof(WNDCLASSEX)); + + wcex.cbSize = sizeof(WNDCLASSEX); + + HBRUSH background = CreateSolidBrush(GetSysColor(COLOR_3DFACE)); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = updateDialogWindowProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hIcon = 0; + wcex.hCursor = LoadCursor(0,IDC_ARROW); + wcex.hbrBackground = (HBRUSH)background; + wcex.lpszMenuName = (LPCTSTR)0; + wcex.lpszClassName = updateDialogClassName; + wcex.hIconSm = 0; + wcex.hInstance = GetModuleHandle(0); + + RegisterClassEx(&wcex); +} + +UpdateDialogWin32::UpdateDialogWin32() +{ + registerWindowClass(); +} + + +UpdateDialogWin32::~UpdateDialogWin32() +{ + for (std::map::iterator iter = windowDialogMap.begin(); + iter != windowDialogMap.end(); + iter++) + { + if (iter->second == this) + { + std::map::iterator oldIter = iter; + ++iter; + windowDialogMap.erase(oldIter); + } + else + { + ++iter; + } + } +} + +void UpdateDialogWin32::init() +{ + int width = 300; + int height = 130; + + DWORD style = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX; + m_window.CreateEx(0 /* dwExStyle */, + updateDialogClassName /* class name */, + "Mendeley Updater", + style, + 0, 0, width, height, + 0 /* parent */, 0 /* menu */, 0 /* reserved */); + m_progressBar.Create(&m_window); + m_finishButton.Create(&m_window); + m_progressLabel.Create(&m_window); + + installWindowProc(&m_window); + installWindowProc(&m_finishButton); + + setDefaultFont(m_progressLabel); + setDefaultFont(m_finishButton); + + m_progressBar.SetRange(0,100); + m_finishButton.SetWindowText("Finish"); + m_finishButton.EnableWindow(false); + m_progressLabel.SetWindowText("Installing Updates"); + + m_window.SetWindowPos(0,0,0,width,height,0); + m_progressBar.SetWindowPos(0,10,40,width - 30,20,0); + m_progressLabel.SetWindowPos(0,10,15,width - 30,20,0); + m_finishButton.SetWindowPos(0,width-100,70,80,25,0); + m_window.CenterWindow(); + m_window.ShowWindow(); +} + +void UpdateDialogWin32::exec() +{ + m_app.Run(); +} + +void UpdateDialogWin32::updateError(const std::string& errorMessage) +{ +} + +bool UpdateDialogWin32::updateRetryCancel(const std::string& message) +{ + return false; +} + +void UpdateDialogWin32::updateProgress(int percentage) +{ + Message* message = new Message(Message::UpdateProgress); + message->progress = percentage; + SendNotifyMessage(m_window.GetHwnd(),WM_USER,reinterpret_cast(message),0); +} + +void UpdateDialogWin32::updateFinished() +{ + Message* message = new Message(Message::UpdateFinished); + SendNotifyMessage(m_window.GetHwnd(),WM_USER,reinterpret_cast(message),0); +} + +LRESULT WINAPI UpdateDialogWin32::windowProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_COMMAND: + { + if (reinterpret_cast(lParam) == m_finishButton.GetHwnd()) + { + PostQuitMessage(0); + } + } + break; + case WM_USER: + { + if (window == m_window.GetHwnd()) + { + Message* message = reinterpret_cast(wParam); + switch (message->type) + { + case Message::UpdateFailed: + break; + case Message::UpdateProgress: + m_progressBar.SetPos(message->progress); + break; + case Message::UpdateFinished: + m_finishButton.EnableWindow(true); + m_progressLabel.SetWindowText("Updates installed. Click 'Finish' to restart the application."); + break; + } + delete message; + } + } + break; + } + return DefWindowProc(window,message,wParam,lParam); +} + +void UpdateDialogWin32::installWindowProc(CWnd* window) +{ + windowDialogMap[window->GetHwnd()] = this; +} diff --git a/src/UpdateDialogWin32.h b/src/UpdateDialogWin32.h index 4a49758..11d2972 100644 --- a/src/UpdateDialogWin32.h +++ b/src/UpdateDialogWin32.h @@ -1,7 +1,56 @@ #pragma once -class UpdateDialogWin32 +#include "Platform.h" +#include "UpdateObserver.h" + +#include "wincore.h" +#include "controls.h" +#include "stdcontrols.h" + +class UpdateDialogWin32 : public UpdateObserver { - // TODO - UI for update dialog on Windows + public: + UpdateDialogWin32(); + ~UpdateDialogWin32(); + + void init(); + void exec(); + + // implements UpdateObserver + virtual void updateError(const std::string& errorMessage); + virtual bool updateRetryCancel(const std::string& message); + virtual void updateProgress(int percentage); + virtual void updateFinished(); + + LRESULT WINAPI windowProc(HWND window, UINT message, WPARAM wParam, LPARAM lParam); + + private: + struct Message + { + enum Type + { + UpdateFailed, + UpdateProgress, + UpdateFinished + }; + + Message(Type _type) + : type(_type) + , progress(0) + { + } + + Type type; + std::string message; + int progress; + }; + + void installWindowProc(CWnd* window); + + CWinApp m_app; + CWnd m_window; + CStatic m_progressLabel; + CProgressBar m_progressBar; + CButton m_finishButton; }; diff --git a/src/main.cpp b/src/main.cpp index a777444..7bece47 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,6 +14,10 @@ #include "UpdateDialogCocoa.h" #endif +#if defined(PLATFORM_WINDOWS) + #include "UpdateDialogWin32.h" +#endif + #include void runWithUi(int argc, char** argv, UpdateInstaller* installer); @@ -118,7 +122,12 @@ void runWithUi(int argc, char** argv, UpdateInstaller* installer) #ifdef PLATFORM_WINDOWS void runWithUi(int argc, char** argv, UpdateInstaller* installer) { - // TODO - Windows UI - installer->run(); + UpdateDialogWin32 dialog; + installer->setObserver(&dialog); + dialog.init(); + tthread::thread updaterThread(runUpdaterThread,installer); + dialog.exec(); + updaterThread.join(); + installer->restartMainApp(); } #endif