kmail

customtemplates.cpp
1 /*
2  * kmail: KDE mail client
3  * This file: Copyright (C) 2006 Dmitry Morozhnikov
4  *
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.
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
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 this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  *
19  */
20 
21 #include <config.h>
22 
23 #include <tqpopupmenu.h>
24 #include <tqpushbutton.h>
25 #include <tqtextedit.h>
26 #include <tqlabel.h>
27 #include <tqlineedit.h>
28 #include <tqtoolbox.h>
29 #include <tqtooltip.h>
30 #include <tqwhatsthis.h>
31 #include <tqfont.h>
32 
33 #include <kdebug.h>
34 #include <tdelocale.h>
35 #include <tdeglobal.h>
36 #include <kiconloader.h>
37 #include <kpushbutton.h>
38 #include <tdelistview.h>
39 #include <klineedit.h>
40 #include <tqcombobox.h>
41 #include <tdeshortcut.h>
42 #include <tdemessagebox.h>
43 #include <kkeybutton.h>
44 #include <kactivelabel.h>
45 
46 #include "customtemplates_base.h"
47 #include "customtemplates_kfg.h"
48 #include "globalsettings.h"
49 #include "kmkernel.h"
50 #include "kmmainwidget.h"
51 #include "kmfawidgets.h"
52 
53 #include "customtemplates.h"
54 
55 CustomTemplates::CustomTemplates( TQWidget *parent, const char *name )
56  :CustomTemplatesBase( parent, name ),
57  mCurrentItem( 0 ),
58  mBlockChangeSignal( false )
59 {
60  TQFont f = TDEGlobalSettings::fixedFont();
61  mEdit->setFont( f );
62 
63  mAdd->setIconSet( BarIconSet( "add", TDEIcon::SizeSmall ) );
64  mRemove->setIconSet( BarIconSet( "remove", TDEIcon::SizeSmall ) );
65 
66  mList->setColumnWidth( 0, 50 );
67  mList->setColumnWidth( 1, 100 );
68 
69  mEditFrame->setEnabled( false );
70 
71  connect( mName, TQ_SIGNAL( textChanged ( const TQString &) ),
72  this, TQ_SLOT( slotNameChanged( const TQString & ) ) );
73  connect( mEdit, TQ_SIGNAL( textChanged() ),
74  this, TQ_SLOT( slotTextChanged( void ) ) );
75  connect( mToEdit, TQ_SIGNAL( textChanged(const TQString&) ),
76  this, TQ_SLOT( slotTextChanged( void ) ) );
77  connect( mCCEdit, TQ_SIGNAL( textChanged(const TQString&) ),
78  this, TQ_SLOT( slotTextChanged( void ) ) );
79 
80  connect( mInsertCommand, TQ_SIGNAL( insertCommand(TQString, int) ),
81  this, TQ_SLOT( slotInsertCommand(TQString, int) ) );
82 
83  connect( mAdd, TQ_SIGNAL( clicked() ),
84  this, TQ_SLOT( slotAddClicked() ) );
85  connect( mRemove, TQ_SIGNAL( clicked() ),
86  this, TQ_SLOT( slotRemoveClicked() ) );
87  connect( mList, TQ_SIGNAL( selectionChanged() ),
88  this, TQ_SLOT( slotListSelectionChanged() ) );
89  connect( mType, TQ_SIGNAL( activated( int ) ),
90  this, TQ_SLOT( slotTypeActivated( int ) ) );
91 
92  connect( mKeyButton, TQ_SIGNAL( capturedShortcut( const TDEShortcut& ) ),
93  this, TQ_SLOT( slotShortcutCaptured( const TDEShortcut& ) ) );
94 
95  mReplyPix = TDEIconLoader().loadIcon( "mail-reply-sender", TDEIcon::Small );
96  mReplyAllPix = TDEIconLoader().loadIcon( "mail-reply-all", TDEIcon::Small );
97  mForwardPix = TDEIconLoader().loadIcon( "mail-forward", TDEIcon::Small );
98 
99  mType->clear();
100  mType->insertItem( TQPixmap(), i18n( "Message->", "Universal" ), TUniversal );
101  mType->insertItem( mReplyPix, i18n( "Message->", "Reply" ), TReply );
102  mType->insertItem( mReplyAllPix, i18n( "Message->", "Reply to All" ), TReplyAll );
103  mType->insertItem( mForwardPix, i18n( "Message->", "Forward" ), TForward );
104 
105  TQString help =
106  i18n( "<qt>"
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 selecting it using the right mouse "
110  " button menu or toolbar menu. Also, you can bind a keyboard "
111  "combination to the template for faster operations.</p>"
112  "<p>Message templates support substitution commands "
113  "by simple typing them or selecting them from menu "
114  "<i>Insert command</i>.</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 kind of operations. "
118  "You cannot bind keyboard shortcut to <i>Universal</i> templates.</p>"
119  "</qt>" );
120  mHelp->setText( i18n( "<a href=\"whatsthis:%1\">How does this work?</a>" ).arg( help ) );
121 
122  const TQString toToolTip = i18n( "Additional recipients of the message when forwarding" );
123  const TQString ccToolTip = i18n( "Additional recipients who get a copy of the message when forwarding" );
124  const TQString toWhatsThis = i18n( "When using this template for forwarding, the default recipients are those you enter here. This is a comma-separated list of mail addresses." );
125  const TQString ccWhatsThis = i18n( "When using this template for forwarding, the recipients you enter here will by default get a copy of this message. This is a comma-separated list of mail addresses." );
126 
127  // We only want to set the tooltip/whatsthis to the lineedit, not the complete widget,
128  // so we use the name here to find the lineedit. This is similar to what KMFilterActionForward
129  // does.
130  KLineEdit *ccLineEdit = dynamic_cast<KLineEdit*>( mCCEdit->child( "addressEdit" ) );
131  KLineEdit *toLineEdit = dynamic_cast<KLineEdit*>( mToEdit->child( "addressEdit" ) );
132  Q_ASSERT( ccLineEdit && toLineEdit );
133 
134  TQToolTip::add( mCCLabel, ccToolTip );
135  TQToolTip::add( ccLineEdit, ccToolTip );
136  TQToolTip::add( mToLabel, toToolTip );
137  TQToolTip::add( toLineEdit, toToolTip );
138  TQWhatsThis::add( mCCLabel, ccWhatsThis );
139  TQWhatsThis::add( ccLineEdit, ccWhatsThis );
140  TQWhatsThis::add( mToLabel, toWhatsThis );
141  TQWhatsThis::add( toLineEdit, toWhatsThis );
142 
143  slotNameChanged( mName->text() );
144 }
145 
146 CustomTemplates::~CustomTemplates()
147 {
148  TQDictIterator<CustomTemplateItem> it(mItemList);
149  for ( ; it.current() ; ++it ) {
150  CustomTemplateItem *vitem = mItemList.take( it.currentKey() );
151  if ( vitem ) {
152  delete vitem;
153  }
154  }
155 }
156 
157 void CustomTemplates::setRecipientsEditsEnabled( bool enabled )
158 {
159  mToEdit->setHidden( !enabled );
160  mCCEdit->setHidden( !enabled );
161  mToLabel->setHidden( !enabled );
162  mCCLabel->setHidden( !enabled );
163 }
164 
165 void CustomTemplates::slotNameChanged( const TQString& text )
166 {
167  mAdd->setEnabled( !text.isEmpty() );
168 }
169 
170 TQString CustomTemplates::indexToType( int index )
171 {
172  TQString typeStr;
173  switch ( index ) {
174  case TUniversal:
175  // typeStr = i18n( "Any" ); break;
176  break;
177 /* case TNewMessage:
178  typeStr = i18n( "New Message" ); break;*/
179  case TReply:
180  typeStr = i18n( "Message->", "Reply" ); break;
181  case TReplyAll:
182  typeStr = i18n( "Message->", "Reply to All" ); break;
183  case TForward:
184  typeStr = i18n( "Message->", "Forward" ); break;
185  default:
186  typeStr = i18n( "Message->", "Unknown" ); break;
187  }
188  return typeStr;
189 }
190 
191 void CustomTemplates::slotTextChanged()
192 {
193  if ( !mBlockChangeSignal )
194  emit changed();
195 }
196 
197 void CustomTemplates::load()
198 {
199  TQStringList list = GlobalSettings::self()->customTemplates();
200  for ( TQStringList::iterator it = list.begin(); it != list.end(); ++it ) {
201  CTemplates t(*it);
202  // TQString typeStr = indexToType( t.type() );
203  TQString typeStr;
204  TDEShortcut shortcut( t.shortcut() );
205  CustomTemplateItem *vitem =
206  new CustomTemplateItem( *it, t.content(),
207  shortcut,
208  static_cast<Type>( t.type() ), t.to(), t.cC() );
209  mItemList.insert( *it, vitem );
210  TQListViewItem *item = new TQListViewItem( mList, typeStr, *it, t.content() );
211  switch ( t.type() ) {
212  case TReply:
213  item->setPixmap( 0, mReplyPix );
214  break;
215  case TReplyAll:
216  item->setPixmap( 0, mReplyAllPix );
217  break;
218  case TForward:
219  item->setPixmap( 0, mForwardPix );
220  break;
221  default:
222  item->setPixmap( 0, TQPixmap() );
223  item->setText( 0, indexToType( t.type() ) );
224  break;
225  };
226  }
227 }
228 
229 void CustomTemplates::save()
230 {
231  // Before saving the new templates, delete the old ones. That needs to be done before
232  // saving, since otherwise a new template with the new name wouldn't get saved.
233  for ( TQStringList::const_iterator it = mItemsToDelete.constBegin();
234  it != mItemsToDelete.constEnd(); ++it ) {
235  CTemplates t( (*it) );
236  const TQString configGroup = t.currentGroup();
237  kmkernel->config()->deleteGroup( configGroup );
238  }
239 
240  if ( mCurrentItem ) {
241  CustomTemplateItem *vitem = mItemList[ mCurrentItem->text( 1 ) ];
242  if ( vitem ) {
243  vitem->mContent = mEdit->text();
244  vitem->mShortcut = mKeyButton->shortcut();
245  vitem->mTo = mToEdit->text();
246  vitem->mCC = mCCEdit->text();
247  }
248  }
249  TQStringList list;
250  TQListViewItemIterator lit( mList );
251  while ( lit.current() ) {
252  list.append( (*lit)->text( 1 ) );
253  ++lit;
254  }
255  for ( TQDictIterator<CustomTemplateItem> it( mItemList ); it.current() ; ++it ) {
256  // list.append( (*it)->mName );
257  CTemplates t( (*it)->mName );
258  TQString &content = (*it)->mContent;
259  if ( content.stripWhiteSpace().isEmpty() ) {
260  content = "%BLANK";
261  }
262  t.setContent( content );
263  t.setShortcut( (*it)->mShortcut.toString() );
264  t.setType( (*it)->mType );
265  t.setTo( (*it)->mTo );
266  t.setCC( (*it)->mCC );
267  t.writeConfig();
268  }
269  GlobalSettings::self()->setCustomTemplates( list );
270  GlobalSettings::self()->writeConfig();
271 
272  // update kmail menus related to custom templates
273  if ( kmkernel->getKMMainWidget() )
274  kmkernel->getKMMainWidget()->updateCustomTemplateMenus();
275 }
276 
277 void CustomTemplates::slotInsertCommand( TQString cmd, int adjustCursor )
278 {
279  int para, index;
280  mEdit->getCursorPosition( &para, &index );
281  mEdit->insertAt( cmd, para, index );
282 
283  index += adjustCursor;
284 
285  mEdit->setCursorPosition( para, index + cmd.length() );
286 }
287 
288 void CustomTemplates::slotAddClicked()
289 {
290  TQString str = mName->text();
291  if ( !str.isEmpty() ) {
292  CustomTemplateItem *vitem = mItemList[ str ];
293  if ( !vitem ) {
294  vitem = new CustomTemplateItem( str, "", TDEShortcut::null(), TUniversal,
295  TQString(), TQString() );
296  mItemList.insert( str, vitem );
297  TQListViewItem *item =
298  new TQListViewItem( mList, indexToType( TUniversal ), str, "" );
299  mList->setSelected( item, true );
300  mKeyButton->setEnabled( false );
301  if ( !mBlockChangeSignal )
302  emit changed();
303  }
304  }
305 }
306 
307 void CustomTemplates::slotRemoveClicked()
308 {
309  if ( mCurrentItem ) {
310  const TQString templateName = mCurrentItem->text( 1 );
311  mItemsToDelete.append( templateName );
312  CustomTemplateItem *vitem = mItemList.take( templateName );
313  delete vitem;
314  delete mCurrentItem;
315  mCurrentItem = 0;
316  if ( !mBlockChangeSignal )
317  emit changed();
318  }
319 }
320 
321 void CustomTemplates::slotListSelectionChanged()
322 {
323  if ( mCurrentItem ) {
324  CustomTemplateItem *vitem = mItemList[ mCurrentItem->text( 1 ) ];
325  if ( vitem ) {
326  vitem->mContent = mEdit->text();
327  vitem->mShortcut = mKeyButton->shortcut();
328  }
329  }
330  TQListViewItem *item = mList->selectedItem();
331  if ( item ) {
332  mEditFrame->setEnabled( true );
333  mCurrentItem = item;
334  CustomTemplateItem *vitem = mItemList[ mCurrentItem->text( 1 ) ];
335  if ( vitem ) {
336 
337  mBlockChangeSignal = true;
338  mEdit->setText( vitem->mContent );
339  mKeyButton->setShortcut( vitem->mShortcut, false );
340  mType->setCurrentItem( vitem->mType );
341  mToEdit->setText( vitem->mTo );
342  mCCEdit->setText( vitem->mCC );
343  mBlockChangeSignal = false;
344 
345  if ( vitem->mType == TUniversal )
346  {
347  mKeyButton->setEnabled( false );
348  } else {
349  mKeyButton->setEnabled( true );
350  }
351  setRecipientsEditsEnabled( vitem->mType == TForward ||
352  vitem->mType == TUniversal );
353  }
354  } else {
355  mEditFrame->setEnabled( false );
356  mCurrentItem = 0;
357  mEdit->clear();
358  mToEdit->clear();
359  mCCEdit->clear();
360  mKeyButton->setShortcut( TDEShortcut::null(), false );
361  mType->setCurrentItem( 0 );
362  }
363 }
364 
365 void CustomTemplates::slotTypeActivated( int index )
366 {
367  if ( mCurrentItem ) {
368  // mCurrentItem->setText( 0, indexToType( index ) );
369  CustomTemplateItem *vitem = mItemList[ mCurrentItem->text( 1 ) ];
370  if ( !vitem ) {
371  return;
372  }
373  vitem->mType = static_cast<Type>(index);
374  switch ( vitem->mType ) {
375  case TReply:
376  mCurrentItem->setPixmap( 0, mReplyPix );
377  break;
378  case TReplyAll:
379  mCurrentItem->setPixmap( 0, mReplyAllPix );
380  break;
381  case TForward:
382  mCurrentItem->setPixmap( 0, mForwardPix );
383  break;
384  default:
385  mCurrentItem->setPixmap( 0, TQPixmap() );
386  break;
387  };
388  if ( index == TUniversal )
389  {
390  mKeyButton->setEnabled( false );
391  } else {
392  mKeyButton->setEnabled( true );
393  }
394 
395  setRecipientsEditsEnabled( vitem->mType == TForward ||
396  vitem->mType == TUniversal );
397  if ( !mBlockChangeSignal )
398  emit changed();
399  }
400  else
401  setRecipientsEditsEnabled( false );
402 }
403 
404 void CustomTemplates::slotShortcutCaptured( const TDEShortcut &shortcut )
405 {
406  TDEShortcut sc( shortcut );
407  if ( sc == mKeyButton->shortcut() )
408  return;
409  if ( sc.isNull() || sc.toString().isEmpty() )
410  sc.clear();
411  bool assign = true;
412  bool customused = false;
413  // check if shortcut is already used for custom templates
414  TQDictIterator<CustomTemplateItem> it(mItemList);
415  for ( ; it.current() ; ++it ) {
416  if ( !mCurrentItem || (*it)->mName != mCurrentItem->text( 1 ) )
417  {
418  if ( (*it)->mShortcut == sc )
419  {
420  TQString title( I18N_NOOP("Key Conflict") );
421  TQString msg( I18N_NOOP("The selected shortcut is already used "
422  "for another custom template, "
423  "would you still like to continue with the assignment?" ) );
424  assign = ( KMessageBox::warningYesNo( this, msg, title )
425  == KMessageBox::Yes );
426  if ( assign )
427  {
428  (*it)->mShortcut = TDEShortcut::null();
429  }
430  customused = true;
431  }
432  }
433  }
434  // check if shortcut is used somewhere else
435  if ( !customused && !sc.isNull() &&
436  !( kmkernel->getKMMainWidget()->shortcutIsValid( sc ) ) ) {
437  TQString title( I18N_NOOP("Key Conflict") );
438  TQString msg( I18N_NOOP("The selected shortcut is already used, "
439  "would you still like to continue with the assignment?" ) );
440  assign = ( KMessageBox::warningYesNo( this, msg, title )
441  == KMessageBox::Yes );
442  }
443  if ( assign ) {
444  mKeyButton->setShortcut( sc, false );
445  if ( !mBlockChangeSignal )
446  emit changed();
447  }
448 }
449 
450 #include "customtemplates.moc"