2 * Copyright (C) 2006 Dmitry Morozhnikov <dmiceman@mail.ru>
3 * Copyright (C) 2011,2012 Laurent Montel <montel@kde.org>
5 * This program 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.
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
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 #include "customtemplates.h"
21 #include "customtemplates_kfg.h"
22 #include "globalsettings_base.h"
23 #include "ui_customtemplates_base.h"
25 #include <KIconLoader>
27 #include <KMessageBox>
31 using namespace TemplateParser
;
33 CustomTemplates::CustomTemplates( const QList
<KActionCollection
*> &actionCollection
,
36 mBlockChangeSignal( false )
38 mUi
= new Ui_CustomTemplatesBase
;
41 mUi
->mAdd
->setIcon( KIcon( "list-add" ) );
42 mUi
->mAdd
->setEnabled( false );
43 mUi
->mRemove
->setIcon( KIcon( "list-remove" ) );
44 mUi
->mDuplicate
->setIcon( KIcon( "edit-copy" ) );
46 mUi
->mList
->setColumnWidth( 0, 100 );
47 mUi
->mList
->header()->setStretchLastSection( true );
48 mUi
->mList
->setItemDelegate( new CustomTemplateItemDelegate( this ) );
49 mUi
->mEditFrame
->setEnabled( false );
51 mUi
->mName
->setTrapReturnKey( true );
52 connect( mUi
->mEdit
, SIGNAL(textChanged()),
53 this, SLOT(slotTextChanged()) );
54 connect( mUi
->mToEdit
, SIGNAL(textChanged()),
55 this, SLOT(slotTextChanged()) );
56 connect( mUi
->mCCEdit
, SIGNAL(textChanged()),
57 this, SLOT(slotTextChanged()) );
59 connect( mUi
->mName
, SIGNAL(textChanged(QString
)),
60 this, SLOT(slotNameChanged(QString
)) );
62 connect( mUi
->mName
, SIGNAL(returnPressed()),
63 this, SLOT(slotAddClicked()) );
65 connect( mUi
->mInsertCommand
, SIGNAL(insertCommand(QString
,int)),
66 this, SLOT(slotInsertCommand(QString
,int)) );
68 connect( mUi
->mAdd
, SIGNAL(clicked()),
69 this, SLOT(slotAddClicked()) );
70 connect( mUi
->mRemove
, SIGNAL(clicked()),
71 this, SLOT(slotRemoveClicked()) );
72 connect( mUi
->mDuplicate
, SIGNAL(clicked()),
73 this, SLOT(slotDuplicateClicked()) );
74 connect( mUi
->mList
, SIGNAL(currentItemChanged(QTreeWidgetItem
*,QTreeWidgetItem
*)),
75 this, SLOT(slotListSelectionChanged()) );
76 connect( mUi
->mList
, SIGNAL(itemChanged(QTreeWidgetItem
*,int)),
77 this, SLOT(slotItemChanged(QTreeWidgetItem
*,int)) );
78 connect( mUi
->mType
, SIGNAL(activated(int)),
79 this, SLOT(slotTypeActivated(int)) );
81 connect( mUi
->mKeySequenceWidget
, SIGNAL(keySequenceChanged(QKeySequence
)),
82 this, SLOT(slotShortcutChanged(QKeySequence
)) );
84 mUi
->mKeySequenceWidget
->setCheckActionCollections( actionCollection
);
86 mReplyPix
= KIconLoader().loadIcon( "mail-reply-sender", KIconLoader::Small
);
87 mReplyAllPix
= KIconLoader().loadIcon( "mail-reply-all", KIconLoader::Small
);
88 mForwardPix
= KIconLoader().loadIcon( "mail-forward", KIconLoader::Small
);
91 mUi
->mType
->addItem( QPixmap(), i18nc( "Message->", "Universal" ) );
92 mUi
->mType
->addItem( mReplyPix
, i18nc( "Message->", "Reply" ) );
93 mUi
->mType
->addItem( mReplyAllPix
, i18nc( "Message->", "Reply to All" ) );
94 mUi
->mType
->addItem( mForwardPix
, i18nc( "Message->", "Forward" ) );
96 mUi
->mHelp
->setText( i18n( "<a href=\"whatsthis\">How does this work?</a>" ) );
97 connect( mUi
->mHelp
, SIGNAL(linkActivated(QString
)),
98 SLOT(slotHelpLinkClicked(QString
)) );
100 slotNameChanged( mUi
->mName
->text() );
103 void CustomTemplates::slotHelpLinkClicked( const QString
& )
107 "<p>Here you can add, edit, and delete custom message "
108 "templates to use when you compose a reply or forwarding message. "
109 "Create the custom template by typing the name into the input box "
110 "and press the '+' button. Also, you can bind a keyboard "
111 "combination to the template for faster operations.</p>"
112 "<p>Message templates support substitution commands, "
113 "by simply typing them or selecting them from the "
114 "<i>Insert command</i> menu.</p>"
115 "<p>There are four types of custom templates: used to "
116 "<i>Reply</i>, <i>Reply to All</i>, <i>Forward</i>, and "
117 "<i>Universal</i> which can be used for all kinds of operations. "
118 "You cannot bind a keyboard shortcut to <i>Universal</i> templates.</p>"
121 QWhatsThis::showText( QCursor::pos(), help
);
124 CustomTemplates::~CustomTemplates()
130 void CustomTemplates::slotNameChanged( const QString
&text
)
132 mUi
->mAdd
->setEnabled( !text
.isEmpty() );
135 QString
CustomTemplates::indexToType( int index
)
140 typeStr
= i18nc( "Message->", "Universal" );
143 typeStr = i18n( "New Message" );
146 typeStr
= i18nc( "Message->", "Reply" );
149 typeStr
= i18nc( "Message->", "Reply to All" );
152 typeStr
= i18nc( "Message->", "Forward" );
155 typeStr
= i18nc( "Message->", "Unknown" );
161 void CustomTemplates::slotTextChanged()
163 QTreeWidgetItem
*item
= mUi
->mList
->currentItem();
165 CustomTemplateItem
*vitem
= static_cast<CustomTemplateItem
*>( item
);
166 vitem
->setContent( mUi
->mEdit
->toPlainText() );
167 if ( !mBlockChangeSignal
) {
168 vitem
->setTo( mUi
->mToEdit
->text() );
169 vitem
->setCc( mUi
->mCCEdit
->text() );
173 if ( !mBlockChangeSignal
) {
178 void CustomTemplates::iconFromType( CustomTemplates::Type type
, CustomTemplateItem
*item
)
182 item
->setIcon( 0, mReplyPix
);
185 item
->setIcon( 0, mReplyAllPix
);
188 item
->setIcon( 0, mForwardPix
);
191 item
->setIcon( 0, QPixmap() );
197 void CustomTemplates::load()
199 const QStringList list
= GlobalSettings::self()->customTemplates();
201 QStringList::const_iterator
end( list
.constEnd() );
202 for ( QStringList::const_iterator it
= list
.constBegin(); it
!= end
; ++it
) {
204 QKeySequence
shortcut( t
.shortcut() );
205 CustomTemplates::Type type
= static_cast<Type
>( t
.type() );
206 CustomTemplateItem
*item
= new CustomTemplateItem( mUi
->mList
, *it
, t
.content(),
207 shortcut
, type
, t
.to(), t
.cC() );
208 item
->setText( 1, *it
);
209 item
->setText( 0, indexToType( type
) );
210 iconFromType( type
, item
);
213 mUi
->mRemove
->setEnabled( mUi
->mList
->topLevelItemCount() > 0 && mUi
->mList
->currentItem() );
214 mUi
->mDuplicate
->setEnabled( mUi
->mList
->topLevelItemCount() > 0 && mUi
->mList
->currentItem() );
217 void CustomTemplates::save()
219 // Before saving the new templates, delete the old ones. That needs to be done before
220 // saving, since otherwise a new template with the new name wouldn't get saved.
221 KSharedConfig::Ptr config
= KSharedConfig::openConfig( "customtemplatesrc", KConfig::NoGlobals
);
222 foreach ( const QString
&item
, mItemsToDelete
) {
223 CTemplates
t( item
);
224 const QString configGroup
= t
.currentGroup();
225 config
->deleteGroup( configGroup
);
229 QTreeWidgetItemIterator
lit( mUi
->mList
);
231 CustomTemplateItem
* it
= static_cast<CustomTemplateItem
*>( *lit
);
232 const QString name
= it
->text( 1 );
236 QString content
= it
->content();
237 if ( content
.trimmed().isEmpty() ) {
241 t
.setContent( content
);
242 t
.setShortcut( it
->shortcut().toString() );
243 t
.setType( it
->customType() );
250 GlobalSettings::self()->setCustomTemplates( list
);
251 GlobalSettings::self()->writeConfig();
253 emit
templatesUpdated();
256 void CustomTemplates::slotInsertCommand( const QString
&cmd
, int adjustCursor
)
258 QTextCursor cursor
= mUi
->mEdit
->textCursor();
259 cursor
.insertText( cmd
);
260 cursor
.setPosition( cursor
.position() + adjustCursor
);
261 mUi
->mEdit
->setTextCursor( cursor
);
262 mUi
->mEdit
->setFocus();
265 bool CustomTemplates::nameAlreadyExists( const QString
&str
, QTreeWidgetItem
*item
)
267 QTreeWidgetItemIterator
lit( mUi
->mList
);
269 const QString name
= ( *lit
)->text( 1 );
270 if ( ( name
== str
) && ( (*lit
) != item
) ) {
273 i18n( "A template with same name already exists." ),
274 i18n( "Can not create template" ) );
282 void CustomTemplates::slotAddClicked()
284 const QString str
= mUi
->mName
->text();
285 if ( !str
.isEmpty() ) {
286 if ( nameAlreadyExists( str
) ) {
290 // KShortcut::null() doesn't seem to be present, although documented
291 // see slotShortcutChanged(). oh, and you should look up documentation on the EBN!
292 // FIXME There must be a better way of doing this...
293 QKeySequence nullShortcut
;
294 CustomTemplateItem
*item
=
295 new CustomTemplateItem( mUi
->mList
, str
, "", nullShortcut
, TUniversal
,
296 QString(), QString() );
297 item
->setText( 0, indexToType( TUniversal
) );
298 item
->setText( 1, str
);
299 mUi
->mList
->setCurrentItem( item
);
300 mUi
->mRemove
->setEnabled( true );
301 mUi
->mDuplicate
->setEnabled( true );
303 mUi
->mKeySequenceWidget
->setEnabled( false );
304 if ( !mBlockChangeSignal
) {
310 QString
CustomTemplates::createUniqueName( const QString
&name
) const
312 QString uniqueName
= name
;
320 QTreeWidgetItemIterator
lit( mUi
->mList
);
322 const QString itemName
= ( *lit
)->text( 1 );
323 if ( !itemName
.compare( uniqueName
) ) {
327 uniqueName
+= QString( " (" ) + QString::number( counter
) + QString( ")" );
337 void CustomTemplates::slotDuplicateClicked()
339 QTreeWidgetItem
*currentItem
= mUi
->mList
->currentItem();
340 if ( !currentItem
) {
343 CustomTemplateItem
* origItem
= static_cast<CustomTemplateItem
*>( currentItem
);
344 const QString templateName
= createUniqueName( origItem
->text( 1 ) );
345 // KShortcut::null() doesn't seem to be present, although documented
346 // see slotShortcutChanged(). oh, and you should look up documentation on the EBN!
347 // FIXME There must be a better way of doing this...
348 QKeySequence nullShortcut
;
349 CustomTemplates::Type type
= origItem
->customType();
350 CustomTemplateItem
*item
=
351 new CustomTemplateItem( mUi
->mList
, templateName
, origItem
->content(), nullShortcut
, type
,
352 origItem
->to(), origItem
->cc() );
353 item
->setText( 0, indexToType( type
) );
354 item
->setText( 1, templateName
);
355 iconFromType( type
, item
);
357 mUi
->mList
->setCurrentItem( item
);
358 mUi
->mRemove
->setEnabled( true );
359 mUi
->mDuplicate
->setEnabled( true );
361 mUi
->mKeySequenceWidget
->setEnabled( type
!= TUniversal
);
366 void CustomTemplates::slotRemoveClicked()
368 QTreeWidgetItem
*item
= mUi
->mList
->currentItem();
373 const QString templateName
= item
->text( 1 );
375 if ( KMessageBox::warningContinueCancel(
377 i18nc( "@info", "Do you really want to remove template \"%1\"?", templateName
),
378 i18nc( "@title:window", "Remove Template?" ),
379 KStandardGuiItem::remove(),
380 KStandardGuiItem::cancel() ) == KMessageBox::Continue
) {
381 mItemsToDelete
.append( templateName
);
382 delete mUi
->mList
->takeTopLevelItem( mUi
->mList
->indexOfTopLevelItem( item
) );
383 mUi
->mRemove
->setEnabled( mUi
->mList
->topLevelItemCount() > 0 );
384 mUi
->mDuplicate
->setEnabled( mUi
->mList
->topLevelItemCount() > 0 );
385 if ( !mBlockChangeSignal
) {
391 void CustomTemplates::slotListSelectionChanged()
393 QTreeWidgetItem
*item
= mUi
->mList
->currentItem();
395 mUi
->mEditFrame
->setEnabled( true );
396 mUi
->mRemove
->setEnabled(true);
397 mUi
->mDuplicate
->setEnabled(true);
398 CustomTemplateItem
*vitem
= static_cast<CustomTemplateItem
*>( item
);
399 mBlockChangeSignal
= true;
400 mUi
->mEdit
->setText( vitem
->content() );
401 mUi
->mKeySequenceWidget
->setKeySequence( vitem
->shortcut(),
402 KKeySequenceWidget::NoValidate
);
403 CustomTemplates::Type type
= vitem
->customType();
405 mUi
->mType
->setCurrentIndex( mUi
->mType
->findText( indexToType ( type
) ) );
406 mUi
->mToEdit
->setText( vitem
->to() );
407 mUi
->mCCEdit
->setText( vitem
->cc() );
408 mBlockChangeSignal
= false;
410 // I think the logic (originally 'vitem->mType==TUniversal') was inverted here:
411 // a key shortcut is only allowed for a specific type of template and not for
412 // a universal, as otherwise we won't know what sort of action to do when the
413 // key sequence is activated!
414 // This agrees with KMMainWidget::updateCustomTemplateMenus() -- marten
415 mUi
->mKeySequenceWidget
->setEnabled( type
!= TUniversal
);
417 mUi
->mEditFrame
->setEnabled( false );
420 mUi
->mKeySequenceWidget
->clearKeySequence();
421 mUi
->mType
->setCurrentIndex( 0 );
422 mUi
->mToEdit
->clear();
423 mUi
->mCCEdit
->clear();
427 void CustomTemplates::slotTypeActivated( int index
)
429 QTreeWidgetItem
*item
= mUi
->mList
->currentItem();
431 CustomTemplateItem
*vitem
= static_cast<CustomTemplateItem
*>( item
);
432 CustomTemplates::Type customtype
= static_cast<Type
>(index
);
433 vitem
->setCustomType( customtype
);
434 vitem
->setText( 0, indexToType( customtype
) );
436 iconFromType( customtype
, vitem
);
438 // see slotListSelectionChanged() above
439 mUi
->mKeySequenceWidget
->setEnabled( customtype
!= TUniversal
);
441 if ( !mBlockChangeSignal
) {
447 void CustomTemplates::slotShortcutChanged( const QKeySequence
&newSeq
)
449 QTreeWidgetItem
*item
= mUi
->mList
->currentItem();
451 CustomTemplateItem
* vitem
= static_cast<CustomTemplateItem
*>( item
);
452 vitem
->setShortcut( newSeq
);
453 mUi
->mKeySequenceWidget
->applyStealShortcut();
456 if ( !mBlockChangeSignal
) {
461 void CustomTemplates::slotItemChanged( QTreeWidgetItem
*item
, int column
)
464 CustomTemplateItem
* vitem
= static_cast<CustomTemplateItem
*>( item
);
466 const QString newName
= vitem
->text( 1 );
467 if( !newName
.isEmpty() ) {
468 const QString oldName
= vitem
->oldName();
469 if ( nameAlreadyExists( newName
, item
) ) {
470 vitem
->setText( 1, oldName
);
473 if ( newName
!= oldName
) {
474 mItemsToDelete
.append( oldName
);
475 vitem
->setOldName( newName
);
476 if ( !mBlockChangeSignal
) {
485 CustomTemplateItemDelegate::CustomTemplateItemDelegate( QObject
*parent
)
486 : QStyledItemDelegate( parent
)
490 CustomTemplateItemDelegate::~CustomTemplateItemDelegate()
494 void CustomTemplateItemDelegate::setModelData( QWidget
*editor
, QAbstractItemModel
*model
,
495 const QModelIndex
&index
) const
497 KLineEdit
*lineEdit
= static_cast<KLineEdit
*>( editor
);
498 const QString text
= lineEdit
->text();
499 if( !text
.isEmpty() ) {
500 model
->setData( index
, text
, Qt::EditRole
);
504 QWidget
*CustomTemplateItemDelegate::createEditor( QWidget
*parent
,
505 const QStyleOptionViewItem
&option
,
506 const QModelIndex
&index
) const
508 if ( index
.column() == 1 ) {
509 return QStyledItemDelegate::createEditor( parent
, option
, index
);
514 CustomTemplateItem::CustomTemplateItem( QTreeWidget
*parent
,
516 const QString
&content
,
517 const QKeySequence
&shortcut
,
518 CustomTemplates::Type type
,
521 : QTreeWidgetItem( parent
),
529 setFlags( flags() | Qt::ItemIsEditable
);
532 CustomTemplateItem::~CustomTemplateItem()
536 void CustomTemplateItem::setCustomType( CustomTemplates::Type type
)
541 CustomTemplates::Type
CustomTemplateItem::customType() const
546 QString
CustomTemplateItem::to() const
551 QString
CustomTemplateItem::cc() const
556 QString
CustomTemplateItem::content() const
561 void CustomTemplateItem::setContent( const QString
&content
)
566 void CustomTemplateItem::setTo( const QString
&to
)
571 void CustomTemplateItem::setCc( const QString
&cc
)
576 QKeySequence
CustomTemplateItem::shortcut() const
581 void CustomTemplateItem::setShortcut( const QKeySequence
&shortcut
)
583 mShortcut
= shortcut
;
586 QString
CustomTemplateItem::oldName() const
591 void CustomTemplateItem::setOldName( const QString
&name
)
596 #include "customtemplates.moc"