• Skip to content
  • Skip to link menu
KDE 4.0 API Reference
  • KDE API Reference
  • kdenetwork
  • Sitemap
  • Contact Us
 

kopete/kopete

chattexteditpart.cpp

Go to the documentation of this file.
00001 /*
00002     chattexteditpart.cpp - Chat Text Edit Part
00003 
00004     Copyright (c) 2004      by Richard Smith         <kde@metafoo.co.uk>
00005 
00006     Kopete    (c) 2002-2004 by the Kopete developers <kopete-devel@kde.org>
00007 
00008     *************************************************************************
00009     *                                                                       *
00010     * This program is free software; you can redistribute it and/or modify  *
00011     * it under the terms of the GNU General Public License as published by  *
00012     * the Free Software Foundation; either version 2 of the License, or     *
00013     * (at your option) any later version.                                   *
00014     *                                                                       *
00015     *************************************************************************
00016 */
00017 
00018 #include "chattexteditpart.h"
00019 
00020 #include "kopetecontact.h"
00021 #include "kopetechatsession.h"
00022 #include "kopeteonlinestatus.h"
00023 #include "kopeteprotocol.h"
00024 #include "kopeteglobal.h"
00025 #include <kopeteappearancesettings.h>
00026 
00027 #include <kcompletion.h>
00028 #include <kdebug.h>
00029 #include <ktextedit.h>
00030 #include <sonnet/highlighter.h>
00031 
00032 #include <QtCore/QTimer>
00033 #include <QtCore/QRegExp>
00034 
00035 ChatTextEditPart::ChatTextEditPart( Kopete::ChatSession *session, QWidget *parent )
00036     : KRichTextEditPart(parent, 0, QStringList()), m_session(session)
00037 {
00038     // Set rich support in the part
00039     setProtocolRichTextSupport();
00040 
00041     m_autoSpellCheckEnabled = true;
00042     historyPos = -1;
00043     
00044     mComplete = new KCompletion();
00045     mComplete->setIgnoreCase( true );
00046     mComplete->setOrder( KCompletion::Weighted );
00047     
00048     // set params on the edit widget
00049     textEdit()->setMinimumSize( QSize( 75, 20 ) );
00050 //  textEdit()->setWordWrap( Q3TextEdit::WidgetWidth );
00051 //  textEdit()->setWrapPolicy( Q3TextEdit::AtWhiteSpace );
00052 //  textEdit()->setAutoFormatting( Q3TextEdit::AutoNone );
00053 
00054     m_highlighter = new Sonnet::Highlighter( textEdit() );
00055     // some signals and slots connections
00056     connect( textEdit(), SIGNAL( textChanged()), this, SLOT( slotTextChanged() ) );
00057 
00058     // timers for typing notifications
00059     m_typingRepeatTimer = new QTimer(this);
00060     m_typingRepeatTimer->setObjectName("m_typingRepeatTimer");
00061     m_typingStopTimer   = new QTimer(this);
00062     m_typingStopTimer->setObjectName("m_typingStopTimer");
00063 
00064     connect( m_typingRepeatTimer, SIGNAL( timeout() ), this, SLOT( slotRepeatTypingTimer() ) );
00065     connect( m_typingStopTimer,   SIGNAL( timeout() ), this, SLOT( slotStoppedTypingTimer() ) );
00066 
00067     connect( session, SIGNAL( contactAdded(const Kopete::Contact*, bool) ),
00068              this, SLOT( slotContactAdded(const Kopete::Contact*) ) );
00069     connect( session, SIGNAL( contactRemoved(const Kopete::Contact*, const QString&, Qt::TextFormat, bool) ),
00070              this, SLOT( slotContactRemoved(const Kopete::Contact*) ) );
00071     connect( session, SIGNAL( onlineStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus & , const Kopete::OnlineStatus &) ),
00072              this, SLOT( slotContactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &, const Kopete::OnlineStatus & ) ) );
00073     
00074     setFont( Kopete::AppearanceSettings::self()->chatFont() );
00075 
00076     slotContactAdded( session->myself() );
00077 
00078     foreach( Kopete::Contact *contact, session->members() )
00079         slotContactAdded( contact );
00080 }
00081 
00082 ChatTextEditPart::~ChatTextEditPart()
00083 {
00084     delete mComplete;
00085 }
00086 
00087 void ChatTextEditPart::toggleAutoSpellCheck( bool enabled )
00088 {
00089     if ( useRichText() )
00090         enabled = false;
00091 
00092     m_autoSpellCheckEnabled = enabled;
00093     if ( spellHighlighter() )
00094     {
00095         spellHighlighter()->setAutomatic( enabled );
00096         spellHighlighter()->setActive( enabled );
00097     }
00098     textEdit()->setCheckSpellingEnabled( enabled );
00099 }
00100 
00101 bool ChatTextEditPart::autoSpellCheckEnabled() const
00102 {
00103     return m_autoSpellCheckEnabled;
00104 }
00105 
00106 Sonnet::Highlighter* ChatTextEditPart::spellHighlighter()
00107 {
00108     return m_highlighter;
00109 }
00110 
00111 // NAUGHTY, BAD AND WRONG! (but needed to fix nick complete bugs)
00112 /*
00113 #include <private/qrichtext_p.h>
00114 class EvilTextEdit : public KTextEdit
00115 {
00116 public:
00117     // grab the paragraph as plain text - very very evil.
00118     QString plainText( int para )
00119     {
00120         QString str = document()->paragAt( para )->string()->toString();
00121         // str includes an extra space on the end (from the newline character?) - remove it
00122         return str.left( str.length() - 1 );
00123     }
00124 };
00125 */
00126 void ChatTextEditPart::complete()
00127 {
00128 #ifdef __GNUC__
00129 #warning disabled nick completion to make it compile
00130 #endif
00131 #if 0
00132     int para = 1, parIdx = 1;
00133     textEdit()->getCursorPosition( &para, &parIdx);
00134 
00135     // FIXME: strips out all formatting
00136 //QString txt = static_cast<EvilTextEdit*>(textEdit())->plainText( para );
00137     QString txt = textEdit()->text(para);
00138 
00139     if ( parIdx > 0 )
00140     {
00141         int firstSpace = txt.lastIndexOf( QRegExp( QLatin1String("\\s\\S+") ), parIdx - 1 ) + 1;
00142         int lastSpace = txt.find( QRegExp( QLatin1String("[\\s\\:]") ), firstSpace );
00143         if( lastSpace == -1 )
00144             lastSpace = txt.length();
00145 
00146         QString word = txt.mid( firstSpace, lastSpace - firstSpace );
00147         QString match;
00148 
00149         kDebug(14000) << word << " from '" << txt << "'";
00150 
00151         if ( word != m_lastMatch )
00152         {
00153             match = mComplete->makeCompletion( word );
00154             m_lastMatch.clear();
00155             parIdx -= word.length();
00156         }
00157         else
00158         {
00159             match = mComplete->nextMatch();
00160             parIdx -= m_lastMatch.length();
00161         }
00162 
00163         if ( !match.isNull() && !match.isEmpty() )
00164         {
00165             QString rightText = txt.right( txt.length() - lastSpace );
00166 
00167             if ( para == 0 && firstSpace == 0 && rightText[0] != QChar(':') )
00168             {
00169                 rightText = match + QLatin1String(": ") + rightText;
00170                 parIdx += 2;
00171             }
00172             else
00173                 rightText = match + rightText;
00174 
00175             // insert *before* remove. this is becase Qt adds an extra blank line
00176             // if the rich text control becomes empty (if you remove the only para).
00177             // disable updates while we change the contents to eliminate flicker.
00178             textEdit()->setUpdatesEnabled( false );
00179             textEdit()->insertParagraph( txt.left(firstSpace) + rightText, para );
00180             textEdit()->removeParagraph( para + 1 );
00181             textEdit()->setCursorPosition( para, parIdx + match.length() );
00182             textEdit()->setUpdatesEnabled( true );
00183             // must call this rather than update because QTextEdit is broken :(
00184             textEdit()->updateContents();
00185             m_lastMatch = match;
00186         }
00187         else
00188         {
00189             kDebug(14000) << "No completions! Tried " << mComplete->items();
00190         }
00191     }
00192 #endif
00193 }
00194 
00195 void ChatTextEditPart::slotPropertyChanged( Kopete::PropertyContainer*, const QString &key,
00196         const QVariant& oldValue, const QVariant &newValue  )
00197 {
00198     if ( key == Kopete::Global::Properties::self()->nickName().key() )
00199     {
00200         mComplete->removeItem( oldValue.toString() );
00201         mComplete->addItem( newValue.toString() );
00202     }
00203 }
00204 
00205 void ChatTextEditPart::slotContactAdded( const Kopete::Contact *contact )
00206 {
00207     connect( contact, SIGNAL( propertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ),
00208              this, SLOT( slotPropertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ) ) ;
00209     
00210     QString contactName = contact->property(Kopete::Global::Properties::self()->nickName()).value().toString();
00211     mComplete->addItem( contactName );
00212 }
00213 
00214 void ChatTextEditPart::slotContactRemoved( const Kopete::Contact *contact )
00215 {
00216     disconnect( contact, SIGNAL( propertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ),
00217                 this, SLOT( slotPropertyChanged( Kopete::PropertyContainer *, const QString &, const QVariant &, const QVariant & ) ) ) ;
00218     
00219     QString contactName = contact->property(Kopete::Global::Properties::self()->nickName()).value().toString();
00220     mComplete->removeItem( contactName );
00221 }
00222 
00223 bool ChatTextEditPart::canSend()
00224 {
00225     int i;
00226     
00227     if ( !m_session ) return false;
00228 
00229     // can't send if there's nothing *to* send...
00230     if ( text(Qt::PlainText).isEmpty() )
00231         return false;
00232 
00233     Kopete::ContactPtrList members = m_session->members();
00234     
00235     // if we can't send offline, make sure we have a reachable contact...
00236     if ( !( m_session->protocol()->capabilities() & Kopete::Protocol::CanSendOffline ) )
00237     {
00238         bool reachableContactFound = false;
00239 
00240         //TODO: does this perform badly in large / busy IRC channels? - no, doesn't seem to
00241         for( i = 0; i != members.size(); i++ )
00242         {
00243             if ( members[i]->isReachable() )
00244             {
00245                 reachableContactFound = true;
00246                 break;
00247             }
00248         }
00249 
00250         // no online contact found and can't send offline? can't send.
00251         if ( !reachableContactFound )
00252             return false;
00253     }
00254 
00255     return true;
00256 }
00257 
00258 void ChatTextEditPart::slotContactStatusChanged( Kopete::Contact *, const Kopete::OnlineStatus &newStatus, const Kopete::OnlineStatus &oldStatus )
00259 {
00260     //FIXME: should use signal contact->isReachableChanged, but it doesn't exist ;(
00261     if ( ( oldStatus.status() == Kopete::OnlineStatus::Offline )
00262       != ( newStatus.status() == Kopete::OnlineStatus::Offline ) )
00263     {
00264         emit canSendChanged( canSend() );
00265     }
00266 }
00267 
00268 void ChatTextEditPart::sendMessage()
00269 {
00270     QString txt = this->text( Qt::PlainText );
00271     // avoid sending emtpy messages or enter keys (see bug 100334)
00272     if ( txt.isEmpty() || txt == "\n" )
00273         return;
00274 
00275     if ( m_lastMatch.isNull() && ( txt.indexOf( QRegExp( QLatin1String("^\\w+:\\s") ) ) > -1 ) )
00276     { //no last match and it finds something of the form of "word:" at the start of a line
00277         QString search = txt.left( txt.indexOf(':') );
00278         if( !search.isEmpty() )
00279         {
00280             QString match = mComplete->makeCompletion( search );
00281             if( !match.isNull() )
00282                 textEdit()->setText( txt.replace(0,search.length(),match) );
00283         }
00284     }
00285 
00286     if ( !m_lastMatch.isNull() )
00287     {
00288         //FIXME: what is the next line for?
00289         mComplete->addItem( m_lastMatch );
00290         m_lastMatch.clear();
00291     }
00292 
00293     slotStoppedTypingTimer();
00294     Kopete::Message sentMessage = contents();
00295     emit messageSent( sentMessage );
00296     historyList.prepend( this->text( Qt::PlainText) );
00297     historyPos = -1;
00298     clear();
00299     emit canSendChanged( false );
00300 }
00301 
00302 bool ChatTextEditPart::isTyping()
00303 {
00304     QString txt = text( Qt::PlainText );
00305 
00306     //Make sure the message is empty. QString::isEmpty()
00307     //returns false if a message contains just whitespace
00308     //which is the reason why we strip the whitespace   
00309     return !txt.trimmed().isEmpty();
00310 }
00311 
00312 void ChatTextEditPart::slotTextChanged()
00313 {
00314     if ( isTyping() )
00315     {
00316         // And they were previously typing
00317         if( !m_typingRepeatTimer->isActive() )
00318         {
00319             m_typingRepeatTimer->setSingleShot( false );
00320             m_typingRepeatTimer->start( 4000 );
00321             slotRepeatTypingTimer();
00322         }
00323 
00324         // Reset the stop timer again, regardless of status
00325         m_typingStopTimer->setSingleShot( true );
00326         m_typingStopTimer->start( 4500 );
00327     }
00328 
00329     emit canSendChanged( canSend() );
00330 }
00331 
00332 void ChatTextEditPart::historyUp()
00333 {
00334     if ( historyList.empty() || historyPos == historyList.count() - 1 )
00335         return;
00336     
00337     QString text = this->text(Qt::PlainText);
00338     bool empty = text.trimmed().isEmpty();
00339     
00340     // got text? save it
00341     if ( !empty )
00342     {
00343         if ( historyPos == -1 )
00344         {
00345             historyList.prepend( text );
00346             historyPos = 0;
00347         }
00348         else
00349         {
00350             historyList[historyPos] = text;
00351         }
00352     }
00353     
00354     historyPos++;
00355     
00356     QString newText = historyList[historyPos];
00357 //  TextFormat format=textEdit()->textFormat();
00358 //  textEdit()->setTextFormat(AutoText); //workaround bug 115690
00359     textEdit()->setText( newText );
00360 //  textEdit()->setTextFormat(format);
00361     textEdit()->moveCursor( QTextCursor::End );
00362 }
00363 
00364 void ChatTextEditPart::historyDown()
00365 {
00366     if ( historyList.empty() || historyPos == -1 )
00367         return;
00368     
00369     QString text = this->text(Qt::PlainText);
00370     bool empty = text.trimmed().isEmpty();
00371     
00372     // got text? save it
00373     if ( !empty )
00374     {
00375         historyList[historyPos] = text;
00376     }
00377     
00378     historyPos--;
00379     
00380     QString newText = ( historyPos >= 0 ? historyList[historyPos] : QString() );
00381     
00382     
00383 //  TextFormat format=textEdit()->textFormat();
00384 //  textEdit()->setTextFormat(AutoText); //workaround bug 115690
00385     textEdit()->setText( newText );
00386 //  textEdit()->setTextFormat(format);
00387     textEdit()->moveCursor( QTextCursor::End );
00388 }
00389 
00390 void ChatTextEditPart::addText( const QString &text )
00391 {
00392     if( Qt::mightBeRichText(text) )
00393     {
00394         textEdit()->insertHtml( text );
00395     }
00396     else
00397     {
00398         textEdit()->insertPlainText( text );
00399     }
00400 }
00401 
00402 void ChatTextEditPart::setContents( const Kopete::Message &message )
00403 {
00404     if ( useRichText() )
00405         textEdit()->setHtml ( message.escapedBody() );
00406     else
00407         textEdit()->setPlainText ( message.plainBody() );
00408 
00409     setFont( message.font() );
00410     setTextColor( message.foregroundColor() );
00411 //  setBackgroundColorColor( message.backgroundColor() );
00412 }
00413 
00414 Kopete::Message ChatTextEditPart::contents()
00415 {
00416     Kopete::Message currentMsg( m_session->myself(), m_session->members() );
00417     currentMsg.setDirection( Kopete::Message::Outbound );
00418     useRichText() ? currentMsg.setHtmlBody( text() ) : currentMsg.setPlainBody( text() );
00419     
00420 //  currentMsg.setBackgroundColor( bgColor() );
00421     currentMsg.setForegroundColor( textColor() );
00422     currentMsg.setFont( font() );
00423     
00424     return currentMsg;
00425 }
00426 
00427 void ChatTextEditPart::slotRepeatTypingTimer()
00428 {
00429     emit typing( true );
00430 }
00431 
00432 void ChatTextEditPart::slotStoppedTypingTimer()
00433 {
00434     m_typingRepeatTimer->stop();
00435     m_typingStopTimer->stop();
00436     emit typing( false );
00437 }
00438 
00439 void ChatTextEditPart::setProtocolRichTextSupport()
00440 {
00441     KRichTextEditPart::RichTextSupport richText;
00442     Kopete::Protocol::Capabilities protocolCaps = m_session->protocol()->capabilities();
00443 
00444     // Check for bold
00445     if( (protocolCaps & Kopete::Protocol::BaseBFormatting) || (protocolCaps & Kopete::Protocol::RichBFormatting) )
00446     {
00447         richText |= KRichTextEditPart::SupportBold;
00448     }
00449     // Check for italic
00450     if( (protocolCaps & Kopete::Protocol::BaseIFormatting) || (protocolCaps & Kopete::Protocol::RichIFormatting) )
00451     {
00452         richText |= KRichTextEditPart::SupportItalic;
00453     }
00454     // Check for underline
00455     if( (protocolCaps & Kopete::Protocol::BaseUFormatting) || (protocolCaps & Kopete::Protocol::RichUFormatting) )
00456     {
00457         richText |= KRichTextEditPart::SupportUnderline;
00458     }
00459     // Check for font support
00460     if( (protocolCaps & Kopete::Protocol::BaseFont) || (protocolCaps & Kopete::Protocol::RichFont) )
00461     {
00462         richText |= KRichTextEditPart::SupportFont;
00463     }
00464     // Check for text color support
00465     if( (protocolCaps & Kopete::Protocol::BaseFgColor) || (protocolCaps & Kopete::Protocol::RichFgColor) )
00466     {
00467         richText |= KRichTextEditPart::SupportTextColor;
00468     }
00469     // Check for alignment
00470     if( protocolCaps & Kopete::Protocol::Alignment )
00471     {
00472         richText |= KRichTextEditPart::SupportAlignment;
00473     }
00474 
00475     // Set rich text support in KRichTextEditPart
00476     setRichTextSupport( richText );
00477 }
00478 
00479 #include "chattexteditpart.moc"
00480 
00481 // vim: set noet ts=4 sts=4 sw=4:

kopete/kopete

Skip menu "kopete/kopete"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdenetwork

Skip menu "kdenetwork"
  • kget
  • kopete
  •   kopete
  •   libkopete
  •       libpapillon
  • krfb
Generated for kdenetwork by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal