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:
parent
e19a4fa943
commit
1320879ab4
@ -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());
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user