Audacity 3.2.0
Public Member Functions | Static Public Member Functions | Private Member Functions | Private Attributes | List of all members
TagsEditorDialog Class Referencefinal

Derived from ExpandingToolBar, this dialog allows editing of Tags. More...

#include <TagsEditor.h>

Inheritance diagram for TagsEditorDialog:
[legend]
Collaboration diagram for TagsEditorDialog:
[legend]

Public Member Functions

 TagsEditorDialog (wxWindow *parent, const TranslatableString &title, std::vector< Tags * > tags, std::vector< wxString > names, bool editTitle, bool editTrack)
 
virtual ~TagsEditorDialog ()
 
void PopulateOrExchange (ShuttleGui &S)
 
void OnHelp (wxCommandEvent &Evt)
 
bool TransferDataToWindow () override
 
bool TransferDataFromWindow () override
 
- Public Member Functions inherited from wxDialogWrapper
 wxDialogWrapper ()
 
 wxDialogWrapper (wxWindow *parent, wxWindowID id, const TranslatableString &title, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxDEFAULT_DIALOG_STYLE, const TranslatableString &name=XO("Dialog"))
 
bool Create (wxWindow *parent, wxWindowID id, const TranslatableString &title, const wxPoint &pos=wxDefaultPosition, const wxSize &size=wxDefaultSize, long style=wxDEFAULT_DIALOG_STYLE, const TranslatableString &name=XO("Dialog"))
 
void SetTitle (const TranslatableString &title)
 
void SetLabel (const TranslatableString &title)
 
void SetName (const TranslatableString &title)
 
void SetName ()
 
- Public Member Functions inherited from wxTabTraversalWrapper< wxDialog >
 wxTabTraversalWrapper (Args &&... args)
 
 wxTabTraversalWrapper (const wxTabTraversalWrapper &)=delete
 
 wxTabTraversalWrapper (wxTabTraversalWrapper &&)=delete
 
wxTabTraversalWrapperoperator= (const wxTabTraversalWrapper &)=delete
 
wxTabTraversalWrapperoperator= (wxTabTraversalWrapper &&)=delete
 

Static Public Member Functions

static AUDACITY_DLL_API bool ShowEditDialog (Tags &tags, wxWindow *parent, const TranslatableString &title)
 
static AUDACITY_DLL_API bool EditProjectMetadata (AudacityProject &project, const TranslatableString &title, const TranslatableString &shortUndoDescription)
 

Private Member Functions

void PopulateGenres ()
 
void SetEditors ()
 
void OnNext (wxCommandEvent &)
 
void OnPrev (wxCommandEvent &)
 
void OnChange (wxGridEvent &event)
 
void OnEdit (wxCommandEvent &event)
 
void OnReset (wxCommandEvent &event)
 
void OnClear (wxCommandEvent &event)
 
void OnLoad (wxCommandEvent &event)
 
void OnSave (wxCommandEvent &event)
 
void OnSaveDefaults (wxCommandEvent &event)
 
void OnAdd (wxCommandEvent &event)
 
void OnRemove (wxCommandEvent &event)
 
void OnOk (wxCommandEvent &event)
 
void DoCancel (bool escKey)
 
void OnCancel (wxCommandEvent &event)
 
void OnKeyDown (wxKeyEvent &event)
 
bool IsWindowRectValid (const wxRect *windowRect) const
 

Private Attributes

std::vector< Tags * > mTags
 
std::vector< wxString > mNames
 
int mSelectedIndex {0}
 
bool mEditTitle
 
bool mEditTrack
 
std::vector< std::unique_ptr< Tags > > mEditTags
 
GridmGrid
 
wxStaticText * mName {}
 
wxButton * mNext {}
 
wxButton * mPrev {}
 
ComboEditormComboEditor
 
wxGridCellStringRenderer * mStringRenderer
 

Detailed Description

Derived from ExpandingToolBar, this dialog allows editing of Tags.

Definition at line 21 of file TagsEditor.h.

Constructor & Destructor Documentation

◆ TagsEditorDialog()

TagsEditorDialog::TagsEditorDialog ( wxWindow *  parent,
const TranslatableString title,
std::vector< Tags * >  tags,
std::vector< wxString >  names,
bool  editTitle,
bool  editTrack 
)

Definition at line 212 of file TagsEditor.cpp.

218: wxDialogWrapper(parent, wxID_ANY, title, wxDefaultPosition, wxDefaultSize,
219 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
220 mTags(std::move(tags)),
221 mNames(std::move(names)),
222 mEditTitle(editTitle),
223 mEditTrack(editTrack)
224{
225 assert(mTags.size() == 1 || (mTags.size() > 1 && mTags.size() == mNames.size()));
226
227 SetName();
228
229 mGrid = NULL;
230
231 // Make a local copy of the passed in tags
232 mEditTags.reserve(mTags.size());
233 for(auto ptr : mTags)
234 mEditTags.push_back(std::make_unique<Tags>(*ptr));
235
236 // Build, size, and position the dialog
237 ShuttleGui S(this, eIsCreating);
239
241
242 Layout();
243 Fit();
244 Center();
245 wxSize sz = GetSize();
246 const auto maxHeight = std::max(1, wxDisplay().GetGeometry().GetHeight() - 100);
247 const auto minHeight = std::min({ sz.y, 600, maxHeight });
248 SetSizeHints(sz.x, minHeight, wxDefaultCoord, maxHeight);
249
250 // Restore the original tags because TransferDataToWindow() will be called again
251 for(unsigned i = 0; i < mEditTags.size(); ++i)
252 {
253 mEditTags[i]->Clear();
254 *mEditTags[i] = *mTags[i];
255 }
256 // Override size and position with last saved
257 wxRect r = GetRect();
258 gPrefs->Read(wxT("/TagsEditorDialog/x"), &r.x, r.x);
259 gPrefs->Read(wxT("/TagsEditorDialog/y"), &r.y, r.y);
260 gPrefs->Read(wxT("/TagsEditorDialog/width"), &r.width, r.width);
261 gPrefs->Read(wxT("/TagsEditorDialog/height"), &r.height, r.height);
262 //On multi-monitor systems, there's a chance the last saved window position is
263 //on a monitor that has been removed or is unavailable.
264 if (IsWindowRectValid(&r))
265 Move(r.GetPosition());
266
267 SetSize(r.GetSize());
268 Layout();
269
270 // Resize value column width based on width of columns and the vertical scrollbar
271 wxScrollBar sb(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL);
272 r = mGrid->GetClientRect();
273 r.width -= mGrid->GetColSize(0);
274 r.width -= sb.GetSize().GetWidth();
275 r.width -= 10;
276 r.width -= r.x;
277 mGrid->SetColSize(1, r.width);
278 //Bug 2038
279 mGrid->SetFocus();
280
281 // Load the genres
283}
wxT("CloseDown"))
int min(int a, int b)
static const auto title
audacity::BasicSettings * gPrefs
Definition: Prefs.cpp:68
@ eIsCreating
Definition: ShuttleGui.h:37
static TranslatableStrings names
Definition: TagsEditor.cpp:153
#define S(N)
Definition: ToChars.cpp:64
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:640
std::vector< Tags * > mTags
Definition: TagsEditor.h:83
std::vector< std::unique_ptr< Tags > > mEditTags
Definition: TagsEditor.h:90
bool IsWindowRectValid(const wxRect *windowRect) const
Definition: TagsEditor.cpp:981
void PopulateOrExchange(ShuttleGui &S)
Definition: TagsEditor.cpp:298
bool TransferDataToWindow() override
Definition: TagsEditor.cpp:453
std::vector< wxString > mNames
Definition: TagsEditor.h:84
virtual bool Read(const wxString &key, bool *value) const =0

References eIsCreating, gPrefs, min(), audacity::BasicSettings::Read(), S, and wxT().

Here is the call graph for this function:

◆ ~TagsEditorDialog()

TagsEditorDialog::~TagsEditorDialog ( )
virtual

Definition at line 285 of file TagsEditor.cpp.

286{
287 // This DELETE is not needed because wxWidgets owns the grid.
288// DELETE mGrid;
289
290// TODO: Need to figure out if these should be deleted. Looks like the wxGrid
291// code takes ownership and uses reference counting, but there's been
292// cases where they show up as memory leaks.
293// PRL: Fixed the leaks, see commit c87eb0804bc5f40659b133cab6e2ade061959645
294// DELETE mStringRenderer;
295// DELETE mComboEditor;
296}

Member Function Documentation

◆ DoCancel()

void TagsEditorDialog::DoCancel ( bool  escKey)
private

Definition at line 891 of file TagsEditor.cpp.

892{
893 if (mGrid->IsCellEditControlShown()) {
894 auto editor = mGrid->GetCellEditor(mGrid->GetGridCursorRow(),
895 mGrid->GetGridCursorCol());
896 editor->Reset();
897 // To avoid memory leak, don't forget DecRef()!
898 editor->DecRef();
899 mGrid->HideCellEditControl();
900#if defined(__WXMSW__)
901 return;
902#endif
903 }
904
905 auto focus = wxWindow::FindFocus();
906 if (escKey && focus == mGrid)
907 return;
908
909 EndModal(wxID_CANCEL);
910}
std::unique_ptr< WindowPlacement > FindFocus()
Find the window that is accepting keyboard input, if any.
Definition: BasicUI.h:375

References BasicUI::FindFocus(), and mGrid.

Referenced by OnCancel(), and OnKeyDown().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ EditProjectMetadata()

bool TagsEditorDialog::EditProjectMetadata ( AudacityProject project,
const TranslatableString title,
const TranslatableString shortUndoDescription 
)
static

Invoke ShowEditDialog on project's tags; commit change in undo history, return true if not cancelled

Definition at line 1003 of file TagsEditor.cpp.

1006{
1007 auto &tags = Tags::Get( project );
1008
1009 // Back up my tags
1010 // Tags (artist name, song properties, MP3 ID3 info, etc.)
1011 // The structure may be shared with undo history entries
1012 // To keep undo working correctly, always replace this with a NEW duplicate
1013 // BEFORE doing any editing of it!
1014 auto newTags = tags.Duplicate();
1015
1016 if (ShowEditDialog(*newTags, &GetProjectFrame( project ), title)) {
1017 if (tags != *newTags) {
1018 // Commit the change to project state only now.
1019 Tags::Set( project, newTags );
1020 ProjectHistory::Get( project ).PushState( title, shortUndoDescription);
1021 }
1022 return true;
1023 }
1024
1025 return false;
1026}
AUDACITY_DLL_API wxFrame & GetProjectFrame(AudacityProject &project)
Get the top-level window associated with the project (as a wxFrame only, when you do not need to use ...
const auto project
void PushState(const TranslatableString &desc, const TranslatableString &shortDesc)
static ProjectHistory & Get(AudacityProject &project)
static AUDACITY_DLL_API bool ShowEditDialog(Tags &tags, wxWindow *parent, const TranslatableString &title)
Definition: TagsEditor.cpp:30
static Tags & Get(AudacityProject &project)
Definition: Tags.cpp:214
static Tags & Set(AudacityProject &project, const std::shared_ptr< Tags > &tags)
Definition: Tags.cpp:224

References ProjectHistory::Get(), Tags::Get(), GetProjectFrame(), project, ProjectHistory::PushState(), Tags::Set(), ShowEditDialog(), and title.

Referenced by anonymous_namespace{TagsEditor.cpp}::OnEditMetadata(), TimerRecordExportDialog::OnEditMetadata(), and ExportAudioDialog::OnEditMetadata().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ IsWindowRectValid()

bool TagsEditorDialog::IsWindowRectValid ( const wxRect *  windowRect) const
private

Definition at line 981 of file TagsEditor.cpp.

982{
983 wxDisplay display;
984 wxPoint topLeft(windowRect->GetTopLeft().x, windowRect->GetTopLeft().y);
985 wxPoint topRight(windowRect->GetTopRight().x, windowRect->GetTopRight().y);
986 wxPoint bottomLeft(windowRect->GetBottomLeft().x, windowRect->GetBottomLeft().y);
987 wxPoint bottomRight(windowRect->GetBottomRight().x, windowRect->GetBottomRight().y);
988 display.GetFromPoint(topLeft);
989 if (display.GetFromPoint(topLeft) == -1 &&
990 display.GetFromPoint(topRight) == -1 &&
991 display.GetFromPoint(bottomLeft) == -1 &&
992 display.GetFromPoint(bottomRight) == -1) {
993 return false;
994 }
995
996 return true;
997}

◆ OnAdd()

void TagsEditorDialog::OnAdd ( wxCommandEvent &  event)
private

Definition at line 830 of file TagsEditor.cpp.

831{
832 mGrid->AppendRows();
833}

References mGrid.

◆ OnCancel()

void TagsEditorDialog::OnCancel ( wxCommandEvent &  event)
private

Definition at line 886 of file TagsEditor.cpp.

887{
888 DoCancel(false);
889}
void DoCancel(bool escKey)
Definition: TagsEditor.cpp:891

References DoCancel().

Here is the call graph for this function:

◆ OnChange()

void TagsEditorDialog::OnChange ( wxGridEvent &  event)
private

Definition at line 524 of file TagsEditor.cpp.

525{
526 static bool ischanging = false;
527
528 // Prevent recursion
529 if (ischanging) {
530 return;
531 }
532
533 event.Skip();
534
535 if (event.GetCol() != 0) {
536 return;
537 }
538
539 // Do not permit duplication of any of the tags.
540 // Tags differing only in case are nondistinct.
541 auto row = event.GetRow();
542 const wxString key0 = mGrid->GetCellValue(row, 0).Upper();
543 auto nn = mGrid->GetNumberRows();
544 for (decltype(nn) ii = 0; ii < nn; ++ii) {
545 if (ii == row)
546 continue;
547 auto key = mGrid->GetCellValue(ii, 0).Upper();
548 if (key0.CmpNoCase(key) == 0) {
549 ischanging = true;
550 wxBell();
551 mGrid->SetGridCursor(ii, 0);
552 event.Veto();
553 ischanging = false;
554 break;
555 }
556 }
557
558 return;
559}
static const AudacityProject::AttachedObjects::RegisteredFactory key

References key, anonymous_namespace{TracksPrefs.cpp}::key0, and mGrid.

◆ OnClear()

void TagsEditorDialog::OnClear ( wxCommandEvent &  event)
private

Definition at line 663 of file TagsEditor.cpp.

664{
665 mEditTags[mSelectedIndex]->Clear();
666
668}

References mEditTags, mSelectedIndex, and TransferDataToWindow().

Here is the call graph for this function:

◆ OnEdit()

void TagsEditorDialog::OnEdit ( wxCommandEvent &  event)
private

Definition at line 561 of file TagsEditor.cpp.

562{
563 if (mGrid->IsCellEditControlShown()) {
564 mGrid->SaveEditControlValue();
565 mGrid->HideCellEditControl();
566 }
567
568 wxDialogWrapper dlg(this, wxID_ANY, XO("Edit Genres"),
569 wxDefaultPosition, wxDefaultSize,
570 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
571 dlg.SetName();
572 wxTextCtrl *tc;
573
574 ShuttleGui S(&dlg, eIsCreating);
575
576 S.StartVerticalLay(true);
577 {
578 tc = S.AddTextWindow(wxT(""));
579 }
580 S.EndVerticalLay();
581
582 S.AddStandardButtons();
583
584 auto& local = *mEditTags[mSelectedIndex];
585
586 wxArrayString g;
587 int cnt = local.GetNumUserGenres();
588 for (int i = 0; i < cnt; i++) {
589 g.push_back(local.GetUserGenre(i));
590 }
591 std::sort( g.begin(), g.end() );
592
593 for (int i = 0; i < cnt; i++) {
594 tc->AppendText(g[i] + wxT("\n"));
595 }
596
597 dlg.Center();
598 if (dlg.ShowModal() == wxID_CANCEL) {
599 return;
600 }
601
602 wxFileName fn(FileNames::DataDir(), wxT("genres.txt"));
603 wxFile f(fn.GetFullPath(), wxFile::write);
604 if (!f.IsOpened() || !f.Write(tc->GetValue())) {
606 XO("Unable to save genre file."),
607 XO("Reset Genres") );
608 return;
609 }
610
611 local.LoadGenres();
612
614}
int AudacityMessageBox(const TranslatableString &message, const TranslatableString &caption, long style, wxWindow *parent, int x, int y)
XO("Cut/Copy/Paste")
static const auto fn
FILES_API FilePath DataDir()
Audacity user data directory.

References AudacityMessageBox(), FileNames::DataDir(), eIsCreating, fn, mEditTags, mGrid, mSelectedIndex, PopulateGenres(), S, wxDialogWrapper::SetName(), wxT(), and XO().

Here is the call graph for this function:

◆ OnHelp()

void TagsEditorDialog::OnHelp ( wxCommandEvent &  Evt)

Definition at line 392 of file TagsEditor.cpp.

393{
394 HelpSystem::ShowHelp(this, L"Metadata_Editor", true);
395}
static void ShowHelp(wxWindow *parent, const FilePath &localFileName, const URLString &remoteURL, bool bModal=false, bool alwaysDefaultBrowser=false)
Definition: HelpSystem.cpp:231

References HelpSystem::ShowHelp().

Here is the call graph for this function:

◆ OnKeyDown()

void TagsEditorDialog::OnKeyDown ( wxKeyEvent &  event)
private

Definition at line 912 of file TagsEditor.cpp.

913{
914 if (event.GetKeyCode() == WXK_ESCAPE)
915 DoCancel(true);
916 else
917 event.Skip();
918}

References DoCancel().

Here is the call graph for this function:

◆ OnLoad()

void TagsEditorDialog::OnLoad ( wxCommandEvent &  event)
private

Definition at line 670 of file TagsEditor.cpp.

671{
672 wxString fn;
673
674 auto& local = *mEditTags[mSelectedIndex];
675
676 // Ask the user for the real name
677 fn = SelectFile(FileNames::Operation::_None,
678 XO("Load Metadata As:"),
680 wxT("Tags.xml"),
681 wxT("xml"),
683 wxFD_OPEN | wxRESIZE_BORDER,
684 this);
685
686 // User canceled...
687 if (fn.empty()) {
688 return;
689 }
690
691 // Load the metadata
692 Tags temp;
693 XMLFileReader reader;
694 if (!reader.Parse(&temp, fn)) {
695 // Inform user of load failure
697 reader.GetErrorStr(),
698 XO("Error Loading Metadata"),
699 wxOK | wxCENTRE,
700 this);
701 return;
702 }
703
704 // Remember title and track in case they're read only
705 wxString title = local.GetTag(TAG_TITLE);
706 wxString track = local.GetTag(TAG_TRACK);
707
708 // Replace existing tags with loaded ones
709 local = temp;
710
711 // Restore title
712 if (!mEditTitle) {
713 local.SetTag(TAG_TITLE, title);
714 }
715
716 // Restore track
717 if (!mEditTrack) {
718 local.SetTag(TAG_TRACK, track);
719 }
720
721 // Go fill up the window
723
724 return;
725}
FilePath SelectFile(FileNames::Operation op, const TranslatableString &message, const FilePath &default_path, const FilePath &default_filename, const FileExtension &default_extension, const FileTypes &fileTypes, int flags, wxWindow *parent)
Definition: SelectFile.cpp:17
#define TAG_TRACK
Definition: Tags.h:61
#define TAG_TITLE
Definition: Tags.h:58
FILES_API const FileType XMLFiles
Definition: FileNames.h:73
ID3 Tags (for MP3)
Definition: Tags.h:73
void SetTag(const wxString &name, const wxString &value, const bool bSpecialTag=false)
Definition: Tags.cpp:431
Reads a file and passes the results through an XMLTagHandler.
Definition: XMLFileReader.h:19
const TranslatableString & GetErrorStr() const
bool Parse(XMLTagHandler *baseHandler, const FilePath &fname)

References AudacityMessageBox(), FileNames::DataDir(), fn, XMLFileReader::GetErrorStr(), mEditTags, mEditTitle, mEditTrack, mSelectedIndex, XMLFileReader::Parse(), SelectFile(), Tags::SetTag(), TAG_TITLE, TAG_TRACK, title, TransferDataToWindow(), wxT(), FileNames::XMLFiles, and XO().

Here is the call graph for this function:

◆ OnNext()

void TagsEditorDialog::OnNext ( wxCommandEvent &  )
private

Definition at line 936 of file TagsEditor.cpp.

937{
938 if(mSelectedIndex == static_cast<int>(mEditTags.size() - 1))
939 return;
940
944}
bool TransferDataFromWindow() override
Definition: TagsEditor.cpp:397

References mEditTags, mSelectedIndex, TransferDataFromWindow(), and TransferDataToWindow().

Here is the call graph for this function:

◆ OnOk()

void TagsEditorDialog::OnOk ( wxCommandEvent &  event)
private

Definition at line 856 of file TagsEditor.cpp.

857{
858 if (mGrid->IsCellEditControlShown()) {
859 mGrid->SaveEditControlValue();
860 mGrid->HideCellEditControl();
861#if defined(__WXMAC__)
862 // The cell editors do not capture the ENTER key, so it invokes
863 // the default button ("Ok") when it should just close the
864 // editor. So, cancel the "Ok" action.
865 return;
866#endif
867 }
868
869 if (!Validate() || !TransferDataFromWindow()) {
870 return;
871 }
872
873 for(unsigned i = 0; i < mEditTags.size(); ++i)
874 *mTags[i] = *mEditTags[i];
875
876 wxRect r = GetRect();
877 gPrefs->Write(wxT("/TagsEditorDialog/x"), r.x);
878 gPrefs->Write(wxT("/TagsEditorDialog/y"), r.y);
879 gPrefs->Write(wxT("/TagsEditorDialog/width"), r.width);
880 gPrefs->Write(wxT("/TagsEditorDialog/height"), r.height);
881 gPrefs->Flush();
882
883 EndModal(wxID_OK);
884}
virtual bool Flush() noexcept=0
virtual bool Write(const wxString &key, bool value)=0

References audacity::BasicSettings::Flush(), gPrefs, mEditTags, mGrid, mTags, TransferDataFromWindow(), audacity::BasicSettings::Write(), and wxT().

Here is the call graph for this function:

◆ OnPrev()

void TagsEditorDialog::OnPrev ( wxCommandEvent &  )
private

Definition at line 946 of file TagsEditor.cpp.

947{
948 if(mSelectedIndex == 0)
949 return;
950
954}

References mSelectedIndex, TransferDataFromWindow(), and TransferDataToWindow().

Here is the call graph for this function:

◆ OnRemove()

void TagsEditorDialog::OnRemove ( wxCommandEvent &  event)
private

Definition at line 835 of file TagsEditor.cpp.

836{
837 size_t row = mGrid->GetGridCursorRow();
838
839 if (!mEditTitle &&
840 mGrid->GetCellValue(row, 0).CmpNoCase(LABEL_TITLE.Translation()) == 0) {
841 return;
842 }
843 else if (!mEditTrack &&
844 mGrid->GetCellValue(row, 0)
845 .CmpNoCase(LABEL_TRACK.Translation()) == 0) {
846 return;
847 }
848 else if (row < STATICCNT) {
849 mGrid->SetCellValue(row, 1, wxEmptyString);
850 }
851 else if (row >= STATICCNT) {
852 mGrid->DeleteRows(row, 1);
853 }
854}
#define LABEL_TITLE
Definition: TagsEditor.cpp:146
#define STATICCNT
Definition: TagsEditor.cpp:179
#define LABEL_TRACK
Definition: TagsEditor.cpp:148

References LABEL_TITLE, LABEL_TRACK, mEditTitle, mEditTrack, mGrid, and STATICCNT.

◆ OnReset()

void TagsEditorDialog::OnReset ( wxCommandEvent &  event)
private

Definition at line 616 of file TagsEditor.cpp.

617{
618 int id = AudacityMessageBox(
619 XO("Are you sure you want to reset the genre list to defaults?"),
620 XO("Reset Genres"),
621 wxYES_NO);
622
623 if (id == wxNO) {
624 return;
625 }
626 auto& local = *mEditTags[mSelectedIndex];
627
628 local.LoadDefaultGenres();
629
630 wxFileName fn(FileNames::DataDir(), wxT("genres.txt"));
631 wxTextFile tf(fn.GetFullPath());
632
633 bool open = (tf.Exists() && tf.Open()) ||
634 (!tf.Exists() && tf.Create());
635
636 if (!open) {
638 XO("Unable to open genre file."),
639 XO("Reset Genres") );
640 local.LoadGenres();
641 return;
642 }
643
644 tf.Clear();
645 int cnt = local.GetNumUserGenres();
646 for (int i = 0; i < cnt; i++) {
647 tf.AddLine(local.GetUserGenre(i));
648 }
649
650 if (!tf.Write()) {
652 XO("Unable to save genre file."),
653 XO("Reset Genres") );
654 local.LoadGenres();
655 return;
656 }
657
658 local.LoadGenres();
659
661}

References AudacityMessageBox(), FileNames::DataDir(), fn, mEditTags, mSelectedIndex, PopulateGenres(), wxT(), and XO().

Here is the call graph for this function:

◆ OnSave()

void TagsEditorDialog::OnSave ( wxCommandEvent &  event)
private

Definition at line 727 of file TagsEditor.cpp.

728{
729 wxString fn;
730
731 auto& local = *mEditTags[mSelectedIndex];
732
733 // Refresh tags
735
736 // Ask the user for the real name
737 fn = SelectFile(FileNames::Operation::_None,
738 XO("Save Metadata As:"),
740 wxT("Tags.xml"),
741 wxT("xml"),
743 wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER,
744 this);
745
746 // User canceled...
747 if (fn.empty()) {
748 return;
749 }
750
751 GuardedCall( [&] {
752 // Create/Open the file
753 XMLFileWriter writer{ fn, XO("Error Saving Tags File") };
754
755 // Remember title and track in case they're read only
756 wxString title = local.GetTag(TAG_TITLE);
757 wxString track = local.GetTag(TAG_TRACK);
758
759 // Clear title
760 if (!mEditTitle) {
761 local.SetTag(TAG_TITLE, wxEmptyString);
762 }
763
764 // Clear track
765 if (!mEditTrack) {
766 local.SetTag(TAG_TRACK, wxEmptyString);
767 }
768
769 auto cleanup = finally( [&] {
770 // Restore title
771 if (!mEditTitle) {
772 local.SetTag(TAG_TITLE, title);
773 }
774
775 // Restore track
776 if (!mEditTrack) {
777 local.SetTag(TAG_TRACK, track);
778 }
779 } );
780
781 // Write the metadata
782 local.WriteXML(writer);
783
784 writer.Commit();
785 } );
786}
R GuardedCall(const F1 &body, const F2 &handler=F2::Default(), F3 delayedHandler=DefaultDelayedHandlerAction) noexcept(noexcept(handler(std::declval< AudacityException * >())) &&noexcept(handler(nullptr)) &&noexcept(std::function< void(AudacityException *)>{std::move(delayedHandler)}))
Execute some code on any thread; catch any AudacityException; enqueue error report on the main thread...
Wrapper to output XML data to files.
Definition: XMLWriter.h:84

References FileNames::DataDir(), fn, GuardedCall(), mEditTags, mEditTitle, mEditTrack, mSelectedIndex, SelectFile(), TAG_TITLE, TAG_TRACK, title, TransferDataFromWindow(), wxT(), FileNames::XMLFiles, and XO().

Here is the call graph for this function:

◆ OnSaveDefaults()

void TagsEditorDialog::OnSaveDefaults ( wxCommandEvent &  event)
private

Definition at line 788 of file TagsEditor.cpp.

789{
790 auto& local = *mEditTags[mSelectedIndex];
791 // Refresh tags
793
794 // Remember title and track in case they're read only
795 wxString title = local.GetTag(TAG_TITLE);
796 wxString track = local.GetTag(TAG_TRACK);
797
798 // Clear title
799 if (!mEditTitle) {
800 local.SetTag(TAG_TITLE, wxEmptyString);
801 }
802
803 // Clear track
804 if (!mEditTrack) {
805 local.SetTag(TAG_TRACK, wxEmptyString);
806 }
807
808 // Remove any previous defaults
809 gPrefs->DeleteGroup(wxT("/Tags"));
810
811 // Write out each tag
812 for (const auto &pair : local.GetRange()) {
813 const auto &n = pair.first;
814 const auto &v = pair.second;
815 gPrefs->Write(wxT("/Tags/") + n, v);
816 }
817 gPrefs->Flush();
818
819 // Restore title
820 if (!mEditTitle) {
821 local.SetTag(TAG_TITLE, title);
822 }
823
824 // Restore track
825 if (!mEditTrack) {
826 local.SetTag(TAG_TRACK, track);
827 }
828}
bool DeleteGroup(const wxString &key)
Deletes specified group if exists.

References audacity::BasicSettings::DeleteGroup(), audacity::BasicSettings::Flush(), gPrefs, mEditTags, mEditTitle, mEditTrack, mSelectedIndex, TAG_TITLE, TAG_TRACK, title, TransferDataFromWindow(), audacity::BasicSettings::Write(), and wxT().

Here is the call graph for this function:

◆ PopulateGenres()

void TagsEditorDialog::PopulateGenres ( )
private

Definition at line 956 of file TagsEditor.cpp.

957{
958 auto local = *mEditTags[mSelectedIndex];
959
960 int cnt = local.GetNumUserGenres();
961 int i;
962 wxString parm;
963 wxArrayString g;
964
965 for (i = 0; i < cnt; i++) {
966 g.push_back(local.GetUserGenre(i));
967 }
968 std::sort( g.begin(), g.end() );
969
970 for (i = 0; i < cnt; i++) {
971 parm = parm + (i == 0 ? wxT("") : wxT(",")) + g[i];
972 }
973
974 // Here was a memory leak! wxWidgets docs for wxGrid::GetDefaultEditorForType() say:
975 // "The caller must call DecRef() on the returned pointer."
976 auto editor = mGrid->GetDefaultEditorForType(wxT("Combo"));
977 editor->SetParameters(parm);
978 editor->DecRef();
979}

References mEditTags, mGrid, mSelectedIndex, and wxT().

Referenced by OnEdit(), and OnReset().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ PopulateOrExchange()

void TagsEditorDialog::PopulateOrExchange ( ShuttleGui S)

Definition at line 298 of file TagsEditor.cpp.

299{
300 S.StartVerticalLay();
301 {
302 S.StartHorizontalLay(wxALIGN_LEFT, 0);
303 {
304 S.AddUnits(XO("Use arrow keys (or ENTER key after editing) to navigate fields."));
305 }
306 S.EndHorizontalLay();
307
308 if(mTags.size() > 1)
309 {
310 S.StartHorizontalLay(wxEXPAND, 0);
311 {
312 mPrev = S.Id(PrevID).Style(wxBU_EXACTFIT).AddButton(TranslatableString("<", {}));
313 mName = S.Style(wxALIGN_CENTER).AddVariableText({}, true, wxEXPAND);
314 mNext = S.Id(NextID).Style(wxBU_EXACTFIT).AddButton(TranslatableString(">", {}));
315 }
316 S.EndHorizontalLay();
317 }
318
319 if (mGrid == NULL) {
321 wxID_ANY,
322 wxDefaultPosition,
323 wxDefaultSize,
324 wxSUNKEN_BORDER);
325
326 mGrid->RegisterDataType(wxT("Combo"),
327 (mStringRenderer = safenew wxGridCellStringRenderer),
328 (mComboEditor = safenew ComboEditor(wxArrayString(), true)));
329
330 mGrid->SetColLabelSize(mGrid->GetDefaultRowSize());
331
332 auto cs = transform_container<wxArrayStringEx>(
333 names, std::mem_fn( &TranslatableString::Translation ) );
334
335 // Build the initial (empty) grid
336 mGrid->CreateGrid(0, 2, wxGrid::wxGridSelectRows);
337 mGrid->SetRowLabelSize(0);
338 mGrid->SetDefaultCellAlignment(wxALIGN_LEFT, wxALIGN_CENTER);
339 mGrid->SetColLabelValue(0, _("Tag"));
340 mGrid->SetColLabelValue(1, _("Value"));
341
342 // Resize the name column and set default row height.
343 wxComboBox tc(this, wxID_ANY, wxT(""), wxDefaultPosition, wxDefaultSize, cs);
344 mGrid->SetColSize(0, tc.GetSize().x);
345 mGrid->SetColMinimalWidth(0, tc.GetSize().x);
346 }
347 S.Prop(1)
348 .Position(wxEXPAND | wxALL)
349 .AddWindow(mGrid);
350
351 S.StartMultiColumn(4, wxALIGN_CENTER);
352 {
353 S.Id(AddID).AddButton(XXO("&Add"));
354 S.Id(RemoveID).AddButton(XXO("&Remove"));
355 S.AddTitle( {} );
356 S.Id(ClearID).AddButton(XXO("Cl&ear"));
357 }
358 S.EndMultiColumn();
359
360 S.StartHorizontalLay(wxALIGN_CENTRE, 0);
361 {
362 S.StartStatic(XO("Genres"));
363 {
364 S.StartMultiColumn(4, wxALIGN_CENTER);
365 {
366 S.Id(EditID).AddButton(XXO("E&dit..."));
367 S.Id(ResetID).AddButton(XXO("Rese&t..."));
368 }
369 S.EndMultiColumn();
370 }
371 S.EndStatic();
372 S.StartStatic(XO("Template"));
373 {
374 S.StartMultiColumn(4, wxALIGN_CENTER);
375 {
376 S.Id(LoadID).AddButton(XXO("&Load..."));
377 S.Id(SaveID).AddButton(XXO("&Save..."));
378 S.AddTitle( {} );
379 S.Id(SaveDefaultsID).AddButton(XXO("Set De&fault"));
380 }
381 S.EndMultiColumn();
382 }
383 S.EndStatic();
384 }
385 S.EndHorizontalLay();
386 }
387 S.EndVerticalLay();
388
389 S.AddStandardButtons(eOkButton | eCancelButton | eHelpButton);
390}
XXO("&Cut/Copy/Paste Toolbar")
#define _(s)
Definition: Internat.h:73
#define safenew
Definition: MemoryX.h:9
@ eOkButton
Definition: ShuttleGui.h:609
@ eCancelButton
Definition: ShuttleGui.h:610
@ eHelpButton
Definition: ShuttleGui.h:613
@ SaveDefaultsID
Definition: TagsEditor.cpp:189
@ ClearID
Definition: TagsEditor.cpp:182
@ RemoveID
Definition: TagsEditor.cpp:191
@ PrevID
Definition: TagsEditor.cpp:183
@ LoadID
Definition: TagsEditor.cpp:187
@ ResetID
Definition: TagsEditor.cpp:186
@ SaveID
Definition: TagsEditor.cpp:188
@ EditID
Definition: TagsEditor.cpp:185
@ NextID
Definition: TagsEditor.cpp:184
@ AddID
Definition: TagsEditor.cpp:190
static FormatterContext EmptyContext()
Supplies an accessible grid based on wxGrid.
Definition: Grid.h:190
wxButton * mPrev
Definition: TagsEditor.h:95
wxGridCellStringRenderer * mStringRenderer
Definition: TagsEditor.h:97
wxStaticText * mName
Definition: TagsEditor.h:93
ComboEditor * mComboEditor
Definition: TagsEditor.h:96
wxButton * mNext
Definition: TagsEditor.h:94
Holds a msgid for the translation catalog; may also bind format arguments.
wxString Translation() const

References _, AddID, ClearID, eCancelButton, EditID, eHelpButton, FormatterContext::EmptyContext(), eOkButton, LoadID, mComboEditor, mGrid, mName, mNext, mPrev, mStringRenderer, mTags, names, NextID, PrevID, RemoveID, ResetID, S, safenew, SaveDefaultsID, SaveID, TranslatableString::Translation(), wxT(), XO(), and XXO().

Here is the call graph for this function:

◆ SetEditors()

void TagsEditorDialog::SetEditors ( )
private

Definition at line 920 of file TagsEditor.cpp.

921{
922 int cnt = mGrid->GetNumberRows();
923
924 for (int i = 0; i < cnt; i++) {
925 wxString label = mGrid->GetCellValue(i, 0);
926 if (label.CmpNoCase(LABEL_GENRE.Translation()) == 0) {
927 // This use of GetDefaultEditorForType does not require DecRef.
928 mGrid->SetCellEditor(i, 1, mGrid->GetDefaultEditorForType(wxT("Combo")));
929 }
930 else {
931 mGrid->SetCellEditor(i, 1, NULL); //mGrid->GetDefaultEditor());
932 }
933 }
934}
TranslatableString label
Definition: TagsEditor.cpp:165
#define LABEL_GENRE
Definition: TagsEditor.cpp:150

References label, LABEL_GENRE, mGrid, and wxT().

Referenced by TransferDataToWindow().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ ShowEditDialog()

bool TagsEditorDialog::ShowEditDialog ( Tags tags,
wxWindow *  parent,
const TranslatableString title 
)
static

Definition at line 30 of file TagsEditor.cpp.

31{
32 TagsEditorDialog dlg(parent, title, { &tags }, {}, true, true);
33 return dlg.ShowModal() == wxID_OK;
34}
Derived from ExpandingToolBar, this dialog allows editing of Tags.
Definition: TagsEditor.h:22

References title.

Referenced by EditProjectMetadata().

Here is the caller graph for this function:

◆ TransferDataFromWindow()

bool TagsEditorDialog::TransferDataFromWindow ( )
override

Definition at line 397 of file TagsEditor.cpp.

398{
399 int i, cnt = mGrid->GetNumberRows();
400
401 if (mGrid->IsCellEditControlShown()) {
402 mGrid->SaveEditControlValue();
403 mGrid->HideCellEditControl();
404 }
405
406 auto& local = *mEditTags[mSelectedIndex];
407
408 local.Clear();
409 for (i = 0; i < cnt; i++) {
410 // Get tag name from the grid
411
412 auto n = mGrid->GetCellValue(i, 0);
413 wxString v = mGrid->GetCellValue(i, 1);
414
415 if (n.empty()) {
416 continue;
417 }
418
419 bool bSpecialTag = true;
420
421 // Map special tag names back to internal keys
422 if (n.CmpNoCase(LABEL_ARTIST.Translation()) == 0) {
423 n = TAG_ARTIST;
424 }
425 else if (n.CmpNoCase(LABEL_TITLE.Translation()) == 0) {
426 n = TAG_TITLE;
427 }
428 else if (n.CmpNoCase(LABEL_ALBUM.Translation()) == 0) {
429 n = TAG_ALBUM;
430 }
431 else if (n.CmpNoCase(LABEL_TRACK.Translation()) == 0) {
432 n = TAG_TRACK;
433 }
434 else if (n.CmpNoCase(LABEL_YEAR.Translation()) == 0) {
435 n = TAG_YEAR;
436 }
437 else if (n.CmpNoCase(LABEL_GENRE.Translation()) == 0) {
438 n = TAG_GENRE;
439 }
440 else if (n.CmpNoCase(LABEL_COMMENTS.Translation()) == 0) {
441 n = TAG_COMMENTS;
442 }
443 else {
444 bSpecialTag = false;
445 }
446
447 local.SetTag(n, v, bSpecialTag);
448 }
449
450 return true;
451}
#define TAG_COMMENTS
Definition: Tags.h:64
#define TAG_GENRE
Definition: Tags.h:63
#define TAG_ALBUM
Definition: Tags.h:60
#define TAG_YEAR
Definition: Tags.h:62
#define TAG_ARTIST
Definition: Tags.h:59
#define LABEL_COMMENTS
Definition: TagsEditor.cpp:151
#define LABEL_YEAR
Definition: TagsEditor.cpp:149
#define LABEL_ARTIST
Definition: TagsEditor.cpp:145
#define LABEL_ALBUM
Definition: TagsEditor.cpp:147

References LABEL_ALBUM, LABEL_ARTIST, LABEL_COMMENTS, LABEL_GENRE, LABEL_TITLE, LABEL_TRACK, LABEL_YEAR, mEditTags, mGrid, mSelectedIndex, TAG_ALBUM, TAG_ARTIST, TAG_COMMENTS, TAG_GENRE, TAG_TITLE, TAG_TRACK, and TAG_YEAR.

Referenced by OnNext(), OnOk(), OnPrev(), OnSave(), and OnSaveDefaults().

Here is the caller graph for this function:

◆ TransferDataToWindow()

bool TagsEditorDialog::TransferDataToWindow ( )
override

Definition at line 453 of file TagsEditor.cpp.

454{
455 size_t i;
456 TagMap popTagMap;
457
458 auto& local = *mEditTags[mSelectedIndex];
459
460 if(mName)
461 {
462 mPrev->Enable(mSelectedIndex > 0);
463 mName->SetLabel(mNames[mSelectedIndex]);
464 mNext->Enable(mSelectedIndex < mNames.size() - 1);
465 }
466
467 // Disable redrawing until we're done
468 mGrid->BeginBatch();
469
470 // Delete all rows
471 if (mGrid->GetNumberRows()) {
472 mGrid->DeleteRows(0, mGrid->GetNumberRows());
473 }
474
475 // Populate the static rows
476 for (i = 0; i < STATICCNT; i++) {
477 mGrid->AppendRows();
478
479 mGrid->SetReadOnly(i, 0);
480 // The special tag name that's displayed and translated may not match
481 // the key string used for internal lookup.
482 mGrid->SetCellValue(i, 0, labelmap[i].label.Translation() );
483 mGrid->SetCellValue(i, 1, local.GetTag(labelmap[i].name));
484
485 if (!mEditTitle &&
486 mGrid->GetCellValue(i, 0).CmpNoCase(LABEL_TITLE.Translation()) == 0) {
487 mGrid->SetReadOnly(i, 1);
488 }
489
490 if (!mEditTrack &&
491 mGrid->GetCellValue(i, 0).CmpNoCase(LABEL_TRACK.Translation()) == 0) {
492 mGrid->SetReadOnly(i, 1);
493 }
494
495 popTagMap[ labelmap[i].name ] = mGrid->GetCellValue(i, 1);
496 }
497
498 // Populate the rest
499 for (const auto &pair : local.GetRange()) {
500 const auto &n = pair.first;
501 const auto &v = pair.second;
502 if (popTagMap.find(n) == popTagMap.end()) {
503 mGrid->AppendRows();
504 mGrid->SetCellValue(i, 0, n);
505 mGrid->SetCellValue(i, 1, v);
506 i++;
507 }
508 }
509
510 // Add an extra one to help with initial sizing and to show it can be done
511 mGrid->AppendRows(1);
512
513 // We're done, so allow the grid to redraw
514 mGrid->EndBatch();
515
516 // Set the editors
517 SetEditors();
518 Layout();
519 Fit();
520
521 return true;
522}
std::unordered_map< wxString, wxString > TagMap
Definition: Tags.h:56
static const struct @64 labelmap[]

References label, LABEL_TITLE, LABEL_TRACK, labelmap, mEditTags, mEditTitle, mEditTrack, mGrid, mName, mNames, mNext, mPrev, mSelectedIndex, SetEditors(), STATICCNT, and TranslatableString::Translation().

Referenced by OnClear(), OnLoad(), OnNext(), and OnPrev().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ mComboEditor

ComboEditor* TagsEditorDialog::mComboEditor
private

Definition at line 96 of file TagsEditor.h.

Referenced by PopulateOrExchange().

◆ mEditTags

std::vector<std::unique_ptr<Tags> > TagsEditorDialog::mEditTags
private

◆ mEditTitle

bool TagsEditorDialog::mEditTitle
private

Definition at line 87 of file TagsEditor.h.

Referenced by OnLoad(), OnRemove(), OnSave(), OnSaveDefaults(), and TransferDataToWindow().

◆ mEditTrack

bool TagsEditorDialog::mEditTrack
private

Definition at line 88 of file TagsEditor.h.

Referenced by OnLoad(), OnRemove(), OnSave(), OnSaveDefaults(), and TransferDataToWindow().

◆ mGrid

Grid* TagsEditorDialog::mGrid
private

◆ mName

wxStaticText* TagsEditorDialog::mName {}
private

Definition at line 93 of file TagsEditor.h.

Referenced by PopulateOrExchange(), and TransferDataToWindow().

◆ mNames

std::vector<wxString> TagsEditorDialog::mNames
private

Definition at line 84 of file TagsEditor.h.

Referenced by TransferDataToWindow().

◆ mNext

wxButton* TagsEditorDialog::mNext {}
private

Definition at line 94 of file TagsEditor.h.

Referenced by PopulateOrExchange(), and TransferDataToWindow().

◆ mPrev

wxButton* TagsEditorDialog::mPrev {}
private

Definition at line 95 of file TagsEditor.h.

Referenced by PopulateOrExchange(), and TransferDataToWindow().

◆ mSelectedIndex

int TagsEditorDialog::mSelectedIndex {0}
private

◆ mStringRenderer

wxGridCellStringRenderer* TagsEditorDialog::mStringRenderer
private

Definition at line 97 of file TagsEditor.h.

Referenced by PopulateOrExchange().

◆ mTags

std::vector<Tags*> TagsEditorDialog::mTags
private

Definition at line 83 of file TagsEditor.h.

Referenced by OnOk(), and PopulateOrExchange().


The documentation for this class was generated from the following files: