]> cloud.milkyroute.net Git - dolphin.git/blob - src/tooltips/tooltipmanager.cpp
SVN_SILENT: Minor simplificiation of the code. No change of behavior has been done.
[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 <kicon.h>
26 #include <kio/previewjob.h>
27 #include <kseparator.h>
28
29 #include "panels/information/kmetadatawidget.h"
30 #include "tooltips/ktooltip.h"
31
32 #include <QApplication>
33 #include <QDesktopWidget>
34 #include <QHBoxLayout>
35 #include <QLabel>
36 #include <QScrollArea>
37 #include <QScrollBar>
38 #include <QTimer>
39 #include <QVBoxLayout>
40
41 ToolTipManager::ToolTipManager(QAbstractItemView* parent,
42 DolphinSortFilterProxyModel* model) :
43 QObject(parent),
44 m_view(parent),
45 m_dolphinModel(0),
46 m_proxyModel(model),
47 m_timer(0),
48 m_previewTimer(0),
49 m_waitOnPreviewTimer(0),
50 m_item(),
51 m_itemRect(),
52 m_generatingPreview(false),
53 m_hasDefaultIcon(false),
54 m_previewPixmap()
55 {
56 m_dolphinModel = static_cast<DolphinModel*>(m_proxyModel->sourceModel());
57 connect(parent, SIGNAL(entered(const QModelIndex&)),
58 this, SLOT(requestToolTip(const QModelIndex&)));
59 connect(parent, SIGNAL(viewportEntered()),
60 this, SLOT(hideToolTip()));
61
62 m_timer = new QTimer(this);
63 m_timer->setSingleShot(true);
64 connect(m_timer, SIGNAL(timeout()),
65 this, SLOT(prepareToolTip()));
66
67 m_previewTimer = new QTimer(this);
68 m_previewTimer->setSingleShot(true);
69 connect(m_previewTimer, SIGNAL(timeout()),
70 this, SLOT(startPreviewJob()));
71
72 m_waitOnPreviewTimer = new QTimer(this);
73 m_waitOnPreviewTimer->setSingleShot(true);
74 connect(m_waitOnPreviewTimer, SIGNAL(timeout()),
75 this, SLOT(prepareToolTip()));
76
77 // When the mousewheel is used, the items don't get a hovered indication
78 // (Qt-issue #200665). To assure that the tooltip still gets hidden,
79 // the scrollbars are observed.
80 connect(parent->horizontalScrollBar(), SIGNAL(valueChanged(int)),
81 this, SLOT(hideTip()));
82 connect(parent->verticalScrollBar(), SIGNAL(valueChanged(int)),
83 this, SLOT(hideTip()));
84
85 m_view->viewport()->installEventFilter(this);
86 m_view->installEventFilter(this);
87 }
88
89 ToolTipManager::~ToolTipManager()
90 {
91 }
92
93 void ToolTipManager::hideTip()
94 {
95 hideToolTip();
96 }
97
98 bool ToolTipManager::eventFilter(QObject* watched, QEvent* event)
99 {
100 if (watched == m_view->viewport()) {
101 switch (event->type()) {
102 case QEvent::Leave:
103 case QEvent::MouseButtonPress:
104 hideToolTip();
105 break;
106 default:
107 break;
108 }
109 } else if ((watched == m_view) && (event->type() == QEvent::KeyPress)) {
110 hideToolTip();
111 }
112
113 return QObject::eventFilter(watched, event);
114 }
115
116 void ToolTipManager::requestToolTip(const QModelIndex& index)
117 {
118 // only request a tooltip for the name column and when no selection or
119 // drag & drop operation is done (indicated by the left mouse button)
120 if ((index.column() == DolphinModel::Name) && !(QApplication::mouseButtons() & Qt::LeftButton)) {
121 m_waitOnPreviewTimer->stop();
122 KToolTip::hideTip();
123
124 m_itemRect = m_view->visualRect(index);
125 const QPoint pos = m_view->viewport()->mapToGlobal(m_itemRect.topLeft());
126 m_itemRect.moveTo(pos);
127
128 const QModelIndex dirIndex = m_proxyModel->mapToSource(index);
129 m_item = m_dolphinModel->itemForIndex(dirIndex);
130
131 // only start the previewJob when the mouse has been over this item for 200 milliseconds,
132 // this prevents a lot of useless preview jobs when passing rapidly over a lot of items
133 m_previewTimer->start(200);
134 m_previewPixmap = QPixmap();
135 m_hasDefaultIcon = false;
136
137 m_timer->start(500);
138 } else {
139 hideToolTip();
140 }
141 }
142
143 void ToolTipManager::hideToolTip()
144 {
145 m_timer->stop();
146 m_previewTimer->stop();
147 m_waitOnPreviewTimer->stop();
148 KToolTip::hideTip();
149 }
150
151 void ToolTipManager::prepareToolTip()
152 {
153 if (m_generatingPreview) {
154 m_waitOnPreviewTimer->start(250);
155 }
156
157 if (!m_previewPixmap.isNull()) {
158 showToolTip(m_previewPixmap);
159 } else if (!m_hasDefaultIcon) {
160 const QPixmap image(KIcon(m_item.iconName()).pixmap(128, 128));
161 showToolTip(image);
162 m_hasDefaultIcon = true;
163 }
164 }
165
166 void ToolTipManager::startPreviewJob()
167 {
168 m_generatingPreview = true;
169 KIO::PreviewJob* job = KIO::filePreview(KFileItemList() << m_item, 256, 256);
170
171 connect(job, SIGNAL(gotPreview(const KFileItem&, const QPixmap&)),
172 this, SLOT(setPreviewPix(const KFileItem&, const QPixmap&)));
173 connect(job, SIGNAL(failed(const KFileItem&)),
174 this, SLOT(previewFailed()));
175 }
176
177
178 void ToolTipManager::setPreviewPix(const KFileItem& item,
179 const QPixmap& pixmap)
180 {
181 if ((m_item.url() != item.url()) || pixmap.isNull()) {
182 // an old preview or an invalid preview has been received
183 previewFailed();
184 } else {
185 m_previewPixmap = pixmap;
186 m_generatingPreview = false;
187 }
188 }
189
190 void ToolTipManager::previewFailed()
191 {
192 m_generatingPreview = false;
193 }
194
195
196 void ToolTipManager::showToolTip(const QPixmap& pixmap)
197 {
198 if (QApplication::mouseButtons() & Qt::LeftButton) {
199 return;
200 }
201
202 QWidget* tip = createTipContent(pixmap);
203
204 // calculate the x- and y-position of the tooltip
205 const QSize size = tip->sizeHint();
206 const QRect desktop = QApplication::desktop()->screenGeometry(m_itemRect.bottomRight());
207
208 // m_itemRect defines the area of the item, where the tooltip should be
209 // shown. Per default the tooltip is shown in the bottom right corner.
210 // If the tooltip content exceeds the desktop borders, it must be assured that:
211 // - the content is fully visible
212 // - the content is not drawn inside m_itemRect
213 const bool hasRoomToLeft = (m_itemRect.left() - size.width() >= desktop.left());
214 const bool hasRoomToRight = (m_itemRect.right() + size.width() <= desktop.right());
215 const bool hasRoomAbove = (m_itemRect.top() - size.height() >= desktop.top());
216 const bool hasRoomBelow = (m_itemRect.bottom() + size.height() <= desktop.bottom());
217 if (!hasRoomAbove && !hasRoomBelow && !hasRoomToLeft && !hasRoomToRight) {
218 delete tip;
219 tip = 0;
220 return;
221 }
222
223 int x = 0;
224 int y = 0;
225 if (hasRoomBelow || hasRoomAbove) {
226 x = QCursor::pos().x() + 16; // TODO: use mouse pointer width instead of the magic value of 16
227 if (x + size.width() >= desktop.right()) {
228 x = desktop.right() - size.width();
229 }
230 y = hasRoomBelow ? m_itemRect.bottom() : m_itemRect.top() - size.height();
231 } else {
232 Q_ASSERT(hasRoomToLeft || hasRoomToRight);
233 x = hasRoomToRight ? m_itemRect.right() : m_itemRect.left() - size.width();
234
235 // Put the tooltip at the bottom of the screen. The x-coordinate has already
236 // been adjusted, so that no overlapping with m_itemRect occurs.
237 y = desktop.bottom() - size.height();
238 }
239
240 // the ownership of tip is transferred to KToolTip
241 KToolTip::showTip(QPoint(x, y), tip);
242 }
243
244 QWidget* ToolTipManager::createTipContent(const QPixmap& pixmap) const
245 {
246 QWidget* tipContent = new QWidget();
247
248 // add pixmap
249 QLabel* pixmapLabel = new QLabel(tipContent);
250 pixmapLabel->setPixmap(pixmap);
251 pixmapLabel->setFixedSize(pixmap.size());
252
253 // add item name
254 QLabel* nameLabel = new QLabel(tipContent);
255 nameLabel->setText(m_item.name());
256 nameLabel->setWordWrap(true);
257 QFont font = nameLabel->font();
258 font.setBold(true);
259 nameLabel->setFont(font);
260 nameLabel->setAlignment(Qt::AlignHCenter);
261 nameLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
262
263 // add meta data
264 KMetaDataWidget* metaDataWidget = new KMetaDataWidget(tipContent);
265 metaDataWidget->setForegroundRole(QPalette::ToolTipText);
266 metaDataWidget->setItem(m_item);
267 metaDataWidget->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
268 metaDataWidget->setReadOnly(true);
269
270 // the stretchwidget allows the metadata widget to be top aligned and fills
271 // the remaining vertical space
272 QWidget* stretchWidget = new QWidget(tipContent);
273 stretchWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::MinimumExpanding);
274
275 QWidget* textContainer = new QWidget(tipContent);
276 QVBoxLayout* textLayout = new QVBoxLayout(textContainer);
277 textLayout->addWidget(nameLabel);
278 textLayout->addWidget(new KSeparator());
279 textLayout->addWidget(metaDataWidget);
280 textLayout->addWidget(stretchWidget);
281
282 QHBoxLayout* tipLayout = new QHBoxLayout(tipContent);
283 tipLayout->setMargin(0);
284 tipLayout->addWidget(pixmapLabel);
285 tipLayout->addWidget(textContainer);
286
287 return tipContent;
288 }
289
290 #include "tooltipmanager.moc"