1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 08:09:32 +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());
// Two column layout. Line spacing must match for both columns.
// First column
wxString strMsg = _("Recording start:\n") +
_("Duration:\n") +
_("Recording end:\n\n") +
_("Automatic Save enabled:\n") +
_("Automatic Export enabled:\n") +
_("Action after Timer Recording:");
// Two column layout.
TimerProgressDialog::MessageTable columns(2);
auto &column1 = columns[0];
auto &column2 = columns[1];
strMsg += ProgressDialog::ColoumnSplitMarker;
column1.push_back( _("Recording start:") );
column2.push_back( GetDisplayDate(m_DateTime_Start) );
// Second column
strMsg += wxString::Format(wxT("%s\n%s\n%s\n\n%s\n%s\n%s"),
GetDisplayDate(m_DateTime_Start),
m_TimeSpan_Duration.Format(),
GetDisplayDate(m_DateTime_End),
(m_bAutoSaveEnabled ? _("Yes") : _("No")),
(m_bAutoExportEnabled ? _("Yes") : _("No")),
sPostAction);
column1.push_back( _("Duration:") );
column2.push_back( m_TimeSpan_Duration.Format() );
column1.push_back( _("Recording end:") );
column2.push_back( GetDisplayDate(m_DateTime_End) );
column1.push_back( {} );
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
progress(m_TimeSpan_Duration.GetMilliseconds().GetValue(),
_("Audacity Timer Record Progress"),
strMsg,
columns,
pdlgHideCancelButton | pdlgConfirmStopCancel);
// 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.
while (bIsRecording && (updateResult == ProgressResult::Success)) {
updateResult = progress.Update();
updateResult = progress.UpdateProgress();
wxMilliSleep(kTimerInterval);
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
wxString sPostAction = m_pTimerAfterCompleteChoiceCtrl->GetString(m_pTimerAfterCompleteChoiceCtrl->GetSelection());
// Two column layout. Line spacing must match for both columns.
// First column
wxString strMsg = _("Waiting to start recording at:\n") +
_("Recording duration:\n") +
_("Scheduled to stop at:\n\n") +
_("Automatic Save enabled:\n") +
_("Automatic Export enabled:\n") +
_("Action after Timer Recording:");
// Two column layout.
TimerProgressDialog::MessageTable columns(2);
auto &column1 = columns[0];
auto &column2 = columns[1];
strMsg += ProgressDialog::ColoumnSplitMarker;
column1.push_back(_("Waiting to start recording at:"));
column2.push_back(GetDisplayDate(m_DateTime_Start));
// Second column
strMsg += wxString::Format(wxT("%s\n%s\n%s\n\n%s\n%s\n%s"),
GetDisplayDate(m_DateTime_Start),
m_TimeSpan_Duration.Format(),
GetDisplayDate(m_DateTime_End),
(m_bAutoSaveEnabled ? _("Yes") : _("No")),
(m_bAutoExportEnabled ? _("Yes") : _("No")),
sPostAction);
column1.push_back(_("Recording duration:"));
column2.push_back(m_TimeSpan_Duration.Format());
column1.push_back(_("Scheduled to stop at:"));
column2.push_back(GetDisplayDate(m_DateTime_End));
column1.push_back( {} );
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();
wxTimeSpan waitDuration = m_DateTime_Start - startWait_DateTime;
TimerProgressDialog progress(waitDuration.GetMilliseconds().GetValue(),
_("Audacity Timer Record - Waiting for Start"),
strMsg,
columns,
pdlgHideStopButton | pdlgConfirmStopCancel | pdlgHideElapsedTime,
_("Recording will commence in:"));
@ -1039,7 +1051,7 @@ ProgressResult TimerRecordDialog::WaitForStart()
bool bIsRecording = false;
while (updateResult == ProgressResult::Success && !bIsRecording)
{
updateResult = progress.Update();
updateResult = progress.UpdateProgress();
wxMilliSleep(10);
bIsRecording = (m_DateTime_Start <= wxDateTime::UNow());
}
@ -1052,20 +1064,25 @@ ProgressResult TimerRecordDialog::PreActionDelay(int iActionIndex, TimerRecordCo
wxString sCountdownLabel;
sCountdownLabel.Printf("%s in:", sAction);
// Two column layout. Line spacing must match for both columns.
// First column
wxString strMsg = _("Timer Recording completed.\n\n") +
_("Recording Saved:\n") +
_("Recording Exported:\n") +
_("Action after Timer Recording:");
// Two column layout.
TimerProgressDialog::MessageTable columns(2);
auto &column1 = columns[0];
auto &column2 = columns[1];
strMsg += ProgressDialog::ColoumnSplitMarker;
column1.push_back(_("Timer Recording completed."));
column2.push_back( {} );
// Second column
strMsg += wxString::Format(wxT("\n\n%s\n%s\n%s"),
((eCompletedActions & TR_ACTION_SAVED) ? _("Yes") : _("No")),
((eCompletedActions & TR_ACTION_EXPORTED) ? _("Yes") : _("No")),
sAction);
column1.push_back( {} );
column2.push_back( {} );
column1.push_back(_("Recording Saved:"));
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();
wxTimeSpan tsWait = wxTimeSpan(0, 1, 0, 0);
@ -1073,7 +1090,7 @@ ProgressResult TimerRecordDialog::PreActionDelay(int iActionIndex, TimerRecordCo
TimerProgressDialog dlgAction(tsWait.GetMilliseconds().GetValue(),
_("Audacity Timer Record - Waiting"),
strMsg,
columns,
pdlgHideStopButton | pdlgHideElapsedTime,
sCountdownLabel);
@ -1081,7 +1098,7 @@ ProgressResult TimerRecordDialog::PreActionDelay(int iActionIndex, TimerRecordCo
bool bIsTime = false;
while (iUpdateResult == ProgressResult::Success && !bIsTime)
{
iUpdateResult = dlgAction.Update();
iUpdateResult = dlgAction.UpdateProgress();
wxMilliSleep(10);
bIsTime = (dtActionTime <= wxDateTime::UNow());
}

View File

@ -1010,6 +1010,17 @@ ProgressDialog::ProgressDialog(const wxString & title,
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
//
@ -1071,13 +1082,18 @@ void ProgressDialog::Init()
}
// 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.
if (sText.IsEmpty())
{
if (column.empty())
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
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)
// 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) {
mMessage = oText;
}
@ -1101,6 +1117,25 @@ bool ProgressDialog::Create(const wxString & title,
const wxString & message /* = wxEmptyString */,
int flags /* = pdlgDefaultFlags */,
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);
@ -1126,15 +1161,19 @@ bool ProgressDialog::Create(const wxString & title,
{
wxWindow *window;
wxArrayString arMessages(wxSplit(message, ProgressDialog::ColoumnSplitMarker));
// There may be more than one column, so create a BoxSizer container
auto uColSizer = std::make_unique<wxBoxSizer>(wxHORIZONTAL);
auto colSizer = uColSizer.get();
for (size_t column = 0; column < arMessages.GetCount(); column++) {
bool bFirstCol = (column == 0);
AddMessageAsColumn(colSizer, arMessages[column], bFirstCol);
// TODO: this setting-up of a grid of text in a sizer might be worth
// extracting as a utility for building other dialogs.
{
bool bFirstCol = true;
for (const auto &column : columns) {
AddMessageAsColumn(colSizer, column, bFirstCol);
bFirstCol = false;
}
}
// and put message column(s) into a main vertical sizer.
@ -1225,13 +1264,6 @@ bool ProgressDialog::Create(const wxString & title,
}
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);
mStartTime = wxGetLocalTimeMillis().GetValue();
@ -1459,6 +1491,7 @@ void ProgressDialog::SetMessage(const wxString & message)
bool sizeUpdated = false;
wxSize ds = GetClientSize();
// TODO: make the following work in case of message tables
if (w > mLastW)
{
ds.x += (w - mLastW);
@ -1591,15 +1624,15 @@ bool ProgressDialog::ConfirmAction(const wxString & sPrompt,
TimerProgressDialog::TimerProgressDialog(const wxLongLong_t duration,
const wxString & title,
const wxString & message /* = wxEmptyString */,
const MessageTable & columns,
int flags /* = pdlgDefaultFlags */,
const wxString & sRemainingLabelText /* = wxEmptyString */)
: ProgressDialog(title, message, flags, sRemainingLabelText)
: ProgressDialog(title, columns, flags, sRemainingLabelText)
{
mDuration = duration;
}
ProgressResult TimerProgressDialog::Update(const wxString & message /*= wxEmptyString*/)
ProgressResult TimerProgressDialog::UpdateProgress()
{
if (mCancel)
{
@ -1625,8 +1658,6 @@ ProgressResult TimerProgressDialog::Update(const wxString & message /*= wxEmptyS
mIsTransparent = false;
}
SetMessage(message);
wxLongLong_t remains = mStartTime + mDuration - now;
int nGaugeValue = (1000 * elapsed) / mDuration; // range = [0,1000]

View File

@ -21,6 +21,7 @@
#include "../Audacity.h"
#include "../MemoryX.h"
#include <vector>
#include <wx/defs.h>
#include <wx/evtloop.h>
#include <wx/gauge.h>
@ -56,18 +57,42 @@ class AUDACITY_DLL_API ProgressDialog /* not final */ : public wxDialogWrapper
{
public:
ProgressDialog();
// Display a simple message.
ProgressDialog(const wxString & title,
const wxString & message = wxEmptyString,
int flags = pdlgDefaultFlags,
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();
// NEW virtual? It doesn't override wxDialog
virtual bool Create(const wxString & title,
const wxString & message = wxEmptyString,
int flags = pdlgDefaultFlags,
const wxString & sRemainingLabelText = wxEmptyString);
bool Create(const wxString & title,
const wxString & message = wxEmptyString,
int flags = pdlgDefaultFlags,
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(double current, 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);
void SetMessage(const wxString & message);
// 'ETB' character to indicate a NEW column in the message text.
static const wxChar ColoumnSplitMarker = (char)23;
protected:
wxWindow *mHadFocus;
@ -113,7 +135,8 @@ private:
const wxString & sTitle,
int iButtonID = -1);
void AddMessageAsColumn(wxBoxSizer * pSizer, const wxString & sText, bool bFirstColumn);
void AddMessageAsColumn(wxBoxSizer * pSizer,
const MessageColumn &column, bool bFirstColumn);
private:
// This guarantees we have an active event loop...possible during OnInit()
@ -121,9 +144,9 @@ private:
std::unique_ptr<wxWindowDisabler> mDisable;
wxStaticText *mMessage;
int mLastW;
int mLastH;
wxStaticText *mMessage{} ;
int mLastW{ 0 };
int mLastH{ 0 };
DECLARE_EVENT_TABLE()
};
@ -132,14 +155,21 @@ class AUDACITY_DLL_API TimerProgressDialog final : public ProgressDialog
{
public:
TimerProgressDialog(const wxLongLong_t duration,
const wxString & title,
const wxString & message = wxEmptyString,
const wxString &title,
const MessageTable & columns,
int flags = pdlgDefaultFlags,
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:
wxLongLong_t mDuration;
// Disallow direct use of the inherited overloads of Update because it
// doesn't support changes of message
using ProgressDialog::Update;
};
#endif