File indexing completed on 2024-04-28 16:31:54
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 "controller.h" 0026 #include "mainwindow.h" 0027 #include "groupview.h" 0028 #include "detailedlistview.h" 0029 #include "entryeditdialog.h" 0030 #include "entryview.h" 0031 #include "entryiconview.h" 0032 #include "entry.h" 0033 #include "entrygroup.h" 0034 #include "field.h" 0035 #include "filter.h" 0036 #include "filterdialog.h" 0037 #include "tellico_kernel.h" 0038 #include "collection.h" 0039 #include "document.h" 0040 #include "borrower.h" 0041 #include "filterview.h" 0042 #include "loanview.h" 0043 #include "entryupdater.h" 0044 #include "entrymerger.h" 0045 #include "utils/cursorsaver.h" 0046 #include "gui/lineedit.h" 0047 #include "gui/tabwidget.h" 0048 #include "tellico_debug.h" 0049 0050 #include <KLocalizedString> 0051 #include <KMessageBox> 0052 #include <KActionMenu> 0053 0054 #include <QMenu> 0055 0056 using Tellico::Controller; 0057 0058 Controller* Controller::s_self = nullptr; 0059 0060 Controller::Controller(Tellico::MainWindow* parent_) 0061 : QObject(parent_), m_mainWindow(parent_), m_working(false) { 0062 } 0063 0064 Controller::~Controller() { 0065 } 0066 0067 void Controller::addObserver(Tellico::Observer* obs) { 0068 m_observers.append(obs); 0069 } 0070 0071 void Controller::removeObserver(Tellico::Observer* obs) { 0072 m_observers.removeAll(obs); 0073 } 0074 0075 QString Controller::groupBy() const { 0076 return m_mainWindow->m_groupView->groupBy(); 0077 } 0078 0079 QStringList Controller::expandedGroupBy() const { 0080 QStringList g; 0081 g << groupBy(); 0082 // special case for pseudo-group 0083 if(g[0] == Data::Collection::s_peopleGroupName) { 0084 g.clear(); 0085 Data::FieldList fields = Data::Document::self()->collection()->peopleFields(); 0086 foreach(Data::FieldPtr field, fields) { 0087 g << field->name(); 0088 } 0089 } 0090 // special case for no groups 0091 if(g[0].isEmpty()) { 0092 g.clear(); 0093 } 0094 return g; 0095 } 0096 0097 QStringList Controller::sortTitles() const { 0098 QStringList list; 0099 list << m_mainWindow->m_detailedView->sortColumnTitle1(); 0100 list << m_mainWindow->m_detailedView->sortColumnTitle2(); 0101 list << m_mainWindow->m_detailedView->sortColumnTitle3(); 0102 return list; 0103 } 0104 0105 QStringList Controller::visibleColumns() const { 0106 return m_mainWindow->m_detailedView->visibleColumns(); 0107 } 0108 0109 Tellico::Data::EntryList Controller::visibleEntries() { 0110 return m_mainWindow->m_detailedView->visibleEntries(); 0111 } 0112 0113 void Controller::slotCollectionAdded(Tellico::Data::CollPtr coll_) { 0114 MARK; 0115 // at start-up, this might get called too early, so check and bail 0116 if(!coll_ || !m_mainWindow->m_groupView) { 0117 return; 0118 } 0119 0120 // do this first because the group view will need it later 0121 m_mainWindow->readCollectionOptions(coll_); 0122 m_mainWindow->slotUpdateToolbarIcons(); 0123 m_mainWindow->updateEntrySources(); // has to be called before all the addCollection() 0124 // calls in the widgets since they may want menu updates 0125 0126 m_mainWindow->m_detailedView->addCollection(coll_); 0127 m_mainWindow->m_groupView->addCollection(coll_); 0128 m_mainWindow->m_editDialog->resetLayout(coll_); 0129 if(!coll_->filters().isEmpty()) { 0130 m_mainWindow->addFilterView(); 0131 m_mainWindow->m_filterView->addCollection(coll_); 0132 m_mainWindow->m_viewTabs->setTabBarHidden(false); 0133 } 0134 if(!coll_->borrowers().isEmpty()) { 0135 m_mainWindow->addLoanView(); 0136 m_mainWindow->m_loanView->addCollection(coll_); 0137 m_mainWindow->m_viewTabs->setTabBarHidden(false); 0138 } 0139 0140 m_mainWindow->slotStatusMsg(i18n("Ready.")); 0141 0142 m_selectedEntries.clear(); 0143 m_mainWindow->slotEntryCount(); 0144 0145 // there really should be a lot of signals to connect to, but right now, the only one 0146 // is used when a field is added on a merge 0147 connect(&*coll_, &Data::Collection::mergeAddedField, 0148 this, &Controller::slotFieldAdded); 0149 0150 emit collectionAdded(coll_->type()); 0151 0152 updateActions(); 0153 0154 connect(&*coll_, &Data::Collection::signalGroupsModified, 0155 m_mainWindow->m_groupView, &GroupView::slotModifyGroups); 0156 connect(&*coll_, &Data::Collection::signalRefreshField, 0157 this, &Controller::slotRefreshField); 0158 } 0159 0160 void Controller::slotCollectionModified(Tellico::Data::CollPtr coll_, bool structuralChange_) { 0161 Data::EntryList prevSelection = m_selectedEntries; 0162 blockAllSignals(true); 0163 m_mainWindow->m_groupView->removeCollection(coll_); 0164 if(m_mainWindow->m_filterView) { 0165 m_mainWindow->m_filterView->slotReset(); 0166 } 0167 if(m_mainWindow->m_loanView) { 0168 m_mainWindow->m_loanView->slotReset(); 0169 } 0170 // TODO: removing and adding the collection in the detailed view is overkill 0171 // find a more elegant way to refresh the view 0172 m_mainWindow->m_detailedView->removeCollection(coll_); 0173 blockAllSignals(false); 0174 0175 if(structuralChange_) { 0176 m_mainWindow->m_editDialog->resetLayout(coll_); 0177 } 0178 // the selected entries list gets cleared when the detailed list view removes the collection 0179 m_selectedEntries = prevSelection; 0180 m_mainWindow->m_editDialog->setContents(m_selectedEntries); 0181 m_mainWindow->m_detailedView->addCollection(coll_); 0182 m_mainWindow->m_groupView->addCollection(coll_); 0183 if(!coll_->filters().isEmpty()) { 0184 m_mainWindow->addFilterView(); 0185 m_mainWindow->m_filterView->addCollection(coll_); 0186 m_mainWindow->m_viewTabs->setTabBarHidden(false); 0187 } 0188 if(!coll_->borrowers().isEmpty()) { 0189 m_mainWindow->addLoanView(); 0190 m_mainWindow->m_loanView->addCollection(coll_); 0191 m_mainWindow->m_viewTabs->setTabBarHidden(false); 0192 } 0193 0194 m_mainWindow->slotStatusMsg(i18n("Ready.")); 0195 m_mainWindow->slotEntryCount(); 0196 0197 // https://bugs.kde.org/show_bug.cgi?id=386549 0198 // at some point, I need to revisit the ::setImagesAreAvailable() methodology 0199 // there are too many workarounds in the code for that 0200 m_mainWindow->m_detailedView->slotRefreshImages(); 0201 } 0202 0203 void Controller::slotCollectionDeleted(Tellico::Data::CollPtr coll_) { 0204 blockAllSignals(true); 0205 m_mainWindow->saveCollectionOptions(coll_); 0206 m_mainWindow->m_groupView->removeCollection(coll_); 0207 if(m_mainWindow->m_filterView) { 0208 m_mainWindow->m_filterView->slotReset(); 0209 } 0210 if(m_mainWindow->m_loanView) { 0211 m_mainWindow->m_loanView->slotReset(); 0212 } 0213 m_mainWindow->m_detailedView->removeCollection(coll_); 0214 m_mainWindow->m_entryView->clear(); 0215 blockAllSignals(false); 0216 0217 // disconnect all signals from the collection 0218 // this is needed because the Collection::appendCollection() and mergeCollection() 0219 // functions signal collection deleted then added for the same collection 0220 coll_->disconnect(); 0221 } 0222 0223 void Controller::slotFieldAdded(Tellico::Data::CollPtr coll_, Tellico::Data::FieldPtr field_) { 0224 addedField(coll_, field_); 0225 } 0226 0227 // TODO: should be adding entries to models rather than to widget observers 0228 void Controller::addedEntries(Tellico::Data::EntryList entries_) { 0229 blockAllSignals(true); 0230 foreach(Observer* obs, m_observers) { 0231 obs->addEntries(entries_); 0232 } 0233 m_mainWindow->slotQueueFilter(); 0234 blockAllSignals(false); 0235 } 0236 0237 void Controller::modifiedEntries(Tellico::Data::EntryList entries_) { 0238 // when a new document is being loaded, loans are added to borrowers, which 0239 // end up calling Entry::checkIn() which called Document::saveEntry() which calls here 0240 // ignore that 0241 if(!m_mainWindow->m_initialized) { 0242 return; 0243 } 0244 blockAllSignals(true); 0245 foreach(Observer* obs, m_observers) { 0246 obs->modifyEntries(entries_); 0247 } 0248 m_mainWindow->m_entryView->slotRefresh(); // special case 0249 blockAllSignals(false); 0250 } 0251 0252 void Controller::removedEntries(Tellico::Data::EntryList entries_) { 0253 blockAllSignals(true); 0254 foreach(Observer* obs, m_observers) { 0255 obs->removeEntries(entries_); 0256 } 0257 foreach(Data::EntryPtr entry, entries_) { 0258 m_selectedEntries.removeAll(entry); 0259 } 0260 m_mainWindow->slotEntryCount(); 0261 m_mainWindow->slotQueueFilter(); 0262 blockAllSignals(false); 0263 } 0264 0265 void Controller::addedField(Tellico::Data::CollPtr coll_, Tellico::Data::FieldPtr field_) { 0266 foreach(Observer* obs, m_observers) { 0267 obs->addField(coll_, field_); 0268 } 0269 m_mainWindow->m_entryView->slotRefresh(); 0270 m_mainWindow->slotUpdateCollectionToolBar(coll_); 0271 m_mainWindow->slotQueueFilter(); 0272 } 0273 0274 void Controller::removedField(Tellico::Data::CollPtr coll_, Tellico::Data::FieldPtr field_) { 0275 foreach(Observer* obs, m_observers) { 0276 obs->removeField(coll_, field_); 0277 } 0278 m_mainWindow->m_entryView->slotRefresh(); 0279 m_mainWindow->slotUpdateCollectionToolBar(coll_); 0280 m_mainWindow->slotQueueFilter(); 0281 } 0282 0283 void Controller::modifiedField(Tellico::Data::CollPtr coll_, Tellico::Data::FieldPtr oldField_, Tellico::Data::FieldPtr newField_) { 0284 foreach(Observer* obs, m_observers) { 0285 obs->modifyField(coll_, oldField_, newField_); 0286 } 0287 m_mainWindow->m_entryView->slotRefresh(); 0288 m_mainWindow->slotUpdateCollectionToolBar(coll_); 0289 m_mainWindow->slotQueueFilter(); 0290 } 0291 0292 void Controller::reorderedFields(Tellico::Data::CollPtr coll_) { 0293 m_mainWindow->m_editDialog->resetLayout(coll_); 0294 m_mainWindow->m_detailedView->reorderFields(coll_->fields()); 0295 m_mainWindow->slotUpdateCollectionToolBar(coll_); 0296 m_mainWindow->m_entryView->slotRefresh(); 0297 } 0298 0299 void Controller::slotClearSelection() { 0300 if(m_working) { 0301 return; 0302 } 0303 0304 m_working = true; 0305 blockAllSignals(true); 0306 0307 m_mainWindow->m_detailedView->clearSelection(); 0308 m_mainWindow->m_iconView->clearSelection(); 0309 m_mainWindow->m_groupView->clearSelection(); 0310 if(m_mainWindow->m_filterView) { 0311 m_mainWindow->m_filterView->clearSelection(); 0312 } 0313 if(m_mainWindow->m_loanView) { 0314 m_mainWindow->m_loanView->clearSelection(); 0315 } 0316 0317 blockAllSignals(false); 0318 0319 m_selectedEntries.clear(); 0320 updateActions(); 0321 m_mainWindow->slotEntryCount(); 0322 m_working = false; 0323 } 0324 0325 void Controller::slotUpdateSelection(const Tellico::Data::EntryList& entries_) { 0326 if(m_working) { 0327 return; 0328 } 0329 m_working = true; 0330 0331 m_selectedEntries = entries_; 0332 updateActions(); 0333 m_mainWindow->slotEntryCount(); 0334 m_working = false; 0335 } 0336 0337 void Controller::slotUpdateSelectedEntries(const QString& source_) { 0338 if(m_selectedEntries.isEmpty()) { 0339 return; 0340 } 0341 0342 // it deletes itself when done 0343 // signal mapper strings can't be empty, "_all" is set in mainwindow 0344 if(source_.isEmpty() || source_ == QLatin1String("_all")) { 0345 new EntryUpdater(m_selectedEntries.front()->collection(), m_selectedEntries, this); 0346 } else { 0347 new EntryUpdater(source_, m_selectedEntries.front()->collection(), m_selectedEntries, this); 0348 } 0349 } 0350 0351 void Controller::slotDeleteSelectedEntries() { 0352 if(m_selectedEntries.isEmpty()) { 0353 return; 0354 } 0355 0356 m_working = true; 0357 0358 // confirm delete 0359 if(m_selectedEntries.count() == 1) { 0360 QString str = i18n("Do you really want to delete this entry?"); 0361 QString dontAsk = QStringLiteral("DeleteEntry"); 0362 int ret = KMessageBox::warningContinueCancel(Kernel::self()->widget(), str, i18n("Delete Entry"), 0363 KStandardGuiItem::del(), 0364 KStandardGuiItem::cancel(), dontAsk); 0365 if(ret != KMessageBox::Continue) { 0366 m_working = false; 0367 return; 0368 } 0369 } else { 0370 QStringList names; 0371 foreach(Data::EntryPtr entry, m_selectedEntries) { 0372 names += entry->title(); 0373 } 0374 QString str = i18n("Do you really want to delete these entries?"); 0375 // historically called DeleteMultipleBooks, don't change 0376 QString dontAsk = QStringLiteral("DeleteMultipleBooks"); 0377 int ret = KMessageBox::warningContinueCancelList(Kernel::self()->widget(), str, names, 0378 i18n("Delete Multiple Entries"), 0379 KStandardGuiItem::del(), 0380 KStandardGuiItem::cancel(), dontAsk); 0381 if(ret != KMessageBox::Continue) { 0382 m_working = false; 0383 return; 0384 } 0385 } 0386 0387 GUI::CursorSaver cs; 0388 Kernel::self()->removeEntries(m_selectedEntries); 0389 updateActions(); 0390 0391 m_working = false; 0392 0393 // special case, the detailed list view selects the next item, so handle that 0394 // Data::EntryList newList; 0395 // for(GUI::ListViewItemListIt it(m_mainWindow->m_detailedView->selectedItems()); it.current(); ++it) { 0396 // newList.append(static_cast<EntryItem*>(it.current())->entry()); 0397 // } 0398 // slotUpdateSelection(m_mainWindow->m_detailedView, newList); 0399 slotClearSelection(); 0400 } 0401 0402 void Controller::slotMergeSelectedEntries() { 0403 // merge requires at least 2 entries 0404 if(m_selectedEntries.count() < 2) { 0405 return; 0406 } 0407 0408 new EntryMerger(m_selectedEntries, this); 0409 } 0410 0411 void Controller::slotRefreshField(Tellico::Data::FieldPtr field_) { 0412 // DEBUG_LINE; 0413 // group view only needs to refresh if it's the title 0414 if(field_->name() == QLatin1String("title")) { 0415 m_mainWindow->m_groupView->populateCollection(); 0416 } 0417 m_mainWindow->m_detailedView->slotRefresh(); 0418 m_mainWindow->m_entryView->slotRefresh(); 0419 } 0420 0421 void Controller::slotCopySelectedEntries() { 0422 if(m_selectedEntries.isEmpty()) { 0423 return; 0424 } 0425 0426 // keep copy of selected entries 0427 Data::EntryList old = m_selectedEntries; 0428 0429 GUI::CursorSaver cs; 0430 // need to create copies 0431 Data::EntryList entries; 0432 foreach(Data::EntryPtr it, m_selectedEntries) { 0433 entries.append(Data::EntryPtr(new Data::Entry(*it))); 0434 } 0435 Kernel::self()->addEntries(entries, false); 0436 slotUpdateSelection(old); 0437 } 0438 0439 void Controller::blockAllSignals(bool block_) const { 0440 // sanity check 0441 if(!m_mainWindow->m_initialized) { 0442 return; 0443 } 0444 m_mainWindow->m_detailedView->blockSignals(block_); 0445 m_mainWindow->m_groupView->blockSignals(block_); 0446 m_mainWindow->m_quickFilter->blockSignals(block_); 0447 if(m_mainWindow->m_loanView) { 0448 m_mainWindow->m_loanView->blockSignals(block_); 0449 } 0450 if(m_mainWindow->m_filterView) { 0451 m_mainWindow->m_filterView->blockSignals(block_); 0452 } 0453 m_mainWindow->m_editDialog->blockSignals(block_); 0454 m_mainWindow->m_iconView->blockSignals(block_); 0455 } 0456 0457 void Controller::slotUpdateFilter(Tellico::FilterPtr filter_) { 0458 blockAllSignals(true); 0459 0460 // the view takes over ownership of the filter 0461 if(filter_ && !filter_->isEmpty()) { 0462 // clear the icon view selection only 0463 // the detailed view takes care of itself 0464 m_mainWindow->m_iconView->clearSelection(); 0465 m_selectedEntries.clear(); 0466 } 0467 updateActions(); 0468 0469 m_mainWindow->m_detailedView->setFilter(filter_); // takes ownership 0470 if(!filter_ && m_mainWindow->m_filterView && !m_mainWindow->m_dontQueueFilter) { 0471 // for example, when quick filter clears the selection 0472 // the check against m_dontQueueFilter is to prevent the situation when the FilterView has an Entry selected 0473 // which sends an empty filter selection, which would then clear the whole FilterView selection 0474 m_mainWindow->m_filterView->clearSelection(); 0475 } 0476 0477 blockAllSignals(false); 0478 0479 m_mainWindow->slotEntryCount(); 0480 } 0481 0482 void Controller::clearFilter() { 0483 blockAllSignals(true); 0484 m_mainWindow->m_quickFilter->clear(); 0485 m_mainWindow->m_detailedView->setFilter(Tellico::FilterPtr()); 0486 blockAllSignals(false); 0487 } 0488 0489 void Controller::editEntry(Tellico::Data::EntryPtr entry_) const { 0490 m_mainWindow->slotShowEntryEditor(); 0491 m_mainWindow->m_editDialog->setContents(Data::EntryList() << entry_); 0492 } 0493 0494 void Controller::plugCollectionActions(QMenu* popup_) { 0495 if(!popup_) { 0496 return; 0497 } 0498 0499 popup_->addAction(m_mainWindow->action("coll_rename_collection")); 0500 popup_->addAction(m_mainWindow->action("coll_fields")); 0501 popup_->addAction(m_mainWindow->action("change_entry_grouping")); 0502 } 0503 0504 void Controller::plugEntryActions(QMenu* popup_) { 0505 if(!popup_) { 0506 return; 0507 } 0508 0509 // m_mainWindow->m_newEntry->plug(popup_); 0510 popup_->addAction(m_mainWindow->m_editEntry); 0511 popup_->addAction(m_mainWindow->m_copyEntry); 0512 popup_->addAction(m_mainWindow->m_deleteEntry); 0513 popup_->addAction(m_mainWindow->m_mergeEntry); 0514 popup_->addMenu(m_mainWindow->m_updateEntryMenu->menu()); 0515 // there's a bug in KActionMenu with KXMLGUIFactory::plugActionList 0516 // pluging the menu isn't enough to have the popup get populated 0517 plugUpdateMenu(popup_); 0518 popup_->addSeparator(); 0519 popup_->addAction(m_mainWindow->m_checkOutEntry); 0520 } 0521 0522 QMenu* Controller::plugSortActions(QMenu* popup_) { 0523 if(!popup_) { 0524 return nullptr; 0525 } 0526 0527 QMenu* sortMenu = popup_->addMenu(i18n("&Sort By")); 0528 sortMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-sort"), 0529 QIcon::fromTheme(QStringLiteral("view-sort-ascending")))); 0530 foreach(Data::FieldPtr field, Data::Document::self()->collection()->fields()) { 0531 // not allowed to sort by Image, Table, Para, or URL 0532 if(field->type() == Data::Field::Image || 0533 field->type() == Data::Field::Table || 0534 field->type() == Data::Field::URL || 0535 field->type() == Data::Field::Para) { 0536 continue; 0537 } 0538 sortMenu->addAction(field->title())->setData(QVariant::fromValue(field)); 0539 } 0540 return sortMenu; 0541 } 0542 0543 void Controller::plugUpdateMenu(QMenu* popup_) { 0544 QMenu* updatePopup = nullptr; 0545 foreach(QAction* action, popup_->actions()) { 0546 if(action && action->text() == m_mainWindow->m_updateEntryMenu->text()) { 0547 updatePopup = action->menu(); 0548 break; 0549 } 0550 } 0551 0552 if(!updatePopup) { 0553 return; 0554 } 0555 0556 // I can't figure out why the actions get duplicated, but they do 0557 // so clear them all 0558 updatePopup->removeAction(m_mainWindow->m_updateAll); 0559 foreach(QAction* action, m_mainWindow->m_fetchActions) { 0560 updatePopup->removeAction(action); 0561 } 0562 0563 // clear separator, too 0564 updatePopup->clear(); 0565 0566 updatePopup->addAction(m_mainWindow->m_updateAll); 0567 updatePopup->addSeparator(); 0568 foreach(QAction* action, m_mainWindow->m_fetchActions) { 0569 updatePopup->addAction(action); 0570 } 0571 } 0572 0573 void Controller::updateActions() const { 0574 const bool emptySelection = m_selectedEntries.isEmpty(); 0575 m_mainWindow->stateChanged(QStringLiteral("empty_selection"), 0576 emptySelection ? KXMLGUIClient::StateNoReverse : KXMLGUIClient::StateReverse); 0577 foreach(QAction* action, m_mainWindow->m_fetchActions) { 0578 action->setEnabled(!emptySelection); 0579 } 0580 //only enable citation items when it's a bibliography 0581 const bool isBibtex = Kernel::self()->collectionType() == Data::Collection::Bibtex; 0582 if(isBibtex) { 0583 m_mainWindow->action("cite_clipboard")->setEnabled(!emptySelection); 0584 m_mainWindow->action("cite_lyxpipe")->setEnabled(!emptySelection); 0585 } 0586 m_mainWindow->m_checkInEntry->setEnabled(canCheckIn()); 0587 0588 if(m_selectedEntries.count() < 2) { 0589 m_mainWindow->m_editEntry->setText(i18n("&Edit Entry...")); 0590 m_mainWindow->m_copyEntry->setText(i18n("D&uplicate Entry")); 0591 m_mainWindow->m_updateEntryMenu->setText(i18n("&Update Entry")); 0592 m_mainWindow->m_deleteEntry->setText(i18n("&Delete Entry")); 0593 m_mainWindow->m_mergeEntry->setEnabled(false); 0594 } else { 0595 m_mainWindow->m_editEntry->setText(i18n("&Edit Entries...")); 0596 m_mainWindow->m_copyEntry->setText(i18n("D&uplicate Entries")); 0597 m_mainWindow->m_updateEntryMenu->setText(i18n("&Update Entries")); 0598 m_mainWindow->m_deleteEntry->setText(i18n("&Delete Entries")); 0599 m_mainWindow->m_mergeEntry->setEnabled(true); 0600 } 0601 } 0602 0603 void Controller::addedBorrower(Tellico::Data::BorrowerPtr borrower_) { 0604 m_mainWindow->addLoanView(); // just in case 0605 foreach(Observer* obs, m_observers) { 0606 obs->addBorrower(borrower_); 0607 } 0608 m_mainWindow->m_viewTabs->setTabBarHidden(false); 0609 } 0610 0611 void Controller::modifiedBorrower(Tellico::Data::BorrowerPtr borrower_) { 0612 foreach(Observer* obs, m_observers) { 0613 if(borrower_->isEmpty()) { 0614 obs->removeBorrower(borrower_); 0615 } else { 0616 obs->modifyBorrower(borrower_); 0617 } 0618 } 0619 hideTabs(); 0620 } 0621 0622 void Controller::addedFilter(Tellico::FilterPtr filter_) { 0623 m_mainWindow->addFilterView(); // just in case 0624 foreach(Observer* obs, m_observers) { 0625 obs->addFilter(filter_); 0626 } 0627 m_mainWindow->m_viewTabs->setTabBarHidden(false); 0628 } 0629 0630 void Controller::removedFilter(Tellico::FilterPtr filter_) { 0631 foreach(Observer* obs, m_observers) { 0632 obs->removeFilter(filter_); 0633 } 0634 hideTabs(); 0635 } 0636 0637 void Controller::slotCheckOut() { 0638 if(m_selectedEntries.isEmpty()) { 0639 return; 0640 } 0641 0642 Data::EntryList loanedEntries = m_selectedEntries; 0643 0644 // check to see if any of the entries are already on-loan, and warn user 0645 QMap<QString, Data::EntryPtr> alreadyLoaned; 0646 foreach(Data::BorrowerPtr borrower, Data::Document::self()->collection()->borrowers()) { 0647 foreach(Data::LoanPtr loan, borrower->loans()) { 0648 if(m_selectedEntries.contains(loan->entry())) { 0649 alreadyLoaned.insert(loan->entry()->title(), loan->entry()); 0650 } 0651 } 0652 } 0653 if(!alreadyLoaned.isEmpty()) { 0654 KMessageBox::informationList(Kernel::self()->widget(), 0655 i18n("The following items are already loaned, but Tellico " 0656 "does not currently support lending an item multiple " 0657 "times. They will be removed from the list of items " 0658 "to lend."), 0659 alreadyLoaned.keys()); 0660 QMap<QString, Data::EntryPtr>::const_iterator it = alreadyLoaned.constBegin(); 0661 QMap<QString, Data::EntryPtr>::const_iterator end = alreadyLoaned.constEnd(); 0662 for( ; it != end; ++it) { 0663 loanedEntries.removeAll(it.value()); 0664 } 0665 if(loanedEntries.isEmpty()) { 0666 return; 0667 } 0668 } 0669 0670 if(Kernel::self()->addLoans(loanedEntries)) { 0671 m_mainWindow->m_checkInEntry->setEnabled(true); 0672 } 0673 } 0674 0675 void Controller::slotCheckIn() { 0676 slotCheckIn(m_selectedEntries); 0677 } 0678 0679 void Controller::slotCheckIn(const Tellico::Data::EntryList& entries_) { 0680 if(entries_.isEmpty()) { 0681 return; 0682 } 0683 0684 Data::LoanList loans; 0685 foreach(Data::EntryPtr entry, entries_) { 0686 // these have to be in the loop since if a borrower gets empty 0687 // it will be deleted, so the vector could change, for every entry iterator 0688 Data::BorrowerList vec = Data::Document::self()->collection()->borrowers(); 0689 foreach(Data::BorrowerPtr borrower, vec) { 0690 Data::LoanPtr l = borrower->loan(entry); 0691 if(l) { 0692 loans.append(l); 0693 // assume it's only loaned once 0694 break; 0695 } 0696 } 0697 } 0698 0699 if(Kernel::self()->removeLoans(loans)) { 0700 m_mainWindow->m_checkInEntry->setEnabled(false); 0701 } 0702 hideTabs(); 0703 } 0704 0705 void Controller::hideTabs() const { 0706 if((!m_mainWindow->m_filterView || m_mainWindow->m_filterView->isEmpty()) && 0707 (!m_mainWindow->m_loanView || m_mainWindow->m_loanView->isEmpty())) { 0708 int idx = m_mainWindow->m_viewTabs->indexOf(m_mainWindow->m_groupView); 0709 m_mainWindow->m_viewTabs->setCurrentIndex(idx); 0710 m_mainWindow->m_viewTabs->setTabBarHidden(true); 0711 } 0712 } 0713 0714 bool Controller::canCheckIn() const { 0715 foreach(Data::EntryPtr entry, m_selectedEntries) { 0716 if(entry->field(QStringLiteral("loaned")) == QLatin1String("true")) { 0717 return true; 0718 } 0719 } 0720 return false; 0721 } 0722 0723 void Controller::updatedFetchers() { 0724 m_mainWindow->updateEntrySources(); 0725 }