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