1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-09-17 16:50:26 +02:00

Reimplement multi-column format of TimerProgressDialog...

... Specify an array of arrays of strings.  Don't pack it all as a single
string that is parsed.  This makes the setup clearer.

It also avoids some concatenations of localized strings (which I want to
make uncompilable some day), and also removes the need for translators to
count the \n's and replicate precisely.
This commit is contained in:
Paul Licameli 2018-01-05 15:41:36 -05:00
parent e19a4fa943
commit 1320879ab4
3 changed files with 166 additions and 88 deletions

View File

@ -522,30 +522,36 @@ int TimerRecordDialog::RunWaitDialog()
wxString sPostAction = m_pTimerAfterCompleteChoiceCtrl->GetString(m_pTimerAfterCompleteChoiceCtrl->GetSelection()); wxString sPostAction = m_pTimerAfterCompleteChoiceCtrl->GetString(m_pTimerAfterCompleteChoiceCtrl->GetSelection());
// Two column layout. Line spacing must match for both columns. // Two column layout.
// First column TimerProgressDialog::MessageTable columns(2);
wxString strMsg = _("Recording start:\n") + auto &column1 = columns[0];
_("Duration:\n") + auto &column2 = columns[1];
_("Recording end:\n\n") +
_("Automatic Save enabled:\n") +
_("Automatic Export enabled:\n") +
_("Action after Timer Recording:");
strMsg += ProgressDialog::ColoumnSplitMarker; column1.push_back( _("Recording start:") );
column2.push_back( GetDisplayDate(m_DateTime_Start) );
// Second column column1.push_back( _("Duration:") );
strMsg += wxString::Format(wxT("%s\n%s\n%s\n\n%s\n%s\n%s"), column2.push_back( m_TimeSpan_Duration.Format() );
GetDisplayDate(m_DateTime_Start),
m_TimeSpan_Duration.Format(), column1.push_back( _("Recording end:") );
GetDisplayDate(m_DateTime_End), column2.push_back( GetDisplayDate(m_DateTime_End) );
(m_bAutoSaveEnabled ? _("Yes") : _("No")),
(m_bAutoExportEnabled ? _("Yes") : _("No")), column1.push_back( {} );
sPostAction); column2.push_back( {} );
column1.push_back( _("Automatic Save enabled:") );
column2.push_back( (m_bAutoSaveEnabled ? _("Yes") : _("No")) );
column1.push_back( _("Automatic Export enabled:") );
column2.push_back( (m_bAutoExportEnabled ? _("Yes") : _("No")) );
column1.push_back( _("Action after Timer Recording:") );
column2.push_back( sPostAction );
TimerProgressDialog TimerProgressDialog
progress(m_TimeSpan_Duration.GetMilliseconds().GetValue(), progress(m_TimeSpan_Duration.GetMilliseconds().GetValue(),
_("Audacity Timer Record Progress"), _("Audacity Timer Record Progress"),
strMsg, columns,
pdlgHideCancelButton | pdlgConfirmStopCancel); pdlgHideCancelButton | pdlgConfirmStopCancel);
// Make sure that start and end time are updated, so we always get the full // Make sure that start and end time are updated, so we always get the full
@ -555,7 +561,7 @@ int TimerRecordDialog::RunWaitDialog()
// Loop for progress display during recording. // Loop for progress display during recording.
while (bIsRecording && (updateResult == ProgressResult::Success)) { while (bIsRecording && (updateResult == ProgressResult::Success)) {
updateResult = progress.Update(); updateResult = progress.UpdateProgress();
wxMilliSleep(kTimerInterval); wxMilliSleep(kTimerInterval);
bIsRecording = (wxDateTime::UNow() <= m_DateTime_End); // Call UNow() again for extra accuracy... bIsRecording = (wxDateTime::UNow() <= m_DateTime_End); // Call UNow() again for extra accuracy...
} }
@ -1007,31 +1013,37 @@ ProgressResult TimerRecordDialog::WaitForStart()
// MY: The Waiting For Start dialog now shows what actions will occur after recording has completed // MY: The Waiting For Start dialog now shows what actions will occur after recording has completed
wxString sPostAction = m_pTimerAfterCompleteChoiceCtrl->GetString(m_pTimerAfterCompleteChoiceCtrl->GetSelection()); wxString sPostAction = m_pTimerAfterCompleteChoiceCtrl->GetString(m_pTimerAfterCompleteChoiceCtrl->GetSelection());
// Two column layout. Line spacing must match for both columns. // Two column layout.
// First column TimerProgressDialog::MessageTable columns(2);
wxString strMsg = _("Waiting to start recording at:\n") + auto &column1 = columns[0];
_("Recording duration:\n") + auto &column2 = columns[1];
_("Scheduled to stop at:\n\n") +
_("Automatic Save enabled:\n") +
_("Automatic Export enabled:\n") +
_("Action after Timer Recording:");
strMsg += ProgressDialog::ColoumnSplitMarker; column1.push_back(_("Waiting to start recording at:"));
column2.push_back(GetDisplayDate(m_DateTime_Start));
// Second column column1.push_back(_("Recording duration:"));
strMsg += wxString::Format(wxT("%s\n%s\n%s\n\n%s\n%s\n%s"), column2.push_back(m_TimeSpan_Duration.Format());
GetDisplayDate(m_DateTime_Start),
m_TimeSpan_Duration.Format(), column1.push_back(_("Scheduled to stop at:"));
GetDisplayDate(m_DateTime_End), column2.push_back(GetDisplayDate(m_DateTime_End));
(m_bAutoSaveEnabled ? _("Yes") : _("No")),
(m_bAutoExportEnabled ? _("Yes") : _("No")), column1.push_back( {} );
sPostAction); column2.push_back( {} );
column1.push_back(_("Automatic Save enabled:"));
column2.push_back((m_bAutoSaveEnabled ? _("Yes") : _("No")));
column1.push_back(_("Automatic Export enabled:"));
column2.push_back((m_bAutoExportEnabled ? _("Yes") : _("No")));
column1.push_back(_("Action after Timer Recording:"));
column2.push_back(sPostAction);
wxDateTime startWait_DateTime = wxDateTime::UNow(); wxDateTime startWait_DateTime = wxDateTime::UNow();
wxTimeSpan waitDuration = m_DateTime_Start - startWait_DateTime; wxTimeSpan waitDuration = m_DateTime_Start - startWait_DateTime;
TimerProgressDialog progress(waitDuration.GetMilliseconds().GetValue(), TimerProgressDialog progress(waitDuration.GetMilliseconds().GetValue(),
_("Audacity Timer Record - Waiting for Start"), _("Audacity Timer Record - Waiting for Start"),
strMsg, columns,
pdlgHideStopButton | pdlgConfirmStopCancel | pdlgHideElapsedTime, pdlgHideStopButton | pdlgConfirmStopCancel | pdlgHideElapsedTime,
_("Recording will commence in:")); _("Recording will commence in:"));
@ -1039,7 +1051,7 @@ ProgressResult TimerRecordDialog::WaitForStart()
bool bIsRecording = false; bool bIsRecording = false;
while (updateResult == ProgressResult::Success && !bIsRecording) while (updateResult == ProgressResult::Success && !bIsRecording)
{ {
updateResult = progress.Update(); updateResult = progress.UpdateProgress();
wxMilliSleep(10); wxMilliSleep(10);
bIsRecording = (m_DateTime_Start <= wxDateTime::UNow()); bIsRecording = (m_DateTime_Start <= wxDateTime::UNow());
} }
@ -1052,20 +1064,25 @@ ProgressResult TimerRecordDialog::PreActionDelay(int iActionIndex, TimerRecordCo
wxString sCountdownLabel; wxString sCountdownLabel;
sCountdownLabel.Printf("%s in:", sAction); sCountdownLabel.Printf("%s in:", sAction);
// Two column layout. Line spacing must match for both columns. // Two column layout.
// First column TimerProgressDialog::MessageTable columns(2);
wxString strMsg = _("Timer Recording completed.\n\n") + auto &column1 = columns[0];
_("Recording Saved:\n") + auto &column2 = columns[1];
_("Recording Exported:\n") +
_("Action after Timer Recording:");
strMsg += ProgressDialog::ColoumnSplitMarker; column1.push_back(_("Timer Recording completed."));
column2.push_back( {} );
// Second column column1.push_back( {} );
strMsg += wxString::Format(wxT("\n\n%s\n%s\n%s"), column2.push_back( {} );
((eCompletedActions & TR_ACTION_SAVED) ? _("Yes") : _("No")),
((eCompletedActions & TR_ACTION_EXPORTED) ? _("Yes") : _("No")), column1.push_back(_("Recording Saved:"));
sAction); column2.push_back(((eCompletedActions & TR_ACTION_SAVED) ? _("Yes") : _("No")));
column1.push_back(_("Recording Exported:"));
column2.push_back(((eCompletedActions & TR_ACTION_EXPORTED) ? _("Yes") : _("No")));
column1.push_back(_("Action after Timer Recording:"));
column2.push_back(sAction);
wxDateTime dtNow = wxDateTime::UNow(); wxDateTime dtNow = wxDateTime::UNow();
wxTimeSpan tsWait = wxTimeSpan(0, 1, 0, 0); wxTimeSpan tsWait = wxTimeSpan(0, 1, 0, 0);
@ -1073,7 +1090,7 @@ ProgressResult TimerRecordDialog::PreActionDelay(int iActionIndex, TimerRecordCo
TimerProgressDialog dlgAction(tsWait.GetMilliseconds().GetValue(), TimerProgressDialog dlgAction(tsWait.GetMilliseconds().GetValue(),
_("Audacity Timer Record - Waiting"), _("Audacity Timer Record - Waiting"),
strMsg, columns,
pdlgHideStopButton | pdlgHideElapsedTime, pdlgHideStopButton | pdlgHideElapsedTime,
sCountdownLabel); sCountdownLabel);
@ -1081,7 +1098,7 @@ ProgressResult TimerRecordDialog::PreActionDelay(int iActionIndex, TimerRecordCo
bool bIsTime = false; bool bIsTime = false;
while (iUpdateResult == ProgressResult::Success && !bIsTime) while (iUpdateResult == ProgressResult::Success && !bIsTime)
{ {
iUpdateResult = dlgAction.Update(); iUpdateResult = dlgAction.UpdateProgress();
wxMilliSleep(10); wxMilliSleep(10);
bIsTime = (dtActionTime <= wxDateTime::UNow()); bIsTime = (dtActionTime <= wxDateTime::UNow());
} }

View File

@ -1010,6 +1010,17 @@ ProgressDialog::ProgressDialog(const wxString & title,
Create(title, message, flags, sRemainingLabelText); Create(title, message, flags, sRemainingLabelText);
} }
ProgressDialog::ProgressDialog(const wxString & title,
const MessageTable &columns,
int flags /* = pdlgDefaultFlags */,
const wxString & sRemainingLabelText /* = wxEmptyString */)
: wxDialogWrapper()
{
Init();
Create(title, columns, flags, sRemainingLabelText);
}
// //
// Destructor // Destructor
// //
@ -1071,13 +1082,18 @@ void ProgressDialog::Init()
} }
// Add a NEW text column each time this is called. // Add a NEW text column each time this is called.
void ProgressDialog::AddMessageAsColumn(wxBoxSizer * pSizer, const wxString & sText, bool bFirstColumn) { void ProgressDialog::AddMessageAsColumn(wxBoxSizer * pSizer,
const MessageColumn & column,
bool bFirstColumn) {
// Assuming that we don't want empty columns, bail out if there is no text. // Assuming that we don't want empty columns, bail out if there is no text.
if (sText.IsEmpty()) if (column.empty())
{
return; return;
}
// Join strings
auto sText = column[0];
std::for_each( column.begin() + 1, column.end(),
[&](const wxString &text) { sText += wxT("\n") + text; });
// Create a statictext object and add to the sizer // Create a statictext object and add to the sizer
wxStaticText* oText = safenew wxStaticText(this, wxStaticText* oText = safenew wxStaticText(this,
@ -1089,7 +1105,7 @@ void ProgressDialog::AddMessageAsColumn(wxBoxSizer * pSizer, const wxString & sT
oText->SetName(sText); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs) oText->SetName(sText); // fix for bug 577 (NVDA/Narrator screen readers do not read static text in dialogs)
// If this is the first column then set the mMessage pointer so non-TimerRecord usages // If this is the first column then set the mMessage pointer so non-TimerRecord usages
// will still work correctly // will still work correctly in SetMessage()
if (bFirstColumn) { if (bFirstColumn) {
mMessage = oText; mMessage = oText;
} }
@ -1101,6 +1117,25 @@ bool ProgressDialog::Create(const wxString & title,
const wxString & message /* = wxEmptyString */, const wxString & message /* = wxEmptyString */,
int flags /* = pdlgDefaultFlags */, int flags /* = pdlgDefaultFlags */,
const wxString & sRemainingLabelText /* = wxEmptyString */) const wxString & sRemainingLabelText /* = wxEmptyString */)
{
MessageTable columns(1);
columns.back().push_back(message);
auto result = Create(title, columns, flags, sRemainingLabelText);
if (result) {
// Record some values used in case of change of message
// TODO: make the following work in case of message tables
wxClientDC dc(this);
dc.GetMultiLineTextExtent(message, &mLastW, &mLastH);
}
return result;
}
bool ProgressDialog::Create(const wxString & title,
const MessageTable & columns,
int flags /* = pdlgDefaultFlags */,
const wxString & sRemainingLabelText /* = wxEmptyString */)
{ {
wxWindow *parent = GetParentForModalDialog(NULL, 0); wxWindow *parent = GetParentForModalDialog(NULL, 0);
@ -1126,15 +1161,19 @@ bool ProgressDialog::Create(const wxString & title,
{ {
wxWindow *window; wxWindow *window;
wxArrayString arMessages(wxSplit(message, ProgressDialog::ColoumnSplitMarker));
// There may be more than one column, so create a BoxSizer container // There may be more than one column, so create a BoxSizer container
auto uColSizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL); auto uColSizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
auto colSizer = uColSizer.get(); auto colSizer = uColSizer.get();
for (size_t column = 0; column < arMessages.GetCount(); column++) { // TODO: this setting-up of a grid of text in a sizer might be worth
bool bFirstCol = (column == 0); // extracting as a utility for building other dialogs.
AddMessageAsColumn(colSizer, arMessages[column], bFirstCol); {
bool bFirstCol = true;
for (const auto &column : columns) {
AddMessageAsColumn(colSizer, column, bFirstCol);
bFirstCol = false;
}
} }
// and put message column(s) into a main vertical sizer. // and put message column(s) into a main vertical sizer.
@ -1225,13 +1264,6 @@ bool ProgressDialog::Create(const wxString & title,
} }
Layout(); Layout();
wxClientDC dc(this);
dc.GetMultiLineTextExtent(message, &mLastW, &mLastH);
// Add a little bit more width when we have TABs to stop words wrapping
int iTabFreq = wxMax((message.Freq('\t') - 1), 0);
mLastW = mLastW + (iTabFreq * 8);
Centre(wxCENTER_FRAME | wxBOTH); Centre(wxCENTER_FRAME | wxBOTH);
mStartTime = wxGetLocalTimeMillis().GetValue(); mStartTime = wxGetLocalTimeMillis().GetValue();
@ -1459,6 +1491,7 @@ void ProgressDialog::SetMessage(const wxString & message)
bool sizeUpdated = false; bool sizeUpdated = false;
wxSize ds = GetClientSize(); wxSize ds = GetClientSize();
// TODO: make the following work in case of message tables
if (w > mLastW) if (w > mLastW)
{ {
ds.x += (w - mLastW); ds.x += (w - mLastW);
@ -1591,15 +1624,15 @@ bool ProgressDialog::ConfirmAction(const wxString & sPrompt,
TimerProgressDialog::TimerProgressDialog(const wxLongLong_t duration, TimerProgressDialog::TimerProgressDialog(const wxLongLong_t duration,
const wxString & title, const wxString & title,
const wxString & message /* = wxEmptyString */, const MessageTable & columns,
int flags /* = pdlgDefaultFlags */, int flags /* = pdlgDefaultFlags */,
const wxString & sRemainingLabelText /* = wxEmptyString */) const wxString & sRemainingLabelText /* = wxEmptyString */)
: ProgressDialog(title, message, flags, sRemainingLabelText) : ProgressDialog(title, columns, flags, sRemainingLabelText)
{ {
mDuration = duration; mDuration = duration;
} }
ProgressResult TimerProgressDialog::Update(const wxString & message /*= wxEmptyString*/) ProgressResult TimerProgressDialog::UpdateProgress()
{ {
if (mCancel) if (mCancel)
{ {
@ -1625,8 +1658,6 @@ ProgressResult TimerProgressDialog::Update(const wxString & message /*= wxEmptyS
mIsTransparent = false; mIsTransparent = false;
} }
SetMessage(message);
wxLongLong_t remains = mStartTime + mDuration - now; wxLongLong_t remains = mStartTime + mDuration - now;
int nGaugeValue = (1000 * elapsed) / mDuration; // range = [0,1000] int nGaugeValue = (1000 * elapsed) / mDuration; // range = [0,1000]

View File

@ -21,6 +21,7 @@
#include "../Audacity.h" #include "../Audacity.h"
#include "../MemoryX.h" #include "../MemoryX.h"
#include <vector>
#include <wx/defs.h> #include <wx/defs.h>
#include <wx/evtloop.h> #include <wx/evtloop.h>
#include <wx/gauge.h> #include <wx/gauge.h>
@ -56,18 +57,42 @@ class AUDACITY_DLL_API ProgressDialog /* not final */ : public wxDialogWrapper
{ {
public: public:
ProgressDialog(); ProgressDialog();
// Display a simple message.
ProgressDialog(const wxString & title, ProgressDialog(const wxString & title,
const wxString & message = wxEmptyString, const wxString & message = wxEmptyString,
int flags = pdlgDefaultFlags, int flags = pdlgDefaultFlags,
const wxString & sRemainingLabelText = wxEmptyString); const wxString & sRemainingLabelText = wxEmptyString);
using MessageColumn = std::vector< wxString >;
using MessageTable = std::vector< MessageColumn >;
protected:
// Display a table of messages.
// Each member of the table is a column of messages.
// Each member of a column is a string that should have no newlines,
// and each column should have the same number of elements, to make
// proper correspondences, but this is not checked.
ProgressDialog(const wxString & title,
const MessageTable & columns,
int flags = pdlgDefaultFlags,
const wxString & sRemainingLabelText = wxEmptyString);
public:
virtual ~ProgressDialog(); virtual ~ProgressDialog();
// NEW virtual? It doesn't override wxDialog bool Create(const wxString & title,
virtual bool Create(const wxString & title,
const wxString & message = wxEmptyString, const wxString & message = wxEmptyString,
int flags = pdlgDefaultFlags, int flags = pdlgDefaultFlags,
const wxString & sRemainingLabelText = wxEmptyString); const wxString & sRemainingLabelText = wxEmptyString);
protected:
bool Create(const wxString & title,
const MessageTable & columns,
int flags = pdlgDefaultFlags,
const wxString & sRemainingLabelText = wxEmptyString);
public:
ProgressResult Update(int value, const wxString & message = wxEmptyString); ProgressResult Update(int value, const wxString & message = wxEmptyString);
ProgressResult Update(double current, const wxString & message = wxEmptyString); ProgressResult Update(double current, const wxString & message = wxEmptyString);
ProgressResult Update(double current, double total, const wxString & message = wxEmptyString); ProgressResult Update(double current, double total, const wxString & message = wxEmptyString);
@ -77,9 +102,6 @@ public:
ProgressResult Update(int current, int total, const wxString & message = wxEmptyString); ProgressResult Update(int current, int total, const wxString & message = wxEmptyString);
void SetMessage(const wxString & message); void SetMessage(const wxString & message);
// 'ETB' character to indicate a NEW column in the message text.
static const wxChar ColoumnSplitMarker = (char)23;
protected: protected:
wxWindow *mHadFocus; wxWindow *mHadFocus;
@ -113,7 +135,8 @@ private:
const wxString & sTitle, const wxString & sTitle,
int iButtonID = -1); int iButtonID = -1);
void AddMessageAsColumn(wxBoxSizer * pSizer, const wxString & sText, bool bFirstColumn); void AddMessageAsColumn(wxBoxSizer * pSizer,
const MessageColumn &column, bool bFirstColumn);
private: private:
// This guarantees we have an active event loop...possible during OnInit() // This guarantees we have an active event loop...possible during OnInit()
@ -121,9 +144,9 @@ private:
std::unique_ptr<wxWindowDisabler> mDisable; std::unique_ptr<wxWindowDisabler> mDisable;
wxStaticText *mMessage; wxStaticText *mMessage{} ;
int mLastW; int mLastW{ 0 };
int mLastH; int mLastH{ 0 };
DECLARE_EVENT_TABLE() DECLARE_EVENT_TABLE()
}; };
@ -133,13 +156,20 @@ class AUDACITY_DLL_API TimerProgressDialog final : public ProgressDialog
public: public:
TimerProgressDialog(const wxLongLong_t duration, TimerProgressDialog(const wxLongLong_t duration,
const wxString &title, const wxString &title,
const wxString & message = wxEmptyString, const MessageTable & columns,
int flags = pdlgDefaultFlags, int flags = pdlgDefaultFlags,
const wxString & sRemainingLabelText = wxEmptyString); const wxString & sRemainingLabelText = wxEmptyString);
ProgressResult Update(const wxString & message = wxEmptyString);
// Oh no, there is an inherited nullary "Update" in wxDialog!
// Choose another name then...
ProgressResult UpdateProgress();
protected: protected:
wxLongLong_t mDuration; wxLongLong_t mDuration;
// Disallow direct use of the inherited overloads of Update because it
// doesn't support changes of message
using ProgressDialog::Update;
}; };
#endif #endif