1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2007-03-05
7  * Description : digiKam light table GUI
8  *
9  * Copyright (C) 2007-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
10  *
11  * This program is free software; you can redistribute it
12  * and/or modify it under the terms of the GNU General
13  * Public License as published by the Free Software Foundation;
14  * either version 2, or (at your option)
15  * any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * ============================================================ */
23 
24 #include "lighttablewindow_p.h"
25 
26 namespace Digikam
27 {
28 
29 LightTableWindow* LightTableWindow::m_instance = nullptr;
30 
lightTableWindow()31 LightTableWindow* LightTableWindow::lightTableWindow()
32 {
33     if (!m_instance)
34     {
35         new LightTableWindow();
36     }
37 
38     return m_instance;
39 }
40 
lightTableWindowCreated()41 bool LightTableWindow::lightTableWindowCreated()
42 {
43     return m_instance;
44 }
45 
LightTableWindow()46 LightTableWindow::LightTableWindow()
47     : DXmlGuiWindow(nullptr),
48       d            (new Private)
49 {
50     setObjectName(QLatin1String("Light Table"));
51     setConfigGroupName(QLatin1String("LightTable Settings"));
52     setXMLFile(QLatin1String("lighttablewindowui5.rc"));
53 
54     m_instance = this;
55 
56     setWindowFlags(Qt::Window);
57     setCaption(i18n("Light Table"));
58 
59     // We don't want to be deleted on close
60 
61     setAttribute(Qt::WA_DeleteOnClose, false);
62     setFullScreenOptions(FS_LIGHTTABLE);
63 
64     // -- Build the GUI -------------------------------
65 
66     setupUserArea();
67     setupActions();
68     setupStatusBar();
69 
70     // ------------------------------------------------
71 
72     setupConnections();
73     slotColorManagementOptionsChanged();
74 
75     readSettings();
76 
77     d->leftSideBar->populateTags();
78     d->rightSideBar->populateTags();
79 
80     applySettings();
81     setAutoSaveSettings(configGroupName(), true);
82 }
83 
~LightTableWindow()84 LightTableWindow::~LightTableWindow()
85 {
86     m_instance = nullptr;
87 
88     delete d->thumbView;
89     delete d->rightSideBar;
90     delete d->leftSideBar;
91     delete d;
92 }
93 
refreshView()94 void LightTableWindow::refreshView()
95 {
96     d->leftSideBar->refreshTagsView();
97     d->rightSideBar->refreshTagsView();
98 }
99 
closeEvent(QCloseEvent * e)100 void LightTableWindow::closeEvent(QCloseEvent* e)
101 {
102     if (!e)
103     {
104         return;
105     }
106 
107     if (d->clearOnCloseAction->isChecked())
108     {
109         slotClearItemsList();
110     }
111 
112     // There is one nasty habit with the thumbnail bar if it is floating: it
113     // doesn't close when the parent window does, so it needs to be manually
114     // closed. If the light table is opened again, its original state needs to
115     // be restored.
116     // This only needs to be done when closing a visible window and not when
117     // destroying a closed window, since the latter case will always report that
118     // the thumbnail bar isn't visible.
119 
120     if (isVisible())
121     {
122         d->barViewDock->hide();
123     }
124 
125     writeSettings();
126 
127     DXmlGuiWindow::closeEvent(e);
128 }
129 
showEvent(QShowEvent *)130 void LightTableWindow::showEvent(QShowEvent*)
131 {
132     // Restore the visibility of the thumbbar and start autosaving again.
133 
134     d->barViewDock->restoreVisibility();
135 }
136 
137 /**
138  * Deal with items dropped onto the thumbbar (e.g. from the Album view)
139  */
slotThumbbarDroppedItems(const QList<ItemInfo> & list)140 void LightTableWindow::slotThumbbarDroppedItems(const QList<ItemInfo>& list)
141 {
142     // Setting the third parameter of loadItemInfos to true
143     // means that the images are added to the presently available images.
144 
145     loadItemInfos(ItemInfoList(list), ItemInfo(), true);
146 }
147 
148 /**
149  * We get here either
150  * - via CTRL+L (from the albumview)
151  *     a) digikamapp.cpp:  CTRL+key_L leads to slotImageLightTable())
152  *     b) digikamview.cpp: void ItemIconView::slotImageLightTable()
153  *          calls d->iconView->insertToLightTable(list, info);
154  *     c) albumiconview.cpp: AlbumIconView::insertToLightTable
155  *          calls ltview->loadItemInfos(list, current);
156  * - via drag&drop, i.e. calls issued by the ...Dropped... routines
157  */
loadItemInfos(const ItemInfoList & list,const ItemInfo & givenItemInfoCurrent,bool addTo)158 void LightTableWindow::loadItemInfos(const ItemInfoList& list,
159                                       const ItemInfo& givenItemInfoCurrent,
160                                       bool  addTo)
161 {
162     // Clear all items before adding new images to the light table.
163 
164     qCDebug(DIGIKAM_GENERAL_LOG) << "Clearing LT" << (!addTo);
165 
166     if (!addTo)
167     {
168         slotClearItemsList();
169     }
170 
171     ItemInfoList l            = list;
172     ItemInfo imageInfoCurrent = givenItemInfoCurrent;
173 
174     if (imageInfoCurrent.isNull() && !l.isEmpty())
175     {
176         imageInfoCurrent = l.first();
177     }
178 
179     d->thumbView->setItems(l);
180 
181     QModelIndex index = d->thumbView->findItemByInfo(imageInfoCurrent);
182 
183     if (index.isValid())
184     {
185         d->thumbView->setCurrentIndex(index);
186     }
187     else
188     {
189         d->thumbView->setCurrentWhenAvailable(imageInfoCurrent.id());
190     }
191 }
192 
isEmpty() const193 bool LightTableWindow::isEmpty() const
194 {
195     return (d->thumbView->countItems() == 0);
196 }
197 
slotRefreshStatusBar()198 void LightTableWindow::slotRefreshStatusBar()
199 {
200     d->statusProgressBar->setProgressBarMode(StatusProgressBar::TextMode,
201                                              i18np("%1 item on Light Table",
202                                                    "%1 items on Light Table",
203                                              d->thumbView->countItems()));
204 }
205 
slotFileChanged(const QString & path)206 void LightTableWindow::slotFileChanged(const QString& path)
207 {
208     QUrl url = QUrl::fromLocalFile(path);
209 
210     // NOTE: Thumbbar handle change through ItemCategorizedView
211 
212     if (!d->previewView->leftItemInfo().isNull())
213     {
214         if (d->previewView->leftItemInfo().fileUrl() == url)
215         {
216             d->previewView->leftReload();
217             d->leftSideBar->itemChanged(d->previewView->leftItemInfo());
218         }
219     }
220 
221     if (!d->previewView->rightItemInfo().isNull())
222     {
223         if (d->previewView->rightItemInfo().fileUrl() == url)
224         {
225             d->previewView->rightReload();
226             d->rightSideBar->itemChanged(d->previewView->rightItemInfo());
227         }
228     }
229 }
230 
slotLeftPanelLeftButtonClicked()231 void LightTableWindow::slotLeftPanelLeftButtonClicked()
232 {
233     if (d->navigateByPairAction->isChecked())
234     {
235         return;
236     }
237 
238     d->thumbView->setCurrentInfo(d->previewView->leftItemInfo());
239 }
240 
slotRightPanelLeftButtonClicked()241 void LightTableWindow::slotRightPanelLeftButtonClicked()
242 {
243     // With navigate by pair option, only the left panel can be selected.
244 
245     if (d->navigateByPairAction->isChecked())
246     {
247         return;
248     }
249 
250     d->thumbView->setCurrentInfo(d->previewView->rightItemInfo());
251 }
252 
slotLeftPreviewLoaded(bool b)253 void LightTableWindow::slotLeftPreviewLoaded(bool b)
254 {
255     d->leftZoomBar->setEnabled(b);
256     d->leftFileName->setAdjustedText();
257 
258     if (b)
259     {
260         d->leftFileName->setAdjustedText(d->previewView->leftItemInfo().name());
261         d->previewView->checkForSelection(d->thumbView->currentInfo());
262         d->thumbView->setOnLeftPanel(d->previewView->leftItemInfo());
263 
264         QModelIndex index = d->thumbView->findItemByInfo(d->previewView->leftItemInfo());
265 
266         if (d->navigateByPairAction->isChecked() && index.isValid())
267         {
268             QModelIndex next = d->thumbView->nextIndex(index);
269 
270             if (next.isValid())
271             {
272                 d->thumbView->setOnRightPanel(d->thumbView->findItemByIndex(next));
273                 slotSetItemOnRightPanel(d->thumbView->findItemByIndex(next));
274             }
275             else
276             {
277                 QModelIndex first = d->thumbView->firstIndex();
278                 slotSetItemOnRightPanel(first.isValid() ? d->thumbView->findItemByIndex(first) : ItemInfo());
279             }
280         }
281     }
282 }
283 
slotRightPreviewLoaded(bool b)284 void LightTableWindow::slotRightPreviewLoaded(bool b)
285 {
286     d->rightZoomBar->setEnabled(b);
287     d->rightFileName->setAdjustedText();
288 
289     if (b)
290     {
291         d->rightFileName->setAdjustedText(d->previewView->rightItemInfo().name());
292         d->previewView->checkForSelection(d->thumbView->currentInfo());
293         d->thumbView->setOnRightPanel(d->previewView->rightItemInfo());
294 
295         QModelIndex index = d->thumbView->findItemByInfo(d->previewView->rightItemInfo());
296 
297         if (index.isValid())
298         {
299             d->thumbView->setOnRightPanel(d->thumbView->findItemByIndex(index));
300         }
301     }
302 }
303 
slotItemSelected(const ItemInfo & info)304 void LightTableWindow::slotItemSelected(const ItemInfo& info)
305 {
306     bool hasInfo = !info.isNull();
307 
308     d->setItemLeftAction->setEnabled(hasInfo);
309     d->setItemRightAction->setEnabled(hasInfo);
310     d->editItemAction->setEnabled(hasInfo);
311     d->removeItemAction->setEnabled(hasInfo);
312     d->clearListAction->setEnabled(hasInfo);
313     d->fileDeleteAction->setEnabled(hasInfo);
314     d->fileDeleteFinalAction->setEnabled(hasInfo);
315     d->backwardAction->setEnabled(hasInfo);
316     d->forwardAction->setEnabled(hasInfo);
317     d->firstAction->setEnabled(hasInfo);
318     d->lastAction->setEnabled(hasInfo);
319     d->syncPreviewAction->setEnabled(hasInfo);
320     d->navigateByPairAction->setEnabled(hasInfo);
321 
322     if (hasInfo)
323     {
324         QModelIndex curr = d->thumbView->findItemByInfo(info);
325 
326         if (curr.isValid())
327         {
328             if (!d->thumbView->previousIndex(curr).isValid())
329             {
330                 d->firstAction->setEnabled(false);
331             }
332 
333             if (!d->thumbView->nextIndex(curr).isValid())
334             {
335                 d->lastAction->setEnabled(false);
336             }
337 
338             if      (d->navigateByPairAction->isChecked())
339             {
340                 d->setItemLeftAction->setEnabled(false);
341                 d->setItemRightAction->setEnabled(false);
342 
343                 d->thumbView->setOnLeftPanel(info);
344                 slotSetItemOnLeftPanel(info);
345             }
346             else if (d->autoLoadOnRightPanel && !d->thumbView->isOnLeftPanel(info))
347             {
348                 d->thumbView->setOnRightPanel(info);
349                 slotSetItemOnRightPanel(info);
350             }
351         }
352     }
353 
354     d->previewView->checkForSelection(info);
355 }
356 
357 /**
358  * Deal with one (or more) items dropped onto the left panel
359  */
slotLeftDroppedItems(const ItemInfoList & list)360 void LightTableWindow::slotLeftDroppedItems(const ItemInfoList& list)
361 {
362     ItemInfo info = list.first();
363 
364     // add the image to the existing images
365 
366     loadItemInfos(list, info, true);
367 
368     // We will check if first item from list is already stored in thumbbar
369     // Note that the thumbbar stores all ItemInfo reference
370     // in memory for preview object.
371 
372     QModelIndex index = d->thumbView->findItemByInfo(info);
373 
374     if (index.isValid())
375     {
376         slotSetItemOnLeftPanel(info);
377     }
378 }
379 
380 /**
381  * Deal with one (or more) items dropped onto the right panel
382  */
slotRightDroppedItems(const ItemInfoList & list)383 void LightTableWindow::slotRightDroppedItems(const ItemInfoList& list)
384 {
385     ItemInfo info = list.first();
386 
387     // add the image to the existing images
388 
389     loadItemInfos(list, info, true);
390 
391     // We will check if first item from list is already stored in thumbbar
392     // Note that the thumbbar stores all ItemInfo reference
393     // in memory for preview object.
394 
395     QModelIndex index = d->thumbView->findItemByInfo(info);
396 
397     if (index.isValid())
398     {
399         slotSetItemOnRightPanel(info);
400 
401         // Make this item the current one.
402 
403         d->thumbView->setCurrentInfo(info);
404     }
405 }
406 
407 /**
408  * Set the images for the left and right panel.
409  */
setLeftRightItems(const ItemInfoList & list,bool addTo)410 void LightTableWindow::setLeftRightItems(const ItemInfoList& list, bool addTo)
411 {
412     ItemInfoList l = list;
413 
414     if (l.count() == 0)
415     {
416         return;
417     }
418 
419     ItemInfo info     = l.first();
420     QModelIndex index = d->thumbView->findItemByInfo(info);
421 
422     if ((l.count() == 1) && !addTo)
423     {
424         // Just one item; this is used for the left panel.
425 
426         d->thumbView->setOnLeftPanel(info);
427         slotSetItemOnLeftPanel(info);
428         d->thumbView->setCurrentInfo(info);
429 
430         return;
431     }
432 
433     if (index.isValid())
434     {
435         // The first item is used for the left panel.
436 
437         if (!addTo)
438         {
439             d->thumbView->setOnLeftPanel(info);
440             slotSetItemOnLeftPanel(info);
441         }
442 
443         // The subsequent item is used for the right panel.
444 
445         QModelIndex next = d->thumbView->nextIndex(index);
446 
447         if (next.isValid() && !addTo)
448         {
449             ItemInfo nextInf = d->thumbView->findItemByIndex(next);
450             d->thumbView->setOnRightPanel(nextInf);
451             slotSetItemOnRightPanel(nextInf);
452 
453             if (!d->navigateByPairAction->isChecked())
454             {
455                 d->thumbView->setCurrentInfo(nextInf);
456             }
457         }
458 
459         // If navigate by pairs is active, the left panel item is selected.
460         // (Fixes parts of bug #150296)
461 
462         if (d->navigateByPairAction->isChecked())
463         {
464             d->thumbView->setCurrentInfo(info);
465         }
466     }
467 }
468 
slotSetItemLeft()469 void LightTableWindow::slotSetItemLeft()
470 {
471     if (!d->thumbView->currentInfo().isNull())
472     {
473         slotSetItemOnLeftPanel(d->thumbView->currentInfo());
474     }
475 }
476 
slotSetItemRight()477 void LightTableWindow::slotSetItemRight()
478 {
479     if (!d->thumbView->currentInfo().isNull())
480     {
481         slotSetItemOnRightPanel(d->thumbView->currentInfo());
482     }
483 }
484 
slotSetItemOnLeftPanel(const ItemInfo & info)485 void LightTableWindow::slotSetItemOnLeftPanel(const ItemInfo& info)
486 {
487     d->previewView->setLeftItemInfo(info);
488 
489     if (!info.isNull())
490     {
491         d->leftSideBar->itemChanged(info);
492     }
493     else
494     {
495         d->leftSideBar->slotNoCurrentItem();
496     }
497 }
498 
slotSetItemOnRightPanel(const ItemInfo & info)499 void LightTableWindow::slotSetItemOnRightPanel(const ItemInfo& info)
500 {
501     d->previewView->setRightItemInfo(info);
502 
503     if (!info.isNull())
504     {
505         d->rightSideBar->itemChanged(info);
506     }
507     else
508     {
509         d->rightSideBar->slotNoCurrentItem();
510     }
511 }
512 
slotClearItemsList()513 void LightTableWindow::slotClearItemsList()
514 {
515     if (!d->previewView->leftItemInfo().isNull())
516     {
517         d->previewView->setLeftItemInfo();
518         d->leftSideBar->slotNoCurrentItem();
519     }
520 
521     if (!d->previewView->rightItemInfo().isNull())
522     {
523         d->previewView->setRightItemInfo();
524         d->rightSideBar->slotNoCurrentItem();
525     }
526 
527     d->thumbView->clear();
528 }
529 
slotDeleteItem()530 void LightTableWindow::slotDeleteItem()
531 {
532     deleteItem(false);
533 }
534 
slotDeleteItem(const ItemInfo & info)535 void LightTableWindow::slotDeleteItem(const ItemInfo& info)
536 {
537     deleteItem(info, false);
538 }
539 
slotDeleteFinalItem()540 void LightTableWindow::slotDeleteFinalItem()
541 {
542     deleteItem(true);
543 }
544 
slotDeleteFinalItem(const ItemInfo & info)545 void LightTableWindow::slotDeleteFinalItem(const ItemInfo& info)
546 {
547     deleteItem(info, true);
548 }
549 
deleteItem(bool permanently)550 void LightTableWindow::deleteItem(bool permanently)
551 {
552     if (!d->thumbView->currentInfo().isNull())
553     {
554         deleteItem(d->thumbView->currentInfo(), permanently);
555     }
556 }
557 
deleteItem(const ItemInfo & info,bool permanently)558 void LightTableWindow::deleteItem(const ItemInfo& info, bool permanently)
559 {
560     QUrl u               = info.fileUrl();
561     PAlbum* const palbum = AlbumManager::instance()->findPAlbum(u.adjusted(QUrl::RemoveFilename));
562 
563     if (!palbum)
564     {
565         return;
566     }
567 
568     qCDebug(DIGIKAM_GENERAL_LOG) << "Item to delete: " << u;
569 
570     bool useTrash;
571     bool preselectDeletePermanently = permanently;
572 
573     DeleteDialog dialog(this);
574 
575     QList<QUrl> urlList;
576     urlList.append(u);
577 
578     if (!dialog.confirmDeleteList(urlList, DeleteDialogMode::Files, preselectDeletePermanently ?
579                                   DeleteDialogMode::NoChoiceDeletePermanently : DeleteDialogMode::NoChoiceTrash))
580     {
581         return;
582     }
583 
584     useTrash = !dialog.shouldDelete();
585 
586     DIO::del(info, useTrash);
587 }
588 
slotRemoveItem()589 void LightTableWindow::slotRemoveItem()
590 {
591     if (!d->thumbView->currentInfo().isNull())
592     {
593         slotRemoveItem(d->thumbView->currentInfo());
594     }
595 }
596 
slotRemoveItem(const ItemInfo & info)597 void LightTableWindow::slotRemoveItem(const ItemInfo& info)
598 {
599 /*
600     if (!d->previewView->leftItemInfo().isNull())
601     {
602         if (d->previewView->leftItemInfo() == info)
603         {
604             d->previewView->setLeftItemInfo();
605             d->leftSideBar->slotNoCurrentItem();
606         }
607     }
608 
609     if (!d->previewView->rightItemInfo().isNull())
610     {
611         if (d->previewView->rightItemInfo() == info)
612         {
613             d->previewView->setRightItemInfo();
614             d->rightSideBar->slotNoCurrentItem();
615         }
616     }
617 
618     d->thumbView->removeItemByInfo(info);
619     d->thumbView->setSelected(d->thumbView->currentItem());
620 */
621 
622     // When either the image from the left or right panel is removed,
623     // there are various situations to account for.
624     // To describe them, 4 images A B C D are used
625     // and the subscript _L and _ R  mark the currently
626     // active item on the left and right panel
627 
628     ItemInfo new_linfo;
629     ItemInfo new_rinfo;
630     bool leftPanelActive = false;
631     ItemInfo curr_linfo = d->previewView->leftItemInfo();
632     ItemInfo curr_rinfo = d->previewView->rightItemInfo();
633     qint64 infoId        = info.id();
634 
635     // First determine the next images to the current left and right image:
636 
637     ItemInfo next_linfo;
638     ItemInfo next_rinfo;
639 
640     if (!curr_linfo.isNull())
641     {
642         QModelIndex index = d->thumbView->findItemByInfo(curr_linfo);
643 
644         if (index.isValid())
645         {
646             QModelIndex next = d->thumbView->nextIndex(index);
647 
648             if (next.isValid())
649             {
650                 next_linfo = d->thumbView->findItemByIndex(next);
651             }
652         }
653     }
654 
655     if (!curr_rinfo.isNull())
656     {
657         QModelIndex index = d->thumbView->findItemByInfo(curr_rinfo);
658 
659         if (index.isValid())
660         {
661             QModelIndex next = d->thumbView->nextIndex(index);
662 
663             if (next.isValid())
664             {
665                 next_rinfo = d->thumbView->findItemByIndex(next);
666             }
667         }
668     }
669 
670     d->thumbView->removeItemByInfo(info);
671 
672     // Make sure that next_linfo and next_rinfo are still available:
673 
674     if (!d->thumbView->findItemByInfo(next_linfo).isValid())
675     {
676         next_linfo = ItemInfo();
677     }
678 
679     if (!d->thumbView->findItemByInfo(next_rinfo).isValid())
680     {
681         next_rinfo = ItemInfo();
682     }
683 
684     // removal of the left panel item?
685 
686     if (!curr_linfo.isNull())
687     {
688         if (curr_linfo.id() == infoId)
689         {
690             leftPanelActive = true;
691 
692             // Delete the item A_L of the left panel:
693             // 1)  A_L  B_R  C    D   ->   B_L  C_R  D
694             // 2)  A_L  B    C_R  D   ->   B    C_L  D_R
695             // 3)  A_L  B    C    D_R ->   B_R  C    D_L
696             // 4)  A_L  B_R           ->   A_L
697             // some more corner cases:
698             // 5)  A    B_L  C_R  D   ->   A    C_L  D_R
699             // 6)  A    B_L  C_R      ->   A_R  C_L
700             // 7)  A_LR B    C    D   ->   B_L    C_R  D  (does not yet work)
701             // I.e. in 3) we wrap around circularly.
702 
703             // When removing the left panel image,
704             // put the right panel image into the left panel.
705             // Check if this one is not the same (i.e. also removed).
706 
707             if (!curr_rinfo.isNull())
708             {
709                 if (curr_rinfo.id() != infoId)
710                 {
711                     new_linfo = curr_rinfo;
712 
713                     // Set the right panel to the next image:
714 
715                     new_rinfo = next_rinfo;
716 
717                     // set the right panel active, but not in pair mode
718 
719                     if (!d->navigateByPairAction->isChecked())
720                     {
721                         leftPanelActive = false;
722                     }
723                 }
724             }
725         }
726     }
727 
728     // removal of the right panel item?
729 
730     if (!curr_rinfo.isNull())
731     {
732         if (curr_rinfo.id() == infoId)
733         {
734             // Leave the left panel as the current one
735 
736             new_linfo = curr_linfo;
737 
738             // Set the right panel to the next image
739 
740             new_rinfo = next_rinfo;
741         }
742     }
743 
744     // Now we deal with the corner cases, where no left or right item exists.
745     // If the right panel would be set, but not the left-one, then swap
746 
747     if (new_linfo.isNull() && !new_rinfo.isNull())
748     {
749         new_linfo       = new_rinfo;
750         new_rinfo       = ItemInfo();
751         leftPanelActive = true;
752     }
753 
754     if (new_linfo.isNull())
755     {
756         if (d->thumbView->countItems() > 0)
757         {
758             QModelIndex first = d->thumbView->firstIndex();
759             new_linfo         = d->thumbView->findItemByIndex(first);
760         }
761     }
762 
763     // Make sure that new_linfo and new_rinfo exist.
764     // This addresses a crash occurring if the last image is removed
765     // in the navigate by pairs mode.
766 
767     if (!d->thumbView->findItemByInfo(new_linfo).isValid())
768     {
769         new_linfo = ItemInfo();
770     }
771 
772     if (!d->thumbView->findItemByInfo(new_rinfo).isValid())
773     {
774         new_rinfo = ItemInfo();
775     }
776 
777     // no right item defined?
778 
779     if (new_rinfo.isNull())
780     {
781         // If there are at least two items, we can find reasonable right image.
782 
783         if (d->thumbView->countItems() > 1)
784         {
785             // See if there is an item next to the left one:
786 
787             QModelIndex index = d->thumbView->findItemByInfo(new_linfo);
788             QModelIndex next;
789 
790             if (index.isValid())
791             {
792                 next = d->thumbView->nextIndex(index);
793             }
794 
795             if (next.isValid())
796             {
797                 new_rinfo = d->thumbView->findItemByIndex(next);
798             }
799             else
800             {
801                 // If there is no item to the right of new_linfo
802                 // then we can choose the first item for new_rinfo
803                 // (as we made sure that there are at least two items)
804 
805                 QModelIndex first = d->thumbView->firstIndex();
806                 new_rinfo         = d->thumbView->findItemByIndex(first);
807             }
808         }
809     }
810 
811     // Check if left and right are set to the same
812 
813     if (!new_linfo.isNull() && !new_rinfo.isNull())
814     {
815         if (new_linfo.id() == new_rinfo.id())
816         {
817             // Only keep the left one
818 
819             new_rinfo = ItemInfo();
820         }
821     }
822 
823     // If the right panel would be set, but not the left-one, then swap
824     // (note that this has to be done here again!)
825 
826     if (new_linfo.isNull() && !new_rinfo.isNull())
827     {
828         new_linfo       = new_rinfo;
829         new_rinfo       = ItemInfo();
830         leftPanelActive = true;
831     }
832 
833     // set the image for the left panel
834 
835     if (!new_linfo.isNull())
836     {
837         d->thumbView->setOnLeftPanel(new_linfo);
838         slotSetItemOnLeftPanel(new_linfo);
839 
840         //  make this the selected item if the left was active before
841 
842         if (leftPanelActive)
843         {
844             d->thumbView->setCurrentInfo(new_linfo);
845         }
846     }
847     else
848     {
849         d->previewView->setLeftItemInfo();
850         d->leftSideBar->slotNoCurrentItem();
851     }
852 
853     // set the image for the right panel
854 
855     if (!new_rinfo.isNull())
856     {
857         d->thumbView->setOnRightPanel(new_rinfo);
858         slotSetItemOnRightPanel(new_rinfo);
859 
860         //  make this the selected item if the left was active before
861 
862         if (!leftPanelActive)
863         {
864             d->thumbView->setCurrentInfo(new_rinfo);
865         }
866     }
867     else
868     {
869         d->previewView->setRightItemInfo();
870         d->rightSideBar->slotNoCurrentItem();
871     }
872 }
873 
slotLeftZoomFactorChanged(double zoom)874 void LightTableWindow::slotLeftZoomFactorChanged(double zoom)
875 {
876     double zmin = d->previewView->leftZoomMin();
877     double zmax = d->previewView->leftZoomMax();
878     d->leftZoomBar->setZoom(zoom, zmin, zmax);
879 
880     d->leftZoomPlusAction->setEnabled(!d->previewView->leftMaxZoom());
881     d->leftZoomMinusAction->setEnabled(!d->previewView->leftMinZoom());
882 }
883 
slotRightZoomFactorChanged(double zoom)884 void LightTableWindow::slotRightZoomFactorChanged(double zoom)
885 {
886     double zmin = d->previewView->rightZoomMin();
887     double zmax = d->previewView->rightZoomMax();
888     d->rightZoomBar->setZoom(zoom, zmin, zmax);
889 
890     d->rightZoomPlusAction->setEnabled(!d->previewView->rightMaxZoom());
891     d->rightZoomMinusAction->setEnabled(!d->previewView->rightMinZoom());
892 }
893 
slotToggleSyncPreview()894 void LightTableWindow::slotToggleSyncPreview()
895 {
896     d->previewView->setSyncPreview(d->syncPreviewAction->isChecked());
897 }
898 
slotToggleOnSyncPreview(bool t)899 void LightTableWindow::slotToggleOnSyncPreview(bool t)
900 {
901     d->syncPreviewAction->setEnabled(t);
902 
903     if (!t)
904     {
905         d->syncPreviewAction->setChecked(false);
906     }
907     else
908     {
909         if (d->autoSyncPreview)
910         {
911             d->syncPreviewAction->setChecked(true);
912         }
913     }
914 }
915 
slotBackward()916 void LightTableWindow::slotBackward()
917 {
918     d->thumbView->toPreviousIndex();
919 }
920 
slotForward()921 void LightTableWindow::slotForward()
922 {
923     d->thumbView->toNextIndex();
924 }
925 
slotFirst()926 void LightTableWindow::slotFirst()
927 {
928     d->thumbView->toFirstIndex();
929 }
930 
slotLast()931 void LightTableWindow::slotLast()
932 {
933     d->thumbView->toLastIndex();
934 }
935 
slotToggleNavigateByPair()936 void LightTableWindow::slotToggleNavigateByPair()
937 {
938     d->thumbView->setNavigateByPair(d->navigateByPairAction->isChecked());
939     d->previewView->setNavigateByPair(d->navigateByPairAction->isChecked());
940     slotItemSelected(d->thumbView->currentInfo());
941 }
942 
slotComponentsInfo()943 void LightTableWindow::slotComponentsInfo()
944 {
945     showDigikamComponentsInfo();
946 }
947 
slotDBStat()948 void LightTableWindow::slotDBStat()
949 {
950     showDigikamDatabaseStat();
951 }
952 
slotOnlineVersionCheck()953 void LightTableWindow::slotOnlineVersionCheck()
954 {
955     Setup::onlineVersionCheck();
956 }
957 
moveEvent(QMoveEvent * e)958 void LightTableWindow::moveEvent(QMoveEvent* e)
959 {
960     Q_UNUSED(e)
961     emit signalWindowHasMoved();
962 }
963 
toggleTag(int tagID)964 void LightTableWindow::toggleTag(int tagID)
965 {
966     d->thumbView->toggleTag(tagID);
967 }
968 
slotAssignPickLabel(int pickId)969 void LightTableWindow::slotAssignPickLabel(int pickId)
970 {
971     d->thumbView->slotAssignPickLabel(pickId);
972 }
973 
slotAssignColorLabel(int colorId)974 void LightTableWindow::slotAssignColorLabel(int colorId)
975 {
976     d->thumbView->slotAssignColorLabel(colorId);
977 }
978 
slotAssignRating(int rating)979 void LightTableWindow::slotAssignRating(int rating)
980 {
981     d->thumbView->slotAssignRating(rating);
982 }
983 
showSideBars(bool visible)984 void LightTableWindow::showSideBars(bool visible)
985 {
986     if (visible)
987     {
988         d->leftSideBar->restore();
989         d->rightSideBar->restore();
990     }
991     else
992     {
993         d->leftSideBar->backup();
994         d->rightSideBar->backup();
995     }
996 }
997 
slotToggleLeftSideBar()998 void LightTableWindow::slotToggleLeftSideBar()
999 {
1000     d->leftSideBar->isExpanded() ? d->leftSideBar->shrink()
1001                                  : d->leftSideBar->expand();
1002 }
1003 
slotToggleRightSideBar()1004 void LightTableWindow::slotToggleRightSideBar()
1005 {
1006     d->rightSideBar->isExpanded() ? d->rightSideBar->shrink()
1007                                   : d->rightSideBar->expand();
1008 }
1009 
slotPreviousLeftSideBarTab()1010 void LightTableWindow::slotPreviousLeftSideBarTab()
1011 {
1012     d->leftSideBar->activePreviousTab();
1013 }
1014 
slotNextLeftSideBarTab()1015 void LightTableWindow::slotNextLeftSideBarTab()
1016 {
1017     d->leftSideBar->activeNextTab();
1018 }
1019 
slotPreviousRightSideBarTab()1020 void LightTableWindow::slotPreviousRightSideBarTab()
1021 {
1022     d->rightSideBar->activePreviousTab();
1023 }
1024 
slotNextRightSideBarTab()1025 void LightTableWindow::slotNextRightSideBarTab()
1026 {
1027     d->rightSideBar->activeNextTab();
1028 }
1029 
customizedFullScreenMode(bool set)1030 void LightTableWindow::customizedFullScreenMode(bool set)
1031 {
1032     showStatusBarAction()->setEnabled(!set);
1033     toolBarMenuAction()->setEnabled(!set);
1034     showMenuBarAction()->setEnabled(!set);
1035     d->showBarAction->setEnabled(!set);
1036 
1037     d->previewView->toggleFullScreen(set);
1038 }
1039 
slotFileWithDefaultApplication()1040 void LightTableWindow::slotFileWithDefaultApplication()
1041 {
1042     if (!d->thumbView->currentInfo().isNull())
1043     {
1044         DFileOperations::openFilesWithDefaultApplication(QList<QUrl>() << d->thumbView->currentInfo().fileUrl());
1045     }
1046 }
1047 
slotRightSideBarActivateTitles()1048 void LightTableWindow::slotRightSideBarActivateTitles()
1049 {
1050     d->rightSideBar->setActiveTab(d->rightSideBar->imageDescEditTab());
1051     d->rightSideBar->imageDescEditTab()->setFocusToTitlesEdit();
1052 }
1053 
slotRightSideBarActivateComments()1054 void LightTableWindow::slotRightSideBarActivateComments()
1055 {
1056     d->rightSideBar->setActiveTab(d->rightSideBar->imageDescEditTab());
1057     d->rightSideBar->imageDescEditTab()->setFocusToCommentsEdit();
1058 }
1059 
slotRightSideBarActivateAssignedTags()1060 void LightTableWindow::slotRightSideBarActivateAssignedTags()
1061 {
1062     d->rightSideBar->setActiveTab(d->rightSideBar->imageDescEditTab());
1063     d->rightSideBar->imageDescEditTab()->activateAssignedTagsButton();
1064 }
1065 
slotLeftSideBarActivateTitles()1066 void LightTableWindow::slotLeftSideBarActivateTitles()
1067 {
1068     d->leftSideBar->setActiveTab(d->leftSideBar->imageDescEditTab());
1069     d->leftSideBar->imageDescEditTab()->setFocusToTitlesEdit();
1070 }
1071 
slotLeftSideBarActivateComments()1072 void LightTableWindow::slotLeftSideBarActivateComments()
1073 {
1074     d->leftSideBar->setActiveTab(d->leftSideBar->imageDescEditTab());
1075     d->leftSideBar->imageDescEditTab()->setFocusToCommentsEdit();
1076 }
1077 
slotLeftSideBarActivateAssignedTags()1078 void LightTableWindow::slotLeftSideBarActivateAssignedTags()
1079 {
1080     d->leftSideBar->setActiveTab(d->leftSideBar->imageDescEditTab());
1081     d->leftSideBar->imageDescEditTab()->activateAssignedTagsButton();
1082 }
1083 
slotToggleColorManagedView()1084 void LightTableWindow::slotToggleColorManagedView()
1085 {
1086     if (!IccSettings::instance()->isEnabled())
1087     {
1088         return;
1089     }
1090 
1091     bool cmv = !IccSettings::instance()->settings().useManagedPreviews;
1092     IccSettings::instance()->setUseManagedPreviews(cmv);
1093 }
1094 
infoIface(DPluginAction * const ac)1095 DInfoInterface* LightTableWindow::infoIface(DPluginAction* const ac)
1096 {
1097     ApplicationSettings::OperationType aset = ApplicationSettings::Unspecified;
1098 
1099     switch (ac->actionCategory())
1100     {
1101         case DPluginAction::GenericExport:
1102         case DPluginAction::GenericImport:
1103             aset = ApplicationSettings::ImportExport;
1104             break;
1105 
1106         case DPluginAction::GenericMetadata:
1107             aset = ApplicationSettings::Metadata;
1108             break;
1109 
1110         case DPluginAction::GenericTool:
1111             aset = ApplicationSettings::Tools;
1112             break;
1113 
1114         case DPluginAction::GenericView:
1115             aset = ApplicationSettings::Slideshow;
1116             break;
1117 
1118         default:
1119             break;
1120     }
1121 
1122     DBInfoIface* const iface = new DBInfoIface(this, d->thumbView->allUrls(), aset);
1123 
1124     connect(iface, SIGNAL(signalImportedImage(QUrl)),
1125             this, SLOT(slotImportedImagefromScanner(QUrl)));
1126 
1127     return iface;
1128 }
1129 
1130 } // namespace Digikam
1131