1/*
2 This file is part of the KDE project
3 Copyright (C) 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 "editactiondialog.h"
22
23#include <QtGui/QItemDelegate>
24#include <QtGui/QComboBox>
25
26#include <KDebug>
27
28#include "urlgrabber.h"
29#include "ui_editactiondialog.h"
30
31namespace {
32 static QString output2text(ClipCommand::Output output) {
33 switch(output) {
34 case ClipCommand::IGNORE:
35 return QString(i18n("Ignore"));
36 case ClipCommand::REPLACE:
37 return QString(i18n("Replace Clipboard"));
38 case ClipCommand::ADD:
39 return QString(i18n("Add to Clipboard"));
40 }
41 return QString();
42 }
43
44}
45
46/**
47 * Show dropdown of editing Output part of commands
48 */
49class ActionOutputDelegate : public QItemDelegate {
50 public:
51 ActionOutputDelegate(QObject* parent = 0) : QItemDelegate(parent){
52 }
53
54 virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const {
55 QComboBox* editor = new QComboBox(parent);
56 editor->setInsertPolicy(QComboBox::NoInsert);
57 editor->addItem(output2text(ClipCommand::IGNORE), QVariant::fromValue<ClipCommand::Output>(ClipCommand::IGNORE));
58 editor->addItem(output2text(ClipCommand::REPLACE), QVariant::fromValue<ClipCommand::Output>(ClipCommand::REPLACE));
59 editor->addItem(output2text(ClipCommand::ADD), QVariant::fromValue<ClipCommand::Output>(ClipCommand::ADD));
60 return editor;
61
62 }
63
64 virtual void setEditorData(QWidget* editor, const QModelIndex& index) const {
65 QComboBox* ed = static_cast<QComboBox*>(editor);
66 QVariant data(index.model()->data(index, Qt::EditRole));
67 ed->setCurrentIndex(static_cast<int>(data.value<ClipCommand::Output>()));
68 }
69
70 virtual void setModelData(QWidget* editor, QAbstractItemModel* model, const QModelIndex& index) const {
71 QComboBox* ed = static_cast<QComboBox*>(editor);
72 model->setData(index, ed->itemData(ed->currentIndex()));
73 }
74
75 virtual void updateEditorGeometry(QWidget* editor, const QStyleOptionViewItem& option, const QModelIndex& /*index*/) const {
76 editor->setGeometry(option.rect);
77 }
78};
79
80class ActionDetailModel : public QAbstractTableModel {
81 public:
82 ActionDetailModel(ClipAction* action, QObject* parent = 0);
83 virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
84 virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
85 virtual Qt::ItemFlags flags(const QModelIndex& index) const;
86 virtual int rowCount(const QModelIndex& parent = QModelIndex()) const;
87 virtual int columnCount(const QModelIndex& parent) const;
88 virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
89 const QList<ClipCommand>& commands() const { return m_commands; }
90 void addCommand(const ClipCommand& command);
91 void removeCommand(const QModelIndex& index);
92
93 private:
94 enum column_t {
95 COMMAND_COL = 0,
96 OUTPUT_COL = 1,
97 DESCRIPTION_COL = 2
98 };
99 QList<ClipCommand> m_commands;
100 QVariant displayData(ClipCommand* command, column_t colunm) const;
101 QVariant editData(ClipCommand* command, column_t column) const;
102 QVariant decorationData(ClipCommand* command, column_t column) const;
103 void setIconForCommand(ClipCommand& cmd);
104};
105
106ActionDetailModel::ActionDetailModel(ClipAction* action, QObject* parent):
107 QAbstractTableModel(parent),
108 m_commands(action->commands())
109{
110
111}
112
113Qt::ItemFlags ActionDetailModel::flags(const QModelIndex& /*index*/) const
114{
115 return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemIsSelectable;
116}
117
118
119void ActionDetailModel::setIconForCommand(ClipCommand& cmd)
120{
121 // let's try to update icon of the item according to command
122 QString command = cmd.command;
123 if ( command.contains( ' ' ) ) {
124 // get first word
125 command = command.section( ' ', 0, 0 );
126 }
127
128 QPixmap iconPix = KIconLoader::global()->loadIcon(
129 command, KIconLoader::Small, 0,
130 KIconLoader::DefaultState,
131 QStringList(), 0, true /* canReturnNull */ );
132
133 if ( !iconPix.isNull() ) {
134 cmd.icon = command;
135 } else {
136 cmd.icon.clear();
137 }
138
139}
140
141bool ActionDetailModel::setData(const QModelIndex& index, const QVariant& value, int role)
142{
143 if (role == Qt::EditRole) {
144 ClipCommand cmd = m_commands.at(index.row());
145 switch (static_cast<column_t>(index.column())) {
146 case COMMAND_COL:
147 cmd.command = value.value<QString>();
148 setIconForCommand(cmd);
149 break;
150 case OUTPUT_COL:
151 cmd.output = value.value<ClipCommand::Output>();
152 break;
153 case DESCRIPTION_COL:
154 cmd.description = value.value<QString>();
155 break;
156 }
157 m_commands.replace(index.row(), cmd);
158 emit dataChanged(index, index);
159 return true;
160 }
161 return false;
162}
163
164int ActionDetailModel::columnCount(const QModelIndex& /*parent*/) const
165{
166 return 3;
167}
168
169int ActionDetailModel::rowCount(const QModelIndex&) const
170{
171 return m_commands.count();
172}
173
174QVariant ActionDetailModel::displayData(ClipCommand* command, ActionDetailModel::column_t column) const
175{
176 switch (column) {
177 case COMMAND_COL:
178 return command->command;
179 case OUTPUT_COL:
180 return output2text(command->output);
181 case DESCRIPTION_COL:
182 return command->description;
183 }
184 return QVariant();
185}
186
187QVariant ActionDetailModel::decorationData(ClipCommand* command, ActionDetailModel::column_t column) const
188{
189 switch (column) {
190 case COMMAND_COL:
191 return command->icon.isEmpty() ? KIcon( "system-run" ) : KIcon( command->icon );
192 case OUTPUT_COL:
193 case DESCRIPTION_COL:
194 break;
195 }
196 return QVariant();
197
198}
199
200QVariant ActionDetailModel::editData(ClipCommand* command, ActionDetailModel::column_t column) const
201{
202 switch (column) {
203 case COMMAND_COL:
204 return command->command;
205 case OUTPUT_COL:
206 return QVariant::fromValue<ClipCommand::Output>(command->output);
207 case DESCRIPTION_COL:
208 return command->description;
209 }
210 return QVariant();
211
212}
213
214QVariant ActionDetailModel::headerData(int section, Qt::Orientation orientation, int role) const
215{
216 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
217 switch(static_cast<column_t>(section)) {
218 case COMMAND_COL:
219 return i18n("Command");
220 case OUTPUT_COL:
221 return i18n("Output Handling");
222 case DESCRIPTION_COL:
223 return i18n("Description");
224 }
225 }
226 return QAbstractTableModel::headerData(section, orientation, role);
227}
228
229
230QVariant ActionDetailModel::data(const QModelIndex& index, int role) const
231{
232 const int column = index.column();
233 const int row = index.row();
234 ClipCommand cmd = m_commands.at(row);
235 switch (role) {
236 case Qt::DisplayRole:
237 return displayData(&cmd, static_cast<column_t>(column));
238 case Qt::DecorationRole:
239 return decorationData(&cmd, static_cast<column_t>(column));
240 case Qt::EditRole:
241 return editData(&cmd, static_cast<column_t>(column));
242 }
243 return QVariant();
244}
245
246void ActionDetailModel::addCommand(const ClipCommand& command) {
247 beginInsertRows(QModelIndex(), rowCount(), rowCount());
248 m_commands << command;
249 endInsertRows();
250}
251
252void ActionDetailModel::removeCommand(const QModelIndex& index) {
253 int row = index.row();
254 beginRemoveRows(QModelIndex(), row, row);
255 m_commands.removeAt(row);
256 endRemoveRows();
257
258}
259
260EditActionDialog::EditActionDialog(QWidget* parent)
261 : KDialog(parent)
262{
263 setCaption(i18n("Action Properties"));
264 setButtons(KDialog::Ok | KDialog::Cancel);
265
266 QWidget* dlgWidget = new QWidget(this);
267 m_ui = new Ui::EditActionDialog;
268 m_ui->setupUi(dlgWidget);
269
270 m_ui->leRegExp->setClearButtonShown(true);
271 m_ui->leDescription->setClearButtonShown(true);
272
273 m_ui->pbAddCommand->setIcon(KIcon("list-add"));
274 m_ui->pbRemoveCommand->setIcon(KIcon("list-remove"));
275
276 // For some reason, the default row height is 30 pixel. Set it to the minimum sectionSize instead,
277 // which is the font height+struts.
278 m_ui->twCommandList->verticalHeader()->setDefaultSectionSize(m_ui->twCommandList->verticalHeader()->minimumSectionSize());
279 m_ui->twCommandList->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft);
280 setMainWidget(dlgWidget);
281
282 connect(m_ui->pbAddCommand, SIGNAL(clicked()), SLOT(onAddCommand()) );
283 connect(m_ui->pbRemoveCommand, SIGNAL(clicked()), SLOT(onRemoveCommand()) );
284
285 const KConfigGroup grp = KGlobal::config()->group("EditActionDialog");
286 restoreDialogSize(grp);
287 QByteArray hdrState = grp.readEntry("ColumnState", QByteArray());
288 if (!hdrState.isEmpty()) {
289 kDebug() << "Restoring column state";
290 m_ui->twCommandList->horizontalHeader()->restoreState(QByteArray::fromBase64(hdrState));
291 }
292 // do this after restoreState()
293 m_ui->twCommandList->horizontalHeader()->setHighlightSections(false);
294}
295
296EditActionDialog::~EditActionDialog()
297{
298 delete m_ui;
299}
300
301void EditActionDialog::setAction(ClipAction* act, int commandIdxToSelect)
302{
303 m_action = act;
304 m_model = new ActionDetailModel(act, this);
305 m_ui->twCommandList->setModel(m_model);
306 m_ui->twCommandList->setItemDelegateForColumn(1, new ActionOutputDelegate);
307 connect(m_ui->twCommandList->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), SLOT(onSelectionChanged()));
308
309 updateWidgets( commandIdxToSelect );
310}
311
312void EditActionDialog::updateWidgets(int commandIdxToSelect)
313{
314 if (!m_action) {
315 kDebug() << "no action to edit was set";
316 return;
317 }
318
319 m_ui->leRegExp->setText(m_action->regExp());
320 m_ui->automatic->setChecked(m_action->automatic());
321 m_ui->leDescription->setText(m_action->description());
322
323 if (commandIdxToSelect != -1) {
324 m_ui->twCommandList->setCurrentIndex( m_model->index( commandIdxToSelect ,0 ) );
325 }
326
327 // update Remove button
328 onSelectionChanged();
329}
330
331void EditActionDialog::saveAction()
332{
333 if (!m_action) {
334 kDebug() << "no action to edit was set";
335 return;
336 }
337
338 m_action->setRegExp( m_ui->leRegExp->text() );
339 m_action->setDescription( m_ui->leDescription->text() );
340 m_action->setAutomatic( m_ui->automatic->isChecked() );
341
342 m_action->clearCommands();
343
344 foreach ( const ClipCommand& cmd, m_model->commands() ){
345 m_action->addCommand( cmd );
346 }
347}
348
349void EditActionDialog::slotButtonClicked( int button )
350{
351 if ( button == KDialog::Ok ) {
352 saveAction();
353
354 kDebug() << "Saving dialogue state";
355 KConfigGroup grp = KGlobal::config()->group("EditActionDialog");
356 saveDialogSize(grp);
357 grp.writeEntry("ColumnState",
358 m_ui->twCommandList->horizontalHeader()->saveState().toBase64());
359 }
360
361 KDialog::slotButtonClicked( button );
362}
363
364void EditActionDialog::onAddCommand()
365{
366 m_model->addCommand(ClipCommand(i18n( "new command" ),
367 i18n( "Command Description" ),
368 true,
369 "" ));
370 m_ui->twCommandList->edit( m_model->index( m_model->rowCount()-1, 0 ));
371}
372
373void EditActionDialog::onRemoveCommand()
374{
375 m_model->removeCommand(m_ui->twCommandList->selectionModel()->currentIndex());
376}
377
378void EditActionDialog::onSelectionChanged()
379{
380 m_ui->pbRemoveCommand->setEnabled( m_ui->twCommandList->selectionModel() && m_ui->twCommandList->selectionModel()->hasSelection() );
381}
382
383#include "editactiondialog.moc"
384