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 "config/preferences.h"
12 #include "ignore.h"
13 #include "highlight.h"
14
15 #include <QHashIterator>
16 #include <QHeaderView>
17 #include <QTreeView>
18
19 #include <KLocalizedString>
20 #include <KUser>
21 #include <KSharedConfig>
22 #include <QStandardPaths>
23
24 struct PreferencesSingleton
25 {
26 Preferences prefs;
27 };
28
Q_GLOBAL_STATIC(PreferencesSingleton,s_prefs)29 Q_GLOBAL_STATIC(PreferencesSingleton, s_prefs)
30
31 Preferences *Preferences::self()
32 {
33 if ( !s_prefs.exists() ) {
34 s_prefs->prefs.load();
35 }
36
37 return &s_prefs->prefs;
38 }
39
40
Preferences()41 Preferences::Preferences()
42 {
43 // create default identity
44 mIdentity=new Identity();
45 mIdentity->setName(i18n("Default Identity"));
46 mIdentityList.append(mIdentity);
47
48 KUser user(KUser::UseRealUserID);
49 mIdentity->setIdent(user.loginName());
50 mIdentity->setRealName(user.property(KUser::FullName).toString());
51
52 const QStringList nickList = {
53 user.loginName(),
54 user.loginName() + QLatin1Char('_'),
55 user.loginName() + QStringLiteral("__"),
56 };
57 mIdentity->setNicknameList(nickList);
58
59 Konversation::ServerGroupSettingsPtr serverGroup(new Konversation::ServerGroupSettings);
60 serverGroup->setName(QStringLiteral("libera"));
61 Konversation::ServerSettings server;
62 server.setHost(QStringLiteral("irc.libera.chat"));
63 server.setPort(6697);
64 server.setSSLEnabled(true);
65 serverGroup->addServer(server);
66 serverGroup->setIdentityId(mIdentity->id());
67 Konversation::ChannelSettings channel;
68 channel.setName(QStringLiteral("#konversation"));
69 serverGroup->addChannel(channel);
70 serverGroup->setExpanded(false);
71 mServerGroupHash.insert(0, serverGroup);
72 mQuickButtonList = defaultQuickButtonList();
73 mAutoreplaceList = defaultAutoreplaceList();
74 }
75
~Preferences()76 Preferences::~Preferences()
77 {
78 mIdentityList.clear();
79 qDeleteAll(mIgnoreList);
80 qDeleteAll(mHighlightList);
81 }
serverGroupHash()82 const Konversation::ServerGroupHash Preferences::serverGroupHash()
83 {
84 return self()->mServerGroupHash;
85 }
86
defaultQuickButtonList()87 const QStringList Preferences::defaultQuickButtonList()
88 {
89 return QStringList {
90 QStringLiteral("Op,/OP %u%n"),
91 QStringLiteral("DeOp,/DEOP %u%n"),
92 QStringLiteral("WhoIs,/WHOIS %s,%%u%n"),
93 QStringLiteral("Version,/CTCP %s,%%u VERSION%n"),
94 QStringLiteral("Kick,/KICK %u%n"),
95 QStringLiteral("Ban,/BAN %u%n"),
96 QStringLiteral("Part,/PART %c Leaving...%n"),
97 QStringLiteral("Quit,/QUIT Leaving...%n"),
98 };
99 }
100
quickButtonList()101 const QStringList Preferences::quickButtonList()
102 {
103 return self()->mQuickButtonList;
104 }
105
setQuickButtonList(const QStringList & newList)106 void Preferences::setQuickButtonList(const QStringList& newList)
107 {
108 self()->mQuickButtonList=newList;
109 }
110
clearQuickButtonList()111 void Preferences::clearQuickButtonList()
112 {
113 self()->mQuickButtonList.clear();
114 }
115
116 // --------------------------- AutoReplace ---------------------------
117
defaultAutoreplaceList()118 const QList<QStringList> Preferences::defaultAutoreplaceList()
119 {
120 return QList<QStringList> {
121 { QStringLiteral("1"), QStringLiteral("o"), QStringLiteral("\\[\\[([^\\s]+)\\]\\]"), QStringLiteral("http://en.wikipedia.org/wiki/Special:Search?go=Go&search=%1") },
122 { QStringLiteral("1"), QStringLiteral("o"), QStringLiteral("(BUG:|bug:)([0-9]+)"), QStringLiteral("https://bugs.kde.org/show_bug.cgi?id=%2") },
123 };
124 }
125
autoreplaceList()126 const QList<QStringList> Preferences::autoreplaceList()
127 {
128 return self()->mAutoreplaceList;
129 }
130
setAutoreplaceList(const QList<QStringList> & newList)131 void Preferences::setAutoreplaceList(const QList<QStringList> &newList)
132 {
133 self()->mAutoreplaceList=newList;
134 }
135
clearAutoreplaceList()136 void Preferences::clearAutoreplaceList()
137 {
138 self()->mAutoreplaceList.clear();
139 }
140
141 // --------------------------- AutoReplace ---------------------------
142
setServerGroupHash(const Konversation::ServerGroupHash & hash)143 void Preferences::setServerGroupHash(const Konversation::ServerGroupHash& hash)
144 {
145 self()->mServerGroupHash.clear();
146 self()->mServerGroupHash = hash;
147 }
148
addServerGroup(Konversation::ServerGroupSettingsPtr serverGroup)149 void Preferences::addServerGroup(Konversation::ServerGroupSettingsPtr serverGroup)
150 {
151 Konversation::ServerGroupHash hash = self()->mServerGroupHash;
152 hash.insert(serverGroup->id(), serverGroup);
153 self()->mServerGroupHash = hash;
154 }
155
serverGroupById(int id)156 const Konversation::ServerGroupSettingsPtr Preferences::serverGroupById(int id)
157 {
158 return self()->mServerGroupHash.value(id);
159 }
160
serverGroupsByServer(const QString & server)161 const QList<Konversation::ServerGroupSettingsPtr> Preferences::serverGroupsByServer(const QString& server)
162 {
163 QList<Konversation::ServerGroupSettingsPtr> serverGroups;
164 const Konversation::ServerGroupHash& serverGroupHash = self()->mServerGroupHash;
165 for (const auto& serverGroupSettings : serverGroupHash) {
166 const auto serverList = serverGroupSettings->serverList();
167 for (const auto& serverSertting : serverList) {
168 if (serverSertting.host().toLower() == server)
169 serverGroups.append(serverGroupSettings);
170 }
171 }
172 return serverGroups;
173 }
174
serverGroupIdsByName(const QString & serverGroup)175 QList<int> Preferences::serverGroupIdsByName(const QString& serverGroup)
176 {
177 QList<int> serverIds;
178 QHashIterator<int, Konversation::ServerGroupSettingsPtr> it(self()->mServerGroupHash);
179 while(it.hasNext())
180 {
181 if(it.next().value()->name().toLower() == serverGroup.toLower())
182 serverIds.append(it.key());
183 }
184 if (serverIds.isEmpty())
185 serverIds.append(-1);
186
187 return serverIds;
188 }
189
isServerGroup(const QString & server)190 bool Preferences::isServerGroup(const QString& server)
191 {
192 const Konversation::ServerGroupHash& serverGroupHash = self()->mServerGroupHash;
193 for (const auto& serverGroupSettings : serverGroupHash) {
194 if (serverGroupSettings->name().toLower() == server.toLower())
195 return true;
196 }
197 return false;
198 }
199
removeServerGroup(int id)200 void Preferences::removeServerGroup(int id)
201 {
202 self()->mServerGroupHash.remove(id);
203 }
204
205
highlightList()206 const QList<Highlight*> Preferences::highlightList()
207 {
208 return self()->mHighlightList;
209 }
210
setHighlightList(const QList<Highlight * > & newList)211 void Preferences::setHighlightList(const QList<Highlight*> &newList)
212 {
213 qDeleteAll(self()->mHighlightList);
214 self()->mHighlightList.clear();
215 self()->mHighlightList=newList;
216 }
217
addHighlight(const QString & highlight,bool regExp,const QColor & color,const QString & soundURL,const QString & autoText,const QString & chatWindows,bool notify)218 void Preferences::addHighlight(const QString& highlight, bool regExp, const QColor& color,
219 const QString& soundURL, const QString& autoText, const QString& chatWindows, bool notify)
220 {
221 self()->mHighlightList.append(new Highlight(highlight, regExp, color,
222 QUrl(soundURL), autoText, chatWindows, notify));
223 }
224
setIgnoreList(const QList<Ignore * > & newList)225 void Preferences::setIgnoreList(const QList<Ignore*> &newList)
226 {
227 clearIgnoreList();
228 self()->mIgnoreList=newList;
229 }
230
addIgnore(const QString & newIgnore)231 void Preferences::addIgnore(const QString &newIgnore)
232 {
233 QStringList ignore = newIgnore.split(QLatin1Char(','));
234 self()->mIgnoreList.append(new Ignore(ignore[0],ignore[1].toInt()));
235 }
236
removeIgnore(const QString & oldIgnore)237 bool Preferences::removeIgnore(const QString &oldIgnore)
238 {
239 for (Ignore *ignore : qAsConst(self()->mIgnoreList)) {
240 if (ignore->getName().toLower() == oldIgnore.toLower())
241 {
242 self()->mIgnoreList.removeOne(ignore);
243 delete ignore;
244 return true;
245 }
246 }
247
248 return false;
249 }
250
isIgnored(const QString & nickname)251 bool Preferences::isIgnored(const QString &nickname)
252 {
253 for (Ignore *ignore : qAsConst(self()->mIgnoreList)) {
254 if (ignore->getName().section(QLatin1Char('!'),0,0).toLower()==nickname.toLower())
255 {
256 return true;
257 }
258 }
259
260 return false;
261 }
262
setNotifyList(const QMap<int,QStringList> & newList)263 void Preferences::setNotifyList(const QMap<int, QStringList> &newList)
264 { self()->mNotifyList=newList; }
265
notifyList()266 const QMap<int, QStringList> Preferences::notifyList() { return self()->mNotifyList; }
267
notifyListByGroupId(int serverGroupId)268 const QStringList Preferences::notifyListByGroupId(int serverGroupId)
269 {
270 return self()->mNotifyList.value(serverGroupId);
271 }
272
notifyStringByGroupId(int serverGroupId)273 const QString Preferences::notifyStringByGroupId(int serverGroupId)
274 {
275 return notifyListByGroupId(serverGroupId).join(QLatin1Char(' '));
276 }
277
addNotify(int serverGroupId,const QString & newPattern)278 bool Preferences::addNotify(int serverGroupId, const QString& newPattern)
279 {
280 QStringList& list = self()->mNotifyList[serverGroupId];
281
282 if (!list.contains(newPattern, Qt::CaseInsensitive))
283 {
284 list.append(newPattern);
285
286 if (list.size() == 1)
287 Q_EMIT self()->notifyListStarted(serverGroupId);
288
289 return true;
290 }
291
292 return false;
293 }
294
removeNotify(int serverGroupId,const QString & pattern)295 bool Preferences::removeNotify(int serverGroupId, const QString& pattern)
296 {
297 if (self()->mNotifyList.contains(serverGroupId))
298 {
299 QString lowered = pattern.toLower();
300 const QStringList& oldList = self()->mNotifyList[serverGroupId];
301 QStringList newList;
302
303 for (const QString& nick : oldList) {
304 if (nick.toLower() != lowered)
305 newList << nick;
306 }
307
308 if (newList.size() < oldList.size())
309 {
310 if (newList.isEmpty())
311 self()->mNotifyList.remove(serverGroupId);
312 else
313 self()->mNotifyList[serverGroupId] = newList;
314
315 return true;
316 }
317 }
318
319 return false;
320 }
321
isNotify(int serverGroupId,const QString & pattern)322 bool Preferences::isNotify(int serverGroupId, const QString& pattern)
323 {
324 if (self()->mNotifyList.contains(serverGroupId))
325 if (self()->mNotifyList.value(serverGroupId).contains(pattern, Qt::CaseInsensitive))
326 return true;
327
328 return false;
329 }
330
331 // Default identity functions
addIdentity(const IdentityPtr & identity)332 void Preferences::addIdentity(const IdentityPtr &identity) { self()->mIdentityList.append(identity); }
removeIdentity(const IdentityPtr & identity)333 void Preferences::removeIdentity(const IdentityPtr &identity) { self()->mIdentityList.removeOne(identity); }
334
clearIdentityList()335 void Preferences::clearIdentityList()
336 {
337 self()->mIdentityList.clear();
338 }
339
identityList()340 const IdentityList Preferences::identityList() { return self()->mIdentityList; }
341
setIdentityList(const IdentityList & list)342 void Preferences::setIdentityList(const IdentityList& list)
343 {
344 self()->mIdentityList.clear();
345 self()->mIdentityList = list;
346 }
347
identityByName(const QString & name)348 const IdentityPtr Preferences::identityByName(const QString& name)
349 {
350 const QList<IdentityPtr> identities = identityList();
351 for (const auto& identity : identities) {
352 if (identity->getName() == name) {
353 return identity;
354 }
355 }
356
357 // no self()->matching identity found, return default identity
358 return identities.first();
359 }
360
identityById(int id)361 const IdentityPtr Preferences::identityById(int id)
362 {
363 const QList<IdentityPtr> identities = identityList();
364 for (const auto& identity : identities) {
365 if (identity->id() == id) {
366 return identity;
367 }
368 }
369
370 return identities.first();
371 }
372
defaultAliasList()373 QStringList Preferences::defaultAliasList()
374 {
375 // Auto-alias scripts
376 const QStringList scriptDirs = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, QStringLiteral("scripts"), QStandardPaths::LocateDirectory);
377 QSet<QString> scripts;
378
379 for (const QString &dir : scriptDirs) {
380
381 const QStringList scriptFiles = QDir(dir).entryList(QDir::Files | QDir::NoDotAndDotDot | QDir::Executable);
382
383 for (const QString &script : scriptFiles) {
384 scripts << script;
385 }
386 }
387
388 QStringList aliasList;
389
390 for (const QString& script : qAsConst(scripts)) {
391 aliasList.append(QStringLiteral("%1 /exec %1").arg(script));
392
393 // FIXME: Historically, defaultAliasList() is primarily used to dynamically
394 // compile a list of installed scripts and generate appropriate aliases for
395 // them. It's not only used when the alias preferences are reset or initia-
396 // lized, but also on application start. The following crudely adds two
397 // aliases when the 'media' script is found, to provide easy access to its
398 // capability to differentiate between audio and video media. This method
399 // needs at the very least to be split up in two, or scripts may in the
400 // future determine what aliases they want to add.
401 if (script == QLatin1String("media"))
402 {
403 aliasList.append(QStringLiteral("audio /exec media audio"));
404 aliasList.append(QStringLiteral("video /exec media video"));
405 }
406 }
407
408 return aliasList;
409 }
410
clearIgnoreList()411 void Preferences::clearIgnoreList() { qDeleteAll(self()->mIgnoreList); self()->mIgnoreList.clear(); }
ignoreList()412 const QList<Ignore*> Preferences::ignoreList() { return self()->mIgnoreList; }
413
setShowTrayIcon(bool state)414 void Preferences::setShowTrayIcon(bool state)
415 {
416 self()->PreferencesBase::setShowTrayIcon(state);
417 Q_EMIT self()->updateTrayIcon();
418 }
419
setTrayNotify(bool state)420 void Preferences::setTrayNotify(bool state)
421 {
422 self()->PreferencesBase::setTrayNotify(state);
423 Q_EMIT self()->updateTrayIcon();
424 }
425
426
setAutoUserhost(bool state)427 void Preferences::setAutoUserhost(bool state)
428 {
429 self()->PreferencesBase::setAutoUserhost(state);
430 }
431
dialogFlag(const QString & flagName)432 bool Preferences::dialogFlag(const QString& flagName)
433 {
434 KConfigGroup config(KSharedConfig::openConfig()->group("Notification self()->Messages"));
435
436 if (!config.readEntry(flagName).isEmpty())
437 return false;
438 else
439 return true;
440 }
441
setDialogFlag(const QString & flagName,bool state)442 void Preferences::setDialogFlag(const QString& flagName,bool state)
443 {
444 KConfigGroup config(KSharedConfig::openConfig()->group("Notification self()->Messages"));
445
446 if (state)
447 config.deleteEntry(flagName);
448 else
449 {
450 if (config.readEntry(flagName).isEmpty())
451 config.writeEntry(flagName,"no");
452 }
453
454 config.sync();
455 }
456
457
458 // Channel Encodings
channelEncoding(const QString & server,const QString & channel)459 const QString Preferences::channelEncoding(const QString& server,const QString& channel)
460 {
461 //check all matching server's encodings
462 //TODO when we rewrite dbus, allow for multiple encodings to be returned
463 //for true 'duplicity compatibility'
464 const QList<int> serverIds = serverGroupIdsByName(server);
465 if(serverIds.count() > 1)
466 {
467 for (int serverId : serverIds) {
468 const QString codec = channelEncoding(serverId, channel);
469 if(!codec.isEmpty())
470 return codec;
471 }
472 return QString();
473 }
474 else
475 return channelEncoding(serverIds.first(),channel);
476 }
477
channelEncoding(int serverGroupId,const QString & channel)478 const QString Preferences::channelEncoding(int serverGroupId,const QString& channel)
479 {
480 if(self()->mChannelEncodingsMap.contains(serverGroupId))
481 if(self()->mChannelEncodingsMap[serverGroupId].contains(channel.toLower()))
482 return self()->mChannelEncodingsMap[serverGroupId][channel.toLower()];
483 return QString();
484 }
485
setChannelEncoding(const QString & server,const QString & channel,const QString & encoding)486 void Preferences::setChannelEncoding(const QString& server,const QString& channel,const QString& encoding)
487 {
488 //set channel encoding for ALL matching servergroups
489 const QList<int> serverIds = serverGroupIdsByName(server);
490 if(serverIds.count() > 1)
491 {
492 for (int serverId : serverIds)
493 setChannelEncoding(serverId, channel, encoding);
494 }
495 else
496 setChannelEncoding(serverIds.first(),channel,encoding);
497 }
498
setChannelEncoding(int serverGroupId,const QString & channel,const QString & encoding)499 void Preferences::setChannelEncoding(int serverGroupId,const QString& channel,const QString& encoding)
500 {
501 self()->mChannelEncodingsMap[serverGroupId][channel.toLower()]=encoding;
502 }
503
channelEncodingsServerGroupIdList()504 const QList<int> Preferences::channelEncodingsServerGroupIdList()
505 {
506 return self()->mChannelEncodingsMap.keys();
507 }
508
channelEncodingsChannelList(int serverGroupId)509 const QStringList Preferences::channelEncodingsChannelList(int serverGroupId)
510 {
511 return self()->mChannelEncodingsMap[serverGroupId].keys();
512 }
513
spellCheckingLanguage(const Konversation::ServerGroupSettingsPtr & serverGroup,const QString & key)514 const QString Preferences::spellCheckingLanguage(const Konversation::ServerGroupSettingsPtr &serverGroup, const QString& key)
515 {
516 if (self()->mServerGroupSpellCheckingLanguages.contains(serverGroup))
517 return self()->mServerGroupSpellCheckingLanguages.value(serverGroup).value(key);
518
519 return QString();
520 }
521
spellCheckingLanguage(const QString & server,const QString & key)522 const QString Preferences::spellCheckingLanguage(const QString& server, const QString& key)
523 {
524 if (self()->mServerSpellCheckingLanguages.contains(server))
525 return self()->mServerSpellCheckingLanguages.value(server).value(key);
526
527 return QString();
528 }
529
setSpellCheckingLanguage(const Konversation::ServerGroupSettingsPtr & serverGroup,const QString & key,const QString & language)530 void Preferences::setSpellCheckingLanguage(const Konversation::ServerGroupSettingsPtr &serverGroup, const QString& key, const QString& language)
531 {
532 QHash<QString, QString> languageHash;
533
534 if (self()->mServerGroupSpellCheckingLanguages.contains(serverGroup))
535 languageHash = self()->mServerGroupSpellCheckingLanguages.value(serverGroup);
536
537 languageHash.insert(key, language);
538
539 self()->mServerGroupSpellCheckingLanguages.insert(serverGroup, languageHash);
540 }
541
setSpellCheckingLanguage(const QString & server,const QString & key,const QString & language)542 void Preferences::setSpellCheckingLanguage(const QString& server, const QString& key, const QString& language)
543 {
544 QHash<QString, QString> languageHash;
545
546 if (self()->mServerSpellCheckingLanguages.contains(server))
547 languageHash = self()->mServerSpellCheckingLanguages.value(server);
548
549 languageHash.insert(key, language);
550
551 self()->mServerSpellCheckingLanguages.insert(server, languageHash);
552 }
553
serverGroupSpellCheckingLanguages()554 const QHash< Konversation::ServerGroupSettingsPtr, QHash< QString, QString > > Preferences::serverGroupSpellCheckingLanguages()
555 {
556 return self()->mServerGroupSpellCheckingLanguages;
557 }
558
serverSpellCheckingLanguages()559 const QHash< QString, QHash< QString, QString > > Preferences::serverSpellCheckingLanguages()
560 {
561 return self()->mServerSpellCheckingLanguages;
562 }
563
defaultNicknameSortingOrder()564 const QString Preferences::defaultNicknameSortingOrder()
565 {
566 return QStringLiteral("qpohv-");
567 }
568
569 // override to add %u if needed
webBrowserCmd()570 QString Preferences::webBrowserCmd()
571 {
572 // add %u to command if it's not in there
573 QString cmd=self()->mWebBrowserCmd;
574 if (!cmd.contains(QLatin1String("%u")))
575 cmd += QStringLiteral(" %u");
576 return cmd;
577 }
578
saveColumnState(QTreeView * treeView,const QString & name)579 void Preferences::saveColumnState(QTreeView *treeView, const QString &name)
580 {
581 KConfigGroup group(KSharedConfig::openConfig(), name);
582
583 QList<int> columnWidths;
584 const int columnCount = treeView->header()->count();
585 columnWidths.reserve(columnCount);
586 for (int i = 0; i < columnCount; ++i)
587 columnWidths.append(treeView->columnWidth(i));
588 // save column widths
589 group.writeEntry("ColumnWidths", columnWidths);
590 group.writeEntry("ColumnSorted", treeView->header()->sortIndicatorSection());
591 group.writeEntry("ColumnSortDescending", treeView->header()->sortIndicatorOrder() == Qt::DescendingOrder ? true : false );
592 }
593
restoreColumnState(QTreeView * treeView,const QString & name,int defaultColumn,Qt::SortOrder defaultSortOrder)594 void Preferences::restoreColumnState(QTreeView* treeView, const QString &name, int defaultColumn , Qt::SortOrder defaultSortOrder)
595 {
596 KConfigGroup group(KSharedConfig::openConfig(), name);
597
598 QList<int> columnWidths = group.readEntry("ColumnWidths", QList<int>());
599 for (int i = 0; i < columnWidths.count(); ++i)
600 if (columnWidths.at(i))
601 treeView->setColumnWidth(i, columnWidths.at(i));
602
603 if (group.readEntry("ColumnSortDescending", (defaultSortOrder == Qt::DescendingOrder)))
604 treeView->header()->setSortIndicator(group.readEntry("ColumnSorted", defaultColumn), Qt::DescendingOrder);
605 else
606 treeView->header()->setSortIndicator(group.readEntry("ColumnSorted", defaultColumn), Qt::AscendingOrder);
607 }
608
slotSetUseOSD(bool use)609 void Preferences::slotSetUseOSD(bool use)
610 {
611 self()->setUseOSD(use);
612 }
613
614
615