Commit 9864ea24 authored by Melroy van den Berg's avatar Melroy van den Berg

Improve new bottle creating connection & loading bar, add new toolbar menu...

Improve new bottle creating connection & loading bar, add new toolbar menu options (like Open C: drive, reboot, kill processes, ..) and clear the detail panel after bottle removal
parent 9adbe132
Pipeline #1739 passed with stages
in 3 minutes and 18 seconds
......@@ -57,9 +57,14 @@ public:
BottleTypes::Bit bit,
BottleTypes::AudioDriver audio);
void DeleteBottle();
void SetActiveBottle(BottleItem* bottle);
const Glib::ustring& GetErrorMessage();
void RunProgram(string filename, bool is_msi_file);
void SetActiveBottle(BottleItem* bottle);
void OpenDriveC();
void Reboot();
void Update();
void KillProcesses();
private:
// Synchronizes access to data members
mutable std::mutex m_Mutex;
......@@ -72,6 +77,7 @@ private:
//// error_message is used by both the GUI thread and NewBottle thread (used a 'temp' location)
Glib::ustring m_error_message;
bool isBottleNotNull();
string GetWineVersion();
std::map<string, unsigned long> GetBottlePaths();
std::list<BottleItem> CreateWineBottles(string wineVersion, std::map<string, unsigned long> bottleDirs);
......
......@@ -51,13 +51,18 @@ public:
sigc::signal<void, BottleItem*> activeBottle; /*!< Set the active bottle in manager, based on the selected bottle */
sigc::signal<void, Glib::ustring&, Glib::ustring&, BottleTypes::Windows, BottleTypes::Bit, BottleTypes::AudioDriver> newBottle; /*!< Create new Wine Bottle Signal */
sigc::signal<void, string, bool> runProgram; /*!< Run an EXE or MSI application in Wine with provided filename */
sigc::signal<void> openDriveC;
sigc::signal<void> rebootBottle;
sigc::signal<void> updateBottle;
sigc::signal<void> killRunningProcesses;
explicit MainWindow(Menu& menu);
virtual ~MainWindow();
void SetDispatcher(SignalDispatcher& signalDispatcher);
void SetWineBottles(std::list<BottleItem>& bottles);
void SetDetailedInfo(BottleItem& bottle);
void ResetDetailedInfo();
void ShowErrorMessage(const Glib::ustring& message);
bool ShowConfirmDialog(const Glib::ustring& message);
......@@ -66,7 +71,6 @@ public:
virtual void on_new_bottle_created();
virtual void on_run_button_clicked();
virtual void on_hide_window();
virtual void on_not_implemented(); // Shall be removed later!
virtual void on_exec_failure();
protected:
......@@ -100,6 +104,8 @@ protected:
Gtk::ToolButton edit_button; /*!< Edit toolbar button */
Gtk::ToolButton settings_button; /*!< Settings toolbar button */
Gtk::ToolButton reboot_button; /*!< Reboot toolbar button */
Gtk::ToolButton update_button; /*!< Update toolbar button */
Gtk::ToolButton kill_processes_button; /*!< Kill processes toolbar button */
private:
NewBottleAssistant newBottleAssistant; /*!< New bottle wizard (behind: new button toolbar) */
......
......@@ -29,15 +29,16 @@
class Menu: public Gtk::MenuBar
{
public:
sigc::signal<void> signal_preferences; /*!< on preferences button clicked signal */
sigc::signal<void> signal_quit; /*!< on quite button clicked signal */
sigc::signal<void> signal_refresh; /*!< on refresh button clicked signal */
sigc::signal<void> signal_new_machine; /*!< on new button clicked signal */
sigc::signal<void> signal_run; /*!< on new new button clicked signal */
sigc::signal<void> signal_edit_machine; /*!< on edit button clicked signal */
sigc::signal<void> signal_settings_machine; /*!< on settings button clicked signal */
sigc::signal<void> signal_remove_machine; /*!< on remove button clicked signal */
sigc::signal<void> signal_show_about; /*!< on about button clicked signal */
sigc::signal<void> signal_preferences; /*!< preferences button clicked signal */
sigc::signal<void> signal_quit; /*!< quite button clicked signal */
sigc::signal<void> signal_refresh; /*!< refresh button clicked signal */
sigc::signal<void> signal_new_machine; /*!< new machine button clicked signal */
sigc::signal<void> signal_run; /*!< run button clicked signal */
sigc::signal<void> signal_open_drive_c; /*!< open C: drive clicked signal */
sigc::signal<void> signal_edit_machine; /*!< edit button clicked signal */
sigc::signal<void> signal_settings_machine; /*!< settings button clicked signal */
sigc::signal<void> signal_remove_machine; /*!< remove button clicked signal */
sigc::signal<void> signal_show_about; /*!< about button clicked signal */
Menu();
virtual ~Menu();
......
......@@ -61,6 +61,8 @@ private:
void createSecondPage();
void createThirdPage();
sigc::connection timer; /*!< Timer connection */
// Child widgets:
Gtk::Box m_vbox;
Gtk::Box m_vbox2;
......
/**
* Copyright (c) 2019 WineGUI
* Copyright (c) 2019-2020 WineGUI
*
* \file bottle_manager.cc
* \brief The controller controls it all
......@@ -27,6 +27,10 @@
#include <chrono>
#include <stdexcept>
/*************************************************************
* Public member functions *
*************************************************************/
/**
* \brief Constructor
* \param mainWindow Address to the main Window
......@@ -54,28 +58,22 @@ void BottleManager::Prepare()
{
// Install winetricks if not yet present,
// Winetricks script is used by WineGUI.
if(!Helper::FileExists(Helper::GetWinetricksLocation()))
{
if (!Helper::FileExists(Helper::GetWinetricksLocation())) {
try {
Helper::InstallOrUpdateWinetricks();
}
catch(const std::runtime_error& error)
{
catch(const std::runtime_error& error) {
mainWindow.ShowErrorMessage(error.what());
}
}
else
{
} else {
// Update existing script
try {
Helper::SelfUpdateWinetricks();
}
catch(const std::invalid_argument& msg)
{
catch (const std::invalid_argument& msg) {
std::cout << "WARN: " << msg.what() << std::endl;
}
catch(const std::runtime_error& error)
{
catch (const std::runtime_error& error) {
mainWindow.ShowErrorMessage(error.what());
}
}
......@@ -98,25 +96,23 @@ void BottleManager::UpdateBottles()
try {
bottleDirs = GetBottlePaths();
}
catch(const std::runtime_error& error)
catch (const std::runtime_error& error)
{
mainWindow.ShowErrorMessage(error.what());
return; // stop
}
if(bottleDirs.size() > 0) {
if (bottleDirs.size() > 0) {
try {
// Create wine bottles from bottle directories and wine version
bottles = CreateWineBottles(GetWineVersion(), bottleDirs);
}
catch(const std::runtime_error& error)
{
catch (const std::runtime_error& error) {
mainWindow.ShowErrorMessage(error.what());
return; // stop
}
if (!bottles.empty())
{
if (!bottles.empty()) {
// Update main Window
mainWindow.SetWineBottles(bottles);
......@@ -126,9 +122,7 @@ void BottleManager::UpdateBottles()
mainWindow.SetDetailedInfo(*first);
// Set active bottle at the first
this->activeBottle = &(*first);
}
else
{
} else {
mainWindow.ShowErrorMessage("Could not create an overview of Windows Machines. Empty list.");
// Send reset signal to reset the active bottle to NULL
......@@ -136,9 +130,7 @@ void BottleManager::UpdateBottles()
// Reset locally
this->activeBottle = nullptr;
}
}
else
{
} else {
// Send reset signal to reset the active bottle to NULL
resetActiveBottle.emit();
// Reset locally
......@@ -169,12 +161,10 @@ void BottleManager::NewBottle(
bool bottle_created = false;
try {
// First create a new Wine Bottle
Helper::CreateWineBottle(wine_prefix, bit);
Helper::CreateWineBottle(wine_prefix, bit);
bottle_created = true;
}
catch (const std::runtime_error& error)
{
catch (const std::runtime_error& error) {
{
std::lock_guard<std::mutex> lock(m_Mutex);
m_error_message = ("Something went wrong during creation of a new Windows machine!\n" +
......@@ -185,16 +175,13 @@ void BottleManager::NewBottle(
}
// Continue with additional settings
if (bottle_created)
{
if (bottle_created) {
// Only change Windows OS when NOT default
if (windows_version != WineDefaults::WINDOWS_OS)
{
if (windows_version != WineDefaults::WINDOWS_OS) {
try {
Helper::SetWindowsVersion(wine_prefix, windows_version);
}
catch (const std::runtime_error& error)
{
catch (const std::runtime_error& error) {
{
std::lock_guard<std::mutex> lock(m_Mutex);
m_error_message = ("Something went wrong during setting another Windows version.\n" +
......@@ -206,8 +193,7 @@ void BottleManager::NewBottle(
}
// Only if virtual desktop is not empty, enable it
if (!virtual_desktop_resolution.empty())
{
if (!virtual_desktop_resolution.empty()) {
try {
Helper::SetVirtualDesktop(wine_prefix, virtual_desktop_resolution);
}
......@@ -224,13 +210,11 @@ void BottleManager::NewBottle(
}
// Only if Audio driver is not default, change it
if (audio != WineDefaults::AUDIO_DRIVER)
{
if (audio != WineDefaults::AUDIO_DRIVER) {
try {
Helper::SetAudioDriver(wine_prefix, audio);
}
catch (const std::runtime_error& error)
{
catch (const std::runtime_error& error) {
{
std::lock_guard<std::mutex> lock(m_Mutex);
m_error_message = ("Something went wrong during setting another audio driver.\n" +
......@@ -260,15 +244,13 @@ void BottleManager::NewBottle(
*/
void BottleManager::DeleteBottle()
{
if (activeBottle != nullptr)
{
try
{
if (activeBottle != nullptr) {
try {
Glib::ustring prefix_path = activeBottle->wine_location();
string windows = BottleTypes::toString(activeBottle->windows());
// Are you sure?'
if(mainWindow.ShowConfirmDialog("Are you sure you want to *permanently* remove machine named '" + Helper::GetName(prefix_path) + "' running " + windows + "?\n\nNote: This action cannot be undone!"))
{
if (mainWindow.ShowConfirmDialog("Are you sure you want to *permanently* remove machine named '" +
Helper::GetName(prefix_path) + "' running " + windows + "?\n\nNote: This action cannot be undone!")) {
Helper::RemoveWineBottle(prefix_path);
this->UpdateBottles();
}
......@@ -276,8 +258,7 @@ void BottleManager::DeleteBottle()
// Nothing, canceled
}
}
catch (const std::runtime_error& error)
{
catch (const std::runtime_error& error) {
mainWindow.ShowErrorMessage(error.what());
}
}
......@@ -287,6 +268,17 @@ void BottleManager::DeleteBottle()
}
}
/**
* \brief Signal handler when the active bottle changes, update active bottle
* \param[in] bottle - New bottle
*/
void BottleManager::SetActiveBottle(BottleItem* bottle)
{
if (bottle != nullptr) {
this->activeBottle = bottle;
}
}
/**
* \brief Get error message (stored from manager thread)
* \return Return the error message
......@@ -304,28 +296,74 @@ const Glib::ustring& BottleManager::GetErrorMessage()
*/
void BottleManager::RunProgram(string filename, bool is_msi_file = false)
{
// Check if there is an active bottle set
if (activeBottle != nullptr)
{
if (isBottleNotNull()) {
Glib::ustring wine_prefix = activeBottle->wine_location();
std::thread t(&Helper::RunProgram, wine_prefix, filename, is_msi_file);
t.detach();
}
else
{
mainWindow.ShowErrorMessage("No Windows Machine selected/empty. First create a new machine!\n\nAborted.");
}
/**
* \brief Open the Wine C: drive on the current active bottle
*/
void BottleManager::OpenDriveC()
{
if (isBottleNotNull()) {
GError *error = NULL;
if (!g_app_info_launch_default_for_uri(("file://" + activeBottle->wine_c_drive()).c_str(), NULL, &error)) {
g_warning("Failed to open uri: %s", error->message);
mainWindow.ShowErrorMessage("Could not open the C:/ drive.");
}
}
}
/**
* \brief Signal handler when the active bottle changes, update active bottle
* \param[in] bottle - New bottle
* \brief Reboot bottle
*/
void BottleManager::SetActiveBottle(BottleItem* bottle)
void BottleManager::Reboot()
{
if (bottle != nullptr) {
this->activeBottle = bottle;
if (isBottleNotNull()) {
Glib::ustring wine_prefix = activeBottle->wine_location();
std::thread t(&Helper::RunProgram, wine_prefix, "wineboot -r", false);
t.detach();
}
}
/**
* \brief Update bottle
*/
void BottleManager::Update()
{
if (isBottleNotNull()) {
Glib::ustring wine_prefix = activeBottle->wine_location();
std::thread t(&Helper::RunProgram, wine_prefix, "wineboot -u", false);
t.detach();
}
}
/**
* \brief Kill running processes in bottle
*/
void BottleManager::KillProcesses()
{
if (isBottleNotNull()) {
Glib::ustring wine_prefix = activeBottle->wine_location();
std::thread t(&Helper::RunProgram, wine_prefix, "wineboot -k", false);
t.detach();
}
}
/*************************************************************
* Private member functions *
*************************************************************/
bool BottleManager::isBottleNotNull()
{
bool isNull = (activeBottle == nullptr);
if (isNull) {
mainWindow.ShowErrorMessage("No Windows Machine selected/empty. First create a new machine!\n\nAborted.");
}
return !isNull;
}
/**
......
......@@ -62,6 +62,9 @@ MainWindow::MainWindow(Menu& menu)
// Using a Vertical box container
add(vbox);
// Reset the right panel to default values
this->ResetDetailedInfo();
// Right panel menu signals
new_button.signal_clicked().connect(sigc::mem_fun(*this,
&MainWindow::on_new_bottle_button_clicked));
......@@ -69,11 +72,11 @@ MainWindow::MainWindow(Menu& menu)
&MainWindow::on_new_bottle_apply));
run_button.signal_clicked().connect(sigc::mem_fun(*this,
&MainWindow::on_run_button_clicked));
open_c_driver_button.signal_clicked().connect(sigc::mem_fun(*this,
&MainWindow::on_not_implemented));
open_c_driver_button.signal_clicked().connect(openDriveC);
reboot_button.signal_clicked().connect(sigc::mem_fun(*this,
&MainWindow::on_not_implemented));// TODO: Really handy at all?
reboot_button.signal_clicked().connect(rebootBottle);// TODO: execute wineboot -r
update_button.signal_clicked().connect(updateBottle);// TODO: execute wineboot -u
kill_processes_button.signal_clicked().connect(killRunningProcesses);// TODO: execute wineboot -k
// Show the widget children
show_all_children();
......@@ -144,6 +147,21 @@ void MainWindow::SetDetailedInfo(BottleItem& bottle)
virtual_desktop.set_text(bottle.virtual_desktop());
}
/**
* \brief Reset the detailed info panel
*/
void MainWindow::ResetDetailedInfo()
{
name.set_text("-");
window_version.set_text("");
wine_version.set_text("v?");
wine_location.set_text("");
c_drive_location.set_text("");
wine_last_changed.set_text("");
audio_driver.set_text("");
virtual_desktop.set_text("");
}
/**
* \brief Just show an error message
* \param[in] message - Show this error message
......@@ -271,17 +289,6 @@ void MainWindow::on_hide_window()
hide();
}
/**
* \brief Not implemented feature
*/
void MainWindow::on_not_implemented()
{
Gtk::MessageDialog dialog(*this, "This feature is not yet implemented. Sorry :\\", false, Gtk::MESSAGE_INFO, Gtk::BUTTONS_OK);
dialog.set_title("Not implemented!");
dialog.set_modal(false);
dialog.run();
}
/**
* \brief Not implemented feature
*/
......@@ -358,24 +365,28 @@ void MainWindow::CreateRightPanel()
Gtk::Image* new_image = Gtk::manage(new Gtk::Image());
new_image->set_from_icon_name("list-add", Gtk::IconSize(Gtk::ICON_SIZE_LARGE_TOOLBAR));
new_button.set_label("New");
new_button.set_tooltip_text("Create a new machine!");
new_button.set_icon_widget(*new_image);
toolbar.insert(new_button, 0);
Gtk::Image* run_image = Gtk::manage(new Gtk::Image());
run_image->set_from_icon_name("media-playback-start", Gtk::IconSize(Gtk::ICON_SIZE_LARGE_TOOLBAR));
run_button.set_label("Run Program...");
run_button.set_tooltip_text("Run exe or msi in Wine Machine");
run_button.set_icon_widget(*run_image);
toolbar.insert(run_button, 1);
Gtk::Image* open_c_drive_image = Gtk::manage(new Gtk::Image());
open_c_drive_image->set_from_icon_name("drive-harddisk", Gtk::IconSize(Gtk::ICON_SIZE_LARGE_TOOLBAR));
open_c_driver_button.set_label("Open C: Drive");
open_c_driver_button.set_tooltip_text("Open the C: drive location in file manager");
open_c_driver_button.set_icon_widget(*open_c_drive_image);
toolbar.insert(open_c_driver_button, 2);
Gtk::Image* edit_image = Gtk::manage(new Gtk::Image());
edit_image->set_from_icon_name("document-edit", Gtk::IconSize(Gtk::ICON_SIZE_LARGE_TOOLBAR));
edit_button.set_label("Edit");
edit_button.set_tooltip_text("Edit Wine Machine");
edit_button.set_icon_widget(*edit_image);
toolbar.insert(edit_button, 3);
......@@ -388,9 +399,24 @@ void MainWindow::CreateRightPanel()
Gtk::Image* reboot_image = Gtk::manage(new Gtk::Image());
reboot_image->set_from_icon_name("view-refresh", Gtk::IconSize(Gtk::ICON_SIZE_LARGE_TOOLBAR));
reboot_button.set_label("Reboot");
reboot_button.set_tooltip_text("Reboot the Wine Machine");
reboot_button.set_icon_widget(*reboot_image);
toolbar.insert(reboot_button, 5);
Gtk::Image* update_image = Gtk::manage(new Gtk::Image());
update_image->set_from_icon_name("system-software-update", Gtk::IconSize(Gtk::ICON_SIZE_LARGE_TOOLBAR));
update_button.set_label("Update");
update_button.set_tooltip_text("Update the Wine Machine");
update_button.set_icon_widget(*update_image);
toolbar.insert(update_button, 6);
Gtk::Image* kill_processes_image = Gtk::manage(new Gtk::Image());
kill_processes_image->set_from_icon_name("process-stop", Gtk::IconSize(Gtk::ICON_SIZE_LARGE_TOOLBAR));
kill_processes_button.set_label("Kill processes");
kill_processes_button.set_tooltip_text("Kill all running processes in Wine Machine");
kill_processes_button.set_icon_widget(*kill_processes_image);
toolbar.insert(kill_processes_button, 7);
// Add toolbar to right box
right_box.add(toolbar);
right_box.add(separator1);
......
......@@ -49,8 +49,10 @@ Menu::Menu()
// Machine submenu
auto newitem = CreateImageMenuItem("New", "list-add");
newitem->signal_activate().connect(signal_new_machine);
auto run = CreateImageMenuItem("Run...", "system-run");
auto run = CreateImageMenuItem("Run...", "media-playback-start");
run->signal_activate().connect(signal_run);
auto open_drive_c = CreateImageMenuItem("Open C: Drive", "drive-harddisk");
open_drive_c->signal_activate().connect(signal_open_drive_c);
auto edit = CreateImageMenuItem("Edit", "document-edit");
edit->signal_activate().connect(signal_edit_machine);
auto settings = CreateImageMenuItem("Settings", "preferences-other");
......@@ -77,6 +79,7 @@ Menu::Menu()
machine_submenu.append(*newitem);
machine_submenu.append(separator2);
machine_submenu.append(*run);
machine_submenu.append(*open_drive_c);
machine_submenu.append(*edit);
machine_submenu.append(*settings);
machine_submenu.append(*remove);
......
......@@ -90,6 +90,10 @@ void NewBottleAssistant::setDefaultValues()
virtual_desktop_check.set_active(false);
virtual_desktop_resolution_entry.set_text("960x540");
loading_bar.set_fraction(0.0);
if (timer) {
timer.disconnect();
}
}
/**
......@@ -287,7 +291,7 @@ void NewBottleAssistant::on_assistant_apply()
}
/* Start a timer to give the user feedback about the changes taking a few seconds to apply. */
sigc::connection conn = Glib::signal_timeout().connect(
timer = Glib::signal_timeout().connect(
sigc::mem_fun(*this, &NewBottleAssistant::apply_changes_gradually),
time_interval);
}
......@@ -348,7 +352,8 @@ void NewBottleAssistant::on_virtual_desktop_toggle()
}
/**
* \brief Smooth loading bar
* \brief Smooth loading bar and pulse loading bar when
* it takes longer than expected
*/
bool NewBottleAssistant::apply_changes_gradually()
{
......@@ -356,11 +361,12 @@ bool NewBottleAssistant::apply_changes_gradually()
if (fraction <= 1.0)
{
loading_bar.set_fraction(fraction);
return true;
} else {
// Say something else to the user
loading_bar.set_pulse_step(0.3);
loading_bar.pulse();
// Say it takes a bit longer
apply_label.set_text("Almost done creating the new machine...");
// Stop timer
return false;
}
// Never stop the timer (only when disconnect() is called)
return true;
}
......@@ -77,12 +77,14 @@ void SignalDispatcher::SetMainWindow(MainWindow* mainWindow)
*/
void SignalDispatcher::DispatchSignals()
{
// Menu signals
menu.signal_preferences.connect(sigc::mem_fun(preferencesWindow, &PreferencesWindow::show));
menu.signal_quit.connect(sigc::mem_fun(*mainWindow, &MainWindow::on_hide_window)); /*!< When quit button is pressed, hide main window and therefor closes the app */
menu.signal_show_about.connect(sigc::mem_fun(about, &AboutDialog::show));
menu.signal_refresh.connect(sigc::mem_fun(manager, &BottleManager::UpdateBottles));
menu.signal_new_machine.connect(sigc::mem_fun(*mainWindow, &MainWindow::on_new_bottle_button_clicked));
menu.signal_run.connect(sigc::mem_fun(*mainWindow, &MainWindow::on_run_button_clicked));
menu.signal_open_drive_c.connect(sigc::mem_fun(manager, &BottleManager::OpenDriveC));
menu.signal_edit_machine.connect(sigc::mem_fun(editWindow, &EditWindow::show));
menu.signal_settings_machine.connect(sigc::mem_fun(settingsWindow, &SettingsWindow::Show));
menu.signal_remove_machine.connect(sigc::mem_fun(manager, &BottleManager::DeleteBottle));
......@@ -97,12 +99,20 @@ void SignalDispatcher::DispatchSignals()
// Distribute the reset bottle signal
manager.resetActiveBottle.connect(sigc::mem_fun(editWindow, &EditWindow::ResetActiveBottle));
manager.resetActiveBottle.connect(sigc::mem_fun(settingsWindow, &SettingsWindow::ResetActiveBottle));
manager.resetActiveBottle.connect(sigc::mem_fun(*mainWindow, &MainWindow::ResetDetailedInfo));
mainWindow->newBottle.connect(sigc::mem_fun(this, &SignalDispatcher::on_new_bottle));
mainWindow->runProgram.connect(sigc::mem_fun(manager, &BottleManager::RunProgram));
mainWindow->openDriveC.connect(sigc::mem_fun(manager, &BottleManager::OpenDriveC));
mainWindow->rebootBottle.connect(sigc::mem_fun(manager, &BottleManager::Reboot));
mainWindow->updateBottle.connect(sigc::mem_fun(manager, &BottleManager::Update));
mainWindow->killRunningProcesses.connect(sigc::mem_fun(manager, &BottleManager::KillProcesses));
// When bottle created, the finish (or error message) event is called
m_FinishDispatcher.connect(sigc::mem_fun(this, &SignalDispatcher::on_new_bottle_created));
m_ErrorMessageDispatcher.connect(sigc::mem_fun(this, &SignalDispatcher::on_error_message));
// When the WineExec() results into a non-zero exit code the failureOnExec it triggered
Helper* helper = Helper::getInstance();
// Using Dispatcher instead of signal, will result in that the message box runs in the main thread.
if (helper != NULL) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment