1 //=============================================================================
2 //
3 //   File : KviApplication.cpp
4 //   Creation date : Sun Jun 18 2000 12:39:45 by Szymon Stefanek
5 //
6 //   This file is part of the KVIrc IRC client distribution
7 //   Copyright (C) 2000-2010 Szymon Stefanek (pragma at kvirc dot net)
8 //
9 //   This program is FREE software. You can redistribute it and/or
10 //   modify it under the terms of the GNU General Public License
11 //   as published by the Free Software Foundation; either version 2
12 //   of the License, or (at your option) any later version.
13 //
14 //   This program is distributed in the HOPE that it will be USEFUL,
15 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
16 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17 //   See the GNU General Public License for more details.
18 //
19 //   You should have received a copy of the GNU General Public License
20 //   along with this program. If not, write to the Free Software Foundation,
21 //   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 //
23 //=============================================================================
24 
25 #define _KVI_APP_CPP_
26 
27 #include "kvi_socket.h"
28 #include "KviApplication.h"
29 #include "kvi_debug.h"
30 #include "KviMainWindow.h"
31 #include "KviMessageBox.h"
32 #include "KviIconManager.h"
33 #include "KviInput.h"
34 #include "KviInputHistory.h"
35 #include "KviConfigurationFile.h"
36 #include "KviColorSelectionWindow.h"
37 #include "KviWindow.h"
38 #include "KviIrcServerDataBase.h"
39 #include "KviProxyDataBase.h"
40 #include "KviMenuBar.h"
41 #include "KviOptions.h"
42 #include "KviIrcServerParser.h"
43 #include "KviModuleManager.h"
44 #include "KviMediaManager.h"
45 #include "KviRegisteredUserDataBase.h"
46 #include "KviThread.h"
47 #include "KviSharedFilesManager.h"
48 #include "kvi_confignames.h"
49 #include "KviWindowListBase.h"
50 #include "kvi_defaults.h"
51 #include "KviLocale.h"
52 #include "kvi_out.h"
53 #include "KviNickServRuleSet.h"
54 #include "KviIdentityProfileSet.h"
55 #include "KviDefaultScript.h"
56 #include "KviXlib.h"
57 #include "KviTextIconManager.h"
58 #include "KviTextIconWindow.h"
59 #include "KviHistoryWindow.h"
60 #include "KviCtcpPageDialog.h"
61 #include "KviRegisteredChannelDataBase.h"
62 #include "KviModuleExtension.h"
63 #include "KviInternalCommand.h"
64 #include "KviFileTransfer.h"
65 #include "KviControlCodes.h"
66 #include "KviIrcUrl.h"
67 #include "KviAvatarCache.h"
68 #include "KviActionManager.h"
69 #include "KviCustomToolBarManager.h"
70 #include "KviFileUtils.h"
71 #include "KviTimeUtils.h"
72 #include "KviStringConversion.h"
73 #include "KviUserIdentityManager.h"
74 #include "KviIrcView.h"
75 #include "KviEnvironment.h"
76 #include "KviAnimatedPixmapCache.h"
77 #include "KviKvs.h"
78 #include "KviKvsScript.h"
79 #include "KviKvsPopupManager.h"
80 #include "KviKvsKernel.h"
81 #include "KviKvsObjectController.h"
82 #include "KviKvsEventTriggers.h"
83 #include "kvi_sourcesdate.h"
84 #include "KviPointerHashTable.h"
85 #include "KviQueryWindow.h"
86 #include "KviCaster.h"
87 #include "KviSignalHandler.h"
88 #include "KviPtrListIterator.h"
89 #include "KviIrcNetwork.h"
90 #include "KviRuntimeInfo.h"
91 
92 #include <QMenu>
93 #include <QPainter>
94 #include <algorithm>
95 
96 #ifndef COMPILE_NO_IPC
97 #include "KviIpcSentinel.h"
98 #endif
99 
100 #ifdef COMPILE_CRYPT_SUPPORT
101 #include "KviCryptEngineManager.h"
102 #endif
103 
104 #ifdef COMPILE_SSL_SUPPORT
105 #include "KviSSL.h"
106 #endif
107 
108 #include <QFileInfo>
109 #include <QSplitter>
110 #include <QClipboard>
111 #include <QMessageBox>
112 #include <QTextCodec>
113 #include <QMetaObject>
114 #include <QCommonStyle>
115 
116 #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
117 #include <QPluginLoader>
118 #include <QtWin>
119 #endif
120 
121 #ifdef COMPILE_DBUS_SUPPORT
122 #include <QDBusInterface>
123 #endif
124 
125 #ifdef COMPILE_KDE_SUPPORT
126 #ifdef COMPILE_KDE4_SUPPORT
127 #include <KStandardDirs>
128 #else
129 #include <QStandardPaths>
130 #endif
131 #include <KNotification>
132 #endif
133 
134 #ifdef COMPILE_QX11INFO_SUPPORT
135 #include <QX11Info>
136 #endif
137 
138 /*
139 HACK These 2 hacks are defined because X11 defines Unsorted and None
140 which conflicts with QDir and KviApplication::KvircSubdir
141 DO NOT REMOVE THEM EVEN IF THEY ARE DEFINED ALSO IN KviApplication.h
142 */
143 #ifdef Unsorted
144 #undef Unsorted
145 #endif
146 
147 #ifdef None
148 #undef None
149 #endif
150 
151 #include <QDir>
152 
153 #include <cstdlib>  // rand & srand
154 #include <ctime>    // time() in srand()
155 #include <map>      // std::map<>
156 
157 // Global application pointer
158 KVIRC_API KviApplication * g_pApp = nullptr;
159 
160 KviConfigurationFile * g_pWinPropertiesConfig = nullptr;
161 KVIRC_API KviIrcServerDataBase * g_pServerDataBase = nullptr;
162 KVIRC_API KviProxyDataBase * g_pProxyDataBase = nullptr;
163 
164 // Global windows
165 KVIRC_API KviColorWindow * g_pColorWindow = nullptr;
166 KVIRC_API KviTextIconWindow * g_pTextIconWindow = nullptr;
167 KVIRC_API QMenu * g_pInputPopup = nullptr;
168 KVIRC_API QStringList * g_pRecentTopicList = nullptr;
169 KVIRC_API std::map<QString, KviWindow *> g_pGlobalWindowDict;
170 KVIRC_API KviMediaManager * g_pMediaManager = nullptr;
171 KVIRC_API KviSharedFilesManager * g_pSharedFilesManager = nullptr;
172 KVIRC_API KviNickServRuleSet * g_pNickServRuleSet = nullptr;
173 KVIRC_API KviCtcpPageDialog * g_pCtcpPageDialog = nullptr;
174 KVIRC_API KviRegisteredChannelDataBase * g_pRegisteredChannelDataBase = nullptr;
175 KVIRC_API KviHistoryWindowWidget * g_pHistoryWindow = nullptr;
176 
177 // this is eventually set by libkviident
178 KVIRC_API int g_iIdentDaemonRunningUsers = 0;
179 
180 // Loaded and destroyed by KviIconManager
181 QPixmap * g_pUserChanStatePixmap = nullptr;
182 QPixmap * g_pActivityMeterPixmap = nullptr;
183 
184 #ifdef COMPILE_PSEUDO_TRANSPARENCY
185 
186 #include <QImage>
187 
188 KVIRC_API QPixmap * g_pShadedParentGlobalDesktopBackground = nullptr; // the pixmap that we use for MdiManager
189 KVIRC_API QPixmap * g_pShadedChildGlobalDesktopBackground = nullptr;  // the pixmap that we use for MdiChild
190 #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
191 #include <winuser.h>
192 #include <QDesktopWidget>
193 #endif
194 #endif
195 
196 #ifdef COMPILE_CRYPT_SUPPORT
197 #include "KviCryptEngine.h"
198 // global crypt engine manager
199 KVIRC_API KviCryptEngineManager * g_pCryptEngineManager = nullptr;
200 #endif
201 
202 #include <QStyleFactory>
203 
KviApplication(int & argc,char ** argv)204 KviApplication::KviApplication(int & argc, char ** argv)
205     : KviTalApplication(argc, argv)
206 {
207 	setApplicationName("KVIrc");
208 	setApplicationVersion(KVIRC_VERSION_RELEASE);
209 	setOrganizationDomain("kvirc.net");
210 	setOrganizationName("KVIrc");
211 
212 	// Ok...everything begins here
213 	g_pApp = this;
214 	m_szConfigFile = QString();
215 	m_bCreateConfig = false;
216 	m_bUpdateGuiPending = false;
217 	m_pRecentChannelDict = nullptr;
218 #ifndef COMPILE_NO_IPC
219 	m_pIpcSentinel = nullptr;
220 #endif
221 	m_iHeartbeatTimerId = -1;
222 	m_fntDefaultFont = font();
223 	m_bSetupDone = false;
224 	m_bClosingDown = false;
225 	kvi_socket_flushTrafficCounters();
226 	// don't let qt quit the application by itself
227 	setQuitOnLastWindowClosed(false);
228 #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
229 	m_bPortable = KviFileUtils::fileExists(g_pApp->applicationDirPath() + KVI_PATH_SEPARATOR_CHAR + "portable");
230 #endif
231 
232 //note: the early qApp->style() call leads to a crash on osx
233 #if !defined(COMPILE_ENABLE_GTKSTYLE) && !defined(COMPILE_ON_MAC)
234 	// workaround for gtk+ style forcing a crappy white background (ticket #777, #964, #1009, ..)
235 	if(QString("QGtkStyle").compare(qApp->style()->metaObject()->className()) == 0)
236 	{
237 		setStyle(QStyleFactory::create("Fusion"));
238 		setPalette(style()->standardPalette());
239 	}
240 #endif
241 }
242 
setup()243 void KviApplication::setup()
244 {
245 	// Here we start up all the substystems.
246 	// This is a really critical phase since in general subsystems depend
247 	// on each other and we must activate them in the right order.
248 	// Don't move stuff around unless you really know what you're doing.
249 
250 	// Initialize the random number generator
251 	::srand(::time(nullptr));
252 
253 #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
254 	// setup winsock.dll
255 	WSADATA w;
256 	WSAStartup(MAKELONG((unsigned short)2, (unsigned short)0), &w);
257 #endif
258 
259 #ifndef COMPILE_NO_IPC
260 	// Create this very early
261 	// FIXME: this is still not early enough... we actually HAVE race conditions (should use a file for locking this ?)
262 	createIpcSentinel();
263 #endif
264 
265 	// Initialize the thread manager early
266 	KviThreadManager::globalInit();
267 
268 #ifdef COMPILE_SSL_SUPPORT
269 	KviSSL::globalInit();
270 #endif
271 
272 	::kvi_signalHandlerSetup();
273 
274 	// Setup our filesystem and initialize locale
275 	loadDirectories();
276 	KviStringConversion::init(m_szGlobalKvircDir, m_szLocalKvircDir);
277 
278 	g_pIconManager = new KviIconManager();
279 
280 	// add KVIrc common dirs to QT searchpath
281 	QString szPath;
282 	getLocalKvircDirectory(szPath, None, "avatars");
283 	QDir::addSearchPath("avatars", szPath);
284 	getLocalKvircDirectory(szPath, None, "pics");
285 	QDir::addSearchPath("pics", szPath);
286 	getLocalKvircDirectory(szPath, None, "audio");
287 	QDir::addSearchPath("audio", szPath);
288 
289 #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
290 	// need to load image plugins :(
291 	QString szPluginsDir;
292 	getGlobalKvircDirectory(szPluginsDir, None, "qt-plugins/");
293 	addLibraryPath(szPluginsDir);
294 //MessageBox(0,QApplication::libraryPaths().join(";").toLocal8Bit().data(),"KVIrc",0);
295 //KviMessageBox::information(libraryPaths().join(";"));
296 //qDebug("%1",loader.isLoaded());
297 #endif
298 
299 	// Make sure that the C strings are translated to utf8
300 	// This is a fallback solution anyway: we should use the appropriate
301 	// encoding every time.
302 	QTextCodec * pUTF8Codec = KviLocale::instance()->codecForName("UTF-8");
303 	if(pUTF8Codec)
304 		QTextCodec::setCodecForLocale(pUTF8Codec);
305 	else
306 		qDebug("Aaargh... have no UTF-8 codec?");
307 
308 	QString szTmp;
309 
310 	// Initialize the scripting engine
311 	KviKvs::init();
312 
313 	// Initialize the action manager
314 	KviActionManager::init();
315 
316 	// Create the module manager early: so the other managers can load modules
317 	g_pModuleExtensionManager = new KviModuleExtensionManager();
318 
319 	g_pModuleManager = new KviModuleManager();
320 
321 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_USERACTIONS))
322 		KviActionManager::instance()->load(szTmp);
323 
324 	// Initialize and load the identities
325 	KviUserIdentityManager::init();
326 
327 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_IDENTITIES))
328 		KviUserIdentityManager::instance()->load(szTmp);
329 
330 	KviAnimatedPixmapCache::init();
331 
332 	// Load the remaining configuration
333 	// Note that loadOptions() assumes that the current progress is 12 and
334 	// will bump it up to 45 in small steps
335 	loadOptions();
336 
337 	// set the global font if needed
338 	updateApplicationFont();
339 
340 #ifdef COMPILE_PSEUDO_TRANSPARENCY
341 	updatePseudoTransparency();
342 #endif
343 
344 	// enforce our "icon in popups" option - this is done also in each updateGui() call
345 	setAttribute(Qt::AA_DontShowIconsInMenus, !KVI_OPTION_BOOL(KviOption_boolShowIconsInPopupMenus));
346 
347 	// Load the win properties config
348 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_WINPROPERTIES);
349 	g_pWinPropertiesConfig = new KviConfigurationFile(szTmp, KviConfigurationFile::ReadWrite);
350 
351 	// Load the server database
352 	g_pServerDataBase = new KviIrcServerDataBase();
353 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_SERVERDB))
354 		g_pServerDataBase->load(szTmp);
355 
356 	// Load the proxy database
357 	g_pProxyDataBase = new KviProxyDataBase();
358 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_PROXYDB))
359 		g_pProxyDataBase->load(szTmp);
360 
361 	// Event manager
362 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_EVENTS))
363 		KviKvs::loadAppEvents(szTmp);
364 
365 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_RAWEVENTS))
366 		KviKvs::loadRawEvents(szTmp);
367 
368 	// Popup manager
369 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_POPUPS))
370 		KviKvs::loadPopups(szTmp);
371 
372 	KviCustomToolBarManager::init();
373 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_CUSTOMTOOLBARS))
374 		KviCustomToolBarManager::instance()->load(szTmp);
375 
376 	// Alias manager
377 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_ALIASES))
378 		KviKvs::loadAliases(szTmp);
379 
380 	// Script addons manager (this in fact has delayed loading, so we don't even care
381 	// about showing up an entry in the splash screen)
382 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_SCRIPTADDONS))
383 		KviKvs::loadScriptAddons(szTmp);
384 
385 	g_pTextIconManager = new KviTextIconManager();
386 	g_pTextIconManager->load();
387 
388 	// load the recent data lists
389 	g_pRecentTopicList = new QStringList();
390 	//g_pBookmarkList = new QStringList();
391 	loadRecentEntries();
392 
393 	// media manager
394 	g_pMediaManager = new KviMediaManager();
395 	g_pMediaManager->lock();
396 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_MEDIATYPES))
397 		g_pMediaManager->load(szTmp);
398 	g_pMediaManager->unlock();
399 
400 	// registered user data base
401 	g_pRegisteredUserDataBase = new KviRegisteredUserDataBase();
402 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_REGUSERDB))
403 		g_pRegisteredUserDataBase->load(szTmp);
404 
405 	// registered channel data base
406 	g_pRegisteredChannelDataBase = new KviRegisteredChannelDataBase();
407 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_REGCHANDB))
408 		g_pRegisteredChannelDataBase->load(szTmp);
409 
410 	// file trader
411 	g_pSharedFilesManager = new KviSharedFilesManager();
412 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_SHAREDFILES))
413 		g_pSharedFilesManager->load(szTmp);
414 
415 	// nick serv data base
416 	g_pNickServRuleSet = new KviNickServRuleSet();
417 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_NICKSERVDATABASE))
418 		g_pNickServRuleSet->load(szTmp);
419 
420 	// Identity profiles database
421 	KviIdentityProfileSet::init();
422 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_PROFILESDATABASE))
423 		KviIdentityProfileSet::instance()->load(szTmp);
424 
425 	KviAvatarCache::init();
426 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_AVATARCACHE))
427 		KviAvatarCache::instance()->load(szTmp);
428 
429 	KviInputHistory::init();
430 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_INPUTHISTORY))
431 		KviInputHistory::instance()->load(szTmp);
432 
433 	KviDefaultScriptManager::init();
434 	if(getReadOnlyConfigPath(szTmp, KVI_CONFIGFILE_DEFAULTSCRIPT))
435 		KviDefaultScriptManager::instance()->load(szTmp);
436 	else
437 		KviDefaultScriptManager::instance()->loadEmptyConfig();
438 
439 // Eventually initialize the crypt engine manager
440 #ifdef COMPILE_CRYPT_SUPPORT
441 	g_pCryptEngineManager = new KviCryptEngineManager();
442 #endif
443 
444 	// and the input popup
445 	g_pInputPopup = new QMenu();
446 
447 	// create the server parser
448 	g_pServerParser = new KviIrcServerParser();
449 
450 	// Script object controller
451 	//g_pScriptObjectController = new KviScriptObjectController(); gone
452 
453 	// Cache the QStyle theme before it's overriden
454 	(void)KviRuntimeInfo::qtTheme();
455 
456 	// create the frame window, we're almost up and running...
457 	createFrame();
458 
459 	// ok, we also have an UI now
460 
461 	// check if this is the first time this version of KVIrc runs...
462 	if(firstTimeRun())
463 	{
464 		// Finish the setup...
465 		setupFinish();
466 		// ensure mainwindow is visible
467 		g_pMainWindow->show();
468 	}
469 
470 	// hello world!
471 	m_bSetupDone = true;
472 
473 	// We're REALLY up and running!
474 	// kill the splash screen
475 
476 	if(KVI_OPTION_BOOL(KviOption_boolShowServersConnectDialogOnStart))
477 		g_pMainWindow->executeInternalCommand(KVI_INTERNALCOMMAND_SERVERSJOIN_OPEN);
478 
479 	// check if we're in trouble...
480 	checkSuggestRestoreDefaultScript();
481 
482 	// start our heartbeat now
483 	m_iHeartbeatTimerId = startTimer(1000);
484 }
485 
frameDestructorCallback()486 void KviApplication::frameDestructorCallback()
487 {
488 	// here we should kill anything that depends on g_pMainWindow, g_pActiveWindow and related being non zero...
489 	// kill all the objects, while we have windows...
490 	if(KviKvsKernel::instance())
491 	{
492 		if(KviKvsKernel::instance()->objectController())
493 		{
494 			KviKvsKernel::instance()->objectController()->clearInstances();
495 		}
496 	}
497 }
498 
~KviApplication()499 KviApplication::~KviApplication()
500 {
501 	// Another critical phase.
502 	// We shutdown our subsystems in the right order here.
503 
504 	m_bClosingDown = true;
505 
506 #ifndef COMPILE_NO_IPC
507 	destroyIpcSentinel();
508 #endif
509 
510 	// if we still have a frame: kill it
511 	if(g_pMainWindow)
512 		delete g_pMainWindow;
513 	g_pActiveWindow = nullptr; // .. but it should be already 0 anyway
514 
515 	if(g_pCtcpPageDialog)
516 		delete g_pCtcpPageDialog;
517 
518 	if(!m_bSetupDone)
519 		return; // killed with IPC (nothing except the m_pFrameList was created yet)
520 
521 	KviFileTransferManager::cleanup();
522 
523 	// No more events are triggered from now on and no KVS commands are executed
524 
525 	// Dangerous part.... we're unloading all the modules
526 	// We need to unload them early since they may use other subsystems
527 	// that we are going to kill now.
528 	delete g_pModuleManager;
529 	delete g_pModuleExtensionManager;
530 	// No more external modules exist: all that happens from now is generated
531 	// from inside the kvirc core.
532 
533 	// We should have almost no UI here: only certain dialogs or popup windows may
534 	// still exist: they should be harmless tough.
535 	saveOptions();
536 	saveIdentities();
537 	KviUserIdentityManager::done();
538 	if(m_pRecentChannelDict)
539 		delete m_pRecentChannelDict;
540 	// now kill the stuff that the frame depends on
541 	saveIrcServerDataBase();
542 	delete g_pServerDataBase;
543 	saveProxyDataBase();
544 	delete g_pProxyDataBase;
545 	delete g_pWinPropertiesConfig;
546 	saveTextIcons();
547 	if(g_pTextIconWindow)
548 		delete g_pTextIconWindow;
549 	delete g_pTextIconManager;
550 	delete g_pIconManager;
551 	delete g_pServerParser;
552 	saveMediaTypes();
553 	delete g_pMediaManager;
554 	saveRecentEntries();
555 	saveAvatarCache();
556 	KviAvatarCache::done();
557 	//delete g_pBookmarkList;
558 	delete g_pRecentTopicList;
559 	saveRegisteredUsers();
560 	delete g_pRegisteredUserDataBase;
561 	saveRegisteredChannels();
562 	delete g_pRegisteredChannelDataBase;
563 	saveNickServ();
564 	delete g_pNickServRuleSet;
565 	saveIdentityProfiles();
566 	KviIdentityProfileSet::done();
567 	saveDefaultScripts();
568 	KviDefaultScriptManager::done();
569 	saveSharedFiles();
570 	delete g_pSharedFilesManager;
571 	saveAppEvents();
572 	saveRawEvents();
573 	saveToolBars();
574 	KviCustomToolBarManager::done();
575 	savePopups();
576 	saveAliases();
577 	g_pGlobalWindowDict.clear();
578 	saveScriptAddons();
579 	// kill the remaining resources
580 	delete g_pColorWindow;
581 	if(g_pHistoryWindow)
582 		delete g_pHistoryWindow;
583 	saveInputHistory();
584 	KviInputHistory::done();
585 	delete g_pInputPopup;
586 //delete g_pScriptObjectController;
587 #ifdef COMPILE_CRYPT_SUPPORT
588 	delete g_pCryptEngineManager;
589 #endif
590 #ifdef COMPILE_PSEUDO_TRANSPARENCY
591 	destroyPseudoTransparency();
592 #endif
593 	m_PendingAvatarChanges.clear();
594 	KviAnimatedPixmapCache::done();
595 // Kill the thread manager.... all the slave threads should have been already terminated ...
596 #ifdef COMPILE_SSL_SUPPORT
597 	KviSSL::globalDestroy();
598 #endif
599 	KviThreadManager::globalDestroy();
600 	// kill the scripting engine
601 	KviKvs::done();
602 	// shut up the action manager
603 	saveActions();
604 	KviActionManager::done();
605 
606 #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
607 	WSACleanup();
608 #endif
609 
610 	KviLocale::done();
611 	// goodbye cruel world...
612 }
613 
614 static int g_iGloballyUniqueId = 0;
615 
getGloballyUniqueId()616 int KviApplication::getGloballyUniqueId()
617 {
618 	g_iGloballyUniqueId++;
619 	return g_iGloballyUniqueId;
620 }
621 
supportsCompositing()622 bool KviApplication::supportsCompositing()
623 {
624 #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
625 	//we need >= win2000
626 	return true;
627 #endif
628 
629 #ifdef COMPILE_QX11INFO_SUPPORT
630 	// Qt5 does not support QX11Info::isCompositingManagerRunning()
631 	// Well...assume we're compositing capable, should be true on all recent Linux distros
632 	return true;
633 #endif // COMPILE_QX11INFO_SUPPORT
634 #ifdef COMPILE_ON_MAC
635 	return true;
636 #endif
637 	return false;
638 };
639 
notifierMessage(KviWindow * pWnd,int iIconId,const QString & szMsg,unsigned int uMessageLifetime)640 void KviApplication::notifierMessage(KviWindow * pWnd, int iIconId, const QString & szMsg, unsigned int uMessageLifetime)
641 {
642 	/*
643 	We have different behaviour depending on support enabled at compile time (env)
644 	and options enabled by the user (opt).
645 	Let's see the scheme to understand which is chosen:
646 
647 	1: env: DBus, KDE
648 	   opt: a: enabled, enabled   -> KDE
649 	        b: enabled, disabled  -> DBus
650 	        c: disabled, disabled -> KVIrc
651 
652 	2: env: DBus, no KDE
653 	   opt: a: enabled  -> DBus
654 	        b: disabled -> KVIrc
655 
656 	3: env: no DBus, no KDE -> KVIrc
657 	 ______           __________           ______________
658 	|      |         | Want KDE |         |  Scheme 1a   |
659 	| KDE  |---YES-->| Notifier |---YES-->| KDE Notifier |
660 	|______|         |__________|         |______________|
661 	   |                  |
662 	   NO                 NO
663 	   |                  |
664 	 __V___          _____V_____           _______________
665 	|      |        | Want DBus |         | Schemes 1b-2a |
666 	| DBus |--YES-->| Notifier  |---YES-->| DBus Notifier |
667 	|______|        |___________|         |_______________|
668 	   |                  |
669 	   NO                 NO
670 	   |                  |
671 	 __V__________________V_____
672 	|    Schemes 1c - 2b - 3    |
673 	|           KVIrc           |
674 	|___________________________|
675 
676 	:)
677 	*/
678 
679 	if(!pWnd)
680 		return;
681 
682 	QString szTitle = __tr2qs("KVIrc Messaging");
683 	QString szText = __tr2qs("Message from %1:\n\n").arg(pWnd->target());
684 	szText += KviControlCodes::stripControlBytes(szMsg);
685 
686 #ifdef COMPILE_KDE_SUPPORT
687 	if(KVI_OPTION_BOOL(KviOption_boolUseKDENotifier))
688 	{
689 		// Scheme 1a: KDE
690 		static bool bKNotifyConfigFileChecked = false;
691 
692 		if(!bKNotifyConfigFileChecked)
693 		{
694 #ifdef COMPILE_KDE4_SUPPORT
695 			QString szFileName = KStandardDirs::locateLocal("data", QString::fromUtf8("kvirc/kvirc.notifyrc"));
696 #else
697 			QString szFileName = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/" + QString::fromUtf8("kvirc/kvirc.notifyrc");
698 #endif
699 			if(szFileName.isEmpty())
700 				szFileName = QString::fromUtf8("%1/.kde/share/apps/kvirc/kvirc.notifyrc").arg(QDir::homePath());
701 
702 			QFileInfo inf(szFileName);
703 
704 			if(!inf.exists())
705 			{
706 				KviFileUtils::makeDir(inf.absolutePath());
707 
708 				QString szKNotifyConfig = QString::fromUtf8(
709 				    "[Global]\n"
710 				    "IconName=kvirc\n"
711 				    "Comment=The K-Visual IRC Client\n"
712 				    "Name=kvirc\n"
713 				    "\n"
714 				    "[Event/incomingMessage]\n"
715 				    "Name=KVIrc\n"
716 				    "Comment=Someone sent us a message\n"
717 				    "Action=Popup|Taskbar\n"
718 				    "Persistent=true\n");
719 
720 				KviFileUtils::writeFile(szFileName, szKNotifyConfig);
721 			}
722 
723 			bKNotifyConfigFileChecked = true;
724 		}
725 
726 		QStringList actions;
727 		actions << __tr2qs("View");
728 		actions << __tr2qs("Ignore");
729 
730 		QPixmap * pIcon = nullptr;
731 		KviIconManager::SmallIcon eIcon = KviIconManager::None;
732 		switch(pWnd->type())
733 		{
734 			case KviWindow::Console:
735 				eIcon = KviIconManager::Links;
736 				break;
737 			case KviWindow::Channel:
738 				eIcon = KviIconManager::Channel;
739 				break;
740 			case KviWindow::Query:
741 			{
742 				KviQueryWindow * pQuery = KVI_DYNAMIC(KviQueryWindow *, pWnd);
743 				if(!pQuery)
744 					break;
745 
746 				KviUserListEntry * pEntry = pQuery->userListView()->findEntry(pWnd->target());
747 				if(!pEntry)
748 					break;
749 
750 				KviAvatar * pAvatar = pEntry->globalData()->avatar();
751 				if(!pAvatar)
752 					break;
753 
754 				pIcon = pAvatar->pixmap();
755 				if(!pIcon)
756 					eIcon = KviIconManager::Query;
757 			}
758 			break;
759 			case KviWindow::SocketSpy:
760 				eIcon = KviIconManager::Spy;
761 				break;
762 			case KviWindow::DccChat:
763 				eIcon = KviIconManager::DccMsg;
764 				break;
765 			case KviWindow::DccTransfer:
766 				eIcon = KviIconManager::Dcc;
767 				break;
768 			case KviWindow::UserWindow:
769 				eIcon = KviIconManager::UserWindow;
770 				break;
771 			case KviWindow::Debug:
772 				eIcon = KviIconManager::Bomb;
773 				break;
774 			//KviWindow::DeadChannel
775 			//KviWindow::DeadQuery
776 			//KviWindow::Editor
777 			//KviWindow::Help
778 			//KviWindow::Terminal
779 			//KviWindow::Links
780 			//KviWindow::List
781 			//KviWindow::DccCanvas
782 			//KviWindow::DccVoice
783 			//KviWindow::DccVideo
784 			//KviWindow::Tool
785 			//KviWindow::IOGraph
786 			//KviWindow::ScriptEditor
787 			//KviWindow::ScriptObject
788 			//KviWindow::LogView
789 			//KviWindow::Offer
790 			case KviWindow::Unknown:
791 			default:
792 				eIcon = KviIconManager::KVIrc;
793 				break;
794 		}
795 
796 		if(!pIcon)
797 			pIcon = g_pIconManager->getSmallIcon(eIcon);
798 
799 		KNotification * pNotify = nullptr;
800 #if defined(COMPILE_KDE4_SUPPORT)
801 #if KDE_IS_VERSION(4, 4, 0)
802 		pNotify = new KNotification("incomingMessage", KNotification::CloseWhenWidgetActivated, this);
803 #else
804 		pNotify = new KNotification("incomingMessage", g_pMainWindow, KNotification::CloseWhenWidgetActivated);
805 #endif
806 #else
807 		pNotify = new KNotification("incomingMessage", g_pMainWindow, KNotification::CloseWhenWidgetActivated);
808 #endif
809 		pNotify->setFlags(KNotification::Persistent);
810 		pNotify->setTitle(szTitle);
811 		pNotify->setText(szText);
812 		pNotify->setActions(actions);
813 		pNotify->setPixmap(*pIcon);
814 
815 #if defined(COMPILE_KDE4_SUPPORT)
816 		pNotify->setComponentData(KComponentData(aboutData()));
817 #else
818 		pNotify->setComponentName("kvirc");
819 #endif
820 
821 		connect(pNotify, SIGNAL(activated()), this, SLOT(showParentFrame()));
822 		connect(pNotify, SIGNAL(action1Activated()), this, SLOT(showParentFrame()));
823 		connect(pNotify, SIGNAL(action2Activated()), pNotify, SLOT(close()));
824 		connect(pNotify, SIGNAL(ignored()), pNotify, SLOT(close()));
825 
826 		pNotify->sendEvent();
827 	}
828 	else
829 	{
830 #endif // COMPILE_KDE_SUPPORT
831 #ifdef COMPILE_DBUS_SUPPORT
832 		if(KVI_OPTION_BOOL(KviOption_boolUseDBusNotifier))
833 		{
834 			// Scheme 1b-2a: DBus
835 			QString szIcon = g_pIconManager->getSmallIconName(KviIconManager::KVIrc);
836 
837 			// org.freedesktop.Notifications.Notify
838 			QVariantList args;
839 			args << QString("KVIrc");                          // application name
840 			args << QVariant(QVariant::UInt);                  // notification id
841 			args << szIcon;                                    // application icon
842 			args << szTitle;                                   // summary text
843 			args << szText;                                    // detailed text
844 			args << QStringList();                             // actions, optional
845 			args << QVariantMap();                             // hints, optional
846 			args << static_cast<int>(uMessageLifetime * 1000); // timeout in msecs
847 
848 			QDBusInterface * pNotify = new QDBusInterface("org.freedesktop.Notifications", "/org/freedesktop/Notifications", "org.freedesktop.Notifications", QDBusConnection::sessionBus(), this);
849 			QDBusMessage reply = pNotify->callWithArgumentList(QDBus::Block, "Notify", args);
850 			if(reply.type() == QDBusMessage::ErrorMessage)
851 			{
852 				QDBusError err = reply;
853 				qDebug("D-Bus notify error\nID: %u\nName: %s\nMessage: %s\n", reply.arguments().first().toUInt(), qPrintable(err.name()), qPrintable(err.message()));
854 			}
855 		}
856 		else
857 		{
858 #endif // COMPILE_DBUS_SUPPORT
859 			// Scheme 1c-2b-3: KVIrc
860 			KviModule * m = g_pModuleManager->getModule("notifier");
861 			if(!m)
862 				return;
863 
864 			KviNotifierMessageParam s;
865 			s.pWindow = pWnd;
866 			s.szIcon.sprintf("%d", iIconId);
867 			s.szMessage = szMsg;
868 			s.uMessageLifetime = uMessageLifetime;
869 
870 			m->ctrl("notifier::message", (void *)&s);
871 #ifdef COMPILE_DBUS_SUPPORT
872 		}
873 #endif // COMPILE_DBUS_SUPPORT
874 #ifdef COMPILE_KDE_SUPPORT
875 	}
876 #endif // COMPILE_KDE_SUPPORT
877 }
878 
showParentFrame()879 void KviApplication::showParentFrame()
880 {
881 	if(!g_pMainWindow)
882 		return;
883 
884 	if(g_pMainWindow->isMinimized())
885 		g_pMainWindow->setWindowState(g_pMainWindow->windowState() & (~Qt::WindowMinimized | Qt::WindowActive));
886 
887 	g_pMainWindow->showMaximized();
888 }
889 
defaultTextCodec()890 QTextCodec * KviApplication::defaultTextCodec()
891 {
892 	QTextCodec * pCodec = nullptr;
893 	if(!KVI_OPTION_STRING(KviOption_stringDefaultTextEncoding).isEmpty())
894 	{
895 		pCodec = KviLocale::instance()->codecForName(KVI_OPTION_STRING(KviOption_stringDefaultTextEncoding).toLatin1());
896 		if(pCodec)
897 			return pCodec;
898 	}
899 	pCodec = QTextCodec::codecForLocale();
900 	if(pCodec)
901 		return pCodec;
902 	pCodec = KviLocale::instance()->codecForName("UTF-8");
903 	if(!pCodec)
904 		qDebug("KviApplication::defaultTextCodec(): can't find a suitable text codec for locale :/");
905 	return pCodec;
906 }
907 
defaultSrvCodec()908 QTextCodec * KviApplication::defaultSrvCodec()
909 {
910 	QTextCodec * pCodec = nullptr;
911 	if(!KVI_OPTION_STRING(KviOption_stringDefaultSrvEncoding).isEmpty())
912 	{
913 		pCodec = KviLocale::instance()->codecForName(KVI_OPTION_STRING(KviOption_stringDefaultSrvEncoding).toLatin1());
914 		if(pCodec)
915 			return pCodec;
916 	}
917 	pCodec = QTextCodec::codecForLocale();
918 	if(pCodec)
919 		return pCodec;
920 	pCodec = KviLocale::instance()->codecForName("UTF-8");
921 	if(!pCodec)
922 		qDebug("KviApplication::defaultSrcCodec(): can't find a suitable text codec for locale :/");
923 	return pCodec;
924 }
925 
loadDefaultScript(QString szItem)926 void KviApplication::loadDefaultScript(QString szItem)
927 {
928 	QString szCmd;
929 
930 	if(szItem.isEmpty())
931 		szItem = "default";
932 
933 	getGlobalKvircDirectory(szCmd, KviApplication::DefScript, QString("%1.kvs").arg(szItem));
934 	szCmd.prepend("parse \"");
935 	szCmd.append("\"");
936 #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
937 	szCmd.replace("\\", "\\\\");
938 #endif
939 	KviKvsScript::run(szCmd, g_pMainWindow->firstConsole());
940 }
941 
checkSuggestRestoreDefaultScript()942 void KviApplication::checkSuggestRestoreDefaultScript()
943 {
944 	static bool bSuggestedOnce = false;
945 	if(KVI_OPTION_BOOL(KviOption_boolDoNotSuggestRestoreDefaultScript))
946 		return;
947 
948 	if(bSuggestedOnce)
949 		return; // already suggested in this kvirc session
950 
951 	// first: check if the user configuration has ever been updated to the current version
952 	if(!KviDefaultScriptManager::instance()->isDefscriptUpToDate())
953 	{
954 		switch(
955 		    QMessageBox::question(nullptr, __tr2qs("Update Default Scripts - KVIrc"),
956 		        __tr2qs("<b>Your KVirc installation has been updated successfully.</b><br><br>"
957 		                "KVIrc's default scripts should also be updated. "
958 		                "<b>Do you want to restore the default scripts?</b><br><br>"
959 		                "Hint: If you choose \"No\" you can always restore the "
960 		                "default scripts by selecting the appropriate entry from the \"Scripting\" menu later."),
961 		        __tr2qs("No and Don't Ask Me Again"), __tr2qs("No"), __tr2qs("Yes"), 1, 1))
962 		{
963 			case 0:
964 				KVI_OPTION_BOOL(KviOption_boolDoNotSuggestRestoreDefaultScript) = true;
965 				return;
966 				break;
967 			case 1:
968 				// we want to execute the next checks, don't return now
969 				break;
970 			default:
971 				restoreDefaultScript();
972 				// we want to execute the next checks after the attempted restore, don't return now
973 				break;
974 		}
975 	}
976 
977 	//second, check for some common corruptions in scripts
978 	int iScore = 0;
979 
980 	if(KviCustomToolBarManager::instance()->descriptorCount() < 1)
981 		iScore += 100; // straight suggest
982 	else if(KviKvsPopupManager::instance()->popupCount() < 1)
983 		iScore += 100; // straight suggest
984 	else
985 	{
986 		// other stuff is not that critical
987 		if(!KviKvsEventManager::instance()->hasAppHandlers(KviEvent_OnChannelNickPopupRequest))
988 			iScore += 80;
989 		if(!KviKvsEventManager::instance()->hasAppHandlers(KviEvent_OnDCCChatPopupRequest))
990 			iScore += 20;
991 		if(!KviKvsEventManager::instance()->hasAppHandlers(KviEvent_OnConsolePopupRequest))
992 			iScore += 20;
993 		if(!KviKvsEventManager::instance()->hasAppHandlers(KviEvent_OnChannelPopupRequest))
994 			iScore += 20;
995 		if(!KviKvsEventManager::instance()->hasAppHandlers(KviEvent_OnChannelNickPopupRequest))
996 			iScore += 20;
997 		if(!KviKvsEventManager::instance()->hasAppHandlers(KviEvent_OnQueryPopupRequest))
998 			iScore += 20;
999 		if(!KviKvsEventManager::instance()->hasAppHandlers(KviEvent_OnQueryNickPopupRequest))
1000 			iScore += 20;
1001 		if(KviCustomToolBarManager::instance()->descriptorCount() < 4)
1002 			iScore += 20;
1003 		if(KviKvsPopupManager::instance()->popupCount() < 3)
1004 			iScore += 20;
1005 		if(KviCustomToolBarManager::instance()->visibleToolBarCount() < 2)
1006 		{
1007 			iScore += 20;
1008 			if(KviCustomToolBarManager::instance()->visibleToolBarCount() < 1)
1009 				iScore += 20;
1010 		}
1011 	}
1012 
1013 	if(iScore < 100)
1014 		return;
1015 
1016 	bSuggestedOnce = true;
1017 
1018 	switch(
1019 	    QMessageBox::question(nullptr, __tr2qs("Detected Installation Issues - KVIrc"),
1020 	        __tr2qs("<b>Your KVIrc installation is incomplete.</b><br><br>"
1021 	                "You seem to be missing some of the features that the KVIrc default scripts provide. "
1022 	                "<b>Do you want to restore the default scripts?</b><br><br>"
1023 	                "Hint: If you choose \"No\" you can always restore the "
1024 	                "default scripts by selecting the appropriate entry from the \"Scripting\" menu later."),
1025 	        __tr2qs("No and Don't Ask Me Again"), __tr2qs("No"), __tr2qs("Yes"), 1, 1))
1026 	{
1027 		case 0:
1028 			KVI_OPTION_BOOL(KviOption_boolDoNotSuggestRestoreDefaultScript) = true;
1029 			return;
1030 			break;
1031 		case 1:
1032 			return;
1033 			break;
1034 		default:
1035 			restoreDefaultScript();
1036 			break;
1037 	}
1038 }
1039 
restoreDefaultScript()1040 void KviApplication::restoreDefaultScript()
1041 {
1042 	KviDefaultScriptManager::instance()->restore();
1043 }
1044 
1045 #ifndef COMPILE_NO_IPC
createIpcSentinel()1046 void KviApplication::createIpcSentinel()
1047 {
1048 	m_pIpcSentinel = new KviIpcSentinel();
1049 }
1050 
destroyIpcSentinel()1051 void KviApplication::destroyIpcSentinel()
1052 {
1053 	delete m_pIpcSentinel;
1054 	m_pIpcSentinel = nullptr;
1055 }
1056 
ipcMessage(char * pcMessage)1057 void KviApplication::ipcMessage(char * pcMessage)
1058 {
1059 	if(!g_pMainWindow)
1060 		return;
1061 	KviConsoleWindow * pConsole = g_pMainWindow->firstConsole();
1062 	if(!pConsole)
1063 		return;
1064 	if(_OUTPUT_VERBOSE)
1065 	{
1066 		KviCString szCmd = pcMessage;
1067 		if(szCmd.len() > 30)
1068 			szCmd.cutRight(szCmd.len() - 30);
1069 		int iIdx = szCmd.findFirstIdx('\n');
1070 		if(iIdx != -1)
1071 			szCmd.cutRight(szCmd.len() - (iIdx + 1));
1072 		pConsole->output(KVI_OUT_SYSTEMMESSAGE, __tr2qs("Remote command received (%s ...)"), szCmd.ptr());
1073 	}
1074 	if (kvi_strEqualCIN(pcMessage, "openurl ", 8))
1075 	{
1076 		// there actually is no reliable way of raising the main window, but we try our best!
1077 #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
1078 		SetForegroundWindow((HWND)g_pMainWindow->winId());
1079 #endif
1080 		g_pMainWindow->activateWindow();
1081 	}
1082 	KviKvsScript::run(pcMessage, pConsole);
1083 }
1084 #endif // COMPILE_NO_IPC
1085 
setAvatarFromOptions()1086 void KviApplication::setAvatarFromOptions()
1087 {
1088 	for(auto & it : g_pGlobalWindowDict)
1089 	{
1090 		KviConsoleWindow * pWindow = dynamic_cast<KviConsoleWindow *>(it.second);
1091 		if(pWindow)
1092 			pWindow->setAvatarFromOptions();
1093 	}
1094 }
1095 
setAvatarOnFileReceived(KviConsoleWindow * pConsole,const QString & szRemoteUrl,const QString & szNick,const QString & szUser,const QString & szHost)1096 void KviApplication::setAvatarOnFileReceived(
1097     KviConsoleWindow * pConsole,
1098     const QString & szRemoteUrl,
1099     const QString & szNick,
1100     const QString & szUser,
1101     const QString & szHost)
1102 {
1103 	if(m_PendingAvatarChanges.size() >= KVI_MAX_PENDING_AVATARS) // can't be...
1104 		// kill an entry to make space
1105 		m_PendingAvatarChanges.erase(m_PendingAvatarChanges.begin());
1106 
1107 	std::unique_ptr<KviPendingAvatarChange> pAvatar(new KviPendingAvatarChange());
1108 	pAvatar->pConsole = pConsole;
1109 	pAvatar->szRemoteUrl = szRemoteUrl;
1110 	pAvatar->szNick = szNick;
1111 	pAvatar->szUser = szUser;
1112 	pAvatar->szHost = szHost;
1113 
1114 	m_PendingAvatarChanges.emplace(pAvatar.get(), std::move(pAvatar));
1115 }
1116 
findPendingAvatarChange(KviConsoleWindow * pConsole,const QString & szNick,const QString & szRemoteUrl)1117 KviPendingAvatarChange * KviApplication::findPendingAvatarChange(
1118     KviConsoleWindow * pConsole,
1119     const QString & szNick,
1120     const QString & szRemoteUrl)
1121 {
1122 	for(auto & upAvatarPair : m_PendingAvatarChanges)
1123 	{
1124 		KviPendingAvatarChange * pAvatar = upAvatarPair.second.get();
1125 		if(!pConsole || (pAvatar->pConsole == pConsole))
1126 		{
1127 			if(szNick.isNull() || (szNick == pAvatar->szNick))
1128 			{
1129 				if(szRemoteUrl == pAvatar->szRemoteUrl)
1130 					return pAvatar;
1131 			}
1132 		}
1133 	}
1134 
1135 	return nullptr;
1136 }
1137 
fileDownloadTerminated(bool bSuccess,const QString & szRemoteUrl,const QString & szLocalFileName,const QString & szNick,const QString & szError,bool bQuiet)1138 void KviApplication::fileDownloadTerminated(
1139     bool bSuccess,
1140     const QString & szRemoteUrl,
1141     const QString & szLocalFileName,
1142     const QString & szNick,
1143     const QString & szError,
1144     bool bQuiet)
1145 {
1146 
1147 	KviPendingAvatarChange * pAvatar = findPendingAvatarChange(nullptr, szNick, szRemoteUrl);
1148 
1149 	if(pAvatar == nullptr)
1150 	{
1151 		// signal dcc completion only for NON-avatars
1152 		// FIXME: This option is misnamed and misplaced in the options dialog :(
1153 		//        it seems to refer only to DCC while it refers to any file transfer
1154 		if(KVI_OPTION_BOOL(KviOption_boolNotifyDccSendSuccessInNotifier) && (!bQuiet))
1155 		{
1156 			if(!g_pActiveWindow)
1157 				return;
1158 			if(g_pActiveWindow->hasAttention(KviWindow::MainWindowIsVisible))
1159 				return;
1160 			QString szMsg;
1161 			int iIconId;
1162 			if(!bSuccess)
1163 			{
1164 				iIconId = KviIconManager::DccError;
1165 				if(szNick.isEmpty())
1166 					szMsg = __tr2qs("File download failed");
1167 				else
1168 					szMsg = __tr2qs("File download from %1 failed").arg(szNick);
1169 				szMsg += ": ";
1170 				szMsg += szError;
1171 				szMsg += " (";
1172 				szMsg += szLocalFileName;
1173 				szMsg += ")";
1174 			}
1175 			else
1176 			{
1177 				iIconId = KviIconManager::DccMsg;
1178 				if(szNick.isEmpty())
1179 					szMsg = __tr2qs("File download successfully complete");
1180 				else
1181 					szMsg = __tr2qs("File download from %1 successfully complete").arg(szNick);
1182 				szMsg += " (";
1183 				szMsg += szLocalFileName;
1184 				szMsg += ")";
1185 			}
1186 			notifierMessage(nullptr, iIconId, KviQString::toHtmlEscaped(szMsg), KVI_OPTION_UINT(KviOption_uintNotifierAutoHideTime));
1187 		}
1188 		return;
1189 	}
1190 
1191 	if(bSuccess)
1192 	{
1193 		if(windowExists(pAvatar->pConsole))
1194 		{
1195 			pAvatar->pConsole->setAvatar(
1196 			    pAvatar->szNick,
1197 			    pAvatar->szUser,
1198 			    pAvatar->szHost,
1199 			    szLocalFileName,
1200 			    (KviQString::equalCIN("http://", szRemoteUrl, 7) || KviQString::equalCIN("https://", szRemoteUrl, 8)) ? szRemoteUrl : QString());
1201 		}
1202 	}
1203 	else
1204 	{
1205 		if((!_OUTPUT_MUTE) && (!bQuiet))
1206 		{
1207 			pAvatar->pConsole->output(KVI_OUT_AVATAR, __tr2qs("Avatar download failed for %Q!%Q@%Q and URL %Q: %Q"),
1208 			    &(pAvatar->szNick), &(pAvatar->szUser), &(pAvatar->szHost), &(szRemoteUrl), &(szError));
1209 		}
1210 	}
1211 
1212 	m_PendingAvatarChanges.erase(pAvatar);
1213 }
1214 
1215 #ifdef COMPILE_PSEUDO_TRANSPARENCY
destroyPseudoTransparency()1216 void KviApplication::destroyPseudoTransparency()
1217 {
1218 	if(g_pShadedParentGlobalDesktopBackground)
1219 	{
1220 		delete g_pShadedParentGlobalDesktopBackground;
1221 		g_pShadedParentGlobalDesktopBackground = nullptr;
1222 	}
1223 	if(g_pShadedChildGlobalDesktopBackground)
1224 	{
1225 		delete g_pShadedChildGlobalDesktopBackground;
1226 		g_pShadedChildGlobalDesktopBackground = nullptr;
1227 	}
1228 }
1229 
triggerUpdatePseudoTransparency()1230 void KviApplication::triggerUpdatePseudoTransparency()
1231 {
1232 	if(m_bUpdatePseudoTransparencyPending)
1233 		return;
1234 	m_bUpdatePseudoTransparencyPending = true;
1235 	QTimer::singleShot(0, this, SLOT(updatePseudoTransparency()));
1236 }
1237 
createGlobalBackgrounds(QPixmap * pix)1238 void KviApplication::createGlobalBackgrounds(QPixmap * pix)
1239 {
1240 	// create shaded copies...
1241 	if(g_pShadedParentGlobalDesktopBackground)
1242 		delete g_pShadedParentGlobalDesktopBackground;
1243 	if(g_pShadedChildGlobalDesktopBackground)
1244 		delete g_pShadedChildGlobalDesktopBackground;
1245 	g_pShadedParentGlobalDesktopBackground = new QPixmap(pix->width(), pix->height());
1246 	g_pShadedChildGlobalDesktopBackground = new QPixmap(pix->width(), pix->height());
1247 	QPainter p;
1248 	p.begin(g_pShadedParentGlobalDesktopBackground);
1249 	p.drawPixmap(0, 0, *pix);
1250 	p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
1251 	if(KVI_OPTION_UINT(KviOption_uintGlobalTransparencyParentFadeFactor) > 100)
1252 		KVI_OPTION_UINT(KviOption_uintGlobalTransparencyParentFadeFactor) = 100;
1253 	p.setOpacity((float)((float)KVI_OPTION_UINT(KviOption_uintGlobalTransparencyParentFadeFactor) / (float)100));
1254 	p.fillRect(0, 0, pix->width(), pix->height(), QBrush(KVI_OPTION_COLOR(KviOption_colorGlobalTransparencyFade)));
1255 	p.end();
1256 
1257 	p.begin(g_pShadedChildGlobalDesktopBackground);
1258 	p.drawPixmap(0, 0, *pix);
1259 	p.setCompositionMode(QPainter::CompositionMode_SourceAtop);
1260 	if(KVI_OPTION_UINT(KviOption_uintGlobalTransparencyChildFadeFactor) > 100)
1261 		KVI_OPTION_UINT(KviOption_uintGlobalTransparencyChildFadeFactor) = 100;
1262 	p.setOpacity((float)((float)KVI_OPTION_UINT(KviOption_uintGlobalTransparencyChildFadeFactor) / (float)100));
1263 	p.fillRect(0, 0, pix->width(), pix->height(), QBrush(KVI_OPTION_COLOR(KviOption_colorGlobalTransparencyFade)));
1264 	p.end();
1265 
1266 	if(g_pMainWindow)
1267 		g_pMainWindow->updatePseudoTransparency();
1268 }
1269 #endif //COMPILE_PSEUDO_TRANSPARENCY
1270 
updatePseudoTransparency()1271 void KviApplication::updatePseudoTransparency()
1272 {
1273 #ifdef COMPILE_PSEUDO_TRANSPARENCY
1274 	m_bUpdatePseudoTransparencyPending = false;
1275 	if(KVI_OPTION_BOOL(KviOption_boolUseGlobalPseudoTransparency))
1276 	{
1277 #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
1278 		if(KVI_OPTION_BOOL(KviOption_boolUseWindowsDesktopForTransparency))
1279 		{
1280 			QSize size = g_pApp->desktop()->screenGeometry(g_pApp->desktop()->primaryScreen()).size();
1281 			// get the Program Manager
1282 			HWND hWnd = FindWindow(TEXT("Progman"), TEXT("Program Manager"));
1283 			// Create and setup bitmap
1284 			const HDC displayDc = GetDC(0);
1285 			HDC bitmap_dc = CreateCompatibleDC(displayDc);
1286 			HBITMAP bitmap = CreateCompatibleBitmap(displayDc, size.width(), size.height());
1287 			HGDIOBJ null_bitmap = SelectObject(bitmap_dc, bitmap);
1288 
1289 			PrintWindow(hWnd, bitmap_dc, 0);
1290 
1291 			SelectObject(bitmap_dc, null_bitmap);
1292 			DeleteDC(bitmap_dc);
1293 			ReleaseDC(0, displayDc);
1294 			QPixmap pix = QtWin::fromHBITMAP(bitmap);
1295 
1296 			DeleteObject(bitmap);
1297 
1298 			createGlobalBackgrounds(&pix);
1299 		}
1300 		else
1301 #endif // COMPILE_ON_WINDOWS || COMPILE_ON_MINGW
1302 
1303 		if(KVI_OPTION_PIXMAP(KviOption_pixmapGlobalTransparencyBackground).pixmap())
1304 		{
1305 			createGlobalBackgrounds(KVI_OPTION_PIXMAP(KviOption_pixmapGlobalTransparencyBackground).pixmap());
1306 		}
1307 		else
1308 		{
1309 			//destroy pseudo transparency pixmaps
1310 			destroyPseudoTransparency();
1311 //if we get here, no pseudo transparency method can be used / is enabled.
1312 #ifdef COMPILE_X11_SUPPORT
1313 			//under x11, we still have to check real transparency methods
1314 			if(!KVI_OPTION_BOOL(KviOption_boolUseCompositingForTransparency))
1315 			{
1316 				//real transparency is off => turn off transparency support at all
1317 				KVI_OPTION_BOOL(KviOption_boolUseGlobalPseudoTransparency) = false;
1318 			}
1319 #else
1320 			// on other platforms, no way to proceed
1321 			KVI_OPTION_BOOL(KviOption_boolUseGlobalPseudoTransparency) = false;
1322 #endif // COMPILE_X11_SUPPORT
1323 		}
1324 	}
1325 	else
1326 	{
1327 		//transparency is disabled
1328 		//destroy pseudo transparency pixmaps
1329 		destroyPseudoTransparency();
1330 		//make sure real transparency is disabled
1331 		KVI_OPTION_BOOL(KviOption_boolUseCompositingForTransparency) = false;
1332 
1333 		//update widgets
1334 		if(g_pMainWindow)
1335 			g_pMainWindow->updatePseudoTransparency();
1336 	}
1337 #endif //COMPILE_PSEUDO_TRANSPARENCY
1338 }
1339 
triggerUpdateGui()1340 void KviApplication::triggerUpdateGui()
1341 {
1342 	if(m_bUpdateGuiPending)
1343 		return;
1344 	m_bUpdateGuiPending = true;
1345 	QTimer::singleShot(0, this, SLOT(updateGui()));
1346 }
1347 
updateGui()1348 void KviApplication::updateGui()
1349 {
1350 	m_bUpdateGuiPending = false;
1351 	// enforce our "icon in popups" option
1352 	setAttribute(Qt::AA_DontShowIconsInMenus, !KVI_OPTION_BOOL(KviOption_boolShowIconsInPopupMenus));
1353 	g_pMainWindow->applyOptions();
1354 }
1355 
updateApplicationFont()1356 void KviApplication::updateApplicationFont()
1357 {
1358 	if(KVI_OPTION_BOOL(KviOption_boolUseGlobalApplicationFont))
1359 	{
1360 		setFont(KVI_OPTION_FONT(KviOption_fontApplication));
1361 		if(g_pMainWindow)
1362 			g_pMainWindow->setFont(font());
1363 	}
1364 	else
1365 	{
1366 		setFont(m_fntDefaultFont);
1367 		if(g_pMainWindow)
1368 			g_pMainWindow->setFont(m_fntDefaultFont);
1369 	}
1370 }
1371 
loadRecentEntries()1372 void KviApplication::loadRecentEntries()
1373 {
1374 	QString szTmp;
1375 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_RECENT);
1376 	KviConfigurationFile cfg(szTmp, KviConfigurationFile::Read);
1377 	*g_pRecentTopicList = cfg.readStringListEntry("RecentTopicList", QStringList());
1378 }
1379 
saveRecentEntries()1380 void KviApplication::saveRecentEntries()
1381 {
1382 	QString szTmp;
1383 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_RECENT);
1384 	KviConfigurationFile cfg(szTmp, KviConfigurationFile::Write);
1385 	cfg.writeEntry("RecentTopicList", *g_pRecentTopicList);
1386 }
1387 
saveAvatarCache()1388 void KviApplication::saveAvatarCache()
1389 {
1390 	QString szTmp;
1391 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_AVATARCACHE);
1392 	KviAvatarCache::instance()->save(szTmp);
1393 }
1394 
saveToolBars()1395 void KviApplication::saveToolBars()
1396 {
1397 	QString szTmp;
1398 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_CUSTOMTOOLBARS);
1399 	KviCustomToolBarManager::instance()->save(szTmp);
1400 }
1401 
savePopups()1402 void KviApplication::savePopups()
1403 {
1404 	QString szTmp;
1405 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_POPUPS);
1406 	KviKvs::savePopups(szTmp);
1407 }
1408 
saveInputHistory()1409 void KviApplication::saveInputHistory()
1410 {
1411 	if(KVI_OPTION_BOOL(KviOption_boolEnableInputHistory))
1412 	{
1413 		QString szTmp;
1414 		getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_INPUTHISTORY);
1415 		KviInputHistory::instance()->save(szTmp);
1416 	}
1417 }
1418 
saveAliases()1419 void KviApplication::saveAliases()
1420 {
1421 	QString szTmp;
1422 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_ALIASES);
1423 	KviKvs::saveAliases(szTmp);
1424 }
1425 
saveScriptAddons()1426 void KviApplication::saveScriptAddons()
1427 {
1428 	QString szTmp;
1429 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_SCRIPTADDONS);
1430 	KviKvs::saveScriptAddons(szTmp);
1431 }
1432 
saveTextIcons()1433 void KviApplication::saveTextIcons()
1434 {
1435 	g_pTextIconManager->save();
1436 }
1437 
saveAppEvents()1438 void KviApplication::saveAppEvents()
1439 {
1440 	QString szTmp;
1441 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_EVENTS);
1442 	KviKvs::saveAppEvents(szTmp);
1443 }
1444 
saveRawEvents()1445 void KviApplication::saveRawEvents()
1446 {
1447 	QString szTmp;
1448 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_RAWEVENTS);
1449 	KviKvs::saveRawEvents(szTmp);
1450 }
1451 
saveMediaTypes()1452 void KviApplication::saveMediaTypes()
1453 {
1454 	QString szTmp;
1455 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_MEDIATYPES);
1456 	g_pMediaManager->lock();
1457 	g_pMediaManager->save(szTmp);
1458 	g_pMediaManager->unlock();
1459 }
1460 
saveIrcServerDataBase()1461 void KviApplication::saveIrcServerDataBase()
1462 {
1463 	QString szTmp;
1464 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_SERVERDB);
1465 	g_pServerDataBase->save(szTmp);
1466 }
1467 
saveProxyDataBase()1468 void KviApplication::saveProxyDataBase()
1469 {
1470 	QString szTmp;
1471 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_PROXYDB);
1472 	g_pProxyDataBase->save(szTmp);
1473 }
1474 
saveRegisteredUsers()1475 void KviApplication::saveRegisteredUsers()
1476 {
1477 	QString szTmp;
1478 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_REGUSERDB);
1479 	g_pRegisteredUserDataBase->save(szTmp);
1480 }
1481 
saveRegisteredChannels()1482 void KviApplication::saveRegisteredChannels()
1483 {
1484 	QString szTmp;
1485 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_REGCHANDB);
1486 	g_pRegisteredChannelDataBase->save(szTmp);
1487 }
1488 
saveNickServ()1489 void KviApplication::saveNickServ()
1490 {
1491 	QString szTmp;
1492 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_NICKSERVDATABASE);
1493 	g_pNickServRuleSet->save(szTmp);
1494 }
1495 
saveIdentityProfiles()1496 void KviApplication::saveIdentityProfiles()
1497 {
1498 	QString szTmp;
1499 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_PROFILESDATABASE);
1500 	KviIdentityProfileSet::instance()->save(szTmp);
1501 }
1502 
saveDefaultScripts()1503 void KviApplication::saveDefaultScripts()
1504 {
1505 	QString szTmp;
1506 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_DEFAULTSCRIPT);
1507 	KviDefaultScriptManager::instance()->save(szTmp);
1508 }
1509 
saveSharedFiles()1510 void KviApplication::saveSharedFiles()
1511 {
1512 	QString szTmp;
1513 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_SHAREDFILES);
1514 	g_pSharedFilesManager->save(szTmp);
1515 }
1516 
saveActions()1517 void KviApplication::saveActions()
1518 {
1519 	QString szTmp;
1520 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_USERACTIONS);
1521 	KviActionManager::instance()->save(szTmp);
1522 }
1523 
saveIdentities()1524 void KviApplication::saveIdentities()
1525 {
1526 	QString szTmp;
1527 	getLocalKvircDirectory(szTmp, Config, KVI_CONFIGFILE_IDENTITIES);
1528 	KviUserIdentityManager::instance()->save(szTmp);
1529 }
1530 
saveConfiguration()1531 void KviApplication::saveConfiguration()
1532 {
1533 	// this is NOT called when the application is closing down
1534 	KviCustomToolBarManager::instance()->storeVisibilityState();
1535 	saveOptions();
1536 	saveIdentities();
1537 	saveActions();
1538 	saveIrcServerDataBase();
1539 	saveProxyDataBase();
1540 	saveRecentEntries();
1541 	saveAvatarCache();
1542 	saveAppEvents();
1543 	saveRawEvents();
1544 	saveMediaTypes();
1545 	saveRegisteredUsers();
1546 	saveRegisteredChannels();
1547 	saveNickServ();
1548 	saveIdentityProfiles();
1549 	saveDefaultScripts();
1550 	saveSharedFiles();
1551 	savePopups();
1552 	saveToolBars();
1553 	saveAliases();
1554 	saveTextIcons();
1555 	saveInputHistory();
1556 	saveScriptAddons();
1557 	KviKvs::flushUserClasses();
1558 	g_pWinPropertiesConfig->sync();
1559 }
1560 
autoConnectToServers()1561 void KviApplication::autoConnectToServers()
1562 {
1563 	KviPointerList<KviIrcServer> * pList = g_pServerDataBase->autoConnectOnStartupServers();
1564 	if(pList)
1565 	{
1566 		for(auto & pServer : pList)
1567 		{
1568 			QString szCommand = "server -u \"id:";
1569 			if(pServer.id().isEmpty())
1570 				pServer.generateUniqueId();
1571 			szCommand += pServer.id();
1572 			szCommand += "\"";
1573 			KviKvsScript::run(szCommand, activeConsole());
1574 		}
1575 		g_pServerDataBase->clearAutoConnectOnStartupServers();
1576 	}
1577 
1578 	KviPointerList<KviIrcNetwork> * pListNet = g_pServerDataBase->autoConnectOnStartupNetworks();
1579 	if(pListNet)
1580 	{
1581 		for(auto & pNetwork : pListNet)
1582 		{
1583 			QString szCommandx = "server -u \"net:";
1584 			szCommandx += pNetwork.name();
1585 			szCommandx += "\"";
1586 			KviKvsScript::run(szCommandx, activeConsole());
1587 		}
1588 		g_pServerDataBase->clearAutoConnectOnStartupNetworks();
1589 	}
1590 }
1591 
createFrame()1592 void KviApplication::createFrame()
1593 {
1594 	Q_ASSERT(g_pMainWindow == nullptr);
1595 
1596 #if defined(COMPILE_ON_WINDOWS) || defined(COMPILE_ON_MINGW)
1597 	if(KVI_OPTION_BOOL(KviOption_boolShowTaskBarButton))
1598 		new KviMainWindow(0);
1599 	else
1600 		new KviMainWindow(new QWidget(0, 0));
1601 #else
1602 	new KviMainWindow(nullptr);
1603 #endif
1604 
1605 	Q_ASSERT(g_pMainWindow != nullptr);
1606 
1607 	g_pMainWindow->createNewConsole(true);
1608 
1609 	if(!m_szExecAfterStartup.isEmpty())
1610 	{
1611 		KviKvsScript::run(m_szExecAfterStartup, g_pMainWindow->firstConsole());
1612 		m_szExecAfterStartup = "";
1613 	}
1614 
1615 	// auto connect to servers if needed
1616 	if(g_pServerDataBase->autoConnectOnStartupServers() || g_pServerDataBase->autoConnectOnStartupNetworks())
1617 		autoConnectToServers();
1618 
1619 	if(KVI_OPTION_BOOL(KviOption_boolShowDockExtension))
1620 		g_pMainWindow->executeInternalCommand(KVI_INTERNALCOMMAND_TRAYICON_SHOW);
1621 
1622 	if(KVI_OPTION_BOOL(KviOption_boolStartupMinimized))
1623 		g_pMainWindow->showMinimized();
1624 	else
1625 		g_pMainWindow->show();
1626 }
1627 
connectionExists(KviIrcConnection * pConn)1628 bool KviApplication::connectionExists(KviIrcConnection * pConn)
1629 {
1630 	for(auto & it : g_pGlobalWindowDict)
1631 	{
1632 		if(it.second->connection() == pConn)
1633 			return true;
1634 	}
1635 	return false;
1636 }
1637 
windowExists(KviWindow * pWnd)1638 bool KviApplication::windowExists(KviWindow * pWnd)
1639 {
1640 	for(auto & it : g_pGlobalWindowDict)
1641 	{
1642 		if(it.second == pWnd)
1643 			return true;
1644 	}
1645 	return false;
1646 }
1647 
windowCount()1648 unsigned int KviApplication::windowCount()
1649 {
1650 	return g_pGlobalWindowDict.size();
1651 }
1652 
findConsole(QString & szServer,QString & szNick)1653 KviConsoleWindow * KviApplication::findConsole(QString & szServer, QString & szNick)
1654 {
1655 	for(auto & it : g_pGlobalWindowDict)
1656 	{
1657 		KviConsoleWindow * pWindow = dynamic_cast<KviConsoleWindow *>(it.second);
1658 		if(!(pWindow && pWindow->type() == KviWindow::Console && pWindow->isConnected()))
1659 			continue;
1660 
1661 		if(!szServer.isEmpty())
1662 		{
1663 			if(KviQString::equalCI(szServer, pWindow->connection()->currentServerName()))
1664 			{
1665 				if(szNick.isEmpty())
1666 					return pWindow;
1667 
1668 				if(KviQString::equalCI(szNick, pWindow->connection()->currentNickName()))
1669 					return pWindow;
1670 			}
1671 		}
1672 		else
1673 		{
1674 			if(!szNick.isEmpty())
1675 			{
1676 				if(KviQString::equalCI(szNick, pWindow->connection()->currentNickName()))
1677 					return pWindow;
1678 			}
1679 		}
1680 	}
1681 	return nullptr;
1682 }
1683 
restartLagMeters()1684 void KviApplication::restartLagMeters()
1685 {
1686 	for(auto & it : g_pGlobalWindowDict)
1687 	{
1688 		KviConsoleWindow * pWindow = dynamic_cast<KviConsoleWindow *>(it.second);
1689 		if(pWindow && pWindow->type() == KviWindow::Console && pWindow->connection())
1690 			pWindow->connection()->restartLagMeter();
1691 	}
1692 }
1693 
restartNotifyLists()1694 void KviApplication::restartNotifyLists()
1695 {
1696 	for(auto & it : g_pGlobalWindowDict)
1697 	{
1698 		KviConsoleWindow * pWindow = dynamic_cast<KviConsoleWindow *>(it.second);
1699 		if(pWindow && pWindow->type() == KviWindow::Console && pWindow->connection())
1700 			pWindow->connection()->restartNotifyList();
1701 	}
1702 }
1703 
resetAvatarForMatchingUsers(KviRegisteredUser * pUser)1704 void KviApplication::resetAvatarForMatchingUsers(KviRegisteredUser * pUser)
1705 {
1706 	for(auto & it : g_pGlobalWindowDict)
1707 	{
1708 		KviConsoleWindow * pWindow = dynamic_cast<KviConsoleWindow *>(it.second);
1709 		if(pWindow && pWindow->type() == KviWindow::Console)
1710 			pWindow->resetAvatarForMatchingUsers(pUser);
1711 	}
1712 }
1713 
findConsole(unsigned int uIrcContextId)1714 KviConsoleWindow * KviApplication::findConsole(unsigned int uIrcContextId)
1715 {
1716 	for(auto & it : g_pGlobalWindowDict)
1717 	{
1718 		KviConsoleWindow * pWindow = dynamic_cast<KviConsoleWindow *>(it.second);
1719 		if(pWindow && pWindow->context()->id() == uIrcContextId)
1720 			return pWindow;
1721 	}
1722 	return nullptr;
1723 }
1724 
topmostConnectedConsole()1725 KviConsoleWindow * KviApplication::topmostConnectedConsole()
1726 {
1727 	// check the foreground window console
1728 
1729 	KviConsoleWindow * pConsole = activeConsole();
1730 	if(!pConsole)
1731 		return nullptr;
1732 	if(pConsole->isConnected())
1733 		return pConsole;
1734 
1735 	// try ANY connected console
1736 
1737 	for(auto & it : g_pGlobalWindowDict)
1738 	{
1739 		KviConsoleWindow * pWindow = dynamic_cast<KviConsoleWindow *>(it.second);
1740 		if(pWindow && pWindow->type() == KviWindow::Console && pWindow->isConnected())
1741 			return pWindow;
1742 	}
1743 
1744 	return nullptr;
1745 }
1746 
findWindow(const QString & szWindowId)1747 KviWindow * KviApplication::findWindow(const QString & szWindowId)
1748 {
1749 	auto search = g_pGlobalWindowDict.find(szWindowId);
1750 	return search != g_pGlobalWindowDict.end() ? search->second : nullptr;
1751 }
1752 
findWindowByCaption(const QString & szWindowCaption,int iContextId)1753 KviWindow * KviApplication::findWindowByCaption(const QString & szWindowCaption, int iContextId)
1754 {
1755 	for(auto & it : g_pGlobalWindowDict)
1756 	{
1757 		if(KviQString::equalCI(szWindowCaption, it.second->plainTextCaption()) && (iContextId == -1 || it.second->context()->id() == static_cast<uint>(iContextId)))
1758 			return it.second;
1759 	}
1760 
1761 	return nullptr;
1762 }
1763 
registerWindow(KviWindow * pWnd)1764 void KviApplication::registerWindow(KviWindow * pWnd)
1765 {
1766 	g_pGlobalWindowDict.emplace(pWnd->id(), pWnd);
1767 }
1768 
unregisterWindow(KviWindow * pWnd)1769 void KviApplication::unregisterWindow(KviWindow * pWnd)
1770 {
1771 	g_pGlobalWindowDict.erase(pWnd->id());
1772 }
1773 
activeConsole()1774 KviConsoleWindow * KviApplication::activeConsole()
1775 {
1776 	if(!g_pMainWindow)
1777 		return nullptr;
1778 	if(g_pActiveWindow)
1779 	{
1780 		if(g_pActiveWindow->console())
1781 			return g_pActiveWindow->console();
1782 	}
1783 	return g_pMainWindow->firstConsole();
1784 }
1785 
1786 //
1787 // RECENT STUFF LISTS & POPUPS HANDLING
1788 //
1789 
1790 // Helper for KviApplication::addRecent*()
merge_to_stringlist_option(const QString & szItem,int iOption,int iMaxEntries)1791 static void merge_to_stringlist_option(const QString & szItem, int iOption, int iMaxEntries)
1792 {
1793 	for(QStringList::Iterator it = KVI_OPTION_STRINGLIST(iOption).begin();
1794 	    it != KVI_OPTION_STRINGLIST(iOption).end(); ++it)
1795 	{
1796 		// Do a case-insensitive search (for nicknames, servers, and channels)
1797 		if(!QString::compare(szItem.toLower(), (*it).toLower()))
1798 		{
1799 			// In the recent list, remove and put as first so more recent items
1800 			// are always first
1801 			it = KVI_OPTION_STRINGLIST(iOption).erase(it);
1802 			--it;
1803 		}
1804 	}
1805 	while(KVI_OPTION_STRINGLIST(iOption).count() >= iMaxEntries)
1806 	{
1807 		KVI_OPTION_STRINGLIST(iOption)
1808 		    .erase(KVI_OPTION_STRINGLIST(iOption).isEmpty() ? KVI_OPTION_STRINGLIST(iOption).end() : --KVI_OPTION_STRINGLIST(iOption).end());
1809 	}
1810 	KVI_OPTION_STRINGLIST(iOption)
1811 	    .prepend(szItem);
1812 }
1813 
addRecentUrl(const QString & szText)1814 void KviApplication::addRecentUrl(const QString & szText)
1815 {
1816 	merge_to_stringlist_option(szText, KviOption_stringlistRecentIrcUrls, 50);
1817 	emit(recentUrlsChanged());
1818 }
1819 
addRecentNickname(const QString & szNewNick)1820 void KviApplication::addRecentNickname(const QString & szNewNick)
1821 {
1822 	merge_to_stringlist_option(szNewNick, KviOption_stringlistRecentNicknames, KVI_MAX_RECENT_NICKNAMES);
1823 }
1824 
addRecentChannel(const QString & szChan,const QString & szNet)1825 void KviApplication::addRecentChannel(const QString & szChan, const QString & szNet)
1826 {
1827 	if(!m_pRecentChannelDict)
1828 		buildRecentChannels();
1829 
1830 	QStringList * pList = m_pRecentChannelDict->find(szNet);
1831 	if(!pList)
1832 	{
1833 		pList = new QStringList(szChan);
1834 		m_pRecentChannelDict->insert(szNet, pList);
1835 	}
1836 
1837 	if(!pList->contains(szChan))
1838 		pList->append(szChan);
1839 }
1840 
buildRecentChannels()1841 void KviApplication::buildRecentChannels()
1842 {
1843 	if(m_pRecentChannelDict)
1844 		delete m_pRecentChannelDict;
1845 
1846 	m_pRecentChannelDict = new KviPointerHashTable<QString, QStringList>;
1847 	m_pRecentChannelDict->setAutoDelete(true);
1848 
1849 	QString szChan, szNet;
1850 
1851 	for(auto & it : KVI_OPTION_STRINGLIST(KviOption_stringlistRecentChannels))
1852 	{
1853 		if(it.isEmpty())
1854 			continue;
1855 
1856 		szChan = it.section(KVI_RECENT_CHANNELS_SEPARATOR, 0, 0);
1857 		szNet = it.section(KVI_RECENT_CHANNELS_SEPARATOR, 1);
1858 
1859 		if(szNet.isEmpty())
1860 			continue;
1861 
1862 		QStringList * pList = m_pRecentChannelDict->find(szNet);
1863 		if(!pList)
1864 		{
1865 			pList = new QStringList(szChan);
1866 			m_pRecentChannelDict->insert(szNet, pList);
1867 		}
1868 
1869 		if(!pList->contains(szChan))
1870 			pList->append(szChan);
1871 	}
1872 }
1873 
saveRecentChannels()1874 void KviApplication::saveRecentChannels()
1875 {
1876 	if(!m_pRecentChannelDict)
1877 		return;
1878 
1879 	QString szTmp;
1880 
1881 	KVI_OPTION_STRINGLIST(KviOption_stringlistRecentChannels)
1882 	    .clear();
1883 
1884 	KviPointerHashTableIterator<QString, QStringList> it(*m_pRecentChannelDict);
1885 
1886 	for(; it.current(); ++it)
1887 	{
1888 		for(QStringList::Iterator it_str = it.current()->begin(); it_str != it.current()->end(); ++it_str)
1889 		{
1890 			szTmp = *it_str;
1891 			szTmp.append(KVI_RECENT_CHANNELS_SEPARATOR);
1892 			szTmp.append(it.currentKey());
1893 			KVI_OPTION_STRINGLIST(KviOption_stringlistRecentChannels)
1894 			    .append(szTmp);
1895 		}
1896 	}
1897 }
1898 
recentChannelsForNetwork(const QString & szNet)1899 QStringList * KviApplication::recentChannelsForNetwork(const QString & szNet)
1900 {
1901 	if(!m_pRecentChannelDict)
1902 		buildRecentChannels();
1903 	return m_pRecentChannelDict->find(szNet);
1904 }
1905 
addRecentServer(const QString & szServer)1906 void KviApplication::addRecentServer(const QString & szServer)
1907 {
1908 	merge_to_stringlist_option(szServer, KviOption_stringlistRecentServers, KVI_MAX_RECENT_SERVERS);
1909 }
1910 
fillRecentServersPopup(QMenu * pMenu)1911 void KviApplication::fillRecentServersPopup(QMenu * pMenu)
1912 {
1913 	// FIXME: #warning "MAYBE DISABLE THE SERVERS THAT WE ARE ALREADY CONNECTED TO ?"
1914 	pMenu->clear();
1915 	for(auto & it : KVI_OPTION_STRINGLIST(KviOption_stringlistRecentServers))
1916 	{
1917 		if(it == "")
1918 			continue;
1919 		pMenu->addAction(*(g_pIconManager->getSmallIcon(KviIconManager::Server)), it);
1920 	}
1921 }
1922 
fillRecentNicknamesPopup(QMenu * pMenu,KviConsoleWindow * pConsole)1923 void KviApplication::fillRecentNicknamesPopup(QMenu * pMenu, KviConsoleWindow * pConsole)
1924 {
1925 	pMenu->clear();
1926 	QAction * pAction;
1927 	bool bAlreadyFound = false;
1928 	for(auto & it : KVI_OPTION_STRINGLIST(KviOption_stringlistRecentNicknames))
1929 	{
1930 		if(it == "")
1931 			continue;
1932 		pAction = pMenu->addAction(*(g_pIconManager->getSmallIcon(KviIconManager::Nick)), it);
1933 		if(!pConsole->isConnected())
1934 			pAction->setEnabled(false);
1935 		else
1936 		{
1937 			if(!bAlreadyFound)
1938 			{
1939 				bool bIsCurrent = KviQString::equalCS(pConsole->connection()->currentNickName(), it);
1940 				pAction->setEnabled(!bIsCurrent);
1941 				if(bIsCurrent)
1942 					bAlreadyFound = true;
1943 			}
1944 		}
1945 	}
1946 }
1947 
fillRecentChannelsPopup(QMenu * pMenu,KviConsoleWindow * pConsole)1948 void KviApplication::fillRecentChannelsPopup(QMenu * pMenu, KviConsoleWindow * pConsole)
1949 {
1950 	pMenu->clear();
1951 	QAction * pAction;
1952 	QStringList * pList = recentChannelsForNetwork(pConsole->currentNetworkName());
1953 	if(pList)
1954 	{
1955 		for(auto & it : *pList)
1956 		{
1957 			if(it == "")
1958 				continue; // ?
1959 			pAction = pMenu->addAction(*(g_pIconManager->getSmallIcon(KviIconManager::Channel)), it);
1960 			if(!pConsole->isConnected())
1961 				pAction->setEnabled(false);
1962 			else
1963 				pAction->setEnabled(!(pConsole->connection()->findChannel(it)));
1964 		}
1965 	}
1966 }
1967 
heartbeat(kvi_time_t tNow)1968 void KviApplication::heartbeat(kvi_time_t tNow)
1969 {
1970 	const struct tm * pTm = localtime(&tNow);
1971 
1972 	if(g_pApp->topmostConnectedConsole())
1973 	{
1974 		// FIXME: this has huge precision problems...
1975 		KVI_OPTION_UINT(KviOption_uintTotalConnectionTime)
1976 		++;
1977 	}
1978 
1979 	if(pTm && !pTm->tm_hour && !pTm->tm_min && !pTm->tm_sec)
1980 	{
1981 		for(auto & it : g_pGlobalWindowDict)
1982 		{
1983 			if(it.second->view() && it.second->view()->isLogging())
1984 				it.second->view()->startLogging(nullptr);
1985 		}
1986 	}
1987 }
1988 
timerEvent(QTimerEvent * e)1989 void KviApplication::timerEvent(QTimerEvent * e)
1990 {
1991 	if(e->timerId() != m_iHeartbeatTimerId)
1992 	{
1993 		QObject::timerEvent(e);
1994 		return;
1995 	}
1996 
1997 	// our heartbeat
1998 
1999 	kvi_time_t tNow = kvi_unixTime();
2000 
2001 	heartbeat(tNow);
2002 }
2003 
2004 // qvariant.h uses this, and it is included by the qt generated moc file for Qt >= 3.0.0
2005 #ifdef Bool
2006 #undef Bool
2007 #endif
2008