Changeset View
Changeset View
Standalone View
Standalone View
applets/notifications/filemenu.cpp
- This file was moved from applets/notifications/plugin/thumbnailer.cpp.
1 | /* | 1 | /* | ||
---|---|---|---|---|---|
2 | Copyright (C) 2016 Kai Uwe Broulik <kde@privat.broulik.de> | 2 | Copyright (C) 2016,2019 Kai Uwe Broulik <kde@privat.broulik.de> | ||
3 | 3 | | |||
4 | This library is free software; you can redistribute it and/or | 4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | 5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | 6 | License as published by the Free Software Foundation; either | ||
7 | version 2.1 of the License, or (at your option) any later version. | 7 | version 2.1 of the License, or (at your option) any later version. | ||
8 | 8 | | |||
9 | This library is distributed in the hope that it will be useful, | 9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | 12 | Lesser General Public License for more details. | ||
13 | 13 | | |||
14 | You should have received a copy of the GNU Lesser General Public | 14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with this library; if not, write to the Free Software | 15 | License along with this library; if not, write to the Free Software | ||
16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | 16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
17 | */ | 17 | */ | ||
18 | 18 | | |||
19 | #include "thumbnailer.h" | 19 | #include "filemenu.h" | ||
20 | | ||||
21 | #include <KIO/PreviewJob> | | |||
22 | 20 | | |||
23 | #include <QApplication> | 21 | #include <QApplication> | ||
24 | #include <QClipboard> | 22 | #include <QClipboard> | ||
25 | #include <QIcon> | 23 | #include <QIcon> | ||
26 | #include <QMimeData> | 24 | #include <QMimeData> | ||
27 | #include <QMenu> | 25 | #include <QMenu> | ||
28 | #include <QQuickItem> | 26 | #include <QQuickItem> | ||
29 | #include <QTimer> | 27 | #include <QTimer> | ||
30 | #include <QQuickWindow> | 28 | #include <QQuickWindow> | ||
31 | 29 | | |||
32 | #include <KFileItemActions> | 30 | #include <KFileItemActions> | ||
33 | #include <KFileItemListProperties> | 31 | #include <KFileItemListProperties> | ||
34 | #include <KLocalizedString> | 32 | #include <KLocalizedString> | ||
35 | #include <KProtocolManager> | 33 | #include <KProtocolManager> | ||
36 | #include <KPropertiesDialog> | 34 | #include <KPropertiesDialog> | ||
37 | #include <KUrlMimeData> | 35 | #include <KUrlMimeData> | ||
38 | 36 | | |||
39 | #include <KIO/OpenFileManagerWindowJob> | 37 | #include <KIO/OpenFileManagerWindowJob> | ||
40 | 38 | | |||
41 | Thumbnailer::Thumbnailer(QObject *parent) : QObject(parent) | 39 | FileMenu::FileMenu(QObject *parent) : QObject(parent) | ||
42 | { | 40 | { | ||
43 | 41 | | |||
44 | } | 42 | } | ||
45 | 43 | | |||
46 | Thumbnailer::~Thumbnailer() = default; | 44 | FileMenu::~FileMenu() = default; | ||
47 | | ||||
48 | void Thumbnailer::classBegin() | | |||
49 | { | | |||
50 | | ||||
51 | } | | |||
52 | 45 | | |||
53 | void Thumbnailer::componentComplete() | 46 | QUrl FileMenu::url() const | ||
54 | { | | |||
55 | m_inited = true; | | |||
56 | generatePreview(); | | |||
57 | } | | |||
58 | | ||||
59 | QUrl Thumbnailer::url() const | | |||
60 | { | 47 | { | ||
61 | return m_url; | 48 | return m_url; | ||
62 | } | 49 | } | ||
63 | 50 | | |||
64 | void Thumbnailer::setUrl(const QUrl &url) | 51 | void FileMenu::setUrl(const QUrl &url) | ||
65 | { | 52 | { | ||
66 | if (m_url != url) { | 53 | if (m_url != url) { | ||
67 | m_url = url; | 54 | m_url = url; | ||
68 | emit urlChanged(); | 55 | emit urlChanged(); | ||
69 | | ||||
70 | generatePreview(); | | |||
71 | } | 56 | } | ||
72 | } | 57 | } | ||
73 | 58 | | |||
74 | QSize Thumbnailer::size() const | 59 | QQuickItem *FileMenu::visualParent() const | ||
75 | { | 60 | { | ||
76 | return m_size; | 61 | return m_visualParent.data(); | ||
77 | } | 62 | } | ||
78 | 63 | | |||
79 | void Thumbnailer::setSize(const QSize &size) | 64 | void FileMenu::setVisualParent(QQuickItem *visualParent) | ||
80 | { | 65 | { | ||
81 | if (m_size != size) { | 66 | if (m_visualParent.data() == visualParent) { | ||
82 | m_size = size; | 67 | return; | ||
83 | emit sizeChanged(); | 68 | } | ||
84 | 69 | | |||
85 | generatePreview(); | 70 | if (m_visualParent) { | ||
71 | disconnect(m_visualParent.data(), nullptr, this, nullptr); | ||||
86 | } | 72 | } | ||
73 | m_visualParent = visualParent; | ||||
74 | if (m_visualParent) { | ||||
75 | connect(m_visualParent.data(), &QObject::destroyed, this, &FileMenu::visualParentChanged); | ||||
87 | } | 76 | } | ||
88 | 77 | emit visualParentChanged(); | |||
89 | bool Thumbnailer::hasPreview() const | | |||
90 | { | | |||
91 | return !m_pixmap.isNull(); | | |||
92 | } | 78 | } | ||
93 | 79 | | |||
94 | QPixmap Thumbnailer::pixmap() const | 80 | bool FileMenu::visible() const | ||
95 | { | 81 | { | ||
96 | return m_pixmap; | 82 | return m_visible; | ||
97 | } | 83 | } | ||
98 | 84 | | |||
99 | QSize Thumbnailer::pixmapSize() const | 85 | void FileMenu::setVisible(bool visible) | ||
100 | { | 86 | { | ||
101 | return m_pixmap.size(); | 87 | if (m_visible == visible) { | ||
88 | return; | ||||
102 | } | 89 | } | ||
103 | 90 | | |||
104 | QString Thumbnailer::iconName() const | 91 | if (visible) { | ||
105 | { | 92 | open(0, 0); | ||
106 | return m_iconName; | 93 | } else { | ||
94 | // TODO warning or close? | ||||
107 | } | 95 | } | ||
108 | | ||||
109 | bool Thumbnailer::menuVisible() const | | |||
110 | { | | |||
111 | return m_menuVisible; | | |||
112 | } | 96 | } | ||
113 | 97 | | |||
114 | void Thumbnailer::showContextMenu(int x, int y, const QString &path, QQuickItem *ctx) | 98 | void FileMenu::open(int x, int y) | ||
115 | { | 99 | { | ||
116 | if (!ctx || !ctx->window()) { | 100 | if (!m_visualParent || !m_visualParent->window()) { | ||
117 | return; | 101 | return; | ||
118 | } | 102 | } | ||
119 | 103 | | |||
120 | const QUrl url(path); | 104 | if (!m_url.isValid()) { | ||
121 | if (!url.isValid()) { | | |||
122 | return; | 105 | return; | ||
123 | } | 106 | } | ||
124 | 107 | | |||
125 | KFileItem fileItem(url); | 108 | KFileItem fileItem(m_url); | ||
126 | 109 | | |||
127 | QMenu *menu = new QMenu(); | 110 | QMenu *menu = new QMenu(); | ||
128 | menu->setAttribute(Qt::WA_DeleteOnClose, true); | 111 | menu->setAttribute(Qt::WA_DeleteOnClose, true); | ||
112 | connect(menu, &QMenu::triggered, this, &FileMenu::actionTriggered); | ||||
129 | 113 | | |||
130 | connect(menu, &QMenu::aboutToHide, this, [this] { | 114 | connect(menu, &QMenu::aboutToHide, this, [this] { | ||
131 | m_menuVisible = false; | 115 | m_visible = false; | ||
132 | emit menuVisibleChanged(); | 116 | emit visibleChanged(); | ||
133 | }); | 117 | }); | ||
134 | 118 | | |||
135 | if (KProtocolManager::supportsListing(url)) { | 119 | if (KProtocolManager::supportsListing(m_url)) { | ||
136 | QAction *openContainingFolderAction = menu->addAction(QIcon::fromTheme(QStringLiteral("folder-open")), i18n("Open Containing Folder")); | 120 | QAction *openContainingFolderAction = menu->addAction(QIcon::fromTheme(QStringLiteral("folder-open")), i18n("Open Containing Folder")); | ||
137 | connect(openContainingFolderAction, &QAction::triggered, [url] { | 121 | connect(openContainingFolderAction, &QAction::triggered, [this] { | ||
138 | KIO::highlightInFileManager({url}); | 122 | KIO::highlightInFileManager({m_url}); | ||
139 | }); | 123 | }); | ||
140 | } | 124 | } | ||
141 | 125 | | |||
142 | KFileItemActions *actions = new KFileItemActions(menu); | 126 | KFileItemActions *actions = new KFileItemActions(menu); | ||
143 | KFileItemListProperties itemProperties(KFileItemList({fileItem})); | 127 | KFileItemListProperties itemProperties(KFileItemList({fileItem})); | ||
144 | actions->setItemListProperties(itemProperties); | 128 | actions->setItemListProperties(itemProperties); | ||
145 | 129 | | |||
146 | actions->addOpenWithActionsTo(menu); | 130 | actions->addOpenWithActionsTo(menu); | ||
Show All 19 Lines | |||||
166 | 150 | | |||
167 | //this is a workaround where Qt will fail to realize a mouse has been released | 151 | //this is a workaround where Qt will fail to realize a mouse has been released | ||
168 | // this happens if a window which does not accept focus spawns a new window that takes focus and X grab | 152 | // this happens if a window which does not accept focus spawns a new window that takes focus and X grab | ||
169 | // whilst the mouse is depressed | 153 | // whilst the mouse is depressed | ||
170 | // https://bugreports.qt.io/browse/QTBUG-59044 | 154 | // https://bugreports.qt.io/browse/QTBUG-59044 | ||
171 | // this causes the next click to go missing | 155 | // this causes the next click to go missing | ||
172 | 156 | | |||
173 | //by releasing manually we avoid that situation | 157 | //by releasing manually we avoid that situation | ||
174 | auto ungrabMouseHack = [ctx]() { | 158 | auto ungrabMouseHack = [this]() { | ||
175 | if (ctx->window()->mouseGrabberItem()) { | 159 | if (m_visualParent && m_visualParent->window() && m_visualParent->window()->mouseGrabberItem()) { | ||
176 | ctx->window()->mouseGrabberItem()->ungrabMouse(); | 160 | m_visualParent->window()->mouseGrabberItem()->ungrabMouse(); | ||
177 | } | 161 | } | ||
178 | }; | 162 | }; | ||
179 | 163 | | |||
180 | QTimer::singleShot(0, ctx, ungrabMouseHack); | 164 | QTimer::singleShot(0, m_visualParent, ungrabMouseHack); | ||
181 | //end workaround | 165 | //end workaround | ||
182 | 166 | | |||
183 | QPoint pos; | 167 | QPoint pos; | ||
184 | if (x == -1 && y == -1) { // align "bottom left of ctx" | 168 | if (x == -1 && y == -1) { // align "bottom left of visualParent" | ||
185 | menu->adjustSize(); | 169 | menu->adjustSize(); | ||
186 | 170 | | |||
187 | pos = ctx->mapToGlobal(QPointF(0, ctx->height())).toPoint(); | 171 | pos = m_visualParent->mapToGlobal(QPointF(0, m_visualParent->height())).toPoint(); | ||
188 | 172 | | |||
189 | if (!qApp->isRightToLeft()) { | 173 | if (!qApp->isRightToLeft()) { | ||
190 | pos.rx() += ctx->width(); | 174 | pos.rx() += m_visualParent->width(); | ||
191 | pos.rx() -= menu->width(); | 175 | pos.rx() -= menu->width(); | ||
192 | } | 176 | } | ||
193 | } else { | 177 | } else { | ||
194 | pos = ctx->mapToGlobal(QPointF(x, y)).toPoint(); | 178 | pos = m_visualParent->mapToGlobal(QPointF(x, y)).toPoint(); | ||
195 | } | 179 | } | ||
196 | 180 | | |||
197 | menu->popup(pos); | 181 | menu->popup(pos); | ||
198 | 182 | | |||
199 | m_menuVisible = true; | 183 | m_visible = true; | ||
200 | emit menuVisibleChanged(); | 184 | emit visibleChanged(); | ||
201 | } | | |||
202 | | ||||
203 | void Thumbnailer::generatePreview() | | |||
204 | { | | |||
205 | if (!m_inited) { | | |||
206 | return; | | |||
207 | } | | |||
208 | | ||||
209 | if (!m_url.isValid() || !m_url.isLocalFile() || !m_size.isValid()) { | | |||
210 | return; | | |||
211 | } | | |||
212 | | ||||
213 | auto maxSize = qMax(m_size.width(), m_size.height()); | | |||
214 | KIO::PreviewJob *job = KIO::filePreview(KFileItemList({KFileItem(m_url)}), QSize(maxSize,maxSize)); | | |||
215 | job->setScaleType(KIO::PreviewJob::Scaled); | | |||
216 | job->setIgnoreMaximumSize(true); | | |||
217 | | ||||
218 | connect(job, &KIO::PreviewJob::gotPreview, this, [this](const KFileItem &item, const QPixmap &preview) { | | |||
219 | Q_UNUSED(item); | | |||
220 | m_pixmap = preview; | | |||
221 | emit pixmapChanged(); | | |||
222 | | ||||
223 | if (!m_iconName.isEmpty()) { | | |||
224 | m_iconName.clear(); | | |||
225 | emit iconNameChanged(); | | |||
226 | } | | |||
227 | }); | | |||
228 | | ||||
229 | connect(job, &KIO::PreviewJob::failed, this, [this](const KFileItem &item) { | | |||
230 | m_pixmap = QPixmap(); | | |||
231 | emit pixmapChanged(); | | |||
232 | | ||||
233 | const QString &iconName = item.determineMimeType().iconName(); | | |||
234 | if (m_iconName != iconName) { | | |||
235 | m_iconName = iconName; | | |||
236 | emit iconNameChanged(); | | |||
237 | } | | |||
238 | }); | | |||
239 | | ||||
240 | job->start(); | | |||
241 | } | 185 | } |