diff options
Diffstat (limited to 'ui/progress_indicator.cpp')
-rw-r--r-- | ui/progress_indicator.cpp | 277 |
1 files changed, 215 insertions, 62 deletions
diff --git a/ui/progress_indicator.cpp b/ui/progress_indicator.cpp index 7da143a1..4102db9d 100644 --- a/ui/progress_indicator.cpp +++ b/ui/progress_indicator.cpp @@ -8,19 +8,20 @@ #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 "../lib/resources.h" +#include <wx+/string_conv.h> +#include <wx+/format_unit.h> +#include "../lib/statistics.h" #include <wx/wupdlock.h> -#include "../shared/global_func.h" +#include <zen/basic_math.h> #include "tray_icon.h" #include <memory> -#include "../shared/mouse_move_dlg.h" -#include "../library/error_log.h" -#include "../shared/toggle_button.h" -#include "../shared/taskbar.h" -#include "../shared/image_tools.h" +#include <wx+/mouse_move_dlg.h> +#include "../lib/error_log.h" +#include <wx+/toggle_button.h> +#include "taskbar.h" +#include <wx+/image_tools.h> +#include <wx+/graph.h> using namespace zen; @@ -94,10 +95,11 @@ private: CurrentStatus status; - std::unique_ptr<util::Taskbar> taskbar_; + std::unique_ptr<Taskbar> taskbar_; //remaining time std::unique_ptr<Statistics> statistics; + long lastStatCallSpeed; //used for calculating intervals between statistics update long lastStatCallRemTime; // }; @@ -175,9 +177,9 @@ void CompareStatus::CompareStatusImpl::init() try //try to get access to Windows 7/Ubuntu taskbar { - taskbar_.reset(new util::Taskbar(parentWindow_)); + taskbar_.reset(new Taskbar(parentWindow_)); } - catch (const util::TaskbarNotAvailable&) {} + catch (const TaskbarNotAvailable&) {} status = SCANNING; @@ -271,7 +273,6 @@ void CompareStatus::CompareStatusImpl::showProgressExternally(const wxString& pr parentWindow_.SetTitle(progressText); //show progress on Windows 7 taskbar - using namespace util; if (taskbar_.get()) switch (status) @@ -304,32 +305,32 @@ void CompareStatus::CompareStatusImpl::updateStatusPanelNow() showProgressExternally(toStringSep(scannedObjects) + wxT(" - ") + _("Scanning...")); break; case COMPARING_CONTENT: - showProgressExternally(formatPercentage(fraction) + wxT(" - ") + _("Comparing content..."), fraction); + showProgressExternally(percentageToShortString(fraction) + wxT(" - ") + _("Comparing content..."), fraction); break; } bool updateLayout = false; //avoid screen flicker by calling layout() only if necessary //remove linebreaks from currentStatusText - wxString formattedStatusText = currentStatusText; - replace(formattedStatusText, L'\n', L' '); + wxString statusTextFmt = currentStatusText; + replace(statusTextFmt, L'\n', L' '); //status texts - if (m_textCtrlStatus->GetValue() != formattedStatusText) //no layout update for status texts! - m_textCtrlStatus->ChangeValue(formattedStatusText); + if (m_textCtrlStatus->GetValue() != statusTextFmt) //no layout update for status texts! + m_textCtrlStatus->ChangeValue(statusTextFmt); //nr of scanned objects setNewText(toStringSep(scannedObjects), *m_staticTextScanned, updateLayout); //progress indicator for "compare file content" - m_gauge2->SetValue(common::round(fraction * GAUGE_FULL_RANGE)); + m_gauge2->SetValue(numeric::round(fraction * GAUGE_FULL_RANGE)); //remaining files left for file comparison const wxString filesToCompareTmp = toStringSep(totalObjects - currentObjects); setNewText(filesToCompareTmp, *m_staticTextFilesRemaining, updateLayout); //remaining bytes left for file comparison - const wxString remainingBytesTmp = zen::formatFilesizeToShortString(to<zen::UInt64>(totalData - currentData)); + const wxString remainingBytesTmp = zen::filesizeToShortString(to<zen::UInt64>(totalData - currentData)); setNewText(remainingBytesTmp, *m_staticTextDataRemaining, updateLayout); if (statistics.get()) @@ -348,7 +349,7 @@ void CompareStatus::CompareStatusImpl::updateStatusPanelNow() lastStatCallRemTime = timeElapsed.Time(); //remaining time - setNewText(statistics->getRemainingTime(), *m_staticTextTimeRemaining, updateLayout); + setNewText(statistics->getRemainingTime(), *m_staticTextRemTime, updateLayout); } } } @@ -479,6 +480,110 @@ private: //######################################################################################## +namespace +{ +class GraphDataBytes : public GraphData +{ +public: + void addCurrentValue(double dataCurrent) + { + data.insert(data.end(), std::make_pair(timer.Time(), dataCurrent)); + //documentation differs about whether "hint" should be before or after the to be inserted element! + //however "std::map<>::end()" is interpreted correctly by GCC and VS2010 + + if (data.size() > MAX_BUFFER_SIZE) //guard against too large a buffer + data.erase(data.begin()); + } + + void pauseTimer () { timer.Pause(); } + void resumeTimer() { timer.Resume(); } + +private: + static const size_t MAX_BUFFER_SIZE = 2500000; //sizeof(single node) worst case ~ 3 * 8 byte ptr + 16 byte key/value = 40 byte + + virtual double getValue(double x) const //x: seconds since begin + { + auto iter = data.lower_bound(x * 1000); + if (iter == data.end()) + return data.empty() ? 0 : (--data.end())->second; + return iter->second; + } + virtual double getXBegin() const { return 0; } //{ return data.empty() ? 0 : data.begin()->first / 1000.0; } + virtual double getXEnd () const { return data.empty() ? 0 : (--data.end())->first / 1000.0; } + //example: two-element range is accessible within [0, 2) + + wxStopWatch timer; + std::map<long, double> data; +}; + + +struct LabelFormatterBytes : public LabelFormatter +{ + virtual double getOptimalBlockSize(double bytesProposed) const + { + //round to next number which is a convenient to read block size + if (bytesProposed <= 0) + return 0; + + const double k = std::floor(std::log(bytesProposed) / std::log(2.0)); + const double e = std::pow(2.0, k); + const double a = bytesProposed / e; //bytesProposed = a * 2^k with a in (1, 2) + return (a < 1.5 ? 1 : 2) * e; + } + + virtual wxString formatText(double value, double optimalBlockSize) const { return filesizeToShortString(UInt64(value)); }; +}; + + +inline +double bestFit(double val, double low, double high) { return val < (high + low) / 2 ? low : high; } + + +struct LabelFormatterTimeElapsed : public LabelFormatter +{ + virtual double getOptimalBlockSize(double secProposed) const + { + if (secProposed <= 5) + return 5; //minimum block size + + //for seconds and minutes: nice numbers are 1, 5, 10, 15, 20, 30 + auto calcBlock = [](double val) -> double + { + if (val <= 5) + return bestFit(val, 1, 5); // + if (val <= 10) + return bestFit(val, 5, 10); // a good candidate for a variadic template! + if (val <= 15) + return bestFit(val, 10, 15); // + if (val <= 20) + return bestFit(val, 15, 20); + if (val <= 30) + return bestFit(val, 20, 30); + return bestFit(val, 30, 60); + }; + + if (secProposed <= 60) + return calcBlock(secProposed); + else if (secProposed <= 3600) + return calcBlock(secProposed / 60) * 60; + else if (secProposed <= 3600 * 24) + return nextNiceNumber(secProposed / 3600) * 3600; + else + return nextNiceNumber(secProposed / (24 * 3600)) * 24 * 3600; //round up to full days + } + + virtual wxString formatText(double timeElapsed, double optimalBlockSize) const + { + return timeElapsed < 60 ? + remainingTimeToShortString(timeElapsed) : + timeElapsed < 3600 ? + wxTimeSpan::Seconds(timeElapsed).Format( L"%M:%S") : + wxTimeSpan::Seconds(timeElapsed).Format(L"%H:%M:%S"); + } +}; +} + + class SyncStatus::SyncStatusImpl : public SyncStatusDlgGenerated { public: @@ -529,13 +634,15 @@ private: bool processPaused; SyncStatus::SyncStatusID currentStatus; - std::unique_ptr<util::Taskbar> taskbar_; + std::unique_ptr<Taskbar> taskbar_; //remaining time std::unique_ptr<Statistics> statistics; long lastStatCallSpeed; //used for calculating intervals between statistics update long lastStatCallRemTime; // + std::shared_ptr<GraphDataBytes> graphDataBytes; + wxString titelTextBackup; std::unique_ptr<FfsTrayIcon> trayIcon; //optional: if filled all other windows should be hidden and conversely @@ -610,6 +717,19 @@ void SyncStatus::processHasFinished(SyncStatusID id, const ErrorLogging& log) } //######################################################################################## +namespace +{ +void fitHeight(wxTopLevelWindow& wnd) +{ + if (wnd.IsMaximized()) + return; + //Fit() height only: + int width = wnd.GetSize().GetWidth(); + wnd.Fit(); + int height = wnd.GetSize().GetHeight(); + wnd.SetSize(wxSize(width, height)); +} +} SyncStatus::SyncStatusImpl::SyncStatusImpl(AbortCallback& abortCb, MainDialog* parentWindow, @@ -618,7 +738,7 @@ SyncStatus::SyncStatusImpl::SyncStatusImpl(AbortCallback& abortCb, SyncStatusDlgGenerated(parentWindow, wxID_ANY, parentWindow ? wxString(wxEmptyString) : (wxString(wxT("FreeFileSync - ")) + _("Folder Comparison and Synchronization")), - wxDefaultPosition, wxSize(638, 350), + wxDefaultPosition, wxSize(620, 330), 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), @@ -642,11 +762,11 @@ SyncStatus::SyncStatusImpl::SyncStatusImpl(AbortCallback& abortCb, if (mainDialog) //save old title (will be used as progress indicator) titelTextBackup = mainDialog->GetTitle(); - m_animationControl1->SetAnimation(*GlobalResources::instance().animationSync); + m_animationControl1->SetAnimation(GlobalResources::instance().animationSync); m_animationControl1->Play(); m_staticTextSpeed->SetLabel(wxT("-")); - m_staticTextTimeRemaining->SetLabel(wxT("-")); + m_staticTextRemTime->SetLabel(wxT("-")); //initialize gauge m_gauge1->SetRange(GAUGE_FULL_RANGE); @@ -665,20 +785,29 @@ SyncStatus::SyncStatusImpl::SyncStatusImpl(AbortCallback& abortCb, try //try to get access to Windows 7/Ubuntu taskbar { - taskbar_.reset(new util::Taskbar(mainDialog != NULL ? *static_cast<wxTopLevelWindow*>(mainDialog) : *this)); + taskbar_.reset(new Taskbar(mainDialog != NULL ? *static_cast<wxTopLevelWindow*>(mainDialog) : *this)); } - catch (const util::TaskbarNotAvailable&) {} + catch (const TaskbarNotAvailable&) {} //hide "processed" statistics until end of process - bSizerObjectsProcessed->Show(false); - //bSizerDataProcessed->Show(false); + bSizerFinalStat->Show(false); - SetIcon(*GlobalResources::instance().programIcon); //set application icon + SetIcon(GlobalResources::instance().programIcon); //set application icon //register key event Connect(wxEVT_CHAR_HOOK, wxKeyEventHandler(SyncStatusImpl::OnKeyPressed), NULL, this); setCurrentStatus(startStatus); //first state: will be shown while waiting for dir locks (if at all) + + //init graph + graphDataBytes = std::make_shared<GraphDataBytes>(); + m_panelGraph->setAttributes(Graph2D::GraphAttributes(). + setLabelX(Graph2D::X_LABEL_BOTTOM, 20, std::make_shared<LabelFormatterTimeElapsed>()). + setLabelY(Graph2D::Y_LABEL_RIGHT, 60, std::make_shared<LabelFormatterBytes>())); + m_panelGraph->setData(graphDataBytes, Graph2D::LineAttributes().setLineWidth(2).setColor(wxColor(0, 192, 0))); //medium green + + //Fit() height only: + //fitHeight(*this); } @@ -747,6 +876,9 @@ void SyncStatus::SyncStatusImpl::incProgressIndicator_NoUpdate(int objectsProces currentData += dataProcessed; currentObjects += objectsProcessed; + + //update graph data + graphDataBytes->addCurrentValue(to<double>(currentData)); } @@ -782,7 +914,6 @@ void SyncStatus::SyncStatusImpl::showProgressExternally(const wxString& progress this->SetTitle(progressTextFmt); } - using namespace util; //show progress on Windows 7 taskbar if (taskbar_.get()) @@ -811,6 +942,7 @@ void SyncStatus::SyncStatusImpl::showProgressExternally(const wxString& progress } } + #ifdef FFS_WIN namespace { @@ -818,7 +950,7 @@ enum Zorder { ZORDER_CORRECT, ZORDER_WRONG, - ZORDER_INDEFIINTE, + ZORDER_INDEFINITE, }; Zorder validateZorder(const wxWindow& top, const wxWindow& bottom) @@ -835,11 +967,12 @@ Zorder validateZorder(const wxWindow& top, const wxWindow& bottom) if (hAbove == hBottom) return ZORDER_WRONG; - return ZORDER_INDEFIINTE; + return ZORDER_INDEFINITE; } } #endif + void SyncStatus::SyncStatusImpl::updateStatusDialogNow(bool allowYield) { //static RetrieveStatistics statistic; @@ -857,13 +990,13 @@ void SyncStatus::SyncStatusImpl::updateStatusDialogNow(bool allowYield) showProgressExternally(toStringSep(scannedObjects) + wxT(" - ") + _("Scanning...") + postFix); break; case SyncStatus::COMPARING_CONTENT: - showProgressExternally(formatPercentage(fraction) + wxT(" - ") + _("Comparing content...") + postFix, fraction); + showProgressExternally(percentageToShortString(fraction) + wxT(" - ") + _("Comparing content...") + postFix, fraction); break; case SyncStatus::SYNCHRONIZING: - showProgressExternally(formatPercentage(fraction) + wxT(" - ") + _("Synchronizing...") + postFix, fraction); + showProgressExternally(percentageToShortString(fraction) + wxT(" - ") + _("Synchronizing...") + postFix, fraction); break; case SyncStatus::PAUSE: - showProgressExternally(formatPercentage(fraction) + wxT(" - ") + _("Paused") + postFix, fraction); + showProgressExternally(percentageToShortString(fraction) + wxT(" - ") + _("Paused") + postFix, fraction); break; case SyncStatus::ABORTED: showProgressExternally(_("Aborted") + postFix, fraction); @@ -891,24 +1024,29 @@ void SyncStatus::SyncStatusImpl::updateStatusDialogNow(bool allowYield) case SyncStatus::FINISHED_WITH_SUCCESS: case SyncStatus::FINISHED_WITH_ERROR: case SyncStatus::ABORTED: - m_gauge1->SetValue(common::round(fraction * GAUGE_FULL_RANGE)); + m_gauge1->SetValue(numeric::round(fraction * GAUGE_FULL_RANGE)); break; case SyncStatus::PAUSE: //no change to gauge: don't switch between indeterminate/determinate modus break; } + + wxString statusTextFmt = currentStatusText; //remove linebreaks + replace(statusTextFmt, L'\n', L' '); // + //status text - if (m_textCtrlInfo->GetValue() != currentStatusText) //no layout update for status texts! - m_textCtrlInfo->ChangeValue(currentStatusText); + if (m_textCtrlInfo->GetValue() != statusTextFmt) //no layout update for status texts! + m_textCtrlInfo->ChangeValue(statusTextFmt); //remaining objects const wxString remainingObjTmp = toStringSep(totalObjects - currentObjects); setNewText(remainingObjTmp, *m_staticTextRemainingObj, updateLayout); //remaining bytes left for copy - const wxString remainingBytesTmp = zen::formatFilesizeToShortString(to<zen::UInt64>(totalData - currentData)); + const wxString remainingBytesTmp = zen::filesizeToShortString(to<zen::UInt64>(totalData - currentData)); setNewText(remainingBytesTmp, *m_staticTextDataRemaining, updateLayout); + //statistics if (statistics.get()) { if (timeElapsed.Time() - lastStatCallSpeed >= 500) //call method every 500 ms @@ -925,19 +1063,24 @@ void SyncStatus::SyncStatusImpl::updateStatusDialogNow(bool allowYield) lastStatCallRemTime = timeElapsed.Time(); //remaining time - setNewText(statistics->getRemainingTime(), *m_staticTextTimeRemaining, updateLayout); + setNewText(statistics->getRemainingTime(), *m_staticTextRemTime, updateLayout); } } } + m_panelGraph->Refresh(); + //time elapsed - setNewText(wxTimeSpan::Milliseconds(timeElapsed.Time()).Format(), *m_staticTextTimeElapsed, updateLayout); + const int timeElapSec = timeElapsed.Time() / 1000; + setNewText(timeElapSec < 3600 ? + wxTimeSpan::Seconds(timeElapSec).Format( L"%M:%S") : + wxTimeSpan::Seconds(timeElapSec).Format(L"%H:%M:%S"), *m_staticTextTimeElapsed, updateLayout); //do the ui update if (updateLayout) { - bSizer28->Layout(); - bSizer31->Layout(); + bSizerProgressStat->Layout(); + bSizerProgressMain->Layout(); } } @@ -962,6 +1105,7 @@ void SyncStatus::SyncStatusImpl::updateStatusDialogNow(bool allowYield) if (processPaused) { if (statistics.get()) statistics->pauseTimer(); + graphDataBytes->pauseTimer(); while (processPaused && currentProcessIsRunning()) { @@ -970,6 +1114,7 @@ void SyncStatus::SyncStatusImpl::updateStatusDialogNow(bool allowYield) } if (statistics.get()) statistics->resumeTimer(); + graphDataBytes->resumeTimer(); } /* @@ -1059,30 +1204,38 @@ void SyncStatus::SyncStatusImpl::processHasFinished(SyncStatus::SyncStatusID id, m_animationControl1->Stop(); m_animationControl1->Hide(); - //hide speed and remaining time - bSizerSpeed ->Show(false); - bSizerRemTime->Show(false); + //hide current operation status + bSizerCurrentOperation->Show(false); - //if everything was processed successfully, hide remaining statistics (is 0 anyway) - if (totalObjects == currentObjects && - totalData == currentData) - { - bSizerObjectsRemaining->Show(false); + //hide progress statistics + bSizerProgressMain->Show(false); - bSizerObjectsProcessed->Show(true); + //show and prepare final statistics + bSizerFinalStat->Show(true); + m_staticTextProcessedObj->SetLabel(toStringSep(currentObjects)); + m_staticTextDataProcessed->SetLabel(zen::filesizeToShortString(to<zen::UInt64>(currentData))); - m_staticTextProcessedObj->SetLabel(toStringSep(currentObjects)); - m_staticTextDataProcessed->SetLabel(zen::formatFilesizeToShortString(to<zen::UInt64>(currentData))); - } + m_staticTextTimeTotal->SetLabel(m_staticTextTimeElapsed->GetLabel()); + + // if (totalObjects == currentObjects && ->if everything was processed successfully + // totalData == currentData) + // ; updateStatusDialogNow(false); //keep this sequence to avoid display distortion, if e.g. only 1 item is sync'ed - //hide progress text control and show log control instead - m_textCtrlInfo->Hide(); - LogControl* logControl = new LogControl(this, log); - bSizerProgressText->Add(logControl, 3, wxEXPAND | wxALL, 5); + //fill result listbox: + //1. log file + LogControl* logControl = new LogControl(m_listbookResult, log); + m_listbookResult->AddPage(logControl, _("Logging"), true); + //bSizerHoldStretch->Insert(0, logControl, 1, wxEXPAND); + + //2. re-arrange graph into results listbook + bSizerProgressMain->Detach(m_panelGraph); + m_panelGraph->Reparent(m_listbookResult); + m_listbookResult->AddPage(m_panelGraph, _("Statistics"), false); - Layout(); // + //fitHeight(*this); + Layout(); //Raise(); -> don't! user may be watching a movie in the meantime ;) } |