1 /*
2 * LibrePCB - Professional EDA for everyone!
3 * Copyright (C) 2013 LibrePCB Developers, see AUTHORS.md for contributors.
4 * https://librepcb.org/
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*******************************************************************************
21 * Includes
22 ******************************************************************************/
23 #include "packagecategoryeditorwidget.h"
24
25 #include "../common/categorychooserdialog.h"
26 #include "../common/categorytreelabeltextbuilder.h"
27 #include "ui_packagecategoryeditorwidget.h"
28
29 #include <librepcb/library/cat/cmd/cmdlibrarycategoryedit.h>
30 #include <librepcb/library/cat/packagecategory.h>
31 #include <librepcb/library/msg/msgmissingauthor.h>
32 #include <librepcb/library/msg/msgnamenottitlecase.h>
33 #include <librepcb/workspace/workspace.h>
34
35 #include <QtCore>
36 #include <QtWidgets>
37
38 /*******************************************************************************
39 * Namespace
40 ******************************************************************************/
41 namespace librepcb {
42 namespace library {
43 namespace editor {
44
45 /*******************************************************************************
46 * Constructors / Destructor
47 ******************************************************************************/
48
PackageCategoryEditorWidget(const Context & context,const FilePath & fp,QWidget * parent)49 PackageCategoryEditorWidget::PackageCategoryEditorWidget(const Context& context,
50 const FilePath& fp,
51 QWidget* parent)
52 : EditorWidgetBase(context, fp, parent),
53 mUi(new Ui::PackageCategoryEditorWidget) {
54 mUi->setupUi(this);
55 mUi->lstMessages->setHandler(this);
56 mUi->lstMessages->setProvideFixes(!mContext.readOnly);
57 mUi->edtName->setReadOnly(mContext.readOnly);
58 mUi->edtDescription->setReadOnly(mContext.readOnly);
59 mUi->edtKeywords->setReadOnly(mContext.readOnly);
60 mUi->edtAuthor->setReadOnly(mContext.readOnly);
61 mUi->edtVersion->setReadOnly(mContext.readOnly);
62 mUi->cbxDeprecated->setCheckable(!mContext.readOnly);
63 mUi->btnChooseParentCategory->setEnabled(!mContext.readOnly);
64 mUi->btnResetParentCategory->setEnabled(!mContext.readOnly);
65 setWindowIcon(QIcon(":/img/places/folder_green.png"));
66 connect(mUi->btnChooseParentCategory, &QToolButton::clicked, this,
67 &PackageCategoryEditorWidget::btnChooseParentCategoryClicked);
68 connect(mUi->btnResetParentCategory, &QToolButton::clicked, this,
69 &PackageCategoryEditorWidget::btnResetParentCategoryClicked);
70
71 // Load element.
72 mCategory.reset(new PackageCategory(std::unique_ptr<TransactionalDirectory>(
73 new TransactionalDirectory(mFileSystem)))); // can throw
74 updateMetadata();
75
76 // Reload metadata on undo stack state changes.
77 connect(mUndoStack.data(), &UndoStack::stateModified, this,
78 &PackageCategoryEditorWidget::updateMetadata);
79
80 // Handle changes of metadata.
81 connect(mUi->edtName, &QLineEdit::editingFinished, this,
82 &PackageCategoryEditorWidget::commitMetadata);
83 connect(mUi->edtDescription, &PlainTextEdit::editingFinished, this,
84 &PackageCategoryEditorWidget::commitMetadata);
85 connect(mUi->edtKeywords, &QLineEdit::editingFinished, this,
86 &PackageCategoryEditorWidget::commitMetadata);
87 connect(mUi->edtAuthor, &QLineEdit::editingFinished, this,
88 &PackageCategoryEditorWidget::commitMetadata);
89 connect(mUi->edtVersion, &QLineEdit::editingFinished, this,
90 &PackageCategoryEditorWidget::commitMetadata);
91 connect(mUi->cbxDeprecated, &QCheckBox::clicked, this,
92 &PackageCategoryEditorWidget::commitMetadata);
93 }
94
~PackageCategoryEditorWidget()95 PackageCategoryEditorWidget::~PackageCategoryEditorWidget() noexcept {
96 }
97
98 /*******************************************************************************
99 * Public Slots
100 ******************************************************************************/
101
save()102 bool PackageCategoryEditorWidget::save() noexcept {
103 // Commit metadata.
104 QString errorMsg = commitMetadata();
105 if (!errorMsg.isEmpty()) {
106 QMessageBox::critical(this, tr("Invalid metadata"), errorMsg);
107 return false;
108 }
109
110 // Save element.
111 try {
112 mCategory->save(); // can throw
113 mFileSystem->save(); // can throw
114 return EditorWidgetBase::save();
115 } catch (const Exception& e) {
116 QMessageBox::critical(this, tr("Save failed"), e.getMsg());
117 return false;
118 }
119 }
120
121 /*******************************************************************************
122 * Private Methods
123 ******************************************************************************/
124
updateMetadata()125 void PackageCategoryEditorWidget::updateMetadata() noexcept {
126 setWindowTitle(*mCategory->getNames().getDefaultValue());
127 mUi->edtName->setText(*mCategory->getNames().getDefaultValue());
128 mUi->edtDescription->setPlainText(
129 mCategory->getDescriptions().getDefaultValue());
130 mUi->edtKeywords->setText(mCategory->getKeywords().getDefaultValue());
131 mUi->edtAuthor->setText(mCategory->getAuthor());
132 mUi->edtVersion->setText(mCategory->getVersion().toStr());
133 mUi->cbxDeprecated->setChecked(mCategory->isDeprecated());
134 mParentUuid = mCategory->getParentUuid();
135 updateCategoryLabel();
136 }
137
commitMetadata()138 QString PackageCategoryEditorWidget::commitMetadata() noexcept {
139 try {
140 QScopedPointer<CmdLibraryCategoryEdit> cmd(
141 new CmdLibraryCategoryEdit(*mCategory));
142 try {
143 // throws on invalid name
144 cmd->setName("", ElementName(mUi->edtName->text().trimmed()));
145 } catch (const Exception& e) {
146 }
147 cmd->setDescription("", mUi->edtDescription->toPlainText().trimmed());
148 cmd->setKeywords("", mUi->edtKeywords->text().trimmed());
149 try {
150 // throws on invalid version
151 cmd->setVersion(Version::fromString(mUi->edtVersion->text().trimmed()));
152 } catch (const Exception& e) {
153 }
154 cmd->setAuthor(mUi->edtAuthor->text().trimmed());
155 cmd->setDeprecated(mUi->cbxDeprecated->isChecked());
156 cmd->setParentUuid(mParentUuid);
157
158 // Commit all changes.
159 mUndoStack->execCmd(cmd.take()); // can throw
160
161 // Reload metadata into widgets to discard invalid input.
162 updateMetadata();
163 } catch (const Exception& e) {
164 return e.getMsg();
165 }
166 return QString();
167 }
168
runChecks(LibraryElementCheckMessageList & msgs) const169 bool PackageCategoryEditorWidget::runChecks(
170 LibraryElementCheckMessageList& msgs) const {
171 msgs = mCategory->runChecks(); // can throw
172 mUi->lstMessages->setMessages(msgs);
173 return true;
174 }
175
176 template <>
fixMsg(const MsgNameNotTitleCase & msg)177 void PackageCategoryEditorWidget::fixMsg(const MsgNameNotTitleCase& msg) {
178 mUi->edtName->setText(*msg.getFixedName());
179 commitMetadata();
180 }
181
182 template <>
fixMsg(const MsgMissingAuthor & msg)183 void PackageCategoryEditorWidget::fixMsg(const MsgMissingAuthor& msg) {
184 Q_UNUSED(msg);
185 mUi->edtAuthor->setText(getWorkspaceSettingsUserName());
186 commitMetadata();
187 }
188
189 template <typename MessageType>
fixMsgHelper(std::shared_ptr<const LibraryElementCheckMessage> msg,bool applyFix)190 bool PackageCategoryEditorWidget::fixMsgHelper(
191 std::shared_ptr<const LibraryElementCheckMessage> msg, bool applyFix) {
192 if (msg) {
193 if (auto m = msg->as<MessageType>()) {
194 if (applyFix) fixMsg(*m); // can throw
195 return true;
196 }
197 }
198 return false;
199 }
200
processCheckMessage(std::shared_ptr<const LibraryElementCheckMessage> msg,bool applyFix)201 bool PackageCategoryEditorWidget::processCheckMessage(
202 std::shared_ptr<const LibraryElementCheckMessage> msg, bool applyFix) {
203 if (fixMsgHelper<MsgNameNotTitleCase>(msg, applyFix)) return true;
204 if (fixMsgHelper<MsgMissingAuthor>(msg, applyFix)) return true;
205 return false;
206 }
207
btnChooseParentCategoryClicked()208 void PackageCategoryEditorWidget::btnChooseParentCategoryClicked() noexcept {
209 PackageCategoryChooserDialog dialog(mContext.workspace);
210 if (dialog.exec()) {
211 mParentUuid = dialog.getSelectedCategoryUuid();
212 commitMetadata();
213 }
214 }
215
btnResetParentCategoryClicked()216 void PackageCategoryEditorWidget::btnResetParentCategoryClicked() noexcept {
217 mParentUuid = tl::nullopt;
218 commitMetadata();
219 }
220
updateCategoryLabel()221 void PackageCategoryEditorWidget::updateCategoryLabel() noexcept {
222 const workspace::WorkspaceLibraryDb& db = mContext.workspace.getLibraryDb();
223 PackageCategoryTreeLabelTextBuilder textBuilder(db, getLibLocaleOrder(),
224 *mUi->lblParentCategories);
225 textBuilder.setEndlessRecursionUuid(mCategory->getUuid());
226 textBuilder.setHighlightLastLine(true);
227 textBuilder.updateText(mParentUuid, mUi->edtName->text());
228 }
229
230 /*******************************************************************************
231 * End of File
232 ******************************************************************************/
233
234 } // namespace editor
235 } // namespace library
236 } // namespace librepcb
237