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"