1/* This file is part of the KDE project
2 Copyright (C) 2000 by Carsten Pfeiffer <pfeiffer@kde.org>
3 Copyright (C) 2008-2009 by Dmitry Suzdalev <dimsuz@gmail.com>
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public
7 License as published by the Free Software Foundation; either
8 version 2 of the License, or (at your option) any later version.
9
10 This program 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 GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; see the file COPYING. If not, write to
17 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19*/
20
21#include "configdialog.h"
22
23#include <KLocale>
24#include <KMenu>
25#include <KShortcutsEditor>
26#include <KDebug>
27#include <KEditListWidget>
28
29#include "klipper.h"
30#include "editactiondialog.h"
31
32GeneralWidget::GeneralWidget(QWidget* parent)
33 : QWidget(parent)
34{
35 m_ui.setupUi(this);
36 m_ui.kcfg_TimeoutForActionPopups->setSuffix(ki18np(" second", " seconds"));
37 m_ui.kcfg_MaxClipItems->setSuffix(ki18np(" entry", " entries"));
38
39}
40
41void GeneralWidget::updateWidgets()
42{
43 if (m_ui.kcfg_IgnoreSelection->isChecked()) {
44 m_ui.kcfg_SyncClipboards->setEnabled(false);
45 m_ui.kcfg_SelectionTextOnly->setEnabled(false);
46 } else if (m_ui.kcfg_SyncClipboards->isChecked()) {
47 m_ui.kcfg_IgnoreSelection->setEnabled(false);
48 }
49
50}
51
52ActionsWidget::ActionsWidget(QWidget* parent)
53 : QWidget(parent), m_editActDlg(0)
54{
55 m_ui.setupUi(this);
56
57 m_ui.pbAddAction->setIcon(KIcon("list-add"));
58 m_ui.pbDelAction->setIcon(KIcon("list-remove"));
59 m_ui.pbEditAction->setIcon(KIcon("document-edit"));
60 m_ui.pbAdvanced->setIcon(KIcon("configure"));
61
62 const KConfigGroup grp = KGlobal::config()->group("ActionsWidget");
63 QByteArray hdrState = grp.readEntry("ColumnState", QByteArray());
64 if (!hdrState.isEmpty())
65 {
66 kDebug() << "Restoring column state";
67 m_ui.kcfg_ActionList->header()->restoreState(QByteArray::fromBase64(hdrState));
68 }
69 else
70 {
71 m_ui.kcfg_ActionList->header()->resizeSection(0, 250);
72 }
73
74#if 0
75 if ( /*KServiceTypeTrader::self()->query("KRegExpEditor/KRegExpEditor").isEmpty()*/ true) // see notice in configdialog.cpp about KRegExpEditor
76 {
77 cbUseGUIRegExpEditor->hide();
78 cbUseGUIRegExpEditor->setChecked( false );
79 }
80#endif
81
82 connect(m_ui.kcfg_ActionList, SIGNAL(itemSelectionChanged()), SLOT(onSelectionChanged()));
83 connect(m_ui.kcfg_ActionList, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)),
84 SLOT(onEditAction()));
85
86 connect(m_ui.pbAddAction, SIGNAL(clicked()), SLOT(onAddAction()));
87 connect(m_ui.pbEditAction, SIGNAL(clicked()), SLOT(onEditAction()));
88 connect(m_ui.pbDelAction, SIGNAL(clicked()), SLOT(onDeleteAction()));
89 connect(m_ui.pbAdvanced, SIGNAL(clicked()), SLOT(onAdvanced()));
90
91 onSelectionChanged();
92}
93
94void ActionsWidget::setActionList(const ActionList& list)
95{
96 qDeleteAll( m_actionList );
97 m_actionList.clear();
98
99 foreach (ClipAction* action, list) {
100 if (!action) {
101 kDebug() << "action is null!";
102 continue;
103 }
104
105 // make a copy for us to work with from now on
106 m_actionList.append( new ClipAction( *action ) );
107 }
108
109 updateActionListView();
110}
111
112void ActionsWidget::updateActionListView()
113{
114 m_ui.kcfg_ActionList->clear();
115
116 foreach (ClipAction* action, m_actionList) {
117 if (!action) {
118 kDebug() << "action is null!";
119 continue;
120 }
121
122 QTreeWidgetItem *item = new QTreeWidgetItem;
123 updateActionItem( item, action );
124
125 m_ui.kcfg_ActionList->addTopLevelItem( item );
126 }
127
128 // after all actions loaded, reset modified state of tree widget.
129 // Needed because tree widget reacts on item changed events to tell if it is changed
130 // this will ensure that apply button state will be correctly changed
131 m_ui.kcfg_ActionList->resetModifiedState();
132}
133
134void ActionsWidget::updateActionItem( QTreeWidgetItem* item, ClipAction* action )
135{
136 if ( !item || !action ) {
137 kDebug() << "null pointer passed to function, nothing done";
138 return;
139 }
140
141 // clear children if any
142 item->takeChildren();
143 item->setText( 0, action->regExp() );
144 item->setText( 1, action->description() );
145
146 foreach (const ClipCommand& command, action->commands()) {
147 QStringList cmdProps;
148 cmdProps << command.command << command.description;
149 QTreeWidgetItem *child = new QTreeWidgetItem(item, cmdProps);
150 child->setIcon(0, KIcon(command.icon.isEmpty() ? "system-run" : command.icon));
151 }
152}
153
154void ActionsWidget::setExcludedWMClasses(const QStringList& excludedWMClasses)
155{
156 m_exclWMClasses = excludedWMClasses;
157}
158
159QStringList ActionsWidget::excludedWMClasses() const
160{
161 return m_exclWMClasses;
162}
163
164ActionList ActionsWidget::actionList() const
165{
166 // return a copy of our action list
167 ActionList list;
168 foreach( ClipAction* action, m_actionList ) {
169 if ( !action ) {
170 kDebug() << "action is null";
171 continue;
172 }
173 list.append( new ClipAction( *action ) );
174 }
175
176 return list;
177}
178
179void ActionsWidget::resetModifiedState()
180{
181 m_ui.kcfg_ActionList->resetModifiedState();
182
183 kDebug() << "Saving column state";
184 KConfigGroup grp = KGlobal::config()->group("ActionsWidget");
185 grp.writeEntry("ColumnState",
186 m_ui.kcfg_ActionList->header()->saveState().toBase64());
187}
188
189void ActionsWidget::onSelectionChanged()
190{
191 bool itemIsSelected = !m_ui.kcfg_ActionList->selectedItems().isEmpty();
192 m_ui.pbEditAction->setEnabled(itemIsSelected);
193 m_ui.pbDelAction->setEnabled(itemIsSelected);
194}
195
196void ActionsWidget::onAddAction()
197{
198 if (!m_editActDlg) {
199 m_editActDlg = new EditActionDialog(this);
200 }
201
202 ClipAction* newAct = new ClipAction;
203 m_editActDlg->setAction(newAct);
204 if (m_editActDlg->exec() == QDialog::Accepted) {
205 m_actionList.append( newAct );
206
207 QTreeWidgetItem* item = new QTreeWidgetItem;
208 updateActionItem( item, newAct );
209 m_ui.kcfg_ActionList->addTopLevelItem( item );
210 }
211}
212
213void ActionsWidget::onEditAction()
214{
215 if (!m_editActDlg) {
216 m_editActDlg = new EditActionDialog(this);
217 }
218
219 QTreeWidgetItem *item = m_ui.kcfg_ActionList->currentItem();
220 int commandIdx = -1;
221 if (item) {
222 if (item->parent()) {
223 commandIdx = item->parent()->indexOfChild( item );
224 item = item->parent(); // interested in toplevel action
225 }
226
227 int idx = m_ui.kcfg_ActionList->indexOfTopLevelItem( item );
228 ClipAction* action = m_actionList.at( idx );
229
230 if ( !action ) {
231 kDebug() << "action is null";
232 return;
233 }
234
235 m_editActDlg->setAction(action, commandIdx);
236 // dialog will save values into action if user hits OK
237 m_editActDlg->exec();
238
239 updateActionItem(item, action);
240 }
241}
242
243
244void ActionsWidget::onDeleteAction()
245{
246 QTreeWidgetItem *item = m_ui.kcfg_ActionList->currentItem();
247 if ( item && item->parent() )
248 item = item->parent();
249
250 if ( item )
251 {
252 int idx = m_ui.kcfg_ActionList->indexOfTopLevelItem( item );
253 m_actionList.removeAt( idx );
254 }
255
256 delete item;
257}
258
259void ActionsWidget::onAdvanced()
260{
261 KDialog dlg(this);
262 dlg.setModal(true);
263 dlg.setCaption( i18n("Advanced Settings") );
264 dlg.setButtons( KDialog::Ok | KDialog::Cancel );
265
266 AdvancedWidget *widget = new AdvancedWidget(&dlg);
267 widget->setWMClasses( m_exclWMClasses );
268
269 dlg.setMainWidget(widget);
270
271 if ( dlg.exec() == KDialog::Accepted ) {
272 m_exclWMClasses = widget->wmClasses();
273 }
274}
275
276ConfigDialog::ConfigDialog(QWidget* parent, KConfigSkeleton* skeleton, const Klipper* klipper, KActionCollection*collection, bool isApplet)
277 : KConfigDialog(parent, "preferences", skeleton),
278 m_generalPage(new GeneralWidget(this)),
279 m_actionsPage(new ActionsWidget(this)),
280 m_klipper(klipper)
281{
282 if ( isApplet ) {
283 setHelp( QString(), "klipper" );
284 }
285
286 addPage(m_generalPage, i18nc("General Config", "General"), "klipper", i18n("General Configuration"));
287 addPage(m_actionsPage, i18nc("Actions Config", "Actions"), "system-run", i18n("Actions Configuration"));
288
289 QWidget* w = new QWidget(this);
290 m_shortcutsWidget = new KShortcutsEditor( collection, w, KShortcutsEditor::GlobalAction );
291 addPage(m_shortcutsWidget, i18nc("Shortcuts Config", "Shortcuts"), "configure-shortcuts", i18n("Shortcuts Configuration"));
292
293 const KConfigGroup grp = KGlobal::config()->group("ConfigDialog");
294 restoreDialogSize(grp);
295}
296
297
298ConfigDialog::~ConfigDialog()
299{
300}
301
302
303void ConfigDialog::updateSettings()
304{
305 // user clicked Ok or Apply
306
307 if (!m_klipper) {
308 kDebug() << "Klipper object is null";
309 return;
310 }
311
312 m_shortcutsWidget->save();
313
314 m_actionsPage->resetModifiedState();
315
316 m_klipper->urlGrabber()->setActionList(m_actionsPage->actionList());
317 m_klipper->urlGrabber()->setExcludedWMClasses(m_actionsPage->excludedWMClasses());
318 m_klipper->saveSettings();
319
320 KConfigGroup grp = KGlobal::config()->group("ConfigDialog");
321 saveDialogSize(grp);
322}
323
324void ConfigDialog::updateWidgets()
325{
326 // settings were updated, update widgets
327
328 if (m_klipper && m_klipper->urlGrabber() ) {
329 m_actionsPage->setActionList(m_klipper->urlGrabber()->actionList());
330 m_actionsPage->setExcludedWMClasses(m_klipper->urlGrabber()->excludedWMClasses());
331 } else {
332 kDebug() << "Klipper or grabber object is null";
333 return;
334 }
335 m_generalPage->updateWidgets();
336}
337
338void ConfigDialog::updateWidgetsDefault()
339{
340 // default widget values requested
341
342 m_shortcutsWidget->allDefault();
343}
344
345// it does not make sense to port / enable this since KRegExpEditor is in a very bad shape. just keep this
346// code here because it will probably help at a later point to port it when KRegExpEditor is again usable.
347// 2007-10-20, uwolfer
348#if 0
349void ListView::rename( Q3ListViewItem* item, int c )
350{
351 bool gui = false;
352 if ( item->childCount() != 0 && c == 0) {
353 // This is the regular expression
354 if ( _configWidget->useGUIRegExpEditor() ) {
355 gui = true;
356 }
357 }
358
359 if ( gui ) {
360 if ( ! _regExpEditor )
361 _regExpEditor = KServiceTypeTrader::createInstanceFromQuery<QDialog>( "KRegExpEditor/KRegExpEditor", QString(), this );
362 KRegExpEditorInterface *iface = qobject_cast<KRegExpEditorInterface *>(_regExpEditor);
363
364 Q_ASSERT( iface );
365 iface->setRegExp( item->text( 0 ) );
366
367 bool ok = _regExpEditor->exec();
368 if ( ok )
369 item->setText( 0, iface->regExp() );
370 }
371 else
372 K3ListView::rename( item ,c );
373}
374#endif
375
376AdvancedWidget::AdvancedWidget( QWidget *parent )
377 : QWidget(parent)
378{
379 QVBoxLayout *mainLayout = new QVBoxLayout(this);
380
381 QGroupBox *groupBox = new QGroupBox(i18n("D&isable Actions for Windows of Type WM_CLASS"), this);
382 groupBox->setLayout(new QVBoxLayout(groupBox));
383
384 editListBox = new KEditListWidget(groupBox);
385
386 editListBox->setButtons(KEditListWidget::Add | KEditListWidget::Remove);
387 editListBox->setCheckAtEntering(true);
388
389 editListBox->setWhatsThis(i18n("<qt>This lets you specify windows in which Klipper should "
390 "not invoke \"actions\". Use<br /><br />"
391 "<center><b>xprop | grep WM_CLASS</b></center><br />"
392 "in a terminal to find out the WM_CLASS of a window. "
393 "Next, click on the window you want to examine. The "
394 "first string it outputs after the equal sign is the one "
395 "you need to enter here.</qt>"));
396 groupBox->layout()->addWidget(editListBox);
397
398 mainLayout->addWidget(groupBox);
399
400 editListBox->setFocus();
401}
402
403AdvancedWidget::~AdvancedWidget()
404{
405}
406
407void AdvancedWidget::setWMClasses( const QStringList& items )
408{
409 editListBox->setItems(items);
410}
411
412QStringList AdvancedWidget::wmClasses() const
413{
414 return editListBox->items();
415}
416
417#include "configdialog.moc"
418