1 /*
2  * This file is part of Licq, an instant messaging client for UNIX.
3  * Copyright (C) 2007-2012 Licq developers <licq-dev@googlegroups.com>
4  *
5  * Licq is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * Licq is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with Licq; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  */
19 
20 #include "events.h"
21 
22 #include "config.h"
23 
24 #include <boost/foreach.hpp>
25 
26 #include <QCheckBox>
27 #include <QComboBox>
28 #include <QGridLayout>
29 #include <QGroupBox>
30 #include <QLabel>
31 #include <QPushButton>
32 #include <QTreeWidget>
33 #include <QVBoxLayout>
34 
35 #include <licq/contactlist/owner.h>
36 #include <licq/daemon.h>
37 #include <licq/filter.h>
38 #include <licq/plugin/pluginmanager.h>
39 #include <licq/oneventmanager.h>
40 #include <licq/userevents.h>
41 
42 #include "config/chat.h"
43 #include "config/contactlist.h"
44 #include "config/general.h"
45 #include "config/shortcuts.h"
46 #include "dialogs/filterruledlg.h"
47 #include "dialogs/hintsdlg.h"
48 #include "widgets/shortcutedit.h"
49 
50 #include "oneventbox.h"
51 #include "settingsdlg.h"
52 
53 
54 using Licq::OnEventData;
55 using Licq::gOnEventManager;
56 using namespace LicqQtGui;
57 /* TRANSLATOR LicqQtGui::Settings::Events */
58 
Events(SettingsDlg * parent)59 Settings::Events::Events(SettingsDlg* parent)
60   : QObject(parent)
61 {
62   parent->addPage(SettingsDlg::OnEventPage, createPageOnEvent(parent),
63       tr("Events"));
64   parent->addPage(SettingsDlg::SoundsPage, createPageSounds(parent),
65       tr("Sounds"), SettingsDlg::OnEventPage);
66   parent->addPage(SettingsDlg::FilterPage, createPageFilter(parent),
67       tr("Filter"), SettingsDlg::OnEventPage);
68 
69   load();
70 }
71 
createPageOnEvent(QWidget * parent)72 QWidget* Settings::Events::createPageOnEvent(QWidget* parent)
73 {
74   QWidget* w = new QWidget(parent);
75   myPageOnEventLayout = new QVBoxLayout(w);
76   myPageOnEventLayout->setContentsMargins(0, 0, 0, 0);
77 
78   myNewMsgActionsBox = new QGroupBox(tr("Actions on Incoming Messages"));
79   myMsgActionsLayout = new QGridLayout(myNewMsgActionsBox);
80 
81   myBoldOnMsgCheck = new QCheckBox(tr("Bold message label"));
82   myBoldOnMsgCheck->setToolTip(tr("Show the message info label in bold font if there are incoming messages"));
83   myMsgActionsLayout->addWidget(myBoldOnMsgCheck, 0, 0);
84 
85   myAutoFocusCheck = new QCheckBox(tr("Auto-focus message"));
86   myAutoFocusCheck->setToolTip(tr("Automatically focus opened message windows."));
87   myMsgActionsLayout->addWidget(myAutoFocusCheck, 1, 0);
88 
89   myAutoRaiseCheck = new QCheckBox(tr("Auto-raise main window"));
90   myAutoRaiseCheck->setToolTip(tr("Raise the main window on incoming messages"));
91   myMsgActionsLayout->addWidget(myAutoRaiseCheck, 2, 0);
92 
93   QHBoxLayout* autoPopupLayout = new QHBoxLayout();
94   QLabel* autoPopupLabel = new QLabel(tr("Auto-popup message:"));
95   autoPopupLayout->addWidget(autoPopupLabel);
96   myAutoPopupCombo = new QComboBox();
97   myAutoPopupCombo->addItem(tr("Never"));
98   myAutoPopupCombo->addItem(tr("Only when online"));
99   myAutoPopupCombo->addItem(tr("When online or away"));
100   myAutoPopupCombo->addItem(tr("When online, away or N/A"));
101   myAutoPopupCombo->addItem(tr("Always except DND"));
102   myAutoPopupCombo->addItem(tr("Always"));
103   myAutoPopupCombo->setToolTip(tr("Select for which statuses incoming messages should "
104       "open automatically.\nOnline also includes Free for chat."));
105   autoPopupLabel->setBuddy(myAutoPopupCombo);
106   autoPopupLayout->addWidget(myAutoPopupCombo);
107   myMsgActionsLayout->addLayout(autoPopupLayout, 3, 0);
108 
109   myAutoUrgentCheck = new QCheckBox(tr("Auto-popup urgent only"));
110   myAutoUrgentCheck->setToolTip(tr("Only auto-popup urgent messages."));
111   myMsgActionsLayout->addWidget(myAutoUrgentCheck, 4, 0);
112 
113   myFlashTaskbarCheck = new QCheckBox(tr("Flash taskbar"));
114   myFlashTaskbarCheck->setToolTip(tr("Flash the taskbar on incoming messages"));
115   myMsgActionsLayout->addWidget(myFlashTaskbarCheck, 0, 1);
116 
117   myFlashAllCheck = new QCheckBox(tr("Blink all events"));
118   myFlashAllCheck->setToolTip(tr("All incoming events will blink"));
119   myMsgActionsLayout->addWidget(myFlashAllCheck, 1, 1);
120 
121   myFlashUrgentCheck = new QCheckBox(tr("Blink urgent events"));
122   myFlashUrgentCheck->setToolTip(tr("Only urgent events will blink"));
123   myMsgActionsLayout->addWidget(myFlashUrgentCheck, 2, 1);
124 
125 #ifdef Q_WS_X11
126   QHBoxLayout* hotKeyLayout = new QHBoxLayout();
127   myHotKeyLabel = new QLabel(tr("Hot key:"));
128   hotKeyLayout->addWidget(myHotKeyLabel);
129   hotKeyLayout->addStretch();
130   myHotKeyLabel->setToolTip(tr("Hotkey to pop up the next pending message.\n"
131       "Enter the hotkey literally, like \"shift+f10\", or \"none\" for disabling."));
132   myHotKeyEdit = new ShortcutEdit();
133   myHotKeyEdit->setToolTip(myHotKeyLabel->toolTip());
134   myHotKeyLabel->setBuddy(myHotKeyEdit);
135   hotKeyLayout->addWidget(myHotKeyEdit);
136   myMsgActionsLayout->addLayout(hotKeyLayout, 3, 1);
137 #endif
138 
139   // Make the columns evenly wide
140   myMsgActionsLayout->setColumnStretch(0, 1);
141   myMsgActionsLayout->setColumnStretch(1, 1);
142 
143   myParanoiaBox = new QGroupBox(tr("Paranoia"));
144   myParanoiaLayout = new QVBoxLayout(myParanoiaBox);
145 
146   myIgnoreNewUsersCheck = new QCheckBox(tr("Ignore new users"));
147   myIgnoreNewUsersCheck->setToolTip(tr("Determines if new users are automatically added to your list or must first request authorization."));
148   myParanoiaLayout->addWidget(myIgnoreNewUsersCheck);
149 
150   myIgnoreMassMsgCheck = new QCheckBox(tr("Ignore mass messages"));
151   myIgnoreMassMsgCheck->setToolTip(tr("Determines if mass messages are ignored or not."));
152   myParanoiaLayout->addWidget(myIgnoreMassMsgCheck);
153 
154   myIgnoreWebPanelCheck = new QCheckBox(tr("Ignore web panel"));
155   myIgnoreWebPanelCheck->setToolTip(tr("Determines if web panel messages are ignored or not."));
156   myParanoiaLayout->addWidget(myIgnoreWebPanelCheck);
157 
158   myIgnoreEmailPagerCheck = new QCheckBox(tr("Ignore email pager"));
159   myIgnoreEmailPagerCheck->setToolTip(tr("Determines if email pager messages are ignored or not."));
160   myParanoiaLayout->addWidget(myIgnoreEmailPagerCheck);
161 
162 
163   myPageOnEventLayout->addWidget(myNewMsgActionsBox);
164   myPageOnEventLayout->addWidget(myParanoiaBox);
165   myPageOnEventLayout->addStretch(1);
166 
167   return w;
168 }
169 
createPageSounds(QWidget * parent)170 QWidget* Settings::Events::createPageSounds(QWidget* parent)
171 {
172   QWidget* w = new QWidget(parent);
173   myPageSoundsLayout = new QVBoxLayout(w);
174   myPageSoundsLayout->setContentsMargins(0, 0, 0, 0);
175 
176   myOnEventBox = new OnEventBox(true);
177   myPageSoundsLayout->addWidget(myOnEventBox);
178 
179   QGridLayout* soundsLayout = dynamic_cast<QGridLayout*>(myOnEventBox->layout());
180   int soundsRows = soundsLayout->rowCount();
181 
182   myNoSoundInActiveChatCheck = new QCheckBox(tr("Disable sound for active window"));
183   myNoSoundInActiveChatCheck->setToolTip(tr("Don't perform OnEvent command if chat window for user is currently active."));
184   soundsLayout->addWidget(myNoSoundInActiveChatCheck, soundsRows, 1, 1, 3);
185 
186   myPageSoundsLayout->addStretch(1);
187 
188   return w;
189 }
190 
createPageFilter(QWidget * parent)191 QWidget* Settings::Events::createPageFilter(QWidget* parent)
192 {
193   QWidget* w = new QWidget(parent);
194   QVBoxLayout* pageFilterLayout = new QVBoxLayout(w);
195   pageFilterLayout->setContentsMargins(0, 0, 0, 0);
196 
197   QGroupBox* filterRulesBox = new QGroupBox(tr("Rules for Incoming Events"));
198   pageFilterLayout->addWidget(filterRulesBox);
199   QVBoxLayout* filterRulesLayout = new QVBoxLayout(filterRulesBox);
200 
201   myRulesList = new QTreeWidget();
202   QStringList headers;
203   headers << tr("Enabled") << tr("Action") << tr("Protocol") << tr("Event Type") << tr("Expression");
204   myRulesList->setHeaderLabels(headers);
205   myRulesList->setIndentation(0);
206   myRulesList->setAllColumnsShowFocus(true);
207   filterRulesLayout->addWidget(myRulesList);
208 
209   QHBoxLayout* buttons = new QHBoxLayout();
210 
211   QPushButton* ruleHintsButton = new QPushButton(tr("Hints"));
212   buttons->addWidget(ruleHintsButton);
213 
214   QPushButton* rulesResetButton = new QPushButton(tr("Reset"));
215   buttons->addWidget(rulesResetButton);
216 
217   myRuleAddButton = new QPushButton(tr("Add"));
218   buttons->addWidget(myRuleAddButton);
219 
220   myRuleRemoveButton = new QPushButton(tr("Remove"));
221   buttons->addWidget(myRuleRemoveButton);
222 
223   myRuleEditButton = new QPushButton(tr("Modify"));
224   buttons->addWidget(myRuleEditButton);
225 
226   myRuleUpButton = new QPushButton(tr("Move Up"));
227   buttons->addWidget(myRuleUpButton);
228 
229   myRuleDownButton = new QPushButton(tr("Move Down"));
230   buttons->addWidget(myRuleDownButton);
231 
232   filterRulesLayout->addLayout(buttons);
233 
234   myRuleEditor = NULL;
235 
236   connect(myRulesList, SIGNAL(itemSelectionChanged()), SLOT(updateRuleButtons()));
237   connect(myRulesList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
238       SLOT(editRule(QTreeWidgetItem*,int)));
239   connect(ruleHintsButton, SIGNAL(clicked()), SLOT(showRuleHints()));
240   connect(rulesResetButton, SIGNAL(clicked()), SLOT(resetRules()));
241   connect(myRuleAddButton, SIGNAL(clicked()), SLOT(insertRule()));
242   connect(myRuleRemoveButton, SIGNAL(clicked()), SLOT(removeRule()));
243   connect(myRuleEditButton, SIGNAL(clicked()), SLOT(editRule()));
244   connect(myRuleUpButton, SIGNAL(clicked()), SLOT(moveRuleUp()));
245   connect(myRuleDownButton, SIGNAL(clicked()), SLOT(moveRuleDown()));
246 
247   return w;
248 }
249 
load()250 void Settings::Events::load()
251 {
252   Config::Chat* chatConfig = Config::Chat::instance();
253   Config::ContactList* contactListConfig = Config::ContactList::instance();
254   Config::General* generalConfig = Config::General::instance();
255   Config::Shortcuts* shortcutConfig = Config::Shortcuts::instance();
256 
257   myAutoRaiseCheck->setChecked(generalConfig->autoRaiseMainwin());
258   myBoldOnMsgCheck->setChecked(generalConfig->boldOnMsg());
259 
260 #ifdef Q_WS_X11
261   myHotKeyEdit->setKeySequence(shortcutConfig->getShortcut(Config::Shortcuts::GlobalPopupMessage));
262 #endif
263 
264   Config::ContactList::FlashMode flash = contactListConfig->flash();
265   myFlashUrgentCheck->setChecked(flash == Config::ContactList::FlashUrgent || flash == Config::ContactList::FlashAll);
266   myFlashAllCheck->setChecked(flash == Config::ContactList::FlashAll);
267 
268   myAutoPopupCombo->setCurrentIndex(chatConfig->autoPopup());
269   myAutoUrgentCheck->setChecked(chatConfig->autoPopupUrgentOnly());
270   myAutoFocusCheck->setChecked(chatConfig->autoFocus());
271   myFlashTaskbarCheck->setChecked(chatConfig->flashTaskbar());
272   myNoSoundInActiveChatCheck->setChecked(chatConfig->noSoundInActiveChat());
273 
274   myIgnoreNewUsersCheck->setChecked(Licq::gDaemon.ignoreType(Licq::Daemon::IgnoreNewUsers));
275   myIgnoreMassMsgCheck->setChecked(Licq::gDaemon.ignoreType(Licq::Daemon::IgnoreMassMsg));
276   myIgnoreWebPanelCheck->setChecked(Licq::gDaemon.ignoreType(Licq::Daemon::IgnoreWebPanel));
277   myIgnoreEmailPagerCheck->setChecked(Licq::gDaemon.ignoreType(Licq::Daemon::IgnoreEmailPager));
278 
279   const OnEventData* eventData = gOnEventManager.lockGlobal();
280   myOnEventBox->load(eventData, NULL);
281   gOnEventManager.unlock(eventData);
282 
283   Licq::gFilterManager.getRules(myFilterRules);
284   updateRulesList();
285 }
286 
apply()287 void Settings::Events::apply()
288 {
289   Config::Chat* chatConfig = Config::Chat::instance();
290   Config::ContactList* contactListConfig = Config::ContactList::instance();
291   Config::General* generalConfig = Config::General::instance();
292   Config::Shortcuts* shortcutConfig = Config::Shortcuts::instance();
293   chatConfig->blockUpdates(true);
294   contactListConfig->blockUpdates(true);
295   generalConfig->blockUpdates(true);
296 
297   generalConfig->setAutoRaiseMainwin(myAutoRaiseCheck->isChecked());
298   generalConfig->setBoldOnMsg(myBoldOnMsgCheck->isChecked());
299 
300 #ifdef Q_WS_X11
301   shortcutConfig->setShortcut(Config::Shortcuts::GlobalPopupMessage, myHotKeyEdit->keySequence());
302 #endif
303 
304   if (myFlashAllCheck->isChecked())
305     contactListConfig->setFlash(Config::ContactList::FlashAll);
306   else if(myFlashUrgentCheck->isChecked())
307     contactListConfig->setFlash(Config::ContactList::FlashUrgent);
308   else
309     contactListConfig->setFlash(Config::ContactList::FlashNone);
310 
311   chatConfig->setAutoPopup(myAutoPopupCombo->currentIndex());
312   chatConfig->setAutoPopupUrgentOnly(myAutoUrgentCheck->isChecked());
313   chatConfig->setAutoFocus(myAutoFocusCheck->isChecked());
314   chatConfig->setFlashTaskbar(myFlashTaskbarCheck->isChecked());
315   chatConfig->setNoSoundInActiveChat(myNoSoundInActiveChatCheck->isChecked());
316 
317   Licq::gDaemon.setIgnoreType(Licq::Daemon::IgnoreNewUsers, myIgnoreNewUsersCheck->isChecked());
318   Licq::gDaemon.setIgnoreType(Licq::Daemon::IgnoreMassMsg, myIgnoreMassMsgCheck->isChecked());
319   Licq::gDaemon.setIgnoreType(Licq::Daemon::IgnoreWebPanel, myIgnoreWebPanelCheck->isChecked());
320   Licq::gDaemon.setIgnoreType(Licq::Daemon::IgnoreEmailPager, myIgnoreEmailPagerCheck->isChecked());
321 
322   OnEventData* eventData = gOnEventManager.lockGlobal();
323   myOnEventBox->apply(eventData);
324   gOnEventManager.unlock(eventData, true);
325 
326   Licq::gFilterManager.setRules(myFilterRules);
327 
328   chatConfig->blockUpdates(false);
329   contactListConfig->blockUpdates(false);
330   generalConfig->blockUpdates(false);
331 }
332 
updateRulesList()333 void Settings::Events::updateRulesList()
334 {
335   myRulesList->clear();
336   BOOST_FOREACH(const Licq::FilterRule& rule, myFilterRules)
337   {
338     QTreeWidgetItem* item = new QTreeWidgetItem(myRulesList);
339 
340     item->setText(0, (rule.isEnabled ? tr("Yes") : tr("No")));
341 
342     QString actionStr;
343     switch (rule.action)
344     {
345       case Licq::FilterRule::ActionAccept: actionStr = tr("Accept"); break;
346       case Licq::FilterRule::ActionSilent: actionStr = tr("Silent"); break;
347       case Licq::FilterRule::ActionIgnore: actionStr = tr("Ignore"); break;
348     }
349     item->setText(1, actionStr);
350 
351     if (rule.protocolId == 0)
352     {
353       item->setText(2, tr("Any"));
354     }
355     else
356     {
357       Licq::ProtocolPlugin::Ptr proto = Licq::gPluginManager.getProtocolPlugin(rule.protocolId);
358       if (proto.get() != 0)
359         item->setText(2, QString::fromLocal8Bit(proto->name().c_str()));
360     }
361 
362     QString eventName;
363     for (int i = 0; i < 32; ++i)
364     {
365       if ((rule.eventMask & (1<<i)) == 0)
366         continue;
367       if (eventName.isEmpty())
368       {
369         eventName = QString::fromLocal8Bit(Licq::UserEvent::eventName(i).c_str());
370       }
371       else
372       {
373         eventName = tr("(Multiple)");
374         break;
375       }
376     }
377     item->setText(3, eventName);
378 
379     item->setText(4, QString::fromUtf8(rule.expression.c_str()));
380   }
381 
382   myRulesList->resizeColumnToContents(0);
383   myRulesList->resizeColumnToContents(1);
384   myRulesList->resizeColumnToContents(2);
385   myRulesList->resizeColumnToContents(3);
386   myRulesList->resizeColumnToContents(4);
387   updateRuleButtons();
388 }
389 
updateRuleButtons()390 void Settings::Events::updateRuleButtons()
391 {
392   QTreeWidgetItem* item = myRulesList->currentItem();
393   int pos = (item == NULL ? -1 : myRulesList->indexOfTopLevelItem(item));
394   bool editing = (myRuleEditor != NULL);
395   myRuleAddButton->setEnabled(!editing);
396   myRuleRemoveButton->setEnabled(item != NULL && !editing);
397   myRuleEditButton->setEnabled(item != NULL && !editing);
398   myRuleUpButton->setEnabled(item != NULL && !editing && pos > 0);
399   myRuleDownButton->setEnabled(item != NULL && !editing &&
400       pos < myRulesList->topLevelItemCount() - 1);
401 }
402 
showRuleHints()403 void Settings::Events::showRuleHints()
404 {
405   QString h = tr(
406       "<h2>Hints for Event Filter Rules</h2>"
407       "<p>Incoming events are run through the list of rules to decide how to"
408       " handle them. The first rule to match decides the action and if no rule"
409       " matches the default action is to accept the event. (To override the"
410       " default, add a rule last with another action that matches all event"
411       " types and has an empty expression.)</p>"
412       "<p>Any event from a user already in the contact list is always accepted"
413       " (unless they're in the ignore list). The event filter is only applied"
414       " to events from unknown users.</p>"
415       "<p>The following actions are available:</p><ul>"
416       "<li>Accept - the event as handled as normal and on events performed.</li>"
417       "<li>Silent - the event is written to history but otherwise ignored.</li>"
418       "<li>Ignore - the event is completely ignored.</li>"
419       "</ul>"
420       "<p>If the expression is empty, it will match any event. Otherwise it is"
421       " applied as a regular expression to any message in the event. "
422       "The expression must match the entire message text. (To match only part"
423       " of a message, enter it as \".*part.*\".)</p>"
424       "<p>The filter has a default set of rules that will block some common"
425       " spam messages. The defaults can be restored by removing the file"
426       " \"~/.licq/filter.conf\" while Licq is NOT running.</p>"
427       );
428   new HintsDlg(h, dynamic_cast<QWidget*>(parent()));
429 }
430 
resetRules()431 void Settings::Events::resetRules()
432 {
433   Licq::gFilterManager.getDefaultRules(myFilterRules);
434   updateRulesList();
435 }
436 
editRule(QTreeWidgetItem * item,int)437 void Settings::Events::editRule(QTreeWidgetItem* item, int /* index */)
438 {
439   if (myRuleEditor != NULL || item == NULL)
440     return;
441 
442   myRuleEditIndex = myRulesList->indexOfTopLevelItem(item);
443   if (myRuleEditIndex < 0)
444     return;
445 
446   myRuleEditor = new FilterRuleDlg(&myFilterRules[myRuleEditIndex]);
447   connect(myRuleEditor, SIGNAL(finished(int)), SLOT(editRuleDone(int)));
448 
449   updateRuleButtons();
450 }
451 
editRule()452 void Settings::Events::editRule()
453 {
454   editRule(myRulesList->currentItem(), 0);
455 }
456 
insertRule()457 void Settings::Events::insertRule()
458 {
459   myRuleEditIndex = -1;
460   myRuleEditor = new FilterRuleDlg();
461   connect(myRuleEditor, SIGNAL(finished(int)), SLOT(editRuleDone(int)));
462 
463   updateRuleButtons();
464 }
465 
editRuleDone(int dialogCode)466 void Settings::Events::editRuleDone(int dialogCode)
467 {
468   if (dialogCode == QDialog::Rejected)
469   {
470     // User pressed cancel
471     myRuleEditor = NULL;
472     updateRuleButtons();
473     return;
474   }
475 
476   if (myRuleEditIndex == -1)
477   {
478     // Rule is added, make room for it in list
479     myRuleEditIndex = myFilterRules.size();
480     myFilterRules.resize(myRuleEditIndex + 1);
481   }
482 
483   myRuleEditor->getFilterRule(myFilterRules[myRuleEditIndex]);
484   myRuleEditor = NULL;
485 
486   updateRulesList();
487 }
488 
removeRule()489 void Settings::Events::removeRule()
490 {
491   QTreeWidgetItem* item = myRulesList->currentItem();
492   if (item == NULL)
493     return;
494 
495   int pos = myRulesList->indexOfTopLevelItem(item);
496   if (pos < 0)
497     return;
498 
499   for (int i = pos; i < (int)myFilterRules.size() - 1; ++i)
500     myFilterRules[i] = myFilterRules[i+1];
501   myFilterRules.erase(--myFilterRules.end());
502 
503   // Drop the list item instead of reloading entire list
504   delete item;
505 }
506 
moveRuleUp()507 void Settings::Events::moveRuleUp()
508 {
509   QTreeWidgetItem* item = myRulesList->currentItem();
510   if (item == NULL)
511     return;
512 
513   int pos = myRulesList->indexOfTopLevelItem(item);
514   if (pos <= 0)
515     return;
516 
517   Licq::FilterRule tempRule = myFilterRules[pos];
518   myFilterRules[pos] = myFilterRules[pos - 1];
519   myFilterRules[pos - 1] = tempRule;
520 
521   // Swap the list items instead of reloading entire list
522   myRulesList->takeTopLevelItem(pos);
523   myRulesList->insertTopLevelItem(pos-1, item);
524   myRulesList->setCurrentItem(item);
525 }
526 
moveRuleDown()527 void Settings::Events::moveRuleDown()
528 {
529   QTreeWidgetItem* item = myRulesList->currentItem();
530   if (item == NULL)
531     return;
532 
533   int pos = myRulesList->indexOfTopLevelItem(item);
534   if (pos < 0 || pos >= myRulesList->topLevelItemCount() - 1)
535     return;
536 
537   Licq::FilterRule tempRule = myFilterRules[pos];
538   myFilterRules[pos] = myFilterRules[pos + 1];
539   myFilterRules[pos + 1] = tempRule;
540 
541   // Swap the list items instead of reloading entire list
542   myRulesList->takeTopLevelItem(pos);
543   myRulesList->insertTopLevelItem(pos+1, item);
544   myRulesList->setCurrentItem(item);
545 }
546