htmlentrytextedit.cpp
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "htmlentrytextedit.h"
00021 #include "htmlentrytextedit.moc"
00022
00023 #include "stdaction.h"
00024
00025 #include "../blokkalappearancesettings.h"
00026
00027 #include <kmenu.h>
00028 #include <kaction.h>
00029
00030 #include <klocale.h>
00031 #include <kdebug.h>
00032 #include <kglobalsettings.h>
00033
00034 #include <QContextMenuEvent>
00035
00036
00037
00038
00039
00040 class Blokkal::Ui::HtmlTextHighlighter::Private
00041 {
00042 public:
00043 Private( const QTextEdit * edit ) :
00044 edit( edit ){}
00045
00046 const QTextEdit * const edit;
00047
00048 bool highlightSyntax;
00049 bool highlightSpelling;
00050
00051 QTextCharFormat normalFormat;
00052 QTextCharFormat tagFormat;
00053 QTextCharFormat stringFormat;
00054 QTextCharFormat valueFormat;
00055
00056 };
00057
00058
00059 Blokkal::Ui::HtmlTextHighlighter::HtmlTextHighlighter( QTextEdit * parent ):
00060 Sonnet::Highlighter( parent ),
00061 d( new Private( parent ) )
00062 {
00063 slotAppearanceSettingsChanged();
00064
00065 connect( AppearanceSettings::self(),
00066 SIGNAL( appearanceSettingsChanged( void ) ),
00067 SLOT( slotAppearanceSettingsChanged( void ) ) );
00068 connect( KGlobalSettings::self(),
00069 SIGNAL( kdisplayFontChanged ( void )),
00070 SLOT( slotAppearanceSettingsChanged( void ) ) );
00071 }
00072
00073 Blokkal::Ui::HtmlTextHighlighter::~HtmlTextHighlighter( void )
00074 {
00075 delete d;
00076 }
00077
00078 void Blokkal::Ui::HtmlTextHighlighter::highlightBlock( const QString & text )
00079 {
00080 if( d->highlightSpelling ) {
00081 Sonnet::Highlighter::highlightBlock( text );
00082 }
00083 else {
00084 setFormat( 0,text.length(), d->normalFormat );
00085 }
00086
00087 if( d->highlightSyntax
00088 || ( !d->highlightSyntax && d->highlightSpelling ) )
00089 {
00090 int tagStart = 0;
00091 int tagEnd = 0;
00092 while( ( tagStart = text.indexOf( '<', tagStart ) ) > -1 ) {
00093
00094 if( ( ( text.length() > tagStart + 1 )
00095 && text.at( tagStart + 1 ).isLetter() )
00096 || ( ( text.length() > tagStart + 2 )
00097 && ( text.at( tagStart + 1 ) == '/' && text.at( tagStart + 2 ).isLetter() ) ) )
00098 {
00099 tagEnd = text.indexOf( '>', tagStart );
00100 if( tagEnd < 0 ) {
00101 tagEnd = text.length();
00102 }
00103
00104 if( d->highlightSyntax ) {
00105 setFormat( tagStart, tagEnd - tagStart + 1 , d->tagFormat );
00106 bool inString = FALSE;
00107 bool inValue = FALSE;
00108 int subStart = tagStart;
00109 for( int index = tagStart; index < tagEnd; ++index ) {
00110 switch( text.at( index ).toLower().cell() ) {
00111 case '"':
00112 if( inString ) {
00113 if( text.at( index - 1) != '\\' ) {
00114 setFormat( subStart, index - subStart + 1, d->stringFormat );
00115 inString = FALSE;
00116 }
00117 }
00118 else {
00119 subStart = index;
00120 inString = TRUE;
00121 }
00122 break;
00123 case '#':
00124 case '0':
00125 case '1':
00126 case '2':
00127 case '3':
00128 case '4':
00129 case '5':
00130 case '6':
00131 case '7':
00132 case '8':
00133 case '9':
00134 if( !inString
00135 && !inValue
00136 && ( text.at( index-1) == ' '
00137 || text.at( index-1) == '\t'
00138 || text.at( index-1) == '=' ) ) {
00139 subStart = index;
00140 inValue = TRUE;
00141 }
00142 break;
00143 case ' ':
00144 case '\t':
00145 if( inValue ) {
00146 setFormat( subStart, index - subStart + 1, d->valueFormat );
00147 inValue = FALSE;
00148 }
00149 break;
00150 case 'a':
00151 case 'b':
00152 case 'c':
00153 case 'd':
00154 case 'e':
00155 case 'f':
00156 break;
00157 case '/':
00158 if( inValue && text.at( index + 1 ) == '>' ) {
00159 setFormat( subStart, index - subStart, d->valueFormat );
00160 }
00161 inValue = FALSE;
00162 break;
00163 default:
00164 if( inValue ) {
00165 inValue = FALSE;
00166 }
00167 break;
00168 }
00169 }
00170 if( inValue ) {
00171 setFormat( subStart, tagEnd - subStart, d->valueFormat );
00172 }
00173
00174 }
00175 else {
00176 setFormat( 0,text.length(), d->normalFormat );
00177 }
00178
00179 tagStart = tagEnd;
00180 }
00181 else {
00182 tagStart++;
00183 }
00184 }
00185 }
00186 }
00187
00188 void Blokkal::Ui::HtmlTextHighlighter::slotAppearanceSettingsChanged( void )
00189 {
00190 d->highlightSyntax = AppearanceSettings::self()->highlightSyntax();
00191 d->highlightSpelling = AppearanceSettings::self()->checkSpelling();
00192
00193 d->normalFormat.setFont( KGlobalSettings::generalFont() );
00194 d->normalFormat.setForeground( d->edit->palette().text() );
00195
00196 QFont font = KGlobalSettings::generalFont();
00197 font.setBold( AppearanceSettings::self()->highlightTagBold() );
00198 font.setItalic( AppearanceSettings::self()->highlightTagItalic() );
00199 d->tagFormat.setFont( font );
00200 d->tagFormat.setForeground( AppearanceSettings::self()->highlightTagColor() );
00201
00202 font = KGlobalSettings::generalFont();
00203 font.setBold( AppearanceSettings::self()->highlightStringBold() );
00204 font.setItalic( AppearanceSettings::self()->highlightStringItalic() );
00205 d->stringFormat.setFont( font );
00206 d->stringFormat.setForeground( AppearanceSettings::self()->highlightStringColor() );
00207
00208 font = KGlobalSettings::generalFont();
00209 font.setBold( AppearanceSettings::self()->highlightValueBold() );
00210 font.setItalic( AppearanceSettings::self()->highlightValueItalic() );
00211 d->valueFormat.setFont( font );
00212 d->valueFormat.setForeground( AppearanceSettings::self()->highlightValueColor() );
00213
00214 rehighlight();
00215 }
00216
00217
00218
00219
00220 class Blokkal::Ui::HtmlEntryTextEdit::Private
00221 {
00222 public:
00223 Private( void ) :
00224 highlighter( 0 ),
00225 spellCheckAction ( 0 ),
00226 proxy( 0 )
00227 {}
00228
00229 HtmlTextHighlighter * highlighter;
00230
00231 QAction * spellCheckAction;
00232
00233 DropProxy * proxy;
00234
00235 QTextBlock currentTextBlock;
00236 QString blockText;
00237 };
00238
00239 Blokkal::Ui::HtmlEntryTextEdit::HtmlEntryTextEdit( QWidget * parent ):
00240 KTextEdit( parent ),
00241 d( new Private() )
00242 {
00243 setAcceptRichText( FALSE );
00244 setTabChangesFocus( TRUE );
00245 d->highlighter = new HtmlTextHighlighter( this );
00246 d->currentTextBlock = textCursor().block();
00247 d->blockText = d->currentTextBlock.text();
00248 connect( this,
00249 SIGNAL( textChanged( void ) ),
00250 SLOT( checkCurrentBlock( void ) ) );
00251 }
00252
00253 Blokkal::Ui::HtmlEntryTextEdit::~HtmlEntryTextEdit( void )
00254 {
00255 delete d;
00256 }
00257
00258 void Blokkal::Ui::HtmlEntryTextEdit::insertTextAtCursor( const QString & text )
00259 {
00260 QTextCursor cursor( textCursor() );
00261 const int position = cursor.position();
00262 cursor.insertText( text );
00263 cursor.setPosition( position + text.length() );
00264 setTextCursor( cursor );
00265 }
00266
00267 void Blokkal::Ui::HtmlEntryTextEdit::frameSelection( const QString & front, const QString & tail )
00268 {
00269 QTextCursor cursor( textCursor() );
00270 if( cursor.hasSelection() && !cursor.hasComplexSelection() ) {
00271 const int anchor = cursor.anchor();
00272 const int selectionEnd = cursor.selectionEnd();
00273 cursor.clearSelection();
00274 if( anchor < selectionEnd ) {
00275 cursor.insertText( tail );
00276 cursor.setPosition( anchor );
00277 cursor.insertText( front );
00278 }
00279 else {
00280 cursor.insertText( front );
00281 cursor.setPosition( selectionEnd + front.length() );
00282 cursor.insertText( tail );
00283 }
00284
00285 cursor.setPosition( selectionEnd + front.length() );
00286 setTextCursor( cursor );
00287 }
00288 else if( !cursor.hasSelection() ) {
00289 cursor.insertText( front );
00290 int position = cursor.position();
00291 cursor.insertText( tail );
00292 cursor.setPosition( position );
00293 setTextCursor( cursor );
00294 }
00295 }
00296
00297 void Blokkal::Ui::HtmlEntryTextEdit::dropEvent( QDropEvent * event )
00298 {
00299 if( isReadOnly() ) {
00300 KTextEdit::dropEvent( event );
00301 return;
00302 }
00303
00304 if( d->proxy && d->proxy->handleDrop( event, this ) ) {
00305 return;
00306 }
00307
00308
00309 KTextEdit::dropEvent( event );
00310 }
00311
00312 void Blokkal::Ui::HtmlEntryTextEdit::checkCurrentBlock( void )
00313 {
00314 if( d->currentTextBlock == textCursor().block() ) {
00315 if( d->blockText != d->currentTextBlock.text() ) {
00316 d->blockText = d->currentTextBlock.text();
00317 emit plainTextChanged();
00318 }
00319 }
00320 else {
00321 d->currentTextBlock = textCursor().block();
00322 d->blockText = d->currentTextBlock.text();
00323 emit plainTextChanged();
00324 }
00325 }
00326
00327 void Blokkal::Ui::HtmlEntryTextEdit::contextMenuEvent( QContextMenuEvent * event )
00328 {
00329
00330 QMenu *popup = createStandardContextMenu();
00331
00332 connect( popup,
00333 SIGNAL( triggered ( QAction* ) ),
00334 SLOT( menuActivated( QAction* ) ) );
00335
00336 if( !isReadOnly() ) {
00337 QList<QAction *> actionList = popup->actions();
00338 enum { UndoAct, RedoAct, CutAct, CopyAct, PasteAct, ClearAct, SelectAllAct, NCountActs };
00339 QAction * separatorAction = 0L;
00340 int idx = actionList.indexOf( actionList[SelectAllAct] ) + 1;
00341 if ( idx < actionList.count() ) {
00342 separatorAction = actionList.at( idx );
00343 }
00344 if ( separatorAction ) {
00345 KAction * clearAllAction = KStandardAction::clear( this, SLOT( clear() ), this );
00346 if ( toPlainText().isEmpty() ) {
00347 clearAllAction->setEnabled( FALSE );
00348 }
00349 popup->insertAction( separatorAction, clearAllAction );
00350 }
00351 }
00352
00353 KIconTheme::assignIconsToContextMenu( isReadOnly() ? KIconTheme::ReadOnlyText : KIconTheme::TextEditor,
00354 popup->actions() );
00355
00356 if( !isReadOnly() ) {
00357 popup->addSeparator();
00358 if( !acceptRichText() ) {
00359 d->spellCheckAction = popup->addAction( KIcon( "tools-check-spelling" ), i18n( "Check Spelling..." ) );
00360 if ( document()->isEmpty() ) {
00361 d->spellCheckAction->setEnabled( FALSE );
00362 }
00363 }
00364 }
00365 popup->exec( event->globalPos() );
00366
00367 delete popup;
00368 }
00369
00370 void Blokkal::Ui::HtmlEntryTextEdit::menuActivated( QAction * action )
00371 {
00372 if( action == d->spellCheckAction ) {
00373 checkSpelling();
00374 }
00375 }
00376
00377 void Blokkal::Ui::HtmlEntryTextEdit::setDropProxy( Blokkal::Ui::DropProxy * proxy )
00378 {
00379 d->proxy = proxy;
00380 }
00381
00382
00383
00384
00385 Blokkal::Ui::DropProxy::~DropProxy( void )
00386 {}