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 }