summaryrefslogtreecommitdiff
path: root/ui/progress_indicator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ui/progress_indicator.cpp')
-rw-r--r--ui/progress_indicator.cpp970
1 files changed, 970 insertions, 0 deletions
diff --git a/ui/progress_indicator.cpp b/ui/progress_indicator.cpp
new file mode 100644
index 00000000..f41ccc36
--- /dev/null
+++ b/ui/progress_indicator.cpp
@@ -0,0 +1,970 @@
+// **************************************************************************
+// * This file is part of the FreeFileSync project. It is distributed under *
+// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
+// * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) *
+// **************************************************************************
+//
+#include "progress_indicator.h"
+#include <memory>
+#include "gui_generated.h"
+#include <wx/stopwatch.h>
+#include "../library/resources.h"
+#include "../shared/string_conv.h"
+#include "../shared/util.h"
+#include "../library/statistics.h"
+#include <wx/wupdlock.h>
+#include "../shared/global_func.h"
+#include "tray_icon.h"
+#include <boost/shared_ptr.hpp>
+
+#ifdef FFS_WIN
+#include "../shared/taskbar.h"
+#endif
+
+using namespace ffs3;
+
+
+namespace
+{
+void setNewText(const wxString& newText, wxTextCtrl& control, bool& updateLayout)
+{
+ if (control.GetValue().length() != newText.length())
+ updateLayout = true; //avoid screen flicker: apply only when necessary
+
+ if (control.GetValue() != newText)
+ control.ChangeValue(newText);
+}
+
+void setNewText(const wxString& newText, wxStaticText& control, bool& updateLayout)
+{
+ if (control.GetLabel().length() != newText.length())
+ updateLayout = true; //avoid screen flicker
+
+ if (control.GetLabel() != newText)
+ control.SetLabel(newText);
+}
+}
+
+
+class CompareStatus::CompareStatusImpl : public CompareStatusGenerated
+{
+public:
+ CompareStatusImpl(wxTopLevelWindow& parentWindow);
+
+ void init(); //make visible, initialize all status values
+ void finalize(); //hide again
+
+ void switchToCompareBytewise(int totalObjectsToProcess, wxLongLong totalDataToProcess);
+ void incScannedObjects_NoUpdate(int number);
+ void incProcessedCmpData_NoUpdate(int objectsProcessed, wxLongLong dataProcessed);
+ void setStatusText_NoUpdate(const Zstring& text);
+ void updateStatusPanelNow();
+
+private:
+ wxTopLevelWindow& parentWindow_;
+ wxString titleTextBackup;
+
+ //status variables
+ size_t scannedObjects;
+ Zstring currentStatusText;
+
+ wxStopWatch timeElapsed;
+
+ //gauge variables
+ int totalObjects;
+ wxLongLong totalData; //each data element represents one byte for proper progress indicator scaling
+ int currentObjects; //each object represents a file or directory processed
+ wxLongLong currentData;
+ double scalingFactor; //nr of elements has to be normalized to smaller nr. because of range of int limitation
+
+ void showProgressExternally(const wxString& progressText, float percent = 0);
+
+ enum CurrentStatus
+ {
+ SCANNING,
+ COMPARING_CONTENT,
+ };
+
+ CurrentStatus status;
+
+#ifdef FFS_WIN
+ std::auto_ptr<util::TaskbarProgress> taskbar_;
+#endif
+
+ //remaining time
+ std::auto_ptr<Statistics> statistics;
+ long lastStatCallSpeed; //used for calculating intervals between statistics update
+ long lastStatCallRemTime; //
+};
+
+//redirect to implementation
+CompareStatus::CompareStatus(wxTopLevelWindow& parentWindow) :
+ pimpl(new CompareStatusImpl(parentWindow)) {}
+
+CompareStatus::~CompareStatus()
+{
+ //DON'T delete pimpl! it relies on wxWidgets destruction (parent window destroys child windows!)
+}
+
+wxWindow* CompareStatus::getAsWindow()
+{
+ return pimpl;
+}
+
+void CompareStatus::init()
+{
+ pimpl->init();
+}
+
+void CompareStatus::finalize()
+{
+ pimpl->finalize();
+}
+
+void CompareStatus::switchToCompareBytewise(int totalObjectsToProcess, wxLongLong totalDataToProcess)
+{
+ pimpl->switchToCompareBytewise(totalObjectsToProcess, totalDataToProcess);
+}
+
+void CompareStatus::incScannedObjects_NoUpdate(int number)
+{
+ pimpl->incScannedObjects_NoUpdate(number);
+}
+
+void CompareStatus::incProcessedCmpData_NoUpdate(int objectsProcessed, wxLongLong dataProcessed)
+{
+ pimpl->incProcessedCmpData_NoUpdate(objectsProcessed, dataProcessed);
+}
+
+void CompareStatus::setStatusText_NoUpdate(const Zstring& text)
+{
+ pimpl->setStatusText_NoUpdate(text);
+}
+
+void CompareStatus::updateStatusPanelNow()
+{
+ pimpl->updateStatusPanelNow();
+}
+//########################################################################################
+
+
+CompareStatus::CompareStatusImpl::CompareStatusImpl(wxTopLevelWindow& parentWindow) :
+ CompareStatusGenerated(&parentWindow),
+ parentWindow_(parentWindow),
+ scannedObjects(0),
+ totalObjects(0),
+ totalData(0),
+ currentObjects(0),
+ currentData(0),
+ scalingFactor(0),
+ status(SCANNING),
+ lastStatCallSpeed(-1000000), //some big number
+ lastStatCallRemTime(-1000000)
+{
+ init();
+}
+
+
+void CompareStatus::CompareStatusImpl::init()
+{
+ titleTextBackup = parentWindow_.GetTitle();
+
+#ifdef FFS_WIN
+ try //try to get access to Windows 7 Taskbar
+ {
+ taskbar_.reset(new util::TaskbarProgress(parentWindow_));
+ }
+ catch (const util::TaskbarNotAvailable&) {}
+#endif
+
+ status = SCANNING;
+
+ //initialize gauge
+ m_gauge2->SetRange(50000);
+ m_gauge2->SetValue(0);
+
+ //initially hide status that's relevant for comparing bytewise only
+ bSizerFilesFound->Show(true);
+ bSizerFilesRemaining->Show(false);
+ sSizerSpeed->Show(false);
+ sSizerTimeRemaining->Show(false);
+
+ m_gauge2->Hide();
+ bSizer42->Layout();
+
+ scannedObjects = 0;
+ currentStatusText.clear();
+
+ totalObjects = 0;
+ totalData = 0;
+ currentObjects = 0;
+ currentData = 0;
+ scalingFactor = 0;
+
+ statistics.reset();
+
+ timeElapsed.Start(); //measure total time
+
+ updateStatusPanelNow();
+
+ Show(); //make visible
+}
+
+
+void CompareStatus::CompareStatusImpl::finalize() //hide again
+{
+#ifdef FFS_WIN
+ taskbar_.reset();
+#endif
+
+ Hide();
+ parentWindow_.SetTitle(titleTextBackup);
+}
+
+
+void CompareStatus::CompareStatusImpl::switchToCompareBytewise(int totalObjectsToProcess, wxLongLong totalDataToProcess)
+{
+ status = COMPARING_CONTENT;
+
+ currentData = 0;
+ totalData = totalDataToProcess;
+
+ currentObjects = 0;
+ totalObjects = totalObjectsToProcess;
+
+ if (totalData != 0)
+ scalingFactor = 50000 / totalData.ToDouble(); //let's normalize to 50000
+ else
+ scalingFactor = 0;
+
+ //set new statistics handler: 10 seconds "window" for remaining time, 5 seconds for speed
+ statistics.reset(new Statistics(totalObjectsToProcess, totalDataToProcess.ToDouble(), 10000, 5000));
+ lastStatCallSpeed = -1000000; //some big number
+ lastStatCallRemTime = -1000000;
+
+ //show status for comparing bytewise
+ bSizerFilesFound->Show(false);
+ bSizerFilesRemaining->Show(true);
+ sSizerSpeed->Show(true);
+ sSizerTimeRemaining->Show(true);
+
+ m_gauge2->Show();
+ bSizer42->Layout();
+}
+
+
+void CompareStatus::CompareStatusImpl::incScannedObjects_NoUpdate(int number)
+{
+ scannedObjects += number;
+}
+
+
+void CompareStatus::CompareStatusImpl::incProcessedCmpData_NoUpdate(int objectsProcessed, wxLongLong dataProcessed)
+{
+ currentData += dataProcessed;
+ currentObjects += objectsProcessed;
+}
+
+
+void CompareStatus::CompareStatusImpl::setStatusText_NoUpdate(const Zstring& text)
+{
+ currentStatusText = text;
+}
+
+
+void CompareStatus::CompareStatusImpl::showProgressExternally(const wxString& progressText, float percent)
+{
+ if (parentWindow_.GetTitle() != progressText)
+ parentWindow_.SetTitle(progressText);
+
+ //show progress on Windows 7 taskbar
+#ifdef FFS_WIN
+ using namespace util;
+
+ if (taskbar_.get())
+ {
+ const size_t current = 100000 * percent / 100;
+ const size_t total = 100000;
+ switch (status)
+ {
+ case SCANNING:
+ taskbar_->setStatus(TaskbarProgress::STATUS_INDETERMINATE);
+ break;
+ case COMPARING_CONTENT:
+ taskbar_->setStatus(TaskbarProgress::STATUS_NORMAL);
+ taskbar_->setProgress(current, total);
+ break;
+ }
+ }
+#endif
+}
+
+
+void CompareStatus::CompareStatusImpl::updateStatusPanelNow()
+{
+ //static RetrieveStatistics statistic;
+ //statistic.writeEntry(currentData, currentObjects);
+ {
+ //wxWindowUpdateLocker dummy(this) -> not needed
+
+ const float percent = totalData == 0 ? 0 : currentData.ToDouble() * 100 / totalData.ToDouble();
+
+ //write status information to taskbar, parent title ect.
+ switch (status)
+ {
+ case SCANNING:
+ showProgressExternally(numberToStringSep(scannedObjects) + wxT(" - ") + _("Scanning..."));
+ break;
+ case COMPARING_CONTENT:
+ showProgressExternally(formatPercentage(currentData, totalData) + wxT(" - ") + _("Comparing content..."), percent);
+ break;
+ }
+
+
+ bool updateLayout = false; //avoid screen flicker by calling layout() only if necessary
+
+ //remove linebreaks from currentStatusText
+ wxString formattedStatusText = zToWx(currentStatusText);
+ for (wxString::iterator i = formattedStatusText.begin(); i != formattedStatusText.end(); ++i)
+ if (*i == wxChar('\n'))
+ *i = wxChar(' ');
+
+ //status texts
+ if (m_textCtrlStatus->GetValue() != formattedStatusText) //no layout update for status texts!
+ m_textCtrlStatus->ChangeValue(formattedStatusText);
+
+ //nr of scanned objects
+ setNewText(numberToStringSep(scannedObjects), *m_staticTextScanned, updateLayout);
+
+ //progress indicator for "compare file content"
+ m_gauge2->SetValue(int(currentData.ToDouble() * scalingFactor));
+
+ //remaining files left for file comparison
+ const wxString filesToCompareTmp = numberToStringSep(totalObjects - currentObjects);
+ setNewText(filesToCompareTmp, *m_staticTextFilesRemaining, updateLayout);
+
+ //remaining bytes left for file comparison
+ const wxString remainingBytesTmp = ffs3::formatFilesizeToShortString(totalData - currentData);
+ setNewText(remainingBytesTmp, *m_staticTextDataRemaining, updateLayout);
+
+ if (statistics.get())
+ {
+ if (timeElapsed.Time() - lastStatCallSpeed >= 500) //call method every 500 ms
+ {
+ lastStatCallSpeed = timeElapsed.Time();
+
+ statistics->addMeasurement(currentObjects, currentData.ToDouble());
+
+ //current speed
+ setNewText(statistics->getBytesPerSecond(), *m_staticTextSpeed, updateLayout);
+
+ if (timeElapsed.Time() - lastStatCallRemTime >= 2000) //call method every two seconds only
+ {
+ lastStatCallRemTime = timeElapsed.Time();
+
+ //remaining time
+ setNewText(statistics->getRemainingTime(), *m_staticTextTimeRemaining, updateLayout);
+ }
+ }
+ }
+
+ //time elapsed
+ setNewText(wxTimeSpan::Milliseconds(timeElapsed.Time()).Format(), *m_staticTextTimeElapsed, updateLayout);
+
+ //do the ui update
+ if (updateLayout)
+ bSizer42->Layout();
+ }
+ updateUiNow();
+}
+//########################################################################################
+
+
+class SyncStatus::SyncStatusImpl : public SyncStatusDlgGenerated
+{
+public:
+ SyncStatusImpl(StatusHandler& updater, wxTopLevelWindow* parentWindow);
+ ~SyncStatusImpl();
+
+ void resetGauge(int totalObjectsToProcess, wxLongLong totalDataToProcess);
+ void incProgressIndicator_NoUpdate(int objectsProcessed, wxLongLong dataProcessed);
+ void incScannedObjects_NoUpdate(int number);
+ void setStatusText_NoUpdate(const Zstring& text);
+ void updateStatusDialogNow();
+
+ void setCurrentStatus(SyncStatus::SyncStatusID id);
+ void processHasFinished(SyncStatus::SyncStatusID id, const wxString& finalMessage); //essential to call this in StatusUpdater derived class destructor at the LATEST(!) to prevent access to currentStatusUpdater
+
+ void minimizeToTray();
+
+private:
+ void OnKeyPressed(wxKeyEvent& event);
+ virtual void OnOkay(wxCommandEvent& event);
+ virtual void OnPause(wxCommandEvent& event);
+ virtual void OnAbort(wxCommandEvent& event);
+ virtual void OnClose(wxCloseEvent& event);
+ virtual void OnIconize(wxIconizeEvent& event);
+
+ void resumeFromSystray();
+ bool currentProcessIsRunning();
+ void showProgressExternally(const wxString& progressText, float percent = 0); //percent may already be included in progressText
+
+ wxStopWatch timeElapsed;
+
+ StatusHandler* processStatusHandler;
+ wxTopLevelWindow* mainDialog;
+
+ //gauge variables
+ int totalObjects;
+ wxLongLong totalData;
+ int currentObjects; //each object represents a file or directory processed
+ wxLongLong currentData; //each data element represents one byte for proper progress indicator scaling
+ double scalingFactor; //nr of elements has to be normalized to smaller nr. because of range of int limitation
+
+ //status variables
+ size_t scannedObjects;
+ Zstring currentStatusText;
+
+ bool processPaused;
+ SyncStatus::SyncStatusID currentStatus;
+
+#ifdef FFS_WIN
+ std::auto_ptr<util::TaskbarProgress> taskbar_;
+#endif
+
+ //remaining time
+ std::auto_ptr<Statistics> statistics;
+ long lastStatCallSpeed; //used for calculating intervals between statistics update
+ long lastStatCallRemTime; //
+
+ wxString titelTextBackup;
+
+ boost::shared_ptr<MinimizeToTray> minimizedToSysTray; //optional: if filled, hides all visible windows, shows again if destroyed
+};
+
+
+//redirect to implementation
+SyncStatus::SyncStatus(StatusHandler& updater, wxTopLevelWindow* parentWindow, bool startSilent) :
+ pimpl(new SyncStatusImpl(updater, parentWindow))
+{
+ if (startSilent)
+ pimpl->minimizeToTray();
+ else
+ {
+ pimpl->Show();
+ pimpl->updateStatusDialogNow(); //update visual statistics to get rid of "dummy" texts
+ }
+}
+
+SyncStatus::~SyncStatus()
+{
+ //DON'T delete pimpl! it will be deleted by the user clicking "OK/Cancel" -> (wxWindow::Destroy())
+}
+
+wxWindow* SyncStatus::getAsWindow()
+{
+ return pimpl;
+}
+
+void SyncStatus::closeWindowDirectly() //don't wait for user (silent mode)
+{
+ pimpl->Destroy();
+}
+
+void SyncStatus::resetGauge(int totalObjectsToProcess, wxLongLong totalDataToProcess)
+{
+ pimpl->resetGauge(totalObjectsToProcess, totalDataToProcess);
+}
+
+void SyncStatus::incScannedObjects_NoUpdate(int number)
+{
+ pimpl->incScannedObjects_NoUpdate(number);
+}
+
+void SyncStatus::incProgressIndicator_NoUpdate(int objectsProcessed, wxLongLong dataProcessed)
+{
+ pimpl->incProgressIndicator_NoUpdate(objectsProcessed, dataProcessed);
+}
+
+void SyncStatus::setStatusText_NoUpdate(const Zstring& text)
+{
+ pimpl->setStatusText_NoUpdate(text);
+}
+
+void SyncStatus::updateStatusDialogNow()
+{
+ pimpl->updateStatusDialogNow();
+}
+
+void SyncStatus::setCurrentStatus(SyncStatusID id)
+{
+ pimpl->setCurrentStatus(id);
+}
+
+void SyncStatus::processHasFinished(SyncStatusID id, const wxString& finalMessage)
+{
+ pimpl->processHasFinished(id, finalMessage);
+}
+//########################################################################################
+
+
+SyncStatus::SyncStatusImpl::SyncStatusImpl(StatusHandler& updater, wxTopLevelWindow* parentWindow) :
+ SyncStatusDlgGenerated(parentWindow,
+ wxID_ANY,
+ parentWindow ? wxString(wxEmptyString) : (wxString(wxT("FreeFileSync - ")) + _("Folder Comparison and Synchronization")),
+ wxDefaultPosition, wxSize(638, 376),
+ parentWindow ?
+ wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL | wxFRAME_NO_TASKBAR | wxFRAME_FLOAT_ON_PARENT : //wxTAB_TRAVERSAL is needed for standard button handling: wxID_OK/wxID_CANCEL
+ wxDEFAULT_FRAME_STYLE | wxTAB_TRAVERSAL),
+ processStatusHandler(&updater),
+ mainDialog(parentWindow),
+ totalObjects(0),
+ totalData(0),
+ currentObjects(0),
+ currentData(0),
+ scalingFactor(0),
+ scannedObjects(0),
+ processPaused(false),
+ currentStatus(SyncStatus::ABORTED),
+ lastStatCallSpeed(-1000000), //some big number
+ lastStatCallRemTime(-1000000)
+{
+ if (mainDialog) //save old title (will be used as progress indicator)
+ titelTextBackup = mainDialog->GetTitle();
+
+ m_animationControl1->SetAnimation(*GlobalResources::getInstance().animationSync);
+ m_animationControl1->Play();
+
+ m_staticTextSpeed->SetLabel(wxT("-"));
+ m_staticTextTimeRemaining->SetLabel(wxT("-"));
+
+ //initialize gauge
+ m_gauge1->SetRange(50000);
+ m_gauge1->SetValue(0);
+
+ if (IsShown()) //don't steal focus when starting in sys-tray!
+ m_buttonAbort->SetFocus();
+
+ if (mainDialog) //disable (main) window while this status dialog is shown
+ mainDialog->Disable();
+
+ timeElapsed.Start(); //measure total time
+
+#ifdef FFS_WIN
+ try //try to get access to Windows 7 Taskbar
+ {
+ taskbar_.reset(new util::TaskbarProgress(mainDialog != NULL ? *mainDialog : *this));
+ }
+ catch (const util::TaskbarNotAvailable&) {}
+#endif
+
+ //hide "processed" statistics until end of process
+ bSizerObjectsProcessed->Show(false);
+ //bSizerDataProcessed->Show(false);
+
+ SetIcon(*GlobalResources::getInstance().programIcon); //set application icon
+
+ //register key event
+ Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncStatusImpl::OnKeyPressed), NULL, this);
+}
+
+
+SyncStatus::SyncStatusImpl::~SyncStatusImpl()
+{
+ if (mainDialog)
+ {
+ //restore title text
+ mainDialog->SetTitle(titelTextBackup);
+
+ mainDialog->Enable();
+ mainDialog->Raise();
+ mainDialog->SetFocus();
+ }
+
+ if (minimizedToSysTray.get())
+ minimizedToSysTray->keepHidden(); //prevent window from flashing shortly before it is destroyed
+}
+
+
+void SyncStatus::SyncStatusImpl::OnKeyPressed(wxKeyEvent& event)
+{
+ const int keyCode = event.GetKeyCode();
+ if (keyCode == WXK_ESCAPE)
+ Close(); //generate close event: do NOT destroy window unconditionally!
+
+ event.Skip();
+}
+
+
+void SyncStatus::SyncStatusImpl::resetGauge(int totalObjectsToProcess, wxLongLong totalDataToProcess)
+{
+ currentData = 0;
+ totalData = totalDataToProcess;
+
+ currentObjects = 0;
+ totalObjects = totalObjectsToProcess;
+
+ if (totalData != 0)
+ scalingFactor = 50000 / totalData.ToDouble(); //let's normalize to 50000
+ else
+ scalingFactor = 0;
+
+ //set new statistics handler: 10 seconds "window" for remaining time, 5 seconds for speed
+ statistics.reset(new Statistics(totalObjectsToProcess, totalDataToProcess.ToDouble(), 10000, 5000));
+ lastStatCallSpeed = -1000000; //some big number
+ lastStatCallRemTime = -1000000;
+}
+
+
+void SyncStatus::SyncStatusImpl::incProgressIndicator_NoUpdate(int objectsProcessed, wxLongLong dataProcessed)
+{
+ currentData += dataProcessed;
+ currentObjects += objectsProcessed;
+}
+
+
+void SyncStatus::SyncStatusImpl::incScannedObjects_NoUpdate(int number)
+{
+ scannedObjects += number;
+}
+
+
+void SyncStatus::SyncStatusImpl::setStatusText_NoUpdate(const Zstring& text)
+{
+ currentStatusText = text;
+}
+
+
+void SyncStatus::SyncStatusImpl::showProgressExternally(const wxString& progressText, float percent)
+{
+ //write status information to systray, if window is minimized
+ if (minimizedToSysTray.get())
+ minimizedToSysTray->setToolTip(progressText, percent);
+ //minimizedToSysTray may be a zombie... so set title text anyway
+
+ if (mainDialog) //show percentage in maindialog title (and thereby in taskbar)
+ {
+ if (mainDialog->GetTitle() != progressText)
+ mainDialog->SetTitle(progressText);
+ }
+ else //show percentage in this dialog's title (and thereby in taskbar)
+ {
+ if (this->GetTitle() != progressText)
+ this->SetTitle(progressText);
+ }
+
+#ifdef FFS_WIN
+ using namespace util;
+
+ //show progress on Windows 7 taskbar
+ if (taskbar_.get())
+ {
+ const size_t current = 100000 * percent / 100;
+ const size_t total = 100000;
+
+ switch (currentStatus)
+ {
+ case SyncStatus::SCANNING:
+ taskbar_->setStatus(TaskbarProgress::STATUS_INDETERMINATE);
+ break;
+ case SyncStatus::FINISHED_WITH_SUCCESS:
+ case SyncStatus::COMPARING_CONTENT:
+ case SyncStatus::SYNCHRONIZING:
+ taskbar_->setStatus(TaskbarProgress::STATUS_NORMAL);
+ taskbar_->setProgress(current, total);
+ break;
+ case SyncStatus::PAUSE:
+ taskbar_->setStatus(TaskbarProgress::STATUS_PAUSED);
+ taskbar_->setProgress(current, total);
+ break;
+ case SyncStatus::ABORTED:
+ case SyncStatus::FINISHED_WITH_ERROR:
+ taskbar_->setStatus(TaskbarProgress::STATUS_ERROR);
+ taskbar_->setProgress(current, total);
+ break;
+ }
+ }
+#endif
+}
+
+
+void SyncStatus::SyncStatusImpl::updateStatusDialogNow()
+{
+ //static RetrieveStatistics statistic;
+ //statistic.writeEntry(currentData, currentObjects);
+
+ const float percent = totalData == 0 ? 0 : currentData.ToDouble() * 100 / totalData.ToDouble();
+
+ //write status information to systray, taskbar, parent title ect.
+ switch (currentStatus)
+ {
+ case SyncStatus::SCANNING:
+ showProgressExternally(numberToStringSep(scannedObjects) + wxT(" - ") + _("Scanning..."));
+ break;
+ case SyncStatus::COMPARING_CONTENT:
+ showProgressExternally(formatPercentage(currentData, totalData) + wxT(" - ") + _("Comparing content..."), percent);
+ break;
+ case SyncStatus::SYNCHRONIZING:
+ showProgressExternally(formatPercentage(currentData, totalData) + wxT(" - ") + _("Synchronizing..."), percent);
+ break;
+ case SyncStatus::PAUSE:
+ showProgressExternally((totalData != 0 ? formatPercentage(currentData, totalData) + wxT(" - ") : wxString()) + _("Paused"), percent);
+ break;
+ case SyncStatus::ABORTED:
+ showProgressExternally(_("Aborted"), percent);
+ break;
+ case SyncStatus::FINISHED_WITH_SUCCESS:
+ case SyncStatus::FINISHED_WITH_ERROR:
+ showProgressExternally(_("Completed"), percent);
+ break;
+ }
+
+ //write regular status information (whether dialog is visible or not)
+ {
+ //wxWindowUpdateLocker dummy(this); -> not needed
+
+ bool updateLayout = false; //avoid screen flicker by calling layout() only if necessary
+
+ //progress indicator
+ if (currentStatus == SyncStatus::SCANNING)
+ m_gauge1->Pulse();
+ else
+ m_gauge1->SetValue(common::round(currentData.ToDouble() * scalingFactor));
+
+ //status text
+ const wxString statusTxt = zToWx(currentStatusText);
+ if (m_textCtrlInfo->GetValue() != statusTxt) //no layout update for status texts!
+ m_textCtrlInfo->ChangeValue(statusTxt);
+
+ //remaining objects
+ const wxString remainingObjTmp = numberToStringSep(totalObjects - currentObjects);
+ setNewText(remainingObjTmp, *m_staticTextRemainingObj, updateLayout);
+
+ //remaining bytes left for copy
+ const wxString remainingBytesTmp = ffs3::formatFilesizeToShortString(totalData - currentData);
+ setNewText(remainingBytesTmp, *m_staticTextDataRemaining, updateLayout);
+
+ if (statistics.get())
+ {
+ if (timeElapsed.Time() - lastStatCallSpeed >= 500) //call method every 500 ms
+ {
+ lastStatCallSpeed = timeElapsed.Time();
+
+ statistics->addMeasurement(currentObjects, currentData.ToDouble());
+
+ //current speed
+ setNewText(statistics->getBytesPerSecond(), *m_staticTextSpeed, updateLayout);
+
+ if (timeElapsed.Time() - lastStatCallRemTime >= 2000) //call method every two seconds only
+ {
+ lastStatCallRemTime = timeElapsed.Time();
+
+ //remaining time
+ setNewText(statistics->getRemainingTime(), *m_staticTextTimeRemaining, updateLayout);
+ }
+ }
+ }
+
+ //time elapsed
+ setNewText(wxTimeSpan::Milliseconds(timeElapsed.Time()).Format(), *m_staticTextTimeElapsed, updateLayout);
+
+ //do the ui update
+ if (updateLayout)
+ {
+ bSizer28->Layout();
+ bSizer31->Layout();
+ }
+ }
+
+ //support for pause button
+ while (processPaused && currentProcessIsRunning())
+ {
+ wxMilliSleep(UI_UPDATE_INTERVAL);
+ updateUiNow();
+ }
+
+ updateUiNow();
+}
+
+
+bool SyncStatus::SyncStatusImpl::currentProcessIsRunning()
+{
+ return processStatusHandler != NULL;
+}
+
+
+void SyncStatus::SyncStatusImpl::setCurrentStatus(SyncStatus::SyncStatusID id)
+{
+ switch (id)
+ {
+ case SyncStatus::ABORTED:
+ m_bitmapStatus->SetBitmap(GlobalResources::getInstance().getImageByName(wxT("statusError")));
+ m_staticTextStatus->SetLabel(_("Aborted"));
+ break;
+
+ case SyncStatus::FINISHED_WITH_SUCCESS:
+ m_bitmapStatus->SetBitmap(GlobalResources::getInstance().getImageByName(wxT("statusSuccess")));
+ m_staticTextStatus->SetLabel(_("Completed"));
+ break;
+
+ case SyncStatus::FINISHED_WITH_ERROR:
+ m_bitmapStatus->SetBitmap(GlobalResources::getInstance().getImageByName(wxT("statusWarning")));
+ m_staticTextStatus->SetLabel(_("Completed"));
+ break;
+
+ case SyncStatus::PAUSE:
+ m_bitmapStatus->SetBitmap(GlobalResources::getInstance().getImageByName(wxT("statusPause")));
+ m_staticTextStatus->SetLabel(_("Paused"));
+ break;
+
+ case SyncStatus::SCANNING:
+ m_bitmapStatus->SetBitmap(GlobalResources::getInstance().getImageByName(wxT("statusScanning")));
+ m_staticTextStatus->SetLabel(_("Scanning..."));
+ break;
+
+ case SyncStatus::COMPARING_CONTENT:
+ m_bitmapStatus->SetBitmap(GlobalResources::getInstance().getImageByName(wxT("statusBinaryCompare")));
+ m_staticTextStatus->SetLabel(_("Comparing content..."));
+ break;
+
+ case SyncStatus::SYNCHRONIZING:
+ m_bitmapStatus->SetBitmap(GlobalResources::getInstance().getImageByName(wxT("statusSyncing")));
+ m_staticTextStatus->SetLabel(_("Synchronizing..."));
+ break;
+ }
+
+ currentStatus = id;
+ Layout();
+}
+
+
+void SyncStatus::SyncStatusImpl::processHasFinished(SyncStatus::SyncStatusID id, const wxString& finalMessage) //essential to call this in StatusHandler derived class destructor
+{
+ //at the LATEST(!) to prevent access to currentStatusHandler
+ //enable okay and close events; may be set in this method ONLY
+
+ processStatusHandler = NULL; //avoid callback to (maybe) deleted parent process
+
+ setCurrentStatus(id);
+
+ resumeFromSystray(); //if in tray mode...
+
+ m_buttonAbort->Disable();
+ m_buttonAbort->Hide();
+ m_buttonPause->Disable();
+ m_buttonPause->Hide();
+ m_buttonOK->Show();
+ m_buttonOK->Enable();
+
+ if (IsShown()) //don't steal focus when residing in sys-tray!
+ m_buttonOK->SetFocus();
+
+ m_animationControl1->Stop();
+ m_animationControl1->Hide();
+
+ //hide speed and remaining time
+ bSizerSpeed ->Show(false);
+ bSizerRemTime->Show(false);
+
+ //if everything was processed successfully, hide remaining statistics (is 0 anyway)
+ if ( totalObjects == currentObjects &&
+ totalData == currentData)
+ {
+ bSizerObjectsRemaining->Show(false);
+
+ bSizerObjectsProcessed->Show(true);
+
+ m_staticTextProcessedObj->SetLabel(numberToStringSep(currentObjects));
+ m_staticTextDataProcessed->SetLabel(ffs3::formatFilesizeToShortString(currentData));
+ }
+
+ updateStatusDialogNow(); //keep this sequence to avoid display distortion, if e.g. only 1 item is sync'ed
+ m_textCtrlInfo->SetValue(finalMessage); //
+ Layout(); //
+
+ //Raise(); -> don't! user may be watching a movie in the meantime ;)
+}
+
+
+void SyncStatus::SyncStatusImpl::OnOkay(wxCommandEvent& event)
+{
+ Close(); //generate close event: do NOT destroy window unconditionally!
+}
+
+
+void SyncStatus::SyncStatusImpl::OnAbort(wxCommandEvent& event)
+{
+ Close(); //generate close event: do NOT destroy window unconditionally!
+}
+
+
+void SyncStatus::SyncStatusImpl::OnPause(wxCommandEvent& event)
+{
+ static SyncStatus::SyncStatusID previousStatus = SyncStatus::ABORTED;
+
+ if (processPaused)
+ {
+ setCurrentStatus(previousStatus);
+ processPaused = false;
+ m_buttonPause->SetLabel(_("Pause"));
+ m_animationControl1->Play();
+
+ //resume timers
+ timeElapsed.Resume();
+ if (statistics.get())
+ statistics->resumeTimer();
+ }
+ else
+ {
+ previousStatus = currentStatus; //save current status
+
+ setCurrentStatus(SyncStatus::PAUSE);
+ processPaused = true;
+ m_buttonPause->SetLabel(_("Continue"));
+ m_animationControl1->Stop();
+
+ //pause timers
+ timeElapsed.Pause();
+ if (statistics.get())
+ statistics->pauseTimer();
+ }
+}
+
+
+void SyncStatus::SyncStatusImpl::OnClose(wxCloseEvent& event)
+{
+ processPaused = false;
+ if (currentProcessIsRunning())
+ {
+ m_buttonAbort->Disable();
+ m_buttonAbort->Hide();
+ m_buttonPause->Disable();
+ m_buttonPause->Hide();
+
+ setStatusText_NoUpdate(wxToZ(_("Abort requested: Waiting for current operation to finish...")));
+ //no Layout() or UI-update here to avoid cascaded Yield()-call
+
+ processStatusHandler->requestAbortion();
+ }
+ else
+ Destroy();
+}
+
+
+void SyncStatus::SyncStatusImpl::OnIconize(wxIconizeEvent& event)
+{
+ if (event.IsIconized()) //ATTENTION: iconize event is also triggered on "Restore"! (at least under Linux)
+ minimizeToTray();
+}
+
+
+void SyncStatus::SyncStatusImpl::minimizeToTray()
+{
+ minimizedToSysTray.reset(new MinimizeToTray(this, mainDialog));
+}
+
+
+void SyncStatus::SyncStatusImpl::resumeFromSystray()
+{
+ minimizedToSysTray.reset();
+}
bgstack15