File indexing completed on 2024-04-14 15:50:54

0001 /**
0002  * SPDX-FileCopyrightText: (C) 2003 by Sébastien Laoût <slaout@linux62.org>
0003  * SPDX-License-Identifier: GPL-2.0-or-later
0004  */
0005 
0006 #include "bnpview.h"
0007 #include "common.h"
0008 
0009 #include <QAction>
0010 #include <QApplication>
0011 #include <QCommandLineParser>
0012 #include <QGraphicsView>
0013 #include <QMenu>
0014 #include <QProgressDialog>
0015 #include <QStackedWidget>
0016 #include <QUndoStack>
0017 #include <QtCore/QDir>
0018 #include <QtCore/QEvent>
0019 #include <QtCore/QList>
0020 #include <QtCore/QRegExp>
0021 #include <QtGui/QHideEvent>
0022 #include <QtGui/QImage>
0023 #include <QtGui/QKeyEvent>
0024 #include <QtGui/QPixmap>
0025 #include <QtGui/QResizeEvent>
0026 #include <QtGui/QShowEvent>
0027 #include <QtXml/QDomDocument>
0028 
0029 #include <KAboutData>
0030 #include <KActionCollection>
0031 #include <KActionMenu>
0032 #include <KConfigGroup>
0033 #include <KGlobalAccel>
0034 #include <KIconLoader>
0035 #include <KLocalizedString>
0036 #include <KMessageBox>
0037 #include <KPassivePopup>
0038 #include <KStandardShortcut>
0039 #include <KToggleAction>
0040 #include <KWindowSystem>
0041 #include <KXMLGUIFactory>
0042 #include <KMessageWidget>
0043 
0044 #ifndef BASKET_USE_DRKONQI
0045 #include <KCrash>
0046 #endif // BASKET_USE_DRKONQI
0047 
0048 #include <cstdlib>
0049 #include <unistd.h> // usleep
0050 
0051 #include "archive.h"
0052 #include "backgroundmanager.h"
0053 #include "backup.h"
0054 #include "basketfactory.h"
0055 #include "basketlistview.h"
0056 #include "basketproperties.h"
0057 #include "basketscene.h"
0058 #include "basketstatusbar.h"
0059 #include "colorpicker.h"
0060 #include "debugwindow.h"
0061 #include "decoratedbasket.h"
0062 #include "formatimporter.h"
0063 #include "gitwrapper.h"
0064 #include "history.h"
0065 #include "htmlexporter.h"
0066 #include "icon_names.h"
0067 #include "newbasketdialog.h"
0068 #include "notedrag.h"
0069 #include "noteedit.h" // To launch InlineEditors::initToolBars()
0070 #include "notefactory.h"
0071 #include "password.h"
0072 #include "regiongrabber.h"
0073 #include "settings.h"
0074 #include "softwareimporters.h"
0075 #include "tools.h"
0076 #include "xmlwork.h"
0077 
0078 #include <QFileDialog>
0079 #include <QResource>
0080 #include <QStandardPaths>
0081 #include <qdbusconnection.h>
0082 
0083 //#include "bnpviewadaptor.h"
0084 
0085 /** class BNPView: */
0086 
0087 const int BNPView::c_delayTooltipTime = 275;
0088 
0089 BNPView::BNPView(QWidget *parent, const char *name, KXMLGUIClient *aGUIClient, KActionCollection *actionCollection, BasketStatusBar *bar)
0090     : QSplitter(Qt::Horizontal, parent)
0091     , m_actLockBasket(nullptr)
0092     , m_actPassBasket(nullptr)
0093     , m_loading(true)
0094     , m_newBasketPopup(false)
0095     , m_firstShow(true)
0096 #ifndef _WIN32
0097     , m_colorPicker(new ColorPicker(this))
0098 #endif
0099     , m_regionGrabber(nullptr)
0100     , m_passiveDroppedSelection(nullptr)
0101     , m_actionCollection(actionCollection)
0102     , m_guiClient(aGUIClient)
0103     , m_statusbar(bar)
0104 {
0105     // new BNPViewAdaptor(this);
0106     QDBusConnection dbus = QDBusConnection::sessionBus();
0107     dbus.registerObject("/BNPView", this);
0108 
0109     setObjectName(name);
0110 
0111     /* Settings */
0112     Settings::loadConfig();
0113 
0114     Global::bnpView = this;
0115 
0116     // Needed when loading the baskets:
0117     Global::backgroundManager = new BackgroundManager();
0118 
0119     setupGlobalShortcuts();
0120     m_history = new QUndoStack(this);
0121     initialize();
0122     QTimer::singleShot(0, this, SLOT(lateInit()));
0123 }
0124 
0125 BNPView::~BNPView()
0126 {
0127     int treeWidth = Global::bnpView->sizes()[Settings::treeOnLeft() ? 0 : 1];
0128 
0129     Settings::setBasketTreeWidth(treeWidth);
0130 
0131     if (currentBasket() && currentBasket()->isDuringEdit())
0132         currentBasket()->closeEditor();
0133 
0134     Settings::saveConfig();
0135 
0136     Global::bnpView = nullptr;
0137 
0138     delete m_statusbar;
0139     delete m_history;
0140     m_history = nullptr;
0141 
0142     NoteDrag::createAndEmptyCuttingTmpFolder(); // Clean the temporary folder we used
0143 }
0144 
0145 void BNPView::lateInit()
0146 {
0147     // If the main window is hidden when session is saved, Container::queryClose()
0148     //  isn't called and the last value would be kept
0149     Settings::saveConfig();
0150 
0151     // Load baskets
0152     DEBUG_WIN << "Baskets are loaded from " + Global::basketsFolder();
0153 
0154     NoteDrag::createAndEmptyCuttingTmpFolder(); // If last exec hasn't done it: clean the temporary folder we will use
0155     Tag::loadTags();                            // Tags should be ready before loading baskets, but tags need the mainContainer to be ready to create KActions!
0156     load();
0157 
0158     // If no basket has been found, try to import from an older version,
0159     if (topLevelItemCount() <= 0) {
0160         QDir dir;
0161         dir.mkdir(Global::basketsFolder());
0162         if (FormatImporter::shouldImportBaskets()) {
0163             FormatImporter::importBaskets();
0164             load();
0165         }
0166         if (topLevelItemCount() <= 0) {
0167             // Create first basket:
0168             BasketFactory::newBasket(QString(), i18n("General"));
0169             GitWrapper::commitBasket(currentBasket());
0170             GitWrapper::commitTagsXml();
0171         }
0172     }
0173 
0174     // Load the Welcome Baskets if it is the First Time:
0175     if (!Settings::welcomeBasketsAdded()) {
0176         addWelcomeBaskets();
0177         Settings::setWelcomeBasketsAdded(true);
0178         Settings::saveConfig();
0179     }
0180 }
0181 
0182 void BNPView::addWelcomeBaskets()
0183 {
0184     // Possible paths where to find the welcome basket archive, trying the translated one, and falling back to the English one:
0185     QStringList possiblePaths;
0186     if (QString(Tools::systemCodeset()) == QString("UTF-8")) { // Welcome baskets are encoded in UTF-8. If the system is not, then use the English version:
0187         QString lang = QLocale().languageToString(QLocale().language());
0188         possiblePaths.append(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "basket/welcome/Welcome_" + lang + ".baskets"));
0189         possiblePaths.append(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "basket/welcome/Welcome_" + lang.split('_')[0] + ".baskets"));
0190     }
0191     possiblePaths.append(QStandardPaths::locate(QStandardPaths::GenericDataLocation, "basket/welcome/Welcome_en_US.baskets"));
0192 
0193     // Take the first EXISTING basket archive found:
0194     QDir dir;
0195     QString path;
0196     for (QStringList::Iterator it = possiblePaths.begin(); it != possiblePaths.end(); ++it) {
0197         if (dir.exists(*it)) {
0198             path = *it;
0199             break;
0200         }
0201     }
0202 
0203     // Extract:
0204     if (!path.isEmpty())
0205         Archive::open(path);
0206 }
0207 
0208 void BNPView::onFirstShow()
0209 {
0210     // In late init, because we need qApp->mainWidget() to be set!
0211     connectTagsMenu();
0212 
0213     m_statusbar->setupStatusBar();
0214 
0215     int treeWidth = Settings::basketTreeWidth();
0216     if (treeWidth < 0)
0217         treeWidth = m_tree->fontMetrics().maxWidth() * 11;
0218     QList<int> splitterSizes;
0219     splitterSizes.append(treeWidth);
0220     setSizes(splitterSizes);
0221 }
0222 
0223 void BNPView::setupGlobalShortcuts()
0224 {
0225     KActionCollection *ac = new KActionCollection(this);
0226     QAction *a = nullptr;
0227 
0228     // Ctrl+Shift+W only works when started standalone:
0229 
0230     int modifier = Qt::CTRL + Qt::ALT + Qt::SHIFT;
0231 
0232     a = ac->addAction("global_paste", Global::bnpView, SLOT(globalPasteInCurrentBasket()));
0233     a->setText(i18n("Paste clipboard contents in current basket"));
0234     a->setStatusTip(
0235         i18n("Allows you to paste clipboard contents in the current basket "
0236              "without having to open the main window."));
0237     KGlobalAccel::setGlobalShortcut(a, QKeySequence(modifier + Qt::Key_V));
0238 
0239     a = ac->addAction("global_paste_selection", Global::bnpView, SLOT(pasteSelInCurrentBasket()));
0240     a->setText(i18n("Paste selection in current basket"));
0241     a->setStatusTip(
0242         i18n("Allows you to paste clipboard selection in the current basket "
0243              "without having to open the main window."));
0244     KGlobalAccel::setGlobalShortcut(a, (QKeySequence(Qt::CTRL + Qt::ALT + Qt::SHIFT + Qt::Key_S)));
0245 
0246     a = ac->addAction("global_new_basket", Global::bnpView, SLOT(askNewBasket()));
0247     a->setText(i18n("Create a new basket"));
0248     a->setStatusTip(
0249         i18n("Allows you to create a new basket without having to open the "
0250              "main window (you then can use the other global shortcuts to add "
0251              "a note, paste clipboard or paste selection in this new basket)."));
0252 
0253     a = ac->addAction("global_previous_basket", Global::bnpView, SLOT(goToPreviousBasket()));
0254     a->setText(i18n("Go to previous basket"));
0255     a->setStatusTip(
0256         i18n("Allows you to change current basket to the previous one without "
0257              "having to open the main window."));
0258 
0259     a = ac->addAction("global_next_basket", Global::bnpView, SLOT(goToNextBasket()));
0260     a->setText(i18n("Go to next basket"));
0261     a->setStatusTip(
0262         i18n("Allows you to change current basket to the next one "
0263              "without having to open the main window."));
0264 
0265     a = ac->addAction("global_note_add_html", Global::bnpView, SLOT(addNoteHtml()));
0266     a->setText(i18n("Insert text note"));
0267     a->setStatusTip(
0268         i18n("Add a text note to the current basket without having to open "
0269              "the main window."));
0270     KGlobalAccel::setGlobalShortcut(a, (QKeySequence(modifier + Qt::Key_T)));
0271 
0272     a = ac->addAction("global_note_add_image", Global::bnpView, SLOT(addNoteImage()));
0273     a->setText(i18n("Insert image note"));
0274     a->setStatusTip(
0275         i18n("Add an image note to the current basket without having to open "
0276              "the main window."));
0277 
0278     a = ac->addAction("global_note_add_link", Global::bnpView, SLOT(addNoteLink()));
0279     a->setText(i18n("Insert link note"));
0280     a->setStatusTip(
0281         i18n("Add a link note to the current basket without having "
0282              "to open the main window."));
0283 
0284     a = ac->addAction("global_note_add_color", Global::bnpView, SLOT(addNoteColor()));
0285     a->setText(i18n("Insert color note"));
0286     a->setStatusTip(
0287         i18n("Add a color note to the current basket without having to open "
0288              "the main window."));
0289 
0290     a = ac->addAction("global_note_pick_color", Global::bnpView, SLOT(slotColorFromScreenGlobal()));
0291     a->setText(i18n("Pick color from screen"));
0292     a->setStatusTip(
0293         i18n("Add a color note picked from one pixel on screen to the current "
0294              "basket without "
0295              "having to open the main window."));
0296 
0297     a = ac->addAction("global_note_grab_screenshot", Global::bnpView, SLOT(grabScreenshotGlobal()));
0298     a->setText(i18n("Grab screen zone"));
0299     a->setStatusTip(
0300         i18n("Grab a screen zone as an image in the current basket without "
0301              "having to open the main window."));
0302 
0303 #if 0
0304     a = ac->addAction("global_note_add_text", Global::bnpView,
0305                       SLOT(addNoteText()));
0306     a->setText(i18n("Insert plain text note"));
0307     a->setStatusTip(
0308         i18n("Add a plain text note to the current basket without having to "
0309              "open the main window."));
0310 #endif
0311 }
0312 
0313 void BNPView::initialize()
0314 {
0315     /// Configure the List View Columns:
0316     m_tree = new BasketTreeListView(this);
0317     m_tree->setHeaderLabel(i18n("Baskets"));
0318     m_tree->setSortingEnabled(false /*Disabled*/);
0319     m_tree->setRootIsDecorated(true);
0320     m_tree->setLineWidth(1);
0321     m_tree->setMidLineWidth(0);
0322     m_tree->setFocusPolicy(Qt::NoFocus);
0323 
0324     /// Configure the List View Drag and Drop:
0325     m_tree->setDragEnabled(true);
0326     m_tree->setDragDropMode(QAbstractItemView::DragDrop);
0327     m_tree->setAcceptDrops(true);
0328     m_tree->viewport()->setAcceptDrops(true);
0329 
0330     /// Configure the Splitter:
0331     m_stack = new QStackedWidget(this);
0332 
0333     setOpaqueResize(true);
0334 
0335     setCollapsible(indexOf(m_tree), true);
0336     setCollapsible(indexOf(m_stack), false);
0337     setStretchFactor(indexOf(m_tree), 0);
0338     setStretchFactor(indexOf(m_stack), 1);
0339 
0340     /// Configure the List View Signals:
0341     connect(m_tree, &BasketTreeListView::itemActivated, this, &BNPView::slotPressed);
0342     connect(m_tree, &BasketTreeListView::itemPressed, this, &BNPView::slotPressed);
0343     connect(m_tree, &BasketTreeListView::itemClicked, this, &BNPView::slotPressed);
0344 
0345     connect(m_tree, &BasketTreeListView::itemExpanded, this, &BNPView::needSave);
0346     connect(m_tree, &BasketTreeListView::itemCollapsed, this, &BNPView::needSave);
0347     connect(m_tree, &BasketTreeListView::contextMenuRequested, this, &BNPView::slotContextMenu);
0348     connect(m_tree, &BasketTreeListView::itemDoubleClicked, this, &BNPView::slotShowProperties);
0349 
0350     connect(m_tree, &BasketTreeListView::itemExpanded, this, &BNPView::basketChanged);
0351     connect(m_tree, &BasketTreeListView::itemCollapsed, this, &BNPView::basketChanged);
0352 
0353     connect(this, &BNPView::basketChanged, this, &BNPView::slotBasketChanged);
0354 
0355     connect(m_history, &QUndoStack::canRedoChanged, this, &BNPView::canUndoRedoChanged);
0356     connect(m_history, &QUndoStack::canUndoChanged, this, &BNPView::canUndoRedoChanged);
0357 
0358     setupActions();
0359 
0360     /// What's This Help for the tree:
0361     m_tree->setWhatsThis(
0362         i18n("<h2>Basket Tree</h2>"
0363              "Here is the list of your baskets. "
0364              "You can organize your data by putting them in different baskets. "
0365              "You can group baskets by subject by creating new baskets inside others. "
0366              "You can browse between them by clicking a basket to open it, or reorganize them using drag and drop."));
0367 
0368     setTreePlacement(Settings::treeOnLeft());
0369 }
0370 
0371 void BNPView::setupActions()
0372 {
0373     QAction *a = nullptr;
0374     KActionCollection *ac = actionCollection();
0375 
0376     a = ac->addAction("basket_export_basket_archive", this, SLOT(saveAsArchive()));
0377     a->setText(i18n("&Basket Archive..."));
0378     a->setIcon(QIcon::fromTheme("baskets"));
0379     a->setShortcut(0);
0380     m_actSaveAsArchive = a;
0381 
0382     a = ac->addAction("basket_import_basket_archive", this, SLOT(openArchive()));
0383     a->setText(i18n("&Basket Archive..."));
0384     a->setIcon(QIcon::fromTheme("baskets"));
0385     a->setShortcut(0);
0386     m_actOpenArchive = a;
0387 
0388     a = ac->addAction("basket_export_html", this, SLOT(exportToHTML()));
0389     a->setText(i18n("&HTML Web Page..."));
0390     a->setIcon(QIcon::fromTheme("text-html"));
0391     a->setShortcut(0);
0392     m_actExportToHtml = a;
0393 
0394     a = ac->addAction("basket_import_text_file", this, &BNPView::importTextFile);
0395     a->setText(i18n("Text &File..."));
0396     a->setIcon(QIcon::fromTheme("text-plain"));
0397     a->setShortcut(0);
0398 
0399     a = ac->addAction("basket_backup_restore", this, SLOT(backupRestore()));
0400     a->setText(i18n("&Backup && Restore..."));
0401     a->setShortcut(0);
0402 
0403     a = ac->addAction("check_cleanup", this, SLOT(checkCleanup()));
0404     a->setText(i18n("&Check && Cleanup..."));
0405     a->setShortcut(0);
0406     if (Global::commandLineOpts->isSet("debug")) {
0407         a->setEnabled(true);
0408     } else {
0409         a->setEnabled(false);
0410     }
0411 
0412     /** Note : ****************************************************************/
0413 
0414     a = ac->addAction("edit_delete", this, SLOT(delNote()));
0415     a->setText(i18n("D&elete"));
0416     a->setIcon(QIcon::fromTheme("edit-delete"));
0417     m_actionCollection->setDefaultShortcut(a, QKeySequence("Delete"));
0418     m_actDelNote = a;
0419 
0420     m_actCutNote = ac->addAction(KStandardAction::Cut, this, SLOT(cutNote()));
0421     m_actCopyNote = ac->addAction(KStandardAction::Copy, this, SLOT(copyNote()));
0422 
0423     m_actSelectAll = ac->addAction(KStandardAction::SelectAll, this, SLOT(slotSelectAll()));
0424     m_actSelectAll->setStatusTip(i18n("Selects all notes"));
0425 
0426     a = ac->addAction("edit_unselect_all", this, SLOT(slotUnselectAll()));
0427     a->setText(i18n("U&nselect All"));
0428     m_actUnselectAll = a;
0429     m_actUnselectAll->setStatusTip(i18n("Unselects all selected notes"));
0430 
0431     a = ac->addAction("edit_invert_selection", this, SLOT(slotInvertSelection()));
0432     a->setText(i18n("&Invert Selection"));
0433     m_actionCollection->setDefaultShortcut(a, Qt::CTRL + Qt::Key_Asterisk);
0434     m_actInvertSelection = a;
0435 
0436     m_actInvertSelection->setStatusTip(i18n("Inverts the current selection of notes"));
0437 
0438     m_actClearFormatting = ac->addAction("note_clear", this, SLOT(clearFormattingNote()));
0439     m_actClearFormatting->setText(i18n("&Clear Formatting"));
0440     m_actClearFormatting->setIcon(QIcon::fromTheme("text-plain"));
0441     
0442     a = ac->addAction("note_edit", this, SLOT(editNote()));
0443     a->setText(i18nc("Verb; not Menu", "&Edit..."));
0444     // a->setIcon(QIcon::fromTheme("edit"));
0445     m_actionCollection->setDefaultShortcut(a, QKeySequence("Return"));
0446     m_actEditNote = a;
0447 
0448     m_actOpenNote = ac->addAction(KStandardAction::Open, "note_open", this, SLOT(openNote()));
0449     m_actOpenNote->setIcon(QIcon::fromTheme("window-new"));
0450     m_actOpenNote->setText(i18n("&Open"));
0451     m_actionCollection->setDefaultShortcut(m_actOpenNote, QKeySequence("F9"));
0452 
0453     a = ac->addAction("note_open_with", this, SLOT(openNoteWith()));
0454     a->setText(i18n("Open &With..."));
0455     m_actionCollection->setDefaultShortcut(a, QKeySequence("Shift+F9"));
0456     m_actOpenNoteWith = a;
0457 
0458     m_actSaveNoteAs = ac->addAction(KStandardAction::SaveAs, "note_save_to_file", this, SLOT(saveNoteAs()));
0459     m_actSaveNoteAs->setText(i18n("&Save to File..."));
0460     m_actionCollection->setDefaultShortcut(m_actSaveNoteAs, QKeySequence("F10"));
0461 
0462     a = ac->addAction("note_group", this, SLOT(noteGroup()));
0463     a->setText(i18n("&Group"));
0464     a->setIcon(QIcon::fromTheme("mail-attachment"));
0465     m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+G"));
0466     m_actGroup = a;
0467 
0468     a = ac->addAction("note_ungroup", this, SLOT(noteUngroup()));
0469     a->setText(i18n("U&ngroup"));
0470     m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+Shift+G"));
0471     m_actUngroup = a;
0472 
0473     a = ac->addAction("note_move_top", this, SLOT(moveOnTop()));
0474     a->setText(i18n("Move on &Top"));
0475     a->setIcon(QIcon::fromTheme("arrow-up-double"));
0476     m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+Shift+Home"));
0477     m_actMoveOnTop = a;
0478 
0479     a = ac->addAction("note_move_up", this, SLOT(moveNoteUp()));
0480     a->setText(i18n("Move &Up"));
0481     a->setIcon(QIcon::fromTheme("arrow-up"));
0482     m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+Shift+Up"));
0483     m_actMoveNoteUp = a;
0484 
0485     a = ac->addAction("note_move_down", this, SLOT(moveNoteDown()));
0486     a->setText(i18n("Move &Down"));
0487     a->setIcon(QIcon::fromTheme("arrow-down"));
0488     m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+Shift+Down"));
0489     m_actMoveNoteDown = a;
0490 
0491     a = ac->addAction("note_move_bottom", this, SLOT(moveOnBottom()));
0492     a->setText(i18n("Move on &Bottom"));
0493     a->setIcon(QIcon::fromTheme("arrow-down-double"));
0494     m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+Shift+End"));
0495     m_actMoveOnBottom = a;
0496 
0497     m_actPaste = ac->addAction(KStandardAction::Paste, this, SLOT(pasteInCurrentBasket()));
0498 
0499     /** Insert : **************************************************************/
0500 
0501 #if 0
0502     a = ac->addAction("insert_text");
0503     a->setText(i18n("Plai&n Text"));
0504     a->setIcon(QIcon::fromTheme("text"));
0505     m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+T"));
0506     m_actInsertText = a;
0507 #endif
0508 
0509     a = ac->addAction("insert_html");
0510     a->setText(i18n("&Text"));
0511     a->setIcon(QIcon::fromTheme("text-html"));
0512     m_actionCollection->setDefaultShortcut(a, QKeySequence("Insert"));
0513     m_actInsertHtml = a;
0514 
0515     a = ac->addAction("insert_link");
0516     a->setText(i18n("&Link"));
0517     a->setIcon(QIcon::fromTheme(IconNames::LINK));
0518     m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+Y"));
0519     m_actInsertLink = a;
0520 
0521     a = ac->addAction("insert_cross_reference");
0522     a->setText(i18n("Cross &Reference"));
0523     a->setIcon(QIcon::fromTheme(IconNames::CROSS_REF));
0524     m_actInsertCrossReference = a;
0525 
0526     a = ac->addAction("insert_image");
0527     a->setText(i18n("&Image"));
0528     a->setIcon(QIcon::fromTheme(IconNames::IMAGE));
0529     m_actInsertImage = a;
0530 
0531     a = ac->addAction("insert_color");
0532     a->setText(i18n("&Color"));
0533     a->setIcon(QIcon::fromTheme(IconNames::COLOR));
0534     m_actInsertColor = a;
0535 
0536     a = ac->addAction("insert_launcher");
0537     a->setText(i18n("L&auncher"));
0538     a->setIcon(QIcon::fromTheme(IconNames::LAUNCH));
0539     m_actInsertLauncher = a;
0540 
0541     a = ac->addAction("insert_kmenu");
0542     a->setText(i18n("Import Launcher for &desktop application..."));
0543     a->setIcon(QIcon::fromTheme(IconNames::KMENU));
0544     m_actImportKMenu = a;
0545 
0546     a = ac->addAction("insert_icon");
0547     a->setText(i18n("Im&port Icon..."));
0548     a->setIcon(QIcon::fromTheme(IconNames::ICONS));
0549     m_actImportIcon = a;
0550 
0551     a = ac->addAction("insert_from_file");
0552     a->setText(i18n("Load From &File..."));
0553     a->setIcon(QIcon::fromTheme(IconNames::DOCUMENT_IMPORT));
0554     m_actLoadFile = a;
0555 
0556     //  connect( m_actInsertText, QAction::triggered, this, [this] () { insertEmpty(NoteType::Text); });
0557     connect(m_actInsertHtml, &QAction::triggered, this, [this] () { insertEmpty(NoteType::Html); });
0558     connect(m_actInsertImage, &QAction::triggered, this, [this] () { insertEmpty(NoteType::Image); });
0559     connect(m_actInsertLink, &QAction::triggered, this, [this] () { insertEmpty(NoteType::Link); });
0560     connect(m_actInsertCrossReference, &QAction::triggered, this, [this] () { insertEmpty(NoteType::CrossReference); });
0561     connect(m_actInsertColor, &QAction::triggered, this, [this] () { insertEmpty(NoteType::Color); });
0562     connect(m_actInsertLauncher, &QAction::triggered, this, [this] () { insertEmpty(NoteType::Launcher); });
0563 
0564     connect(m_actImportKMenu, &QAction::triggered, this, [this] () { insertWizard(1); });
0565     connect(m_actImportIcon, &QAction::triggered, this, [this] () { insertWizard(2); });
0566     connect(m_actLoadFile, &QAction::triggered, this, [this] () { insertWizard(3); });
0567 
0568 #ifndef _WIN32
0569 a = ac->addAction("insert_screen_color", this, &BNPView::slotColorFromScreen);
0570     a->setText(i18n("C&olor from Screen"));
0571     a->setIcon(QIcon::fromTheme("kcolorchooser"));
0572     m_actColorPicker = a;
0573 
0574     connect(m_colorPicker, &ColorPicker::colorGrabbed, this, &BNPView::colorPicked);
0575 #endif
0576 
0577     a = ac->addAction("insert_screen_capture", this, SLOT(grabScreenshot()));
0578     a->setText(i18n("Grab Screen &Zone"));
0579     a->setIcon(QIcon::fromTheme("ksnapshot"));
0580     m_actGrabScreenshot = a;
0581 
0582     //connect(m_actGrabScreenshot, SIGNAL(regionGrabbed(const QPixmap&)), this, SLOT(screenshotGrabbed(const QPixmap&)));
0583     //connect(m_colorPicker, SIGNAL(canceledPick()), this, SLOT(colorPickingCanceled()));
0584 
0585     //  m_insertActions.append( m_actInsertText     );
0586     m_insertActions.append(m_actInsertHtml);
0587     m_insertActions.append(m_actInsertLink);
0588     m_insertActions.append(m_actInsertCrossReference);
0589     m_insertActions.append(m_actInsertImage);
0590     m_insertActions.append(m_actInsertColor);
0591     m_insertActions.append(m_actImportKMenu);
0592     m_insertActions.append(m_actInsertLauncher);
0593     m_insertActions.append(m_actImportIcon);
0594     m_insertActions.append(m_actLoadFile);
0595     m_insertActions.append(m_actColorPicker);
0596     m_insertActions.append(m_actGrabScreenshot);
0597 
0598     /** Basket : **************************************************************/
0599 
0600     // At this stage, main.cpp has not set qApp->mainWidget(), so Global::runInsideKontact()
0601     // returns true. We do it ourself:
0602     bool runInsideKontact = true;
0603     QWidget *parentWidget = (QWidget *)parent();
0604     while (parentWidget) {
0605         if (parentWidget->inherits("MainWindow"))
0606             runInsideKontact = false;
0607         parentWidget = (QWidget *)parentWidget->parent();
0608     }
0609 
0610     // Use the "basket" icon in Kontact so it is consistent with the Kontact "New..." icon
0611 
0612     a = ac->addAction("basket_new", this, SLOT(askNewBasket()));
0613     a->setText(i18n("&New Basket..."));
0614     a->setIcon(QIcon::fromTheme((runInsideKontact ? "basket" : "document-new")));
0615     m_actionCollection->setDefaultShortcuts(a, KStandardShortcut::shortcut(KStandardShortcut::New));
0616     actNewBasket = a;
0617 
0618     a = ac->addAction("basket_new_sub", this, SLOT(askNewSubBasket()));
0619     a->setText(i18n("New &Sub-Basket..."));
0620     m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+Shift+N"));
0621     actNewSubBasket = a;
0622 
0623     a = ac->addAction("basket_new_sibling", this, SLOT(askNewSiblingBasket()));
0624     a->setText(i18n("New Si&bling Basket..."));
0625     actNewSiblingBasket = a;
0626 
0627     KActionMenu *newBasketMenu = new KActionMenu(i18n("&New"), ac);
0628     newBasketMenu->setIcon(QIcon::fromTheme("document-new"));
0629     ac->addAction("basket_new_menu", newBasketMenu);
0630 
0631     newBasketMenu->addAction(actNewBasket);
0632     newBasketMenu->addAction(actNewSubBasket);
0633     newBasketMenu->addAction(actNewSiblingBasket);
0634     connect(newBasketMenu, SIGNAL(triggered()), this, SLOT(askNewBasket()));
0635 
0636     a = ac->addAction("basket_properties", this, SLOT(propBasket()));
0637     a->setText(i18n("&Properties..."));
0638     a->setIcon(QIcon::fromTheme("document-properties"));
0639     m_actionCollection->setDefaultShortcut(a, QKeySequence("F2"));
0640     m_actPropBasket = a;
0641 
0642     a = ac->addAction("basket_sort_children_asc", this, SLOT(sortChildrenAsc()));
0643     a->setText(i18n("Sort Children Ascending"));
0644     a->setIcon(QIcon::fromTheme("view-sort-ascending"));
0645     m_actSortChildrenAsc = a;
0646 
0647     a = ac->addAction("basket_sort_children_desc", this, SLOT(sortChildrenDesc()));
0648     a->setText(i18n("Sort Children Descending"));
0649     a->setIcon(QIcon::fromTheme("view-sort-descending"));
0650     m_actSortChildrenDesc = a;
0651 
0652     a = ac->addAction("basket_sort_siblings_asc", this, SLOT(sortSiblingsAsc()));
0653     a->setText(i18n("Sort Siblings Ascending"));
0654     a->setIcon(QIcon::fromTheme("view-sort-ascending"));
0655     m_actSortSiblingsAsc = a;
0656 
0657     a = ac->addAction("basket_sort_siblings_desc", this, SLOT(sortSiblingsDesc()));
0658     a->setText(i18n("Sort Siblings Descending"));
0659     a->setIcon(QIcon::fromTheme("view-sort-descending"));
0660     m_actSortSiblingsDesc = a;
0661 
0662     a = ac->addAction("basket_remove", this, SLOT(delBasket()));
0663     a->setText(i18nc("Remove Basket", "&Remove"));
0664     a->setShortcut(0);
0665     m_actDelBasket = a;
0666 
0667 #ifdef HAVE_LIBGPGME
0668     a = ac->addAction("basket_password", this, SLOT(password()));
0669     a->setText(i18nc("Password protection", "Pass&word..."));
0670     a->setShortcut(0);
0671     m_actPassBasket = a;
0672 
0673     a = ac->addAction("basket_lock", this, SLOT(lockBasket()));
0674     a->setText(i18nc("Lock Basket", "&Lock"));
0675     m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+L"));
0676     m_actLockBasket = a;
0677 #endif
0678 
0679     /** Edit : ****************************************************************/
0680 
0681     // m_actUndo     = KStandardAction::undo(  this, SLOT(undo()),                 actionCollection() );
0682     // m_actUndo->setEnabled(false); // Not yet implemented !
0683     // m_actRedo     = KStandardAction::redo(  this, SLOT(redo()),                 actionCollection() );
0684     // m_actRedo->setEnabled(false); // Not yet implemented !
0685 
0686     KToggleAction *toggleAct = nullptr;
0687     toggleAct = new KToggleAction(i18n("&Filter"), ac);
0688     ac->addAction("edit_filter", toggleAct);
0689     toggleAct->setIcon(QIcon::fromTheme("view-filter"));
0690     m_actionCollection->setDefaultShortcuts(toggleAct, KStandardShortcut::shortcut(KStandardShortcut::Find));
0691     m_actShowFilter = toggleAct;
0692 
0693     connect(m_actShowFilter, SIGNAL(toggled(bool)), this, SLOT(showHideFilterBar(bool)));
0694 
0695     toggleAct = new KToggleAction(ac);
0696     ac->addAction("edit_filter_all_baskets", toggleAct);
0697     toggleAct->setText(i18n("&Search All"));
0698     toggleAct->setIcon(QIcon::fromTheme("edit-find"));
0699     m_actionCollection->setDefaultShortcut(toggleAct, QKeySequence("Ctrl+Shift+F"));
0700     m_actFilterAllBaskets = toggleAct;
0701 
0702     connect(m_actFilterAllBaskets, &KToggleAction::toggled, this, &BNPView::toggleFilterAllBaskets);
0703 
0704     a = ac->addAction("edit_filter_reset", this, SLOT(slotResetFilter()));
0705     a->setText(i18n("&Reset Filter"));
0706     a->setIcon(QIcon::fromTheme("edit-clear-locationbar-rtl"));
0707     m_actionCollection->setDefaultShortcut(a, QKeySequence("Ctrl+R"));
0708     m_actResetFilter = a;
0709 
0710     /** Go : ******************************************************************/
0711 
0712     a = ac->addAction("go_basket_previous", this, SLOT(goToPreviousBasket()));
0713     a->setText(i18n("&Previous Basket"));
0714     a->setIcon(QIcon::fromTheme("go-previous"));
0715     m_actionCollection->setDefaultShortcut(a, QKeySequence("Alt+Left"));
0716     m_actPreviousBasket = a;
0717 
0718     a = ac->addAction("go_basket_next", this, SLOT(goToNextBasket()));
0719     a->setText(i18n("&Next Basket"));
0720     a->setIcon(QIcon::fromTheme("go-next"));
0721     m_actionCollection->setDefaultShortcut(a, QKeySequence("Alt+Right"));
0722     m_actNextBasket = a;
0723 
0724     a = ac->addAction("go_basket_fold", this, SLOT(foldBasket()));
0725     a->setText(i18n("&Fold Basket"));
0726     a->setIcon(QIcon::fromTheme("go-up"));
0727     m_actionCollection->setDefaultShortcut(a, QKeySequence("Alt+Up"));
0728     m_actFoldBasket = a;
0729 
0730     a = ac->addAction("go_basket_expand", this, SLOT(expandBasket()));
0731     a->setText(i18n("&Expand Basket"));
0732     a->setIcon(QIcon::fromTheme("go-down"));
0733     m_actionCollection->setDefaultShortcut(a, QKeySequence("Alt+Down"));
0734     m_actExpandBasket = a;
0735 
0736 #if 0
0737     // FOR_BETA_PURPOSE:
0738     a = ac->addAction("beta_convert_texts", this, SLOT(convertTexts()));
0739     a->setText(i18n("Convert text notes to rich text notes"));
0740     a->setIcon(QIcon::fromTheme("run-build-file"));
0741     m_convertTexts = a;
0742 #endif
0743 
0744     InlineEditors::instance()->initToolBars(actionCollection());
0745     /** Help : ****************************************************************/
0746 
0747     a = ac->addAction("help_welcome_baskets", this, SLOT(addWelcomeBaskets()));
0748     a->setText(i18n("&Welcome Baskets"));
0749 }
0750 
0751 BasketListViewItem *BNPView::topLevelItem(int i)
0752 {
0753     return (BasketListViewItem *)m_tree->topLevelItem(i);
0754 }
0755 
0756 void BNPView::slotShowProperties(QTreeWidgetItem *item)
0757 {
0758     if (item)
0759         propBasket();
0760 }
0761 
0762 void BNPView::slotContextMenu(const QPoint &pos)
0763 {
0764     QTreeWidgetItem *item;
0765     item = m_tree->itemAt(pos);
0766     QString menuName;
0767     if (item) {
0768         BasketScene *basket = ((BasketListViewItem *)item)->basket();
0769 
0770         setCurrentBasket(basket);
0771         menuName = "basket_popup";
0772     } else {
0773         menuName = "tab_bar_popup";
0774         /*
0775          * "File -> New" create a new basket with the same parent basket as the current one.
0776          * But when invoked when right-clicking the empty area at the bottom of the basket tree,
0777          * it is obvious the user want to create a new basket at the bottom of the tree (with no parent).
0778          * So we set a temporary variable during the time the popup menu is shown,
0779          * so the slot askNewBasket() will do the right thing:
0780          */
0781         setNewBasketPopup();
0782     }
0783 
0784     QMenu *menu = popupMenu(menuName);
0785     connect(menu, &QMenu::aboutToHide, this, &BNPView::aboutToHideNewBasketPopup);
0786     menu->exec(m_tree->mapToGlobal(pos));
0787 }
0788 
0789 /* this happens every time we switch the basket (but not if we tell the user we save the stuff
0790  */
0791 void BNPView::save()
0792 {
0793     DEBUG_WIN << "Basket Tree: Saving...";
0794 
0795     QString data;
0796     QXmlStreamWriter stream(&data);
0797     XMLWork::setupXmlStream(stream, "basketTree");
0798 
0799     // Save Basket Tree:
0800     save(m_tree, nullptr, stream);
0801 
0802     stream.writeEndElement();
0803     stream.writeEndDocument();
0804 
0805     // Write to Disk:
0806     FileStorage::safelySaveToFile(Global::basketsFolder() + "baskets.xml", data);
0807 
0808     GitWrapper::commitBasketView();
0809 }
0810 
0811 void BNPView::save(QTreeWidget *listView, QTreeWidgetItem *item, QXmlStreamWriter &stream)
0812 {
0813     if (item == nullptr) {
0814         if (listView == nullptr) {
0815             // This should not happen: we call either save(listView, 0) or save(0, item)
0816             DEBUG_WIN << "BNPView::save error: listView=NULL and item=NULL";
0817             return;
0818         }
0819 
0820         // For each basket:
0821         for (int i = 0; i < listView->topLevelItemCount(); i++) {
0822             item = listView->topLevelItem(i);
0823             save(nullptr, item, stream);
0824         }
0825     } else {
0826         saveSubHierarchy(item, stream, true);
0827     }
0828 }
0829 
0830 void BNPView::writeBasketElement(QTreeWidgetItem *item, QXmlStreamWriter &stream)
0831 {
0832     BasketScene *basket = ((BasketListViewItem *)item)->basket();
0833 
0834     // Save Attributes:
0835     stream.writeAttribute("folderName", basket->folderName());
0836     if (item->childCount() >= 0) // If it can be expanded/folded:
0837         stream.writeAttribute("folded", XMLWork::trueOrFalse(!item->isExpanded()));
0838 
0839     if (((BasketListViewItem *)item)->isCurrentBasket())
0840         stream.writeAttribute("lastOpened", "true");
0841 
0842     basket->saveProperties(stream);
0843 }
0844 
0845 void BNPView::saveSubHierarchy(QTreeWidgetItem *item, QXmlStreamWriter &stream, bool recursive)
0846 {
0847     stream.writeStartElement("basket");
0848     writeBasketElement(item, stream); // create root <basket>
0849     if (recursive) {
0850         for (int i = 0; i < item->childCount(); i++) {
0851             saveSubHierarchy(item->child(i), stream, true);
0852         }
0853     }
0854     stream.writeEndElement();
0855 }
0856 
0857 void BNPView::load()
0858 {
0859     QScopedPointer<QDomDocument> doc(XMLWork::openFile("basketTree", Global::basketsFolder() + "baskets.xml"));
0860     // BEGIN Compatibility with 0.6.0 Pre-Alpha versions:
0861     if (!doc)
0862         doc.reset(XMLWork::openFile("basketsTree", Global::basketsFolder() + "baskets.xml"));
0863     // END
0864     if (doc != nullptr) {
0865         QDomElement docElem = doc->documentElement();
0866         load(nullptr, docElem);
0867     }
0868     m_loading = false;
0869 }
0870 
0871 void BNPView::load(QTreeWidgetItem *item, const QDomElement &baskets)
0872 {
0873     QDomNode n = baskets.firstChild();
0874     while (!n.isNull()) {
0875         QDomElement element = n.toElement();
0876         if ((!element.isNull()) && element.tagName() == "basket") {
0877             QString folderName = element.attribute("folderName");
0878             if (!folderName.isEmpty()) {
0879                 BasketScene *basket = loadBasket(folderName);
0880                 BasketListViewItem *basketItem = appendBasket(basket, item);
0881                 basketItem->setExpanded(!XMLWork::trueOrFalse(element.attribute("folded", "false"), false));
0882                 basket->loadProperties(XMLWork::getElement(element, "properties"));
0883                 if (XMLWork::trueOrFalse(element.attribute("lastOpened", element.attribute("lastOpened", "false")), false)) // Compat with 0.6.0-Alphas
0884                     setCurrentBasket(basket);
0885                 // Load Sub-baskets:
0886                 load(basketItem, element);
0887             }
0888         }
0889         n = n.nextSibling();
0890     }
0891 }
0892 
0893 BasketScene *BNPView::loadBasket(const QString &folderName)
0894 {
0895     if (folderName.isEmpty())
0896         return nullptr;
0897 
0898     DecoratedBasket *decoBasket = new DecoratedBasket(m_stack, folderName);
0899     BasketScene *basket = decoBasket->basket();
0900     m_stack->addWidget(decoBasket);
0901 
0902     connect(this, &BNPView::showErrorMessage, decoBasket, &DecoratedBasket::showErrorMessage);
0903     connect(basket, &BasketScene::countsChanged, this, &BNPView::countsChanged);
0904 
0905     // Important: Create listViewItem and connect signal BEFORE loadProperties(), so we get the listViewItem updated without extra work:
0906     connect(basket, &BasketScene::propertiesChanged, this, &BNPView::updateBasketListViewItem);
0907 
0908     connect(basket->decoration()->filterBar(), &FilterBar::newFilter, this, &BNPView::newFilterFromFilterBar);
0909     connect(basket, &BasketScene::crossReference, this, &BNPView::loadCrossReference);
0910 
0911     return basket;
0912 }
0913 
0914 int BNPView::basketCount(QTreeWidgetItem *parent)
0915 {
0916     int count = 1;
0917     if (parent == nullptr)
0918         return 0;
0919 
0920     for (int i = 0; i < parent->childCount(); i++) {
0921         count += basketCount(parent->child(i));
0922     }
0923 
0924     return count;
0925 }
0926 
0927 bool BNPView::canFold()
0928 {
0929     BasketListViewItem *item = listViewItemForBasket(currentBasket());
0930     if (!item)
0931         return false;
0932     return (item->childCount() > 0 && item->isExpanded());
0933 }
0934 
0935 bool BNPView::canExpand()
0936 {
0937     BasketListViewItem *item = listViewItemForBasket(currentBasket());
0938     if (!item)
0939         return false;
0940     return (item->childCount() > 0 && !item->isExpanded());
0941 }
0942 
0943 BasketListViewItem *BNPView::appendBasket(BasketScene *basket, QTreeWidgetItem *parentItem)
0944 {
0945     BasketListViewItem *newBasketItem;
0946     if (parentItem)
0947         newBasketItem = new BasketListViewItem(parentItem, parentItem->child(parentItem->childCount() - 1), basket);
0948     else {
0949         newBasketItem = new BasketListViewItem(m_tree, m_tree->topLevelItem(m_tree->topLevelItemCount() - 1), basket);
0950     }
0951 
0952     return newBasketItem;
0953 }
0954 
0955 void BNPView::loadNewBasket(const QString &folderName, const QDomElement &properties, BasketScene *parent)
0956 {
0957     BasketScene *basket = loadBasket(folderName);
0958     appendBasket(basket, (basket ? listViewItemForBasket(parent) : nullptr));
0959     basket->loadProperties(properties);
0960     setCurrentBasketInHistory(basket);
0961     //  save();
0962 }
0963 
0964 int BNPView::topLevelItemCount()
0965 {
0966     return m_tree->topLevelItemCount();
0967 }
0968 
0969 void BNPView::goToPreviousBasket()
0970 {
0971     if (m_history->canUndo())
0972         m_history->undo();
0973 }
0974 
0975 void BNPView::goToNextBasket()
0976 {
0977     if (m_history->canRedo())
0978         m_history->redo();
0979 }
0980 
0981 void BNPView::foldBasket()
0982 {
0983     BasketListViewItem *item = listViewItemForBasket(currentBasket());
0984     if (item && item->childCount() <= 0)
0985         item->setExpanded(false); // If Alt+Left is hit and there is nothing to close, make sure the focus will go to the parent basket
0986 
0987     QKeyEvent *keyEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Left, Qt::KeyboardModifiers(), nullptr);
0988     QApplication::postEvent(m_tree, keyEvent);
0989 }
0990 
0991 void BNPView::expandBasket()
0992 {
0993     QKeyEvent *keyEvent = new QKeyEvent(QEvent::KeyPress, Qt::Key_Right, Qt::KeyboardModifiers(), nullptr);
0994     QApplication::postEvent(m_tree, keyEvent);
0995 }
0996 
0997 void BNPView::closeAllEditors()
0998 {
0999     QTreeWidgetItemIterator it(m_tree);
1000     while (*it) {
1001         BasketListViewItem *item = (BasketListViewItem *)(*it);
1002         item->basket()->closeEditor();
1003         ++it;
1004     }
1005 }
1006 
1007 bool BNPView::convertTexts()
1008 {
1009     bool convertedNotes = false;
1010     QProgressDialog dialog;
1011     dialog.setWindowTitle(i18n("Plain Text Notes Conversion"));
1012     dialog.setLabelText(i18n("Converting plain text notes to rich text ones..."));
1013     dialog.setModal(true);
1014     dialog.setRange(0, basketCount());
1015     dialog.show(); // setMinimumDuration(50/*ms*/);
1016 
1017     QTreeWidgetItemIterator it(m_tree);
1018     while (*it) {
1019         BasketListViewItem *item = (BasketListViewItem *)(*it);
1020         if (item->basket()->convertTexts())
1021             convertedNotes = true;
1022 
1023         dialog.setValue(dialog.value() + 1);
1024 
1025         if (dialog.wasCanceled())
1026             break;
1027         ++it;
1028     }
1029 
1030     return convertedNotes;
1031 }
1032 
1033 void BNPView::toggleFilterAllBaskets(bool doFilter)
1034 {
1035     // If the filter isn't already showing, we make sure it does.
1036     if (doFilter)
1037         m_actShowFilter->setChecked(true);
1038 
1039     // currentBasket()->decoration()->filterBar()->setFilterAll(doFilter);
1040 
1041     if (doFilter)
1042         currentBasket()->decoration()->filterBar()->setEditFocus();
1043 
1044     // Filter every baskets:
1045     newFilter();
1046 }
1047 
1048 /** This function can be called recursively because we call qApp->processEvents().
1049  * If this function is called whereas another "instance" is running,
1050  * this new "instance" leave and set up a flag that is read by the first "instance"
1051  * to know it should re-begin the work.
1052  * PS: Yes, that's a very lame pseudo-threading but that works, and it's programmer-efforts cheap :-)
1053  */
1054 void BNPView::newFilter()
1055 {
1056     static bool alreadyEntered = false;
1057     static bool shouldRestart = false;
1058 
1059     if (alreadyEntered) {
1060         shouldRestart = true;
1061         return;
1062     }
1063     alreadyEntered = true;
1064     shouldRestart = false;
1065 
1066     BasketScene *current = currentBasket();
1067     const FilterData &filterData = current->decoration()->filterBar()->filterData();
1068 
1069     // Set the filter data for every other baskets, or reset the filter for every other baskets if we just disabled the filterInAllBaskets:
1070     QTreeWidgetItemIterator it(m_tree);
1071     while (*it) {
1072         BasketListViewItem *item = ((BasketListViewItem *)*it);
1073         if (item->basket() != current) {
1074             if (isFilteringAllBaskets())
1075                 item->basket()->decoration()->filterBar()->setFilterData(filterData); // Set the new FilterData for every other baskets
1076             else
1077                 item->basket()->decoration()->filterBar()->setFilterData(FilterData()); // We just disabled the global filtering: remove the FilterData
1078         }
1079         ++it;
1080     }
1081 
1082     // Show/hide the "little filter icons" (during basket load)
1083     // or the "little numbers" (to show number of found notes in the baskets) is the tree:
1084     qApp->processEvents();
1085 
1086     // Load every baskets for filtering, if they are not already loaded, and if necessary:
1087     if (filterData.isFiltering) {
1088         BasketScene *current = currentBasket();
1089         QTreeWidgetItemIterator it(m_tree);
1090         while (*it) {
1091             BasketListViewItem *item = ((BasketListViewItem *)*it);
1092             if (item->basket() != current) {
1093                 BasketScene *basket = item->basket();
1094                 if (!basket->loadingLaunched() && !basket->isLocked())
1095                     basket->load();
1096                 basket->filterAgain();
1097                 qApp->processEvents();
1098                 if (shouldRestart) {
1099                     alreadyEntered = false;
1100                     shouldRestart = false;
1101                     newFilter();
1102                     return;
1103                 }
1104             }
1105             ++it;
1106         }
1107     }
1108 
1109     //  qApp->processEvents();
1110     m_tree->viewport()->update(); // to see the "little numbers"
1111 
1112     alreadyEntered = false;
1113     shouldRestart = false;
1114 }
1115 
1116 void BNPView::newFilterFromFilterBar()
1117 {
1118     if (isFilteringAllBaskets())
1119         QTimer::singleShot(0, this, SLOT(newFilter())); // Keep time for the QLineEdit to display the filtered character and refresh correctly!
1120 }
1121 
1122 bool BNPView::isFilteringAllBaskets()
1123 {
1124     return m_actFilterAllBaskets->isChecked();
1125 }
1126 
1127 BasketListViewItem *BNPView::listViewItemForBasket(BasketScene *basket)
1128 {
1129     QTreeWidgetItemIterator it(m_tree);
1130     while (*it) {
1131         BasketListViewItem *item = ((BasketListViewItem *)*it);
1132         if (item->basket() == basket)
1133             return item;
1134         ++it;
1135     }
1136     return nullptr;
1137 }
1138 
1139 BasketScene *BNPView::currentBasket()
1140 {
1141     DecoratedBasket *decoBasket = (DecoratedBasket *)m_stack->currentWidget();
1142     if (decoBasket)
1143         return decoBasket->basket();
1144     else
1145         return nullptr;
1146 }
1147 
1148 BasketScene *BNPView::parentBasketOf(BasketScene *basket)
1149 {
1150     BasketListViewItem *item = (BasketListViewItem *)(listViewItemForBasket(basket)->parent());
1151     if (item)
1152         return item->basket();
1153     else
1154         return nullptr;
1155 }
1156 
1157 void BNPView::setCurrentBasketInHistory(BasketScene *basket)
1158 {
1159     if (!basket)
1160         return;
1161 
1162     if (currentBasket() == basket)
1163         return;
1164 
1165     m_history->push(new HistorySetBasket(basket));
1166 }
1167 
1168 void BNPView::setCurrentBasket(BasketScene *basket)
1169 {
1170     if (m_tree->currentItem() != nullptr && currentBasket() == basket)
1171         return;
1172 
1173     if (currentBasket())
1174         currentBasket()->closeBasket();
1175 
1176     if (basket)
1177         basket->aboutToBeActivated();
1178 
1179     BasketListViewItem *item = listViewItemForBasket(basket);
1180     if (item) {
1181         m_tree->setCurrentItem(item);
1182         item->ensureVisible();
1183         m_stack->setCurrentWidget(basket->decoration());
1184         // If the window has changed size, only the current basket receive the event,
1185         // the others will receive ony one just before they are shown.
1186         // But this triggers unwanted animations, so we eliminate it:
1187         basket->relayoutNotes();
1188         basket->openBasket();
1189         setWindowTitle(item->basket()->basketName());
1190         countsChanged(basket);
1191         updateStatusBarHint();
1192         m_tree->scrollToItem(m_tree->currentItem());
1193         item->basket()->setFocus();
1194     }
1195     m_tree->viewport()->update();
1196     Q_EMIT basketChanged();
1197 }
1198 
1199 void BNPView::removeBasket(BasketScene *basket)
1200 {
1201     if (basket->isDuringEdit())
1202         basket->closeEditor();
1203 
1204     // Find a new basket to switch to and select it.
1205     // Strategy: get the next sibling, or the previous one if not found.
1206     // If there is no such one, get the parent basket:
1207     BasketListViewItem *basketItem = listViewItemForBasket(basket);
1208     BasketListViewItem *nextBasketItem = (BasketListViewItem *)(m_tree->itemBelow(basketItem));
1209     if (!nextBasketItem)
1210         nextBasketItem = (BasketListViewItem *)m_tree->itemAbove(basketItem);
1211     if (!nextBasketItem)
1212         nextBasketItem = (BasketListViewItem *)(basketItem->parent());
1213 
1214     if (nextBasketItem)
1215         setCurrentBasketInHistory(nextBasketItem->basket());
1216 
1217     // Remove from the view:
1218     basket->unsubscribeBackgroundImages();
1219     m_stack->removeWidget(basket->decoration());
1220     //  delete basket->decoration();
1221     delete basketItem;
1222     //  delete basket;
1223 
1224     // If there is no basket anymore, add a new one:
1225     if (!nextBasketItem) {
1226         BasketFactory::newBasket(QString(), i18n("General"));
1227     } else {    // No need to save two times if we add a basket
1228         save();
1229     }
1230 }
1231 
1232 void BNPView::setTreePlacement(bool onLeft)
1233 {
1234     if (onLeft)
1235         insertWidget(0, m_tree);
1236     else
1237         addWidget(m_tree);
1238     // updateGeometry();
1239     qApp->postEvent(this, new QResizeEvent(size(), size()));
1240 }
1241 
1242 void BNPView::relayoutAllBaskets()
1243 {
1244     QTreeWidgetItemIterator it(m_tree);
1245     while (*it) {
1246         BasketListViewItem *item = ((BasketListViewItem *)*it);
1247         // item->basket()->unbufferizeAll();
1248         item->basket()->unsetNotesWidth();
1249         item->basket()->relayoutNotes();
1250         ++it;
1251     }
1252 }
1253 
1254 void BNPView::recomputeAllStyles()
1255 {
1256     QTreeWidgetItemIterator it(m_tree);
1257     while (*it) {
1258         BasketListViewItem *item = ((BasketListViewItem *)*it);
1259         item->basket()->recomputeAllStyles();
1260         item->basket()->unsetNotesWidth();
1261         item->basket()->relayoutNotes();
1262         ++it;
1263     }
1264 }
1265 
1266 void BNPView::removedStates(const QList<State *> &deletedStates)
1267 {
1268     QTreeWidgetItemIterator it(m_tree);
1269     while (*it) {
1270         BasketListViewItem *item = ((BasketListViewItem *)*it);
1271         item->basket()->removedStates(deletedStates);
1272         ++it;
1273     }
1274 }
1275 
1276 void BNPView::linkLookChanged()
1277 {
1278     QTreeWidgetItemIterator it(m_tree);
1279     while (*it) {
1280         BasketListViewItem *item = ((BasketListViewItem *)*it);
1281         item->basket()->linkLookChanged();
1282         ++it;
1283     }
1284 }
1285 
1286 void BNPView::filterPlacementChanged(bool onTop)
1287 {
1288     QTreeWidgetItemIterator it(m_tree);
1289     while (*it) {
1290         BasketListViewItem *item = static_cast<BasketListViewItem *>(*it);
1291         DecoratedBasket *decoration = static_cast<DecoratedBasket *>(item->basket()->parent());
1292         decoration->setFilterBarPosition(onTop);
1293         ++it;
1294     }
1295 }
1296 
1297 void BNPView::updateBasketListViewItem(BasketScene *basket)
1298 {
1299     if (basket == nullptr) {
1300         return;
1301     }
1302 
1303     BasketListViewItem *item = listViewItemForBasket(basket);
1304     if (item != nullptr) {
1305         item->setup();
1306 
1307         if (basket == currentBasket()) {
1308             setWindowTitle(basket->basketName());
1309         }
1310 
1311         if (basket->backgroundColor().isValid()) {
1312             item->setBackground(0, QBrush(basket->backgroundColor()));
1313         } else {
1314             item->setBackground(0, QBrush());
1315         }
1316 
1317     }
1318 
1319     // Don't save if we are loading!
1320     if (!m_loading) {
1321         save();
1322     }
1323 }
1324 
1325 void BNPView::needSave(QTreeWidgetItem *)
1326 {
1327     if (!m_loading)
1328         // A basket has been collapsed/expanded or a new one is select: this is not urgent:
1329         QTimer::singleShot(500 /*ms*/, this, SLOT(save()));
1330 }
1331 
1332 void BNPView::slotPressed(QTreeWidgetItem *item, int column)
1333 {
1334     Q_UNUSED(column);
1335     BasketScene *basket = currentBasket();
1336     if (basket == nullptr)
1337         return;
1338 
1339     // Impossible to Select no Basket:
1340     if (!item)
1341         m_tree->setCurrentItem(listViewItemForBasket(basket), true);
1342 
1343     else if (dynamic_cast<BasketListViewItem *>(item) != nullptr && currentBasket() != ((BasketListViewItem *)item)->basket()) {
1344         setCurrentBasketInHistory(((BasketListViewItem *)item)->basket());
1345         needSave(nullptr);
1346     }
1347     basket->graphicsView()->viewport()->setFocus();
1348 }
1349 
1350 DecoratedBasket *BNPView::currentDecoratedBasket()
1351 {
1352     if (currentBasket())
1353         return currentBasket()->decoration();
1354     else
1355         return nullptr;
1356 }
1357 
1358 // Redirected actions :
1359 
1360 void BNPView::exportToHTML()
1361 {
1362     HTMLExporter exporter(currentBasket());
1363 }
1364 void BNPView::editNote()
1365 {
1366     currentBasket()->noteEdit();
1367 }
1368 void BNPView::cutNote()
1369 {
1370     currentBasket()->noteCut();
1371 }
1372 void BNPView::copyNote()
1373 {
1374     currentBasket()->noteCopy();
1375 }
1376 void BNPView::delNote()
1377 {
1378     currentBasket()->noteDelete();
1379 }
1380 
1381 void BNPView::clearFormattingNote()
1382 {
1383     currentBasket()->clearFormattingNote();
1384 }
1385 
1386 void BNPView::openNote()
1387 {
1388     currentBasket()->noteOpen();
1389 }
1390 void BNPView::openNoteWith()
1391 {
1392     currentBasket()->noteOpenWith();
1393 }
1394 void BNPView::saveNoteAs()
1395 {
1396     currentBasket()->noteSaveAs();
1397 }
1398 void BNPView::noteGroup()
1399 {
1400     currentBasket()->noteGroup();
1401 }
1402 void BNPView::noteUngroup()
1403 {
1404     currentBasket()->noteUngroup();
1405 }
1406 void BNPView::moveOnTop()
1407 {
1408     currentBasket()->noteMoveOnTop();
1409 }
1410 void BNPView::moveOnBottom()
1411 {
1412     currentBasket()->noteMoveOnBottom();
1413 }
1414 void BNPView::moveNoteUp()
1415 {
1416     currentBasket()->noteMoveNoteUp();
1417 }
1418 void BNPView::moveNoteDown()
1419 {
1420     currentBasket()->noteMoveNoteDown();
1421 }
1422 void BNPView::slotSelectAll()
1423 {
1424     currentBasket()->selectAll();
1425 }
1426 void BNPView::slotUnselectAll()
1427 {
1428     currentBasket()->unselectAll();
1429 }
1430 void BNPView::slotInvertSelection()
1431 {
1432     currentBasket()->invertSelection();
1433 }
1434 void BNPView::slotResetFilter()
1435 {
1436     currentDecoratedBasket()->resetFilter();
1437 }
1438 
1439 void BNPView::importTextFile()
1440 {
1441     SoftwareImporters::importTextFile();
1442 }
1443 
1444 void BNPView::backupRestore()
1445 {
1446     BackupDialog dialog;
1447     dialog.exec();
1448 }
1449 
1450 void checkNote(Note *note, QList<QString> &fileList)
1451 {
1452     while (note) {
1453         note->finishLazyLoad();
1454         if (note->isGroup()) {
1455             checkNote(note->firstChild(), fileList);
1456         } else if (note->content()->useFile()) {
1457             QString noteFileName = note->basket()->folderName() + note->content()->fileName();
1458             int basketFileIndex = fileList.indexOf(noteFileName);
1459             if (basketFileIndex < 0) {
1460                 DEBUG_WIN << "<font color='red'>" + noteFileName + " NOT FOUND!</font>";
1461             } else {
1462                 fileList.removeAt(basketFileIndex);
1463             }
1464         }
1465         note = note->next();
1466     }
1467 }
1468 
1469 void checkBasket(BasketListViewItem *item, QList<QString> &dirList, QList<QString> &fileList)
1470 {
1471     BasketScene *basket = ((BasketListViewItem *)item)->basket();
1472     QString basketFolderName = basket->folderName();
1473     int basketFolderIndex = dirList.indexOf(basket->folderName());
1474     if (basketFolderIndex < 0) {
1475         DEBUG_WIN << "<font color='red'>" + basketFolderName + " NOT FOUND!</font>";
1476     } else {
1477         dirList.removeAt(basketFolderIndex);
1478     }
1479     int basketFileIndex = fileList.indexOf(basket->folderName() + ".basket");
1480     if (basketFileIndex < 0) {
1481         DEBUG_WIN << "<font color='red'>.basket file of " + basketFolderName + ".basket NOT FOUND!</font>";
1482     } else {
1483         fileList.removeAt(basketFileIndex);
1484     }
1485     if (!basket->loadingLaunched() && !basket->isLocked()) {
1486         basket->load();
1487     }
1488     DEBUG_WIN << "\t********************************************************************************";
1489     DEBUG_WIN << basket->basketName() << "(" << basketFolderName << ") loaded.";
1490     Note *note = basket->firstNote();
1491     if (!note) {
1492         DEBUG_WIN << "\tHas NO notes!";
1493     } else {
1494         checkNote(note, fileList);
1495     }
1496     basket->save();
1497     qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 100);
1498     for (int i = 0; i < item->childCount(); i++) {
1499         checkBasket((BasketListViewItem *)item->child(i), dirList, fileList);
1500     }
1501     if (basket != Global::bnpView->currentBasket()) {
1502         DEBUG_WIN << basket->basketName() << "(" << basketFolderName << ") unloading...";
1503         DEBUG_WIN << "\t********************************************************************************";
1504         basket->unbufferizeAll();
1505     } else {
1506         DEBUG_WIN << basket->basketName() << "(" << basketFolderName << ") is the current basket, not unloading.";
1507         DEBUG_WIN << "\t********************************************************************************";
1508     }
1509     qApp->processEvents(QEventLoop::ExcludeUserInputEvents, 100);
1510 }
1511 
1512 void BNPView::checkCleanup()
1513 {
1514     DEBUG_WIN << "Starting the check, cleanup and reindexing... (" + Global::basketsFolder() + ')';
1515     QList<QString> dirList;
1516     QList<QString> fileList;
1517     QString topDirEntry;
1518     QString subDirEntry;
1519     QFileInfo fileInfo;
1520     QDir topDir(Global::basketsFolder(), QString(), QDir::Name | QDir::IgnoreCase, QDir::TypeMask | QDir::Hidden);
1521     Q_FOREACH (topDirEntry, topDir.entryList()) {
1522         if (topDirEntry != QLatin1String(".") && topDirEntry != QLatin1String("..")) {
1523             fileInfo.setFile(Global::basketsFolder() + '/' + topDirEntry);
1524             if (fileInfo.isDir()) {
1525                 dirList << topDirEntry + '/';
1526                 QDir basketDir(Global::basketsFolder() + '/' + topDirEntry, QString(), QDir::Name | QDir::IgnoreCase, QDir::TypeMask | QDir::Hidden);
1527                 Q_FOREACH (subDirEntry, basketDir.entryList()) {
1528                     if (subDirEntry != "." && subDirEntry != "..") {
1529                         fileList << topDirEntry + '/' + subDirEntry;
1530                     }
1531                 }
1532             } else if (topDirEntry != "." && topDirEntry != ".." && topDirEntry != "baskets.xml") {
1533                 fileList << topDirEntry;
1534             }
1535         }
1536     }
1537     DEBUG_WIN << "Directories found: " + QString::number(dirList.count());
1538     DEBUG_WIN << "Files found: " + QString::number(fileList.count());
1539 
1540     DEBUG_WIN << "Checking Baskets:";
1541     for (int i = 0; i < topLevelItemCount(); i++) {
1542         checkBasket(topLevelItem(i), dirList, fileList);
1543     }
1544     DEBUG_WIN << "Baskets checked.";
1545     DEBUG_WIN << "Directories remaining (not in any basket): " + QString::number(dirList.count());
1546     DEBUG_WIN << "Files remaining (not in any basket): " + QString::number(fileList.count());
1547 
1548     Q_FOREACH (topDirEntry, dirList) {
1549         DEBUG_WIN << "<font color='red'>" + topDirEntry + " does not belong to any basket!</font>";
1550         // Tools::deleteRecursively(Global::basketsFolder() + '/' + topDirEntry);
1551         // DEBUG_WIN << "<font color='red'>\t" + topDirEntry + " removed!</font>";
1552         Tools::trashRecursively(Global::basketsFolder() + "/" + topDirEntry);
1553         DEBUG_WIN << "<font color='red'>\t" + topDirEntry + " trashed!</font>";
1554         Q_FOREACH (subDirEntry, fileList) {
1555             fileInfo.setFile(Global::basketsFolder() + '/' + subDirEntry);
1556             if (!fileInfo.isFile()) {
1557                 fileList.removeAll(subDirEntry);
1558                 DEBUG_WIN << "<font color='red'>\t\t" + subDirEntry + " already removed!</font>";
1559             }
1560         }
1561     }
1562     Q_FOREACH (subDirEntry, fileList) {
1563         DEBUG_WIN << "<font color='red'>" + subDirEntry + " does not belong to any note!</font>";
1564         // Tools::deleteRecursively(Global::basketsFolder() + '/' + subDirEntry);
1565         // DEBUG_WIN << "<font color='red'>\t" + subDirEntry + " removed!</font>";
1566         Tools::trashRecursively(Global::basketsFolder() + '/' + subDirEntry);
1567         DEBUG_WIN << "<font color='red'>\t" + subDirEntry + " trashed!</font>";
1568     }
1569     DEBUG_WIN << "Check, cleanup and reindexing completed";
1570 }
1571 
1572 void BNPView::countsChanged(BasketScene *basket)
1573 {
1574     if (basket == currentBasket())
1575         notesStateChanged();
1576 }
1577 
1578 void BNPView::notesStateChanged()
1579 {
1580     BasketScene *basket = currentBasket();
1581 
1582     // Update statusbar message :
1583     if (currentBasket()->isLocked())
1584         setSelectionStatus(i18n("Locked"));
1585     else if (!basket->isLoaded())
1586         setSelectionStatus(i18n("Loading..."));
1587     else if (basket->count() == 0)
1588         setSelectionStatus(i18n("No notes"));
1589     else {
1590         QString count = i18np("%1 note", "%1 notes", basket->count());
1591         QString selecteds = i18np("%1 selected", "%1 selected", basket->countSelecteds());
1592         QString showns = (currentDecoratedBasket()->filterData().isFiltering ? i18n("all matches") : i18n("no filter"));
1593         if (basket->countFounds() != basket->count())
1594             showns = i18np("%1 match", "%1 matches", basket->countFounds());
1595         setSelectionStatus(i18nc("e.g. '18 notes, 10 matches, 5 selected'", "%1, %2, %3", count, showns, selecteds));
1596     }
1597 
1598     if (currentBasket()->redirectEditActions()) {
1599         m_actSelectAll->setEnabled(!currentBasket()->selectedAllTextInEditor());
1600         m_actUnselectAll->setEnabled(currentBasket()->hasSelectedTextInEditor());
1601     } else {
1602         m_actSelectAll->setEnabled(basket->countSelecteds() < basket->countFounds());
1603         m_actUnselectAll->setEnabled(basket->countSelecteds() > 0);
1604     }
1605     m_actInvertSelection->setEnabled(basket->countFounds() > 0);
1606 
1607     updateNotesActions();
1608 }
1609 
1610 void BNPView::updateNotesActions()
1611 {
1612     bool isLocked = currentBasket()->isLocked();
1613     bool oneSelected = currentBasket()->countSelecteds() == 1;
1614     bool oneOrSeveralSelected = currentBasket()->countSelecteds() >= 1;
1615     bool severalSelected = currentBasket()->countSelecteds() >= 2;
1616 
1617     NoteType::Id selectedType = NoteType::Unknown;
1618     if (oneSelected && currentBasket()->theSelectedNote()){
1619         selectedType = currentBasket()->theSelectedNote()->content()->type();
1620     }
1621     // FIXME: m_actCheckNotes is also modified in void BNPView::areSelectedNotesCheckedChanged(bool checked)
1622     //        bool BasketScene::areSelectedNotesChecked() should return false if bool BasketScene::showCheckBoxes() is false
1623     //  m_actCheckNotes->setChecked( oneOrSeveralSelected &&
1624     //                               currentBasket()->areSelectedNotesChecked() &&
1625     //                               currentBasket()->showCheckBoxes()             );
1626 
1627     Note *selectedGroup = (severalSelected ? currentBasket()->selectedGroup() : nullptr);
1628 
1629     m_actEditNote->setEnabled(!isLocked && oneSelected && !currentBasket()->isDuringEdit());
1630     if (currentBasket()->redirectEditActions()) {
1631         m_actCutNote->setEnabled(currentBasket()->hasSelectedTextInEditor());
1632         m_actCopyNote->setEnabled(currentBasket()->hasSelectedTextInEditor());
1633         m_actPaste->setEnabled(true);
1634         m_actDelNote->setEnabled(currentBasket()->hasSelectedTextInEditor());
1635     } else {
1636         m_actCutNote->setEnabled(!isLocked && oneOrSeveralSelected);
1637         m_actCopyNote->setEnabled(oneOrSeveralSelected);
1638         m_actPaste->setEnabled(!isLocked);
1639         m_actDelNote->setEnabled(!isLocked && oneOrSeveralSelected);
1640     }
1641     m_actClearFormatting->setEnabled(!isLocked && !currentBasket()->isDuringEdit() && selectedType == NoteType::Html);
1642     m_actOpenNote->setEnabled(oneOrSeveralSelected);
1643     m_actOpenNoteWith->setEnabled(oneSelected); // TODO: oneOrSeveralSelected IF SAME TYPE
1644     m_actSaveNoteAs->setEnabled(oneSelected);   // IDEM?
1645     m_actGroup->setEnabled(!isLocked && severalSelected && (!selectedGroup || selectedGroup->isColumn()));
1646     m_actUngroup->setEnabled(!isLocked && selectedGroup && !selectedGroup->isColumn());
1647     m_actMoveOnTop->setEnabled(!isLocked && oneOrSeveralSelected && !currentBasket()->isFreeLayout());
1648     m_actMoveNoteUp->setEnabled(!isLocked && oneOrSeveralSelected); // TODO: Disable when unavailable!
1649     m_actMoveNoteDown->setEnabled(!isLocked && oneOrSeveralSelected);
1650     m_actMoveOnBottom->setEnabled(!isLocked && oneOrSeveralSelected && !currentBasket()->isFreeLayout());
1651 
1652     for (QList<QAction *>::const_iterator action = m_insertActions.constBegin(); action != m_insertActions.constEnd(); ++action)
1653         (*action)->setEnabled(!isLocked);
1654 
1655     // From the old Note::contextMenuEvent(...) :
1656     /*  if (useFile() || m_type == Link) {
1657         m_type == Link ? i18n("&Open target")         : i18n("&Open")
1658         m_type == Link ? i18n("Open target &with...") : i18n("Open &with...")
1659         m_type == Link ? i18n("&Save target as...")   : i18n("&Save a copy as...")
1660             // If useFile() there is always a file to open / open with / save, but :
1661         if (m_type == Link) {
1662                 if (url().toDisplayString().isEmpty() && runCommand().isEmpty())     // no URL nor runCommand :
1663         popupMenu->setItemEnabled(7, false);                       //  no possible Open !
1664                 if (url().toDisplayString().isEmpty())                               // no URL :
1665         popupMenu->setItemEnabled(8, false);                       //  no possible Open with !
1666                 if (url().toDisplayString().isEmpty() || url().path().endsWith("/")) // no URL or target a folder :
1667         popupMenu->setItemEnabled(9, false);                       //  not possible to save target file
1668     }
1669     } else if (m_type != Color) {
1670         popupMenu->insertSeparator();
1671         popupMenu->insertItem( QIcon::fromTheme("document-save-as"), i18n("&Save a copy as..."), this, SLOT(slotSaveAs()), 0, 10 );
1672     }*/
1673 }
1674 
1675 // BEGIN Color picker (code from KColorEdit):
1676 
1677 /* Activate the mode
1678  */
1679 void BNPView::slotColorFromScreen(bool global)
1680 {
1681 #ifndef _WIN32
1682     m_colorPickWasGlobal = global;
1683 
1684     currentBasket()->saveInsertionData();
1685     m_colorPicker->grabColor();
1686 #endif
1687 }
1688 
1689 void BNPView::slotColorFromScreenGlobal()
1690 {
1691     slotColorFromScreen(true);
1692 }
1693 
1694 void BNPView::colorPicked(const QColor &color)
1695 {
1696     if (!currentBasket()->isLoaded()) {
1697         currentBasket()->load();
1698     }
1699     currentBasket()->insertColor(color);
1700 }
1701 
1702 void BNPView::slotConvertTexts()
1703 {
1704     /*
1705         int result = KMessageBox::questionYesNoCancel(
1706             this,
1707             i18n(
1708                 "<p>This will convert every text notes into rich text notes.<br>"
1709                 "The content of the notes will not change and you will be able to apply formatting to those notes.</p>"
1710                 "<p>This process cannot be reverted back: you will not be able to convert the rich text notes to plain text ones later.</p>"
1711                 "<p>As a beta-tester, you are strongly encouraged to do the convert process because it is to test if plain text notes are still needed.<br>"
1712                 "If nobody complain about not having plain text notes anymore, then the final version is likely to not support plain text notes anymore.</p>"
1713                 "<p><b>Which basket notes do you want to convert?</b></p>"
1714             ),
1715             i18n("Convert Text Notes"),
1716             KGuiItem(i18n("Only in the Current Basket")),
1717             KGuiItem(i18n("In Every Baskets"))
1718         );
1719         if (result == KMessageBox::Cancel)
1720             return;
1721     */
1722 
1723     bool conversionsDone;
1724     //  if (result == KMessageBox::Yes)
1725     //      conversionsDone = currentBasket()->convertTexts();
1726     //  else
1727     conversionsDone = convertTexts();
1728 
1729     if (conversionsDone)
1730         KMessageBox::information(this, i18n("The plain text notes have been converted to rich text."), i18n("Conversion Finished"));
1731     else
1732         KMessageBox::information(this, i18n("There are no plain text notes to convert."), i18n("Conversion Finished"));
1733 }
1734 
1735 QMenu *BNPView::popupMenu(const QString &menuName)
1736 {
1737     QMenu *menu = nullptr;
1738 
1739     if (m_guiClient) {
1740         KXMLGUIFactory *factory = m_guiClient->factory();
1741         if (factory) {
1742             menu = (QMenu *)factory->container(menuName, m_guiClient);
1743         }
1744     }
1745     if (menu == nullptr) {
1746         QString basketDataPath = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/basket/";
1747 
1748         KMessageBox::error(this,
1749                            i18n("<p><b>The file basketui.rc seems to not exist or is too old.<br>"
1750                                 "%1 cannot run without it and will stop.</b></p>"
1751                                 "<p>Please check your installation of %2.</p>"
1752                                 "<p>If you do not have administrator access to install the application "
1753                                 "system wide, you can copy the file basketui.rc from the installation "
1754                                 "archive to the folder <a href='file://%3'>%4</a>.</p>"
1755                                 "<p>As last resort, if you are sure the application is correctly installed "
1756                                 "but you had a preview version of it, try to remove the "
1757                                 "file %5basketui.rc</p>",
1758                                 QGuiApplication::applicationDisplayName(),
1759                                 QGuiApplication::applicationDisplayName(),
1760                                 basketDataPath,
1761                                 basketDataPath,
1762                                 basketDataPath),
1763                            i18n("Resource not Found"),
1764                            KMessageBox::AllowLink);
1765         exit(1); // We SHOULD exit right now and aboard everything because the caller except menu != 0 to not crash.
1766     }
1767     return menu;
1768 }
1769 
1770 void BNPView::showHideFilterBar(bool show, bool switchFocus)
1771 {
1772     //  if (show != m_actShowFilter->isChecked())
1773     //      m_actShowFilter->setChecked(show);
1774     m_actShowFilter->setChecked(show);
1775 
1776     currentDecoratedBasket()->setFilterBarVisible(show, switchFocus);
1777     if (!show)
1778         currentDecoratedBasket()->resetFilter();
1779 }
1780 
1781 void BNPView::insertEmpty(int type)
1782 {
1783     if (currentBasket()->isLocked()) {
1784         return;
1785     }
1786     currentBasket()->insertEmptyNote(type);
1787 }
1788 
1789 void BNPView::insertWizard(int type)
1790 {
1791     if (currentBasket()->isLocked()) {
1792         Q_EMIT showErrorMessage(i18n("Cannot add note."));
1793         return;
1794     }
1795     currentBasket()->insertWizard(type);
1796 }
1797 
1798 // BEGIN Screen Grabbing:
1799 void BNPView::grabScreenshot(bool global)
1800 {
1801     if (m_regionGrabber) {
1802         KWindowSystem::activateWindow(m_regionGrabber->winId());
1803         return;
1804     }
1805 
1806     // Delay before to take a screenshot because if we hide the main window OR the systray popup menu,
1807     // we should wait the windows below to be repainted!!!
1808     // A special case is where the action is triggered with the global keyboard shortcut.
1809     // In this case, global is true, and we don't wait.
1810     // In the future, if global is also defined for other cases, check for
1811     // enum QAction::ActivationReason { UnknownActivation, EmulatedActivation, AccelActivation, PopupMenuActivation, ToolBarActivation };
1812     int delay = (isMainWindowActive() ? 500 : (global /*qApp->activePopupWidget()*/ ? 0 : 200));
1813 
1814     m_colorPickWasGlobal = global;
1815     hideMainWindow();
1816 
1817     currentBasket()->saveInsertionData();
1818     usleep(delay * 1000);
1819     m_regionGrabber = new RegionGrabber;
1820     connect(m_regionGrabber, &RegionGrabber::regionGrabbed, this, &BNPView::screenshotGrabbed);
1821 }
1822 
1823 void BNPView::hideMainWindow()
1824 {
1825     if (isMainWindowActive()) {
1826         if (Global::activeMainWindow()) {
1827             m_HiddenMainWindow = Global::activeMainWindow();
1828             m_HiddenMainWindow->hide();
1829         }
1830         m_colorPickWasShown = true;
1831     } else
1832         m_colorPickWasShown = false;
1833 }
1834 
1835 void BNPView::grabScreenshotGlobal()
1836 {
1837     grabScreenshot(true);
1838 }
1839 
1840 void BNPView::screenshotGrabbed(const QPixmap &pixmap)
1841 {
1842     delete m_regionGrabber;
1843     m_regionGrabber = nullptr;
1844 
1845     // Cancelled (pressed Escape):
1846     if (pixmap.isNull()) {
1847         if (m_colorPickWasShown)
1848             showMainWindow();
1849         return;
1850     }
1851 
1852     if (!currentBasket()->isLoaded()) {
1853         currentBasket()->load();
1854     }
1855     currentBasket()->insertImage(pixmap);
1856 
1857     if (m_colorPickWasShown)
1858         showMainWindow();
1859 
1860 }
1861 
1862 BasketScene *BNPView::basketForFolderName(const QString &folderName)
1863 {
1864     /*  QPtrList<Basket> basketsList = listBaskets();
1865         BasketScene *basket;
1866         for (basket = basketsList.first(); basket; basket = basketsList.next())
1867         if (basket->folderName() == folderName)
1868         return basket;
1869     */
1870 
1871     QString name = folderName;
1872     if (!name.endsWith('/'))
1873         name += '/';
1874 
1875     QTreeWidgetItemIterator it(m_tree);
1876     while (*it) {
1877         BasketListViewItem *item = ((BasketListViewItem *)*it);
1878         if (item->basket()->folderName() == name)
1879             return item->basket();
1880         ++it;
1881     }
1882 
1883     return nullptr;
1884 }
1885 
1886 Note *BNPView::noteForFileName(const QString &fileName, BasketScene &basket, Note *note)
1887 {
1888     if (!note)
1889         note = basket.firstNote();
1890     if (note->fullPath().endsWith(fileName))
1891         return note;
1892     Note *child = note->firstChild();
1893     Note *found;
1894     while (child) {
1895         found = noteForFileName(fileName, basket, child);
1896         if (found)
1897             return found;
1898         child = child->next();
1899     }
1900     return nullptr;
1901 }
1902 
1903 void BNPView::setFiltering(bool filtering)
1904 {
1905     m_actShowFilter->setChecked(filtering);
1906     m_actResetFilter->setEnabled(filtering);
1907     if (!filtering)
1908         m_actFilterAllBaskets->setEnabled(false);
1909 }
1910 
1911 void BNPView::undo()
1912 {
1913     // TODO
1914 }
1915 
1916 void BNPView::redo()
1917 {
1918     // TODO
1919 }
1920 
1921 void BNPView::pasteToBasket(int /*index*/, QClipboard::Mode /*mode*/)
1922 {
1923     // TODO: REMOVE!
1924     // basketAt(index)->pasteNote(mode);
1925 }
1926 
1927 void BNPView::propBasket()
1928 {
1929     BasketPropertiesDialog dialog(currentBasket(), this);
1930     dialog.exec();
1931 }
1932 
1933 void BNPView::delBasket()
1934 {
1935     //  DecoratedBasket *decoBasket    = currentDecoratedBasket();
1936     BasketScene *basket = currentBasket();
1937 
1938     int really = KMessageBox::questionYesNo(this,
1939                                             i18n("<qt>Do you really want to remove the basket <b>%1</b> and its contents?</qt>", Tools::textToHTMLWithoutP(basket->basketName())),
1940                                             i18n("Remove Basket"),
1941                                             KGuiItem(i18n("&Remove Basket"), "edit-delete"),
1942                                             KStandardGuiItem::cancel());
1943 
1944     if (really == KMessageBox::No)
1945         return;
1946 
1947     QStringList basketsList = listViewItemForBasket(basket)->childNamesTree(0);
1948     if (basketsList.count() > 0) {
1949         int deleteChilds = KMessageBox::questionYesNoList(this,
1950                                                           i18n("<qt><b>%1</b> has the following children baskets.<br>Do you want to remove them too?</qt>", Tools::textToHTMLWithoutP(basket->basketName())),
1951                                                           basketsList,
1952                                                           i18n("Remove Children Baskets"),
1953                                                           KGuiItem(i18n("&Remove Children Baskets"), "edit-delete"));
1954 
1955         if (deleteChilds == KMessageBox::No)
1956             return;
1957     }
1958 
1959     QString basketFolderName = basket->folderName();
1960     doBasketDeletion(basket);
1961 
1962     GitWrapper::commitDeleteBasket(basketFolderName);
1963 }
1964 
1965 void BNPView::doBasketDeletion(BasketScene *basket)
1966 {
1967     basket->closeEditor();
1968 
1969     QTreeWidgetItem *basketItem = listViewItemForBasket(basket);
1970     for (int i = 0; i < basketItem->childCount(); i++) {
1971         // First delete the child baskets:
1972         doBasketDeletion(((BasketListViewItem *)basketItem->child(i))->basket());
1973     }
1974     // Then, basket have no child anymore, delete it:
1975     DecoratedBasket *decoBasket = basket->decoration();
1976     basket->deleteFiles();
1977     removeBasket(basket);
1978     // Remove the action to avoid keyboard-shortcut clashes:
1979     delete basket->m_action; // FIXME: It's quick&dirty. In the future, the Basket should be deleted, and then the QAction deleted in the Basket destructor.
1980     delete decoBasket;
1981     //  delete basket;
1982 }
1983 
1984 void BNPView::password()
1985 {
1986 #ifdef HAVE_LIBGPGME
1987     QPointer<PasswordDlg> dlg = new PasswordDlg(qApp->activeWindow());
1988     BasketScene *cur = currentBasket();
1989 
1990     dlg->setType(cur->encryptionType());
1991     dlg->setKey(cur->encryptionKey());
1992     if (dlg->exec()) {
1993         cur->setProtection(dlg->type(), dlg->key());
1994         if (cur->encryptionType() != BasketScene::NoEncryption) {
1995             // Clear metadata
1996             Tools::deleteMetadataRecursively(cur->fullPath());
1997             cur->lock();
1998         }
1999     }
2000 #endif
2001 }
2002 
2003 void BNPView::lockBasket()
2004 {
2005 #ifdef HAVE_LIBGPGME
2006     BasketScene *cur = currentBasket();
2007 
2008     cur->lock();
2009 #endif
2010 }
2011 
2012 void BNPView::saveAsArchive()
2013 {
2014     BasketScene *basket = currentBasket();
2015 
2016     QDir dir;
2017 
2018     KConfigGroup config = KSharedConfig::openConfig()->group("Basket Archive");
2019     QString folder = config.readEntry("lastFolder", QDir::homePath()) + "/";
2020     QString url = folder + QString(basket->basketName()).replace('/', '_') + ".baskets";
2021 
2022     QString filter = "*.baskets|" + i18n("Basket Archives") + "\n*|" + i18n("All Files");
2023     QString destination = url;
2024     for (bool askAgain = true; askAgain;) {
2025         destination = QFileDialog::getSaveFileName(nullptr, i18n("Save as Basket Archive"), destination, filter);
2026         if (destination.isEmpty()) // User canceled
2027             return;
2028         if (dir.exists(destination)) {
2029             int result = KMessageBox::questionYesNoCancel(
2030                 this, "<qt>" + i18n("The file <b>%1</b> already exists. Do you really want to overwrite it?", QUrl::fromLocalFile(destination).fileName()), i18n("Overwrite File?"), KGuiItem(i18n("&Overwrite"), "document-save"));
2031             if (result == KMessageBox::Cancel)
2032                 return;
2033             else if (result == KMessageBox::Yes)
2034                 askAgain = false;
2035         } else
2036             askAgain = false;
2037     }
2038     bool withSubBaskets = true; // KMessageBox::questionYesNo(this, i18n("Do you want to export sub-baskets too?"), i18n("Save as Basket Archive")) == KMessageBox::Yes;
2039 
2040     config.writeEntry("lastFolder", QUrl::fromLocalFile(destination).adjusted(QUrl::RemoveFilename).path());
2041     config.sync();
2042 
2043     Archive::save(basket, withSubBaskets, destination);
2044 }
2045 
2046 QString BNPView::s_fileToOpen;
2047 
2048 void BNPView::delayedOpenArchive()
2049 {
2050     Archive::open(s_fileToOpen);
2051 }
2052 
2053 QString BNPView::s_basketToOpen;
2054 
2055 void BNPView::delayedOpenBasket()
2056 {
2057     BasketScene *bv = this->basketForFolderName(s_basketToOpen);
2058     this->setCurrentBasketInHistory(bv);
2059 }
2060 
2061 void BNPView::openArchive()
2062 {
2063     QString filter = QStringLiteral("*.baskets|") + i18n("Basket Archives") + QStringLiteral("\n*|") + i18n("All Files");
2064     QString path = QFileDialog::getOpenFileName(this, i18n("Open Basket Archive"), QString(), filter);
2065     if (!path.isEmpty()) { // User has not canceled
2066         Archive::open(path);
2067     }
2068 }
2069 
2070 void BNPView::activatedTagShortcut()
2071 {
2072     Tag *tag = Tag::tagForKAction((QAction *)sender());
2073     currentBasket()->activatedTagShortcut(tag);
2074 }
2075 
2076 void BNPView::slotBasketChanged()
2077 {
2078     m_actFoldBasket->setEnabled(canFold());
2079     m_actExpandBasket->setEnabled(canExpand());
2080     if (currentBasket()->decoration()->filterData().isFiltering)
2081         currentBasket()->decoration()->filterBar()->show(); // especially important for Filter all
2082     setFiltering(currentBasket() && currentBasket()->decoration()->filterData().isFiltering);
2083     this->canUndoRedoChanged();
2084 }
2085 
2086 void BNPView::canUndoRedoChanged()
2087 {
2088     if (m_history) {
2089         m_actPreviousBasket->setEnabled(m_history->canUndo());
2090         m_actNextBasket->setEnabled(m_history->canRedo());
2091     }
2092 }
2093 
2094 void BNPView::currentBasketChanged()
2095 {
2096 }
2097 
2098 void BNPView::isLockedChanged()
2099 {
2100     bool isLocked = currentBasket()->isLocked();
2101 
2102     setLockStatus(isLocked);
2103 
2104     //  m_actLockBasket->setChecked(isLocked);
2105     m_actPropBasket->setEnabled(!isLocked);
2106     m_actDelBasket->setEnabled(!isLocked);
2107     updateNotesActions();
2108 }
2109 
2110 void BNPView::askNewBasket()
2111 {
2112     askNewBasket(nullptr, nullptr);
2113 
2114     GitWrapper::commitCreateBasket();
2115 }
2116 
2117 void BNPView::askNewBasket(BasketScene *parent, BasketScene *pickProperties)
2118 {
2119     NewBasketDefaultProperties properties;
2120     if (pickProperties) {
2121         properties.icon = pickProperties->icon();
2122         properties.backgroundImage = pickProperties->backgroundImageName();
2123         properties.backgroundColor = pickProperties->backgroundColorSetting();
2124         properties.textColor = pickProperties->textColorSetting();
2125         properties.freeLayout = pickProperties->isFreeLayout();
2126         properties.columnCount = pickProperties->columnsCount();
2127     }
2128 
2129     NewBasketDialog(parent, properties, this).exec();
2130 }
2131 
2132 void BNPView::askNewSubBasket()
2133 {
2134     askNewBasket(/*parent=*/currentBasket(), /*pickPropertiesOf=*/currentBasket());
2135 }
2136 
2137 void BNPView::askNewSiblingBasket()
2138 {
2139     askNewBasket(/*parent=*/parentBasketOf(currentBasket()), /*pickPropertiesOf=*/currentBasket());
2140 }
2141 
2142 void BNPView::globalPasteInCurrentBasket()
2143 {
2144     currentBasket()->setInsertPopupMenu();
2145     pasteInCurrentBasket();
2146     currentBasket()->cancelInsertPopupMenu();
2147 }
2148 
2149 void BNPView::pasteInCurrentBasket()
2150 {
2151     currentBasket()->pasteNote();
2152 }
2153 
2154 void BNPView::pasteSelInCurrentBasket()
2155 {
2156     currentBasket()->pasteNote(QClipboard::Selection);
2157 }
2158 
2159 void BNPView::addNoteText()
2160 {
2161     showMainWindow();
2162     currentBasket()->insertEmptyNote(NoteType::Text);
2163 }
2164 void BNPView::addNoteHtml()
2165 {
2166     showMainWindow();
2167     currentBasket()->insertEmptyNote(NoteType::Html);
2168 }
2169 void BNPView::addNoteImage()
2170 {
2171     showMainWindow();
2172     currentBasket()->insertEmptyNote(NoteType::Image);
2173 }
2174 void BNPView::addNoteLink()
2175 {
2176     showMainWindow();
2177     currentBasket()->insertEmptyNote(NoteType::Link);
2178 }
2179 void BNPView::addNoteCrossReference()
2180 {
2181     showMainWindow();
2182     currentBasket()->insertEmptyNote(NoteType::CrossReference);
2183 }
2184 void BNPView::addNoteColor()
2185 {
2186     showMainWindow();
2187     currentBasket()->insertEmptyNote(NoteType::Color);
2188 }
2189 
2190 void BNPView::aboutToHideNewBasketPopup()
2191 {
2192     QTimer::singleShot(0, this, SLOT(cancelNewBasketPopup()));
2193 }
2194 
2195 void BNPView::cancelNewBasketPopup()
2196 {
2197     m_newBasketPopup = false;
2198 }
2199 
2200 void BNPView::setNewBasketPopup()
2201 {
2202     m_newBasketPopup = true;
2203 }
2204 
2205 void BNPView::setWindowTitle(QString s)
2206 {
2207     Q_EMIT setWindowCaption(s);
2208 }
2209 
2210 void BNPView::updateStatusBarHint()
2211 {
2212     m_statusbar->updateStatusBarHint();
2213 }
2214 
2215 void BNPView::setSelectionStatus(QString s)
2216 {
2217     m_statusbar->setSelectionStatus(s);
2218 }
2219 
2220 void BNPView::setLockStatus(bool isLocked)
2221 {
2222     m_statusbar->setLockStatus(isLocked);
2223 }
2224 
2225 void BNPView::postStatusbarMessage(const QString &msg)
2226 {
2227     m_statusbar->postStatusbarMessage(msg);
2228 }
2229 
2230 void BNPView::setStatusBarHint(const QString &hint)
2231 {
2232     m_statusbar->setStatusBarHint(hint);
2233 }
2234 
2235 void BNPView::setUnsavedStatus(bool isUnsaved)
2236 {
2237     m_statusbar->setUnsavedStatus(isUnsaved);
2238 }
2239 
2240 void BNPView::setActive(bool active)
2241 {
2242     KMainWindow *win = Global::activeMainWindow();
2243     if (!win)
2244         return;
2245 
2246     if (active == isMainWindowActive())
2247         return;
2248     // qApp->updateUserTimestamp(); // If "activate on mouse hovering systray", or "on drag through systray"
2249 }
2250 
2251 bool BNPView::isMainWindowActive()
2252 {
2253     KMainWindow *main = Global::activeMainWindow();
2254     if (main && main->isActiveWindow())
2255         return true;
2256     return false;
2257 }
2258 
2259 void BNPView::newBasket()
2260 {
2261     askNewBasket();
2262 }
2263 
2264 bool BNPView::createNoteHtml(const QString content, const QString basket)
2265 {
2266     BasketScene *b = basketForFolderName(basket);
2267     if (!b)
2268         return false;
2269     Note *note = NoteFactory::createNoteHtml(content, b);
2270     if (!note)
2271         return false;
2272     b->insertCreatedNote(note);
2273     return true;
2274 }
2275 
2276 bool BNPView::changeNoteHtml(const QString content, const QString basket, const QString noteName)
2277 {
2278     BasketScene *b = basketForFolderName(basket);
2279     if (!b)
2280         return false;
2281     Note *note = noteForFileName(noteName, *b);
2282     if (!note || note->content()->type() != NoteType::Html)
2283         return false;
2284     HtmlContent *noteContent = (HtmlContent *)note->content();
2285     noteContent->setHtml(content);
2286     note->saveAgain();
2287     return true;
2288 }
2289 
2290 bool BNPView::createNoteFromFile(const QString url, const QString basket)
2291 {
2292     BasketScene *b = basketForFolderName(basket);
2293     if (!b)
2294         return false;
2295     QUrl kurl(url);
2296     if (url.isEmpty())
2297         return false;
2298     Note *n = NoteFactory::copyFileAndLoad(kurl, b);
2299     if (!n)
2300         return false;
2301     b->insertCreatedNote(n);
2302     return true;
2303 }
2304 
2305 QStringList BNPView::listBaskets()
2306 {
2307     QStringList basketList;
2308 
2309     QTreeWidgetItemIterator it(m_tree);
2310     while (*it) {
2311         BasketListViewItem *item = ((BasketListViewItem *)*it);
2312         basketList.append(item->basket()->basketName());
2313         basketList.append(item->basket()->folderName());
2314         ++it;
2315     }
2316     return basketList;
2317 }
2318 
2319 void BNPView::handleCommandLine()
2320 {
2321     QCommandLineParser *parser = Global::commandLineOpts;
2322 
2323     /* Custom data folder */
2324     QString customDataFolder = parser->value("data-folder");
2325     if (!customDataFolder.isNull() && !customDataFolder.isEmpty()) {
2326         Global::setCustomSavesFolder(customDataFolder);
2327     }
2328     /* Debug window */
2329     if (parser->isSet("debug")) {
2330         new DebugWindow();
2331         Global::debugWindow->show();
2332     }
2333 }
2334 
2335 void BNPView::reloadBasket(const QString &folderName)
2336 {
2337     basketForFolderName(folderName)->reload();
2338 }
2339 
2340 
2341 void BNPView::changedSelectedNotes()
2342 {
2343     //  tabChanged(0); // FIXME: NOT OPTIMIZED
2344 }
2345 
2346 /*void BNPView::areSelectedNotesCheckedChanged(bool checked)
2347 {
2348     m_actCheckNotes->setChecked(checked && currentBasket()->showCheckBoxes());
2349 }*/
2350 
2351 void BNPView::enableActions()
2352 {
2353     BasketScene *basket = currentBasket();
2354     if (!basket)
2355         return;
2356     if (m_actLockBasket)
2357         m_actLockBasket->setEnabled(!basket->isLocked() && basket->isEncrypted());
2358     if (m_actPassBasket)
2359         m_actPassBasket->setEnabled(!basket->isLocked());
2360     m_actPropBasket->setEnabled(!basket->isLocked());
2361     m_actDelBasket->setEnabled(!basket->isLocked());
2362     m_actExportToHtml->setEnabled(!basket->isLocked());
2363     m_actShowFilter->setEnabled(!basket->isLocked());
2364     m_actFilterAllBaskets->setEnabled(!basket->isLocked());
2365     m_actResetFilter->setEnabled(!basket->isLocked());
2366     basket->decoration()->filterBar()->setEnabled(!basket->isLocked());
2367 }
2368 
2369 void BNPView::showMainWindow()
2370 {
2371     if (m_HiddenMainWindow) {
2372         m_HiddenMainWindow->show();
2373         m_HiddenMainWindow = nullptr;
2374     } else {
2375         KMainWindow *win = Global::activeMainWindow();
2376 
2377         if (win) {
2378             win->show();
2379         }
2380     }
2381 
2382     setActive(true);
2383     Q_EMIT showPart();
2384 }
2385 
2386 void BNPView::populateTagsMenu()
2387 {
2388     QMenu *menu = (QMenu *)(popupMenu("tags"));
2389     if (menu == nullptr || currentBasket() == nullptr) // TODO: Display a messagebox. [menu is 0, surely because on first launch, the XMLGUI does not work!]
2390         return;
2391     menu->clear();
2392 
2393     Note *referenceNote;
2394     if (currentBasket()->focusedNote() && currentBasket()->focusedNote()->isSelected())
2395         referenceNote = currentBasket()->focusedNote();
2396     else
2397         referenceNote = currentBasket()->firstSelected();
2398 
2399     populateTagsMenu(*menu, referenceNote);
2400 
2401     m_lastOpenedTagsMenu = menu;
2402     //connect(menu, &QMenu::aboutToHide, this, &BNPView::disconnectTagsMenu);
2403 }
2404 
2405 void BNPView::populateTagsMenu(QMenu &menu, Note *referenceNote)
2406 {
2407     if (currentBasket() == nullptr)
2408         return;
2409 
2410     currentBasket()->m_tagPopupNote = referenceNote;
2411     bool enable = currentBasket()->countSelecteds() > 0;
2412 
2413     QList<Tag *>::iterator it;
2414     Tag *currentTag;
2415     State *currentState;
2416     int i = 10;
2417     for (it = Tag::all.begin(); it != Tag::all.end(); ++it) {
2418         // Current tag and first state of it:
2419         currentTag = *it;
2420         currentState = currentTag->states().first();
2421 
2422         QKeySequence sequence;
2423         if (!currentTag->shortcut().isEmpty())
2424             sequence = currentTag->shortcut();
2425 
2426         StateAction *mi = new StateAction(currentState, QKeySequence(sequence), this, true);
2427         mi->setShortcutContext(Qt::WidgetShortcut);
2428 
2429         // The previously set ID will be set in the actions themselves as data.
2430         mi->setData(i);
2431 
2432         if (referenceNote && referenceNote->hasTag(currentTag))
2433             mi->setChecked(true);
2434 
2435         menu.addAction(mi);
2436 
2437         if (!currentTag->shortcut().isEmpty())
2438             m_actionCollection->setDefaultShortcut(mi, sequence);
2439 
2440         mi->setEnabled(enable);
2441         ++i;
2442     }
2443 
2444     menu.addSeparator();
2445 
2446     // I don't like how this is implemented; but I can't think of a better way
2447     // to do this, so I will have to leave it for now
2448     QAction *act = new QAction(i18n("&Assign new Tag..."), &menu);
2449     act->setData(1);
2450     act->setEnabled(enable);
2451     menu.addAction(act);
2452 
2453     act = new QAction(QIcon::fromTheme("edit-delete"), i18n("&Remove All"), &menu);
2454     act->setData(2);
2455     if (!currentBasket()->selectedNotesHaveTags())
2456         act->setEnabled(false);
2457     menu.addAction(act);
2458 
2459     act = new QAction(QIcon::fromTheme("configure"), i18n("&Customize..."), &menu);
2460     act->setData(3);
2461     menu.addAction(act);
2462 
2463     connect(&menu, &QMenu::triggered, currentBasket(), &BasketScene::toggledTagInMenu);
2464     connect(&menu, &QMenu::aboutToHide, currentBasket(), &BasketScene::unlockHovering);
2465     connect(&menu, &QMenu::aboutToHide, currentBasket(), &BasketScene::disableNextClick);
2466 }
2467 
2468 void BNPView::connectTagsMenu()
2469 {
2470     connect(popupMenu("tags"), &QMenu::aboutToShow, this, [this]() { this->populateTagsMenu(); });
2471     connect(popupMenu("tags"), &QMenu::aboutToHide, this, &BNPView::disconnectTagsMenu);
2472 }
2473 
2474 void BNPView::showEvent(QShowEvent *)
2475 {
2476     if (m_firstShow) {
2477         m_firstShow = false;
2478         onFirstShow();
2479     }
2480 }
2481 
2482 void BNPView::disconnectTagsMenu()
2483 {
2484     QTimer::singleShot(0, this, SLOT(disconnectTagsMenuDelayed()));
2485 }
2486 
2487 void BNPView::disconnectTagsMenuDelayed()
2488 {
2489     m_lastOpenedTagsMenu->disconnect(currentBasket());
2490 }
2491 
2492 void BNPView::loadCrossReference(QString link)
2493 {
2494     // remove "basket://" and any encoding.
2495     QString folderName = link.mid(9, link.length() - 9);
2496     folderName = QUrl::fromPercentEncoding(folderName.toUtf8());
2497     BasketScene *basket = this->basketForFolderName(folderName);
2498     if (!basket)
2499         return;
2500 
2501     this->setCurrentBasketInHistory(basket);
2502 }
2503 
2504 QString BNPView::folderFromBasketNameLink(QStringList pages, QTreeWidgetItem *parent)
2505 {
2506     if (pages.isEmpty()) return QString();
2507     QString page = QUrl::fromPercentEncoding(pages.takeFirst().toUtf8()).trimmed();
2508 
2509     if (page == "..")
2510     {
2511         QTreeWidgetItem *p = parent? parent->parent() : m_tree->currentItem()->parent();
2512         return this->folderFromBasketNameLink(pages, p);
2513     }
2514 
2515     if (page == ".")
2516     {
2517         parent = parent? parent : m_tree->currentItem();
2518         return this->folderFromBasketNameLink(pages, parent);
2519     }
2520 
2521     if (!parent && page.isEmpty())
2522     {
2523         parent = m_tree->invisibleRootItem();
2524         return this->folderFromBasketNameLink(pages, parent);
2525     }
2526 
2527     parent = parent? parent : m_tree->currentItem();
2528     QRegExp re(":\\{([0-9]+)\\}");
2529     re.setMinimal(true);
2530     int pos = 0;
2531 
2532     pos = re.indexIn(page, pos);
2533     int basketNum = 1;
2534 
2535     if (pos != -1)
2536         basketNum = re.cap(1).toInt();
2537 
2538     page = page.left(page.length() - re.matchedLength());
2539 
2540     for (int i = 0; i < parent->childCount(); i++) {
2541         QTreeWidgetItem *child = parent->child(i);
2542 
2543         if (child->text(0).toLower() == page.toLower()) {
2544             basketNum--;
2545             if (basketNum == 0) {
2546                 if (pages.count() > 0) {
2547                     return this->folderFromBasketNameLink(pages, child);
2548                 } else {
2549                     return ((BasketListViewItem *)child)->basket()->folderName();
2550                 }
2551             }
2552         }
2553     }
2554 
2555     return QString();
2556 }
2557 
2558 void BNPView::sortChildrenAsc()
2559 {
2560     m_tree->currentItem()->sortChildren(0, Qt::AscendingOrder);
2561 }
2562 
2563 void BNPView::sortChildrenDesc()
2564 {
2565     m_tree->currentItem()->sortChildren(0, Qt::DescendingOrder);
2566 }
2567 
2568 void BNPView::sortSiblingsAsc()
2569 {
2570     QTreeWidgetItem *parent = m_tree->currentItem()->parent();
2571     if (!parent)
2572         m_tree->sortItems(0, Qt::AscendingOrder);
2573     else
2574         parent->sortChildren(0, Qt::AscendingOrder);
2575 }
2576 
2577 void BNPView::sortSiblingsDesc()
2578 {
2579     QTreeWidgetItem *parent = m_tree->currentItem()->parent();
2580     if (!parent)
2581         m_tree->sortItems(0, Qt::DescendingOrder);
2582     else
2583         parent->sortChildren(0, Qt::DescendingOrder);
2584 }
2585 
2586 #include "moc_bnpview.cpp"