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