1 /***************************************************************************
2 Copyright (C) 2003-2009 Robby Stephenson <robby@periapsis.org>
3 ***************************************************************************/
4
5 /***************************************************************************
6 * *
7 * This program is free software; you can redistribute it and/or *
8 * modify it under the terms of the GNU General Public License as *
9 * published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) version 3 or any later version *
11 * accepted by the membership of KDE e.V. (or its successor approved *
12 * by the membership of KDE e.V.), which shall act as a proxy *
13 * defined in Section 14 of version 3 of the license. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program. If not, see <http://www.gnu.org/licenses/>. *
22 * *
23 ***************************************************************************/
24
25 #include "controller.h"
26 #include "mainwindow.h"
27 #include "groupview.h"
28 #include "detailedlistview.h"
29 #include "entryeditdialog.h"
30 #include "entryview.h"
31 #include "entryiconview.h"
32 #include "entry.h"
33 #include "entrygroup.h"
34 #include "field.h"
35 #include "filter.h"
36 #include "filterdialog.h"
37 #include "tellico_kernel.h"
38 #include "collection.h"
39 #include "document.h"
40 #include "borrower.h"
41 #include "filterview.h"
42 #include "loanview.h"
43 #include "entryupdater.h"
44 #include "entrymerger.h"
45 #include "utils/cursorsaver.h"
46 #include "gui/lineedit.h"
47 #include "gui/tabwidget.h"
48 #include "tellico_debug.h"
49
50 #include <KLocalizedString>
51 #include <KMessageBox>
52 #include <KActionMenu>
53
54 #include <QMenu>
55
56 using Tellico::Controller;
57
58 Controller* Controller::s_self = nullptr;
59
Controller(Tellico::MainWindow * parent_)60 Controller::Controller(Tellico::MainWindow* parent_)
61 : QObject(parent_), m_mainWindow(parent_), m_working(false) {
62 }
63
~Controller()64 Controller::~Controller() {
65 }
66
addObserver(Tellico::Observer * obs)67 void Controller::addObserver(Tellico::Observer* obs) {
68 m_observers.append(obs);
69 }
70
removeObserver(Tellico::Observer * obs)71 void Controller::removeObserver(Tellico::Observer* obs) {
72 m_observers.removeAll(obs);
73 }
74
groupBy() const75 QString Controller::groupBy() const {
76 return m_mainWindow->m_groupView->groupBy();
77 }
78
expandedGroupBy() const79 QStringList Controller::expandedGroupBy() const {
80 QStringList g;
81 g << groupBy();
82 // special case for pseudo-group
83 if(g[0] == Data::Collection::s_peopleGroupName) {
84 g.clear();
85 Data::FieldList fields = Data::Document::self()->collection()->peopleFields();
86 foreach(Data::FieldPtr field, fields) {
87 g << field->name();
88 }
89 }
90 // special case for no groups
91 if(g[0].isEmpty()) {
92 g.clear();
93 }
94 return g;
95 }
96
sortTitles() const97 QStringList Controller::sortTitles() const {
98 QStringList list;
99 list << m_mainWindow->m_detailedView->sortColumnTitle1();
100 list << m_mainWindow->m_detailedView->sortColumnTitle2();
101 list << m_mainWindow->m_detailedView->sortColumnTitle3();
102 return list;
103 }
104
visibleColumns() const105 QStringList Controller::visibleColumns() const {
106 return m_mainWindow->m_detailedView->visibleColumns();
107 }
108
visibleEntries()109 Tellico::Data::EntryList Controller::visibleEntries() {
110 return m_mainWindow->m_detailedView->visibleEntries();
111 }
112
slotCollectionAdded(Tellico::Data::CollPtr coll_)113 void Controller::slotCollectionAdded(Tellico::Data::CollPtr coll_) {
114 MARK;
115 // at start-up, this might get called too early, so check and bail
116 if(!coll_ || !m_mainWindow->m_groupView) {
117 return;
118 }
119
120 // do this first because the group view will need it later
121 m_mainWindow->readCollectionOptions(coll_);
122 m_mainWindow->slotUpdateToolbarIcons();
123 m_mainWindow->updateEntrySources(); // has to be called before all the addCollection()
124 // calls in the widgets since they may want menu updates
125
126 // blockAllSignals(true);
127 m_mainWindow->m_detailedView->addCollection(coll_);
128 m_mainWindow->m_groupView->addCollection(coll_);
129 m_mainWindow->m_editDialog->resetLayout(coll_);
130 if(!coll_->filters().isEmpty()) {
131 m_mainWindow->addFilterView();
132 m_mainWindow->m_filterView->addCollection(coll_);
133 m_mainWindow->m_viewTabs->setTabBarHidden(false);
134 }
135 if(!coll_->borrowers().isEmpty()) {
136 m_mainWindow->addLoanView();
137 m_mainWindow->m_loanView->addCollection(coll_);
138 m_mainWindow->m_viewTabs->setTabBarHidden(false);
139 }
140 // blockAllSignals(false);
141
142 m_mainWindow->slotStatusMsg(i18n("Ready."));
143
144 m_selectedEntries.clear();
145 m_mainWindow->slotEntryCount();
146
147 // there really should be a lot of signals to connect to, but right now, the only one
148 // is used when a field is added on a merge
149 connect(&*coll_, &Data::Collection::mergeAddedField,
150 this, &Controller::slotFieldAdded);
151
152 emit collectionAdded(coll_->type());
153
154 updateActions();
155
156 connect(&*coll_, &Data::Collection::signalGroupsModified,
157 m_mainWindow->m_groupView, &GroupView::slotModifyGroups);
158 connect(&*coll_, &Data::Collection::signalRefreshField,
159 this, &Controller::slotRefreshField);
160 }
161
slotCollectionModified(Tellico::Data::CollPtr coll_)162 void Controller::slotCollectionModified(Tellico::Data::CollPtr coll_) {
163 // easiest thing is to signal collection deleted, then added?
164 // FIXME: Signals for delete collection and then added are yucky
165 slotCollectionDeleted(coll_);
166 slotCollectionAdded(coll_);
167 // https://bugs.kde.org/show_bug.cgi?id=386549
168 // at some point, I need to revisit the ::setImagesAreAvailable() methodology
169 // there are too many workarounds in the code for that
170 m_mainWindow->m_detailedView->slotRefreshImages();
171 }
172
slotCollectionDeleted(Tellico::Data::CollPtr coll_)173 void Controller::slotCollectionDeleted(Tellico::Data::CollPtr coll_) {
174 blockAllSignals(true);
175 m_mainWindow->saveCollectionOptions(coll_);
176 m_mainWindow->m_groupView->removeCollection(coll_);
177 if(m_mainWindow->m_filterView) {
178 m_mainWindow->m_filterView->slotReset();
179 }
180 if(m_mainWindow->m_loanView) {
181 m_mainWindow->m_loanView->slotReset();
182 }
183 m_mainWindow->m_detailedView->removeCollection(coll_);
184 m_mainWindow->m_entryView->clear();
185 blockAllSignals(false);
186
187 // disconnect all signals from the collection
188 // this is needed because the Collection::appendCollection() and mergeCollection()
189 // functions signal collection deleted then added for the same collection
190 coll_->disconnect();
191 }
192
slotFieldAdded(Tellico::Data::CollPtr coll_,Tellico::Data::FieldPtr field_)193 void Controller::slotFieldAdded(Tellico::Data::CollPtr coll_, Tellico::Data::FieldPtr field_) {
194 addedField(coll_, field_);
195 }
196
197 // TODO: should be adding entries to models rather than to widget observers
addedEntries(Tellico::Data::EntryList entries_)198 void Controller::addedEntries(Tellico::Data::EntryList entries_) {
199 blockAllSignals(true);
200 foreach(Observer* obs, m_observers) {
201 obs->addEntries(entries_);
202 }
203 m_mainWindow->slotQueueFilter();
204 blockAllSignals(false);
205 }
206
modifiedEntries(Tellico::Data::EntryList entries_)207 void Controller::modifiedEntries(Tellico::Data::EntryList entries_) {
208 // when a new document is being loaded, loans are added to borrowers, which
209 // end up calling Entry::checkIn() which called Document::saveEntry() which calls here
210 // ignore that
211 if(!m_mainWindow->m_initialized) {
212 return;
213 }
214 blockAllSignals(true);
215 foreach(Observer* obs, m_observers) {
216 obs->modifyEntries(entries_);
217 }
218 m_mainWindow->m_entryView->slotRefresh(); // special case
219 blockAllSignals(false);
220 }
221
removedEntries(Tellico::Data::EntryList entries_)222 void Controller::removedEntries(Tellico::Data::EntryList entries_) {
223 blockAllSignals(true);
224 foreach(Observer* obs, m_observers) {
225 obs->removeEntries(entries_);
226 }
227 foreach(Data::EntryPtr entry, entries_) {
228 m_selectedEntries.removeAll(entry);
229 }
230 m_mainWindow->slotEntryCount();
231 m_mainWindow->slotQueueFilter();
232 blockAllSignals(false);
233 }
234
addedField(Tellico::Data::CollPtr coll_,Tellico::Data::FieldPtr field_)235 void Controller::addedField(Tellico::Data::CollPtr coll_, Tellico::Data::FieldPtr field_) {
236 foreach(Observer* obs, m_observers) {
237 obs->addField(coll_, field_);
238 }
239 m_mainWindow->m_entryView->slotRefresh();
240 m_mainWindow->slotUpdateCollectionToolBar(coll_);
241 m_mainWindow->slotQueueFilter();
242 }
243
removedField(Tellico::Data::CollPtr coll_,Tellico::Data::FieldPtr field_)244 void Controller::removedField(Tellico::Data::CollPtr coll_, Tellico::Data::FieldPtr field_) {
245 foreach(Observer* obs, m_observers) {
246 obs->removeField(coll_, field_);
247 }
248 m_mainWindow->m_entryView->slotRefresh();
249 m_mainWindow->slotUpdateCollectionToolBar(coll_);
250 m_mainWindow->slotQueueFilter();
251 }
252
modifiedField(Tellico::Data::CollPtr coll_,Tellico::Data::FieldPtr oldField_,Tellico::Data::FieldPtr newField_)253 void Controller::modifiedField(Tellico::Data::CollPtr coll_, Tellico::Data::FieldPtr oldField_, Tellico::Data::FieldPtr newField_) {
254 foreach(Observer* obs, m_observers) {
255 obs->modifyField(coll_, oldField_, newField_);
256 }
257 m_mainWindow->m_entryView->slotRefresh();
258 m_mainWindow->slotUpdateCollectionToolBar(coll_);
259 m_mainWindow->slotQueueFilter();
260 }
261
reorderedFields(Tellico::Data::CollPtr coll_)262 void Controller::reorderedFields(Tellico::Data::CollPtr coll_) {
263 m_mainWindow->m_editDialog->resetLayout(coll_);
264 m_mainWindow->m_detailedView->reorderFields(coll_->fields());
265 m_mainWindow->slotUpdateCollectionToolBar(coll_);
266 m_mainWindow->m_entryView->slotRefresh();
267 }
268
slotClearSelection()269 void Controller::slotClearSelection() {
270 if(m_working) {
271 return;
272 }
273
274 m_working = true;
275 blockAllSignals(true);
276
277 m_mainWindow->m_detailedView->clearSelection();
278 m_mainWindow->m_iconView->clearSelection();
279 m_mainWindow->m_groupView->clearSelection();
280 if(m_mainWindow->m_filterView) {
281 m_mainWindow->m_filterView->clearSelection();
282 }
283 if(m_mainWindow->m_loanView) {
284 m_mainWindow->m_loanView->clearSelection();
285 }
286
287 blockAllSignals(false);
288
289 m_selectedEntries.clear();
290 updateActions();
291 m_mainWindow->slotEntryCount();
292 m_working = false;
293 }
294
slotUpdateSelection(const Tellico::Data::EntryList & entries_)295 void Controller::slotUpdateSelection(const Tellico::Data::EntryList& entries_) {
296 if(m_working) {
297 return;
298 }
299 m_working = true;
300
301 m_selectedEntries = entries_;
302 updateActions();
303 m_mainWindow->slotEntryCount();
304 m_working = false;
305 }
306
slotUpdateSelectedEntries(const QString & source_)307 void Controller::slotUpdateSelectedEntries(const QString& source_) {
308 if(m_selectedEntries.isEmpty()) {
309 return;
310 }
311
312 // it deletes itself when done
313 // signal mapper strings can't be empty, "_all" is set in mainwindow
314 if(source_.isEmpty() || source_ == QLatin1String("_all")) {
315 new EntryUpdater(m_selectedEntries.front()->collection(), m_selectedEntries, this);
316 } else {
317 new EntryUpdater(source_, m_selectedEntries.front()->collection(), m_selectedEntries, this);
318 }
319 }
320
slotDeleteSelectedEntries()321 void Controller::slotDeleteSelectedEntries() {
322 if(m_selectedEntries.isEmpty()) {
323 return;
324 }
325
326 m_working = true;
327
328 // confirm delete
329 if(m_selectedEntries.count() == 1) {
330 QString str = i18n("Do you really want to delete this entry?");
331 QString dontAsk = QStringLiteral("DeleteEntry");
332 int ret = KMessageBox::warningContinueCancel(Kernel::self()->widget(), str, i18n("Delete Entry"),
333 KStandardGuiItem::del(),
334 KStandardGuiItem::cancel(), dontAsk);
335 if(ret != KMessageBox::Continue) {
336 m_working = false;
337 return;
338 }
339 } else {
340 QStringList names;
341 foreach(Data::EntryPtr entry, m_selectedEntries) {
342 names += entry->title();
343 }
344 QString str = i18n("Do you really want to delete these entries?");
345 // historically called DeleteMultipleBooks, don't change
346 QString dontAsk = QStringLiteral("DeleteMultipleBooks");
347 int ret = KMessageBox::warningContinueCancelList(Kernel::self()->widget(), str, names,
348 i18n("Delete Multiple Entries"),
349 KStandardGuiItem::del(),
350 KStandardGuiItem::cancel(), dontAsk);
351 if(ret != KMessageBox::Continue) {
352 m_working = false;
353 return;
354 }
355 }
356
357 GUI::CursorSaver cs;
358 Kernel::self()->removeEntries(m_selectedEntries);
359 updateActions();
360
361 m_working = false;
362
363 // special case, the detailed list view selects the next item, so handle that
364 // Data::EntryList newList;
365 // for(GUI::ListViewItemListIt it(m_mainWindow->m_detailedView->selectedItems()); it.current(); ++it) {
366 // newList.append(static_cast<EntryItem*>(it.current())->entry());
367 // }
368 // slotUpdateSelection(m_mainWindow->m_detailedView, newList);
369 slotClearSelection();
370 }
371
slotMergeSelectedEntries()372 void Controller::slotMergeSelectedEntries() {
373 // merge requires at least 2 entries
374 if(m_selectedEntries.count() < 2) {
375 return;
376 }
377
378 new EntryMerger(m_selectedEntries, this);
379 }
380
slotRefreshField(Tellico::Data::FieldPtr field_)381 void Controller::slotRefreshField(Tellico::Data::FieldPtr field_) {
382 // DEBUG_LINE;
383 // group view only needs to refresh if it's the title
384 if(field_->name() == QLatin1String("title")) {
385 m_mainWindow->m_groupView->populateCollection();
386 }
387 m_mainWindow->m_detailedView->slotRefresh();
388 m_mainWindow->m_entryView->slotRefresh();
389 }
390
slotCopySelectedEntries()391 void Controller::slotCopySelectedEntries() {
392 if(m_selectedEntries.isEmpty()) {
393 return;
394 }
395
396 // keep copy of selected entries
397 Data::EntryList old = m_selectedEntries;
398
399 GUI::CursorSaver cs;
400 // need to create copies
401 Data::EntryList entries;
402 foreach(Data::EntryPtr it, m_selectedEntries) {
403 entries.append(Data::EntryPtr(new Data::Entry(*it)));
404 }
405 Kernel::self()->addEntries(entries, false);
406 slotUpdateSelection(old);
407 }
408
blockAllSignals(bool block_) const409 void Controller::blockAllSignals(bool block_) const {
410 // sanity check
411 if(!m_mainWindow->m_initialized) {
412 return;
413 }
414 m_mainWindow->m_detailedView->blockSignals(block_);
415 m_mainWindow->m_groupView->blockSignals(block_);
416 m_mainWindow->m_quickFilter->blockSignals(block_);
417 if(m_mainWindow->m_loanView) {
418 m_mainWindow->m_loanView->blockSignals(block_);
419 }
420 if(m_mainWindow->m_filterView) {
421 m_mainWindow->m_filterView->blockSignals(block_);
422 }
423 m_mainWindow->m_editDialog->blockSignals(block_);
424 m_mainWindow->m_iconView->blockSignals(block_);
425 }
426
slotUpdateFilter(Tellico::FilterPtr filter_)427 void Controller::slotUpdateFilter(Tellico::FilterPtr filter_) {
428 blockAllSignals(true);
429
430 // the view takes over ownership of the filter
431 if(filter_ && !filter_->isEmpty()) {
432 // clear the icon view selection only
433 // the detailed view takes care of itself
434 m_mainWindow->m_iconView->clearSelection();
435 m_selectedEntries.clear();
436 }
437 updateActions();
438
439 m_mainWindow->m_detailedView->setFilter(filter_); // takes ownership
440 if(!filter_ && m_mainWindow->m_filterView && !m_mainWindow->m_dontQueueFilter) {
441 // for example, when quick filter clears the selection
442 // the check against m_dontQueueFilter is to prevent the situation when the FilterView has an Entry selected
443 // which sends an empty filter selection, which would then clear the whole FilterView selection
444 m_mainWindow->m_filterView->clearSelection();
445 }
446
447 blockAllSignals(false);
448
449 m_mainWindow->slotEntryCount();
450 }
451
clearFilter()452 void Controller::clearFilter() {
453 blockAllSignals(true);
454 m_mainWindow->m_quickFilter->clear();
455 m_mainWindow->m_detailedView->setFilter(Tellico::FilterPtr());
456 blockAllSignals(false);
457 }
458
editEntry(Tellico::Data::EntryPtr entry_) const459 void Controller::editEntry(Tellico::Data::EntryPtr entry_) const {
460 m_mainWindow->slotShowEntryEditor();
461 m_mainWindow->m_editDialog->setContents(Data::EntryList() << entry_);
462 }
463
plugCollectionActions(QMenu * popup_)464 void Controller::plugCollectionActions(QMenu* popup_) {
465 if(!popup_) {
466 return;
467 }
468
469 popup_->addAction(m_mainWindow->action("coll_rename_collection"));
470 popup_->addAction(m_mainWindow->action("coll_fields"));
471 popup_->addAction(m_mainWindow->action("change_entry_grouping"));
472 }
473
plugEntryActions(QMenu * popup_)474 void Controller::plugEntryActions(QMenu* popup_) {
475 if(!popup_) {
476 return;
477 }
478
479 // m_mainWindow->m_newEntry->plug(popup_);
480 popup_->addAction(m_mainWindow->m_editEntry);
481 popup_->addAction(m_mainWindow->m_copyEntry);
482 popup_->addAction(m_mainWindow->m_deleteEntry);
483 popup_->addAction(m_mainWindow->m_mergeEntry);
484 popup_->addMenu(m_mainWindow->m_updateEntryMenu->menu());
485 // there's a bug in KActionMenu with KXMLGUIFactory::plugActionList
486 // pluging the menu isn't enough to have the popup get populated
487 plugUpdateMenu(popup_);
488 popup_->addSeparator();
489 popup_->addAction(m_mainWindow->m_checkOutEntry);
490 }
491
plugSortActions(QMenu * popup_)492 QMenu* Controller::plugSortActions(QMenu* popup_) {
493 if(!popup_) {
494 return nullptr;
495 }
496
497 QMenu* sortMenu = popup_->addMenu(i18n("&Sort By"));
498 sortMenu->setIcon(QIcon::fromTheme(QStringLiteral("view-sort"),
499 QIcon::fromTheme(QStringLiteral("view-sort-ascending"))));
500 foreach(Data::FieldPtr field, Data::Document::self()->collection()->fields()) {
501 // not allowed to sort by Image, Table, Para, or URL
502 if(field->type() == Data::Field::Image ||
503 field->type() == Data::Field::Table ||
504 field->type() == Data::Field::URL ||
505 field->type() == Data::Field::Para) {
506 continue;
507 }
508 sortMenu->addAction(field->title())->setData(QVariant::fromValue(field));
509 }
510 return sortMenu;
511 }
512
plugUpdateMenu(QMenu * popup_)513 void Controller::plugUpdateMenu(QMenu* popup_) {
514 QMenu* updatePopup = nullptr;
515 foreach(QAction* action, popup_->actions()) {
516 if(action && action->text() == m_mainWindow->m_updateEntryMenu->text()) {
517 updatePopup = action->menu();
518 break;
519 }
520 }
521
522 if(!updatePopup) {
523 return;
524 }
525
526 // I can't figure out why the actions get duplicated, but they do
527 // so clear them all
528 updatePopup->removeAction(m_mainWindow->m_updateAll);
529 foreach(QAction* action, m_mainWindow->m_fetchActions) {
530 updatePopup->removeAction(action);
531 }
532
533 // clear separator, too
534 updatePopup->clear();
535
536 updatePopup->addAction(m_mainWindow->m_updateAll);
537 updatePopup->addSeparator();
538 foreach(QAction* action, m_mainWindow->m_fetchActions) {
539 updatePopup->addAction(action);
540 }
541 }
542
updateActions() const543 void Controller::updateActions() const {
544 const bool emptySelection = m_selectedEntries.isEmpty();
545 m_mainWindow->stateChanged(QStringLiteral("empty_selection"),
546 emptySelection ? KXMLGUIClient::StateNoReverse : KXMLGUIClient::StateReverse);
547 foreach(QAction* action, m_mainWindow->m_fetchActions) {
548 action->setEnabled(!emptySelection);
549 }
550 //only enable citation items when it's a bibliography
551 const bool isBibtex = Kernel::self()->collectionType() == Data::Collection::Bibtex;
552 if(isBibtex) {
553 m_mainWindow->action("cite_clipboard")->setEnabled(!emptySelection);
554 m_mainWindow->action("cite_lyxpipe")->setEnabled(!emptySelection);
555 }
556 m_mainWindow->m_checkInEntry->setEnabled(canCheckIn());
557
558 if(m_selectedEntries.count() < 2) {
559 m_mainWindow->m_editEntry->setText(i18n("&Edit Entry..."));
560 m_mainWindow->m_copyEntry->setText(i18n("D&uplicate Entry"));
561 m_mainWindow->m_updateEntryMenu->setText(i18n("&Update Entry"));
562 m_mainWindow->m_deleteEntry->setText(i18n("&Delete Entry"));
563 m_mainWindow->m_mergeEntry->setEnabled(false);
564 } else {
565 m_mainWindow->m_editEntry->setText(i18n("&Edit Entries..."));
566 m_mainWindow->m_copyEntry->setText(i18n("D&uplicate Entries"));
567 m_mainWindow->m_updateEntryMenu->setText(i18n("&Update Entries"));
568 m_mainWindow->m_deleteEntry->setText(i18n("&Delete Entries"));
569 m_mainWindow->m_mergeEntry->setEnabled(true);
570 }
571 }
572
addedBorrower(Tellico::Data::BorrowerPtr borrower_)573 void Controller::addedBorrower(Tellico::Data::BorrowerPtr borrower_) {
574 m_mainWindow->addLoanView(); // just in case
575 foreach(Observer* obs, m_observers) {
576 obs->addBorrower(borrower_);
577 }
578 m_mainWindow->m_viewTabs->setTabBarHidden(false);
579 }
580
modifiedBorrower(Tellico::Data::BorrowerPtr borrower_)581 void Controller::modifiedBorrower(Tellico::Data::BorrowerPtr borrower_) {
582 foreach(Observer* obs, m_observers) {
583 if(borrower_->isEmpty()) {
584 obs->removeBorrower(borrower_);
585 } else {
586 obs->modifyBorrower(borrower_);
587 }
588 }
589 hideTabs();
590 }
591
addedFilter(Tellico::FilterPtr filter_)592 void Controller::addedFilter(Tellico::FilterPtr filter_) {
593 m_mainWindow->addFilterView(); // just in case
594 foreach(Observer* obs, m_observers) {
595 obs->addFilter(filter_);
596 }
597 m_mainWindow->m_viewTabs->setTabBarHidden(false);
598 }
599
removedFilter(Tellico::FilterPtr filter_)600 void Controller::removedFilter(Tellico::FilterPtr filter_) {
601 foreach(Observer* obs, m_observers) {
602 obs->removeFilter(filter_);
603 }
604 hideTabs();
605 }
606
slotCheckOut()607 void Controller::slotCheckOut() {
608 if(m_selectedEntries.isEmpty()) {
609 return;
610 }
611
612 Data::EntryList loanedEntries = m_selectedEntries;
613
614 // check to see if any of the entries are already on-loan, and warn user
615 QMap<QString, Data::EntryPtr> alreadyLoaned;
616 foreach(Data::BorrowerPtr borrower, Data::Document::self()->collection()->borrowers()) {
617 foreach(Data::LoanPtr loan, borrower->loans()) {
618 if(m_selectedEntries.contains(loan->entry())) {
619 alreadyLoaned.insert(loan->entry()->title(), loan->entry());
620 }
621 }
622 }
623 if(!alreadyLoaned.isEmpty()) {
624 KMessageBox::informationList(Kernel::self()->widget(),
625 i18n("The following items are already loaned, but Tellico "
626 "does not currently support lending an item multiple "
627 "times. They will be removed from the list of items "
628 "to lend."),
629 alreadyLoaned.keys());
630 QMap<QString, Data::EntryPtr>::const_iterator it = alreadyLoaned.constBegin();
631 QMap<QString, Data::EntryPtr>::const_iterator end = alreadyLoaned.constEnd();
632 for( ; it != end; ++it) {
633 loanedEntries.removeAll(it.value());
634 }
635 if(loanedEntries.isEmpty()) {
636 return;
637 }
638 }
639
640 if(Kernel::self()->addLoans(loanedEntries)) {
641 m_mainWindow->m_checkInEntry->setEnabled(true);
642 }
643 }
644
slotCheckIn()645 void Controller::slotCheckIn() {
646 slotCheckIn(m_selectedEntries);
647 }
648
slotCheckIn(const Tellico::Data::EntryList & entries_)649 void Controller::slotCheckIn(const Tellico::Data::EntryList& entries_) {
650 if(entries_.isEmpty()) {
651 return;
652 }
653
654 Data::LoanList loans;
655 foreach(Data::EntryPtr entry, entries_) {
656 // these have to be in the loop since if a borrower gets empty
657 // it will be deleted, so the vector could change, for every entry iterator
658 Data::BorrowerList vec = Data::Document::self()->collection()->borrowers();
659 foreach(Data::BorrowerPtr borrower, vec) {
660 Data::LoanPtr l = borrower->loan(entry);
661 if(l) {
662 loans.append(l);
663 // assume it's only loaned once
664 break;
665 }
666 }
667 }
668
669 if(Kernel::self()->removeLoans(loans)) {
670 m_mainWindow->m_checkInEntry->setEnabled(false);
671 }
672 hideTabs();
673 }
674
hideTabs() const675 void Controller::hideTabs() const {
676 if((!m_mainWindow->m_filterView || m_mainWindow->m_filterView->isEmpty()) &&
677 (!m_mainWindow->m_loanView || m_mainWindow->m_loanView->isEmpty())) {
678 int idx = m_mainWindow->m_viewTabs->indexOf(m_mainWindow->m_groupView);
679 m_mainWindow->m_viewTabs->setCurrentIndex(idx);
680 m_mainWindow->m_viewTabs->setTabBarHidden(true);
681 }
682 }
683
canCheckIn() const684 bool Controller::canCheckIn() const {
685 foreach(Data::EntryPtr entry, m_selectedEntries) {
686 if(entry->field(QStringLiteral("loaned")) == QLatin1String("true")) {
687 return true;
688 }
689 }
690 return false;
691 }
692
updatedFetchers()693 void Controller::updatedFetchers() {
694 m_mainWindow->updateEntrySources();
695 }
696