1 /*
2     SPDX-License-Identifier: GPL-2.0-or-later
3 
4     SPDX-FileCopyrightText: 2002 Dario Abatianni <eisfuchs@tigress.com>
5     SPDX-FileCopyrightText: 2005 Ismail Donmez <ismail@kde.org>
6     SPDX-FileCopyrightText: 2005 Peter Simonsson <psn@linux.se>
7     SPDX-FileCopyrightText: 2005 John Tapsell <johnflux@gmail.com>
8     SPDX-FileCopyrightText: 2005-2008 Eike Hein <hein@kde.org>
9 */
10 
11 #include "mainwindow.h"
12 #include "application.h"
13 #include "settingsdialog.h"
14 #include "viewcontainer.h"
15 #include "statusbar.h"
16 #include "bookmarkhandler.h"
17 #include "trayicon.h"
18 #include "serverlistdialog.h"
19 #include "identitydialog.h"
20 #include "notificationhandler.h"
21 #include "irccharsets.h"
22 #include "connectionmanager.h"
23 #include "awaymanager.h"
24 #include "transfermanager.h"
25 #include "irccontextmenus.h"
26 
27 #include <QMenuBar>
28 
29 #include <kwidgetsaddons_version.h>
30 #include <KActionCollection>
31 #include <QAction>
32 #include <KToggleAction>
33 #include <KSelectAction>
34 #include <KAuthorized>
35 #include <KStandardAction>
36 #include <KLocalizedString>
37 #include <KMessageBox>
38 #include <KConfigGui>
39 
40 #include <QSplitter>
41 #include <QIcon>
42 #include <QMenu>
43 #include <KWindowSystem>
44 #include <KWindowInfo>
45 #include <KShortcutsDialog>
46 #include <KStandardShortcut>
47 #include <KNotifyConfigWidget>
48 #include <KGlobalAccel>
49 #include <KStartupInfo>
50 
MainWindow()51 MainWindow::MainWindow() : KXmlGuiWindow(nullptr)
52 {
53     IrcContextMenus::setupUi(this);
54 
55     m_hasDirtySettings = false;
56     m_closeOnQuitAction = false;
57     m_serverListDialog = nullptr;
58     m_trayIcon = nullptr;
59     m_settingsDialog = nullptr;
60 
61     m_viewContainer = new ViewContainer(this);
62     setCentralWidget(m_viewContainer->getWidget());
63 
64     //used for event compression. See header file for resetHasDirtySettings()
65     connect(Application::instance(), &Application::appearanceChanged, this, &MainWindow::resetHasDirtySettings);
66     connect(Application::instance(), &Application::appearanceChanged, this, &MainWindow::updateTrayIcon);
67 
68 
69     // Set up view container
70     connect(Application::instance(), &Application::appearanceChanged, m_viewContainer, &ViewContainer::updateAppearance);
71     connect(Application::instance(), &Application::serverGroupsChanged,
72             m_viewContainer, &ViewContainer::updateViews);
73     connect(m_viewContainer, &ViewContainer::autoJoinToggled,
74             Application::instance(), &Application::serverGroupsChanged);
75     connect(m_viewContainer, &ViewContainer::autoConnectOnStartupToggled,
76             Application::instance(), &Application::serverGroupsChanged);
77     connect(m_viewContainer, &ViewContainer::setWindowCaption,
78             this, QOverload<const QString&>::of(&MainWindow::setCaption));
79     connect(Application::instance()->getConnectionManager(),
80             &ConnectionManager::connectionChangedState,
81             m_viewContainer, &ViewContainer::connectionStateChanged);
82     connect(this, &MainWindow::triggerRememberLine, m_viewContainer, &ViewContainer::insertRememberLine);
83     connect(this, &MainWindow::triggerRememberLines, m_viewContainer, &ViewContainer::insertRememberLines);
84     connect(this, &MainWindow::cancelRememberLine, m_viewContainer, &ViewContainer::cancelRememberLine);
85     connect(this, &MainWindow::insertMarkerLine, m_viewContainer, &ViewContainer::insertMarkerLine);
86 
87     // Set up status bar
88     m_statusBar = new Konversation::StatusBar(this);
89     connect(Application::instance(), &Application::appearanceChanged, m_statusBar, &Konversation::StatusBar::updateAppearance);
90 
91     createStandardStatusBarAction();
92 
93     connect(m_viewContainer, &ViewContainer::resetStatusBar, m_statusBar, &Konversation::StatusBar::resetStatusBar);
94     connect(m_viewContainer, &ViewContainer::setStatusBarTempText, m_statusBar, &Konversation::StatusBar::setMainLabelTempText);
95     connect(m_viewContainer, &ViewContainer::clearStatusBarTempText, m_statusBar, &Konversation::StatusBar::clearMainLabelTempText);
96     connect(m_viewContainer, &ViewContainer::setStatusBarInfoLabel, m_statusBar, &Konversation::StatusBar::updateInfoLabel);
97     connect(m_viewContainer, &ViewContainer::clearStatusBarInfoLabel, m_statusBar, &Konversation::StatusBar::clearInfoLabel);
98     connect(m_viewContainer, &ViewContainer::setStatusBarLagLabelShown, m_statusBar, &Konversation::StatusBar::setLagLabelShown);
99     connect(m_viewContainer, &ViewContainer::updateStatusBarLagLabel, m_statusBar, &Konversation::StatusBar::updateLagLabel);
100     connect(m_viewContainer, &ViewContainer::resetStatusBarLagLabel, m_statusBar, &Konversation::StatusBar::resetLagLabel);
101     connect(m_viewContainer, &ViewContainer::setStatusBarLagLabelTooLongLag, m_statusBar, &Konversation::StatusBar::setTooLongLag);
102     connect(m_viewContainer, &ViewContainer::updateStatusBarSSLLabel, m_statusBar, &Konversation::StatusBar::updateSSLLabel);
103     connect(m_viewContainer, &ViewContainer::removeStatusBarSSLLabel, m_statusBar, &Konversation::StatusBar::removeSSLLabel);
104 
105 
106     // Actions
107     KStandardAction::quit(this, &MainWindow::quitProgram, actionCollection());
108 
109     m_showMenuBarAction = KStandardAction::showMenubar(this, &MainWindow::toggleMenubar, actionCollection());
110 
111     setStandardToolBarMenuEnabled(true);
112     KStandardAction::configureToolbars(this, &MainWindow::configureToolbars, actionCollection());
113 
114     KStandardAction::keyBindings(this, &MainWindow::openKeyBindings, actionCollection());
115     KStandardAction::preferences(this, &MainWindow::openPrefsDialog, actionCollection());
116 
117     KStandardAction::configureNotifications(this, &MainWindow::openNotifications, actionCollection());
118 
119     QAction* action;
120 
121     action=new QAction(this);
122     action->setText(i18n("Restart"));
123     action->setIcon(QIcon::fromTheme(QStringLiteral("system-reboot")));
124     action->setStatusTip(i18n("Quit and restart the application"));
125     connect(action, &QAction::triggered, Application::instance(), &Application::restart);
126     actionCollection()->addAction(QStringLiteral("restart"), action);
127 
128     action=new QAction(this);
129     action->setText(i18n("&Server List..."));
130     action->setIcon(QIcon::fromTheme(QStringLiteral("network-server")));
131     actionCollection()->setDefaultShortcut(action, QKeySequence(QStringLiteral("F2")));
132     action->setStatusTip(i18n("Manage networks and servers"));
133     connect(action, &QAction::triggered, this, &MainWindow::openServerList);
134     actionCollection()->addAction(QStringLiteral("open_server_list"), action);
135 
136     action=new QAction(this);
137     action->setText(i18n("Quick &Connect..."));
138     action->setIcon(QIcon::fromTheme(QStringLiteral("network-connect")));
139     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("F7")));
140     action->setStatusTip(i18n("Type in the address of a new IRC server to connect to"));
141     connect(action, &QAction::triggered, this, &MainWindow::openQuickConnectDialog);
142     actionCollection()->addAction(QStringLiteral("quick_connect_dialog"), action);
143 
144     action=new QAction(this);
145     action->setText(i18n("&Reconnect"));
146     action->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
147     action->setEnabled(false);
148     action->setStatusTip(i18n("Reconnect to the current server."));
149     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::reconnectFrontServer);
150     actionCollection()->addAction(QStringLiteral("reconnect_server"), action);
151 
152 
153     action=new QAction(this);
154     action->setText(i18n("&Disconnect"));
155     action->setIcon(QIcon::fromTheme(QStringLiteral("network-disconnect")));
156     action->setEnabled(false);
157     action->setStatusTip(i18n("Disconnect from the current server."));
158     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::disconnectFrontServer);
159     actionCollection()->addAction(QStringLiteral("disconnect_server"), action);
160 
161     action=new QAction(this);
162     action->setText(i18n("&Identities..."));
163     action->setIcon(QIcon::fromTheme(QStringLiteral("user-identity")));
164     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("F8")));
165     action->setStatusTip(i18n("Manage your nick, away and other identity settings"));
166     connect(action, &QAction::triggered, this, &MainWindow::openIdentitiesDialog);
167     actionCollection()->addAction(QStringLiteral("identities_dialog"), action);
168 
169     action=new KToggleAction(this);
170     action->setText(i18n("&Watched Nicks"));
171     action->setIcon(QIcon::fromTheme(QStringLiteral("im-user")));
172     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("F4")));
173     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::openNicksOnlinePanel);
174     actionCollection()->addAction(QStringLiteral("open_nicksonline_window"), action);
175 
176 
177     action=new KToggleAction(this);
178     action->setText(i18n("&DCC Status"));
179     action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right-double")));
180     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("F9")));
181     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::toggleDccPanel);
182     actionCollection()->addAction(QStringLiteral("open_dccstatus_window"), action);
183 
184 
185 
186     action=new QAction(this);
187     action->setText(i18n("&Open Logfile"));
188     action->setIcon(QIcon::fromTheme(QStringLiteral("view-history")));
189     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("Ctrl+O")));
190     action->setEnabled(false);
191     action->setStatusTip(i18n("Open the known history for this channel in a new tab"));
192     connect(action, &QAction::triggered, m_viewContainer, QOverload<>::of(&ViewContainer::openLogFile));
193     actionCollection()->addAction(QStringLiteral("open_logfile"), action);
194 
195     action=new QAction(this);
196     action->setText(i18n("&Channel Settings..."));
197     action->setIcon(QIcon::fromTheme(QStringLiteral("configure")));
198     action->setEnabled(false);
199     action->setStatusTip(i18n("Open the channel settings dialog for this tab"));
200     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::openChannelSettings);
201     actionCollection()->addAction(QStringLiteral("channel_settings"), action);
202 
203     action=new KToggleAction(this);
204     action->setText(i18n("Channel &List"));
205     action->setIcon(QIcon::fromTheme(QStringLiteral("view-list-text")));
206     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("F5")));
207     action->setEnabled(false);
208     action->setStatusTip(i18n("Show a list of all the known channels on this server"));
209     connect(action, &QAction::triggered, m_viewContainer, [this]() { m_viewContainer->openChannelList(); });
210     actionCollection()->addAction(QStringLiteral("open_channel_list"), action);
211 
212     action=new KToggleAction(this);
213     action->setText(i18n("&URL Catcher"));
214     action->setIcon(QIcon::fromTheme(QStringLiteral("text-html")));
215     actionCollection()->setDefaultShortcut(action, QKeySequence(QStringLiteral("F6")));
216     action->setStatusTip(i18n("List all URLs that have been mentioned recently in a new tab"));
217     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::addUrlCatcher);
218     actionCollection()->addAction(QStringLiteral("open_url_catcher"), action);
219 
220     if (KAuthorized::authorize(QStringLiteral("shell_access")))
221     {
222         action=new QAction(this);
223         action->setText(i18n("New &Konsole"));
224         action->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal")));
225         action->setStatusTip(i18n("Open a terminal in a new tab"));
226         connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::addKonsolePanel);
227         actionCollection()->addAction(QStringLiteral("open_konsole"), action);
228     }
229 
230     // Actions to navigate through the different pages
231     QList<QKeySequence> nextShortcut = KStandardShortcut::tabNext();
232     QList<QKeySequence> prevShortcut = KStandardShortcut::tabPrev();
233 
234     QString nextIcon, prevIcon;
235     if (QApplication::isRightToLeft())
236     {
237         prevShortcut.append(QKeySequence(QStringLiteral("Alt+Right")));
238         nextShortcut.append(QKeySequence(QStringLiteral("Alt+Left")));
239         prevShortcut.append(QKeySequence(QStringLiteral("Ctrl+Tab")));
240         nextShortcut.append(QKeySequence(QStringLiteral("Ctrl+Shift+Tab")));
241         nextIcon=QStringLiteral("go-previous-view");
242         prevIcon=QStringLiteral("go-next-view");
243     }
244     else
245     {
246         nextShortcut.append(QKeySequence(QStringLiteral("Alt+Right")));
247         prevShortcut.append(QKeySequence(QStringLiteral("Alt+Left")));
248         nextShortcut.append(QKeySequence(QStringLiteral("Ctrl+Tab")));
249         prevShortcut.append(QKeySequence(QStringLiteral("Ctrl+Shift+Tab")));
250         nextIcon=QStringLiteral("go-next-view");
251         prevIcon=QStringLiteral("go-previous-view");
252     }
253 
254     action=new QAction(this);
255     action->setText(i18n("&Next Tab"));
256     action->setIcon(QIcon::fromTheme(nextIcon));
257     actionCollection()->setDefaultShortcuts(action,nextShortcut);
258     action->setEnabled(false);
259     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::showNextView);
260     actionCollection()->addAction(QStringLiteral("next_tab"), action);
261 
262     action=new QAction(this);
263     action->setText(i18n("&Previous Tab"));
264     action->setIcon(QIcon::fromTheme(prevIcon));
265     actionCollection()->setDefaultShortcuts(action, prevShortcut);
266     action->setEnabled(false);
267     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::showPreviousView);
268     actionCollection()->addAction(QStringLiteral("previous_tab"), action);
269 
270     action=new QAction(this);
271     action->setText(i18n("Close &Tab"));
272     action->setIcon(QIcon::fromTheme(QStringLiteral("tab-close-other")));
273     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("Ctrl+w")));
274     action->setEnabled(false);
275     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::closeCurrentView);
276     actionCollection()->addAction(QStringLiteral("close_tab"), action);
277 
278     action=new QAction(this);
279     action->setText(i18n("Last Focused Tab"));
280     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("Alt+Space")));
281     action->setEnabled(false);
282     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::showLastFocusedView);
283     actionCollection()->addAction(QStringLiteral("last_focused_tab"), action);
284 
285     action=new QAction(this);
286     action->setText(i18n("Next Active Tab"));
287     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("Ctrl+Alt+Space")));
288     action->setEnabled(false);
289     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::showNextActiveView);
290     actionCollection()->addAction(QStringLiteral("next_active_tab"), action);
291     KGlobalAccel::setGlobalShortcut(action, QList<QKeySequence>());
292 
293     if (Preferences::self()->tabPlacement()==Preferences::Left)
294     {
295         action=new QAction(this);
296         action->setText(i18n("Move Tab Up"));
297         action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-up")));
298         actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("Alt+Shift+Left")));
299         action->setEnabled(false);
300         action->setStatusTip(i18n("Move this tab"));
301         connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::moveViewLeft);
302         actionCollection()->addAction(QStringLiteral("move_tab_left"), action);
303 
304         action->setEnabled(false);
305         action->setStatusTip(i18n("Move this tab"));
306         action=new QAction(this);
307         action->setText(i18n("Move Tab Down"));
308         action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-down")));
309         actionCollection()->setDefaultShortcut(action, QKeySequence(QStringLiteral("Alt+Shift+Right")));
310         connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::moveViewRight);
311         actionCollection()->addAction(QStringLiteral("move_tab_right"), action);
312     }
313     else
314     {
315         if (QApplication::isRightToLeft())
316         {
317             action=new QAction(this);
318             action->setText(i18n("Move Tab Right"));
319             action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
320             actionCollection()->setDefaultShortcut(action, QKeySequence(QStringLiteral("Alt+Shift+Right")));
321             action->setEnabled(false);
322             action->setStatusTip(i18n("Move this tab"));
323             connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::moveViewLeft);
324             actionCollection()->addAction(QStringLiteral("move_tab_left"), action);
325 
326             action=new QAction(this);
327             action->setText(i18n("Move Tab Left"));
328             action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
329             actionCollection()->setDefaultShortcut(action, QKeySequence(QStringLiteral("Alt+Shift+Left")));
330             action->setEnabled(false);
331             action->setStatusTip(i18n("Move this tab"));
332             connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::moveViewRight);
333             actionCollection()->addAction(QStringLiteral("move_tab_right"), action);
334 
335         }
336         else
337         {
338             action=new QAction(this);
339             action->setText(i18n("Move Tab Left"));
340             action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left")));
341             actionCollection()->setDefaultShortcut(action, QKeySequence(QStringLiteral("Alt+Shift+Left")));
342             action->setEnabled(false);
343             action->setStatusTip(i18n("Move this tab"));
344             connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::moveViewLeft);
345             actionCollection()->addAction(QStringLiteral("move_tab_left"), action);
346 
347             action=new QAction(this);
348             action->setText(i18n("Move Tab Right"));
349             action->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right")));
350             actionCollection()->setDefaultShortcut(action, QKeySequence(QStringLiteral("Alt+Shift+Right")));
351             action->setEnabled(false);
352             action->setStatusTip(i18n("Move this tab"));
353             connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::moveViewRight);
354             actionCollection()->addAction(QStringLiteral("move_tab_right"), action);
355 
356         }
357 
358     }
359 
360     action->setEnabled(false);
361     action=new QAction(this);
362     action->setText(i18n("Rejoin Channel"));
363     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::rejoinChannel);
364     actionCollection()->addAction(QStringLiteral("rejoin_channel"), action);
365 
366     action->setEnabled(false);
367     action=new KToggleAction(this);
368     action->setText(i18n("Enable Notifications"));
369     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::toggleViewNotifications);
370     actionCollection()->addAction(QStringLiteral("tab_notifications"), action);
371 
372     action->setEnabled(false);
373     action=new KToggleAction(this);
374     action->setText(i18n("Join on Connect"));
375     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::toggleAutoJoin);
376     actionCollection()->addAction(QStringLiteral("tab_autojoin"), action);
377 
378     action=new KToggleAction(this);
379     action->setText(i18n("Connect at Startup"));
380     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::toggleConnectOnStartup);
381     actionCollection()->addAction(QStringLiteral("tab_autoconnect"), action);
382 
383     QStringList encodingDescs = Konversation::IRCCharsets::self()->availableEncodingDescriptiveNames();
384     encodingDescs.prepend(i18n("Default"));
385     auto* selectAction = new KSelectAction(this);
386     selectAction->setEditable(false);
387     selectAction->setItems(encodingDescs);
388     selectAction->setEnabled(false);
389     selectAction->setText(i18n("Set Encoding"));
390     selectAction->setIcon(QIcon::fromTheme(QStringLiteral("character-set")));
391 #if KWIDGETSADDONS_VERSION >= QT_VERSION_CHECK(5, 78, 0)
392     connect(selectAction, &KSelectAction::indexTriggered, m_viewContainer, &ViewContainer::changeViewCharset);
393 #else
394     connect(selectAction, QOverload<int>::of(&KSelectAction::triggered), m_viewContainer, &ViewContainer::changeViewCharset);
395 #endif
396     actionCollection()->addAction(QStringLiteral("tab_encoding"), selectAction);
397 
398     for (uint i = 1; i <= 10; ++i)
399     {
400 
401         action=new QAction(this);
402         action->setText(i18n("Go to Tab %1",i));
403         actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("Alt+%1").arg(i%10)));
404         connect(action, &QAction::triggered, this, [=] { m_viewContainer->goToView(static_cast<int>(i - 1)); });
405         actionCollection()->addAction(QStringLiteral("go_to_tab_%1").arg(i), action);
406     }
407 
408     action=new QAction(this);
409     action->setText(i18n("Clear &Marker Lines"));
410     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("Ctrl+Shift+R")));
411     action->setEnabled(false);
412     action->setStatusTip(i18n("Clear marker lines in the current tab"));
413     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::clearViewLines);
414     actionCollection()->addAction(QStringLiteral("clear_lines"), action);
415 
416     action=new QAction(this);
417     action->setText(i18n("Enlarge Font Size"));
418     actionCollection()->setDefaultShortcuts(action, KStandardShortcut::zoomIn());
419     action->setEnabled(false);
420     action->setIcon(QIcon::fromTheme(QStringLiteral("zoom-in")));
421     action->setStatusTip(i18n("Increase the current font size"));
422     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::zoomIn);
423     actionCollection()->addAction(QStringLiteral("increase_font"), action);
424 
425     action=new QAction(this);
426     action->setText(i18n("Reset Font Size"));
427     actionCollection()->setDefaultShortcut(action, QKeySequence(QStringLiteral("Ctrl+0")));
428     action->setEnabled(false);
429     action->setIcon(QIcon::fromTheme(QStringLiteral("zoom-original")));
430     action->setStatusTip(i18n("Reset the current font size to settings values"));
431     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::resetFont);
432     actionCollection()->addAction(QStringLiteral("reset_font"), action);
433 
434     action=new QAction(this);
435     action->setText(i18n("Shrink Font Size"));
436     actionCollection()->setDefaultShortcuts(action, KStandardShortcut::zoomOut());
437     action->setEnabled(false);
438     action->setIcon(QIcon::fromTheme(QStringLiteral("zoom-out")));
439     action->setStatusTip(i18n("Decrease the current font size"));
440     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::zoomOut);
441     actionCollection()->addAction(QStringLiteral("shrink_font"), action);
442 
443     action=new QAction(this);
444     action->setText(i18n("&Clear Window"));
445     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("Ctrl+L")));
446     action->setEnabled(false);
447     action->setStatusTip(i18n("Clear the contents of the current tab"));
448     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::clearView);
449     actionCollection()->addAction(QStringLiteral("clear_window"), action);
450 
451     action=new QAction(this);
452     action->setText(i18n("Clear &All Windows"));
453     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("Ctrl+Shift+L")));
454     action->setEnabled(false);
455     action->setStatusTip(i18n("Clear the contents of all open tabs"));
456     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::clearAllViews);
457     actionCollection()->addAction(QStringLiteral("clear_tabs"), action);
458 
459     auto* awayAction = new KToggleAction(this);
460     awayAction->setText(i18n("Global Away"));
461     actionCollection()->setDefaultShortcut(awayAction,QKeySequence(QStringLiteral("Ctrl+Shift+A")));
462     awayAction->setEnabled(false);
463     awayAction->setIcon(QIcon::fromTheme(QStringLiteral("im-user-away")));
464     connect(awayAction, &QAction::triggered, Application::instance()->getAwayManager(), &AwayManager::setGlobalAway);
465     actionCollection()->addAction(QStringLiteral("toggle_away"), awayAction);
466 
467     action=new QAction(this);
468     action->setText(i18n("&Join Channel..."));
469     action->setIcon(QIcon::fromTheme(QStringLiteral("irc-join-channel")));
470     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("Ctrl+J")));
471     action->setEnabled(false);
472     action->setStatusTip(i18n("Join a new channel on this server"));
473     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::showJoinChannelDialog);
474     actionCollection()->addAction(QStringLiteral("join_channel"), action);
475 
476     action = KStandardAction::find(m_viewContainer, &ViewContainer::findText, actionCollection());
477     action->setEnabled(false);
478     action = KStandardAction::findNext(m_viewContainer, &ViewContainer::findNextText, actionCollection());
479     action->setEnabled(false);
480     action = KStandardAction::findPrev(m_viewContainer, &ViewContainer::findPrevText, actionCollection());
481     action->setEnabled(false);
482 
483     action=new QAction(this);
484     action->setText(i18n("&IRC Color..."));
485     action->setIcon(QIcon::fromTheme(QStringLiteral("format-text-color")));
486     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("Ctrl+K")));
487     action->setEnabled(false);
488     action->setStatusTip(i18n("Set the color of your current IRC message"));
489     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::insertIRCColor);
490     actionCollection()->addAction(QStringLiteral("irc_colors"), action);
491 
492     action=new QAction(this);
493     action->setText(i18n("&Marker Line"));
494     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("Ctrl+R")));
495     action->setEnabled(false);
496     action->setStatusTip(i18n("Insert a horizontal line into the current tab that only you can see"));
497     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::insertMarkerLine);
498     actionCollection()->addAction(QStringLiteral("insert_marker_line"), action);
499 
500     action=new QAction(this);
501     action->setText(i18n("Special &Character..."));
502     action->setIcon(QIcon::fromTheme(QStringLiteral("character-set")));
503     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("Alt+Shift+C")));
504     action->setEnabled(false);
505     action->setStatusTip(i18n("Insert any character into your current IRC message"));
506     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::insertCharacter);
507     actionCollection()->addAction(QStringLiteral("insert_character"), action);
508 
509     action=new QAction(this);
510     action->setText(i18n("Auto Replace"));
511     action->setEnabled(false);
512     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::doAutoReplace);
513     actionCollection()->addAction(QStringLiteral("auto_replace"), action);
514 
515     action=new QAction(this);
516     action->setText(i18n("Focus Input Box"));
517     actionCollection()->setDefaultShortcut(action,QKeySequence(Qt::Key_Escape));
518     action->setEnabled(false);
519     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::focusInputBox);
520     actionCollection()->addAction(QStringLiteral("focus_input_box"), action);
521 
522     action=new QAction(this);
523     action->setText(i18n("Close &All Open Queries"));
524     actionCollection()->setDefaultShortcut(action,QKeySequence(QStringLiteral("F11")));
525     action->setEnabled(false);
526     connect(action, &QAction::triggered, m_viewContainer, &ViewContainer::closeQueries);
527     actionCollection()->addAction(QStringLiteral("close_queries"), action);
528 
529     auto* toggleChannelNickListsAction = new KToggleAction(this);
530     if (Preferences::self()->showNickList())
531         toggleChannelNickListsAction->setChecked(true);
532     toggleChannelNickListsAction->setText(i18n("Show Nicklist"));
533     actionCollection()->setDefaultShortcut(toggleChannelNickListsAction, QKeySequence(QStringLiteral("Ctrl+H")));
534     connect(toggleChannelNickListsAction, &QAction::triggered, m_viewContainer, &ViewContainer::toggleChannelNicklists);
535     actionCollection()->addAction(QStringLiteral("hide_nicknamelist"), toggleChannelNickListsAction);
536 
537     action=new QAction(this);
538     action->setText(i18n("Show/Hide Konversation"));
539     connect(action, &QAction::triggered, this, &MainWindow::toggleVisibility);
540     actionCollection()->addAction(QStringLiteral("toggle_mainwindow_visibility"), action);
541     KGlobalAccel::setGlobalShortcut(action, QList<QKeySequence>());
542 
543     action=new KToggleAction(this);
544     action->setEnabled(true);
545     action->setChecked(Preferences::self()->useOSD());
546     action->setText(i18n("Enable On Screen Display"));
547     action->setIcon(QIcon::fromTheme(QStringLiteral("video-display")));
548     connect(action, QOverload<bool>::of(&KToggleAction::triggered), Preferences::self(), &Preferences::slotSetUseOSD);
549     actionCollection()->addAction(QStringLiteral("toggle_osd"), action);
550 
551     // Bookmarks
552     action=new QAction(this);
553     action->setText(i18n("Bookmarks"));
554     auto *menu = new QMenu(this);
555     action->setMenu(menu);
556     new KonviBookmarkHandler(menu, this);
557     actionCollection()->addAction(QStringLiteral("bookmarks") , action);
558 
559     // decide whether to show the tray icon or not
560     updateTrayIcon();
561 
562     createGUI();
563 
564     setAutoSaveSettings();
565 
566     // Apply menubar show/hide pref
567     m_showMenuBarAction->setChecked(Preferences::self()->showMenuBar());
568     toggleMenubar(true);
569 
570     if (Preferences::self()->useNotify() && Preferences::self()->openWatchedNicksAtStartup())
571         m_viewContainer->openNicksOnlinePanel();
572 
573 }
574 
~MainWindow()575 MainWindow::~MainWindow()
576 {
577 }
578 
sizeHint() const579 QSize MainWindow::sizeHint() const
580 {
581     return QSize(700, 500); // Give the app a sane default size
582 }
583 
confirmQuit()584 int MainWindow::confirmQuit()
585 {
586     Application* konvApp = Application::instance();
587 
588     if (konvApp->getConnectionManager()->connectionCount() == 0)
589         return KMessageBox::Continue;
590 
591     int result = KMessageBox::Cancel;
592 
593     if (!KMessageBox::shouldBeShownContinue(QStringLiteral("systemtrayquitKonversation"))
594          && konvApp->getDccTransferManager()->hasActiveTransfers())
595     {
596         result = KMessageBox::warningContinueCancel(
597             this,
598             i18n("<qt>You have active DCC file transfers. Are you sure you want to quit <b>Konversation</b>?</qt>"),
599             i18n("Confirm Quit"),
600             KStandardGuiItem::quit(),
601             KStandardGuiItem::cancel(),
602             QStringLiteral("QuitWithActiveDccTransfers"));
603     }
604     else
605     {
606         result = KMessageBox::warningContinueCancel(
607             this,
608             i18n("<qt>Are you sure you want to quit <b>Konversation</b>?</qt>"),
609             i18n("Confirm Quit"),
610             KStandardGuiItem::quit(),
611             KStandardGuiItem::cancel(),
612             QStringLiteral("systemtrayquitKonversation"));
613     }
614 
615     if (result != KMessageBox::Continue)
616         konvApp->abortScheduledRestart();
617 
618     return result;
619 }
620 
quitProgram()621 void MainWindow::quitProgram()
622 {
623     if (Preferences::self()->showTrayIcon() &&
624         sender() != m_trayIcon &&
625         confirmQuit() == KMessageBox::Cancel) return;
626 
627     // will call queryClose()
628     m_closeOnQuitAction = true;
629     close();
630     // reset flag in case close was cancelled and close()/queryClose() could be called from elsewhere
631     m_closeOnQuitAction = false;
632 }
633 
queryClose()634 bool MainWindow::queryClose()
635 {
636     Application* konvApp = Application::instance();
637 
638     if (!konvApp->isSavingSession())
639     {
640         const bool isClosingWindowToSystray = !m_closeOnQuitAction && (sender() != m_trayIcon);
641 
642         if (Preferences::self()->showTrayIcon() && isClosingWindowToSystray)
643         {
644             bool doit = KMessageBox::warningContinueCancel(this,
645                         i18n("<p>Closing the main window will keep Konversation running in the system tray. "
646                         "Use <b>Quit</b> from the <b>Konversation</b> menu to quit the application.</p>"),
647                         i18n("Docking in System Tray"),
648                         KStandardGuiItem::cont(),
649                         KStandardGuiItem::cancel(),
650                         QStringLiteral("HideOnCloseInfo")) == KMessageBox::Continue;
651             if (doit)
652                 m_trayIcon->hideWindow();
653 
654             return false;
655         }
656 
657         if (!Preferences::self()->showTrayIcon() && confirmQuit() == KMessageBox::Cancel)
658             return false;
659     }
660 
661     konvApp->prepareShutdown();
662 
663     return true;
664 }
665 
restore()666 bool MainWindow::restore()
667 {
668     // Default restore behavior shows the window based on the flag passed to it
669     // We need to read the respective session data before calling the method,
670     // so we can pass it in.
671     KConfigGroup config(KConfigGui::sessionConfig(), QStringLiteral("1"));
672     const bool show = !config.readEntry("docked", false);
673 
674     // TODO: also save & restore any TrayIcon state, needs API in KStatusNotifierItem
675 
676     // in case no window is shown & registered, the window manager needs to be told directly the start is done
677     if (!show)
678         KStartupInfo::appStarted();
679 
680     return KXmlGuiWindow::restore(1, show);
681 }
682 
saveProperties(KConfigGroup & config)683 void MainWindow::saveProperties(KConfigGroup &config)
684 {
685     KXmlGuiWindow::saveProperties(config);
686     config.writeEntry("docked", isHidden());
687 }
688 
hideEvent(QHideEvent * e)689 void MainWindow::hideEvent(QHideEvent *e)
690 {
691     Q_EMIT triggerRememberLine();
692 
693     m_statusBar->clearMainLabelTempText();
694 
695     KXmlGuiWindow::hideEvent(e);
696 }
697 
showEvent(QShowEvent * e)698 void MainWindow::showEvent(QShowEvent *e)
699 {
700     Q_EMIT cancelRememberLine();
701 
702     KXmlGuiWindow::showEvent(e);
703 }
704 
leaveEvent(QEvent * e)705 void MainWindow::leaveEvent(QEvent* e)
706 {
707     m_statusBar->clearMainLabelTempText();
708 
709     KXmlGuiWindow::leaveEvent(e);
710 }
711 
event(QEvent * e)712 bool MainWindow::event(QEvent* e)
713 {
714     if (e->type() == QEvent::StyleChange)
715     {
716         QMetaObject::invokeMethod(Application::instance(), "appearanceChanged");
717     }
718     else if (e->type() == QEvent::WindowActivate)
719     {
720         Q_EMIT endNotification();
721         Q_EMIT cancelRememberLine();
722     }
723     else if(e->type() == QEvent::WindowDeactivate)
724     {
725         m_statusBar->clearMainLabelTempText();
726 
727         if (qApp->activeModalWidget() == nullptr)
728             Q_EMIT triggerRememberLine();
729     }
730 
731     return KXmlGuiWindow::event(e);
732 }
733 
settingsChangedSlot()734 void MainWindow::settingsChangedSlot()
735 {
736     // This is for compressing the events. m_hasDirtySettings is set to true
737     // when the settings have changed, then set to false when the app reacts to it
738     // via the appearanceChanged signal.  This prevents a series of settingsChanged signals
739     // causing the app expensively rereading its settings many times.
740     // The appearanceChanged signal is connected to resetHasDirtySettings to reset this bool
741     if (!m_hasDirtySettings)
742     {
743         QTimer::singleShot(0, Application::instance(), &Application::appearanceChanged);
744         m_hasDirtySettings = true;
745     }
746 }
747 
resetHasDirtySettings()748 void MainWindow::resetHasDirtySettings()
749 {
750     m_hasDirtySettings = false;
751 }
752 
updateTrayIcon()753 void MainWindow::updateTrayIcon()
754 {
755     if (Preferences::self()->showTrayIcon())
756     {
757         if (!m_trayIcon)
758         {
759             // set up system tray
760             m_trayIcon = new Konversation::TrayIcon(this);
761             connect(this, &MainWindow::endNotification, m_trayIcon, &Konversation::TrayIcon::endNotification);
762             QMenu *trayMenu = m_trayIcon->contextMenu();
763             trayMenu->addAction(actionCollection()->action(QString::fromLatin1(KStandardAction::name(KStandardAction::Preferences))));
764             trayMenu->addAction(actionCollection()->action(QString::fromLatin1(KStandardAction::name(KStandardAction::ConfigureNotifications))));
765             trayMenu->addAction(actionCollection()->action(QStringLiteral("toggle_away")));
766         }
767 
768         m_trayIcon->setNotificationEnabled(Preferences::self()->trayNotify());
769     }
770     else
771     {
772         delete m_trayIcon;
773         m_trayIcon = nullptr;
774     }
775 }
776 
toggleMenubar(bool dontShowWarning)777 void MainWindow::toggleMenubar(bool dontShowWarning)
778 {
779     if (m_showMenuBarAction->isChecked())
780         menuBar()->show();
781     else
782     {
783         bool doit = true;
784         if (!dontShowWarning)
785         {
786             QString accel = m_showMenuBarAction->shortcut().toString();
787             doit = KMessageBox::warningContinueCancel(this,
788                     i18n("<qt>This will hide the menu bar completely. You can show it again by typing %1.</qt>", accel),
789                     i18n("Hide menu bar"),
790                     KStandardGuiItem::cont(),
791                     KStandardGuiItem::cancel(),
792                     QStringLiteral("HideMenuBarWarning")) == KMessageBox::Continue;
793         }
794         if (doit)
795             menuBar()->hide();
796         else
797             m_showMenuBarAction->setChecked (true);
798     }
799 
800     Preferences::self()->setShowMenuBar(m_showMenuBarAction->isChecked());
801 }
802 
openPrefsDialog()803 void MainWindow::openPrefsDialog()
804 {
805     //An instance of your dialog could be already created and could be cached,
806     //in which case you want to display the cached dialog instead of creating
807     //another one
808     if (!m_settingsDialog)
809     {
810         m_settingsDialog = new KonviSettingsDialog(this);
811         //User edited the configuration - update your local copies of the
812         //configuration data
813         connect(m_settingsDialog, &ConfigDialog::settingsChanged, this, &MainWindow::settingsChangedSlot);
814     }
815     m_settingsDialog->show();
816 }
817 
openKeyBindings()818 void MainWindow::openKeyBindings()
819 {
820     // Change a number of action names to make them friendlier for the shortcut list.
821     actionCollection()->action(QStringLiteral("tab_notifications"))->setText(i18n("Toggle Notifications"));
822     actionCollection()->action(QStringLiteral("toggle_away"))->setText(i18n("Toggle Away Globally"));
823     actionCollection()->action(QStringLiteral("irc_colors"))->setText(i18n("Insert &IRC Color..."));
824     actionCollection()->action(QStringLiteral("insert_character"))->setText(i18n("Insert Special &Character..."));
825     actionCollection()->action(QStringLiteral("insert_marker_line"))->setText(i18n("Insert &Marker Line"));
826     QString openChannelListString = actionCollection()->action(QStringLiteral("open_channel_list"))->text();
827     actionCollection()->action(QStringLiteral("open_channel_list"))->setText(i18n("&Channel List"));
828     QString openLogFileString = actionCollection()->action(QStringLiteral("open_logfile"))->text();
829     actionCollection()->action(QStringLiteral("open_logfile"))->setText(i18n("&Open Logfile"));
830 
831     // Open shortcut configuration dialog.
832     auto *dlg = new KShortcutsDialog(KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this);
833     dlg->setAttribute(Qt::WA_DeleteOnClose);
834     dlg->addCollection(actionCollection());
835     connect(dlg, &QDialog::finished, this, [this, openChannelListString, openLogFileString]() {
836         // Reset action names.
837         actionCollection()->action(QStringLiteral("tab_notifications"))->setText(i18n("Enable Notifications"));
838         actionCollection()->action(QStringLiteral("toggle_away"))->setText(i18n("Set &Away Globally"));
839         actionCollection()->action(QStringLiteral("irc_colors"))->setText(i18n("&IRC Color..."));
840         actionCollection()->action(QStringLiteral("insert_character"))->setText(i18n("Special &Character..."));
841         actionCollection()->action(QStringLiteral("insert_marker_line"))->setText(i18n("&Marker Line"));
842         actionCollection()->action(QStringLiteral("open_channel_list"))->setText(openChannelListString);
843         actionCollection()->action(QStringLiteral("open_logfile"))->setText(openLogFileString);
844     });
845 
846     dlg->configure(true /* save settings */);
847 }
848 
openServerList()849 void MainWindow::openServerList()
850 {
851     if (!m_serverListDialog)
852     {
853         m_serverListDialog = new Konversation::ServerListDialog(i18n("Server List"), this);
854         Application* konvApp = Application::instance();
855 
856         connect(m_serverListDialog, &Konversation::ServerListDialog::serverGroupsChanged,
857                 konvApp, &Application::serverGroupsChanged);
858         connect(konvApp, &Application::serverGroupsChanged,
859                 m_serverListDialog, &Konversation::ServerListDialog::updateServerList);
860         connect(m_serverListDialog, QOverload<Konversation::ConnectionFlag,int>::of(&Konversation::ServerListDialog::connectTo),
861                 konvApp->getConnectionManager(), QOverload<Konversation::ConnectionFlag,int>::of(&ConnectionManager::connectTo));
862         connect(m_serverListDialog, QOverload<Konversation::ConnectionFlag, ConnectionSettings>::of(&Konversation::ServerListDialog::connectTo),
863                 konvApp->getConnectionManager(), QOverload<Konversation::ConnectionFlag, ConnectionSettings>::of(&ConnectionManager::connectTo));
864         connect(konvApp->getConnectionManager(), &ConnectionManager::closeServerList, m_serverListDialog, &QDialog::reject);
865     }
866 
867     m_serverListDialog->show();
868 }
869 
openQuickConnectDialog()870 void MainWindow::openQuickConnectDialog()
871 {
872     Q_EMIT showQuickConnectDialog();
873 }
874 
875 
openIdentitiesDialog()876 void MainWindow::openIdentitiesDialog()
877 {
878     QPointer<Konversation::IdentityDialog> dlg = new Konversation::IdentityDialog(this);
879     if (dlg->exec() == QDialog::Accepted)
880     {
881         if (m_serverListDialog)
882             m_serverListDialog->updateServerList();
883         m_viewContainer->updateViewEncoding(m_viewContainer->getFrontView());
884     }
885     delete dlg;
886 }
887 
editIdentity(const IdentityPtr & identity)888 IdentityPtr MainWindow::editIdentity(const IdentityPtr &identity)
889 {
890     IdentityPtr newIdentity;
891 
892     QPointer<Konversation::IdentityDialog> dlg = new Konversation::IdentityDialog(this);
893     newIdentity = dlg->setCurrentIdentity(identity);
894 
895     if ((dlg->exec() == QDialog::Accepted) && m_serverListDialog)
896     {
897         m_serverListDialog->updateServerList();
898         delete dlg;
899         return newIdentity;
900     }
901     else
902     {
903         delete dlg;
904         return IdentityPtr();
905     }
906 }
907 
openNotifications()908 void MainWindow::openNotifications()
909 {
910     (void) KNotifyConfigWidget::configure(this);
911 }
912 
notifyAction(int connectionId,const QString & nick)913 void MainWindow::notifyAction(int connectionId, const QString& nick)
914 {
915     Application* konvApp = Application::instance();
916     Server* server = konvApp->getConnectionManager()->getServerByConnectionId(connectionId);
917     if (server) server->notifyAction(nick);
918 }
919 
920 // TODO: Let an own class handle notify things
setOnlineList(Server * notifyServer,const QStringList &,bool)921 void MainWindow::setOnlineList(Server* notifyServer,const QStringList& /*list*/, bool /*changed*/)
922 {
923     Q_EMIT nicksNowOnline(notifyServer);
924     // FIXME  if (changed && nicksOnlinePanel) newText(nicksOnlinePanel, QString(), true);
925 }
926 
toggleVisibility()927 void MainWindow::toggleVisibility()
928 {
929     if (isActiveWindow())
930     {
931         if (Preferences::self()->showTrayIcon())
932             m_trayIcon->hideWindow();
933         else
934             KWindowSystem::minimizeWindow(winId());
935     }
936     else
937     {
938         activateRaiseAndMoveToDesktop(MoveToDesktop);
939     }
940 }
941 
activateAndRaiseWindow()942 void MainWindow::activateAndRaiseWindow()
943 {
944     activateRaiseAndMoveToDesktop(NoMoveToDesktop);
945 }
946 
activateRaiseAndMoveToDesktop(MoveToDesktopMode moveToDesktop)947 void MainWindow::activateRaiseAndMoveToDesktop(MoveToDesktopMode moveToDesktop)
948 {
949     if (isMinimized())
950         KWindowSystem::unminimizeWindow(winId());
951     else if (Preferences::self()->showTrayIcon() && !isVisible())
952         m_trayIcon->restoreWindow();
953 
954     if (!isActiveWindow())
955     {
956         raise();
957         if (moveToDesktop == MoveToDesktop)
958         {
959             KWindowInfo info(winId(), NET::WMDesktop);
960             if (!info.onAllDesktops())
961                 KWindowSystem::setOnDesktop(winId(), KWindowSystem::currentDesktop());
962         }
963         KWindowSystem::activateWindow(winId());
964     }
965 }
966