1 /* TagFromPath.cpp */
2 
3 /* Copyright (C) 2011-2020 Michael Lugmair (Lucio Carreras)
4  *
5  * This file is part of sayonara player
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11 
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16 
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "GUI_TagFromPath.h"
22 
23 #include "Gui/TagEdit/ui_GUI_TagFromPath.h"
24 #include "Utils/Tagging/Tagging.h"
25 #include "Utils/Message/Message.h"
26 #include "Utils/Language/Language.h"
27 
28 #include <QDesktopServices>
29 #include <QMap>
30 
31 using namespace Tagging;
32 
33 struct GUI_TagFromPath::Private
34 {
35 	QString currentFilepath;
36 	QMap<TagName, ReplacedString> tagReplaceStringMap;
37 };
38 
GUI_TagFromPath(QWidget * parent)39 GUI_TagFromPath::GUI_TagFromPath(QWidget* parent) :
40 	Gui::Widget(parent)
41 {
42 	m = Pimpl::make<Private>();
43 
44 	ui = new Ui::GUI_TagFromPath();
45 	ui->setupUi(this);
46 
47 	showErrorFrame(false);
48 
49 	connect(ui->btnApplyTag, &QPushButton::clicked, this, [&]() {
50 		clearInvalidFilepaths();
51 		emit sigApply();
52 	});
53 
54 	connect(ui->btnApplyTagAll, &QPushButton::clicked, this, [&]() {
55 		clearInvalidFilepaths();
56 		emit sigApplyAll();
57 	});
58 
59 	connect(ui->leTag, &QLineEdit::textChanged, this, &GUI_TagFromPath::tagTextChanged);
60 	connect(ui->btnTagHelp, &QPushButton::clicked, this, &GUI_TagFromPath::btnTagHelpClicked);
61 
62 	initButtons();
63 	languageChanged();
64 }
65 
66 GUI_TagFromPath::~GUI_TagFromPath() = default;
67 
initButtons()68 void GUI_TagFromPath::initButtons()
69 {
70 	connect(ui->btnTitle, &QPushButton::toggled, this, [&](const auto b) {
71 		btnChecked(ui->btnTitle, b, TagTitle);
72 	});
73 
74 	connect(ui->btnArtist, &QPushButton::toggled, this, [&](const auto b) {
75 		btnChecked(ui->btnArtist, b, TagArtist);
76 	});
77 
78 	connect(ui->btnAlbum, &QPushButton::toggled, this, [&](const auto b) {
79 		btnChecked(ui->btnAlbum, b, TagAlbum);
80 	});
81 
82 	connect(ui->btnTrackNumber, &QPushButton::toggled, this, [&](const auto b) {
83 		btnChecked(ui->btnTrackNumber, b, TagTrackNum);
84 	});
85 
86 	connect(ui->btnYear, &QPushButton::toggled, this, [&](const auto b) {
87 		btnChecked(ui->btnYear, b, TagYear);
88 	});
89 
90 	connect(ui->btnDiscNumber, &QPushButton::toggled, this, [&](const auto b) {
91 		btnChecked(ui->btnDiscNumber, b, TagDisc);
92 	});
93 }
94 
checkIfAnyButtonIsChecked() const95 bool GUI_TagFromPath::checkIfAnyButtonIsChecked() const
96 {
97 	return (ui->btnAlbum->isChecked() ||
98 	        ui->btnArtist->isChecked() ||
99 	        ui->btnTitle->isChecked() ||
100 	        ui->btnYear->isChecked() ||
101 	        ui->btnDiscNumber->isChecked() ||
102 	        ui->btnTrackNumber->isChecked());
103 }
104 
setFilepath(const QString & filepath)105 void GUI_TagFromPath::setFilepath(const QString& filepath)
106 {
107 	m->currentFilepath = filepath;
108 
109 	if(ui->leTag->text().isEmpty() || !checkIfAnyButtonIsChecked())
110 	{
111 		ui->leTag->setText(filepath);
112 	}
113 
114 	const auto expression = Tagging::Expression(ui->leTag->text(), filepath);
115 	setTagColors(expression.isValid());
116 
117 	const auto tagType = Tagging::getTagType(filepath);
118 	const auto tagTypeString = Tagging::tagTypeToString(tagType);
119 
120 	ui->labTagType->setText(tr("Tag") + ": " + tagTypeString);
121 }
122 
reset()123 void GUI_TagFromPath::reset()
124 {
125 	ui->leTag->clear();
126 	ui->leTag->setEnabled(true);
127 	ui->lvInvalidFilepaths->clear();
128 
129 	ui->btnAlbum->setChecked(false);
130 	ui->btnArtist->setChecked(false);
131 	ui->btnTitle->setChecked(false);
132 	ui->btnYear->setChecked(false);
133 	ui->btnDiscNumber->setChecked(false);
134 	ui->btnTrackNumber->setChecked(false);
135 }
136 
setTagColors(bool valid)137 void GUI_TagFromPath::setTagColors(bool valid)
138 {
139 	const auto stylesheet = (valid)
140 	                        ? QStringLiteral("font-family: mono; font-size: 120%;")
141 	                        : QStringLiteral("font-family: mono; font-size: 120%; color: red;");
142 
143 	ui->leTag->setStyleSheet(stylesheet);
144 	ui->btnApplyTag->setEnabled(valid);
145 	ui->btnApplyTagAll->setEnabled(valid);
146 }
147 
tagTextChanged(const QString & tagString)148 void GUI_TagFromPath::tagTextChanged(const QString& tagString)
149 {
150 	const auto expression = Tagging::Expression(tagString, m->currentFilepath);
151 	setTagColors(expression.isValid());
152 }
153 
clearInvalidFilepaths()154 void GUI_TagFromPath::clearInvalidFilepaths()
155 {
156 	showErrorFrame(false);
157 	ui->lvInvalidFilepaths->clear();
158 }
159 
addInvalidFilepath(const QString & filepath)160 void GUI_TagFromPath::addInvalidFilepath(const QString& filepath)
161 {
162 	showErrorFrame(true);
163 	ui->lvInvalidFilepaths->addItem(filepath);
164 }
165 
replaceSelectedTagText(TagName tagName,bool buttonChecked)166 bool GUI_TagFromPath::replaceSelectedTagText(TagName tagName, bool buttonChecked)
167 {
168 	const auto textSelection = ui->leTag->textSelection();
169 
170 	if(buttonChecked && (textSelection.selectionStart < 0))
171 	{
172 		Message::info(tr("Please select text first"));
173 		return false;
174 	}
175 
176 	auto lineEditText = ui->leTag->text();
177 	const auto tagString = Tagging::tagNameToString(tagName);
178 
179 	if(buttonChecked)
180 	{
181 		const auto selectedText = lineEditText.mid(textSelection.selectionStart, textSelection.selectionSize);
182 
183 		lineEditText.replace(textSelection.selectionStart, textSelection.selectionSize, tagString);
184 		m->tagReplaceStringMap[tagName] = selectedText;
185 
186 		ui->leTag->setText(lineEditText);
187 	}
188 
189 	else
190 	{
191 		lineEditText.replace(tagString, m->tagReplaceStringMap[tagName]);
192 		m->tagReplaceStringMap.remove(tagName);
193 
194 		ui->leTag->setText(lineEditText);
195 	}
196 
197 	const auto expression = Tagging::Expression(lineEditText, m->currentFilepath);
198 	setTagColors(expression.isValid());
199 
200 	return true;
201 }
202 
btnChecked(QPushButton * btn,bool b,TagName tagName)203 void GUI_TagFromPath::btnChecked(QPushButton* btn, bool b, TagName tagName)
204 {
205 	ui->labTagFromPathWarning->setVisible(false);
206 	ui->lvInvalidFilepaths->setVisible(false);
207 
208 	if(!replaceSelectedTagText(tagName, b))
209 	{
210 		btn->setChecked(false);
211 	}
212 }
213 
showErrorFrame(bool b)214 void GUI_TagFromPath::showErrorFrame(bool b)
215 {
216 	ui->swTagFromPath->setCurrentIndex(b ? 0 : 1);
217 }
218 
btnTagHelpClicked()219 void GUI_TagFromPath::btnTagHelpClicked()
220 {
221 	const auto url = QUrl(QStringLiteral("https://sayonara-player.com/faq.php#tag-edit"));
222 	QDesktopServices::openUrl(url);
223 }
224 
languageChanged()225 void GUI_TagFromPath::languageChanged()
226 {
227 	ui->retranslateUi(this);
228 
229 	ui->btnTitle->setText(Lang::get(Lang::Title));
230 	ui->btnAlbum->setText(Lang::get(Lang::Album));
231 	ui->btnArtist->setText(Lang::get(Lang::Artist));
232 	ui->btnYear->setText(Lang::get(Lang::Year));
233 	ui->btnTrackNumber->setText(Lang::get(Lang::TrackNo).toFirstUpper());
234 	ui->labTagFromPathWarning->setText(Lang::get(Lang::Warning));
235 
236 	ui->btnApplyTagAll->setText(Lang::get(Lang::Apply) + ": " + Lang::get(Lang::All).toFirstUpper());
237 	ui->btnApplyTag->setText(Lang::get(Lang::Apply) + ": " + Lang::get(Lang::Title).toFirstUpper());
238 }
239 
getRegexString() const240 QString GUI_TagFromPath::getRegexString() const
241 {
242 	return ui->leTag->text();
243 }
244