diff --git a/krusader/BookMan/krbookmarkbutton.cpp b/krusader/BookMan/krbookmarkbutton.cpp index 54f12fa7..be7d259e 100644 --- a/krusader/BookMan/krbookmarkbutton.cpp +++ b/krusader/BookMan/krbookmarkbutton.cpp @@ -1,64 +1,60 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This package is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "krbookmarkbutton.h" #include "krbookmarkhandler.h" #include "../krglobal.h" // QtGui #include // QtWidgets #include #include #include #include KrBookmarkButton::KrBookmarkButton(QWidget *parent): QToolButton(parent) { setAutoRaise(true); setIcon(QIcon::fromTheme("bookmarks")); setText(i18n("BookMan II")); setToolTip(i18n("BookMan II")); setPopupMode(QToolButton::InstantPopup); setAcceptDrops(false); acmBookmarks = new KActionMenu(QIcon::fromTheme("bookmarks"), i18n("Bookmarks"), this); acmBookmarks->setDelayed(false); - // TODO KF5 : explicit cast as QMenu doesn't have those methods - //(acmBookmarks->menu())->setKeyboardShortcutsEnabled(true); - //(acmBookmarks->menu())->setKeyboardShortcutsExecute(true); setMenu(acmBookmarks->menu()); connect(acmBookmarks->menu(), SIGNAL(aboutToShow()), this, SLOT(populate())); connect(acmBookmarks->menu(), SIGNAL(aboutToShow()), this, SIGNAL(aboutToShow())); - populate(); } void KrBookmarkButton::populate() { krBookMan->populate(static_cast(menu())); } void KrBookmarkButton::showMenu() { populate(); menu()->exec(mapToGlobal(QPoint(0, height()))); } diff --git a/krusader/BookMan/krbookmarkhandler.cpp b/krusader/BookMan/krbookmarkhandler.cpp index 46c4041f..c09ddee5 100644 --- a/krusader/BookMan/krbookmarkhandler.cpp +++ b/krusader/BookMan/krbookmarkhandler.cpp @@ -1,752 +1,781 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This package is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #include "krbookmarkhandler.h" #include "kraddbookmarkdlg.h" #include "../krglobal.h" #include "../krslots.h" #include "../kractions.h" #include "../krmainwindow.h" #include "../Dialogs/popularurls.h" #include "../FileSystem/filesystem.h" #include "../Panel/krpanel.h" #include "../Panel/listpanelactions.h" // QtCore #include #include #include #include // QtGui #include #include #include #include #include #include #include #include #define SPECIAL_BOOKMARKS true // ------------------------ for internal use #define BOOKMARKS_FILE "krusader/krbookmarks.xml" #define CONNECT_BM(X) { disconnect(X, SIGNAL(activated(QUrl)), 0, 0); connect(X, SIGNAL(activated(QUrl)), this, SLOT(slotActivated(QUrl))); } KrBookmarkHandler::KrBookmarkHandler(KrMainWindow *mainWindow) : QObject(mainWindow->widget()), - _mainWindow(mainWindow), _middleClick(false), _mainBookmarkPopup(0), _specialBookmarks() + _mainWindow(mainWindow), _middleClick(false), _mainBookmarkPopup(0), _specialBookmarks(), + _quickSearchAction(0) { // create our own action collection and make the shortcuts apply only to parent _privateCollection = new KActionCollection(this); _collection = _mainWindow->actions(); // create _root: father of all bookmarks. it is a dummy bookmark and never shown _root = new KrBookmark(i18n("Bookmarks")); _root->setParent(this); // load bookmarks importFromFile(); - // hack + // create bookmark manager QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + BOOKMARKS_FILE; manager = KBookmarkManager::managerForFile(filename, QStringLiteral("krusader")); connect(manager, SIGNAL(changed(QString,QString)), this, SLOT(bookmarksChanged(QString,QString))); + + // create the quick search bar and action + _quickSearchAction = new QWidgetAction(this); + _quickSearchBar = new QLineEdit(); + _quickSearchAction->setDefaultWidget(_quickSearchBar); // ownership of the bar is transferred to the action + _quickSearchAction->setEnabled(false); + _quickSearchAction->setVisible(false); + + // fill a dummy menu to properly init actions (allows toolbar bookmark buttons to work properly) + auto menu = new QMenu(mainWindow->widget()); + populate(menu); + menu->deleteLater(); } KrBookmarkHandler::~KrBookmarkHandler() { delete manager; delete _privateCollection; } void KrBookmarkHandler::bookmarkCurrent(QUrl url) { QPointer dlg = new KrAddBookmarkDlg(_mainWindow->widget(), url); if (dlg->exec() == QDialog::Accepted) { KrBookmark *bm = new KrBookmark(dlg->name(), dlg->url(), _collection); addBookmark(bm, dlg->folder()); } delete dlg; } void KrBookmarkHandler::addBookmark(KrBookmark *bm, KrBookmark *folder) { if (folder == 0) folder = _root; // add to the list (bottom) folder->children().append(bm); exportToFile(); } void KrBookmarkHandler::deleteBookmark(KrBookmark *bm) { if (bm->isFolder()) clearBookmarks(bm); // remove the child bookmarks removeReferences(_root, bm); foreach(QWidget *w, bm->associatedWidgets()) w->removeAction(bm); delete bm; exportToFile(); } void KrBookmarkHandler::removeReferences(KrBookmark *root, KrBookmark *bmToRemove) { int index = root->children().indexOf(bmToRemove); if (index >= 0) root->children().removeAt(index); QListIterator it(root->children()); while (it.hasNext()) { KrBookmark *bm = it.next(); if (bm->isFolder()) removeReferences(bm, bmToRemove); } } void KrBookmarkHandler::exportToFileBookmark(QDomDocument &doc, QDomElement &where, KrBookmark *bm) { if (bm->isSeparator()) { QDomElement bookmark = doc.createElement("separator"); where.appendChild(bookmark); } else { QDomElement bookmark = doc.createElement("bookmark"); // url bookmark.setAttribute("href", bm->url().toDisplayString()); // icon bookmark.setAttribute("icon", bm->iconName()); // title QDomElement title = doc.createElement("title"); title.appendChild(doc.createTextNode(bm->text())); bookmark.appendChild(title); where.appendChild(bookmark); } } void KrBookmarkHandler::exportToFileFolder(QDomDocument &doc, QDomElement &parent, KrBookmark *folder) { QListIterator it(folder->children()); while (it.hasNext()) { KrBookmark *bm = it.next(); if (bm->isFolder()) { QDomElement newFolder = doc.createElement("folder"); newFolder.setAttribute("icon", bm->iconName()); parent.appendChild(newFolder); QDomElement title = doc.createElement("title"); title.appendChild(doc.createTextNode(bm->text())); newFolder.appendChild(title); exportToFileFolder(doc, newFolder, bm); } else { exportToFileBookmark(doc, parent, bm); } } } // export to file using the xbel standard // // // Developer Web Site // // Title of this folder // KDE Web Site // // My own bookmarks // KOffice Web Site // // KDevelop Web Site // // // void KrBookmarkHandler::exportToFile() { QDomDocument doc("xbel"); QDomElement root = doc.createElement("xbel"); doc.appendChild(root); exportToFileFolder(doc, root, _root); if (!doc.firstChild().isProcessingInstruction()) { // adding: if not already present QDomProcessingInstruction instr = doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\" "); doc.insertBefore(instr, doc.firstChild()); } QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + BOOKMARKS_FILE; QFile file(filename); if (file.open(QIODevice::WriteOnly)) { QTextStream stream(&file); stream.setCodec("UTF-8"); stream << doc.toString(); file.close(); } else { KMessageBox::error(_mainWindow->widget(), i18n("Unable to write to %1", filename), i18n("Error")); } } bool KrBookmarkHandler::importFromFileBookmark(QDomElement &e, KrBookmark *parent, QString path, QString *errorMsg) { QString url, name, icon; // verify tag if (e.tagName() != "bookmark") { *errorMsg = i18n("%1 instead of %2", e.tagName(), QLatin1String("bookmark")); return false; } // verify href if (!e.hasAttribute("href")) { *errorMsg = i18n("missing tag %1", QLatin1String("href")); return false; } else url = e.attribute("href"); // verify title QDomElement te = e.firstChild().toElement(); if (te.tagName() != "title") { *errorMsg = i18n("missing tag %1", QLatin1String("title")); return false; } else name = te.text(); // do we have an icon? if (e.hasAttribute("icon")) { icon = e.attribute("icon"); } // ok: got name and url, let's add a bookmark KrBookmark *bm = KrBookmark::getExistingBookmark(path + name, _collection); if (!bm) { bm = new KrBookmark(name, QUrl(url), _collection, icon, path + name); parent->children().append(bm); } return true; } bool KrBookmarkHandler::importFromFileFolder(QDomNode &first, KrBookmark *parent, QString path, QString *errorMsg) { QString name; QDomNode n = first; while (!n.isNull()) { QDomElement e = n.toElement(); if (e.tagName() == "bookmark") { if (!importFromFileBookmark(e, parent, path, errorMsg)) return false; } else if (e.tagName() == "folder") { QString iconName = ""; if (e.hasAttribute("icon")) iconName = e.attribute("icon"); // the title is the first child of the folder QDomElement tmp = e.firstChild().toElement(); if (tmp.tagName() != "title") { *errorMsg = i18n("missing tag %1", QLatin1String("title")); return false; } else name = tmp.text(); KrBookmark *folder = new KrBookmark(name, iconName); parent->children().append(folder); QDomNode nextOne = tmp.nextSibling(); if (!importFromFileFolder(nextOne, folder, path + name + '/', errorMsg)) return false; } else if (e.tagName() == "separator") { parent->children().append(KrBookmark::separator()); } n = n.nextSibling(); } return true; } void KrBookmarkHandler::importFromFile() { clearBookmarks(_root); QString filename = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1Char('/') + BOOKMARKS_FILE; QFile file(filename); if (!file.open(QIODevice::ReadOnly)) return; // no bookmarks file QString errorMsg; QDomNode n; QDomElement e; QDomDocument doc("xbel"); if (!doc.setContent(&file, &errorMsg)) { goto BM_ERROR; } // iterate through the document: first child should be "xbel" (skip all until we find it) n = doc.firstChild(); while (!n.isNull() && n.toElement().tagName() != "xbel") n = n.nextSibling(); if (n.isNull() || n.toElement().tagName() != "xbel") { errorMsg = i18n("%1 does not seem to be a valid bookmarks file", filename); goto BM_ERROR; } else n = n.firstChild(); // skip the xbel part importFromFileFolder(n, _root, "", &errorMsg); goto BM_SUCCESS; BM_ERROR: KMessageBox::error(_mainWindow->widget(), i18n("Error reading bookmarks file: %1", errorMsg), i18n("Error")); BM_SUCCESS: file.close(); } +void KrBookmarkHandler::_setQuickSearchText(const QString &text) +{ + _quickSearchBar->setText(text); + + auto length = text.length(); + _quickSearchAction->setVisible(length > 0); + _quickSearchBar->setVisible(length > 0); + if (length == 0) { + resetShortcuts(); + } +} + +QString KrBookmarkHandler::_quickSearchText() const +{ + return _quickSearchBar->text(); +} + void KrBookmarkHandler::populate(QMenu *menu) { _mainBookmarkPopup = menu; menu->clear(); _specialBookmarks.clear(); buildMenu(_root, menu); } -void KrBookmarkHandler::buildMenu(KrBookmark *parent, QMenu *menu) +void KrBookmarkHandler::buildMenu(KrBookmark *parent, QMenu *menu, int depth) { - static int inSecondaryMenu = 0; // used to know if we're on the top menu + // add search bar widget to the top of the menu + if (depth == 0) { + menu->addAction(_quickSearchAction); + } // run the loop twice, in order to put the folders on top. stupid but easy :-) // note: this code drops the separators put there by the user QListIterator it(parent->children()); while (it.hasNext()) { KrBookmark *bm = it.next(); if (!bm->isFolder()) continue; QMenu *newMenu = new QMenu(menu); newMenu->setIcon(QIcon(krLoader->loadIcon(bm->iconName(), KIconLoader::Small))); newMenu->setTitle(bm->text()); QAction *menuAction = menu->addMenu(newMenu); QVariant v; v.setValue(bm); menuAction->setData(v); - ++inSecondaryMenu; - buildMenu(bm, newMenu); - --inSecondaryMenu; + buildMenu(bm, newMenu, depth + 1); } it.toFront(); while (it.hasNext()) { KrBookmark *bm = it.next(); if (bm->isFolder()) continue; if (bm->isSeparator()) { menu->addSeparator(); continue; } menu->addAction(bm); CONNECT_BM(bm); } - if (!inSecondaryMenu) { + if (depth == 0) { KConfigGroup group(krConfig, "Private"); bool hasPopularURLs = group.readEntry("BM Popular URLs", true); bool hasTrash = group.readEntry("BM Trash", true); bool hasLan = group.readEntry("BM Lan", true); bool hasVirtualFS = group.readEntry("BM Virtual FS", true); bool hasJumpback = group.readEntry("BM Jumpback", true); if (hasPopularURLs) { menu->addSeparator(); // add the popular links submenu QMenu *newMenu = new QMenu(menu); newMenu->setTitle(i18n("Popular URLs")); newMenu->setIcon(QIcon(krLoader->loadIcon("folder-bookmark", KIconLoader::Small))); QAction *bmfAct = menu->addMenu(newMenu); _specialBookmarks.append(bmfAct); // add the top 15 urls #define MAX 15 QList list = _mainWindow->popularUrls()->getMostPopularUrls(MAX); QList::Iterator it; for (it = list.begin(); it != list.end(); ++it) { QString name; if ((*it).isLocalFile()) name = (*it).path(); else name = (*it).toDisplayString(); // note: these bookmark are put into the private collection // as to not spam the general collection KrBookmark *bm = KrBookmark::getExistingBookmark(name, _privateCollection); if (!bm) bm = new KrBookmark(name, *it, _privateCollection); newMenu->addAction(bm); CONNECT_BM(bm); } newMenu->addSeparator(); newMenu->addAction(krPopularUrls); newMenu->installEventFilter(this); } // do we need to add special bookmarks? if (SPECIAL_BOOKMARKS) { if (hasTrash || hasLan || hasVirtualFS || hasJumpback) menu->addSeparator(); KrBookmark *bm; // note: special bookmarks are not kept inside the _bookmarks list and added ad-hoc if (hasTrash) { bm = KrBookmark::trash(_collection); menu->addAction(bm); _specialBookmarks.append(bm); CONNECT_BM(bm); } if (hasLan) { bm = KrBookmark::lan(_collection); menu->addAction(bm); _specialBookmarks.append(bm); CONNECT_BM(bm); } if (hasVirtualFS) { bm = KrBookmark::virt(_collection); menu->addAction(bm); _specialBookmarks.append(bm); CONNECT_BM(bm); } if (hasJumpback) { // add the jump-back button ListPanelActions *actions = _mainWindow->listPanelActions(); - menu->addAction(actions->actJumpBack); - _specialBookmarks.append(actions->actJumpBack); - menu->addSeparator(); - menu->addAction(actions->actSetJumpBack); - _specialBookmarks.append(actions->actSetJumpBack); + if (actions) { + menu->addAction(actions->actJumpBack); + _specialBookmarks.append(actions->actJumpBack); + menu->addSeparator(); + menu->addAction(actions->actSetJumpBack); + _specialBookmarks.append(actions->actSetJumpBack); + } } } if (!hasJumpback) menu->addSeparator(); menu->addAction(KrActions::actAddBookmark); _specialBookmarks.append(KrActions::actAddBookmark); QAction *bmAct = menu->addAction(krLoader->loadIcon("bookmarks", KIconLoader::Small), i18n("Manage Bookmarks"), manager, SLOT(slotEditBookmarks())); _specialBookmarks.append(bmAct); // make sure the menu is connected to us disconnect(menu, SIGNAL(triggered(QAction*)), 0, 0); } menu->installEventFilter(this); } void KrBookmarkHandler::clearBookmarks(KrBookmark *root) { QList::iterator it = root->children().begin(); while (it != root->children().end()) { KrBookmark *bm = *it; if (bm->isFolder()) clearBookmarks(bm); else { foreach(QWidget *w, bm->associatedWidgets()) w->removeAction(bm); delete bm; } it = root->children().erase(it); } } void KrBookmarkHandler::bookmarksChanged(const QString&, const QString&) { importFromFile(); } bool KrBookmarkHandler::eventFilter(QObject *obj, QEvent *ev) { if (obj->inherits("QMenu") && (ev->type() == QEvent::Show || ev->type() == QEvent::Close)) { - _menuSearch = ""; - resetShortcuts(); + _setQuickSearchText(""); } // Having it occur on keypress is consistent with other shortcuts, // such as ctrl+w and the '&' bookmark shortcut standard // (&movies makes m a shortcut for said bookmark) if (ev->type() == QEvent::KeyPress && obj->inherits("QMenu")) { QKeyEvent *kev = static_cast(ev); QMenu *menu = static_cast(obj); QList acts = menu->actions(); if (kev->modifiers() != Qt::NoModifier || kev->text().isEmpty() || kev->key() == Qt::Key_Delete) { return QObject::eventFilter(obj, ev); } if (kev->key() == Qt::Key_Backspace) { - _menuSearch.chop(1); - if (_menuSearch.length() == 0) { - resetShortcuts(); + auto newSearchText = _quickSearchText(); + newSearchText.chop(1); + _setQuickSearchText(newSearchText); + + if (_quickSearchText().length() == 0) { return QObject::eventFilter(obj, ev); } } else { - _menuSearch.append(kev->text()); + _setQuickSearchText(_quickSearchText().append(kev->text())); } QAction* found = nullptr; - const int menuSearchLength = _menuSearch.length(); + const int quickSearchTextLength = _quickSearchText().length(); bool solematch; for (auto act : acts) { if (act->isSeparator() || act->text() == "") { continue; } - if (menuSearchLength == 1 && kev->key() != Qt::Key_Backspace && !kev->text().isEmpty()) { + if (quickSearchTextLength == 1 && kev->key() != Qt::Key_Backspace && !kev->text().isEmpty()) { if (act->text().contains('&' + kev->text(), Qt::CaseInsensitive)) { - _menuSearch = ""; - resetShortcuts(); + _setQuickSearchText(""); break; } _msNamesWithAccelerators.insert(act, act->text()); } act->setText(KLocalizedString::removeAcceleratorMarker(act->text())); - if (act->text().left(menuSearchLength).compare(_menuSearch, Qt::CaseInsensitive) == 0) { - act->setText(createShortcutUnderline(act->text(), menuSearchLength)); + if (act->text().left(quickSearchTextLength).compare(_quickSearchText(), Qt::CaseInsensitive) == 0) { + act->setText(createShortcutUnderline(act->text(), quickSearchTextLength)); if (!found) { found = act; solematch = true; } else { solematch = false; } } } if (found && solematch) { if ((bool) found->menu()) { menu->setActiveAction(found); } else { found->activate(QAction::Trigger); } - _menuSearch = ""; - resetShortcuts(); - } else if (found && menuSearchLength > 1) { + _setQuickSearchText(""); + } else if (found && quickSearchTextLength > 1) { // & bookmark code will give focus as long as there is only one // & character menu->setActiveAction(found); - } else if (!found) { - _menuSearch = ""; - resetShortcuts(); } } if (ev->type() == QEvent::MouseButtonRelease) { switch (static_cast(ev)->button()) { case Qt::RightButton: _middleClick = false; if (obj->inherits("QMenu")) { QMenu *menu = static_cast(obj); QAction *act = menu->actionAt(static_cast(ev)->pos()); if (obj == _mainBookmarkPopup && _specialBookmarks.contains(act)) { rightClickOnSpecialBookmark(); return true; } KrBookmark *bm = dynamic_cast(act); if (bm != 0) { rightClicked(menu, bm); return true; } else if (act && act->data().canConvert()) { KrBookmark *bm = act->data().value(); rightClicked(menu, bm); } } break; case Qt::LeftButton: _middleClick = false; break; case Qt::MidButton: _middleClick = true; break; default: break; } } return QObject::eventFilter(obj, ev); } void KrBookmarkHandler::resetShortcuts() { QHash::const_iterator i = _msNamesWithAccelerators.begin(); for (; i != _msNamesWithAccelerators.end(); ++i) { if (i.key()) { i.key()->setText(i.value()); } } _msNamesWithAccelerators.clear(); } QString KrBookmarkHandler::createShortcutUnderline(const QString &text, int underlineEnd) { if (underlineEnd <= 0) { return text; } if (underlineEnd > text.length()) { underlineEnd = text.length(); } QString underlinedtext = text; for (int i = 0; i < underlineEnd; i++) { if (underlinedtext[2 * i] != '&') { underlinedtext.insert(2 * i, '&'); } } return underlinedtext; } #define POPULAR_URLS_ID 100100 #define TRASH_ID 100101 #define LAN_ID 100103 #define VIRTUAL_FS_ID 100102 #define JUMP_BACK_ID 100104 void KrBookmarkHandler::rightClickOnSpecialBookmark() { KConfigGroup group(krConfig, "Private"); bool hasPopularURLs = group.readEntry("BM Popular URLs", true); bool hasTrash = group.readEntry("BM Trash", true); bool hasLan = group.readEntry("BM Lan", true); bool hasVirtualFS = group.readEntry("BM Virtual FS", true); bool hasJumpback = group.readEntry("BM Jumpback", true); QMenu menu(_mainBookmarkPopup); menu.setTitle(i18n("Enable special bookmarks")); QAction *act; act = menu.addAction(i18n("Popular URLs")); act->setData(QVariant(POPULAR_URLS_ID)); act->setCheckable(true); act->setChecked(hasPopularURLs); act = menu.addAction(i18n("Trash bin")); act->setData(QVariant(TRASH_ID)); act->setCheckable(true); act->setChecked(hasTrash); act = menu.addAction(i18n("Local Network")); act->setData(QVariant(LAN_ID)); act->setCheckable(true); act->setChecked(hasLan); act = menu.addAction(i18n("Virtual Filesystem")); act->setData(QVariant(VIRTUAL_FS_ID)); act->setCheckable(true); act->setChecked(hasVirtualFS); act = menu.addAction(i18n("Jump back")); act->setData(QVariant(JUMP_BACK_ID)); act->setCheckable(true); act->setChecked(hasJumpback); connect(_mainBookmarkPopup, SIGNAL(highlighted(int)), &menu, SLOT(close())); connect(_mainBookmarkPopup, SIGNAL(activated(int)), &menu, SLOT(close())); int result = -1; QAction *res = menu.exec(QCursor::pos()); if (res && res->data().canConvert()) result = res->data().toInt(); bool doCloseMain = true; switch (result) { case POPULAR_URLS_ID: group.writeEntry("BM Popular URLs", !hasPopularURLs); break; case TRASH_ID: group.writeEntry("BM Trash", !hasTrash); break; case LAN_ID: group.writeEntry("BM Lan", !hasLan); break; case VIRTUAL_FS_ID: group.writeEntry("BM Virtual FS", !hasVirtualFS); break; case JUMP_BACK_ID: group.writeEntry("BM Jumpback", !hasJumpback); break; default: doCloseMain = false; break; } menu.close(); if (doCloseMain && _mainBookmarkPopup) _mainBookmarkPopup->close(); } #define OPEN_ID 100200 #define OPEN_NEW_TAB_ID 100201 #define DELETE_ID 100202 void KrBookmarkHandler::rightClicked(QMenu *menu, KrBookmark * bm) { QMenu popup(_mainBookmarkPopup); QAction * act; if (!bm->isFolder()) { act = popup.addAction(krLoader->loadIcon("document-open", KIconLoader::Panel), i18n("Open")); act->setData(QVariant(OPEN_ID)); act = popup.addAction(krLoader->loadIcon("tab-new", KIconLoader::Panel), i18n("Open in a new tab")); act->setData(QVariant(OPEN_NEW_TAB_ID)); popup.addSeparator(); } act = popup.addAction(krLoader->loadIcon("edit-delete", KIconLoader::Panel), i18n("Delete")); act->setData(QVariant(DELETE_ID)); connect(menu, SIGNAL(highlighted(int)), &popup, SLOT(close())); connect(menu, SIGNAL(activated(int)), &popup, SLOT(close())); int result = -1; QAction *res = popup.exec(QCursor::pos()); if (res && res->data().canConvert ()) result = res->data().toInt(); popup.close(); if (_mainBookmarkPopup && result >= OPEN_ID && result <= DELETE_ID) { _mainBookmarkPopup->close(); } switch (result) { case OPEN_ID: SLOTS->refresh(bm->url()); break; case OPEN_NEW_TAB_ID: _mainWindow->activeManager()->newTab(bm->url()); break; case DELETE_ID: deleteBookmark(bm); break; } } // used to monitor middle clicks. if mid is found, then the // bookmark is opened in a new tab. ugly, but easier than overloading // KAction and KActionCollection. void KrBookmarkHandler::slotActivated(const QUrl &url) { if (_mainBookmarkPopup && !_mainBookmarkPopup->isHidden()) _mainBookmarkPopup->close(); if (_middleClick) _mainWindow->activeManager()->newTab(url); else SLOTS->refresh(url); } diff --git a/krusader/BookMan/krbookmarkhandler.h b/krusader/BookMan/krbookmarkhandler.h index 013f7006..f4e89012 100644 --- a/krusader/BookMan/krbookmarkhandler.h +++ b/krusader/BookMan/krbookmarkhandler.h @@ -1,91 +1,98 @@ /***************************************************************************** * Copyright (C) 2002 Shie Erlich * * Copyright (C) 2002 Rafi Yanai * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This package is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this package; if not, write to the Free Software * * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * *****************************************************************************/ #ifndef KRBOOKMARKHANDLER_H #define KRBOOKMARKHANDLER_H // QtCore #include #include #include #include #include // QtXml #include // QtWidgets #include +#include +#include #include "krbookmark.h" class KActionCollection; class KBookmarkManager; class KrMainWindow; class KrBookmarkHandler: public QObject { Q_OBJECT friend class KrAddBookmarkDlg; enum Actions { BookmarkCurrent = 0, ManageBookmarks }; public: explicit KrBookmarkHandler(KrMainWindow *mainWindow); ~KrBookmarkHandler(); void populate(QMenu *menu); void addBookmark(KrBookmark *bm, KrBookmark *parent = 0); void bookmarkCurrent(QUrl url); protected: void deleteBookmark(KrBookmark *bm); void importFromFile(); bool importFromFileBookmark(QDomElement &e, KrBookmark *parent, QString path, QString *errorMsg); bool importFromFileFolder(QDomNode &first, KrBookmark *parent, QString path, QString *errorMsg); void exportToFile(); void exportToFileFolder(QDomDocument &doc, QDomElement &parent, KrBookmark *folder); void exportToFileBookmark(QDomDocument &doc, QDomElement &where, KrBookmark *bm); void clearBookmarks(KrBookmark *root); - void buildMenu(KrBookmark *parent, QMenu *menu); + void buildMenu(KrBookmark *parent, QMenu *menu, int depth = 0); bool eventFilter(QObject *obj, QEvent *ev); QString createShortcutUnderline(const QString &, int underlineEnd); void resetShortcuts(); void rightClicked(QMenu *menu, KrBookmark *bm); void rightClickOnSpecialBookmark(); void removeReferences(KrBookmark *root, KrBookmark *bmToRemove); protected slots: void bookmarksChanged(const QString&, const QString&); void slotActivated(const QUrl &url); private: KrMainWindow *_mainWindow; KActionCollection *_collection, *_privateCollection; KrBookmark *_root; // the whole KBookmarkManager is an ugly hack. use it until we have our own KBookmarkManager *manager; bool _middleClick; // if true, the user clicked the middle button to open the bookmark QPointer _mainBookmarkPopup; // main bookmark popup menu QList _specialBookmarks; // the action list of the special bookmarks - QString _menuSearch; // Search as you type string for the bookmark menu QHash _msNamesWithAccelerators; // List of changed shortcuts and their old values + + QWidgetAction *_quickSearchAction; + QLineEdit *_quickSearchBar; + + void _setQuickSearchText(const QString &text); + QString _quickSearchText() const; }; Q_DECLARE_METATYPE(KrBookmark *) #endif // KRBOOKMARK_HANDLER_H diff --git a/krusader/krusader.cpp b/krusader/krusader.cpp index 71297c7b..b9ffb400 100644 --- a/krusader/krusader.cpp +++ b/krusader/krusader.cpp @@ -1,653 +1,654 @@ /*************************************************************************** krusader.cpp ------------------- copyright : (C) 2000 by Shie Erlich & Rafi Yanai e-mail : krusader@users.sourceforge.net web site : http://krusader.sourceforge.net --------------------------------------------------------------------------- Description *************************************************************************** A db dD d8888b. db db .d8888. .d8b. d8888b. d88888b d8888b. 88 ,8P' 88 `8D 88 88 88' YP d8' `8b 88 `8D 88' 88 `8D 88,8P 88oobY' 88 88 `8bo. 88ooo88 88 88 88ooooo 88oobY' 88`8b 88`8b 88 88 `Y8b. 88~~~88 88 88 88~~~~~ 88`8b 88 `88. 88 `88. 88b d88 db 8D 88 88 88 .8D 88. 88 `88. YP YD 88 YD ~Y8888P' `8888Y' YP YP Y8888D' Y88888P 88 YD S o u r c e F i l e *************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "krusader.h" // QtCore #include #include #include #include // QtGui #include #include // QtWidgets #include #include #include // QtDBus #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "defaults.h" #include "kicons.h" #include "kractions.h" #include "krglobal.h" #include "krservices.h" #include "krslots.h" #include "krtrashhandler.h" #include "krusaderversion.h" #include "krusaderview.h" #include "panelmanager.h" #include "tabactions.h" #include "BookMan/krbookmarkhandler.h" #include "Dialogs/checksumdlg.h" #include "Dialogs/krpleasewait.h" #include "Dialogs/popularurls.h" #include "FileSystem/fileitem.h" #include "FileSystem/krpermhandler.h" #include "GUI/kcmdline.h" #include "GUI/kfnkeys.h" #include "GUI/krremoteencodingmenu.h" #include "GUI/krusaderstatus.h" #include "GUI/terminaldock.h" #include "JobMan/jobman.h" #include "KViewer/krviewer.h" #include "Konfigurator/kgprotocols.h" #include "MountMan/kmountman.h" #include "Panel/PanelView/krview.h" #include "Panel/PanelView/krviewfactory.h" #include "Panel/krcolorcache.h" #include "Panel/krpanel.h" #include "Panel/listpanelactions.h" #include "Panel/viewactions.h" #include "UserAction/expander.h" // This makes gcc-4.1 happy. Warning about possible problem with KrAction's dtor not called #include "UserAction/kraction.h" #include "UserAction/useraction.h" #ifdef __KJSEMBED__ #include "KrJS/krjs.h" #endif // define the static members Krusader *Krusader::App = 0; QString Krusader::AppName; // KrBookmarkHandler *Krusader::bookman = 0; //QTextOStream *Krusader::_krOut = QTextOStream(::stdout); #ifdef __KJSEMBED__ KrJS *Krusader::js = 0; QAction *Krusader::actShowJSConsole = 0; #endif // construct the views, statusbar and menu bars and prepare Krusader to start Krusader::Krusader(const QCommandLineParser &parser) : KParts::MainWindow(0, Qt::Window | Qt::WindowTitleHint | Qt::WindowContextHelpButtonHint), _listPanelActions(0), isStarting(true), isExiting(false), _quit(false) { // create the "krusader" App = this; krMainWindow = this; SLOTS = new KRslots(this); setXMLFile("krusaderui.rc"); // kpart-related xml file plzWait = new KRPleaseWaitHandler(this); const bool runKonfig = versionControl(); QString message; switch (krConfig->accessMode()) { case KConfigBase::NoAccess : message = "Krusader's configuration file can't be found. Default values will be used."; break; case KConfigBase::ReadOnly : message = "Krusader's configuration file is in READ ONLY mode (why is that!?) Changed values will not be saved"; break; case KConfigBase::ReadWrite : message = ""; break; } if (!message.isEmpty()) { KMessageBox::error(krApp, message); } // create an icon loader krLoader = KIconLoader::global(); // iconLoader->addExtraDesktopThemes(); // create MountMan KrGlobal::mountMan = new KMountMan(this); connect(KrGlobal::mountMan, SIGNAL(refreshPanel(QUrl)), SLOTS, SLOT(refresh(QUrl))); + // create popular URLs container + _popularUrls = new PopularUrls(this); + // create bookman krBookMan = new KrBookmarkHandler(this); // create job manager krJobMan = new JobMan(this); - _popularUrls = new PopularUrls(this); - // create the main view MAIN_VIEW = new KrusaderView(this); // setup all the krusader's actions setupActions(); // init the permmision handler class KRpermHandler::init(); // init the protocol handler KgProtocols::init(); const KConfigGroup lookFeelGroup(krConfig, "Look&Feel"); FileItem::loadUserDefinedFolderIcons(lookFeelGroup.readEntry("Load User Defined Folder Icons", _UserDefinedFolderIcons)); const KConfigGroup startupGroup(krConfig, "Startup"); QString startProfile = startupGroup.readEntry("Starter Profile Name", QString()); QList leftTabs; QList rightTabs; // get command-line arguments if (parser.isSet("left")) { leftTabs = KrServices::toUrlList(parser.value("left").split(',')); startProfile.clear(); } if (parser.isSet("right")) { rightTabs = KrServices::toUrlList(parser.value("right").split(',')); startProfile.clear(); } if (parser.isSet("profile")) startProfile = parser.value("profile"); if (!startProfile.isEmpty()) { leftTabs.clear(); rightTabs.clear(); } // starting the panels MAIN_VIEW->start(startupGroup, startProfile.isEmpty(), leftTabs, rightTabs); // create a status bar KrusaderStatus *status = new KrusaderStatus(this); setStatusBar(status); status->setWhatsThis(i18n("Statusbar will show basic information " "about file below mouse pointer.")); // create tray icon (if needed) const bool startToTray = startupGroup.readEntry("Start To Tray", _StartToTray); setTray(startToTray); setCentralWidget(MAIN_VIEW); // manage our keyboard short-cuts //KAcceleratorManager::manage(this,true); setCursor(Qt::ArrowCursor); if (! startProfile.isEmpty()) MAIN_VIEW->profiles(startProfile); // restore gui settings { // now, check if we need to create a konsole_part // call the XML GUI function to draw the UI createGUI(MAIN_VIEW->terminalDock()->part()); // this needs to be called AFTER createGUI() !!! updateUserActions(); _listPanelActions->guiUpdated(); // not using this. See savePosition() //applyMainWindowSettings(); const KConfigGroup cfgToolbar(krConfig, "Main Toolbar"); toolBar()->applySettings(cfgToolbar); const KConfigGroup cfgJobBar(krConfig, "Job Toolbar"); toolBar("jobToolBar")->applySettings(cfgJobBar); const KConfigGroup cfgActionsBar(krConfig, "Actions Toolbar"); toolBar("actionsToolBar")->applySettings(cfgActionsBar); // restore toolbars position and visibility restoreState(startupGroup.readEntry("State", QByteArray())); statusBar()->setVisible(startupGroup.readEntry("Show status bar", _ShowStatusBar)); MAIN_VIEW->updateGUI(startupGroup); // popular urls _popularUrls->load(); } if (runKonfig) SLOTS->runKonfigurator(true); KConfigGroup viewerModuleGrp(krConfig, "ViewerModule"); if (viewerModuleGrp.readEntry("FirstRun", true)) { KrViewer::configureDeps(); viewerModuleGrp.writeEntry("FirstRun", false); } if (!runKonfig) { KConfigGroup cfg(krConfig, "Private"); move(cfg.readEntry("Start Position", _StartPosition)); resize(cfg.readEntry("Start Size", _StartSize)); } // view initialized; show window or only tray if (!startToTray) { show(); } KrTrashHandler::startWatcher(); isStarting = false; //HACK - used by [ListerTextArea|KrSearchDialog|LocateDlg]:keyPressEvent() KrGlobal::copyShortcut = _listPanelActions->actCopy->shortcut(); //HACK: make sure the active view becomes focused // for some reason sometimes the active view cannot be focused immediately at this point, // so queue it for the main loop QTimer::singleShot(0, ACTIVE_PANEL->view->widget(), SLOT(setFocus())); _openUrlTimer.setSingleShot(true); connect(&_openUrlTimer, SIGNAL(timeout()), SLOT(doOpenUrl())); KStartupInfo *startupInfo = new KStartupInfo(0, this); connect(startupInfo, &KStartupInfo::gotNewStartup, this, &Krusader::slotGotNewStartup); connect(startupInfo, &KStartupInfo::gotRemoveStartup, this, &Krusader::slotGotRemoveStartup); } Krusader::~Krusader() { KrTrashHandler::stopWatcher(); if (!isExiting) // save the settings if it was not saved (SIGTERM received) saveSettings(); delete MAIN_VIEW; MAIN_VIEW = 0; App = 0; } void Krusader::setTray(bool forceCreation) { const bool trayIsNeeded = forceCreation || KConfigGroup(krConfig, "Look&Feel") .readEntry("Minimize To Tray", _ShowTrayIcon); if (!sysTray && trayIsNeeded) { sysTray = new KStatusNotifierItem(this); sysTray->setIconByName(privIcon()); // we have our own "quit" method, re-connect QAction *quitAction = sysTray->action(QStringLiteral("quit")); if (quitAction) { disconnect(quitAction, &QAction::triggered, nullptr, nullptr); connect(quitAction, &QAction::triggered, this, &Krusader::quit); } } else if (sysTray && !trayIsNeeded) { // user does not want tray anymore :( sysTray->deleteLater(); } } bool Krusader::versionControl() { // create config file krConfig = KSharedConfig::openConfig().data(); KConfigGroup nogroup(krConfig, QString()); const bool firstRun = nogroup.readEntry("First Time", true); KrGlobal::sCurrentConfigVersion = nogroup.readEntry("Config Version", -1); // first installation of krusader if (firstRun) { KMessageBox::information( krApp, i18n("Welcome to Krusader.

As this is your first run, your machine " "will now be checked for external applications. Then the Konfigurator will " "be launched where you can customize Krusader to your needs.

")); } nogroup.writeEntry("Version", VERSION); nogroup.writeEntry("First Time", false); krConfig->sync(); QDir().mkpath(QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/krusader/")); return firstRun; } void Krusader::statusBarUpdate(const QString& mess) { // change the message on the statusbar for 5 seconds if (statusBar()->isVisible()) statusBar()->showMessage(mess, 5000); } bool Krusader::event(QEvent *e) { if(e->type() == QEvent::ApplicationPaletteChange) { KrColorCache::getColorCache().refreshColors(); } return KParts::MainWindow::event(e); } // Moving from Pixmap actions to generic filenames - thanks to Carsten Pfeiffer void Krusader::setupActions() { QAction *bringToTopAct = new QAction(i18n("Bring Main Window to Top"), this); actionCollection()->addAction("bring_main_window_to_top", bringToTopAct); connect(bringToTopAct, SIGNAL(triggered()), SLOT(moveToTop())); KrActions::setupActions(this); _krActions = new KrActions(this); _viewActions = new ViewActions(this, this); _listPanelActions = new ListPanelActions(this, this); _tabActions = new TabActions(this, this); } /////////////////////////////////////////////////////////////////////////// //////////////////// implementation of slots ////////////////////////////// /////////////////////////////////////////////////////////////////////////// void Krusader::savePosition() { KConfigGroup cfg(krConfig, "Private"); cfg.writeEntry("Start Position", pos()); cfg.writeEntry("Start Size", size()); cfg = krConfig->group("Startup"); MAIN_VIEW->saveSettings(cfg); // NOTE: this would save current window state/size, statusbar and settings for each toolbar. // We are not using this and saving everything manually because // - it does not save window position // - window size save/restore does sometimes not work (multi-monitor setup) // - saving the statusbar visibility should be independent from window position and restoring it // does not work properly. //KConfigGroup cfg = KConfigGroup(&cfg, "MainWindowSettings"); //saveMainWindowSettings(cfg); //statusBar()->setVisible(cfg.readEntry("StatusBar", "Enabled") != "Disabled"); krConfig->sync(); } void Krusader::saveSettings() { // workaround: revert terminal fullscreen mode before saving widget and toolbar visibility if (MAIN_VIEW->isTerminalEmulatorFullscreen()) { MAIN_VIEW->setTerminalEmulator(false, true); } KConfigGroup noGroup(krConfig, QString()); noGroup.writeEntry("Config Version", KrGlobal::sConfigVersion); // save toolbar settings KConfigGroup cfg(krConfig, "Main Toolbar"); toolBar()->saveSettings(cfg); cfg = krConfig->group("Job Toolbar"); toolBar("jobToolBar")->saveSettings(cfg); cfg = krConfig->group("Actions Toolbar"); toolBar("actionsToolBar")->saveSettings(cfg); cfg = krConfig->group("Startup"); // save toolbar visibility and position cfg.writeEntry("State", saveState()); cfg.writeEntry("Show status bar", statusBar()->isVisible()); // save panel and window settings if (cfg.readEntry("Remember Position", _RememberPos)) savePosition(); // save the gui components visibility if (cfg.readEntry("UI Save Settings", _UiSave)) { cfg.writeEntry("Show FN Keys", KrActions::actToggleFnkeys->isChecked()); cfg.writeEntry("Show Cmd Line", KrActions::actToggleCmdline->isChecked()); cfg.writeEntry("Show Terminal Emulator", KrActions::actToggleTerminal->isChecked()); } // save popular links _popularUrls->save(); krConfig->sync(); } void Krusader::closeEvent(QCloseEvent *event) { if (!sysTray || _quit) { _quit = false; // in case quit will be aborted KParts::MainWindow::closeEvent(event); // (may) quit, continues with queryClose()... } else { // close window to tray event->ignore(); hide(); } } void Krusader::showEvent(QShowEvent *event) { const KConfigGroup lookFeelGroup(krConfig, "Look&Feel"); if (sysTray && !lookFeelGroup.readEntry("Minimize To Tray", _ShowTrayIcon)) { // restoring from "start to tray", tray icon is not needed anymore sysTray->deleteLater(); } KParts::MainWindow::showEvent(event); } bool Krusader::queryClose() { if (isStarting || isExiting) return false; if (qApp->isSavingSession()) { // KDE is logging out, accept the close acceptClose(); return true; } const KConfigGroup cfg = krConfig->group("Look&Feel"); const bool confirmExit = cfg.readEntry("Warn On Exit", _WarnOnExit); // ask user and wait until all KIO::job operations are terminated. Krusader won't exit before // that anyway if (!krJobMan->waitForJobs(confirmExit)) return false; /* First try to close the child windows, because it's the safer way to avoid crashes, then close the main window. If closing a child is not successful, then we cannot let the main window close. */ for (;;) { QWidgetList list = QApplication::topLevelWidgets(); QWidget *activeModal = QApplication::activeModalWidget(); QWidget *w = list.at(0); if (activeModal && activeModal != this && activeModal != menuBar() && list.contains(activeModal) && !activeModal->isHidden()) { w = activeModal; } else { int i = 1; for (; i < list.count(); ++i) { w = list.at(i); if (!(w && (w == this || w->isHidden() || w == menuBar()))) break; } if (i == list.count()) w = 0; } if (!w) break; if (!w->close()) { if (w->inherits("QDialog")) { fprintf(stderr, "Failed to close: %s\n", w->metaObject()->className()); } return false; } } acceptClose(); return true; } void Krusader::acceptClose() { saveSettings(); emit shutdown(); // Removes the DBUS registration of the application. Single instance mode requires unique appid. // As Krusader is exiting, we release that unique appid, so new Krusader instances // can be started. QDBusConnection dbus = QDBusConnection::sessionBus(); dbus.unregisterObject("/Instances/" + Krusader::AppName); isExiting = true; } // the please wait dialog functions void Krusader::startWaiting(QString msg, int count , bool cancel) { plzWait->startWaiting(msg , count, cancel); } bool Krusader::wasWaitingCancelled() const { return plzWait->wasCancelled(); } void Krusader::stopWait() { plzWait->stopWait(); } void Krusader::updateUserActions() { KActionMenu *userActionMenu = (KActionMenu *) KrActions::actUserMenu; if (userActionMenu) { userActionMenu->menu()->clear(); userActionMenu->addAction(KrActions::actManageUseractions); userActionMenu->addSeparator(); krUserAction->populateMenu(userActionMenu, NULL); } } const char* Krusader::privIcon() { if (geteuid()) return "krusader_user"; else return "krusader_root"; } void Krusader::quit() { _quit = true; // remember that we want to quit and not close to tray close(); // continues with closeEvent()... } void Krusader::moveToTop() { if (isHidden()) show(); KWindowSystem::forceActiveWindow(winId()); } bool Krusader::isRunning() { moveToTop(); //FIXME - doesn't belong here return true; } bool Krusader::isLeftActive() { return MAIN_VIEW->isLeftActive(); } bool Krusader::openUrl(QString url) { _urlToOpen = url; _openUrlTimer.start(0); return true; } void Krusader::doOpenUrl() { QUrl url = QUrl::fromUserInput(_urlToOpen, QDir::currentPath(), QUrl::AssumeLocalFile); _urlToOpen.clear(); int tab = ACTIVE_MNG->findTab(url); if(tab >= 0) ACTIVE_MNG->setActiveTab(tab); else if((tab = OTHER_MNG->findTab(url)) >= 0) { OTHER_MNG->setActiveTab(tab); OTHER_MNG->currentPanel()->view->widget()->setFocus(); } else ACTIVE_MNG->slotNewTab(url); } void Krusader::slotGotNewStartup(const KStartupInfoId &id, const KStartupInfoData &data) { Q_UNUSED(id) Q_UNUSED(data) // This is here to show busy mouse cursor when _other_ applications are launched, not for krusader itself. qApp->setOverrideCursor(Qt::BusyCursor); } void Krusader::slotGotRemoveStartup(const KStartupInfoId &id, const KStartupInfoData &data) { Q_UNUSED(id) Q_UNUSED(data) qApp->restoreOverrideCursor(); } KrView *Krusader::activeView() { return ACTIVE_PANEL->view; } AbstractPanelManager *Krusader::activeManager() { return MAIN_VIEW->activeManager(); } AbstractPanelManager *Krusader::leftManager() { return MAIN_VIEW->leftManager(); } AbstractPanelManager *Krusader::rightManager() { return MAIN_VIEW->rightManager(); }