Merge remote-tracking branch 'origin/Applications/16.08'
[kdepim.git] / akregator / src / tabwidget.cpp
blob73a1df97aec4751e51682e5b6c8b217e381f8ffb
1 /*
2 This file is part of Akregator.
4 Copyright (C) 2004 Sashmit Bhaduri <smt@vfemail.net>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 As a special exception, permission is given to link this program
21 with any edition of Qt, and distribute the resulting executable,
22 without including the source code for Qt in the source distribution.
25 #include "tabwidget.h"
27 #include <QStyle>
28 #include <QApplication>
29 #include <QIcon>
30 #include <QClipboard>
31 #include <QHash>
32 #include <QString>
33 #include <QToolButton>
35 #include <QMenu>
36 #include <QStyleOption>
37 #include <QDrag>
38 #include <QMimeData>
40 #include "akregator_debug.h"
41 #include <QApplication>
42 #include <QTabWidget>
43 #include <qtabbar.h>
44 #include <krun.h>
45 #include <KLocalizedString>
46 #include <kiconloader.h>
47 #include <ktoolinvocation.h>
48 #include <QUrl>
49 #include <kio/global.h>
50 #include <kio/pixmaploader.h>
52 #include "actionmanager.h"
53 #include "akregatorconfig.h"
54 #include "frame.h"
55 #include "framemanager.h"
56 #include "kernel.h"
57 #include "openurlrequest.h"
58 #include "utils/temporaryvalue.h"
60 #include <cassert>
62 namespace Akregator
65 class Q_DECL_HIDDEN TabWidget::Private
67 private:
68 TabWidget *const q;
70 public:
71 explicit Private(TabWidget *qq) : q(qq), currentMaxLength(30), currentItem(0), tabsClose(0) {}
73 QHash<QWidget *, Frame *> frames;
74 QHash<int, Frame *> framesById;
75 int currentMaxLength;
76 QWidget *currentItem;
77 QToolButton *tabsClose;
79 QWidget *selectedWidget() const
81 return (currentItem && q->indexOf(currentItem) != -1) ? currentItem : q->currentWidget();
84 uint tabBarWidthForMaxChars(int maxLength);
85 void setTitle(const QString &title, QWidget *sender);
86 void updateTabBarVisibility();
87 Frame *currentFrame();
90 void TabWidget::Private::updateTabBarVisibility()
92 const bool tabBarIsHidden = ((q->count() <= 1) && !Settings::alwaysShowTabBar());
93 if (tabBarIsHidden) {
94 q->tabBar()->hide();
95 } else {
96 q->tabBar()->show();
98 if (q->count() >= 1 && Settings::closeButtonOnTabs()) {
99 q->tabBar()->tabButton(0, QTabBar::RightSide)->hide();
103 TabWidget::TabWidget(QWidget *parent)
104 : QTabWidget(parent), d(new Private(this))
106 setMinimumSize(250, 150);
107 setMovable(false);
108 setDocumentMode(true);
109 setContextMenuPolicy(Qt::CustomContextMenu);
110 connect(this, &TabWidget::customContextMenuRequested, this, &TabWidget::slotTabContextMenuRequest);
112 connect(this, &TabWidget::currentChanged, this, &TabWidget::slotTabChanged);
113 connect(this, &QTabWidget::tabCloseRequested, this, &TabWidget::slotCloseRequest);
115 setTabsClosable(Settings::closeButtonOnTabs());
117 d->tabsClose = new QToolButton(this);
118 connect(d->tabsClose, &QToolButton::clicked, this, &TabWidget::slotRemoveCurrentFrame);
120 d->tabsClose->setIcon(QIcon::fromTheme(QStringLiteral("tab-close")));
121 d->tabsClose->setEnabled(false);
122 d->tabsClose->adjustSize();
123 d->tabsClose->setToolTip(i18n("Close the current tab"));
125 #ifndef QT_NO_ACCESSIBILITY
126 d->tabsClose->setAccessibleName(i18n("Close tab"));
127 #endif
129 setCornerWidget(d->tabsClose, Qt::TopRightCorner);
130 d->updateTabBarVisibility();
133 TabWidget::~TabWidget()
135 delete d;
138 void TabWidget::slotTabContextMenuRequest(const QPoint &pos)
141 QTabBar *bar = tabBar();
142 if (count() <= 1) {
143 return;
146 const int indexBar = bar->tabAt(bar->mapFrom(this, pos));
147 if (indexBar == -1) {
148 return;
150 QMenu menu(this);
152 const int countTab = (count() > 1);
153 QAction *detachTab = menu.addAction(i18nc("@action:inmenu", "Detach Tab"));
154 detachTab->setEnabled((indexBar != 0) && countTab);
155 detachTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-detach")));
156 menu.addSeparator();
158 QAction *closeTab = menu.addAction(i18nc("@action:inmenu", "Close Tab"));
159 closeTab->setEnabled((indexBar != 0) && countTab);
160 closeTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-close")));
162 QAction *allOther = menu.addAction(i18nc("@action:inmenu", "Close All Other Tabs"));
163 allOther->setEnabled(countTab);
164 allOther->setIcon(QIcon::fromTheme(QStringLiteral("tab-close-other")));
166 QAction *allTab = menu.addAction(i18nc("@action:inmenu", "Close All Tabs"));
167 allTab->setEnabled(countTab);
168 allTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-close")));
170 QAction *action = menu.exec(mapToGlobal(pos));
172 if (action == allOther) { // Close all other tabs
173 slotCloseAllTabExcept(indexBar);
174 } else if (action == closeTab) {
175 slotCloseRequest(indexBar);
176 } else if (action == allTab) {
177 slotCloseAllTab();
178 } else if (action == detachTab) {
179 slotDetachTab(indexBar);
183 void TabWidget::closeAllTabExcept(int index)
185 //Don't close first tab
186 for (int i = count() - 1; i > 0; --i) {
187 if (i == index) {
188 continue;
190 slotCloseRequest(i);
194 void TabWidget::slotCloseAllTabExcept(int index)
196 closeAllTabExcept(index);
199 void TabWidget::slotCloseAllTab()
201 closeAllTabExcept();
204 void TabWidget::slotSettingsChanged()
206 if (tabsClosable() != Settings::closeButtonOnTabs()) {
207 setTabsClosable(Settings::closeButtonOnTabs());
209 d->updateTabBarVisibility();
212 void TabWidget::slotNextTab()
214 setCurrentIndex((currentIndex() + 1) % count());
217 void TabWidget::slotPreviousTab()
219 if (currentIndex() == 0) {
220 setCurrentIndex(count() - 1);
221 } else {
222 setCurrentIndex(currentIndex() - 1);
225 void TabWidget::slotSelectFrame(int frameId)
227 Frame *frame = d->framesById.value(frameId);
228 if (frame && frame != d->currentFrame()) {
229 setCurrentWidget(frame);
230 frame->setFocus();
234 void TabWidget::slotAddFrame(Frame *frame)
236 if (!frame) {
237 return;
239 d->frames.insert(frame, frame);
240 d->framesById.insert(frame->id(), frame);
241 addTab(frame, frame->title());
242 connect(frame, &Frame::signalTitleChanged, this, &TabWidget::slotSetTitle);
244 slotSetTitle(frame, frame->title());
247 Frame *TabWidget::Private::currentFrame()
249 QWidget *w = q->currentWidget();
250 Q_ASSERT(frames.value(w));
251 return w ? frames.value(w) : 0;
254 void TabWidget::slotZoomChanged(qreal value)
256 if (!d->currentFrame()) {
257 return;
259 Q_EMIT signalZoomChangedInFrame(d->currentFrame()->id(), value);
262 void TabWidget::slotTabChanged(int index)
264 Frame *frame = d->frames.value(widget(index));
265 d->tabsClose->setEnabled(frame && frame->isRemovable());
266 Q_EMIT signalCurrentFrameChanged(frame ? frame->id() : -1);
269 void TabWidget::tabInserted(int)
271 d->updateTabBarVisibility();
274 void TabWidget::tabRemoved(int)
276 d->updateTabBarVisibility();
279 void TabWidget::slotRemoveCurrentFrame()
281 Frame *const frame = d->currentFrame();
282 if (frame) {
283 Q_EMIT signalRemoveFrameRequest(frame->id());
287 void TabWidget::slotRemoveFrame(int frameId)
289 if (!d->framesById.contains(frameId)) {
290 return;
292 Frame *f = d->framesById.value(frameId);
293 d->frames.remove(f);
294 d->framesById.remove(frameId);
295 f->disconnect(this);
296 removeTab(indexOf(f));
297 Q_EMIT signalRemoveFrameRequest(f->id());
298 if (d->currentFrame()) {
299 d->setTitle(d->currentFrame()->title(), currentWidget());
303 // copied wholesale from KonqFrameTabs
304 uint TabWidget::Private::tabBarWidthForMaxChars(int maxLength)
306 int hframe;
307 QStyleOption o;
308 hframe = q->tabBar()->style()->pixelMetric(QStyle::PM_TabBarTabHSpace, &o, q);
310 QFontMetrics fm = q->tabBar()->fontMetrics();
311 int x = 0;
312 for (int i = 0; i < q->count(); ++i) {
313 Frame *f = frames.value(q->widget(i));
314 if (!f) {
315 continue; // frames is out of sync, e.g. because tabInserted wasn't called yet - #185597
317 QString newTitle = f->title();
318 if (newTitle.length() > maxLength) {
319 newTitle = newTitle.left(maxLength - 3) + QLatin1String("...");
322 int lw = fm.width(newTitle);
323 int iw = q->tabBar()->tabIcon(i).pixmap(q->tabBar()->style()->pixelMetric(
324 QStyle::PM_SmallIconSize), QIcon::Normal
325 ).width() + 4;
327 x += (q->tabBar()->style()->sizeFromContents(QStyle::CT_TabBarTab, &o,
328 QSize(qMax(lw + hframe + iw, QApplication::globalStrut().width()), 0), q)).width();
330 return x;
333 void TabWidget::slotSetTitle(Frame *frame, const QString &title)
335 d->setTitle(title, frame);
338 void TabWidget::slotWebPageMutedOrAudibleChanged(Akregator::Frame *frame, bool isAudioMuted, bool wasRecentlyAudible)
340 const int idx = indexOf(frame);
341 if (idx < 0) {
342 return;
344 qDebug() << "void TabWidget::slotWebPageMutedOrAudibleChanged(Akregator::Frame *frame, bool isAudioMuted, bool wasRecentlyAudible)" << isAudioMuted << " wasRecentlyAudible" << wasRecentlyAudible;
345 //TODO
346 //setTabIcon(idx, icon);
349 void TabWidget::slotSetIcon(Akregator::Frame *frame, const QIcon &icon)
351 const int idx = indexOf(frame);
352 if (idx < 0) {
353 return;
355 setTabIcon(idx, icon);
358 void TabWidget::Private::setTitle(const QString &title, QWidget *sender)
360 int senderIndex = q->indexOf(sender);
362 q->setTabToolTip(senderIndex, QString());
364 uint lcw = 0, rcw = 0;
365 int tabBarHeight = q->tabBar()->sizeHint().height();
367 QWidget *leftCorner = q->cornerWidget(Qt::TopLeftCorner);
369 if (leftCorner && leftCorner->isVisible()) {
370 lcw = qMax(leftCorner->width(), tabBarHeight);
373 QWidget *rightCorner = q->cornerWidget(Qt::TopRightCorner);
375 if (rightCorner && rightCorner->isVisible()) {
376 rcw = qMax(rightCorner->width(), tabBarHeight);
378 uint maxTabBarWidth = q->width() - lcw - rcw;
380 int newMaxLength = 30;
382 for (; newMaxLength > 3; newMaxLength--) {
383 if (tabBarWidthForMaxChars(newMaxLength) < maxTabBarWidth) {
384 break;
388 QString newTitle = title;
389 if (newTitle.length() > newMaxLength) {
390 q->setTabToolTip(senderIndex, newTitle);
391 newTitle = newTitle.left(newMaxLength - 3) + QLatin1String("...");
394 newTitle.replace(QLatin1Char('&'), QStringLiteral("&&"));
396 if (q->tabText(senderIndex) != newTitle) {
397 q->setTabText(senderIndex, newTitle);
400 if (newMaxLength != currentMaxLength) {
401 for (int i = 0; i < q->count(); ++i) {
402 Frame *f = frames.value(q->widget(i));
403 if (!f) {
404 continue; // frames is out of sync, e.g. because tabInserted wasn't called yet - #185597
406 newTitle = f->title();
407 int index = q->indexOf(q->widget(i));
408 q->setTabToolTip(index, QString());
410 if (newTitle.length() > newMaxLength) {
411 q->setTabToolTip(index, newTitle);
412 newTitle = newTitle.left(newMaxLength - 3) + QLatin1String("...");
415 newTitle.replace(QLatin1Char('&'), QStringLiteral("&&"));
416 if (newTitle != q->tabText(index)) {
417 q->setTabText(index, newTitle);
420 currentMaxLength = newMaxLength;
424 void TabWidget::slotDetachTab(int index)
426 QWidget *w = widget(index);
427 Frame *frame = d->frames.value(w);
428 if (frame && frame->url().isValid() && frame->isRemovable()) {
429 OpenUrlRequest request;
430 request.setUrl(frame->url());
431 request.setOptions(OpenUrlRequest::ExternalBrowser);
432 Q_EMIT signalOpenUrlRequest(request);
433 slotCloseRequest(index);
437 void TabWidget::slotTextToSpeech()
439 Q_EMIT signalTextToSpeechInFrame(d->currentFrame()->id());
442 void TabWidget::slotFindTextInHtml()
444 Q_EMIT signalFindTextInFrame(d->currentFrame()->id());
447 void TabWidget::slotCopyLinkAddress()
449 Q_EMIT signalCopyLinkAsInFrame(d->currentFrame()->id());
452 void TabWidget::slotSaveLinkAs()
454 Q_EMIT signalSaveLinkAsInFrame(d->currentFrame()->id());
457 void TabWidget::slotPrintPreview()
459 Q_EMIT signalPrintPreviewInFrame(d->currentFrame()->id());
462 void TabWidget::slotPrint()
464 Q_EMIT signalPrintInFrame(d->currentFrame()->id());
467 void TabWidget::slotCopy()
469 Q_EMIT signalCopyInFrame(d->currentFrame()->id());
472 void TabWidget::slotSaveImageOnDisk()
474 Q_EMIT signalSaveImageOnDisk(d->currentFrame()->id());
477 void TabWidget::slotUnMute()
479 Q_EMIT signalMute(d->currentFrame()->id(), false);
482 void TabWidget::slotMute()
484 Q_EMIT signalMute(d->currentFrame()->id(), true);
487 void TabWidget::slotCopyImageLocation()
489 Q_EMIT signalCopyImageLocation(d->currentFrame()->id());
492 void TabWidget::slotCloseTab()
494 QWidget *widget = d->selectedWidget();
495 Frame *frame = d->frames.value(widget);
497 if (frame == 0 || !frame->isRemovable()) {
498 return;
501 Q_EMIT signalRemoveFrameRequest(frame->id());
504 void TabWidget::slotReloadAllTabs()
506 Q_FOREACH (Frame *frame, d->frames) {
507 frame->slotReload();
511 void TabWidget::slotCloseRequest(int index)
513 QWidget *w = widget(index);
514 if (d->frames.value(w)) {
515 Q_EMIT signalRemoveFrameRequest(d->frames.value(w)->id());
519 void TabWidget::slotActivateTab()
521 setCurrentIndex(sender()->objectName().rightRef(2).toInt() - 1);
524 } // namespace Akregator