]> cloud.milkyroute.net Git - dolphin.git/blob - src/tooltips/tooltipmanager.cpp
Share one tooltip instance for all tooltip manager instances
[dolphin.git] / src / tooltips / tooltipmanager.cpp
1 /*******************************************************************************
2 * Copyright (C) 2008 by Konstantin Heil <konst.heil@stud.uni-heidelberg.de> *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
8 * *
9 * This program is distributed in the hope that it will be useful, *
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12 * GNU General Public License for more details. *
13 * *
14 * You should have received a copy of the GNU General Public License *
15 * along with this program; if not, write to the *
16 * Free Software Foundation, Inc., *
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA *
18 *******************************************************************************/
19
20 #include "tooltipmanager.h"
21
22 #include "dolphinmodel.h"
23 #include "dolphinsortfilterproxymodel.h"
24
25 #include "filemetadatatooltip.h"
26 #include <kicon.h>
27 #include <kio/previewjob.h>
28
29 #include <QApplication>
30 #include <QDesktopWidget>
31 #include <QScrollArea>
32 #include <QScrollBar>
33 #include <QTimer>
34
35 ToolTipManager::ToolTipManager(QAbstractItemView* parent,
36 DolphinSortFilterProxyModel* model) :
37 QObject(parent),
38 m_view(parent),
39 m_dolphinModel(0),
40 m_proxyModel(model),
41 m_timer(0),
42 m_previewTimer(0),
43 m_waitOnPreviewTimer(0),
44 m_fileMetaDataToolTip(0),
45 m_item(),
46 m_itemRect(),
47 m_generatingPreview(false),
48 m_hasDefaultIcon(false),
49 m_previewPixmap()
50 {
51 m_dolphinModel = static_cast<DolphinModel*>(m_proxyModel->sourceModel());
52 connect(parent, SIGNAL(entered(const QModelIndex&)),
53 this, SLOT(requestToolTip(const QModelIndex&)));
54 connect(parent, SIGNAL(viewportEntered()),
55 this, SLOT(hideToolTip()));
56
57 m_timer = new QTimer(this);
58 m_timer->setSingleShot(true);
59 connect(m_timer, SIGNAL(timeout()),
60 this, SLOT(prepareToolTip()));
61
62 m_previewTimer = new QTimer(this);
63 m_previewTimer->setSingleShot(true);
64 connect(m_previewTimer, SIGNAL(timeout()),
65 this, SLOT(startPreviewJob()));
66
67 m_waitOnPreviewTimer = new QTimer(this);
68 m_waitOnPreviewTimer->setSingleShot(true);
69 connect(m_waitOnPreviewTimer, SIGNAL(timeout()),
70 this, SLOT(prepareToolTip()));
71
72 // When the mousewheel is used, the items don't get a hovered indication
73 // (Qt-issue #200665). To assure that the tooltip still gets hidden,
74 // the scrollbars are observed.
75 connect(parent->horizontalScrollBar(), SIGNAL(valueChanged(int)),
76 this, SLOT(hideTip()));
77 connect(parent->verticalScrollBar(), SIGNAL(valueChanged(int)),
78 this, SLOT(hideTip()));
79
80 m_view->viewport()->installEventFilter(this);
81 m_view->installEventFilter(this);
82
83 static FileMetaDataToolTip* sharedToolTip = 0;
84 if (sharedToolTip == 0) {
85 sharedToolTip = new FileMetaDataToolTip();
86 // TODO: Using K_GLOBAL_STATIC would be preferable to maintain the
87 // instance, but the cleanup of KMetaDataWidget at this stage does
88 // not work.
89 }
90 m_fileMetaDataToolTip = sharedToolTip;
91 }
92
93 ToolTipManager::~ToolTipManager()
94 {
95 }
96
97 void ToolTipManager::hideTip()
98 {
99 hideToolTip();
100 }
101
102 bool ToolTipManager::eventFilter(QObject* watched, QEvent* event)
103 {
104 if (watched == m_view->viewport()) {
105 switch (event->type()) {
106 case QEvent::Leave:
107 case QEvent::MouseButtonPress:
108 hideToolTip();
109 break;
110 default:
111 break;
112 }
113 } else if ((watched == m_view) && (event->type() == QEvent::KeyPress)) {
114 hideToolTip();
115 }
116
117 return QObject::eventFilter(watched, event);
118 }
119
120 void ToolTipManager::requestToolTip(const QModelIndex& index)
121 {
122 // Only request a tooltip for the name column and when no selection or
123 // drag & drop operation is done (indicated by the left mouse button)
124 if ((index.column() == DolphinModel::Name) && !(QApplication::mouseButtons() & Qt::LeftButton)) {
125 m_waitOnPreviewTimer->stop();
126 m_fileMetaDataToolTip->hide();
127
128 m_itemRect = m_view->visualRect(index);
129 const QPoint pos = m_view->viewport()->mapToGlobal(m_itemRect.topLeft());
130 m_itemRect.moveTo(pos);
131
132 const QModelIndex dirIndex = m_proxyModel->mapToSource(index);
133 m_item = m_dolphinModel->itemForIndex(dirIndex);
134
135 // Only start the previewJob when the mouse has been over this item for 200 milliseconds.
136 // This prevents a lot of useless preview jobs when passing rapidly over a lot of items.
137 m_previewTimer->start(200);
138 m_previewPixmap = QPixmap();
139 m_hasDefaultIcon = false;
140
141 m_timer->start(500);
142 } else {
143 hideToolTip();
144 }
145 }
146
147 void ToolTipManager::hideToolTip()
148 {
149 m_timer->stop();
150 m_previewTimer->stop();
151 m_waitOnPreviewTimer->stop();
152
153 m_fileMetaDataToolTip->hide();
154 }
155
156 void ToolTipManager::prepareToolTip()
157 {
158 if (m_generatingPreview) {
159 m_waitOnPreviewTimer->start(250);
160 }
161
162 if (!m_previewPixmap.isNull()) {
163 showToolTip(m_previewPixmap);
164 } else if (!m_hasDefaultIcon) {
165 const QPixmap image(KIcon(m_item.iconName()).pixmap(128, 128));
166 showToolTip(image);
167 m_hasDefaultIcon = true;
168 }
169 }
170
171 void ToolTipManager::startPreviewJob()
172 {
173 m_generatingPreview = true;
174 KIO::PreviewJob* job = KIO::filePreview(KFileItemList() << m_item, 256, 256);
175
176 connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)),
177 this, SLOT(setPreviewPix(const KFileItem&, const QPixmap&)));
178 connect(job, SIGNAL(failed(const KFileItem&)),
179 this, SLOT(previewFailed()));
180 }
181
182
183 void ToolTipManager::setPreviewPix(const KFileItem& item,
184 const QPixmap& pixmap)
185 {
186 if ((m_item.url() != item.url()) || pixmap.isNull()) {
187 // An old preview or an invalid preview has been received
188 previewFailed();
189 } else {
190 m_previewPixmap = pixmap;
191 m_generatingPreview = false;
192 }
193 }
194
195 void ToolTipManager::previewFailed()
196 {
197 m_generatingPreview = false;
198 }
199
200
201 void ToolTipManager::showToolTip(const QPixmap& pixmap)
202 {
203 if (QApplication::mouseButtons() & Qt::LeftButton) {
204 return;
205 }
206
207 m_fileMetaDataToolTip->setPreview(pixmap);
208 m_fileMetaDataToolTip->setName(m_item.text());
209 m_fileMetaDataToolTip->setItems(KFileItemList() << m_item);
210
211 // Calculate the x- and y-position of the tooltip
212 const QSize size = m_fileMetaDataToolTip->sizeHint();
213 const QRect desktop = QApplication::desktop()->screenGeometry(m_itemRect.bottomRight());
214
215 // m_itemRect defines the area of the item, where the tooltip should be
216 // shown. Per default the tooltip is shown in the bottom right corner.
217 // If the tooltip content exceeds the desktop borders, it must be assured that:
218 // - the content is fully visible
219 // - the content is not drawn inside m_itemRect
220 const bool hasRoomToLeft = (m_itemRect.left() - size.width() >= desktop.left());
221 const bool hasRoomToRight = (m_itemRect.right() + size.width() <= desktop.right());
222 const bool hasRoomAbove = (m_itemRect.top() - size.height() >= desktop.top());
223 const bool hasRoomBelow = (m_itemRect.bottom() + size.height() <= desktop.bottom());
224 if (!hasRoomAbove && !hasRoomBelow && !hasRoomToLeft && !hasRoomToRight) {
225 return;
226 }
227
228 int x = 0;
229 int y = 0;
230 if (hasRoomBelow || hasRoomAbove) {
231 x = QCursor::pos().x() + 16; // TODO: use mouse pointer width instead of the magic value of 16
232 if (x + size.width() >= desktop.right()) {
233 x = desktop.right() - size.width();
234 }
235 y = hasRoomBelow ? m_itemRect.bottom() : m_itemRect.top() - size.height();
236 } else {
237 Q_ASSERT(hasRoomToLeft || hasRoomToRight);
238 x = hasRoomToRight ? m_itemRect.right() : m_itemRect.left() - size.width();
239
240 // Put the tooltip at the bottom of the screen. The x-coordinate has already
241 // been adjusted, so that no overlapping with m_itemRect occurs.
242 y = desktop.bottom() - size.height();
243 }
244
245 m_fileMetaDataToolTip->move(x, y);
246 m_fileMetaDataToolTip->show();
247 }
248
249 #include "tooltipmanager.moc"