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