diff --git a/applets/notifications/plugin/thumbnailer.cpp b/applets/notifications/plugin/thumbnailer.cpp index b913066d1..ed66ab76a 100644 --- a/applets/notifications/plugin/thumbnailer.cpp +++ b/applets/notifications/plugin/thumbnailer.cpp @@ -1,241 +1,242 @@ /* Copyright (C) 2016 Kai Uwe Broulik This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "thumbnailer.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Thumbnailer::Thumbnailer(QObject *parent) : QObject(parent) { } Thumbnailer::~Thumbnailer() = default; void Thumbnailer::classBegin() { } void Thumbnailer::componentComplete() { m_inited = true; generatePreview(); } QUrl Thumbnailer::url() const { return m_url; } void Thumbnailer::setUrl(const QUrl &url) { if (m_url != url) { m_url = url; emit urlChanged(); generatePreview(); } } QSize Thumbnailer::size() const { return m_size; } void Thumbnailer::setSize(const QSize &size) { if (m_size != size) { m_size = size; emit sizeChanged(); generatePreview(); } } bool Thumbnailer::hasPreview() const { return !m_pixmap.isNull(); } QPixmap Thumbnailer::pixmap() const { return m_pixmap; } QSize Thumbnailer::pixmapSize() const { return m_pixmap.size(); } QString Thumbnailer::iconName() const { return m_iconName; } bool Thumbnailer::menuVisible() const { return m_menuVisible; } void Thumbnailer::showContextMenu(int x, int y, const QString &path, QQuickItem *ctx) { if (!ctx || !ctx->window()) { return; } const QUrl url(path); if (!url.isValid()) { return; } KFileItem fileItem(url); QMenu *menu = new QMenu(); menu->setAttribute(Qt::WA_DeleteOnClose, true); connect(menu, &QMenu::aboutToHide, this, [this] { m_menuVisible = false; emit menuVisibleChanged(); }); if (KProtocolManager::supportsListing(url)) { QAction *openContainingFolderAction = menu->addAction(QIcon::fromTheme(QStringLiteral("folder-open")), i18n("Open Containing Folder")); connect(openContainingFolderAction, &QAction::triggered, [url] { KIO::highlightInFileManager({url}); }); } menu->addSeparator(); // KStandardAction? But then the Ctrl+C shortcut makes no sense in this context QAction *copyAction = menu->addAction(QIcon::fromTheme(QStringLiteral("edit-copy")), i18n("&Copy")); connect(copyAction, &QAction::triggered, [fileItem] { // inspired by KDirModel::mimeData() QMimeData *data = new QMimeData(); // who cleans it up? KUrlMimeData::setUrls({fileItem.url()}, {fileItem.mostLocalUrl()}, data); QApplication::clipboard()->setMimeData(data); }); KFileItemActions *actions = new KFileItemActions(menu); KFileItemListProperties itemProperties(KFileItemList({fileItem})); actions->setItemListProperties(itemProperties); actions->addOpenWithActionsTo(menu); actions->addServiceActionsTo(menu); actions->addPluginActionsTo(menu); QAction *propertiesAction = menu->addAction(QIcon::fromTheme(QStringLiteral("document-properties")), i18n("Properties")); connect(propertiesAction, &QAction::triggered, [fileItem] { KPropertiesDialog *dialog = new KPropertiesDialog(fileItem.url()); dialog->setAttribute(Qt::WA_DeleteOnClose); dialog->show(); }); //this is a workaround where Qt will fail to realise a mouse has been released // this happens if a window which does not accept focus spawns a new window that takes focus and X grab // whilst the mouse is depressed // https://bugreports.qt.io/browse/QTBUG-59044 // this causes the next click to go missing //by releasing manually we avoid that situation auto ungrabMouseHack = [ctx]() { if (ctx->window()->mouseGrabberItem()) { ctx->window()->mouseGrabberItem()->ungrabMouse(); } }; QTimer::singleShot(0, ctx, ungrabMouseHack); //end workaround QPoint pos; if (x == -1 && y == -1) { // align "bottom left of ctx" menu->adjustSize(); pos = ctx->mapToGlobal(QPointF(0, ctx->height())).toPoint(); if (!qApp->isRightToLeft()) { pos.rx() += ctx->width(); pos.rx() -= menu->width(); } } else { pos = ctx->mapToGlobal(QPointF(x, y)).toPoint(); } menu->popup(pos); m_menuVisible = true; emit menuVisibleChanged(); } void Thumbnailer::generatePreview() { if (!m_inited) { return; } if (!m_url.isValid() || !m_url.isLocalFile() || !m_size.isValid()) { return; } auto maxSize = qMax(m_size.width(), m_size.height()); KIO::PreviewJob *job = KIO::filePreview(KFileItemList({KFileItem(m_url)}), QSize(maxSize,maxSize)); + job->setScaleType(KIO::PreviewJob::Scaled); job->setIgnoreMaximumSize(true); connect(job, &KIO::PreviewJob::gotPreview, this, [this](const KFileItem &item, const QPixmap &preview) { Q_UNUSED(item); m_pixmap = preview; emit pixmapChanged(); if (!m_iconName.isEmpty()) { m_iconName.clear(); emit iconNameChanged(); } }); connect(job, &KIO::PreviewJob::failed, this, [this](const KFileItem &item) { m_pixmap = QPixmap(); emit pixmapChanged(); const QString &iconName = item.determineMimeType().iconName(); if (m_iconName != iconName) { m_iconName = iconName; emit iconNameChanged(); } }); job->start(); } diff --git a/krunner/view.cpp b/krunner/view.cpp index 92f4bc685..b1e3b0aca 100644 --- a/krunner/view.cpp +++ b/krunner/view.cpp @@ -1,451 +1,392 @@ /* * Copyright 2014 Marco Martin * * 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 program 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "view.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "appadaptor.h" View::View(QWindow *) : PlasmaQuick::Dialog(), m_offset(.5), - m_floating(false), - m_plasmaShell(nullptr) + m_floating(false) { - initWayland(); setClearBeforeRendering(true); setColor(QColor(Qt::transparent)); setFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint); KCrash::setFlags(KCrash::AutoRestart); //used only by screen readers setTitle(i18n("KRunner")); m_config = KConfigGroup(KSharedConfig::openConfig(QStringLiteral("krunnerrc")), "General"); setFreeFloating(m_config.readEntry("FreeFloating", false)); reloadConfig(); new AppAdaptor(this); QDBusConnection::sessionBus().registerObject(QStringLiteral("/App"), this); QAction *a = new QAction(0); QObject::connect(a, &QAction::triggered, this, &View::displayOrHide); a->setText(i18n("Run Command")); a->setObjectName(QStringLiteral("run command")); a->setProperty("componentDisplayName", i18nc("Name for krunner shortcuts category", "Run Command")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << QKeySequence(Qt::ALT + Qt::Key_Space), KGlobalAccel::NoAutoloading); KGlobalAccel::self()->setShortcut(a, QList() << QKeySequence(Qt::ALT + Qt::Key_Space) << QKeySequence(Qt::ALT + Qt::Key_F2) << Qt::Key_Search); a = new QAction(0); QObject::connect(a, &QAction::triggered, this, &View::displayWithClipboardContents); a->setText(i18n("Run Command on clipboard contents")); a->setObjectName(QStringLiteral("run command on clipboard contents")); a->setProperty("componentDisplayName", i18nc("Name for krunner shortcuts category", "Run Command")); KGlobalAccel::self()->setDefaultShortcut(a, QList() << QKeySequence(Qt::ALT+Qt::SHIFT+Qt::Key_F2)); KGlobalAccel::self()->setShortcut(a, QList() << QKeySequence(Qt::ALT+Qt::SHIFT+Qt::Key_F2)); m_qmlObj = new KDeclarative::QmlObject(this); m_qmlObj->setInitializationDelayed(true); connect(m_qmlObj, &KDeclarative::QmlObject::finished, this, &View::objectIncubated); KPackage::Package package = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/LookAndFeel")); KConfigGroup cg(KSharedConfig::openConfig(QStringLiteral("kdeglobals")), "KDE"); const QString packageName = cg.readEntry("LookAndFeelPackage", QString()); if (!packageName.isEmpty()) { package.setPath(packageName); } m_qmlObj->setSource(package.fileUrl("runcommandmainscript")); m_qmlObj->engine()->rootContext()->setContextProperty(QStringLiteral("runnerWindow"), this); m_qmlObj->completeInitialization(); auto screenRemoved = [this](QScreen* screen) { if (screen == this->screen()) { setScreen(qGuiApp->primaryScreen()); hide(); } }; auto screenAdded = [this](QScreen* screen) { connect(screen, &QScreen::geometryChanged, this, &View::screenGeometryChanged); screenGeometryChanged(); }; foreach(QScreen* s, QGuiApplication::screens()) screenAdded(s); connect(qGuiApp, &QGuiApplication::screenAdded, this, screenAdded); connect(qGuiApp, &QGuiApplication::screenRemoved, this, screenRemoved); connect(KWindowSystem::self(), &KWindowSystem::workAreaChanged, this, &View::resetScreenPos); connect(this, &View::visibleChanged, this, &View::resetScreenPos); KDirWatch::self()->addFile(m_config.name()); // Catch both, direct changes to the config file ... connect(KDirWatch::self(), &KDirWatch::dirty, this, &View::reloadConfig); connect(KDirWatch::self(), &KDirWatch::created, this, &View::reloadConfig); if (m_floating) { setLocation(Plasma::Types::Floating); } else { setLocation(Plasma::Types::TopEdge); } connect(qGuiApp, &QGuiApplication::focusWindowChanged, this, &View::slotFocusWindowChanged); } View::~View() { } -void View::initWayland() -{ - if (!KWindowSystem::isPlatformWayland()) { - return; - } - using namespace KWayland::Client; - auto connection = ConnectionThread::fromApplication(this); - if (!connection) { - return; - } - Registry *registry = new Registry(this); - registry->create(connection); - QObject::connect(registry, &Registry::interfacesAnnounced, this, - [registry, this] { - const auto interface = registry->interface(Registry::Interface::PlasmaShell); - if (interface.name != 0) { - m_plasmaShell = registry->createPlasmaShell(interface.name, interface.version, this); - } - } - ); - - registry->setup(); - connection->roundtrip(); -} - void View::objectIncubated() { connect(m_qmlObj->rootObject(), SIGNAL(widthChanged()), this, SLOT(resetScreenPos())); setMainItem(qobject_cast(m_qmlObj->rootObject())); } void View::slotFocusWindowChanged() { if (!QGuiApplication::focusWindow()) { setVisible(false); } } bool View::freeFloating() const { return m_floating; } void View::setFreeFloating(bool floating) { if (m_floating == floating) { return; } m_floating = floating; if (m_floating) { setLocation(Plasma::Types::Floating); } else { setLocation(Plasma::Types::TopEdge); } positionOnScreen(); } void View::reloadConfig() { m_config.config()->reparseConfiguration(); setFreeFloating(m_config.readEntry("FreeFloating", false)); const QStringList history = m_config.readEntry("history", QStringList()); if (m_history != history) { m_history = history; emit historyChanged(); } } bool View::event(QEvent *event) { // QXcbWindow overwrites the state in its show event. There are plans // to fix this in 5.4, but till then we must explicitly overwrite it // each time. const bool retval = Dialog::event(event); bool setState = event->type() == QEvent::Show; if (event->type() == QEvent::PlatformSurface) { setState = (static_cast(event)->surfaceEventType() == QPlatformSurfaceEvent::SurfaceCreated); } if (setState) { KWindowSystem::setState(winId(), NET::SkipTaskbar | NET::SkipPager); } - - if (m_plasmaShell && event->type() == QEvent::Expose) { - using namespace KWayland::Client; - auto ee = static_cast(event); - - if (ee->region().isNull()) { - return retval; - } - - if (!m_plasmaShellSurface && isVisible()) { - Surface *s = Surface::fromWindow(this); - if (!s) { - return retval; - } - m_plasmaShellSurface = m_plasmaShell->createSurface(s, this); - m_plasmaShellSurface->setPanelBehavior(PlasmaShellSurface::PanelBehavior::WindowsGoBelow); - m_plasmaShellSurface->setPanelTakesFocus(true); - m_plasmaShellSurface->setRole(PlasmaShellSurface::Role::Panel); - //this should be on showEvent, but it was too soon so none of those had any effect - KWindowSystem::setOnAllDesktops(winId(), true); - positionOnScreen(); - requestActivate(); - //positionOnScreen tried to position it in the position it already had, so no moveevent happens and we need to manually posiyion the surface - m_plasmaShellSurface->setPosition(position()); - } - } else if (event->type() == QEvent::Hide) { - delete m_plasmaShellSurface; - } else if (m_plasmaShellSurface && event->type() == QEvent::Move) { - QMoveEvent *me = static_cast(event); - m_plasmaShellSurface->setPosition(me->pos()); - } - return retval; } void View::resizeEvent(QResizeEvent *event) { if (event->oldSize().width() != event->size().width()) { positionOnScreen(); } } void View::showEvent(QShowEvent *event) { KWindowSystem::setOnAllDesktops(winId(), true); Dialog::showEvent(event); positionOnScreen(); requestActivate(); } void View::screenGeometryChanged() { if (isVisible()) { positionOnScreen(); } } void View::resetScreenPos() { if (isVisible() && !m_floating) { positionOnScreen(); } } void View::positionOnScreen() { QScreen *shownOnScreen = QGuiApplication::primaryScreen(); Q_FOREACH (QScreen* screen, QGuiApplication::screens()) { if (screen->geometry().contains(QCursor::pos(screen))) { shownOnScreen = screen; break; } } setScreen(shownOnScreen); const QRect r = shownOnScreen->availableGeometry(); if (m_floating && !m_customPos.isNull()) { int x = qBound(r.left(), m_customPos.x(), r.right() - width()); int y = qBound(r.top(), m_customPos.y(), r.bottom() - height()); setPosition(x, y); show(); return; } const int w = width(); int x = r.left() + (r.width() * m_offset) - (w / 2); int y = r.top(); if (m_floating) { y += r.height() / 3; } x = qBound(r.left(), x, r.right() - width()); y = qBound(r.top(), y, r.bottom() - height()); setPosition(x, y); if (m_floating) { KWindowSystem::setOnDesktop(winId(), KWindowSystem::currentDesktop()); KWindowSystem::setType(winId(), NET::Normal); //Turn the sliding effect off KWindowEffects::slideWindow(winId(), KWindowEffects::NoEdge, 0); } else { KWindowSystem::setOnAllDesktops(winId(), true); KWindowEffects::slideWindow(winId(), KWindowEffects::TopEdge, 0); } KWindowSystem::forceActiveWindow(winId()); //qDebug() << "moving to" << m_screenPos[screen]; } void View::displayOrHide() { if (isVisible() && !QGuiApplication::focusWindow()) { KWindowSystem::forceActiveWindow(winId()); return; } setVisible(!isVisible()); } void View::display() { setVisible(true); } void View::displaySingleRunner(const QString &runnerName) { setVisible(true); m_qmlObj->rootObject()->setProperty("runner", runnerName); m_qmlObj->rootObject()->setProperty("query", QString()); } void View::displayWithClipboardContents() { setVisible(true); m_qmlObj->rootObject()->setProperty("runner", QString()); m_qmlObj->rootObject()->setProperty("query", QGuiApplication::clipboard()->text(QClipboard::Selection)); } void View::query(const QString &term) { setVisible(true); m_qmlObj->rootObject()->setProperty("runner", QString()); m_qmlObj->rootObject()->setProperty("query", term); } void View::querySingleRunner(const QString &runnerName, const QString &term) { setVisible(true); m_qmlObj->rootObject()->setProperty("runner", runnerName); m_qmlObj->rootObject()->setProperty("query", term); } void View::switchUser() { QDBusConnection::sessionBus().asyncCall( QDBusMessage::createMethodCall(QStringLiteral("org.kde.ksmserver"), QStringLiteral("/KSMServer"), QStringLiteral("org.kde.KSMServerInterface"), QStringLiteral("openSwitchUserDialog")) ); } void View::displayConfiguration() { QProcess::startDetached(QStringLiteral("kcmshell5"), QStringList() << QStringLiteral("plasmasearch")); } bool View::canConfigure() const { return KAuthorized::authorizeControlModule(QStringLiteral("kcm_plasmasearch.desktop")); } QStringList View::history() const { return m_history; } void View::addToHistory(const QString &item) { if (item == QLatin1String("SESSIONS")) { return; } if (!KAuthorized::authorize(QStringLiteral("lineedit_text_completion"))) { return; } m_history.removeOne(item); m_history.prepend(item); while (m_history.count() > 50) { // make configurable? m_history.removeLast(); } emit historyChanged(); writeHistory(); m_config.sync(); } void View::removeFromHistory(int index) { if (index < 0 || index >= m_history.count()) { return; } m_history.removeAt(index); emit historyChanged(); writeHistory(); } void View::writeHistory() { m_config.writeEntry("history", m_history); } diff --git a/krunner/view.h b/krunner/view.h index e23f4c221..996881e0c 100644 --- a/krunner/view.h +++ b/krunner/view.h @@ -1,107 +1,103 @@ /* * Copyright 2014 Marco Martin * * 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 program 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef VIEW_H #define VIEW_H #include #include #include #include #include #include namespace KDeclarative { class QmlObject; } namespace KWayland { namespace Client { class PlasmaShell; class PlasmaShellSurface; } } class ViewPrivate; class View : public PlasmaQuick::Dialog { Q_OBJECT Q_CLASSINFO("D-Bus Interface", "org.kde.krunner.App") Q_PROPERTY(bool canConfigure READ canConfigure CONSTANT) Q_PROPERTY(QStringList history READ history NOTIFY historyChanged) public: explicit View(QWindow *parent = 0); ~View() override; void positionOnScreen(); bool freeFloating() const; void setFreeFloating(bool floating); bool canConfigure() const; QStringList history() const; Q_INVOKABLE void addToHistory(const QString &item); Q_INVOKABLE void removeFromHistory(int index); Q_SIGNALS: void historyChanged(); protected: bool event(QEvent* event) override; void resizeEvent(QResizeEvent *event) override; void showEvent(QShowEvent *event) override; public Q_SLOTS: void display(); void displaySingleRunner(const QString &runnerName); void displayWithClipboardContents(); void query(const QString &term); void querySingleRunner(const QString &runnerName, const QString &term); void switchUser(); void displayConfiguration(); protected Q_SLOTS: void screenGeometryChanged(); void resetScreenPos(); void displayOrHide(); void reloadConfig(); void objectIncubated(); void slotFocusWindowChanged(); private: void writeHistory(); - void initWayland(); - QPoint m_customPos; KDeclarative::QmlObject *m_qmlObj; KConfigGroup m_config; qreal m_offset; bool m_floating : 1; QStringList m_history; - KWayland::Client::PlasmaShell *m_plasmaShell; - QPointer m_plasmaShellSurface; }; #endif // View_H