set SIGWINCH handler before initializing ncurses to avoid races
[ncmpcpp.git] / src / tiny_tag_editor.cpp
blob8719bda8dc689e9171f0b4dca13976b3e73d79f2
1 /***************************************************************************
2 * Copyright (C) 2008-2014 by Andrzej Rybczak *
3 * electricityispower@gmail.com *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 * *
10 * This program is distributed in the hope that it will be useful, *
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13 * GNU General Public License for more details. *
14 * *
15 * You should have received a copy of the GNU General Public License *
16 * along with this program; if not, write to the *
17 * Free Software Foundation, Inc., *
18 * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. *
19 ***************************************************************************/
21 #include "tiny_tag_editor.h"
23 #ifdef HAVE_TAGLIB_H
25 #include <boost/locale/conversion.hpp>
27 // taglib includes
28 #include <fileref.h>
29 #include <tag.h>
31 #include "browser.h"
32 #include "charset.h"
33 #include "display.h"
34 #include "helpers.h"
35 #include "global.h"
36 #include "song_info.h"
37 #include "playlist.h"
38 #include "search_engine.h"
39 #include "statusbar.h"
40 #include "tag_editor.h"
41 #include "title.h"
42 #include "tags.h"
43 #include "screen_switcher.h"
44 #include "utility/string.h"
46 using Global::MainHeight;
47 using Global::MainStartY;
49 TinyTagEditor *myTinyTagEditor;
51 TinyTagEditor::TinyTagEditor()
52 : Screen(NC::Menu<NC::Buffer>(0, MainStartY, COLS, MainHeight, "", Config.main_color, NC::Border::None))
54 w.setHighlightColor(Config.main_highlight_color);
55 w.cyclicScrolling(Config.use_cyclic_scrolling);
56 w.centeredCursor(Config.centered_cursor);
57 w.setItemDisplayer([](NC::Menu<NC::Buffer> &menu) {
58 menu << menu.drawn()->value();
59 });
62 void TinyTagEditor::resize()
64 size_t x_offset, width;
65 getWindowResizeParams(x_offset, width);
66 w.resize(width, MainHeight);
67 w.moveTo(x_offset, MainStartY);
68 hasToBeResized = 0;
71 void TinyTagEditor::switchTo()
73 using Global::myScreen;
74 if (itsEdited.isStream())
76 Statusbar::print("Streams can't be edited");
78 else if (getTags())
80 m_previous_screen = myScreen;
81 SwitchTo::execute(this);
82 drawHeader();
84 else
86 std::string full_path;
87 if (itsEdited.isFromDatabase())
88 full_path += Config.mpd_music_dir;
89 full_path += itsEdited.getURI();
91 const char msg[] = "Couldn't read file \"%1%\"";
92 Statusbar::printf(msg, wideShorten(full_path, COLS-const_strlen(msg)));
96 std::wstring TinyTagEditor::title()
98 return L"Tiny tag editor";
101 void TinyTagEditor::enterPressed()
103 size_t option = w.choice();
104 Statusbar::lock();
105 if (option < 19) // separator after comment
107 size_t pos = option-8;
108 Statusbar::put() << NC::Format::Bold << SongInfo::Tags[pos].Name << ": " << NC::Format::NoBold;
109 itsEdited.setTags(SongInfo::Tags[pos].Set, Global::wFooter->getString(
110 itsEdited.getTags(SongInfo::Tags[pos].Get, Config.tags_separator)), Config.tags_separator);
111 w.at(option).value().clear();
112 w.at(option).value() << NC::Format::Bold << SongInfo::Tags[pos].Name << ':' << NC::Format::NoBold << ' ';
113 ShowTag(w.at(option).value(), itsEdited.getTags(SongInfo::Tags[pos].Get, Config.tags_separator));
115 else if (option == 20)
117 Statusbar::put() << NC::Format::Bold << "Filename: " << NC::Format::NoBold;
118 std::string filename = itsEdited.getNewName().empty() ? itsEdited.getName() : itsEdited.getNewName();
119 size_t dot = filename.rfind(".");
120 std::string extension = filename.substr(dot);
121 filename = filename.substr(0, dot);
122 std::string new_name = Global::wFooter->getString(filename);
123 if (!new_name.empty())
125 itsEdited.setNewName(new_name + extension);
126 w.at(option).value().clear();
127 w.at(option).value() << NC::Format::Bold << "Filename:" << NC::Format::NoBold << ' ' << (itsEdited.getNewName().empty() ? itsEdited.getName() : itsEdited.getNewName());
130 Statusbar::unlock();
132 if (option == 22)
134 Statusbar::print("Updating tags...");
135 if (Tags::write(itsEdited))
137 Statusbar::print("Tags updated");
138 if (itsEdited.isFromDatabase())
139 Mpd.UpdateDirectory(itsEdited.getDirectory());
140 else
142 if (m_previous_screen == myPlaylist)
143 myPlaylist->main().current().value() = itsEdited;
144 else if (m_previous_screen == myBrowser)
145 myBrowser->GetDirectory(myBrowser->CurrentDir());
148 else
149 Statusbar::print("Error while writing tags");
151 if (option > 21)
152 m_previous_screen->switchTo();
155 void TinyTagEditor::mouseButtonPressed(MEVENT me)
157 if (w.empty() || !w.hasCoords(me.x, me.y) || size_t(me.y) >= w.size())
158 return;
159 if (me.bstate & (BUTTON1_PRESSED | BUTTON3_PRESSED))
161 if (!w.Goto(me.y))
162 return;
163 if (me.bstate & BUTTON3_PRESSED)
165 w.refresh();
166 enterPressed();
169 else
170 Screen<WindowType>::mouseButtonPressed(me);
173 void TinyTagEditor::SetEdited(const MPD::Song &s)
175 if (auto ms = dynamic_cast<const MPD::MutableSong *>(&s))
176 itsEdited = *ms;
177 else
178 itsEdited = s;
181 bool TinyTagEditor::getTags()
183 std::string path_to_file;
184 if (itsEdited.isFromDatabase())
185 path_to_file += Config.mpd_music_dir;
186 path_to_file += itsEdited.getURI();
188 TagLib::FileRef f(path_to_file.c_str());
189 if (f.isNull())
190 return false;
192 std::string ext = itsEdited.getURI();
193 ext = boost::locale::to_lower(ext.substr(ext.rfind(".")+1));
195 w.clear();
196 w.reset();
198 w.resizeList(24);
200 for (size_t i = 0; i < 7; ++i)
201 w.at(i).setInactive(true);
203 w.at(7).setSeparator(true);
204 w.at(19).setSeparator(true);
205 w.at(21).setSeparator(true);
207 if (!Tags::extendedSetSupported(f.file()))
209 w.at(10).setInactive(true);
210 for (size_t i = 15; i <= 17; ++i)
211 w.at(i).setInactive(true);
214 w.highlight(8);
216 w.at(0).value() << NC::Format::Bold << Config.color1 << "Song name: " << NC::Format::NoBold << Config.color2 << itsEdited.getName() << NC::Color::End;
217 w.at(1).value() << NC::Format::Bold << Config.color1 << "Location in DB: " << NC::Format::NoBold << Config.color2;
218 ShowTag(w.at(1).value(), itsEdited.getDirectory());
219 w.at(1).value() << NC::Color::End;
220 w.at(3).value() << NC::Format::Bold << Config.color1 << "Length: " << NC::Format::NoBold << Config.color2 << itsEdited.getLength() << NC::Color::End;
221 w.at(4).value() << NC::Format::Bold << Config.color1 << "Bitrate: " << NC::Format::NoBold << Config.color2 << f.audioProperties()->bitrate() << " kbps" << NC::Color::End;
222 w.at(5).value() << NC::Format::Bold << Config.color1 << "Sample rate: " << NC::Format::NoBold << Config.color2 << f.audioProperties()->sampleRate() << " Hz" << NC::Color::End;
223 w.at(6).value() << NC::Format::Bold << Config.color1 << "Channels: " << NC::Format::NoBold << Config.color2 << (f.audioProperties()->channels() == 1 ? "Mono" : "Stereo") << NC::Color::Default;
225 unsigned pos = 8;
226 for (const SongInfo::Metadata *m = SongInfo::Tags; m->Name; ++m, ++pos)
228 w.at(pos).value() << NC::Format::Bold << m->Name << ":" << NC::Format::NoBold << ' ';
229 ShowTag(w.at(pos).value(), itsEdited.getTags(m->Get, Config.tags_separator));
232 w.at(20).value() << NC::Format::Bold << "Filename:" << NC::Format::NoBold << ' ' << itsEdited.getName();
234 w.at(22).value() << "Save";
235 w.at(23).value() << "Cancel";
236 return true;
239 #endif // HAVE_TAGLIB_H