File indexing completed on 2024-04-28 05:08:26
0001 /*************************************************************************** 0002 Copyright (C) 2003-2009 Robby Stephenson <robby@periapsis.org> 0003 ***************************************************************************/ 0004 0005 /*************************************************************************** 0006 * * 0007 * This program is free software; you can redistribute it and/or * 0008 * modify it under the terms of the GNU General Public License as * 0009 * published by the Free Software Foundation; either version 2 of * 0010 * the License or (at your option) version 3 or any later version * 0011 * accepted by the membership of KDE e.V. (or its successor approved * 0012 * by the membership of KDE e.V.), which shall act as a proxy * 0013 * defined in Section 14 of version 3 of the license. * 0014 * * 0015 * This program is distributed in the hope that it will be useful, * 0016 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0017 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0018 * GNU General Public License for more details. * 0019 * * 0020 * You should have received a copy of the GNU General Public License * 0021 * along with this program. If not, see <http://www.gnu.org/licenses/>. * 0022 * * 0023 ***************************************************************************/ 0024 0025 #include "tellico_kernel.h" 0026 #include "document.h" 0027 #include "collection.h" 0028 #include "filter.h" 0029 #include "filterdialog.h" 0030 #include "loandialog.h" 0031 #include "commands/collectioncommand.h" 0032 #include "commands/fieldcommand.h" 0033 #include "commands/filtercommand.h" 0034 #include "commands/addentries.h" 0035 #include "commands/modifyentries.h" 0036 #include "commands/updateentries.h" 0037 #include "commands/removeentries.h" 0038 #include "commands/removeloans.h" 0039 #include "commands/reorderfields.h" 0040 #include "commands/renamecollection.h" 0041 #include "collectionfactory.h" 0042 #include "utils/cursorsaver.h" 0043 #include "utils/mergeconflictresolver.h" 0044 0045 #include <KMessageBox> 0046 #include <KLocalizedString> 0047 #include <kwidgetsaddons_version.h> 0048 0049 #include <QInputDialog> 0050 #include <QUndoStack> 0051 0052 using Tellico::Kernel; 0053 Kernel* Kernel::s_self = nullptr; 0054 0055 Kernel::Kernel(QWidget* parent) : QObject() 0056 , m_widget(parent) 0057 , m_commandHistory(new QUndoStack(parent)) { 0058 } 0059 0060 Kernel::~Kernel() { 0061 } 0062 0063 QUrl Kernel::URL() const { 0064 return Data::Document::self()->URL(); 0065 } 0066 0067 int Kernel::collectionType() const { 0068 return Data::Document::self()->collection() ? 0069 Data::Document::self()->collection()->type() : 0070 Data::Collection::Base; 0071 } 0072 0073 QString Kernel::collectionTypeName() const { 0074 return CollectionFactory::typeName(collectionType()); 0075 } 0076 0077 void Kernel::sorry(const QString& text_, QWidget* widget_/* =nullptr */) { 0078 if(text_.isEmpty()) { 0079 return; 0080 } 0081 GUI::CursorSaver cs(Qt::ArrowCursor); 0082 KMessageBox::error(widget_ ? widget_ : m_widget, text_); 0083 } 0084 0085 void Kernel::beginCommandGroup(const QString& name_) { 0086 m_commandHistory->beginMacro(name_); 0087 } 0088 0089 void Kernel::endCommandGroup() { 0090 m_commandHistory->endMacro(); 0091 } 0092 0093 void Kernel::resetHistory() { 0094 m_commandHistory->clear(); 0095 m_commandHistory->setClean(); 0096 } 0097 0098 bool Kernel::addField(Tellico::Data::FieldPtr field_) { 0099 if(!field_) { 0100 return false; 0101 } 0102 doCommand(new Command::FieldCommand(Command::FieldCommand::FieldAdd, 0103 Data::Document::self()->collection(), 0104 field_)); 0105 return true; 0106 } 0107 0108 bool Kernel::modifyField(Tellico::Data::FieldPtr field_) { 0109 if(!field_) { 0110 return false; 0111 } 0112 Data::FieldPtr oldField = Data::Document::self()->collection()->fieldByName(field_->name()); 0113 if(!oldField) { 0114 return false; 0115 } 0116 doCommand(new Command::FieldCommand(Command::FieldCommand::FieldModify, 0117 Tellico::Data::Document::self()->collection(), 0118 field_, 0119 oldField)); 0120 return true; 0121 } 0122 0123 bool Kernel::removeField(Tellico::Data::FieldPtr field_) { 0124 if(!field_) { 0125 return false; 0126 } 0127 doCommand(new Command::FieldCommand(Command::FieldCommand::FieldRemove, 0128 Tellico::Data::Document::self()->collection(), 0129 field_)); 0130 return true; 0131 } 0132 0133 void Kernel::addEntries(Tellico::Data::EntryList entries_, bool checkFields_) { 0134 if(entries_.isEmpty()) { 0135 return; 0136 } 0137 0138 QUndoCommand* cmd = new Command::AddEntries(Tellico::Data::Document::self()->collection(), entries_); 0139 if(checkFields_) { 0140 beginCommandGroup(cmd->text()); 0141 0142 // this is the same as in Command::UpdateEntries::redo() 0143 Tellico::Data::CollPtr c = Data::Document::self()->collection(); 0144 Tellico::Data::FieldList fields = entries_[0]->collection()->fields(); 0145 0146 auto p = Merge::mergeFields(c, fields, entries_); 0147 Data::FieldList modifiedFields = p.first; 0148 Data::FieldList addedFields = p.second; 0149 0150 foreach(Tellico::Data::FieldPtr field, modifiedFields) { 0151 if(c->hasField(field->name())) { 0152 doCommand(new Command::FieldCommand(Command::FieldCommand::FieldModify, c, 0153 field, c->fieldByName(field->name()))); 0154 } 0155 } 0156 0157 foreach(Tellico::Data::FieldPtr field, addedFields) { 0158 doCommand(new Command::FieldCommand(Command::FieldCommand::FieldAdd, c, field)); 0159 } 0160 } 0161 doCommand(cmd); 0162 if(checkFields_) { 0163 endCommandGroup(); 0164 } 0165 } 0166 0167 void Kernel::modifyEntries(Tellico::Data::EntryList oldEntries_, Tellico::Data::EntryList newEntries_, const QStringList& modifiedFields_) { 0168 if(newEntries_.isEmpty()) { 0169 return; 0170 } 0171 0172 doCommand(new Command::ModifyEntries(Data::Document::self()->collection(), oldEntries_, newEntries_, modifiedFields_)); 0173 } 0174 0175 void Kernel::updateEntry(Tellico::Data::EntryPtr oldEntry_, Tellico::Data::EntryPtr newEntry_, bool overWrite_) { 0176 if(!newEntry_) { 0177 return; 0178 } 0179 0180 doCommand(new Command::UpdateEntries(Tellico::Data::Document::self()->collection(), oldEntry_, newEntry_, overWrite_)); 0181 } 0182 0183 void Kernel::removeEntries(Tellico::Data::EntryList entries_) { 0184 if(entries_.isEmpty()) { 0185 return; 0186 } 0187 0188 doCommand(new Command::RemoveEntries(Tellico::Data::Document::self()->collection(), entries_)); 0189 } 0190 0191 bool Kernel::addLoans(Tellico::Data::EntryList entries_) { 0192 if(entries_.isEmpty()) { 0193 return false; 0194 } 0195 0196 LoanDialog dlg(entries_, m_widget); 0197 if(dlg.exec() != QDialog::Accepted) { 0198 return false; 0199 } 0200 0201 QUndoCommand* cmd = dlg.createCommand(); 0202 if(!cmd) { 0203 return false; 0204 } 0205 doCommand(cmd); 0206 return true; 0207 } 0208 0209 bool Kernel::modifyLoan(Tellico::Data::LoanPtr loan_) { 0210 if(!loan_) { 0211 return false; 0212 } 0213 0214 LoanDialog dlg(loan_, m_widget); 0215 if(dlg.exec() != QDialog::Accepted) { 0216 return false; 0217 } 0218 0219 QUndoCommand* cmd = dlg.createCommand(); 0220 if(!cmd) { 0221 return false; 0222 } 0223 doCommand(cmd); 0224 return true; 0225 } 0226 0227 bool Kernel::removeLoans(Tellico::Data::LoanList loans_) { 0228 if(loans_.isEmpty()) { 0229 return true; 0230 } 0231 0232 doCommand(new Command::RemoveLoans(loans_)); 0233 return true; 0234 } 0235 0236 void Kernel::addFilter(Tellico::FilterPtr filter_) { 0237 if(!filter_) { 0238 return; 0239 } 0240 0241 doCommand(new Command::FilterCommand(Command::FilterCommand::FilterAdd, filter_)); 0242 } 0243 0244 bool Kernel::modifyFilter(Tellico::FilterPtr filter_) { 0245 if(!filter_) { 0246 return false; 0247 } 0248 0249 FilterDialog filterDlg(FilterDialog::ModifyFilter, m_widget); 0250 // need to create a new filter object 0251 FilterPtr newFilter(new Filter(*filter_)); 0252 filterDlg.setFilter(newFilter); 0253 if(filterDlg.exec() != QDialog::Accepted) { 0254 return false; 0255 } 0256 0257 newFilter = filterDlg.currentFilter(); 0258 doCommand(new Command::FilterCommand(Command::FilterCommand::FilterModify, newFilter, filter_)); 0259 return true; 0260 } 0261 0262 bool Kernel::removeFilter(Tellico::FilterPtr filter_) { 0263 if(!filter_) { 0264 return false; 0265 } 0266 0267 QString str = i18n("Do you really want to delete this filter?"); 0268 QString dontAsk = QStringLiteral("DeleteFilter"); 0269 #if KWIDGETSADDONS_VERSION < QT_VERSION_CHECK(5, 100, 0) 0270 int ret = KMessageBox::questionYesNo(m_widget, str, i18n("Delete Filter?"), 0271 KStandardGuiItem::del(), KStandardGuiItem::cancel(), dontAsk); 0272 if(ret != KMessageBox::Yes) { 0273 #else 0274 int ret = KMessageBox::questionTwoActions(m_widget, str, i18n("Delete Filter?"), 0275 KStandardGuiItem::del(), KStandardGuiItem::cancel(), dontAsk); 0276 if(ret != KMessageBox::ButtonCode::PrimaryAction) { 0277 #endif 0278 return false; 0279 } 0280 0281 doCommand(new Command::FilterCommand(Command::FilterCommand::FilterRemove, filter_)); 0282 return true; 0283 } 0284 0285 void Kernel::reorderFields(const Tellico::Data::FieldList& fields_) { 0286 doCommand(new Command::ReorderFields(Data::Document::self()->collection(), 0287 Data::Document::self()->collection()->fields(), 0288 fields_)); 0289 } 0290 0291 void Kernel::appendCollection(Tellico::Data::CollPtr coll_) { 0292 doCommand(new Command::CollectionCommand(Command::CollectionCommand::Append, 0293 Data::Document::self()->collection(), 0294 coll_)); 0295 } 0296 0297 void Kernel::mergeCollection(Tellico::Data::CollPtr coll_) { 0298 doCommand(new Command::CollectionCommand(Command::CollectionCommand::Merge, 0299 Data::Document::self()->collection(), 0300 coll_)); 0301 } 0302 0303 void Kernel::replaceCollection(Tellico::Data::CollPtr coll_) { 0304 doCommand(new Command::CollectionCommand(Command::CollectionCommand::Replace, 0305 Data::Document::self()->collection(), 0306 coll_)); 0307 } 0308 0309 void Kernel::renameCollection() { 0310 bool ok; 0311 QString newTitle = QInputDialog::getText(m_widget, i18n("Rename Collection"), i18n("New collection name:"), 0312 QLineEdit::Normal, Data::Document::self()->collection()->title(), &ok); 0313 if(ok) { 0314 doCommand(new Command::RenameCollection(Data::Document::self()->collection(), newTitle)); 0315 } 0316 } 0317 0318 void Kernel::doCommand(QUndoCommand* command_) { 0319 m_commandHistory->push(command_); 0320 } 0321 0322 int Kernel::askAndMerge(Tellico::Data::EntryPtr entry1_, Tellico::Data::EntryPtr entry2_, Tellico::Data::FieldPtr field_, 0323 QString value1_, QString value2_) { 0324 QString title1 = entry1_->field(QStringLiteral("title")); 0325 QString title2 = entry2_->field(QStringLiteral("title")); 0326 if(title1 == title2) { 0327 title1 = i18n("Entry 1"); 0328 title2 = i18n("Entry 2"); 0329 } 0330 if(value1_.isEmpty()) { 0331 value1_ = entry1_->field(field_); 0332 } 0333 if(value2_.isEmpty()) { 0334 value2_ = entry2_->field(field_); 0335 } 0336 QString text = QLatin1String("<qt>") 0337 + i18n("Conflicting values for %1 were found while merging entries.", field_->title()) 0338 + QString::fromLatin1("<br/><center><table><tr>" 0339 "<th>%1</th>" 0340 "<th>%2</th></tr>").arg(title1, title2) 0341 + QStringLiteral("<tr><td><em>%1</em></td>").arg(value1_) 0342 + QStringLiteral("<td><em>%1</em></td></tr></table></center>").arg(value2_) 0343 + i18n("Please choose which value to keep.") 0344 + QLatin1String("</qt>"); 0345 0346 #if KWIDGETSADDONS_VERSION < QT_VERSION_CHECK(5, 100, 0) 0347 auto ret = KMessageBox::warningYesNoCancel(Kernel::self()->widget(), 0348 text, 0349 i18n("Merge Entries"), 0350 KGuiItem(i18n("Select value from %1", title1)), 0351 KGuiItem(i18n("Select value from %1", title2))); 0352 switch(ret) { 0353 case KMessageBox::Yes: return Merge::ConflictResolver::KeepFirst; // keep original value 0354 case KMessageBox::No: return Merge::ConflictResolver::KeepSecond; // use newer value 0355 case KMessageBox::Cancel: 0356 default: return Merge::ConflictResolver::CancelMerge; 0357 } 0358 #else 0359 auto ret = KMessageBox::warningTwoActionsCancel(Kernel::self()->widget(), 0360 text, 0361 i18n("Merge Entries"), 0362 KGuiItem(i18n("Select value from %1", title1)), 0363 KGuiItem(i18n("Select value from %1", title2))); 0364 switch(ret) { 0365 case KMessageBox::ButtonCode::PrimaryAction: return Merge::ConflictResolver::KeepFirst; // keep original value 0366 case KMessageBox::ButtonCode::SecondaryAction: return Merge::ConflictResolver::KeepSecond; // use newer value 0367 case KMessageBox::ButtonCode::Cancel: 0368 default: return Merge::ConflictResolver::CancelMerge; 0369 } 0370 #endif 0371 return Merge::ConflictResolver::CancelMerge; 0372 }