Add a basic GTK update installation dialog and a stub function in UpdateInstaller to restart the main app after the update is complete.

* Add optional GTK mode to the updater build.  This pulls in a large number of extra dependencies,
   but dependencies which should be fairly ubiquitous on Linux systems.  If these dependencies prove
   to be a problem we could look at providing an alternative, more basic UI and loading the appropriate
   UI dynamically.
 * Implement update progress dialog for GTK.  The dialog needs visual polish but is functional.
This commit is contained in:
Robert Knight 2011-08-23 12:29:47 +01:00
parent f1367671b6
commit 85c4c58dc9
7 changed files with 252 additions and 22 deletions

View file

@ -3,6 +3,17 @@ add_subdirectory(tests)
find_package(Threads REQUIRED)
include(UsePkgConfig)
pkgconfig(gtk+-2.0 GTK2_INCLUDE_DIR GTK2_LINK_DIR GTK2_LINK_FLAGS GTK2_CFLAGS)
option(ENABLE_GTK ON)
if (ENABLE_GTK)
include_directories(${GTK2_INCLUDE_DIR})
add_definitions(${GTK2_CFLAGS})
add_definitions(-DENABLE_GTK)
endif()
add_definitions(-DTIXML_USE_STL)
set (SOURCES
@ -15,6 +26,10 @@ set (SOURCES
UpdaterOptions.cpp
)
if (ENABLE_GTK)
set(SOURCES ${SOURCES} UpdateDialogGtk.cpp)
endif()
set (HEADERS
Dir.h
FileOps.h
@ -25,6 +40,10 @@ set (HEADERS
UpdaterOptions.h
)
if (ENABLE_GTK)
set(HEADERS ${HEADERS} UpdateDialogGtk.h)
endif()
add_library(updatershared
${SOURCES}
${HEADERS}
@ -35,8 +54,14 @@ target_link_libraries(updatershared
tinyxml
minizip
tinythread
${GTK2_LINK_FLAGS}
)
if (ENABLE_GTK)
target_link_libraries(updatershared
${GTK2_LINK_FLAGS})
endif()
if (UNIX)
target_link_libraries(updatershared pthread)
endif()
@ -52,7 +77,6 @@ if(APPLE)
)
endif()
target_link_libraries(updater
updatershared
)

118
src/UpdateDialogGtk.cpp Normal file
View file

@ -0,0 +1,118 @@
#include "UpdateDialogGtk.h"
#include "Log.h"
#include "StringUtils.h"
#include <glib.h>
#include <gtk/gtk.h>
UpdateDialogGtk::UpdateDialogGtk()
: m_restartApp(false)
{
}
bool UpdateDialogGtk::restartApp() const
{
return m_restartApp;
}
void UpdateDialogGtk::init(int argc, char** argv)
{
gtk_init(&argc,&argv);
m_window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(m_window),"Mendeley Updater");
m_progressLabel = gtk_label_new("Installing Updates");
GtkWidget* windowLayout = gtk_vbox_new(FALSE,3);
GtkWidget* buttonLayout = gtk_hbox_new(FALSE,3);
m_finishButton = gtk_button_new_with_label("Finish");
gtk_widget_set_sensitive(m_finishButton,false);
m_progressBar = gtk_progress_bar_new();
gtk_signal_connect(GTK_OBJECT(m_finishButton),"clicked",
GTK_SIGNAL_FUNC(UpdateDialogGtk::finish),this);
gtk_container_add(GTK_CONTAINER(m_window),windowLayout);
gtk_container_set_border_width(GTK_CONTAINER(m_window),8);
gtk_container_add(GTK_CONTAINER(windowLayout),m_progressLabel);
gtk_container_add(GTK_CONTAINER(windowLayout),m_progressBar);
gtk_container_add(GTK_CONTAINER(windowLayout),buttonLayout);
gtk_box_pack_start(GTK_BOX(buttonLayout),m_finishButton,true,false,0);
gtk_widget_show(m_progressLabel);
gtk_widget_show(windowLayout);
gtk_widget_show(buttonLayout);
gtk_widget_show(m_finishButton);
gtk_widget_show(m_progressBar);
gtk_widget_show(m_window);
}
void UpdateDialogGtk::exec()
{
gtk_main();
}
void UpdateDialogGtk::finish(void* _dialog)
{
UpdateDialogGtk* dialog = static_cast<UpdateDialogGtk*>(_dialog);
dialog->m_restartApp = true;
gtk_main_quit();
}
gboolean UpdateDialogGtk::notify(void* _message)
{
Message* message = static_cast<Message*>(_message);
switch (message->type)
{
case Message::UpdateProgress:
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(message->dialog->m_progressBar),message->progress/100.0);
break;
case Message::UpdateFailed:
gtk_label_set_text(GTK_LABEL(message->dialog->m_progressLabel),
("There was a problem installing the update: " + message->message).c_str());;
gtk_widget_set_sensitive(message->dialog->m_finishButton,true);
break;
case Message::UpdateFinished:
gtk_label_set_text(GTK_LABEL(message->dialog->m_progressLabel),
"Update installed. Click 'Finish' to restart the application.");
gtk_widget_set_sensitive(message->dialog->m_finishButton,true);
break;
}
delete message;
// do not invoke this function again
return false;
}
// callbacks during update installation
void UpdateDialogGtk::updateError(const std::string& errorMessage)
{
Message* message = new Message(this,Message::UpdateFailed);
message->message = errorMessage;
g_idle_add(&UpdateDialogGtk::notify,message);
}
bool UpdateDialogGtk::updateRetryCancel(const std::string& message)
{
// TODO
}
void UpdateDialogGtk::updateProgress(int percentage)
{
Message* message = new Message(this,Message::UpdateProgress);
message->progress = percentage;
g_idle_add(&UpdateDialogGtk::notify,message);
}
void UpdateDialogGtk::updateFinished()
{
Message* message = new Message(this,Message::UpdateFinished);
g_idle_add(&UpdateDialogGtk::notify,message);
}

View file

@ -1,7 +1,55 @@
#pragma once
// TODO - UI for update dialog under Gtk
class UpdateDialogGtk
#include "UpdateObserver.h"
#include <gtk/gtk.h>
class UpdateDialogGtk : public UpdateObserver
{
public:
UpdateDialogGtk();
bool restartApp() const;
void init(int argc, char** argv);
void exec();
// observer callbacks - these may be called
// from a background thread
virtual void updateError(const std::string& errorMessage);
virtual bool updateRetryCancel(const std::string& message);
virtual void updateProgress(int percentage);
virtual void updateFinished();
private:
struct Message
{
enum Type
{
UpdateFailed,
UpdateProgress,
UpdateFinished
};
Message(UpdateDialogGtk* _dialog, Type _type)
: dialog(_dialog)
, type(_type)
{
}
UpdateDialogGtk* dialog;
Type type;
std::string message;
int progress;
};
static void finish(void* dialog);
static gboolean notify(void* message);
GtkWidget* m_window;
GtkWidget* m_progressLabel;
GtkWidget* m_finishButton;
GtkWidget* m_progressBar;
bool m_restartApp;
};

View file

@ -302,3 +302,13 @@ bool UpdateInstaller::checkAccess()
}
}
void UpdateInstaller::setObserver(UpdateObserver* observer)
{
m_observer = observer;
}
void UpdateInstaller::restartMainApp()
{
LOG(Warn,"Restarting main app not implemented");
}

View file

@ -30,6 +30,8 @@ class UpdateInstaller
void run() throw ();
void restartMainApp();
private:
void cleanup();
void revert();

View file

@ -1,5 +1,7 @@
#pragma once
#include <string>
class UpdateObserver
{
public:

View file

@ -6,17 +6,13 @@
#include "tinythread.h"
#if defined(PLATFORM_WINDOWS)
#include "UpdateDialogWin32.h"
#elif defined(PLATFORM_MAC)
#include "UpdateDialogCocoa.h"
#elif defined(PLATFORM_LINUX)
#if defined(PLATFORM_LINUX) and defined(ENABLE_GTK)
#include "UpdateDialogGtk.h"
#endif
#include <iostream>
void setupUi(UpdateInstaller* installer);
void runWithUi(int argc, char** argv, UpdateInstaller* installer);
void runUpdaterThread(void* arg)
{
@ -44,11 +40,6 @@ int main(int argc, char** argv)
script.parse(options.script);
}
if (options.mode == UpdateInstaller::Main)
{
setupUi(&installer);
}
LOG(Info,"started updater. install-dir: " + options.installDir
+ ", package-dir: " + options.packageDir
+ ", wait-pid: " + intToStr(options.waitPid)
@ -61,20 +52,55 @@ int main(int argc, char** argv)
installer.setScript(&script);
installer.setWaitPid(options.waitPid);
tthread::thread updaterThread(runUpdaterThread,&installer);
updaterThread.join();
if (options.mode == UpdateInstaller::Main)
{
runWithUi(argc,argv,&installer);
}
else
{
installer.run();
}
return 0;
}
void setupUi(UpdateInstaller* installer)
#ifdef PLATFORM_LINUX
void runWithUi(int argc, char** argv, UpdateInstaller* installer)
{
#if defined(PLATFORM_WINDOWS)
UpdateDialogWin32 dialog;
#elif defined(PLATFORM_MAC)
UpdateDialogCocoa dialog;
#elif defined(PLATFORM_LINUX)
#ifdef ENABLE_GTK
LOG(Info,"setting up GTK UI");
UpdateDialogGtk dialog;
installer->setObserver(&dialog);
dialog.init(argc,argv);
tthread::thread updaterThread(runUpdaterThread,installer);
dialog.exec();
updaterThread.join();
if (dialog.restartApp())
{
LOG(Info,"Restarting app after install");
installer->restartMainApp();
}
#else
// no UI available - do a silent install
installer->run();
installer->restartMainApp();
#endif
}
#endif
#ifdef PLATFORM_MAC
void runWithUi(int argc, char** argv, UpdateInstaller* installer)
{
// TODO - Cocoa UI
installer->run();
}
#endif
#ifdef PLATFORM_WINDOWS
void runWithUi(int argc, char** argv, UpdateInstaller* installer)
{
// TODO - Windows UI
installer->run();
}
#endif