1 /*
2 SPDX-License-Identifier: GPL-2.0-or-later
3
4 SPDX-FileCopyrightText: 2006-2008 Eike Hein <hein@kde.org>
5 */
6
7 #include "viewcontainer.h"
8
9 #include "connectionmanager.h"
10 #include "queuetuner.h"
11 #include "application.h"
12 #include "notificationhandler.h"
13 #include "images.h"
14 #include "irccharsets.h"
15 #include "ircview.h"
16 #include "ircinput.h"
17 #include "logfilereader.h"
18 #include "konsolepanel.h"
19 #include "urlcatcher.h"
20 #include "transferpanel.h"
21 #include "transfermanager.h"
22 #include "chatcontainer.h"
23 #include "statuspanel.h"
24 #include "channel.h"
25 #include "query.h"
26 #include "rawlog.h"
27 #include "channellistpanel.h"
28 #include "nicksonline.h"
29 #include "insertchardialog.h"
30 #include "irccolorchooser.h"
31 #include "joinchanneldialog.h"
32 #include "servergroupsettings.h"
33 #include "viewtree.h"
34 #include "viewspringloader.h"
35 #include "konversation_log.h"
36
37 #include <KMessageBox>
38 #include <KIO/OpenUrlJob>
39 #include <KIO/JobUiDelegate>
40 #include <QUrl>
41 #include <KXMLGUIFactory>
42 #include <KActionCollection>
43 #include <KToggleAction>
44 #include <KSelectAction>
45
46 #include <QModelIndex>
47 #include <QSplitter>
48 #include <QTabBar>
49 #include <QWidget>
50 #include <QInputDialog>
51
52 using namespace Konversation;
53
ViewMimeData(ChatWindow * view)54 ViewMimeData::ViewMimeData(ChatWindow *view) : QMimeData()
55 , m_view(view)
56 {
57 if (view) {
58 setData(QStringLiteral("application/x-konversation-chatwindow"), view->getName().toUtf8());
59 }
60 }
61
~ViewMimeData()62 ViewMimeData::~ViewMimeData()
63 {
64 }
65
view() const66 ChatWindow* ViewMimeData::view() const
67 {
68 return m_view;
69 }
70
TabWidget(QWidget * parent)71 TabWidget::TabWidget(QWidget* parent) : QTabWidget(parent)
72 {
73 }
74
~TabWidget()75 TabWidget::~TabWidget()
76 {
77 }
78
contextMenuEvent(QContextMenuEvent * event)79 void TabWidget::contextMenuEvent(QContextMenuEvent* event)
80 {
81 event->accept();
82 QPoint pos = event->globalPos();
83 int tabIndex = tabBar()->tabAt(tabBar()->mapFromGlobal(pos));
84
85 if (tabIndex != -1)
86 {
87 Q_EMIT contextMenu(widget(tabIndex), pos);
88 }
89 }
90
mouseReleaseEvent(QMouseEvent * event)91 void TabWidget::mouseReleaseEvent(QMouseEvent* event)
92 {
93 if(event->button() == Qt::MiddleButton)
94 {
95 event->accept();
96 QPoint pos = event->globalPos();
97 int tabIndex = tabBar()->tabAt(tabBar()->mapFromGlobal(pos));
98
99 if(tabIndex != -1)
100 {
101 Q_EMIT tabBarMiddleClicked(tabIndex);
102 }
103 }
104
105 QTabWidget::mouseReleaseEvent(event);
106 }
107
108
ViewContainer(MainWindow * window)109 ViewContainer::ViewContainer(MainWindow* window) : QAbstractItemModel(window)
110 , m_window(window)
111 , m_tabWidget(nullptr)
112 , m_viewTree(nullptr)
113 , m_vbox(nullptr)
114 , m_queueTuner(nullptr)
115 , m_urlCatcherPanel(nullptr)
116 , m_nicksOnlinePanel(nullptr)
117 , m_dccPanel(nullptr)
118 , m_insertCharDialog(nullptr)
119 , m_queryViewCount(0)
120 {
121 m_viewSpringLoader = new ViewSpringLoader(this);
122
123 images = Application::instance()->images();
124
125 m_viewTreeSplitter = new QSplitter(m_window);
126 m_viewTreeSplitter->setObjectName(QStringLiteral("view_tree_splitter"));
127 m_saveSplitterSizesLock = true;
128
129 // The tree needs to be initialized before the tab widget so that it
130 // may assume a leading role in view selection management.
131 if (Preferences::self()->tabPlacement()==Preferences::Left) setupViewTree();
132
133 setupTabWidget();
134
135 initializeSplitterSizes();
136
137 m_dccPanel = new DCC::TransferPanel(m_tabWidget);
138 m_dccPanel->hide();
139 m_dccPanelOpen = false;
140 connect(m_dccPanel, &DCC::TransferPanel::updateTabNotification, this, &ViewContainer::setViewNotification);
141 }
142
~ViewContainer()143 ViewContainer::~ViewContainer()
144 {
145 }
146
showQueueTuner(bool p)147 void ViewContainer::showQueueTuner(bool p)
148 {
149 if (p)
150 m_queueTuner->open();
151 else
152 m_queueTuner->close();
153 }
154
155 ///Use this instead of setting m_frontServer directly so we can emit the frontServerChanging signal easily.
setFrontServer(Server * newserver)156 void ViewContainer::setFrontServer(Server* newserver)
157 {
158 if (m_frontServer == QPointer<Server>(newserver))
159 return;
160 Q_EMIT frontServerChanging(newserver);
161 m_frontServer = newserver;
162 }
163
prepareShutdown()164 void ViewContainer::prepareShutdown()
165 {
166 if (!m_tabWidget) return;
167
168 deleteDccPanel();
169 closeNicksOnlinePanel();
170
171 for (int i = 0; i < m_tabWidget->count(); ++i)
172 m_tabWidget->widget(i)->blockSignals(true);
173
174 m_tabWidget->blockSignals(true);
175
176 m_tabWidget = nullptr;
177 }
178
initializeSplitterSizes()179 void ViewContainer::initializeSplitterSizes()
180 {
181 if (m_viewTree && !m_viewTree->isHidden())
182 {
183 QList<int> sizes = Preferences::self()->treeSplitterSizes();
184
185 if (sizes.isEmpty())
186 sizes << 145 << (m_window->width() - 145); // FIXME: Make DPI-aware.
187 m_viewTreeSplitter->setSizes(sizes);
188
189 m_saveSplitterSizesLock = false;
190 }
191 }
192
saveSplitterSizes()193 void ViewContainer::saveSplitterSizes()
194 {
195 if (!m_saveSplitterSizesLock)
196 {
197 Preferences::self()->setTreeSplitterSizes(m_viewTreeSplitter->sizes());
198 m_saveSplitterSizesLock = false;
199 }
200 }
201
setupTabWidget()202 void ViewContainer::setupTabWidget()
203 {
204 m_popupViewIndex = -1;
205
206 m_vbox = new QWidget(m_viewTreeSplitter);
207 auto* vboxLayout = new QVBoxLayout(m_vbox);
208 vboxLayout->setContentsMargins(0, 0, 0, 0);
209 m_viewTreeSplitter->setStretchFactor(m_viewTreeSplitter->indexOf(m_vbox), 1);
210 m_vbox->setObjectName(QStringLiteral("main_window_right_side"));
211 m_tabWidget = new TabWidget(m_vbox);
212 vboxLayout->addWidget(m_tabWidget);
213 m_tabWidget->setObjectName(QStringLiteral("main_window_tab_widget"));
214 m_viewSpringLoader->addWidget(m_tabWidget->tabBar());
215 m_queueTuner = new QueueTuner(m_vbox, this);
216 vboxLayout->addWidget(m_queueTuner);
217 m_queueTuner->hide();
218
219 m_tabWidget->setMovable(true);
220 m_tabWidget->tabBar()->setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab);
221
222 m_vbox->hide();
223
224 m_tabCloseButton = new QToolButton(m_tabWidget);
225 m_tabCloseButton->setIcon(QIcon::fromTheme(QStringLiteral("tab-close")));
226 m_tabCloseButton->adjustSize();
227 m_tabWidget->setCornerWidget(m_tabCloseButton, Qt::BottomRightCorner);
228 connect(m_tabCloseButton, &QAbstractButton::clicked, this, &ViewContainer::closeCurrentView);
229
230 connect(m_tabWidget, &QTabWidget::currentChanged, this, &ViewContainer::viewSwitched);
231 connect(m_tabWidget->tabBar(), &QTabBar::tabCloseRequested, this, QOverload<int>::of(&ViewContainer::closeView));
232 connect(m_tabWidget, &TabWidget::contextMenu, this, &ViewContainer::showViewContextMenu);
233 connect(m_tabWidget, &TabWidget::tabBarMiddleClicked, this, &ViewContainer::closeViewMiddleClick);
234
235 updateTabWidgetAppearance();
236 }
237
resetFont()238 void ViewContainer::resetFont()
239 {
240 m_frontView->getTextView()->resetFontSize();
241 }
242
zoomIn()243 void ViewContainer::zoomIn()
244 {
245 if (m_frontView->getTextView())
246 m_frontView->getTextView()->increaseFontSize();
247 }
248
zoomOut()249 void ViewContainer::zoomOut()
250 {
251 if (m_frontView->getTextView())
252 m_frontView->getTextView()->decreaseFontSize();
253 }
254
setupViewTree()255 void ViewContainer::setupViewTree()
256 {
257 unclutterTabs();
258
259 m_viewTree = new ViewTree(m_viewTreeSplitter);
260 m_viewTree->setModel(this);
261 m_viewTreeSplitter->insertWidget(0, m_viewTree);
262 m_viewTreeSplitter->setStretchFactor(m_viewTreeSplitter->indexOf(m_viewTree), 0);
263 m_viewSpringLoader->addWidget(m_viewTree->viewport());
264
265 if (m_tabWidget) {
266 m_viewTree->selectView(indexForView(qobject_cast<ChatWindow*>(m_tabWidget->currentWidget())));
267 setViewTreeShown(m_tabWidget->count());
268 } else {
269 setViewTreeShown(false);
270 }
271
272 connect(m_viewTree, &ViewTree::sizeChanged, this, &ViewContainer::saveSplitterSizes);
273 connect(m_viewTree, &ViewTree::showView, this, &ViewContainer::showView);
274 connect(m_viewTree, &ViewTree::closeView, this, QOverload<ChatWindow*>::of(&ViewContainer::closeView));
275 connect(m_viewTree, &ViewTree::showViewContextMenu, this, &ViewContainer::showViewContextMenu);
276 connect(m_viewTree, &QObject::destroyed, this, &ViewContainer::onViewTreeDestroyed);
277 connect(this, &ViewContainer::contextMenuClosed, m_viewTree->viewport(), QOverload<>::of(&QWidget::update));
278 connect(Application::instance(), &Application::appearanceChanged, m_viewTree, &ViewTree::updateAppearance);
279 connect(this, &ViewContainer::viewChanged, m_viewTree, &ViewTree::selectView);
280
281 QAction* action;
282
283 action = actionCollection()->action(QStringLiteral("move_tab_left"));
284
285 if (action)
286 {
287 action->setText(i18n("Move Tab Up"));
288 action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up")));
289 }
290
291 action = actionCollection()->action(QStringLiteral("move_tab_right"));
292
293 if (action)
294 {
295 action->setText(i18n("Move Tab Down"));
296 action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down")));
297 }
298 }
299
onViewTreeDestroyed(QObject * object)300 void ViewContainer::onViewTreeDestroyed(QObject* object)
301 {
302 Q_UNUSED(object)
303
304 setViewTreeShown(false);
305 }
306
setViewTreeShown(bool show)307 void ViewContainer::setViewTreeShown(bool show)
308 {
309 if (m_viewTree)
310 {
311 if (!show)
312 {
313 m_saveSplitterSizesLock = true;
314 m_viewTree->hide();
315 }
316 else
317 {
318 m_viewTree->show();
319 initializeSplitterSizes();
320 m_saveSplitterSizesLock = false;
321 }
322 }
323 }
324
removeViewTree()325 void ViewContainer::removeViewTree()
326 {
327 QAction* action;
328
329 action = actionCollection()->action(QStringLiteral("move_tab_left"));
330
331 if (action)
332 {
333 action->setText(i18n("Move Tab Left"));
334 action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
335 }
336
337 action = actionCollection()->action(QStringLiteral("move_tab_right"));
338
339 if (action)
340 {
341 action->setText(i18n("Move Tab Right"));
342 action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
343 }
344
345 delete m_viewTree;
346 m_viewTree = nullptr;
347 }
348
rowCount(const QModelIndex & parent) const349 int ViewContainer::rowCount(const QModelIndex& parent) const
350 {
351 int count = 0;
352 if (m_tabWidget) {
353 if (parent.isValid()) {
354 auto* statusView = static_cast<ChatWindow*>(parent.internalPointer());
355
356 if (statusView) {
357 for (int i = m_tabWidget->indexOf(statusView) + 1; i < m_tabWidget->count(); ++i) {
358 const auto* view = static_cast<ChatWindow*>(m_tabWidget->widget(i));
359
360 if (view != statusView && view->getServer() && view->getServer()->getStatusView() == statusView) {
361 ++count;
362 }
363
364 if (view->isTopLevelView()) {
365 break;
366 }
367 }
368 }
369 } else {
370 for (int i = 0; i < m_tabWidget->count(); ++i) {
371 const auto* view = static_cast<ChatWindow*>(m_tabWidget->widget(i));
372
373 if (view->isTopLevelView()) {
374 ++count;
375 }
376 }
377 }
378 }
379
380 return count;
381 }
382
columnCount(const QModelIndex & parent) const383 int ViewContainer::columnCount(const QModelIndex& parent) const
384 {
385 Q_UNUSED(parent)
386
387 return 1;
388 }
389
index(int row,int column,const QModelIndex & parent) const390 QModelIndex ViewContainer::index(int row, int column, const QModelIndex& parent) const
391 {
392 if (!m_tabWidget || column != 0) {
393 return QModelIndex();
394 }
395
396 int tabIndex = -1;
397
398 if (parent.isValid()) {
399 int parentTabIndex = m_tabWidget->indexOf(static_cast<QWidget*>(parent.internalPointer()));
400
401 if (parentTabIndex != -1) {
402 tabIndex = parentTabIndex + row + 1;
403 } else {
404 return QModelIndex();
405 }
406 } else {
407 int count = -1;
408
409 for (int i = 0; i < m_tabWidget->count(); ++i) {
410 if (static_cast<ChatWindow*>(m_tabWidget->widget(i))->isTopLevelView()) {
411 ++count;
412 }
413
414 if (count == row) {
415 tabIndex = i;
416
417 break;
418 }
419 }
420 }
421
422 if (tabIndex == -1) {
423 return QModelIndex();
424 }
425
426 return createIndex(row, column, m_tabWidget->widget(tabIndex));
427 }
428
indexForView(ChatWindow * view) const429 QModelIndex ViewContainer::indexForView(ChatWindow* view) const
430 {
431 if (!view || !m_tabWidget) {
432 return QModelIndex();
433 }
434
435 int index = m_tabWidget->indexOf(view);
436
437 if (index == -1) {
438 return QModelIndex();
439 }
440
441 if (view->isTopLevelView()) {
442 int count = -1;
443
444 for (int i = 0; i <= index; ++i) {
445 if (static_cast<ChatWindow*>(m_tabWidget->widget(i))->isTopLevelView()) {
446 ++count;
447 }
448 }
449
450 return createIndex(count, 0, view);
451 } else {
452 if (!view->getServer() || !view->getServer()->getStatusView()) {
453 return QModelIndex();
454 }
455
456 ChatWindow* statusView = view->getServer()->getStatusView();
457
458 int statusViewIndex = m_tabWidget->indexOf(statusView);
459
460 return createIndex(index - statusViewIndex - 1, 0, view);
461 }
462 }
463
parent(const QModelIndex & index) const464 QModelIndex ViewContainer::parent(const QModelIndex& index) const
465 {
466 if (!m_tabWidget) {
467 return QModelIndex();
468 }
469
470 const ChatWindow* view = static_cast<ChatWindow*>(index.internalPointer());
471
472 if (!view || view->isTopLevelView() || !view->getServer() || !view->getServer()->getStatusView()) {
473 return QModelIndex();
474 }
475
476 return indexForView(view->getServer()->getStatusView());
477 }
478
data(const QModelIndex & index,int role) const479 QVariant ViewContainer::data(const QModelIndex& index, int role) const
480 {
481 if (!index.isValid() || !index.internalPointer() || !m_tabWidget) {
482 return QVariant();
483 }
484
485 int row = m_tabWidget->indexOf(static_cast<ChatWindow*>(index.internalPointer()));
486
487 if (role == Qt::DisplayRole) {
488 return static_cast<ChatWindow*>(index.internalPointer())->getName();
489 } else if (role == Qt::DecorationRole) {
490 // FIXME KF5 port: Don't show close buttons on the view tree for now.
491 if (m_viewTree && Preferences::self()->closeButtons() && !Preferences::self()->tabNotificationsLeds()) {
492 return QVariant();
493 }
494
495 return m_tabWidget->tabIcon(row);
496 } else if (role == ColorRole) {
497 const ChatWindow* view = static_cast<ChatWindow*>(index.internalPointer());
498
499 if (view->currentTabNotification() != Konversation::tnfNone) {
500 if ((view->currentTabNotification() == Konversation::tnfNormal && Preferences::self()->tabNotificationsMsgs())
501 || (view->currentTabNotification() == Konversation::tnfPrivate && Preferences::self()->tabNotificationsPrivate())
502 || (view->currentTabNotification() == Konversation::tnfSystem && Preferences::self()->tabNotificationsSystem())
503 || (view->currentTabNotification() == Konversation::tnfControl && Preferences::self()->tabNotificationsEvents())
504 || (view->currentTabNotification() == Konversation::tnfNick && Preferences::self()->tabNotificationsNick())
505 || (view->currentTabNotification() == Konversation::tnfHighlight && Preferences::self()->tabNotificationsHighlights())) {
506 return m_tabWidget->tabBar()->tabTextColor(row);
507 }
508 }
509 } else if (role == DisabledRole) {
510 const ChatWindow* view = static_cast<ChatWindow*>(index.internalPointer());
511
512 if (view->getType() == ChatWindow::Channel) {
513 return !static_cast<const Channel*>(view)->isJoined();
514 } else if (view->getType() == ChatWindow::Query) {
515 return !view->getServer()->isConnected();
516 }
517
518 return false;
519 } else if (role == HighlightRole) {
520 return (row == m_popupViewIndex);
521 }
522
523 return QVariant();
524 }
525
supportedDragActions() const526 Qt::DropActions ViewContainer::supportedDragActions() const
527 {
528 return Qt::MoveAction;
529 }
530
supportedDropActions() const531 Qt::DropActions ViewContainer::supportedDropActions() const
532 {
533 return Qt::MoveAction;
534 }
535
flags(const QModelIndex & index) const536 Qt::ItemFlags ViewContainer::flags(const QModelIndex &index) const
537 {
538 Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index);
539
540 return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags;
541 }
542
mimeTypes() const543 QStringList ViewContainer::mimeTypes() const
544 {
545 return QStringList { QStringLiteral("application/x-konversation-chatwindow") };
546 }
547
mimeData(const QModelIndexList & indexes) const548 QMimeData* ViewContainer::mimeData(const QModelIndexList &indexes) const
549 {
550 if (indexes.isEmpty()) {
551 return new ViewMimeData(nullptr);
552 }
553
554 const QModelIndex &idx = indexes.at(0);
555
556 if (!idx.isValid()) {
557 return new ViewMimeData(nullptr);
558 }
559
560 return new ViewMimeData(static_cast<ChatWindow *>(idx.internalPointer()));
561 }
562
canDropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent) const563 bool ViewContainer::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
564 {
565 if (action != Qt::MoveAction) {
566 return false;
567 }
568
569 if (!data->hasFormat(QStringLiteral("application/x-konversation-chatwindow"))) {
570 return false;
571 }
572
573 if (row == -1 || column != 0) {
574 return false;
575 }
576
577 ChatWindow *dragView = static_cast<const ViewMimeData *>(data)->view();
578
579 if (!dragView->isTopLevelView()
580 && (!parent.isValid()
581 || (dragView->getServer() != static_cast<ChatWindow* >(parent.internalPointer())->getServer()))) {
582 return false;
583 }
584
585 if (dragView->isTopLevelView() && parent.isValid()) {
586 return false;
587 }
588
589 if (m_viewTree && !m_viewTree->showDropIndicator()) {
590 m_viewTree->setDropIndicatorShown(true);
591 m_viewTree->viewport()->update();
592 }
593
594 return true;
595 }
596
dropMimeData(const QMimeData * data,Qt::DropAction action,int row,int column,const QModelIndex & parent)597 bool ViewContainer::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
598 {
599 if (action != Qt::MoveAction || row == -1 || column != 0 ||
600 !data->hasFormat(QStringLiteral("application/x-konversation-chatwindow"))) {
601 return false;
602 }
603
604 ChatWindow *dragView = static_cast<const ViewMimeData *>(data)->view();
605 QModelIndex dragIdx = indexForView(dragView);
606
607 if (dragView->isTopLevelView() && !parent.isValid()) {
608 for (int i = row < dragIdx.row() ? 0 : 1; i < qAbs(dragIdx.row() - row); ++i) {
609 (row < dragIdx.row()) ? moveViewLeft() : moveViewRight();
610 }
611
612 return true;
613 } else if(parent.isValid()) {
614 int from = m_tabWidget->indexOf(dragView);
615 int to = m_tabWidget->indexOf(static_cast<ChatWindow* >(parent.internalPointer())) + row;
616
617 if (to < from) {
618 ++to;
619 }
620
621 if (from != to) {
622 beginMoveRows(parent, dragIdx.row(), dragIdx.row(), parent, row);
623
624 m_tabWidget->blockSignals(true);
625 m_tabWidget->tabBar()->moveTab(from, to);
626 m_tabWidget->blockSignals(false);
627
628 endMoveRows();
629
630 viewSwitched(m_tabWidget->currentIndex());
631
632 return true;
633 }
634 }
635
636 return false;
637 }
638
removeRows(int row,int count,const QModelIndex & parent)639 bool ViewContainer::removeRows(int row, int count, const QModelIndex &parent)
640 {
641 Q_UNUSED(row)
642 Q_UNUSED(count)
643 Q_UNUSED(parent)
644
645 return true;
646 }
647
updateAppearance()648 void ViewContainer::updateAppearance()
649 {
650 if (Preferences::self()->tabPlacement()==Preferences::Left && m_viewTree == nullptr)
651 {
652 m_saveSplitterSizesLock = true;
653 setupViewTree();
654 }
655
656 if (!(Preferences::self()->tabPlacement()==Preferences::Left) && m_viewTree)
657 {
658 m_saveSplitterSizesLock = true;
659 removeViewTree();
660 }
661
662 updateViews();
663 updateTabWidgetAppearance();
664
665 auto* action = qobject_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("hide_nicknamelist")));
666 Q_ASSERT(action);
667 action->setChecked(Preferences::self()->showNickList());
668
669 if (m_insertCharDialog)
670 {
671 QFont font;
672
673 if (Preferences::self()->customTextFont())
674 font = Preferences::self()->textFont();
675 else
676 font = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
677
678 m_insertCharDialog->setFont(font);
679 }
680 }
681
updateTabWidgetAppearance()682 void ViewContainer::updateTabWidgetAppearance()
683 {
684 bool noTabBar = (Preferences::self()->tabPlacement()==Preferences::Left);
685 m_tabWidget->tabBar()->setHidden(noTabBar);
686
687 m_tabWidget->setDocumentMode(true);
688
689 if (Preferences::self()->customTabFont())
690 m_tabWidget->tabBar()->setFont(Preferences::self()->tabFont());
691 else
692 m_tabWidget->tabBar()->setFont(QFontDatabase::systemFont(QFontDatabase::GeneralFont));
693
694 m_tabWidget->setTabPosition((Preferences::self()->tabPlacement()==Preferences::Top) ?
695 QTabWidget::North : QTabWidget::South);
696
697 // the corner widget has to be unset if not wanted,
698 // just hiding it will have the tabbar still reserving the space
699 const bool showTabBarCloseButton = Preferences::self()->showTabBarCloseButton() && !noTabBar;
700 const bool isTabBarCloseButtonVisible = (m_tabWidget->cornerWidget() != nullptr);
701
702 if (showTabBarCloseButton != isTabBarCloseButtonVisible)
703 {
704 if (showTabBarCloseButton)
705 {
706 m_tabWidget->setCornerWidget(m_tabCloseButton, Qt::BottomRightCorner);
707 m_tabCloseButton->show();
708 }
709 else
710 {
711 m_tabWidget->setCornerWidget(nullptr, Qt::BottomRightCorner);
712 m_tabCloseButton->hide();
713 }
714 }
715
716 m_tabWidget->tabBar()->setTabsClosable(Preferences::self()->closeButtons());
717 }
718
updateViewActions(int index)719 void ViewContainer::updateViewActions(int index)
720 {
721 if (!m_tabWidget) return;
722
723 QAction* action;
724 ChatWindow* view = nullptr;
725
726 if (index != -1)
727 view = qobject_cast<ChatWindow*>(m_tabWidget->widget(index));
728
729 if (m_tabWidget->count() > 0 && view)
730 {
731 ChatWindow::WindowType viewType = view->getType();
732 Server* server = view->getServer();
733 bool insertSupported = view->isInsertSupported();
734 IRCView* textView = view->getTextView();
735
736 // FIXME ViewTree port: Take hierarchy into account.
737 action = actionCollection()->action(QStringLiteral("move_tab_left"));
738 if (action) action->setEnabled(canMoveViewLeft());
739
740 action = actionCollection()->action(QStringLiteral("move_tab_right"));
741 if (action) action->setEnabled(canMoveViewRight());
742
743 if (server && (viewType == ChatWindow::Status || server == m_frontServer))
744 {
745 action = actionCollection()->action(QStringLiteral("reconnect_server"));
746 if (action) action->setEnabled(true);
747
748 action = actionCollection()->action(QStringLiteral("disconnect_server"));
749 if (action) action->setEnabled(server->isConnected() || server->isConnecting() || server->isScheduledToConnect());
750
751 action = actionCollection()->action(QStringLiteral("join_channel"));
752 if (action) action->setEnabled(server->isConnected());
753 }
754 else
755 {
756 action = actionCollection()->action(QStringLiteral("reconnect_server"));
757 if (action) action->setEnabled(false);
758
759
760 action = actionCollection()->action(QStringLiteral("disconnect_server"));
761 if (action) action->setEnabled(false);
762
763
764 action = actionCollection()->action(QStringLiteral("join_channel"));
765 if (action) action->setEnabled(false);
766 }
767
768 auto* notifyAction = qobject_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("tab_notifications")));
769 if (notifyAction)
770 {
771 notifyAction->setEnabled(viewType == ChatWindow::Channel || viewType == ChatWindow::Query ||
772 viewType == ChatWindow::Status || viewType == ChatWindow::Konsole ||
773 viewType == ChatWindow::DccTransferPanel || viewType == ChatWindow::RawLog);
774 notifyAction->setChecked(view->notificationsEnabled());
775 }
776
777 auto* autoJoinAction = qobject_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("tab_autojoin")));
778 auto* channel = static_cast<Channel*>(view);
779 if (autoJoinAction && viewType == ChatWindow::Channel && channel->getServer()->getServerGroup())
780 {
781 autoJoinAction->setEnabled(true);
782 autoJoinAction->setChecked(channel->autoJoin());
783 }
784 else if (!(viewType != ChatWindow::Channel && index != m_tabWidget->currentIndex()))
785 {
786 autoJoinAction->setEnabled(false);
787 autoJoinAction->setChecked(false);
788 }
789
790 auto* autoConnectAction = qobject_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("tab_autoconnect")));
791 if (autoConnectAction && server && (viewType == ChatWindow::Status || server == m_frontServer) && server->getServerGroup())
792 {
793 autoConnectAction->setEnabled(true);
794 autoConnectAction->setChecked(server->getServerGroup()->autoConnectEnabled());
795 }
796 else if (!(viewType != ChatWindow::Status && index != m_tabWidget->currentIndex()))
797 {
798 autoConnectAction->setEnabled(false);
799 autoConnectAction->setChecked(false);
800 }
801
802 action = actionCollection()->action(QStringLiteral("rejoin_channel"));
803 if (action) action->setEnabled(viewType == ChatWindow::Channel && channel->rejoinable());
804
805 action = actionCollection()->action(QStringLiteral("close_queries"));
806 if (action) action->setEnabled(m_queryViewCount > 0);
807
808 action = actionCollection()->action(QStringLiteral("clear_tabs"));
809 if (action) action->setEnabled(true);
810
811 action = actionCollection()->action(QStringLiteral("increase_font"));
812 if (action) action->setEnabled(true);
813
814 action = actionCollection()->action(QStringLiteral("shrink_font"));
815 if (action) action->setEnabled(true);
816
817 action = actionCollection()->action(QStringLiteral("reset_font"));
818 if (action) action->setEnabled(true);
819
820 action = actionCollection()->action(QStringLiteral("toggle_away"));
821 if (action) action->setEnabled(true);
822
823 action = actionCollection()->action(QStringLiteral("next_tab"));
824 if (action) action->setEnabled(true);
825
826 action = actionCollection()->action(QStringLiteral("previous_tab"));
827 if (action) action->setEnabled(true);
828
829 action = actionCollection()->action(QStringLiteral("next_active_tab"));
830 if (action) action->setEnabled(true);
831
832 action = actionCollection()->action(QStringLiteral("close_tab"));
833 if (action) action->setEnabled(true);
834
835 if (index == m_tabWidget->currentIndex())
836 {
837 // The following only need to be updated when this run is related
838 // to the active tab, e.g. when it was just changed.
839
840 action = actionCollection()->action(QStringLiteral("insert_marker_line"));
841 if (action) action->setEnabled(textView != nullptr);
842
843 action = actionCollection()->action(QStringLiteral("insert_character"));
844 if (action) action->setEnabled(insertSupported);
845
846 action = actionCollection()->action(QStringLiteral("irc_colors"));
847 if (action) action->setEnabled(insertSupported);
848
849 action = actionCollection()->action(QStringLiteral("auto_replace"));
850 if (action) action->setEnabled(view->getInputBar() != nullptr);
851
852 action = actionCollection()->action(QStringLiteral("focus_input_box"));
853 if (action)
854 {
855 action->setEnabled(view->getInputBar() != nullptr);
856
857 if (view->getTextView() && view->getTextView()->parent()) {
858 //HACK See notes in SearchBar::eventFilter
859 QEvent e(static_cast<QEvent::Type>(QEvent::User+414)); // Magic number to avoid QEvent::registerEventType
860 Application::instance()->sendEvent(view->getTextView()->parent(), &e);
861 }
862 }
863
864 action = actionCollection()->action(QStringLiteral("clear_lines"));
865 if (action) action->setEnabled(textView != nullptr && view->getTextView()->hasLines());
866
867 action = actionCollection()->action(QStringLiteral("clear_window"));
868 if (action) action->setEnabled(textView != nullptr);
869
870 action = actionCollection()->action(QStringLiteral("edit_find"));
871 if (action)
872 {
873 action->setText(i18n("Find Text..."));
874 action->setEnabled(view->searchView());
875 action->setStatusTip(i18n("Search for text in the current tab"));
876 }
877
878 action = actionCollection()->action(QStringLiteral("edit_find_next"));
879 if (action) action->setEnabled(view->searchView());
880
881 action = actionCollection()->action(QStringLiteral("edit_find_prev"));
882 if (action) action->setEnabled(view->searchView());
883
884 auto* channelListAction = qobject_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("open_channel_list")));
885 if (channelListAction)
886 {
887 if (m_frontServer)
888 {
889 QString name = m_frontServer->getDisplayName();
890 name.replace(QLatin1Char('&'), QLatin1String("&&"));
891 channelListAction->setEnabled(true);
892 channelListAction->setChecked(m_frontServer->getChannelListPanel());
893 channelListAction->setText(i18n("Channel &List for %1",name));
894 }
895 else
896 {
897 channelListAction->setEnabled(false);
898 channelListAction->setChecked(false);
899 channelListAction->setText(i18n("Channel &List"));
900 }
901 }
902
903 action = actionCollection()->action(QStringLiteral("open_logfile"));
904 if (action)
905 {
906 action->setEnabled(!view->logFileName().isEmpty());
907 if (view->logFileName().isEmpty())
908 action->setText(i18n("&Open Logfile"));
909 else
910 {
911 QString name = view->getName();
912 name.replace(QLatin1Char('&'), QLatin1String("&&"));
913 action->setText(i18n("&Open Logfile for %1",name));
914 }
915 }
916
917 action = actionCollection()->action(QStringLiteral("hide_nicknamelist"));
918 if (action) action->setEnabled(view->getType() == ChatWindow::Channel);
919
920 action = actionCollection()->action(QStringLiteral("channel_settings"));
921 if (action && view->getType() == ChatWindow::Channel)
922 {
923 action->setEnabled(true);
924 action->setText(i18n("&Channel Settings for %1...",view->getName()));
925 }
926 else if (action)
927 {
928 action->setEnabled(false);
929 action->setText(i18n("&Channel Settings..."));
930 }
931 }
932 }
933 else
934 {
935 action = actionCollection()->action(QStringLiteral("move_tab_left"));
936 if (action) action->setEnabled(false);
937
938 action = actionCollection()->action(QStringLiteral("move_tab_right"));
939 if(action) action->setEnabled(false);
940
941 action = actionCollection()->action(QStringLiteral("next_tab"));
942 if (action) action->setEnabled(false);
943
944 action = actionCollection()->action(QStringLiteral("previous_tab"));
945 if (action) action->setEnabled(false);
946
947 action = actionCollection()->action(QStringLiteral("close_tab"));
948 if (action) action->setEnabled(false);
949
950 action = actionCollection()->action(QStringLiteral("next_active_tab"));
951 if (action) action->setEnabled(false);
952
953 action = actionCollection()->action(QStringLiteral("tab_notifications"));
954 if (action) action->setEnabled(false);
955
956 action = actionCollection()->action(QStringLiteral("tab_autojoin"));
957 if (action) action->setEnabled(false);
958
959 action = actionCollection()->action(QStringLiteral("tab_autoconnect"));
960 if (action) action->setEnabled(false);
961
962 action = actionCollection()->action(QStringLiteral("rejoin_channel"));
963 if (action) action->setEnabled(false);
964
965 action = actionCollection()->action(QStringLiteral("insert_marker_line"));
966 if (action) action->setEnabled(false);
967
968 action = actionCollection()->action(QStringLiteral("insert_character"));
969 if (action) action->setEnabled(false);
970
971 action = actionCollection()->action(QStringLiteral("irc_colors"));
972 if (action) action->setEnabled(false);
973
974 action = actionCollection()->action(QStringLiteral("clear_lines"));
975 if (action) action->setEnabled(false);
976
977 action = actionCollection()->action(QStringLiteral("clear_window"));
978 if (action) action->setEnabled(false);
979
980 action = actionCollection()->action(QStringLiteral("clear_tabs"));
981 if (action) action->setEnabled(false);
982
983 action = actionCollection()->action(QStringLiteral("edit_find"));
984 if (action) action->setEnabled(false);
985
986 action = actionCollection()->action(QStringLiteral("edit_find_next"));
987 if (action) action->setEnabled(false);
988
989 action = actionCollection()->action(QStringLiteral("edit_find_prev"));
990 if (action) action->setEnabled(false);
991
992 action = actionCollection()->action(QStringLiteral("open_channel_list"));
993 if (action) action->setEnabled(false);
994
995 action = actionCollection()->action(QStringLiteral("open_logfile"));
996 if (action) action->setEnabled(false);
997
998 action = actionCollection()->action(QStringLiteral("toggle_away"));
999 if (action) action->setEnabled(false);
1000
1001 action = actionCollection()->action(QStringLiteral("join_channel"));
1002 if (action) action->setEnabled(false);
1003
1004 action = actionCollection()->action(QStringLiteral("disconnect_server"));
1005 if (action) action->setEnabled(false);
1006
1007 action = actionCollection()->action(QStringLiteral("reconnect_server"));
1008 if (action) action->setEnabled(false);
1009
1010 action = actionCollection()->action(QStringLiteral("hide_nicknamelist"));
1011 if (action) action->setEnabled(false);
1012
1013 action = actionCollection()->action(QStringLiteral("channel_settings"));
1014 if (action) action->setEnabled(false);
1015
1016 action = actionCollection()->action(QStringLiteral("close_queries"));
1017 if (action) action->setEnabled(false);
1018 }
1019
1020 action = actionCollection()->action(QStringLiteral("last_focused_tab"));
1021 if (action) action->setEnabled(m_lastFocusedView != nullptr);
1022 }
1023
updateFrontView()1024 void ViewContainer::updateFrontView()
1025 {
1026 if (!m_tabWidget) return;
1027
1028 auto* view = qobject_cast<ChatWindow*>(m_tabWidget->currentWidget());
1029
1030 if (!view) return;
1031
1032 // Make sure that only views with info output get to be the m_frontView
1033 if (m_frontView)
1034 {
1035 disconnect(m_frontView.data(), &ChatWindow::updateInfo, this, &ViewContainer::setStatusBarInfoLabel);
1036 }
1037
1038 if (view->canBeFrontView())
1039 {
1040 m_frontView = view;
1041
1042 connect(view, &ChatWindow::updateInfo, this, &ViewContainer::setStatusBarInfoLabel);
1043 view->emitUpdateInfo();
1044 }
1045 else
1046 {
1047 QString viewName = Konversation::removeIrcMarkup(view->getName());
1048
1049 if(viewName != QLatin1String("ChatWindowObject"))
1050 Q_EMIT setStatusBarInfoLabel(viewName);
1051 else
1052 Q_EMIT clearStatusBarInfoLabel();
1053 }
1054
1055 switch (view->getType())
1056 {
1057 case ChatWindow::Channel:
1058 case ChatWindow::Query:
1059 case ChatWindow::Status:
1060 case ChatWindow::ChannelList:
1061 case ChatWindow::RawLog:
1062 Q_EMIT setStatusBarLagLabelShown(true);
1063 break;
1064
1065 default:
1066 Q_EMIT setStatusBarLagLabelShown(false);
1067 break;
1068 }
1069
1070 // Make sure that only text views get to be the m_searchView
1071 if (view->searchView()) m_searchView = view;
1072
1073 updateViewActions(m_tabWidget->currentIndex());
1074 }
1075
updateViews(const Konversation::ServerGroupSettingsPtr & serverGroup)1076 void ViewContainer::updateViews(const Konversation::ServerGroupSettingsPtr &serverGroup)
1077 {
1078 for (int i = 0; i < m_tabWidget->count(); ++i)
1079 {
1080 auto* view = static_cast<ChatWindow*>(m_tabWidget->widget(i));
1081 bool announce = false;
1082
1083 if (serverGroup)
1084 {
1085 if (view->getType() == ChatWindow::Status && view->getServer()->getServerGroup() == serverGroup)
1086 {
1087 QString label = view->getServer()->getDisplayName();
1088
1089 if (!label.isEmpty() && m_tabWidget->tabText(i) != label)
1090 {
1091 m_tabWidget->setTabText(i, label);
1092
1093 announce = true;
1094
1095 if (view == m_frontView)
1096 {
1097 Q_EMIT setStatusBarInfoLabel(label);
1098 Q_EMIT setWindowCaption(label);
1099 }
1100
1101 static_cast<StatusPanel*>(view)->updateName();
1102 }
1103 }
1104
1105 if (i == m_tabWidget->currentIndex())
1106 updateViewActions(i);
1107 }
1108
1109 if (!Preferences::self()->tabNotificationsLeds()) {
1110 m_tabWidget->setTabIcon(i, QIcon());
1111 announce = true;
1112 }
1113
1114 if (!Preferences::self()->tabNotificationsText()) {
1115 m_tabWidget->tabBar()->setTabTextColor(i, m_window->palette().windowText().color());
1116 announce = true;
1117 }
1118
1119 if (Preferences::self()->tabNotificationsLeds() || Preferences::self()->tabNotificationsText())
1120 {
1121 if (view->currentTabNotification()==Konversation::tnfNone)
1122 unsetViewNotification(view);
1123 else if (view->currentTabNotification()==Konversation::tnfNormal && !Preferences::self()->tabNotificationsMsgs())
1124 unsetViewNotification(view);
1125 else if (view->currentTabNotification()==Konversation::tnfPrivate && !Preferences::self()->tabNotificationsPrivate())
1126 unsetViewNotification(view);
1127 else if (view->currentTabNotification()==Konversation::tnfSystem && !Preferences::self()->tabNotificationsSystem())
1128 unsetViewNotification(view);
1129 else if (view->currentTabNotification()==Konversation::tnfControl && !Preferences::self()->tabNotificationsEvents())
1130 unsetViewNotification(view);
1131 else if (view->currentTabNotification()==Konversation::tnfNick && !Preferences::self()->tabNotificationsNick())
1132 unsetViewNotification(view);
1133 else if (view->currentTabNotification()==Konversation::tnfHighlight && !Preferences::self()->tabNotificationsHighlights())
1134 unsetViewNotification(view);
1135 else if (view==m_tabWidget->currentWidget())
1136 unsetViewNotification(view);
1137 else
1138 setViewNotification(view, view->currentTabNotification());
1139 }
1140
1141 if (announce) {
1142 const QModelIndex& idx = indexForView(view);
1143 Q_EMIT dataChanged(idx, idx);
1144 }
1145 }
1146 }
1147
setViewNotification(ChatWindow * view,const Konversation::TabNotifyType & type)1148 void ViewContainer::setViewNotification(ChatWindow* view, const Konversation::TabNotifyType& type)
1149 {
1150 if (!view || view == m_tabWidget->currentWidget())
1151 return;
1152
1153 if (type < Konversation::tnfControl && !m_activeViewOrderList.contains(view))
1154 m_activeViewOrderList.append(view);
1155
1156 if (!Preferences::self()->tabNotificationsLeds() && !Preferences::self()->tabNotificationsText())
1157 return;
1158
1159 const int tabIndex = m_tabWidget->indexOf(view);
1160
1161 switch (type)
1162 {
1163 case Konversation::tnfNormal:
1164 if (Preferences::self()->tabNotificationsMsgs())
1165 {
1166 if (Preferences::self()->tabNotificationsLeds())
1167 m_tabWidget->setTabIcon(tabIndex, images->getMsgsLed(true));
1168 if (Preferences::self()->tabNotificationsText())
1169 m_tabWidget->tabBar()->setTabTextColor(tabIndex, Preferences::self()->tabNotificationsMsgsColor());
1170 }
1171 break;
1172
1173 case Konversation::tnfPrivate:
1174 if (Preferences::self()->tabNotificationsPrivate())
1175 {
1176 if (Preferences::self()->tabNotificationsLeds())
1177 m_tabWidget->setTabIcon(tabIndex, images->getPrivateLed(true));
1178 if (Preferences::self()->tabNotificationsText())
1179 m_tabWidget->tabBar()->setTabTextColor(tabIndex, Preferences::self()->tabNotificationsPrivateColor());
1180 }
1181 break;
1182
1183 case Konversation::tnfSystem:
1184 if (Preferences::self()->tabNotificationsSystem())
1185 {
1186 if (Preferences::self()->tabNotificationsLeds())
1187 m_tabWidget->setTabIcon(tabIndex, images->getSystemLed(true));
1188 if (Preferences::self()->tabNotificationsText())
1189 m_tabWidget->tabBar()->setTabTextColor(tabIndex, Preferences::self()->tabNotificationsSystemColor());
1190 }
1191 break;
1192
1193 case Konversation::tnfControl:
1194 if (Preferences::self()->tabNotificationsEvents())
1195 {
1196 if (Preferences::self()->tabNotificationsLeds())
1197 m_tabWidget->setTabIcon(tabIndex, images->getEventsLed());
1198 if (Preferences::self()->tabNotificationsText())
1199 m_tabWidget->tabBar()->setTabTextColor(tabIndex, Preferences::self()->tabNotificationsEventsColor());
1200 }
1201 break;
1202
1203 case Konversation::tnfNick:
1204 if (Preferences::self()->tabNotificationsNick())
1205 {
1206 if (Preferences::self()->tabNotificationsOverride() && Preferences::self()->highlightNick())
1207 {
1208 if (Preferences::self()->tabNotificationsLeds())
1209 m_tabWidget->setTabIcon(tabIndex, images->getLed(Preferences::self()->highlightNickColor(),true));
1210 if (Preferences::self()->tabNotificationsText())
1211 m_tabWidget->tabBar()->setTabTextColor(tabIndex, Preferences::self()->highlightNickColor());
1212 }
1213 else
1214 {
1215 if (Preferences::self()->tabNotificationsLeds())
1216 m_tabWidget->setTabIcon(tabIndex, images->getNickLed());
1217 if (Preferences::self()->tabNotificationsText())
1218 m_tabWidget->tabBar()->setTabTextColor(tabIndex, Preferences::self()->tabNotificationsNickColor());
1219 }
1220 }
1221 else
1222 {
1223 setViewNotification(view,Konversation::tnfNormal);
1224 }
1225 break;
1226
1227 case Konversation::tnfHighlight:
1228 if (Preferences::self()->tabNotificationsHighlights())
1229 {
1230 if (Preferences::self()->tabNotificationsOverride() && view->highlightColor().isValid())
1231 {
1232 if (Preferences::self()->tabNotificationsLeds())
1233 m_tabWidget->setTabIcon(tabIndex, images->getLed(view->highlightColor(),true));
1234 if (Preferences::self()->tabNotificationsText())
1235 m_tabWidget->tabBar()->setTabTextColor(tabIndex, view->highlightColor());
1236 }
1237 else
1238 {
1239 if (Preferences::self()->tabNotificationsLeds())
1240 m_tabWidget->setTabIcon(tabIndex, images->getHighlightsLed());
1241 if (Preferences::self()->tabNotificationsText())
1242 m_tabWidget->tabBar()->setTabTextColor(tabIndex, Preferences::self()->tabNotificationsHighlightsColor());
1243 }
1244 }
1245 else
1246 {
1247 setViewNotification(view,Konversation::tnfNormal);
1248 }
1249 break;
1250
1251 default:
1252 break;
1253 }
1254
1255 const QModelIndex& idx = indexForView(view);
1256 Q_EMIT dataChanged(idx, idx, QVector<int> { Qt::DecorationRole, ColorRole });
1257 }
1258
unsetViewNotification(ChatWindow * view)1259 void ViewContainer::unsetViewNotification(ChatWindow* view)
1260 {
1261 if (!m_tabWidget) return;
1262
1263 const int tabIndex = m_tabWidget->indexOf(view);
1264 if (Preferences::self()->tabNotificationsLeds())
1265 {
1266 switch (view->getType())
1267 {
1268 case ChatWindow::Channel:
1269 case ChatWindow::DccChat:
1270 m_tabWidget->setTabIcon(tabIndex, images->getMsgsLed(false));
1271 break;
1272
1273 case ChatWindow::Query:
1274 m_tabWidget->setTabIcon(tabIndex, images->getPrivateLed(false));
1275 break;
1276
1277 case ChatWindow::Status:
1278 m_tabWidget->setTabIcon(tabIndex, images->getServerLed(false));
1279 break;
1280
1281 default:
1282 m_tabWidget->setTabIcon(tabIndex, images->getSystemLed(false));
1283 break;
1284 }
1285 }
1286
1287 QColor textColor = m_window->palette().windowText().color();
1288
1289 if (view->getType() == ChatWindow::Channel)
1290 {
1291 auto* channel = static_cast<Channel*>(view);
1292
1293 if (!channel->isJoined())
1294 textColor = m_tabWidget->palette().color(QPalette::Disabled, QPalette::Text);
1295 }
1296 else if (view->getType() == ChatWindow::Query)
1297 {
1298 if (!view->getServer()->isConnected())
1299 textColor = m_tabWidget->palette().color(QPalette::Disabled, QPalette::Text);
1300 }
1301
1302 m_tabWidget->tabBar()->setTabTextColor(tabIndex, textColor);
1303
1304 const QModelIndex& idx = indexForView(view);
1305 Q_EMIT dataChanged(idx, idx, QVector<int> { Qt::DecorationRole, ColorRole, DisabledRole });
1306
1307 m_activeViewOrderList.removeAll(view);
1308 }
1309
toggleViewNotifications()1310 void ViewContainer::toggleViewNotifications()
1311 {
1312 ChatWindow* view = nullptr;
1313
1314 if (m_popupViewIndex == -1)
1315 view = qobject_cast<ChatWindow*>(m_tabWidget->currentWidget());
1316 else
1317 view = qobject_cast<ChatWindow*>(m_tabWidget->widget(m_popupViewIndex));
1318
1319 if (view)
1320 {
1321 if (!view->notificationsEnabled())
1322 {
1323 view->setNotificationsEnabled(true);
1324 updateViews();
1325 auto* action = qobject_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("tab_notifications")));
1326 if (action) action->setChecked(view->notificationsEnabled());
1327 }
1328 else
1329 {
1330 view->setNotificationsEnabled(false);
1331 unsetViewNotification(view);
1332 auto* action = qobject_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("tab_notifications")));
1333 if (action) action->setChecked(view->notificationsEnabled());
1334 }
1335 }
1336
1337 m_popupViewIndex = -1;
1338 }
1339
toggleAutoJoin()1340 void ViewContainer::toggleAutoJoin()
1341 {
1342 Channel* channel = nullptr;
1343
1344 if (m_popupViewIndex == -1)
1345 channel = qobject_cast<Channel*>(m_tabWidget->currentWidget());
1346 else
1347 channel = qobject_cast<Channel*>(m_tabWidget->widget(m_popupViewIndex));
1348
1349 if (channel && channel->getType() == ChatWindow::Channel)
1350 {
1351 bool autoJoin = channel->autoJoin();
1352
1353 channel->setAutoJoin(!autoJoin);
1354
1355 Q_EMIT autoJoinToggled(channel->getServer()->getServerGroup());
1356 }
1357
1358 m_popupViewIndex = -1;
1359 }
1360
toggleConnectOnStartup()1361 void ViewContainer::toggleConnectOnStartup()
1362 {
1363 ChatWindow* view = nullptr;
1364
1365 if (m_popupViewIndex == -1)
1366 view = qobject_cast<ChatWindow*>(m_tabWidget->currentWidget());
1367 else
1368 view = qobject_cast<ChatWindow*>(m_tabWidget->widget(m_popupViewIndex));
1369
1370 if (view && view->getType() == ChatWindow::Status)
1371 {
1372 Server* server = view->getServer();
1373
1374 if (server)
1375 {
1376 Konversation::ServerGroupSettingsPtr settings = server->getConnectionSettings().serverGroup();
1377 bool autoConnect = settings->autoConnectEnabled();
1378 settings->setAutoConnectEnabled(!autoConnect);
1379
1380 Q_EMIT autoConnectOnStartupToggled(settings);
1381 }
1382 }
1383
1384 m_popupViewIndex = -1;
1385 }
1386
addView(ChatWindow * view,const QString & label,bool weinitiated)1387 void ViewContainer::addView(ChatWindow* view, const QString& label, bool weinitiated)
1388 {
1389 int placement = insertIndex(view);
1390 QIcon iconSet;
1391
1392 if (Preferences::self()->closeButtons() && m_viewTree)
1393 iconSet = QIcon::fromTheme(QStringLiteral("dialog-close"));
1394
1395 connect(Application::instance(), &Application::appearanceChanged, view, &ChatWindow::updateAppearance);
1396 connect(view, &ChatWindow::setStatusBarTempText, this, &ViewContainer::setStatusBarTempText);
1397 connect(view, &ChatWindow::clearStatusBarTempText, this, &ViewContainer::clearStatusBarTempText);
1398 connect(view, &ChatWindow::closing, this, &ViewContainer::cleanupAfterClose);
1399 connect(view, &ChatWindow::showView, this, &ViewContainer::showView);
1400 connect(view, &ChatWindow::windowActivationRequested, m_window, &MainWindow::activateAndRaiseWindow);
1401
1402 switch (view->getType())
1403 {
1404 case ChatWindow::Channel:
1405 if (Preferences::self()->tabNotificationsLeds())
1406 iconSet = images->getMsgsLed(false);
1407
1408 break;
1409
1410 case ChatWindow::RawLog:
1411 if (Preferences::self()->tabNotificationsLeds())
1412 iconSet = images->getSystemLed(false);
1413
1414 break;
1415
1416 case ChatWindow::Query:
1417 if (Preferences::self()->tabNotificationsLeds())
1418 iconSet = images->getPrivateLed(false);
1419
1420 break;
1421
1422 case ChatWindow::DccChat:
1423 if (Preferences::self()->tabNotificationsLeds())
1424 iconSet = images->getMsgsLed(false);
1425
1426 break;
1427
1428 case ChatWindow::Status:
1429 if (Preferences::self()->tabNotificationsLeds())
1430 iconSet = images->getServerLed(false);
1431
1432 break;
1433
1434 case ChatWindow::ChannelList:
1435 if (Preferences::self()->tabNotificationsLeds())
1436 iconSet = images->getSystemLed(false);
1437
1438 break;
1439
1440 default:
1441 if (Preferences::self()->tabNotificationsLeds())
1442 iconSet = images->getSystemLed(false);
1443 break;
1444 }
1445
1446 if (view->isTopLevelView()) {
1447 int diff = 0;
1448
1449 for (int i = 0; i < placement; ++i) {
1450 if (!static_cast<ChatWindow*>(m_tabWidget->widget(i))->isTopLevelView()) {
1451 ++diff;
1452 }
1453 }
1454
1455 beginInsertRows(QModelIndex(), placement - diff, placement - diff);
1456 } else {
1457 int statusViewIndex = m_tabWidget->indexOf(view->getServer()->getStatusView());
1458 const QModelIndex idx = indexForView(view->getServer()->getStatusView());
1459
1460 if (m_viewTree) {
1461 m_viewTree->setExpanded(idx, true);
1462 }
1463
1464 beginInsertRows(idx, placement - statusViewIndex - 1, placement - statusViewIndex - 1);
1465 }
1466
1467 m_tabWidget->insertTab(placement, view, iconSet, QString(label).replace(QLatin1Char('&'), QLatin1String("&&")));
1468
1469 endInsertRows();
1470
1471 m_vbox->show();
1472
1473 // Check, if user was typing in old input line
1474 bool doBringToFront=false;
1475
1476 if (Preferences::self()->focusNewQueries() && view->getType()==ChatWindow::Query && !weinitiated)
1477 doBringToFront = true;
1478
1479 if (Preferences::self()->bringToFront() && view->getType()!=ChatWindow::RawLog)
1480 doBringToFront = true;
1481
1482 // make sure that bring to front only works when the user wasn't typing something
1483 if (m_frontView && view->getType() != ChatWindow::UrlCatcher && view->getType() != ChatWindow::Konsole)
1484 {
1485 if (!m_frontView->getTextInLine().isEmpty())
1486 doBringToFront = false;
1487 }
1488
1489 if (doBringToFront) showView(view);
1490
1491 updateViewActions(m_tabWidget->currentIndex());
1492
1493 if (m_viewTree && m_tabWidget->count() == 1) {
1494 setViewTreeShown(true);
1495 }
1496 }
1497
insertIndex(ChatWindow * view)1498 int ViewContainer::insertIndex(ChatWindow* view)
1499 {
1500 int placement = m_tabWidget->count();
1501 ChatWindow::WindowType wtype;
1502 ChatWindow *tmp_ChatWindow;
1503
1504 // Please be careful about changing any of the grouping behavior in here,
1505 // because it needs to match up with the sorting behavior of the tree list,
1506 // otherwise they may become out of sync, wreaking havoc with the move
1507 // actions. Yes, this would do well with a more reliable approach in the
1508 // future. Then again, while this is ugly, it's also very fast.
1509 switch (view->getType())
1510 {
1511 case ChatWindow::Channel:
1512 for (int sindex = 0; sindex < m_tabWidget->count(); sindex++)
1513 {
1514 tmp_ChatWindow = static_cast<ChatWindow *>(m_tabWidget->widget(sindex));
1515
1516 if (tmp_ChatWindow->getType() == ChatWindow::Status && tmp_ChatWindow->getServer() == view->getServer())
1517 {
1518 for (int index = sindex + 1; index < m_tabWidget->count(); index++)
1519 {
1520 tmp_ChatWindow = static_cast<ChatWindow *>(m_tabWidget->widget(index));
1521 wtype = tmp_ChatWindow->getType();
1522
1523 if (wtype != ChatWindow::Channel && wtype != ChatWindow::RawLog)
1524 {
1525 placement = index;
1526 break;
1527 }
1528 }
1529
1530 break;
1531 }
1532 }
1533
1534 break;
1535
1536 case ChatWindow::RawLog:
1537 for (int sindex = 0; sindex < m_tabWidget->count(); sindex++)
1538 {
1539 tmp_ChatWindow = static_cast<ChatWindow *>(m_tabWidget->widget(sindex));
1540
1541 if (tmp_ChatWindow->getType() == ChatWindow::Status && tmp_ChatWindow->getServer() == view->getServer())
1542 {
1543 placement = sindex + 1;
1544 break;
1545 }
1546 }
1547
1548 break;
1549
1550 case ChatWindow::Query:
1551 for (int sindex = 0; sindex < m_tabWidget->count(); sindex++)
1552 {
1553 tmp_ChatWindow = static_cast<ChatWindow *>(m_tabWidget->widget(sindex));
1554
1555 if (tmp_ChatWindow->getType() == ChatWindow::Status && tmp_ChatWindow->getServer() == view->getServer())
1556 {
1557 for (int index = sindex + 1; index < m_tabWidget->count(); index++)
1558 {
1559 tmp_ChatWindow = static_cast<ChatWindow *>(m_tabWidget->widget(index));
1560 wtype = tmp_ChatWindow->getType();
1561
1562 if (wtype != ChatWindow::Channel && wtype != ChatWindow::RawLog && wtype != ChatWindow::Query)
1563 {
1564 placement = index;
1565 break;
1566 }
1567 }
1568
1569 break;
1570 }
1571 }
1572
1573 break;
1574
1575 case ChatWindow::DccChat:
1576 for (int sindex = 0; sindex < m_tabWidget->count(); sindex++)
1577 {
1578 tmp_ChatWindow = static_cast<ChatWindow*>(m_tabWidget->widget(sindex));
1579 wtype = tmp_ChatWindow->getType();
1580
1581 if (wtype != ChatWindow::Status && wtype != ChatWindow::Channel
1582 && wtype != ChatWindow::RawLog && wtype != ChatWindow::Query
1583 && wtype != ChatWindow::DccChat && wtype != ChatWindow::ChannelList)
1584 {
1585 placement = sindex;
1586 break;
1587 }
1588 }
1589 break;
1590
1591 case ChatWindow::Status:
1592 if (m_viewTree)
1593 {
1594 for (int sindex = 0; sindex < m_tabWidget->count(); sindex++)
1595 {
1596 tmp_ChatWindow = static_cast<ChatWindow *>(m_tabWidget->widget(sindex));
1597
1598 if (tmp_ChatWindow->getType() != ChatWindow::Channel
1599 && tmp_ChatWindow->getType() != ChatWindow::Status
1600 && tmp_ChatWindow->getType() != ChatWindow::RawLog
1601 && tmp_ChatWindow->getType() != ChatWindow::Query
1602 && tmp_ChatWindow->getType() != ChatWindow::DccChat)
1603 {
1604 placement = sindex;
1605 break;
1606 }
1607 }
1608 }
1609 break;
1610
1611 case ChatWindow::ChannelList:
1612 for (int sindex = 0; sindex < m_tabWidget->count(); sindex++)
1613 {
1614 tmp_ChatWindow = static_cast<ChatWindow *>(m_tabWidget->widget(sindex));
1615
1616 if (tmp_ChatWindow->getServer() == view->getServer())
1617 placement = sindex + 1;
1618 }
1619
1620 break;
1621
1622 default:
1623 break;
1624 }
1625
1626 return placement;
1627 }
1628
unclutterTabs()1629 void ViewContainer::unclutterTabs()
1630 {
1631 if (!m_tabWidget || !m_tabWidget->count()) {
1632 return;
1633 }
1634
1635 beginResetModel();
1636
1637 m_tabWidget->blockSignals(true);
1638
1639 QWidget* currentView = m_tabWidget->currentWidget();
1640
1641 QList<ChatWindow *> topLevelViews;
1642 QList<ChatWindow *> nonTopLevelViews;
1643
1644 while (m_tabWidget->count()) {
1645 auto* view = static_cast<ChatWindow* >(m_tabWidget->widget(0));
1646 if (view->isTopLevelView()) {
1647 topLevelViews << view;
1648 } else {
1649 nonTopLevelViews << view;
1650 }
1651 m_tabWidget->removeTab(0);
1652 }
1653
1654 for (ChatWindow *view : qAsConst(topLevelViews)) {
1655 m_tabWidget->insertTab(insertIndex(view), view, QIcon(), view->getName().replace(QLatin1Char('&'), QLatin1String("&&")));
1656 }
1657
1658 for (ChatWindow *view : qAsConst(nonTopLevelViews)) {
1659 m_tabWidget->insertTab(insertIndex(view), view, QIcon(), view->getName().replace(QLatin1Char('&'), QLatin1String("&&")));
1660 }
1661
1662 updateViews();
1663
1664 if (currentView) {
1665 m_tabWidget->setCurrentWidget(currentView);
1666 }
1667
1668 m_tabWidget->blockSignals(false);
1669
1670 endResetModel();
1671
1672 viewSwitched(m_tabWidget->currentIndex());
1673 }
1674
viewSwitched(int newIndex)1675 void ViewContainer::viewSwitched(int newIndex)
1676 {
1677 auto* view = qobject_cast<ChatWindow*>(m_tabWidget->widget(newIndex));
1678 if (!view) return;
1679
1680 m_lastFocusedView = m_currentView;
1681 m_currentView = view;
1682
1683 const QModelIndex &idx = indexForView(view);
1684 Q_EMIT viewChanged(idx);
1685
1686 if (m_frontView)
1687 {
1688 m_frontView->resetTabNotification();
1689
1690 disconnect(m_frontView.data(), &ChatWindow::updateInfo, this, &ViewContainer::setStatusBarInfoLabel);
1691
1692 if (Preferences::self()->automaticRememberLine() && m_frontView->getTextView() != nullptr)
1693 m_frontView->getTextView()->insertRememberLine();
1694 }
1695
1696 m_frontView = nullptr;
1697 m_searchView = nullptr;
1698
1699 setFrontServer(view->getServer());
1700
1701 // display this server's lag time
1702 if (m_frontServer)
1703 {
1704 Q_EMIT updateStatusBarSSLLabel(m_frontServer);
1705 Q_EMIT updateStatusBarLagLabel(m_frontServer, m_frontServer->getLag());
1706 }
1707
1708 Q_EMIT clearStatusBarTempText();
1709
1710 updateFrontView();
1711
1712 unsetViewNotification(view);
1713
1714 view->resetTabNotification();
1715
1716 if (!m_viewTree || !m_viewTree->hasFocus()) view->adjustFocus();
1717
1718 if (view->getTextView() != nullptr) view->getTextView()->cancelRememberLine();
1719
1720 updateViewEncoding(view);
1721
1722 QString tabName = Konversation::removeIrcMarkup(view->getName());
1723
1724 if (tabName != QLatin1String("ChatWindowObject"))
1725 Q_EMIT setWindowCaption(tabName);
1726 else
1727 Q_EMIT setWindowCaption(QString());
1728 }
1729
showView(ChatWindow * view)1730 void ViewContainer::showView(ChatWindow* view)
1731 {
1732 // Don't bring Tab to front if TabWidget is hidden. Otherwise QT gets confused
1733 // and shows the Tab as active but will display the wrong pane
1734 if (m_tabWidget->isVisible()) {
1735 m_tabWidget->setCurrentIndex(m_tabWidget->indexOf(view));
1736 }
1737 }
1738
goToView(int page)1739 void ViewContainer::goToView(int page)
1740 {
1741 if (page == m_tabWidget->currentIndex())
1742 return;
1743
1744 if (page > m_tabWidget->count())
1745 return;
1746
1747 if (page >= m_tabWidget->count())
1748 page = 0;
1749 else if (page < 0)
1750 page = m_tabWidget->count() - 1;
1751
1752 if (page >= 0)
1753 m_tabWidget->setCurrentIndex(page);
1754
1755 m_popupViewIndex = -1;
1756 }
1757
showNextView()1758 void ViewContainer::showNextView()
1759 {
1760 goToView(m_tabWidget->currentIndex()+1);
1761 }
1762
showPreviousView()1763 void ViewContainer::showPreviousView()
1764 {
1765 goToView(m_tabWidget->currentIndex()-1);
1766 }
1767
showNextActiveView()1768 void ViewContainer::showNextActiveView()
1769 {
1770 m_window->activateAndRaiseWindow();
1771
1772 if (!m_activeViewOrderList.isEmpty())
1773 {
1774 ChatWindow* prev = m_activeViewOrderList.first();
1775 ChatWindow* view = prev;
1776
1777 for (ChatWindow* activeView : qAsConst(m_activeViewOrderList)) {
1778 if (activeView->currentTabNotification() < prev->currentTabNotification()) {
1779 view = activeView;
1780 }
1781 }
1782
1783 m_tabWidget->setCurrentIndex(m_tabWidget->indexOf(view));
1784 }
1785 }
1786
showLastFocusedView()1787 void ViewContainer::showLastFocusedView()
1788 {
1789 if (m_lastFocusedView)
1790 showView(m_lastFocusedView);
1791 }
1792
canMoveViewLeft() const1793 bool ViewContainer::canMoveViewLeft() const
1794 {
1795 if (!m_tabWidget || !m_tabWidget->count()) {
1796 return false;
1797 }
1798
1799 int index = (m_popupViewIndex != -1) ? m_popupViewIndex : m_tabWidget->currentIndex();
1800
1801 auto* view = static_cast<ChatWindow*>(m_tabWidget->widget(index));
1802
1803 if (view->isTopLevelView() && index > 0) {
1804 return true;
1805 } else if (!view->isTopLevelView()) {
1806 ChatWindow* statusView = view->getServer()->getStatusView();
1807 int statusViewIndex = m_tabWidget->indexOf(statusView);
1808
1809 index = index - statusViewIndex - 1;
1810
1811 return (index > 0);
1812 }
1813
1814 return false;
1815 }
1816
canMoveViewRight() const1817 bool ViewContainer::canMoveViewRight() const
1818 {
1819 if (!m_tabWidget || !m_tabWidget->count()) {
1820 return false;
1821 }
1822
1823 int index = (m_popupViewIndex != -1) ? m_popupViewIndex : m_tabWidget->currentIndex();
1824
1825 auto* view = static_cast<ChatWindow*>(m_tabWidget->widget(index));
1826
1827 if (view->isTopLevelView()) {
1828 int lastTopLevelView = -1;
1829
1830 for (int i = m_tabWidget->count() - 1; i >= index; --i) {
1831 if (static_cast<ChatWindow*>(m_tabWidget->widget(i))->isTopLevelView()) {
1832 lastTopLevelView = i;
1833 break;
1834 }
1835 }
1836
1837 return (index != lastTopLevelView);
1838 } else if (!view->isTopLevelView()) {
1839 view = qobject_cast<ChatWindow*>(m_tabWidget->widget(index + 1));
1840
1841 return (view && !view->isTopLevelView());
1842 }
1843
1844 return false;
1845 }
1846
moveViewLeft()1847 void ViewContainer::moveViewLeft()
1848 {
1849 if (!m_tabWidget || !m_tabWidget->count()) {
1850 return;
1851 }
1852
1853 int tabIndex = (m_popupViewIndex != -1) ? m_popupViewIndex : m_tabWidget->currentIndex();
1854 auto* view = static_cast<ChatWindow*>(m_tabWidget->widget(tabIndex));
1855 const QModelIndex idx = indexForView(view);
1856
1857 if (view->isTopLevelView()) {
1858 const QModelIndex aboveIdx = index(idx.row() - 1, 0);
1859 int aboveTabIndex = m_tabWidget->indexOf(static_cast<ChatWindow*>(aboveIdx.internalPointer()));
1860
1861 beginMoveRows(QModelIndex(), idx.row(), idx.row(), QModelIndex(), aboveIdx.row());
1862
1863 m_tabWidget->blockSignals(true);
1864
1865 if (view->getType() == ChatWindow::Status) {
1866 for (int i = m_tabWidget->count() - 1; i > tabIndex; --i) {
1867 auto* tab = static_cast<ChatWindow*>(m_tabWidget->widget(i));
1868
1869 if (!tab->isTopLevelView() && tab->getServer()
1870 && tab->getServer()->getStatusView()
1871 && tab->getServer()->getStatusView() == view) {
1872 m_tabWidget->tabBar()->moveTab(i, aboveTabIndex);
1873 ++tabIndex;
1874 ++i;
1875 }
1876 }
1877 }
1878
1879 m_tabWidget->tabBar()->moveTab(tabIndex, aboveTabIndex);
1880
1881 m_tabWidget->blockSignals(false);
1882
1883 endMoveRows();
1884
1885 viewSwitched(m_tabWidget->currentIndex());
1886 } else {
1887 beginMoveRows(idx.parent(), idx.row(), idx.row(), idx.parent(), idx.row() - 1);
1888
1889 m_tabWidget->blockSignals(true);
1890 m_tabWidget->tabBar()->moveTab(tabIndex, tabIndex - 1);
1891 m_tabWidget->blockSignals(false);
1892
1893 endMoveRows();
1894
1895 viewSwitched(m_tabWidget->currentIndex());
1896 }
1897 }
1898
moveViewRight()1899 void ViewContainer::moveViewRight()
1900 {
1901 if (!m_tabWidget || !m_tabWidget->count()) {
1902 return;
1903 }
1904
1905 int tabIndex = (m_popupViewIndex != -1) ? m_popupViewIndex : m_tabWidget->currentIndex();
1906 auto* view = static_cast<ChatWindow*>(m_tabWidget->widget(tabIndex));
1907 const QModelIndex idx = indexForView(view);
1908
1909 if (view->isTopLevelView()) {
1910 const QModelIndex belowIdx = index(idx.row() + 1, 0);
1911 int belowTabIndex = m_tabWidget->indexOf(static_cast<ChatWindow*>(belowIdx.internalPointer()));
1912 int children = rowCount(belowIdx);
1913
1914 if (children) {
1915 belowTabIndex = m_tabWidget->indexOf(static_cast<ChatWindow*>(index(children - 1, 0, belowIdx).internalPointer()));
1916 }
1917
1918 beginMoveRows(QModelIndex(), idx.row(), idx.row(), QModelIndex(), belowIdx.row() + 1);
1919
1920 m_tabWidget->blockSignals(true);
1921
1922 m_tabWidget->tabBar()->moveTab(tabIndex, belowTabIndex);
1923
1924 if (view->getType() == ChatWindow::Status) {
1925 auto* tab = static_cast<ChatWindow*>(m_tabWidget->widget(tabIndex));
1926
1927 while (!tab->isTopLevelView() && tab->getServer()
1928 && tab->getServer()->getStatusView()
1929 && tab->getServer()->getStatusView() == view) {
1930
1931 m_tabWidget->tabBar()->moveTab(tabIndex, belowTabIndex);
1932
1933 tab = static_cast<ChatWindow*>(m_tabWidget->widget(tabIndex));
1934 }
1935 }
1936
1937 m_tabWidget->blockSignals(false);
1938
1939 endMoveRows();
1940
1941 viewSwitched(m_tabWidget->currentIndex());
1942 } else {
1943 beginMoveRows(idx.parent(), idx.row(), idx.row(), idx.parent(), idx.row() + 2);
1944
1945 m_tabWidget->blockSignals(true);
1946 m_tabWidget->tabBar()->moveTab(tabIndex, tabIndex + 1);
1947 m_tabWidget->blockSignals(false);
1948
1949 endMoveRows();
1950
1951 viewSwitched(m_tabWidget->currentIndex());
1952 }
1953 }
1954
closeView(int view)1955 void ViewContainer::closeView(int view)
1956 {
1957 auto* viewToClose = qobject_cast<ChatWindow*>(m_tabWidget->widget(view));
1958
1959 closeView(viewToClose);
1960 }
1961
closeView(ChatWindow * view)1962 void ViewContainer::closeView(ChatWindow* view)
1963 {
1964 if (view)
1965 {
1966 ChatWindow::WindowType viewType = view->getType();
1967
1968 switch (viewType)
1969 {
1970 case ChatWindow::DccTransferPanel:
1971 closeDccPanel();
1972 break;
1973 case ChatWindow::UrlCatcher:
1974 closeUrlCatcher();
1975 break;
1976 case ChatWindow::NicksOnline:
1977 closeNicksOnlinePanel();
1978 break;
1979 default:
1980 view->closeYourself();
1981 break;
1982 }
1983 }
1984 }
1985
cleanupAfterClose(ChatWindow * view)1986 void ViewContainer::cleanupAfterClose(ChatWindow* view)
1987 {
1988 if (view == m_frontView) m_frontView = nullptr;
1989
1990 if (view == m_lastFocusedView)
1991 {
1992 QAction* action = actionCollection()->action(QStringLiteral("last_focused_tab"));
1993 if (action) action->setEnabled(false);
1994 }
1995
1996 if (m_tabWidget)
1997 {
1998 const int tabIndex = m_tabWidget->indexOf(view);
1999
2000 if (tabIndex != -1) {
2001 if (tabIndex == m_popupViewIndex)
2002 m_popupViewIndex = -1;
2003
2004 m_tabWidget->blockSignals(true);
2005
2006 const QModelIndex& idx = indexForView(view);
2007
2008 beginRemoveRows(idx.parent(), idx.row(), idx.row());
2009
2010 if (view->getType() == ChatWindow::Status) {
2011 for (int i = m_tabWidget->count() - 1; i > tabIndex; --i) {
2012 const auto* tab = static_cast<ChatWindow*>(m_tabWidget->widget(i));
2013
2014 if (!tab->isTopLevelView() && tab->getServer()
2015 && tab->getServer()->getStatusView()
2016 && tab->getServer()->getStatusView() == view) {
2017 m_tabWidget->removeTab(i);
2018 }
2019 }
2020 }
2021
2022 m_tabWidget->removeTab(tabIndex);
2023
2024 endRemoveRows();
2025
2026 m_tabWidget->blockSignals(false);
2027
2028 viewSwitched(m_tabWidget->currentIndex());
2029 }
2030
2031 if (m_tabWidget->count() <= 0)
2032 {
2033 m_saveSplitterSizesLock = true;
2034 m_vbox->hide();
2035 Q_EMIT resetStatusBar();
2036 Q_EMIT setWindowCaption(QString());
2037 updateViewActions(-1);
2038 }
2039 }
2040
2041 // Remove the view from the active view list if it's still on it
2042 m_activeViewOrderList.removeAll(view);
2043
2044 if (view->getType() == ChatWindow::Query)
2045 --m_queryViewCount;
2046
2047 if (m_queryViewCount == 0 && actionCollection())
2048 {
2049 QAction* action = actionCollection()->action(QStringLiteral("close_queries"));
2050 if (action) action->setEnabled(false);
2051 }
2052
2053 if (!m_tabWidget->count() && m_viewTree) {
2054 setViewTreeShown(false);
2055 }
2056 }
2057
closeViewMiddleClick(int view)2058 void ViewContainer::closeViewMiddleClick(int view)
2059 {
2060 if (Preferences::self()->middleClickClose())
2061 closeView(view);
2062 }
2063
renameKonsole()2064 void ViewContainer::renameKonsole()
2065 {
2066 bool ok = false;
2067
2068 if (!m_tabWidget)
2069 return;
2070
2071 int popup = m_popupViewIndex ? m_popupViewIndex : m_tabWidget->currentIndex();
2072
2073 QString label = QInputDialog::getText(m_tabWidget->widget(popup),
2074 i18n("Rename Tab"),
2075 i18n("Enter new tab name:"),
2076 QLineEdit::Normal,
2077 m_tabWidget->tabText(popup),
2078 &ok);
2079
2080 if (ok)
2081 {
2082 auto* view = qobject_cast<KonsolePanel*>(m_tabWidget->widget(popup));
2083
2084 if (!view) return;
2085
2086 view->setName(label);
2087
2088 m_tabWidget->setTabText(popup, label);
2089
2090 const QModelIndex& idx = indexForView(view);
2091 Q_EMIT dataChanged(idx, idx, QVector<int>{ Qt::DisplayRole });
2092
2093 if (popup == m_tabWidget->currentIndex())
2094 {
2095 Q_EMIT setStatusBarInfoLabel(label);
2096 Q_EMIT setWindowCaption(label);
2097 }
2098 }
2099 }
2100
closeCurrentView()2101 void ViewContainer::closeCurrentView()
2102 {
2103 if (m_popupViewIndex == -1)
2104 closeView(m_tabWidget->currentIndex());
2105 else
2106 closeView(m_popupViewIndex);
2107
2108 m_popupViewIndex = -1;
2109 }
2110
changeViewCharset(int index)2111 void ViewContainer::changeViewCharset(int index)
2112 {
2113 ChatWindow* chatWin;
2114
2115 if (m_popupViewIndex == -1)
2116 chatWin = qobject_cast<ChatWindow*>(m_tabWidget->currentWidget());
2117 else
2118 chatWin = qobject_cast<ChatWindow*>(m_tabWidget->widget(m_popupViewIndex));
2119
2120 if (chatWin)
2121 {
2122 if (index == 0)
2123 chatWin->setChannelEncoding(QString());
2124 else
2125 chatWin->setChannelEncoding(Konversation::IRCCharsets::self()->availableEncodingShortNames()[index - 1]);
2126 }
2127
2128 m_popupViewIndex = -1;
2129 }
2130
updateViewEncoding(ChatWindow * view)2131 void ViewContainer::updateViewEncoding(ChatWindow* view)
2132 {
2133 if (view)
2134 {
2135 ChatWindow::WindowType viewType = view->getType();
2136 auto* codecAction = qobject_cast<KSelectAction*>(actionCollection()->action(QStringLiteral("tab_encoding")));
2137
2138 if (codecAction)
2139 {
2140 if(viewType == ChatWindow::Channel || viewType == ChatWindow::Query || viewType == ChatWindow::Status)
2141 {
2142 codecAction->setEnabled(view->isChannelEncodingSupported());
2143 QString encoding = view->getChannelEncoding();
2144
2145 if (view->getServer())
2146 {
2147 codecAction->changeItem(0, i18nc("Default encoding", "Default ( %1 )", view->getServer()->getIdentity()->getCodecName()));
2148 }
2149
2150 if (encoding.isEmpty())
2151 {
2152 codecAction->setCurrentItem(0);
2153 }
2154 else
2155 {
2156 codecAction->setCurrentItem(Konversation::IRCCharsets::self()->shortNameToIndex(encoding) + 1);
2157 }
2158 }
2159 else
2160 {
2161 codecAction->setEnabled(false);
2162 }
2163 }
2164 }
2165 }
2166
showViewContextMenu(QWidget * tab,const QPoint & pos)2167 void ViewContainer::showViewContextMenu(QWidget* tab, const QPoint& pos)
2168 {
2169 if (!tab) {
2170 return;
2171 }
2172
2173 auto* view = static_cast<ChatWindow*>(tab);
2174
2175 m_popupViewIndex = m_tabWidget->indexOf(tab);
2176
2177 updateViewActions(m_popupViewIndex);
2178 auto* menu = qobject_cast<QMenu*>(m_window->guiFactory()->container(QStringLiteral("tabContextMenu"), m_window));
2179
2180 if (!menu) return;
2181
2182 auto* autoJoinAction = qobject_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("tab_autojoin")));
2183 auto* autoConnectAction = qobject_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("tab_autoconnect")));
2184 QAction* rejoinAction = actionCollection()->action(QStringLiteral("rejoin_channel"));
2185 QAction* closeAction = actionCollection()->action(QStringLiteral("close_tab"));
2186
2187 auto* renameAct = new QAction(this);
2188 renameAct->setText(i18n("&Rename Tab..."));
2189 connect(renameAct, &QAction::triggered, this, &ViewContainer::renameKonsole);
2190
2191 ChatWindow::WindowType viewType = view->getType();
2192
2193 updateViewEncoding(view);
2194
2195 if (viewType == ChatWindow::Channel)
2196 {
2197 QAction* action = actionCollection()->action(QStringLiteral("tab_encoding"));
2198 menu->insertAction(action, autoJoinAction);
2199
2200 auto* channel = static_cast<Channel*>(view);
2201 if (channel->rejoinable() && rejoinAction)
2202 {
2203 menu->insertAction(closeAction, rejoinAction);
2204 rejoinAction->setEnabled(true);
2205 }
2206 }
2207
2208 if (viewType == ChatWindow::Konsole)
2209 {
2210 QAction* action = actionCollection()->action(QStringLiteral("tab_encoding"));
2211 menu->insertAction(action, renameAct);
2212 }
2213
2214 if (viewType == ChatWindow::Status)
2215 {
2216 QAction* action = actionCollection()->action(QStringLiteral("tab_encoding"));
2217 menu->insertAction(action, autoConnectAction);
2218
2219 QList<QAction *> serverActions;
2220
2221 action = actionCollection()->action(QStringLiteral("disconnect_server"));
2222 if (action) serverActions.append(action);
2223 action = actionCollection()->action(QStringLiteral("reconnect_server"));
2224 if (action) serverActions.append(action);
2225 action = actionCollection()->action(QStringLiteral("join_channel"));
2226 if (action) serverActions.append(action);
2227 // TODO FIXME who wants to own this action?
2228 action = new QAction(this);
2229 action->setSeparator(true);
2230 if (action) serverActions.append(action);
2231 m_window->plugActionList(QStringLiteral("server_actions"), serverActions);
2232 m_contextServer = view->getServer();
2233 }
2234 else {
2235 m_contextServer = nullptr;
2236 }
2237
2238 const QModelIndex& idx = indexForView(view);
2239 Q_EMIT dataChanged(idx, idx, QVector<int>{ HighlightRole });
2240
2241 const QAction* action = menu->exec(pos);
2242
2243 m_popupViewIndex = -1;
2244
2245 menu->removeAction(autoJoinAction);
2246 menu->removeAction(autoConnectAction);
2247 menu->removeAction(rejoinAction);
2248 menu->removeAction(renameAct);
2249 m_window->unplugActionList(QStringLiteral("server_actions"));
2250
2251 Q_EMIT contextMenuClosed();
2252
2253 Q_EMIT dataChanged(idx, idx, QVector<int> { HighlightRole });
2254
2255 if (action != actionCollection()->action(QStringLiteral("close_tab"))) {
2256 updateViewEncoding(view);
2257 }
2258
2259 updateViewActions(m_tabWidget->currentIndex());
2260 }
2261
currentViewTitle() const2262 QString ViewContainer::currentViewTitle() const
2263 {
2264 if (m_frontServer)
2265 {
2266 if (m_frontView && m_frontView->getType() == ChatWindow::Channel)
2267 return m_frontView->getTitle();
2268 else
2269 return m_frontServer->getDisplayName();
2270 }
2271 else
2272 {
2273 return QString();
2274 }
2275 }
2276
currentViewURL(bool passNetwork)2277 QString ViewContainer::currentViewURL(bool passNetwork)
2278 {
2279 QString url;
2280
2281 if (m_frontServer && m_frontView)
2282 {
2283 updateFrontView();
2284
2285 url = m_frontView->getURI(passNetwork);
2286 }
2287
2288 return url;
2289 }
2290
getViewIndex(QWidget * widget) const2291 int ViewContainer::getViewIndex(QWidget* widget) const
2292 {
2293 return m_tabWidget->indexOf(widget);
2294 }
2295
getViewAt(int index) const2296 ChatWindow* ViewContainer::getViewAt(int index) const
2297 {
2298 return qobject_cast<ChatWindow*>(m_tabWidget->widget(index));
2299 }
2300
getChannelsURI() const2301 QList<QPair<QString,QString> > ViewContainer::getChannelsURI() const
2302 {
2303 QList<QPair<QString,QString> > URIList;
2304
2305 for (int i = 0; i < m_tabWidget->count(); ++i)
2306 {
2307 auto* view = static_cast<ChatWindow*>(m_tabWidget->widget(i));
2308
2309 if (view->getType() == ChatWindow::Channel)
2310 {
2311 QString uri = view->getURI();
2312 QString name = QStringLiteral("%1 (%2)")
2313 .arg(view->getName(), view->getServer()->getDisplayName());
2314 URIList += QPair<QString,QString>(name,uri);
2315 }
2316 }
2317
2318 return URIList;
2319 }
2320
clearView()2321 void ViewContainer::clearView()
2322 {
2323 if (m_frontView) m_frontView->getTextView()->clear();
2324 }
2325
clearAllViews()2326 void ViewContainer::clearAllViews()
2327 {
2328 for (int i = 0; i < m_tabWidget->count(); i++)
2329 static_cast<ChatWindow*>(m_tabWidget->widget(i))->clear();
2330 }
2331
findText()2332 void ViewContainer::findText()
2333 {
2334 if (!m_searchView)
2335 {
2336 KMessageBox::sorry(m_window,
2337 i18n("You can only search in text fields."),
2338 i18n("Find Text Information"));
2339 }
2340 else
2341 {
2342 m_searchView->getTextView()->findText();
2343 }
2344 }
2345
findNextText()2346 void ViewContainer::findNextText()
2347 {
2348 if (m_searchView)
2349 {
2350 m_searchView->getTextView()->findNextText();
2351 }
2352 }
2353
findPrevText()2354 void ViewContainer::findPrevText()
2355 {
2356 if (m_searchView)
2357 {
2358 m_searchView->getTextView()->findPreviousText();
2359 }
2360 }
2361
appendToFrontmost(const QString & type,const QString & message,ChatWindow * serverView,const QHash<QString,QString> & messageTags,bool parseURL)2362 void ViewContainer::appendToFrontmost(const QString& type, const QString& message, ChatWindow* serverView, const QHash<QString, QString> &messageTags, bool parseURL)
2363 {
2364 if (!m_tabWidget) return;
2365
2366 if (!serverView) // e.g. DCOP info call
2367 {
2368 if (m_frontView) // m_frontView == NULL if canBeFrontView() == false for active ChatWindow
2369 serverView = m_frontView->getServer()->getStatusView();
2370 else if (m_frontServer) // m_frontView == NULL && m_frontServer != NULL if ChannelListPanel is active.
2371 serverView = m_frontServer->getStatusView();
2372 }
2373
2374 // This might happen if canBeFrontView() is false for active ChatWindow
2375 // and the view does not belong to any server (e.g. DCC Status View).
2376 // Discard message in this case.
2377 if (!serverView) return;
2378
2379 updateFrontView();
2380
2381 if (!m_frontView || // Check if the m_frontView can actually display text or ...
2382 // if it does not belong to this server or...
2383 serverView->getServer()!=m_frontView->getServer() ||
2384 // if the user decided to force it.
2385 Preferences::self()->redirectServerAndAppMsgToStatusPane())
2386 {
2387 // if not, take server specified fallback view instead
2388 serverView->appendServerMessage(type, message, messageTags, parseURL);
2389 // FIXME: this signal should be sent from the status panel instead, so it
2390 // can be using the correct highlight color, would be more consistent
2391 // anyway!
2392 // FIXME newText(serverView,QString(),true);
2393 }
2394 else
2395 m_frontView->appendServerMessage(type, message, messageTags, parseURL);
2396 }
2397
insertCharacter()2398 void ViewContainer::insertCharacter()
2399 {
2400 QFont font;
2401
2402 if (Preferences::self()->customTextFont())
2403 font = Preferences::self()->textFont();
2404 else
2405 font = QFontDatabase::systemFont(QFontDatabase::GeneralFont);
2406
2407
2408 if (!m_insertCharDialog)
2409 {
2410 m_insertCharDialog = new Konversation::InsertCharDialog(font.family(), m_window);
2411 connect(m_insertCharDialog, &InsertCharDialog::insertChar, this, &ViewContainer::insertChar);
2412 }
2413
2414 m_insertCharDialog->setFont(font);
2415 m_insertCharDialog->show();
2416 }
2417
insertChar(uint chr)2418 void ViewContainer::insertChar(uint chr)
2419 {
2420 auto* view = qobject_cast<ChatWindow*>(m_tabWidget->currentWidget());
2421
2422 if (view) view->appendInputText(QString::fromUcs4(&chr, 1), true/*fromCursor*/);
2423 }
2424
insertIRCColor()2425 void ViewContainer::insertIRCColor()
2426 {
2427 // TODO FIXME
2428 QPointer<IRCColorChooser> dlg = new IRCColorChooser(m_window);
2429
2430 if (dlg->exec() == QDialog::Accepted)
2431 {
2432 if(m_frontView)
2433 m_frontView->appendInputText(dlg->color(), true/*fromCursor*/);
2434 }
2435 delete dlg;
2436 }
2437
doAutoReplace()2438 void ViewContainer::doAutoReplace()
2439 {
2440 if (!m_frontView)
2441 return;
2442
2443 // Check for active window in case action was triggered from a modal dialog, like the Paste Editor
2444 if (!m_window->isActiveWindow())
2445 return;
2446
2447 if (m_frontView->getInputBar())
2448 m_frontView->getInputBar()->doInlineAutoreplace();
2449 }
2450
focusInputBox()2451 void ViewContainer::focusInputBox()
2452 {
2453 if (m_frontView && m_frontView->isInsertSupported())
2454 m_frontView->adjustFocus();
2455 }
2456
clearViewLines()2457 void ViewContainer::clearViewLines()
2458 {
2459 if (m_frontView && m_frontView->getTextView() != nullptr)
2460 {
2461 m_frontView->getTextView()->clearLines();
2462
2463 QAction* action = actionCollection()->action(QStringLiteral("clear_lines"));
2464 if (action) action->setEnabled(false);
2465 }
2466 }
2467
insertRememberLine()2468 void ViewContainer::insertRememberLine()
2469 {
2470 if (Preferences::self()->automaticRememberLine())
2471 {
2472 if (m_frontView && m_frontView->getTextView() != nullptr)
2473 m_frontView->getTextView()->insertRememberLine();
2474 }
2475 }
2476
insertRememberLines(Server * server)2477 void ViewContainer::insertRememberLines(Server* server)
2478 {
2479 for (int i = 0; i < m_tabWidget->count(); ++i)
2480 {
2481 auto* view = static_cast<ChatWindow*>(m_tabWidget->widget(i));
2482
2483 if (view->getServer() == server && view->getTextView() != nullptr)
2484 view->getTextView()->insertRememberLine();
2485 }
2486 }
2487
cancelRememberLine()2488 void ViewContainer::cancelRememberLine()
2489 {
2490 if (m_frontView && m_frontView->getTextView() != nullptr)
2491 {
2492 m_frontView->getTextView()->cancelRememberLine();
2493
2494 QAction* action = actionCollection()->action(QStringLiteral("clear_lines"));
2495 if (action) action->setEnabled(m_frontView->getTextView()->hasLines());
2496 }
2497 }
2498
insertMarkerLine()2499 void ViewContainer::insertMarkerLine()
2500 {
2501 if (Preferences::self()->markerLineInAllViews())
2502 {
2503 int total = m_tabWidget->count()-1;
2504 ChatWindow* view;
2505
2506 for (int i = 0; i <= total; ++i)
2507 {
2508 view = static_cast<ChatWindow*>(m_tabWidget->widget(i));
2509
2510 if (view->getTextView() != nullptr) view->getTextView()->insertMarkerLine();
2511 }
2512 }
2513 else
2514 {
2515 if (m_frontView && m_frontView->getTextView() != nullptr)
2516 m_frontView->getTextView()->insertMarkerLine();
2517 }
2518
2519 if (m_frontView && m_frontView->getTextView() != nullptr)
2520 {
2521 QAction* action = actionCollection()->action(QStringLiteral("clear_lines"));
2522 if (action) action->setEnabled(m_frontView->getTextView()->hasLines());
2523 }
2524 }
2525
openLogFile()2526 void ViewContainer::openLogFile()
2527 {
2528 if (m_frontView)
2529 {
2530 if (!m_frontView->logFileName().isEmpty())
2531 openLogFile(m_frontView->getName(), m_frontView->logFileName());
2532 }
2533 }
2534
openLogFile(const QString & caption,const QString & file)2535 void ViewContainer::openLogFile(const QString& caption, const QString& file)
2536 {
2537 if (file.isEmpty())
2538 {
2539 return;
2540 }
2541
2542 if(Preferences::self()->useExternalLogViewer())
2543 {
2544 auto *job = new KIO::OpenUrlJob(QUrl::fromLocalFile(file), QStringLiteral("text/plain"));
2545 job->setUiDelegate(new KIO::JobUiDelegate(KJobUiDelegate::AutoHandlingEnabled, m_window));
2546 job->start();
2547 return;
2548 }
2549
2550 auto* logReader = new LogfileReader(m_tabWidget, file, caption);
2551 addView(logReader, logReader->getName());
2552
2553 logReader->setServer(nullptr);
2554 }
2555
addKonsolePanel()2556 void ViewContainer::addKonsolePanel()
2557 {
2558 auto* panel=new KonsolePanel(m_tabWidget);
2559 panel->setName(i18n("Konsole"));
2560 addView(panel, i18n("Konsole"));
2561 connect(panel, &KonsolePanel::updateTabNotification, this, &ViewContainer::setViewNotification);
2562 connect(panel, &KonsolePanel::closeView, this, QOverload<ChatWindow*>::of(&ViewContainer::closeView));
2563 }
2564
addUrlCatcher()2565 void ViewContainer::addUrlCatcher()
2566 {
2567 if (m_urlCatcherPanel == nullptr)
2568 {
2569 m_urlCatcherPanel=new UrlCatcher(m_tabWidget);
2570 addView(m_urlCatcherPanel, i18n("URL Catcher"));
2571
2572 static_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("open_url_catcher")))->setChecked(true);
2573 }
2574 else
2575 closeUrlCatcher();
2576 }
2577
closeUrlCatcher()2578 void ViewContainer::closeUrlCatcher()
2579 {
2580 if (m_urlCatcherPanel)
2581 {
2582 delete m_urlCatcherPanel;
2583 m_urlCatcherPanel = nullptr;
2584
2585 static_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("open_url_catcher")))->setChecked(false);
2586 }
2587 }
2588
toggleDccPanel()2589 void ViewContainer::toggleDccPanel()
2590 {
2591 if (m_dccPanel==nullptr || !m_dccPanelOpen)
2592 addDccPanel();
2593 else
2594 closeDccPanel();
2595 }
2596
addDccPanel()2597 void ViewContainer::addDccPanel()
2598 {
2599 qCDebug(KONVERSATION_LOG) << __FUNCTION__;
2600 if (!m_dccPanelOpen)
2601 {
2602 addView(m_dccPanel, i18n("DCC Status"));
2603 m_dccPanelOpen=true;
2604 static_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("open_dccstatus_window")))->setChecked(true);
2605 }
2606 }
2607
closeDccPanel()2608 void ViewContainer::closeDccPanel()
2609 {
2610 // if there actually is a dcc panel
2611 if (m_dccPanel && m_dccPanelOpen)
2612 {
2613 // hide it from view, does not delete it
2614 if (m_tabWidget)
2615 {
2616 if (m_popupViewIndex == m_tabWidget->indexOf(m_dccPanel)) {
2617 m_popupViewIndex = -1;
2618 }
2619
2620 cleanupAfterClose(m_dccPanel);
2621 }
2622 m_dccPanelOpen=false;
2623 static_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("open_dccstatus_window")))->setChecked(false);
2624 }
2625 }
2626
deleteDccPanel()2627 void ViewContainer::deleteDccPanel()
2628 {
2629 if (m_dccPanel)
2630 {
2631 closeDccPanel();
2632 delete m_dccPanel;
2633 m_dccPanel=nullptr;
2634 }
2635 }
2636
getDccPanel() const2637 ChatWindow* ViewContainer::getDccPanel() const
2638 {
2639 return m_dccPanel;
2640 }
2641
addDccChat(DCC::Chat * chat)2642 void ViewContainer::addDccChat(DCC::Chat* chat)
2643 {
2644 if (!chat->selfOpened()) // Someone else initiated dcc chat
2645 {
2646 Application* konv_app=Application::instance();
2647 konv_app->notificationHandler()->dccChat(m_frontView, chat->partnerNick());
2648 }
2649
2650 auto *chatcontainer = new DCC::ChatContainer(m_tabWidget,chat);
2651 connect(chatcontainer, &DCC::ChatContainer::updateTabNotification,
2652 this, &ViewContainer::setViewNotification);
2653
2654 addView(chatcontainer, chatcontainer->getName());
2655 }
2656
addStatusView(Server * server)2657 StatusPanel* ViewContainer::addStatusView(Server* server)
2658 {
2659 auto* statusView = new StatusPanel(m_tabWidget);
2660
2661 // Get group name for tab if available
2662 QString label = server->getDisplayName();
2663 statusView->setName(label);
2664
2665 statusView->setServer(server);
2666
2667 if (server->getServerGroup()) statusView->setNotificationsEnabled(server->getServerGroup()->enableNotifications());
2668
2669 QObject::connect(server, &Server::sslInitFailure, this, &ViewContainer::removeStatusBarSSLLabel);
2670 QObject::connect(server, &Server::sslConnected, this, &ViewContainer::updateStatusBarSSLLabel);
2671
2672 // ... then put it into the tab widget, otherwise we'd have a race with server member
2673 addView(statusView, label);
2674
2675 connect(statusView, &StatusPanel::updateTabNotification, this, &ViewContainer::setViewNotification);
2676 connect(statusView, &StatusPanel::sendFile, server, QOverload<>::of(&Server::requestDccSend));
2677 connect(server, &Server::awayState, statusView, &StatusPanel::indicateAway);
2678
2679 // Make sure that m_frontServer gets set on adding the first status panel, too,
2680 // since there won't be a viewSwitched happening.
2681 if (!m_frontServer) setFrontServer(server);
2682
2683 return statusView;
2684 }
2685
addRawLog(Server * server)2686 RawLog* ViewContainer::addRawLog(Server* server)
2687 {
2688 auto* rawLog = new RawLog(m_tabWidget);
2689 rawLog->setServer(server);
2690
2691 if (server->getServerGroup()) rawLog->setNotificationsEnabled(server->getServerGroup()->enableNotifications());
2692
2693 addView(rawLog, i18n("Raw Log"));
2694
2695 connect(rawLog, &RawLog::updateTabNotification, this, &ViewContainer::setViewNotification);
2696
2697 return rawLog;
2698 }
2699
reconnectFrontServer()2700 void ViewContainer::reconnectFrontServer()
2701 {
2702 Server* server = nullptr;
2703
2704 if (m_contextServer)
2705 server = m_contextServer;
2706 else
2707 server = m_frontServer;
2708
2709 if (server) server->reconnectServer();
2710 }
2711
disconnectFrontServer()2712 void ViewContainer::disconnectFrontServer()
2713 {
2714 Server* server = nullptr;
2715
2716 if (m_contextServer)
2717 server = m_contextServer;
2718 else
2719 server = m_frontServer;
2720
2721 if (server && (server->isConnected() || server->isConnecting() || server->isScheduledToConnect()))
2722 server->disconnectServer();
2723 }
2724
showJoinChannelDialog()2725 void ViewContainer::showJoinChannelDialog()
2726 {
2727 Server* server = nullptr;
2728
2729 if (m_contextServer)
2730 server = m_contextServer;
2731 else
2732 server = m_frontServer;
2733
2734 if (!server)
2735 return;
2736
2737 QPointer<Konversation::JoinChannelDialog> dlg = new Konversation::JoinChannelDialog(server, m_window);
2738
2739 if (dlg->exec() == QDialog::Accepted)
2740 {
2741 Server *server = Application::instance()->getConnectionManager()->getServerByConnectionId(dlg->connectionId());
2742 if (server)
2743 server->sendJoinCommand(dlg->channel(), dlg->password());
2744 }
2745 delete dlg;
2746 }
2747
connectionStateChanged(Server * server,Konversation::ConnectionState state)2748 void ViewContainer::connectionStateChanged(Server* server, Konversation::ConnectionState state)
2749 {
2750 Server* updateServer = nullptr;
2751
2752 if (m_contextServer)
2753 updateServer = m_contextServer;
2754 else
2755 updateServer = m_frontServer;
2756
2757 if (updateServer && updateServer == server)
2758 {
2759 QAction* action = actionCollection()->action(QStringLiteral("disconnect_server"));
2760 if (action)
2761 action->setEnabled(state == Konversation::SSConnected || state == Konversation::SSConnecting || state == Konversation::SSScheduledToConnect);
2762
2763 action = actionCollection()->action(QStringLiteral("join_channel"));
2764 if (action)
2765 action->setEnabled(state == Konversation::SSConnected);
2766
2767 if (m_frontView && m_frontView->getServer() == server
2768 && m_frontView->getType() == ChatWindow::Channel)
2769 {
2770 ChatWindow* view = m_frontView;
2771 auto* channel = static_cast<Channel*>(view);
2772
2773 action = actionCollection()->action(QStringLiteral("rejoin_channel"));
2774 if (action) action->setEnabled(state == Konversation::SSConnected && channel->rejoinable());
2775 }
2776 }
2777 }
2778
channelJoined(Channel * channel)2779 void ViewContainer::channelJoined(Channel* channel)
2780 {
2781 ChatWindow* view = m_frontView;
2782
2783 if (view == channel)
2784 {
2785 QAction* action = actionCollection()->action(QStringLiteral("rejoin_channel"));
2786 if (action) action->setEnabled(false);
2787 }
2788 }
2789
addChannel(Server * server,const QString & name)2790 Channel* ViewContainer::addChannel(Server* server, const QString& name)
2791 {
2792 auto* channel=new Channel(m_tabWidget, name);
2793 channel->setServer(server);
2794 channel->setName(name); //still have to do this for now
2795 addView(channel, name);
2796
2797 connect(this, &ViewContainer::updateChannelAppearance, channel, &Channel::updateAppearance);
2798 connect(channel, &Channel::updateTabNotification, this, &ViewContainer::setViewNotification);
2799 connect(server, &Server::awayState, channel, &Channel::indicateAway);
2800 connect(channel, &Channel::joined, this, &ViewContainer::channelJoined);
2801
2802 return channel;
2803 }
2804
rejoinChannel()2805 void ViewContainer::rejoinChannel()
2806 {
2807 Channel* channel = nullptr;
2808
2809 if (m_popupViewIndex == -1)
2810 channel = qobject_cast<Channel*>(m_tabWidget->currentWidget());
2811 else
2812 channel = qobject_cast<Channel*>(m_tabWidget->widget(m_popupViewIndex));
2813
2814 if (channel && channel->getType() == ChatWindow::Channel)
2815 channel->rejoin();
2816 }
2817
openChannelSettings()2818 void ViewContainer::openChannelSettings()
2819 {
2820 if (m_frontView->getType() == ChatWindow::Channel)
2821 {
2822 auto* channel = static_cast<Channel*>(m_tabWidget->currentWidget());
2823 channel->showOptionsDialog();
2824 }
2825 }
2826
toggleChannelNicklists()2827 void ViewContainer::toggleChannelNicklists()
2828 {
2829 auto* action = qobject_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("hide_nicknamelist")));
2830
2831 if (action)
2832 {
2833 Preferences::self()->setShowNickList(action->isChecked());
2834 Preferences::self()->save();
2835
2836 Q_EMIT updateChannelAppearance();
2837 }
2838 }
2839
addQuery(Server * server,const NickInfoPtr & nickInfo,bool weinitiated)2840 Query* ViewContainer::addQuery(Server* server, const NickInfoPtr& nickInfo, bool weinitiated)
2841 {
2842 QString name = nickInfo->getNickname();
2843 auto* query=new Query(m_tabWidget, name);
2844 query->setServer(server);
2845 query->setNickInfo(nickInfo); //still have to do this
2846 addView(query, name, weinitiated);
2847
2848 // About to increase the number of queries, so enable the close action
2849 if (m_queryViewCount == 0)
2850 actionCollection()->action(QStringLiteral("close_queries"))->setEnabled(true);
2851
2852 ++m_queryViewCount;
2853
2854 connect(query, &Query::updateTabNotification, this, &ViewContainer::setViewNotification);
2855 connect(query, &Query::updateQueryChrome, this, &ViewContainer::updateQueryChrome);
2856 connect(server, &Server::awayState, query, &Query::indicateAway);
2857
2858 return query;
2859 }
2860
updateQueryChrome(ChatWindow * view,const QString & name)2861 void ViewContainer::updateQueryChrome(ChatWindow* view, const QString& name)
2862 {
2863 //FIXME: updateQueryChrome is a last minute fix for 0.19 because
2864 // the updateInfo mess is indecipherable. Replace with a sane and
2865 // encompassing system.
2866
2867 QString newName = Konversation::removeIrcMarkup(name);
2868
2869 if (!newName.isEmpty() && m_tabWidget->tabText(m_tabWidget->indexOf(view)) != newName)
2870 {
2871 int tabIndex = m_tabWidget->indexOf(view);
2872
2873 m_tabWidget->setTabText(tabIndex, newName);
2874
2875 const QModelIndex& idx = indexForView(view);
2876 Q_EMIT dataChanged(idx, idx, QVector<int> { Qt::DisplayRole });
2877 }
2878
2879 if (!newName.isEmpty() && view==m_frontView)
2880 Q_EMIT setWindowCaption(newName);
2881 }
2882
closeQueries()2883 void ViewContainer::closeQueries()
2884 {
2885 int total=m_tabWidget->count()-1;
2886 int operations = 0;
2887 ChatWindow* nextPage;
2888
2889 for (int i=0; i <=total; i++)
2890 {
2891 if (operations > total)
2892 break;
2893
2894 nextPage = qobject_cast<ChatWindow*>(m_tabWidget->widget(i));
2895
2896 if (nextPage && nextPage->getType()==ChatWindow::Query)
2897 {
2898 closeView(nextPage);
2899 if (m_tabWidget->indexOf(nextPage) == -1) --i;
2900 }
2901 ++operations;
2902 }
2903
2904 actionCollection()->action(QStringLiteral("close_queries"))->setEnabled(false);
2905 }
2906
addChannelListPanel(Server * server)2907 ChannelListPanel* ViewContainer::addChannelListPanel(Server* server)
2908 {
2909 auto* channelListPanel=new ChannelListPanel(m_tabWidget);
2910 channelListPanel->setServer(server);
2911 addView(channelListPanel, i18n("Channel List"));
2912
2913 auto* action = qobject_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("open_channel_list")));
2914 if ((server == m_frontServer) && action) action->setChecked(true);
2915
2916 return channelListPanel;
2917 }
2918
openChannelList(Server * server,const QString & filter,bool getList)2919 void ViewContainer::openChannelList(Server* server, const QString& filter, bool getList)
2920 {
2921 if (!server)
2922 server = m_frontServer;
2923
2924 if (!server)
2925 {
2926 KMessageBox::information(m_window,
2927 i18n(
2928 "To know which server to display the channel list "
2929 "for, the list can only be opened from a "
2930 "query, channel or status window."
2931 ),
2932 i18n("Channel List"),
2933 QStringLiteral("ChannelListNoServerSelected"));
2934 return;
2935 }
2936
2937 ChannelListPanel* panel = server->getChannelListPanel();
2938
2939 if (panel && filter.isEmpty())
2940 {
2941 closeView(panel);
2942
2943 if (server == m_frontServer)
2944 {
2945 auto* action = qobject_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("open_channel_list")));
2946 if (action) action->setChecked(false);
2947 }
2948
2949 return;
2950 }
2951
2952 if (!panel)
2953 {
2954 int ret = KMessageBox::Continue;
2955
2956 if (filter.isEmpty())
2957 {
2958 ret = KMessageBox::warningContinueCancel(m_window, i18n("Using this function may result in a lot "
2959 "of network traffic. If your connection is not fast "
2960 "enough, it is possible that your client will be "
2961 "disconnected by the server."),
2962 i18n("Channel List Warning"),
2963 KStandardGuiItem::cont(),
2964 KStandardGuiItem::cancel(),
2965 QStringLiteral("ChannelListWarning"));
2966 }
2967
2968 if (ret != KMessageBox::Continue)
2969 {
2970 if (server == m_frontServer)
2971 {
2972 auto* action = qobject_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("open_channel_list")));
2973 if (action) action->setChecked(false);
2974 }
2975
2976 return;
2977 }
2978
2979 panel = server->addChannelListPanel();
2980 }
2981
2982 panel->setFilter(filter);
2983
2984 if (getList) panel->refreshList();
2985 }
2986
openNicksOnlinePanel()2987 void ViewContainer::openNicksOnlinePanel()
2988 {
2989 if (!m_nicksOnlinePanel)
2990 {
2991 m_nicksOnlinePanel=new NicksOnline(m_window);
2992 addView(m_nicksOnlinePanel, i18n("Watched Nicks"));
2993 connect(m_nicksOnlinePanel, &NicksOnline::doubleClicked, m_window, &MainWindow::notifyAction);
2994 connect(m_window, &MainWindow::nicksNowOnline, m_nicksOnlinePanel, &NicksOnline::updateServerOnlineList);
2995 static_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("open_nicksonline_window")))->setChecked(true);
2996 }
2997 else
2998 {
2999 closeNicksOnlinePanel();
3000 }
3001
3002 }
3003
closeNicksOnlinePanel()3004 void ViewContainer::closeNicksOnlinePanel()
3005 {
3006 delete m_nicksOnlinePanel;
3007 m_nicksOnlinePanel = nullptr;
3008 static_cast<KToggleAction*>(actionCollection()->action(QStringLiteral("open_nicksonline_window")))->setChecked(false);
3009 }
3010
3011 /*!
3012 \fn ViewContainer::frontServerChanging(Server *newServer)
3013
3014 This signal is emitted immediately before the front server is changed.
3015
3016 If the server is being removed this will fire with a null pointer.
3017 */
3018
3019
3020