]> cloud.milkyroute.net Git - dolphin.git/blob - src/tooltips/tooltipmanager.cpp
don't use empty dummy images if the preview takes a while to get generated, show...
[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 "dolphintooltip.h"
23 #include "dolphinmodel.h"
24 #include "dolphinsortfilterproxymodel.h"
25
26 #include <kicon.h>
27 #include <tooltips/ktooltip.h>
28 #include <kio/previewjob.h>
29
30 #include <QApplication>
31 #include <QDesktopWidget>
32 #include <QScrollBar>
33 #include <QTimer>
34 #include <QToolTip>
35
36 const int ICON_WIDTH = 128;
37 const int ICON_HEIGHT = 128;
38 const int PREVIEW_DELAY = 250;
39
40 K_GLOBAL_STATIC(DolphinBalloonTooltipDelegate, g_delegate)
41
42 ToolTipManager::ToolTipManager(QAbstractItemView* parent,
43 DolphinSortFilterProxyModel* model) :
44 QObject(parent),
45 m_view(parent),
46 m_dolphinModel(0),
47 m_proxyModel(model),
48 m_timer(0),
49 m_previewTimer(0),
50 m_waitOnPreviewTimer(0),
51 m_item(),
52 m_itemRect(),
53 m_preview(false),
54 m_generatingPreview(false),
55 m_previewPass(0),
56 m_pix()
57 {
58 KToolTip::setToolTipDelegate(g_delegate);
59
60 m_dolphinModel = static_cast<DolphinModel*>(m_proxyModel->sourceModel());
61 connect(parent, SIGNAL(entered(const QModelIndex&)),
62 this, SLOT(requestToolTip(const QModelIndex&)));
63 connect(parent, SIGNAL(viewportEntered()),
64 this, SLOT(hideToolTip()));
65
66 m_timer = new QTimer(this);
67 m_timer->setSingleShot(true);
68 connect(m_timer, SIGNAL(timeout()),
69 this, SLOT(prepareToolTip()));
70
71 m_previewTimer = new QTimer(this);
72 m_previewTimer->setSingleShot(true);
73 connect(m_previewTimer, SIGNAL(timeout()),
74 this, SLOT(startPreviewJob()));
75
76 m_waitOnPreviewTimer = new QTimer(this);
77 m_waitOnPreviewTimer->setSingleShot(true);
78 connect(m_waitOnPreviewTimer, SIGNAL(timeout()),
79 this, SLOT(prepareToolTip()));
80
81 // When the mousewheel is used, the items don't get a hovered indication
82 // (Qt-issue #200665). To assure that the tooltip still gets hidden,
83 // the scrollbars are observed.
84 connect(parent->horizontalScrollBar(), SIGNAL(valueChanged(int)),
85 this, SLOT(hideTip()));
86 connect(parent->verticalScrollBar(), SIGNAL(valueChanged(int)),
87 this, SLOT(hideTip()));
88
89 m_view->viewport()->installEventFilter(this);
90 }
91
92 ToolTipManager::~ToolTipManager()
93 {
94 }
95
96 void ToolTipManager::hideTip()
97 {
98 hideToolTip();
99 }
100
101 bool ToolTipManager::eventFilter(QObject* watched, QEvent* event)
102 {
103 if ((watched == m_view->viewport()) && (event->type() == QEvent::Leave)) {
104 hideToolTip();
105 }
106
107 return QObject::eventFilter(watched, event);
108 }
109
110 void ToolTipManager::requestToolTip(const QModelIndex& index)
111 {
112 // only request a tooltip for the name column and when no selection or
113 // drag & drop operation is done (indicated by the left mouse button)
114 if ((index.column() == DolphinModel::Name) && !(QApplication::mouseButtons() & Qt::LeftButton)) {
115 m_waitOnPreviewTimer->stop();
116 KToolTip::hideTip();
117
118 m_itemRect = m_view->visualRect(index);
119 const QPoint pos = m_view->viewport()->mapToGlobal(m_itemRect.topLeft());
120 m_itemRect.moveTo(pos);
121
122 const QModelIndex dirIndex = m_proxyModel->mapToSource(index);
123 m_item = m_dolphinModel->itemForIndex(dirIndex);
124
125 // only start the previewJob when the mouse has been over this item for 200 milliseconds,
126 // this prevents a lot of useless preview jobs when passing rapidly over a lot of items
127 m_previewTimer->start(200);
128 m_preview = false;
129 m_previewPass = 0;
130
131 m_timer->start(500);
132 } else {
133 hideToolTip();
134 }
135 }
136
137 void ToolTipManager::hideToolTip()
138 {
139 m_timer->stop();
140 m_previewTimer->stop();
141 m_waitOnPreviewTimer->stop();
142 KToolTip::hideTip();
143 }
144
145 void ToolTipManager::prepareToolTip()
146 {
147 if (m_generatingPreview) {
148 if (m_previewPass == 1) {
149 // We waited 250msec and the preview is still not finished,
150 // so show default icon as fallback.
151 QPixmap image(KIcon(m_item.iconName()).pixmap(ICON_WIDTH, ICON_HEIGHT));
152 showToolTip(image, m_item.getToolTipText());
153 }
154
155 ++m_previewPass;
156 m_waitOnPreviewTimer->start(250);
157 } else {
158 KIcon icon;
159 if (m_preview) {
160 // We got a preview.
161 icon = KIcon(m_pix);
162 } else {
163 // No preview, so use an icon.
164 // Force a 128x128 icon, a 256x256 one is far too big.
165 const QPixmap pixmap = KIcon(m_item.iconName()).pixmap(ICON_WIDTH, ICON_HEIGHT);
166 icon = KIcon(pixmap);
167 }
168
169 showToolTip(icon, m_item.getToolTipText());
170 }
171 }
172
173 void ToolTipManager::showToolTip(const QIcon& icon, const QString& text)
174 {
175 if (QApplication::mouseButtons() & Qt::LeftButton) {
176 return;
177 }
178
179 KToolTipItem* tip = new KToolTipItem(icon, text);
180
181 KStyleOptionToolTip option;
182 // TODO: get option content from KToolTip or add KToolTip::sizeHint() method
183 option.direction = QApplication::layoutDirection();
184 option.fontMetrics = QFontMetrics(QToolTip::font());
185 option.activeCorner = KStyleOptionToolTip::TopLeftCorner;
186 option.palette = QToolTip::palette();
187 option.font = QToolTip::font();
188 option.rect = QRect();
189 option.state = QStyle::State_None;
190 option.decorationSize = QSize(32, 32);
191
192 const QSize size = g_delegate->sizeHint(option, *tip);
193 const QRect desktop = QApplication::desktop()->screenGeometry(m_itemRect.bottomRight());
194
195 // m_itemRect defines the area of the item, where the tooltip should be
196 // shown. Per default the tooltip is shown in the bottom right corner.
197 // If the tooltip content exceeds the desktop borders, it must be assured that:
198 // - the content is fully visible
199 // - the content is not drawn inside m_itemRect
200 const bool hasRoomToLeft = (m_itemRect.left() - size.width() >= desktop.left());
201 const bool hasRoomToRight = (m_itemRect.right() + size.width() <= desktop.right());
202 const bool hasRoomAbove = (m_itemRect.top() - size.height() >= desktop.top());
203 const bool hasRoomBelow = (m_itemRect.bottom() + size.height() <= desktop.bottom());
204 if (!hasRoomAbove && !hasRoomBelow && !hasRoomToLeft && !hasRoomToRight) {
205 delete tip;
206 tip = 0;
207 return;
208 }
209
210 int x = 0;
211 int y = 0;
212 if (hasRoomBelow || hasRoomAbove) {
213 x = QCursor::pos().x() + 16; // TODO: use mouse pointer width instead of the magic value of 16
214 if (x + size.width() >= desktop.right()) {
215 x = desktop.right() - size.width();
216 }
217 y = hasRoomBelow ? m_itemRect.bottom() : m_itemRect.top() - size.height();
218 } else {
219 Q_ASSERT(hasRoomToLeft || hasRoomToRight);
220 x = hasRoomToRight ? m_itemRect.right() : m_itemRect.left() - size.width();
221
222 // Put the tooltip at the bottom of the screen. The x-coordinate has already
223 // been adjusted, so that no overlapping with m_itemRect occurs.
224 y = desktop.bottom() - size.height();
225 }
226
227 // the ownership of tip is transferred to KToolTip
228 KToolTip::showTip(QPoint(x, y), tip);
229 }
230
231
232
233 void ToolTipManager::startPreviewJob()
234 {
235 m_generatingPreview = true;
236 KIO::PreviewJob* job = KIO::filePreview(KFileItemList() << m_item,
237 PREVIEW_WIDTH,
238 PREVIEW_HEIGHT);
239
240 connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)),
241 this, SLOT(setPreviewPix(const KFileItem&, const QPixmap&)));
242 connect(job, SIGNAL(failed(const KFileItem&)),
243 this, SLOT(previewFailed(const KFileItem&)));
244 }
245
246
247 void ToolTipManager::setPreviewPix(const KFileItem& item,
248 const QPixmap& pixmap)
249 {
250 if ((m_item.url() != item.url()) || pixmap.isNull()) {
251 // an old preview or an invalid preview has been received
252 m_generatingPreview = false;
253 return;
254 }
255
256 m_pix = pixmap;
257 m_preview = true;
258 m_generatingPreview = false;
259 }
260
261 void ToolTipManager::previewFailed(const KFileItem& item)
262 {
263 Q_UNUSED(item);
264 m_generatingPreview = false;
265 }
266
267 #include "tooltipmanager.moc"