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 SetSizeHints(sz.x, std::min(sz.y, 600));
247
248 // Restore the original tags because TransferDataToWindow() will be called again
249 for(unsigned i = 0; i < mEditTags.size(); ++i)
250 {
251 mEditTags[i]->Clear();
252 *mEditTags[i] = *mTags[i];
253 }
254 // Override size and position with last saved
255 wxRect r = GetRect();
256 gPrefs->Read(wxT("/TagsEditorDialog/x"), &r.x, r.x);
257 gPrefs->Read(wxT("/TagsEditorDialog/y"), &r.y, r.y);
258 gPrefs->Read(wxT("/TagsEditorDialog/width"), &r.width, r.width);
259 gPrefs->Read(wxT("/TagsEditorDialog/height"), &r.height, r.height);
260 //On multi-monitor systems, there's a chance the last saved window position is
261 //on a monitor that has been removed or is unavailable.
262 if (IsWindowRectValid(&r))
263 Move(r.GetPosition());
264
265 SetSize(r.GetSize());
266 Layout();
267
268 // Resize value column width based on width of columns and the vertical scrollbar
269 wxScrollBar sb(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxSB_VERTICAL);
270 r = mGrid->GetClientRect();
271 r.width -= mGrid->GetColSize(0);
272 r.width -= sb.GetSize().GetWidth();
273 r.width -= 10;
274 r.width -= r.x;
275 mGrid->SetColSize(1, r.width);
276 //Bug 2038
277 mGrid->SetFocus();
278
279 // Load the genres
281}
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:630
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:979
void PopulateOrExchange(ShuttleGui &S)
Definition: TagsEditor.cpp:296
bool TransferDataToWindow() override
Definition: TagsEditor.cpp:451
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 283 of file TagsEditor.cpp.

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

Member Function Documentation

◆ DoCancel()

void TagsEditorDialog::DoCancel ( bool  escKey)
private

Definition at line 889 of file TagsEditor.cpp.

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

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 1001 of file TagsEditor.cpp.

1004{
1005 auto &tags = Tags::Get( project );
1006
1007 // Back up my tags
1008 // Tags (artist name, song properties, MP3 ID3 info, etc.)
1009 // The structure may be shared with undo history entries
1010 // To keep undo working correctly, always replace this with a NEW duplicate
1011 // BEFORE doing any editing of it!
1012 auto newTags = tags.Duplicate();
1013
1014 if (ShowEditDialog(*newTags, &GetProjectFrame( project ), title)) {
1015 if (tags != *newTags) {
1016 // Commit the change to project state only now.
1017 Tags::Set( project, newTags );
1018 ProjectHistory::Get( project ).PushState( title, shortUndoDescription);
1019 }
1020 return true;
1021 }
1022
1023 return false;
1024}
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 979 of file TagsEditor.cpp.

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

◆ OnAdd()

void TagsEditorDialog::OnAdd ( wxCommandEvent &  event)
private

Definition at line 828 of file TagsEditor.cpp.

829{
830 mGrid->AppendRows();
831}

References mGrid.

◆ OnCancel()

void TagsEditorDialog::OnCancel ( wxCommandEvent &  event)
private

Definition at line 884 of file TagsEditor.cpp.

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

References DoCancel().

Here is the call graph for this function:

◆ OnChange()

void TagsEditorDialog::OnChange ( wxGridEvent &  event)
private

Definition at line 522 of file TagsEditor.cpp.

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

662{
663 mEditTags[mSelectedIndex]->Clear();
664
666}

References mEditTags, mSelectedIndex, and TransferDataToWindow().

Here is the call graph for this function:

◆ OnEdit()

void TagsEditorDialog::OnEdit ( wxCommandEvent &  event)
private

Definition at line 559 of file TagsEditor.cpp.

560{
561 if (mGrid->IsCellEditControlShown()) {
562 mGrid->SaveEditControlValue();
563 mGrid->HideCellEditControl();
564 }
565
566 wxDialogWrapper dlg(this, wxID_ANY, XO("Edit Genres"),
567 wxDefaultPosition, wxDefaultSize,
568 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER);
569 dlg.SetName();
570 wxTextCtrl *tc;
571
572 ShuttleGui S(&dlg, eIsCreating);
573
574 S.StartVerticalLay(true);
575 {
576 tc = S.AddTextWindow(wxT(""));
577 }
578 S.EndVerticalLay();
579
580 S.AddStandardButtons();
581
582 auto& local = *mEditTags[mSelectedIndex];
583
584 wxArrayString g;
585 int cnt = local.GetNumUserGenres();
586 for (int i = 0; i < cnt; i++) {
587 g.push_back(local.GetUserGenre(i));
588 }
589 std::sort( g.begin(), g.end() );
590
591 for (int i = 0; i < cnt; i++) {
592 tc->AppendText(g[i] + wxT("\n"));
593 }
594
595 dlg.Center();
596 if (dlg.ShowModal() == wxID_CANCEL) {
597 return;
598 }
599
600 wxFileName fn(FileNames::DataDir(), wxT("genres.txt"));
601 wxFile f(fn.GetFullPath(), wxFile::write);
602 if (!f.IsOpened() || !f.Write(tc->GetValue())) {
604 XO("Unable to save genre file."),
605 XO("Reset Genres") );
606 return;
607 }
608
609 local.LoadGenres();
610
612}
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 390 of file TagsEditor.cpp.

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

References HelpSystem::ShowHelp().

Here is the call graph for this function:

◆ OnKeyDown()

void TagsEditorDialog::OnKeyDown ( wxKeyEvent &  event)
private

Definition at line 910 of file TagsEditor.cpp.

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

References DoCancel().

Here is the call graph for this function:

◆ OnLoad()

void TagsEditorDialog::OnLoad ( wxCommandEvent &  event)
private

Definition at line 668 of file TagsEditor.cpp.

669{
670 wxString fn;
671
672 auto& local = *mEditTags[mSelectedIndex];
673
674 // Ask the user for the real name
675 fn = SelectFile(FileNames::Operation::_None,
676 XO("Load Metadata As:"),
678 wxT("Tags.xml"),
679 wxT("xml"),
681 wxFD_OPEN | wxRESIZE_BORDER,
682 this);
683
684 // User canceled...
685 if (fn.empty()) {
686 return;
687 }
688
689 // Load the metadata
690 Tags temp;
691 XMLFileReader reader;
692 if (!reader.Parse(&temp, fn)) {
693 // Inform user of load failure
695 reader.GetErrorStr(),
696 XO("Error Loading Metadata"),
697 wxOK | wxCENTRE,
698 this);
699 return;
700 }
701
702 // Remember title and track in case they're read only
703 wxString title = local.GetTag(TAG_TITLE);
704 wxString track = local.GetTag(TAG_TRACK);
705
706 // Replace existing tags with loaded ones
707 local = temp;
708
709 // Restore title
710 if (!mEditTitle) {
711 local.SetTag(TAG_TITLE, title);
712 }
713
714 // Restore track
715 if (!mEditTrack) {
716 local.SetTag(TAG_TRACK, track);
717 }
718
719 // Go fill up the window
721
722 return;
723}
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:436
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 934 of file TagsEditor.cpp.

935{
936 if(mSelectedIndex == static_cast<int>(mEditTags.size() - 1))
937 return;
938
942}
bool TransferDataFromWindow() override
Definition: TagsEditor.cpp:395

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

Here is the call graph for this function:

◆ OnOk()

void TagsEditorDialog::OnOk ( wxCommandEvent &  event)
private

Definition at line 854 of file TagsEditor.cpp.

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

945{
946 if(mSelectedIndex == 0)
947 return;
948
952}

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

Here is the call graph for this function:

◆ OnRemove()

void TagsEditorDialog::OnRemove ( wxCommandEvent &  event)
private

Definition at line 833 of file TagsEditor.cpp.

834{
835 size_t row = mGrid->GetGridCursorRow();
836
837 if (!mEditTitle &&
838 mGrid->GetCellValue(row, 0).CmpNoCase(LABEL_TITLE.Translation()) == 0) {
839 return;
840 }
841 else if (!mEditTrack &&
842 mGrid->GetCellValue(row, 0)
843 .CmpNoCase(LABEL_TRACK.Translation()) == 0) {
844 return;
845 }
846 else if (row < STATICCNT) {
847 mGrid->SetCellValue(row, 1, wxEmptyString);
848 }
849 else if (row >= STATICCNT) {
850 mGrid->DeleteRows(row, 1);
851 }
852}
#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 614 of file TagsEditor.cpp.

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

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 725 of file TagsEditor.cpp.

726{
727 wxString fn;
728
729 auto& local = *mEditTags[mSelectedIndex];
730
731 // Refresh tags
733
734 // Ask the user for the real name
735 fn = SelectFile(FileNames::Operation::_None,
736 XO("Save Metadata As:"),
738 wxT("Tags.xml"),
739 wxT("xml"),
741 wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER,
742 this);
743
744 // User canceled...
745 if (fn.empty()) {
746 return;
747 }
748
749 GuardedCall( [&] {
750 // Create/Open the file
751 XMLFileWriter writer{ fn, XO("Error Saving Tags File") };
752
753 // Remember title and track in case they're read only
754 wxString title = local.GetTag(TAG_TITLE);
755 wxString track = local.GetTag(TAG_TRACK);
756
757 // Clear title
758 if (!mEditTitle) {
759 local.SetTag(TAG_TITLE, wxEmptyString);
760 }
761
762 // Clear track
763 if (!mEditTrack) {
764 local.SetTag(TAG_TRACK, wxEmptyString);
765 }
766
767 auto cleanup = finally( [&] {
768 // Restore title
769 if (!mEditTitle) {
770 local.SetTag(TAG_TITLE, title);
771 }
772
773 // Restore track
774 if (!mEditTrack) {
775 local.SetTag(TAG_TRACK, track);
776 }
777 } );
778
779 // Write the metadata
780 local.WriteXML(writer);
781
782 writer.Commit();
783 } );
784}
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 786 of file TagsEditor.cpp.

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

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

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 296 of file TagsEditor.cpp.

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

919{
920 int cnt = mGrid->GetNumberRows();
921
922 for (int i = 0; i < cnt; i++) {
923 wxString label = mGrid->GetCellValue(i, 0);
924 if (label.CmpNoCase(LABEL_GENRE.Translation()) == 0) {
925 // This use of GetDefaultEditorForType does not require DecRef.
926 mGrid->SetCellEditor(i, 1, mGrid->GetDefaultEditorForType(wxT("Combo")));
927 }
928 else {
929 mGrid->SetCellEditor(i, 1, NULL); //mGrid->GetDefaultEditor());
930 }
931 }
932}
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 395 of file TagsEditor.cpp.

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

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