knotes

knoteedit.cpp
1 /*******************************************************************
2  KNotes -- Notes for the KDE project
3 
4  Copyright (c) 1997-2005, The KNotes Developers
5 
6  This program is free software; you can redistribute it and/or
7  modify it under the terms of the GNU General Public License
8  as published by the Free Software Foundation; either version 2
9  of the License, or (at your option) any later version.
10 
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, write to the Free Software
18  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *******************************************************************/
20 
21 #include <tqdragobject.h>
22 #include <tqfont.h>
23 
24 #include <kdebug.h>
25 #include <tdelocale.h>
26 #include <tdeaction.h>
27 #include <kurldrag.h>
28 #include <kstdaction.h>
29 #include <kcolordialog.h>
30 #include <tqpopupmenu.h>
31 #include <kiconloader.h>
32 #include "knoteedit.h"
33 #include "knote.h"
34 
35 static const short SEP = 5;
36 static const short ICON_SIZE = 10;
37 
38 
39 KNoteEdit::KNoteEdit( TDEActionCollection *actions, TQWidget *parent, const char *name )
40  : KTextEdit( parent, name ), m_note( 0 )
41 {
42  setAcceptDrops( true );
43  setWordWrap( WidgetWidth );
44  setWrapPolicy( AtWhiteSpace );
45  setLinkUnderline( true );
46  setCheckSpellingEnabled(false);
47  // create the actions for the RMB menu
48  undo = KStdAction::undo( this, TQ_SLOT(undo()), actions );
49  redo = KStdAction::redo( this, TQ_SLOT(redo()), actions );
50  undo->setEnabled( isUndoAvailable() );
51  redo->setEnabled( isRedoAvailable() );
52 
53  m_cut = KStdAction::cut( this, TQ_SLOT(cut()), actions );
54  m_copy = KStdAction::copy( this, TQ_SLOT(copy()), actions );
55  m_paste = KStdAction::paste( this, TQ_SLOT(paste()), actions );
56 
57  m_cut->setEnabled( false );
58  m_copy->setEnabled( false );
59  m_paste->setEnabled( true );
60 
61  connect( this, TQ_SIGNAL(undoAvailable(bool)), this, TQ_SLOT(setEnabledUndo(bool)) );
62  connect( this, TQ_SIGNAL(redoAvailable(bool)), this, TQ_SLOT(setEnabledRedo(bool)) );
63 
64  connect( this, TQ_SIGNAL(copyAvailable(bool)), this, TQ_SLOT( slotCutEnabled( bool ) ) );
65  connect( this, TQ_SIGNAL(copyAvailable(bool)), m_copy, TQ_SLOT(setEnabled(bool)) );
66 
67  new TDEAction( KStdGuiItem::clear(), 0, this, TQ_SLOT(clear()), actions, "edit_clear" );
68  KStdAction::selectAll( this, TQ_SLOT(selectAll()), actions );
69 
70  // create the actions modifying the text format
71  m_textBold = new TDEToggleAction( i18n("Bold"), "format-text-bold", CTRL + Key_B, 0, 0,
72  actions, "format_bold" );
73  m_textItalic = new TDEToggleAction( i18n("Italic"), "format-text-italic", CTRL + Key_I, 0, 0,
74  actions, "format_italic" );
75  m_textUnderline = new TDEToggleAction( i18n("Underline"), "format-text-underline", CTRL + Key_U, 0, 0,
76  actions, "format_underline" );
77  m_textStrikeOut = new TDEToggleAction( i18n("Strike Out"), "format-text-strikethrough", CTRL + Key_S, 0, 0,
78  actions, "format_strikeout" );
79 
80  connect( m_textBold, TQ_SIGNAL(toggled(bool)), TQ_SLOT(setBold(bool)) );
81  connect( m_textItalic, TQ_SIGNAL(toggled(bool)), TQ_SLOT(setItalic(bool)) );
82  connect( m_textUnderline, TQ_SIGNAL(toggled(bool)), TQ_SLOT(setUnderline(bool)) );
83  connect( m_textStrikeOut, TQ_SIGNAL(toggled(bool)), TQ_SLOT(textStrikeOut(bool)) );
84 
85  m_textAlignLeft = new TDEToggleAction( i18n("Align Left"), "format-text-direction-ltr", ALT + Key_L,
86  this, TQ_SLOT(textAlignLeft()),
87  actions, "format_alignleft" );
88  m_textAlignLeft->setChecked( true ); // just a dummy, will be updated later
89  m_textAlignCenter = new TDEToggleAction( i18n("Align Center"), "text_center", ALT + Key_C,
90  this, TQ_SLOT(textAlignCenter()),
91  actions, "format_aligncenter" );
92  m_textAlignRight = new TDEToggleAction( i18n("Align Right"), "format-text-direction-rtl", ALT + Key_R,
93  this, TQ_SLOT(textAlignRight()),
94  actions, "format_alignright" );
95  m_textAlignBlock = new TDEToggleAction( i18n("Align Block"), "text_block", ALT + Key_B,
96  this, TQ_SLOT(textAlignBlock()),
97  actions, "format_alignblock" );
98 
99  m_textAlignLeft->setExclusiveGroup( "align" );
100  m_textAlignCenter->setExclusiveGroup( "align" );
101  m_textAlignRight->setExclusiveGroup( "align" );
102  m_textAlignBlock->setExclusiveGroup( "align" );
103 
104  m_textList = new TDEToggleAction( i18n("List"), "enum_list", 0,
105  this, TQ_SLOT(textList()),
106  actions, "format_list" );
107 
108  m_textList->setExclusiveGroup( "style" );
109 
110  m_textSuper = new TDEToggleAction( i18n("Superscript"), "text_super", 0,
111  this, TQ_SLOT(textSuperScript()),
112  actions, "format_super" );
113  m_textSub = new TDEToggleAction( i18n("Subscript"), "text_sub", 0,
114  this, TQ_SLOT(textSubScript()),
115  actions, "format_sub" );
116 
117  m_textSuper->setExclusiveGroup( "valign" );
118  m_textSub->setExclusiveGroup( "valign" );
119 
120 // There is no easy possibility to implement text indenting with TQTextEdit
121 //
122 // m_textIncreaseIndent = new TDEAction( i18n("Increase Indent"), "format_increaseindent", 0,
123 // this, TQ_SLOT(textIncreaseIndent()),
124 // actions, "format_increaseindent" );
125 //
126 // m_textDecreaseIndent = new TDEAction( i18n("Decrease Indent"), "format_decreaseindent", 0,
127 // this, TQ_SLOT(textDecreaseIndent()),
128 // actions, "format_decreaseindent" );
129 
130  TQPixmap pix( ICON_SIZE, ICON_SIZE );
131  pix.fill( black ); // just a dummy, gets updated before widget is shown
132  m_textColor = new TDEAction( i18n("Text Color..."), pix, 0, this,
133  TQ_SLOT(textColor()), actions, "format_color" );
134 
135  m_textFont = new TDEFontAction( i18n("Text Font"), "text", KKey(),
136  actions, "format_font" );
137  connect( m_textFont, TQ_SIGNAL(activated( const TQString & )),
138  this, TQ_SLOT(setFamily( const TQString & )) );
139 
140  m_textSize = new TDEFontSizeAction( i18n("Text Size"), KKey(),
141  actions, "format_size" );
142  connect( m_textSize, TQ_SIGNAL(fontSizeChanged( int )),
143  this, TQ_SLOT(setPointSize( int )) );
144 
145  // TQTextEdit connections
146  connect( this, TQ_SIGNAL(returnPressed()), TQ_SLOT(slotReturnPressed()) );
147  connect( this, TQ_SIGNAL(currentFontChanged( const TQFont & )),
148  this, TQ_SLOT(fontChanged( const TQFont & )) );
149  connect( this, TQ_SIGNAL(currentColorChanged( const TQColor & )),
150  this, TQ_SLOT(colorChanged( const TQColor & )) );
151  connect( this, TQ_SIGNAL(currentAlignmentChanged( int )),
152  this, TQ_SLOT(alignmentChanged( int )) );
153  connect( this, TQ_SIGNAL(currentVerticalAlignmentChanged( VerticalAlignment )),
154  this, TQ_SLOT(verticalAlignmentChanged( VerticalAlignment )) );
155 }
156 
157 KNoteEdit::~KNoteEdit()
158 {
159 }
160 
161 void KNoteEdit::setEnabledRedo( bool b )
162 {
163  redo->setEnabled( b && !isReadOnly() );
164 }
165 
166 void KNoteEdit::setEnabledUndo( bool b )
167 {
168  undo->setEnabled( b && !isReadOnly() );
169 }
170 
171 void KNoteEdit::slotCutEnabled( bool b )
172 {
173  m_cut->setEnabled( b && !isReadOnly() );
174 }
175 
176 void KNoteEdit::setText( const TQString& text )
177 {
178  // to update the font and font size combo box - TQTextEdit stopped
179  // emitting the currentFontChanged signal with the new optimizations
180  KTextEdit::setText( text );
181  fontChanged( currentFont() );
182 }
183 
184 void KNoteEdit::setTextFont( const TQFont& font )
185 {
186  if ( textFormat() == PlainText )
187  setFont( font );
188  else
189  setCurrentFont( font );
190 }
191 
192 void KNoteEdit::setTextColor( const TQColor& color )
193 {
194  setColor( color );
195  colorChanged( color );
196 }
197 
198 void KNoteEdit::setTabStop( int tabs )
199 {
200  TQFontMetrics fm( font() );
201  setTabStopWidth( fm.width( 'x' ) * tabs );
202 }
203 
204 void KNoteEdit::setAutoIndentMode( bool newmode )
205 {
206  m_autoIndentMode = newmode;
207 }
208 
209 
212 void KNoteEdit::setTextFormat( TextFormat f )
213 {
214  if ( f == textFormat() )
215  return;
216 
217  if ( f == RichText )
218  {
219  TQString t = text();
220  KTextEdit::setTextFormat( f );
221 
222  // if the note contains html/xml source try to display it, otherwise
223  // get the modified text again and set it to preserve newlines
224  if ( TQStyleSheet::mightBeRichText( t ) )
225  setText( t );
226  else
227  setText( text() );
228 
229  enableRichTextActions();
230  }
231  else
232  {
233  KTextEdit::setTextFormat( f );
234  TQString t = text();
235  setText( t );
236 
237  disableRichTextActions();
238  }
239 }
240 
241 void KNoteEdit::textStrikeOut( bool s )
242 {
243  // TQTextEdit does not support stroke out text (no saving,
244  // no changing of more than one selected character)
245  TQFont font;
246 
247  if ( !hasSelectedText() )
248  {
249  font = currentFont();
250  font.setStrikeOut( s );
251  setCurrentFont( font );
252  }
253  else
254  {
255  int pFrom, pTo, iFrom, iTo, iF, iT;
256  int cp, ci;
257 
258  getSelection( &pFrom, &iFrom, &pTo, &iTo );
259  getCursorPosition( &cp, &ci );
260 
261  for ( int p = pFrom; p <= pTo; p++ )
262  {
263  iF = 0;
264  iT = paragraphLength( p );
265 
266  if ( p == pFrom )
267  iF = iFrom;
268 
269  if ( p == pTo )
270  iT = iTo;
271 
272  for ( int i = iF; i < iT; i++ )
273  {
274  setCursorPosition( p, i + 1 );
275  setSelection( p, i, p, i + 1 );
276  font = currentFont();
277  font.setStrikeOut( s );
278  setCurrentFont( font );
279  }
280  }
281 
282  setSelection( pFrom, iFrom, pTo, iTo );
283  setCursorPosition( cp, ci );
284  }
285 }
286 
287 void KNoteEdit::textColor()
288 {
289  if ( m_note )
290  m_note->blockEmitDataChanged( true );
291  TQColor c = color();
292  int ret = KColorDialog::getColor( c, this );
293  if ( ret == TQDialog::Accepted )
294  setTextColor( c );
295  if ( m_note )
296  m_note->blockEmitDataChanged( false );
297 }
298 
299 void KNoteEdit::textAlignLeft()
300 {
301  setAlignment( AlignLeft );
302  m_textAlignLeft->setChecked( true );
303 }
304 
305 void KNoteEdit::textAlignCenter()
306 {
307  setAlignment( AlignCenter );
308  m_textAlignCenter->setChecked( true );
309 }
310 
311 void KNoteEdit::textAlignRight()
312 {
313  setAlignment( AlignRight );
314  m_textAlignRight->setChecked( true );
315 }
316 
317 void KNoteEdit::textAlignBlock()
318 {
319  setAlignment( AlignJustify );
320  m_textAlignBlock->setChecked( true );
321 }
322 
323 void KNoteEdit::textList()
324 {
325  if ( m_textList->isChecked() )
326  setParagType( TQStyleSheetItem::DisplayListItem, TQStyleSheetItem::ListDisc );
327  else
328  setParagType( TQStyleSheetItem::DisplayBlock, TQStyleSheetItem::ListDisc );
329 }
330 
331 void KNoteEdit::textSuperScript()
332 {
333  if ( m_textSuper->isChecked() )
334  setVerticalAlignment( AlignSuperScript );
335  else
336  setVerticalAlignment( AlignNormal );
337 }
338 
339 void KNoteEdit::textSubScript()
340 {
341  if ( m_textSub->isChecked() )
342  setVerticalAlignment( AlignSubScript );
343  else
344  setVerticalAlignment( AlignNormal );
345 }
346 
347 //void KNoteEdit::textIncreaseIndent()
348 //{
349 //}
350 
351 //void KNoteEdit::textDecreaseIndent()
352 //{
353 //}
354 
355 
358 void KNoteEdit::contentsDragEnterEvent( TQDragEnterEvent *e )
359 {
360  if ( KURLDrag::canDecode( e ) )
361  e->accept();
362  else
363  KTextEdit::contentsDragEnterEvent( e );
364 }
365 
366 void KNoteEdit::contentsDropEvent( TQDropEvent *e )
367 {
368  KURL::List list;
369 
370  if ( KURLDrag::decode( e, list ) )
371  {
372  KURL::List::ConstIterator begin = list.constBegin();
373  KURL::List::ConstIterator end = list.constEnd();
374  for ( KURL::List::ConstIterator it = begin; it != end; ++it )
375  {
376  if ( it != begin )
377  insert( ", " );
378 
379  insert( (*it).prettyURL() );
380  }
381  }
382  else
383  KTextEdit::contentsDropEvent( e );
384 }
385 
388 void KNoteEdit::slotReturnPressed()
389 {
390  if ( m_autoIndentMode )
391  autoIndent();
392 }
393 
394 void KNoteEdit::fontChanged( const TQFont &f )
395 {
396  m_textFont->setFont( f.family() );
397  m_textSize->setFontSize( f.pointSize() );
398 
399  m_textBold->setChecked( f.bold() );
400  m_textItalic->setChecked( f.italic() );
401  m_textUnderline->setChecked( f.underline() );
402  m_textStrikeOut->setChecked( f.strikeOut() );
403 }
404 
405 void KNoteEdit::colorChanged( const TQColor &c )
406 {
407  TQPixmap pix( ICON_SIZE, ICON_SIZE );
408  pix.fill( c );
409  m_textColor->setIconSet( pix );
410 }
411 
412 void KNoteEdit::alignmentChanged( int a )
413 {
414  // TODO: AlignAuto
415  if ( ( a == AlignAuto ) || ( a & AlignLeft ) )
416  m_textAlignLeft->setChecked( true );
417  else if ( ( a & AlignHCenter ) )
418  m_textAlignCenter->setChecked( true );
419  else if ( ( a & AlignRight ) )
420  m_textAlignRight->setChecked( true );
421  else if ( ( a & AlignJustify ) )
422  m_textAlignBlock->setChecked( true );
423 }
424 
425 void KNoteEdit::verticalAlignmentChanged( VerticalAlignment a )
426 {
427  if ( a == AlignNormal )
428  {
429  m_textSuper->setChecked( false );
430  m_textSub->setChecked( false );
431  }
432  else if ( a == AlignSuperScript )
433  m_textSuper->setChecked( true );
434  else if ( a == AlignSubScript )
435  m_textSub->setChecked( true );
436 }
437 
438 
441 void KNoteEdit::autoIndent()
442 {
443  int para, index;
444  TQString string;
445  getCursorPosition( &para, &index );
446  while ( para > 0 && string.stripWhiteSpace().isEmpty() )
447  string = text( --para );
448 
449  if ( string.stripWhiteSpace().isEmpty() )
450  return;
451 
452  // This routine returns the whitespace before the first non white space
453  // character in string.
454  // It is assumed that string contains at least one non whitespace character
455  // ie \n \r \t \v \f and space
456  TQString indentString;
457 
458  int len = string.length();
459  int i = 0;
460  while ( i < len && string.at(i).isSpace() )
461  indentString += string.at( i++ );
462 
463  if ( !indentString.isEmpty() )
464  insert( indentString );
465 }
466 
467 void KNoteEdit::emitLinkClicked( const TQString &s )
468 {
469  kdDebug(5500) << k_funcinfo << s << endl;
470 }
471 
472 void KNoteEdit::enableRichTextActions()
473 {
474  m_textColor->setEnabled( true );
475  m_textFont->setEnabled( true );
476  m_textSize->setEnabled( true );
477 
478  m_textBold->setEnabled( true );
479  m_textItalic->setEnabled( true );
480  m_textUnderline->setEnabled( true );
481  m_textStrikeOut->setEnabled( true );
482 
483  m_textAlignLeft->setEnabled( true );
484  m_textAlignCenter->setEnabled( true );
485  m_textAlignRight->setEnabled( true );
486  m_textAlignBlock->setEnabled( true );
487 
488  m_textList->setEnabled( true );
489  m_textSuper->setEnabled( true );
490  m_textSub->setEnabled( true );
491 
492 // m_textIncreaseIndent->setEnabled( true );
493 // m_textDecreaseIndent->setEnabled( true );
494 }
495 
496 void KNoteEdit::disableRichTextActions()
497 {
498  m_textColor->setEnabled( false );
499  m_textFont->setEnabled( false );
500  m_textSize->setEnabled( false );
501 
502  m_textBold->setEnabled( false );
503  m_textItalic->setEnabled( false );
504  m_textUnderline->setEnabled( false );
505  m_textStrikeOut->setEnabled( false );
506 
507  m_textAlignLeft->setEnabled( false );
508  m_textAlignCenter->setEnabled( false );
509  m_textAlignRight->setEnabled( false );
510  m_textAlignBlock->setEnabled( false );
511 
512  m_textList->setEnabled( false );
513  m_textSuper->setEnabled( false );
514  m_textSub->setEnabled( false );
515 
516 // m_textIncreaseIndent->setEnabled( false );
517 // m_textDecreaseIndent->setEnabled( false );
518 }
519 
520 void KNoteEdit::slotAllowTab()
521 {
522  setTabChangesFocus(!tabChangesFocus());
523 }
524 
525 TQPopupMenu *KNoteEdit::createPopupMenu( const TQPoint &pos )
526 {
527  enum { IdUndo, IdRedo, IdSep1, IdCut, IdCopy, IdPaste, IdClear, IdSep2, IdSelectAll };
528 
529  TQPopupMenu *menu = TQTextEdit::createPopupMenu( pos );
530 
531  if ( isReadOnly() )
532  menu->changeItem( menu->idAt(0), SmallIconSet("edit-copy"), menu->text( menu->idAt(0) ) );
533  else {
534  int id = menu->idAt(0);
535  menu->changeItem( id - IdUndo, SmallIconSet("edit-undo"), menu->text( id - IdUndo) );
536  menu->changeItem( id - IdRedo, SmallIconSet("edit-redo"), menu->text( id - IdRedo) );
537  menu->changeItem( id - IdCut, SmallIconSet("edit-cut"), menu->text( id - IdCut) );
538  menu->changeItem( id - IdCopy, SmallIconSet("edit-copy"), menu->text( id - IdCopy) );
539  menu->changeItem( id - IdPaste, SmallIconSet("edit-paste"), menu->text( id - IdPaste) );
540  menu->changeItem( id - IdClear, SmallIconSet("edit-clear"), menu->text( id - IdClear) );
541 
542  menu->insertSeparator();
543  id = menu->insertItem( SmallIconSet( "tools-check-spelling" ), i18n( "Check Spelling..." ),
544  this, TQ_SLOT( checkSpelling() ) );
545 
546  if( text().isEmpty() )
547  menu->setItemEnabled( id, false );
548 
549  menu->insertSeparator();
550  id=menu->insertItem(i18n("Allow Tabulations"),this,TQ_SLOT(slotAllowTab()));
551  menu->setItemChecked(id, !tabChangesFocus());
552  }
553 
554  return menu;
555 }
556 
557 #include "knoteedit.moc"