knotes

knoteedit.cpp

00001 /*******************************************************************
00002  KNotes -- Notes for the KDE project
00003 
00004  Copyright (c) 1997-2005, The KNotes Developers
00005 
00006  This program is free software; you can redistribute it and/or
00007  modify it under the terms of the GNU General Public License
00008  as published by the Free Software Foundation; either version 2
00009  of the License, or (at your option) any later version.
00010 
00011  This program is distributed in the hope that it will be useful,
00012  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  GNU General Public License for more details.
00015 
00016  You should have received a copy of the GNU General Public License
00017  along with this program; if not, write to the Free Software
00018  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019 *******************************************************************/
00020 
00021 #include <qdragobject.h>
00022 #include <qfont.h>
00023 
00024 #include <kdebug.h>
00025 #include <klocale.h>
00026 #include <kaction.h>
00027 #include <kurldrag.h>
00028 #include <kstdaction.h>
00029 #include <kcolordialog.h>
00030 #include <qpopupmenu.h>
00031 #include <kiconloader.h>
00032 #include "knoteedit.h"
00033 
00034 static const short SEP = 5;
00035 static const short ICON_SIZE = 10;
00036 
00037 
00038 KNoteEdit::KNoteEdit( KActionCollection *actions, QWidget *parent, const char *name )
00039     : KTextEdit( parent, name )
00040 {
00041     setAcceptDrops( true );
00042     setWordWrap( WidgetWidth );
00043     setWrapPolicy( AtWhiteSpace );
00044     setLinkUnderline( true );
00045     setCheckSpellingEnabled(false);
00046     // create the actions for the RMB menu
00047     undo = KStdAction::undo( this, SLOT(undo()), actions );
00048     redo = KStdAction::redo( this, SLOT(redo()), actions );
00049     undo->setEnabled( isUndoAvailable() );
00050     redo->setEnabled( isRedoAvailable() );
00051 
00052     m_cut = KStdAction::cut( this, SLOT(cut()), actions );
00053     m_copy = KStdAction::copy( this, SLOT(copy()), actions );
00054     m_paste = KStdAction::paste( this, SLOT(paste()), actions );
00055 
00056     m_cut->setEnabled( false );
00057     m_copy->setEnabled( false );
00058     m_paste->setEnabled( true );
00059 
00060     connect( this, SIGNAL(undoAvailable(bool)), this, SLOT(setEnabledUndo(bool)) );
00061     connect( this, SIGNAL(redoAvailable(bool)), this, SLOT(setEnabledRedo(bool)) );
00062 
00063     connect( this, SIGNAL(copyAvailable(bool)), this, SLOT( slotCutEnabled( bool ) ) );
00064     connect( this, SIGNAL(copyAvailable(bool)), m_copy, SLOT(setEnabled(bool)) );
00065 
00066     new KAction( KStdGuiItem::clear(), 0, this, SLOT(clear()), actions, "edit_clear" );
00067     KStdAction::selectAll( this, SLOT(selectAll()), actions );
00068 
00069     // create the actions modifying the text format
00070     m_textBold = new KToggleAction( i18n("Bold"), "text_bold", CTRL + Key_B, 0, 0,
00071                                     actions, "format_bold" );
00072     m_textItalic = new KToggleAction( i18n("Italic"), "text_italic", CTRL + Key_I, 0, 0,
00073                                       actions, "format_italic" );
00074     m_textUnderline = new KToggleAction( i18n("Underline"), "text_under", CTRL + Key_U, 0, 0,
00075                                          actions, "format_underline" );
00076     m_textStrikeOut = new KToggleAction( i18n("Strike Out"), "text_strike", CTRL + Key_S, 0, 0,
00077                                          actions, "format_strikeout" );
00078 
00079     connect( m_textBold, SIGNAL(toggled(bool)), SLOT(setBold(bool)) );
00080     connect( m_textItalic, SIGNAL(toggled(bool)), SLOT(setItalic(bool)) );
00081     connect( m_textUnderline, SIGNAL(toggled(bool)), SLOT(setUnderline(bool)) );
00082     connect( m_textStrikeOut, SIGNAL(toggled(bool)), SLOT(textStrikeOut(bool)) );
00083 
00084     m_textAlignLeft = new KToggleAction( i18n("Align Left"), "text_left", ALT + Key_L,
00085                                  this, SLOT(textAlignLeft()),
00086                                  actions, "format_alignleft" );
00087     m_textAlignLeft->setChecked( true ); // just a dummy, will be updated later
00088     m_textAlignCenter = new KToggleAction( i18n("Align Center"), "text_center", ALT + Key_C,
00089                                  this, SLOT(textAlignCenter()),
00090                                  actions, "format_aligncenter" );
00091     m_textAlignRight = new KToggleAction( i18n("Align Right"), "text_right", ALT + Key_R,
00092                                  this, SLOT(textAlignRight()),
00093                                  actions, "format_alignright" );
00094     m_textAlignBlock = new KToggleAction( i18n("Align Block"), "text_block", ALT + Key_B,
00095                                  this, SLOT(textAlignBlock()),
00096                                  actions, "format_alignblock" );
00097 
00098     m_textAlignLeft->setExclusiveGroup( "align" );
00099     m_textAlignCenter->setExclusiveGroup( "align" );
00100     m_textAlignRight->setExclusiveGroup( "align" );
00101     m_textAlignBlock->setExclusiveGroup( "align" );
00102 
00103     m_textList = new KToggleAction( i18n("List"), "enum_list", 0,
00104                                     this, SLOT(textList()),
00105                                     actions, "format_list" );
00106 
00107     m_textList->setExclusiveGroup( "style" );
00108 
00109     m_textSuper = new KToggleAction( i18n("Superscript"), "text_super", 0,
00110                                      this, SLOT(textSuperScript()),
00111                                      actions, "format_super" );
00112     m_textSub = new KToggleAction( i18n("Subscript"), "text_sub", 0,
00113                                    this, SLOT(textSubScript()),
00114                                    actions, "format_sub" );
00115 
00116     m_textSuper->setExclusiveGroup( "valign" );
00117     m_textSub->setExclusiveGroup( "valign" );
00118 
00119 // There is no easy possibility to implement text indenting with QTextEdit
00120 //
00121 //     m_textIncreaseIndent = new KAction( i18n("Increase Indent"), "format_increaseindent", 0,
00122 //                                         this, SLOT(textIncreaseIndent()),
00123 //                                         actions, "format_increaseindent" );
00124 //
00125 //     m_textDecreaseIndent = new KAction( i18n("Decrease Indent"), "format_decreaseindent", 0,
00126 //                                         this, SLOT(textDecreaseIndent()),
00127 //                                         actions, "format_decreaseindent" );
00128 
00129     QPixmap pix( ICON_SIZE, ICON_SIZE );
00130     pix.fill( black );     // just a dummy, gets updated before widget is shown
00131     m_textColor = new KAction( i18n("Text Color..."), pix, 0, this,
00132                                   SLOT(textColor()), actions, "format_color" );
00133 
00134     m_textFont = new KFontAction( i18n("Text Font"), "text", KKey(),
00135                                   actions, "format_font" );
00136     connect( m_textFont, SIGNAL(activated( const QString & )),
00137              this, SLOT(setFamily( const QString & )) );
00138 
00139     m_textSize = new KFontSizeAction( i18n("Text Size"), KKey(),
00140                                       actions, "format_size" );
00141     connect( m_textSize, SIGNAL(fontSizeChanged( int )),
00142              this, SLOT(setPointSize( int )) );
00143 
00144     // QTextEdit connections
00145     connect( this, SIGNAL(returnPressed()), SLOT(slotReturnPressed()) );
00146     connect( this, SIGNAL(currentFontChanged( const QFont & )),
00147              this, SLOT(fontChanged( const QFont & )) );
00148     connect( this, SIGNAL(currentColorChanged( const QColor & )),
00149              this, SLOT(colorChanged( const QColor & )) );
00150     connect( this, SIGNAL(currentAlignmentChanged( int )),
00151              this, SLOT(alignmentChanged( int )) );
00152     connect( this, SIGNAL(currentVerticalAlignmentChanged( VerticalAlignment )),
00153              this, SLOT(verticalAlignmentChanged( VerticalAlignment )) );
00154 }
00155 
00156 KNoteEdit::~KNoteEdit()
00157 {
00158 }
00159 
00160 void KNoteEdit::setEnabledRedo( bool b )
00161 {
00162     redo->setEnabled( b && !isReadOnly() );
00163 }
00164 
00165 void KNoteEdit::setEnabledUndo( bool b )
00166 {
00167     undo->setEnabled( b && !isReadOnly() );
00168 }
00169 
00170 void KNoteEdit::slotCutEnabled( bool b )
00171 {
00172     m_cut->setEnabled( b && !isReadOnly() );
00173 }
00174 
00175 void KNoteEdit::setText( const QString& text )
00176 {
00177     // to update the font and font size combo box - QTextEdit stopped
00178     // emitting the currentFontChanged signal with the new optimizations
00179     KTextEdit::setText( text );
00180     fontChanged( currentFont() );
00181 }
00182 
00183 void KNoteEdit::setTextFont( const QFont& font )
00184 {
00185     if ( textFormat() == PlainText )
00186         setFont( font );
00187     else
00188         setCurrentFont( font );
00189 }
00190 
00191 void KNoteEdit::setTextColor( const QColor& color )
00192 {
00193     setColor( color );
00194     colorChanged( color );
00195 }
00196 
00197 void KNoteEdit::setTabStop( int tabs )
00198 {
00199     QFontMetrics fm( font() );
00200     setTabStopWidth( fm.width( 'x' ) * tabs );
00201 }
00202 
00203 void KNoteEdit::setAutoIndentMode( bool newmode )
00204 {
00205     m_autoIndentMode = newmode;
00206 }
00207 
00208 
00211 void KNoteEdit::setTextFormat( TextFormat f )
00212 {
00213     if ( f == textFormat() )
00214         return;
00215 
00216     if ( f == RichText )
00217     {
00218         QString t = text();
00219         KTextEdit::setTextFormat( f );
00220 
00221         // if the note contains html/xml source try to display it, otherwise
00222         // get the modified text again and set it to preserve newlines
00223         if ( QStyleSheet::mightBeRichText( t ) )
00224             setText( t );
00225         else
00226             setText( text() );
00227 
00228         enableRichTextActions();
00229     }
00230     else
00231     {
00232         KTextEdit::setTextFormat( f );
00233         QString t = text();
00234         setText( t );
00235 
00236         disableRichTextActions();
00237     }
00238 }
00239 
00240 void KNoteEdit::textStrikeOut( bool s )
00241 {
00242     // QTextEdit does not support stroke out text (no saving,
00243     // no changing of more than one selected character)
00244     QFont font;
00245 
00246     if ( !hasSelectedText() )
00247     {
00248         font = currentFont();
00249         font.setStrikeOut( s );
00250         setCurrentFont( font );
00251     }
00252     else
00253     {
00254         int pFrom, pTo, iFrom, iTo, iF, iT;
00255         int cp, ci;
00256 
00257         getSelection( &pFrom, &iFrom, &pTo, &iTo );
00258         getCursorPosition( &cp, &ci );
00259 
00260         for ( int p = pFrom; p <= pTo; p++ )
00261         {
00262             iF = 0;
00263             iT = paragraphLength( p );
00264 
00265             if ( p == pFrom )
00266                 iF = iFrom;
00267 
00268             if ( p == pTo )
00269                 iT = iTo;
00270 
00271             for ( int i = iF; i < iT; i++ )
00272             {
00273                 setCursorPosition( p, i + 1 );
00274                 setSelection( p, i, p, i + 1 );
00275                 font = currentFont();
00276                 font.setStrikeOut( s );
00277                 setCurrentFont( font );
00278             }
00279         }
00280 
00281         setSelection( pFrom, iFrom, pTo, iTo );
00282         setCursorPosition( cp, ci );
00283     }
00284 }
00285 
00286 void KNoteEdit::textColor()
00287 {
00288     QColor c = color();
00289     int ret = KColorDialog::getColor( c, this );
00290     if ( ret == QDialog::Accepted )
00291         setTextColor( c );
00292 }
00293 
00294 void KNoteEdit::textAlignLeft()
00295 {
00296     setAlignment( AlignLeft );
00297     m_textAlignLeft->setChecked( true );
00298 }
00299 
00300 void KNoteEdit::textAlignCenter()
00301 {
00302     setAlignment( AlignCenter );
00303     m_textAlignCenter->setChecked( true );
00304 }
00305 
00306 void KNoteEdit::textAlignRight()
00307 {
00308     setAlignment( AlignRight );
00309     m_textAlignRight->setChecked( true );
00310 }
00311 
00312 void KNoteEdit::textAlignBlock()
00313 {
00314     setAlignment( AlignJustify );
00315     m_textAlignBlock->setChecked( true );
00316 }
00317 
00318 void KNoteEdit::textList()
00319 {
00320     if ( m_textList->isChecked() )
00321         setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListDisc );
00322     else
00323         setParagType( QStyleSheetItem::DisplayBlock, QStyleSheetItem::ListDisc );
00324 }
00325 
00326 void KNoteEdit::textSuperScript()
00327 {
00328     if ( m_textSuper->isChecked() )
00329         setVerticalAlignment( AlignSuperScript );
00330     else
00331         setVerticalAlignment( AlignNormal );
00332 }
00333 
00334 void KNoteEdit::textSubScript()
00335 {
00336     if ( m_textSub->isChecked() )
00337         setVerticalAlignment( AlignSubScript );
00338     else
00339         setVerticalAlignment( AlignNormal );
00340 }
00341 
00342 //void KNoteEdit::textIncreaseIndent()
00343 //{
00344 //}
00345 
00346 //void KNoteEdit::textDecreaseIndent()
00347 //{
00348 //}
00349 
00350 
00353 void KNoteEdit::contentsDragEnterEvent( QDragEnterEvent *e )
00354 {
00355     if ( KURLDrag::canDecode( e ) )
00356         e->accept();
00357     else
00358         KTextEdit::contentsDragEnterEvent( e );
00359 }
00360 
00361 void KNoteEdit::contentsDropEvent( QDropEvent *e )
00362 {
00363     KURL::List list;
00364 
00365     if ( KURLDrag::decode( e, list ) )
00366     {
00367     KURL::List::ConstIterator begin = list.constBegin();
00368     KURL::List::ConstIterator end = list.constEnd();
00369         for ( KURL::List::ConstIterator it = begin; it != end; ++it )
00370         {
00371             if ( it != begin )
00372                 insert( ", " );
00373 
00374             insert( (*it).prettyURL() );
00375         }
00376     }
00377     else
00378         KTextEdit::contentsDropEvent( e );
00379 }
00380 
00383 void KNoteEdit::slotReturnPressed()
00384 {
00385     if ( m_autoIndentMode )
00386         autoIndent();
00387 }
00388 
00389 void KNoteEdit::fontChanged( const QFont &f )
00390 {
00391     m_textFont->setFont( f.family() );
00392     m_textSize->setFontSize( f.pointSize() );
00393 
00394     m_textBold->setChecked( f.bold() );
00395     m_textItalic->setChecked( f.italic() );
00396     m_textUnderline->setChecked( f.underline() );
00397     m_textStrikeOut->setChecked( f.strikeOut() );
00398 }
00399 
00400 void KNoteEdit::colorChanged( const QColor &c )
00401 {
00402     QPixmap pix( ICON_SIZE, ICON_SIZE );
00403     pix.fill( c );
00404     m_textColor->setIconSet( pix );
00405 }
00406 
00407 void KNoteEdit::alignmentChanged( int a )
00408 {
00409     // TODO: AlignAuto
00410     if ( ( a == AlignAuto ) || ( a & AlignLeft ) )
00411         m_textAlignLeft->setChecked( true );
00412     else if ( ( a & AlignHCenter ) )
00413         m_textAlignCenter->setChecked( true );
00414     else if ( ( a & AlignRight ) )
00415         m_textAlignRight->setChecked( true );
00416     else if ( ( a & AlignJustify ) )
00417         m_textAlignBlock->setChecked( true );
00418 }
00419 
00420 void KNoteEdit::verticalAlignmentChanged( VerticalAlignment a )
00421 {
00422     if ( a == AlignNormal )
00423     {
00424         m_textSuper->setChecked( false );
00425         m_textSub->setChecked( false );
00426     }
00427     else if ( a == AlignSuperScript )
00428         m_textSuper->setChecked( true );
00429     else if ( a == AlignSubScript )
00430         m_textSub->setChecked( true );
00431 }
00432 
00433 
00436 void KNoteEdit::autoIndent()
00437 {
00438     int para, index;
00439     QString string;
00440     getCursorPosition( &para, &index );
00441     while ( para > 0 && string.stripWhiteSpace().isEmpty() )
00442         string = text( --para );
00443 
00444     if ( string.stripWhiteSpace().isEmpty() )
00445         return;
00446 
00447     // This routine returns the whitespace before the first non white space
00448     // character in string.
00449     // It is assumed that string contains at least one non whitespace character
00450     // ie \n \r \t \v \f and space
00451     QString indentString;
00452 
00453     int len = string.length();
00454     int i = 0;
00455     while ( i < len && string.at(i).isSpace() )
00456         indentString += string.at( i++ );
00457 
00458     if ( !indentString.isEmpty() )
00459         insert( indentString );
00460 }
00461 
00462 void KNoteEdit::emitLinkClicked( const QString &s )
00463 {
00464     kdDebug(5500) << k_funcinfo << s << endl;
00465 }
00466 
00467 void KNoteEdit::enableRichTextActions()
00468 {
00469     m_textColor->setEnabled( true );
00470     m_textFont->setEnabled( true );
00471     m_textSize->setEnabled( true );
00472 
00473     m_textBold->setEnabled( true );
00474     m_textItalic->setEnabled( true );
00475     m_textUnderline->setEnabled( true );
00476     m_textStrikeOut->setEnabled( true );
00477 
00478     m_textAlignLeft->setEnabled( true );
00479     m_textAlignCenter->setEnabled( true );
00480     m_textAlignRight->setEnabled( true );
00481     m_textAlignBlock->setEnabled( true );
00482 
00483     m_textList->setEnabled( true );
00484     m_textSuper->setEnabled( true );
00485     m_textSub->setEnabled( true );
00486 
00487 //    m_textIncreaseIndent->setEnabled( true );
00488 //    m_textDecreaseIndent->setEnabled( true );
00489 }
00490 
00491 void KNoteEdit::disableRichTextActions()
00492 {
00493     m_textColor->setEnabled( false );
00494     m_textFont->setEnabled( false );
00495     m_textSize->setEnabled( false );
00496 
00497     m_textBold->setEnabled( false );
00498     m_textItalic->setEnabled( false );
00499     m_textUnderline->setEnabled( false );
00500     m_textStrikeOut->setEnabled( false );
00501 
00502     m_textAlignLeft->setEnabled( false );
00503     m_textAlignCenter->setEnabled( false );
00504     m_textAlignRight->setEnabled( false );
00505     m_textAlignBlock->setEnabled( false );
00506 
00507     m_textList->setEnabled( false );
00508     m_textSuper->setEnabled( false );
00509     m_textSub->setEnabled( false );
00510 
00511 //    m_textIncreaseIndent->setEnabled( false );
00512 //    m_textDecreaseIndent->setEnabled( false );
00513 }
00514 
00515 void KNoteEdit::slotAllowTab()
00516 {
00517     setTabChangesFocus(!tabChangesFocus());
00518 }
00519 
00520 QPopupMenu *KNoteEdit::createPopupMenu( const QPoint &pos )
00521 {
00522     enum { IdUndo, IdRedo, IdSep1, IdCut, IdCopy, IdPaste, IdClear, IdSep2, IdSelectAll };
00523 
00524     QPopupMenu *menu = QTextEdit::createPopupMenu( pos );
00525 
00526     if ( isReadOnly() )
00527       menu->changeItem( menu->idAt(0), SmallIconSet("editcopy"), menu->text( menu->idAt(0) ) );
00528     else {
00529       int id = menu->idAt(0);
00530       menu->changeItem( id - IdUndo, SmallIconSet("undo"), menu->text( id - IdUndo) );
00531       menu->changeItem( id - IdRedo, SmallIconSet("redo"), menu->text( id - IdRedo) );
00532       menu->changeItem( id - IdCut, SmallIconSet("editcut"), menu->text( id - IdCut) );
00533       menu->changeItem( id - IdCopy, SmallIconSet("editcopy"), menu->text( id - IdCopy) );
00534       menu->changeItem( id - IdPaste, SmallIconSet("editpaste"), menu->text( id - IdPaste) );
00535       menu->changeItem( id - IdClear, SmallIconSet("editclear"), menu->text( id - IdClear) );
00536 
00537         menu->insertSeparator();
00538         id = menu->insertItem( SmallIconSet( "spellcheck" ), i18n( "Check Spelling..." ),
00539                                    this, SLOT( checkSpelling() ) );
00540 
00541         if( text().isEmpty() )
00542             menu->setItemEnabled( id, false );
00543 
00544     menu->insertSeparator();
00545     id=menu->insertItem(i18n("Allow Tabulations"),this,SLOT(slotAllowTab()));
00546     menu->setItemChecked(id, !tabChangesFocus());
00547     }
00548 
00549     return menu;
00550 }
00551 
00552 #include "knoteedit.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys