diff options
Diffstat (limited to 'ksirc/kstextview.h')
-rw-r--r-- | ksirc/kstextview.h | 577 |
1 files changed, 577 insertions, 0 deletions
diff --git a/ksirc/kstextview.h b/ksirc/kstextview.h new file mode 100644 index 00000000..7d29cbe9 --- /dev/null +++ b/ksirc/kstextview.h @@ -0,0 +1,577 @@ +/* This file is part of the KDE project + Copyright (C) 2001 Simon Hausmann <hausmann@kde.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ +#ifndef __kstextview_h__ +#define __kstextview_h__ + +#include <qscrollview.h> +#include <qpen.h> +#include <qptrlist.h> +#include <qvaluelist.h> +#include <qmap.h> +#include <qpixmap.h> + +class QTimer; +class QDragObject; + +namespace KSirc +{ + +class TextView; + +struct StringPtr +{ + StringPtr() : ptr( 0 ), len( 0 ) {} + StringPtr( const QChar *_ptr, uint _len ) + : ptr( _ptr ), len( _len ) {} + explicit StringPtr( const QString &s ) // use with care! + : ptr( s.unicode() ), len( s.length() ) {} + + inline bool isNull() const { return ptr == 0; } + + // makes deep copy + inline QString toQString() const + { return ( ptr && len > 0 ) ? QString( ptr, len ) : QString::null; } + + const QChar *ptr; + uint len; +}; + +#define CONSTSTRING( substr ) QConstString( substr.ptr, substr.len ).string() + +inline bool operator<( const StringPtr &s1, const StringPtr &s2 ) +{ + return CONSTSTRING( s1 ) < CONSTSTRING( s2 ); +} + +inline bool operator==( const StringPtr &s1, const StringPtr &s2 ) +{ + return CONSTSTRING( s1 ) == CONSTSTRING( s2 ); +} + +inline bool operator==( const StringPtr &s1, const char *s2 ) +{ + return CONSTSTRING( s1 ) == s2; +} + +class AttributeMap : public QMap<StringPtr, StringPtr> +{ +public: + AttributeMap() {} + AttributeMap( const AttributeMap &rhs ) : QMap<StringPtr, StringPtr>( rhs ) {} + AttributeMap &operator=( const AttributeMap &rhs ) + { QMap<StringPtr, StringPtr>::operator=( rhs ); return *this; } + + // helper for 'const char *' key... + ConstIterator findAttribute( const char *key ) const + { + QString qkey( key ); + return find( StringPtr( qkey ) ); + } + Iterator findAttribute( const char *key ) + { + QString qkey( key ); + return find( StringPtr( qkey ) ); + } + + StringPtr operator[]( const char *key ) const + { + ConstIterator it = findAttribute( key ); + if ( it == end() ) + return StringPtr(); + return it.data(); + } + StringPtr &operator[]( const StringPtr &key ) + { + return QMap<StringPtr, StringPtr>::operator[]( key ); + } +}; + +struct Token +{ + Token() : id( -1 ) {} + + enum Id { TagOpen, Text, TagClose }; + int id; + StringPtr value; + AttributeMap attributes; +}; + +struct ItemProperties +{ + ItemProperties(); + ItemProperties( const QFont &defaultFont ); + ItemProperties( const ItemProperties &other, + const Token &token, + TextView *textView ); + ItemProperties( const ItemProperties &rhs ); + ItemProperties &operator=( const ItemProperties &rhs ); + + void updateFont( const QFont &newFont ); + + // these three are inherited/merged + QFont font; + QColor color; + QColor selColor; + QColor bgColor; + QColor bgSelColor; + bool reversed; + // ### todo: inherit these, too + AttributeMap attributes; +}; + +class TextParag; +class TextLine; +class SelectionPoint; + +class Item +{ +public: + enum LayoutResetStatus { DeleteItem, KeepItem }; + enum SelectionStatus { SelectionStart = 0, InSelection, SelectionEnd, SelectionBoth, + NoSelection }; + enum SelectionAccuracy { SelectExact, SelectFuzzy }; + + Item( TextParag *parag, const ItemProperties &props = ItemProperties() ); + + virtual ~Item(); + + virtual const char *type() { return "Item"; } + + virtual void paint( QPainter &painter ) = 0; + + int width() const; + + int minWidth() const; + + int height() const; + + virtual Item *breakLine( int width ); + + virtual LayoutResetStatus resetLayout() = 0; + + virtual int calcSelectionOffset( int x ); + + void setSelectionStatus( SelectionStatus status ) { m_selection = status; } + + SelectionStatus selectionStatus() const { return m_selection; } + + void selectionOffsets( int &startOffset, int &endOffset ); + + int maxSelectionOffset() const; + + void setLine(TextLine *line); + + // ### + virtual StringPtr text() const; + + virtual void setProps( const ItemProperties &props ); + ItemProperties &props() { return m_props; } + + static Item *create( TextParag *parag, const Token &tok, + const ItemProperties &props = ItemProperties() ); + +protected: + mutable bool m_extendsDirty; + mutable int m_minWidth; + mutable int m_width; + mutable int m_height; + + virtual void calcExtends() const = 0; + + SelectionStatus m_selection; + TextLine *m_line; + TextParag *m_parag; + ItemProperties m_props; +}; + +class TextChunk : public Item +{ +public: + TextChunk( TextParag *parag, const StringPtr &text, const ItemProperties &props ); + + virtual const char *type() { return "TextChunk"; } + + virtual void paint( QPainter &painter ); + + virtual Item *breakLine( int width ); + + virtual LayoutResetStatus resetLayout(); + + virtual int calcSelectionOffset( int x ); + + virtual StringPtr text() const; + + virtual void setProps( const ItemProperties &props ); + +protected: + virtual void calcExtends() const; + +private: + StringPtr breakInTheMiddle( int width ); + Item *hardBreak( const StringPtr &rightHandSide ); + + void paintSelection( QPainter &p ); + int paintSelection( QPainter &p, int x, const StringPtr &text ); + int paintText( QPainter &p, int x, const StringPtr &text ); + + void mergeSelection( TextChunk *child, SelectionPoint *selection ); + + StringPtr m_text; + uint m_originalTextLength; + QFontMetrics m_metrics; + class TextChunk *m_parent; +}; + +class ImageItem : public Item +{ +public: + ImageItem( TextParag *parag, const QPixmap &pixmap ); + + virtual const char *type() { return "Image"; } + + virtual void paint( QPainter &painter ); + + virtual LayoutResetStatus resetLayout(); + +protected: + virtual void calcExtends() const; + +private: + QPixmap m_pixmap; +}; + +class Tokenizer +{ +public: + struct TagIndex + { + enum Type { Open, Close }; + TagIndex() : index( 0 ), type( -1 ) {} + TagIndex( int _index, int _type ) + : index( _index ), type( _type ) {} + uint index; + int type; + }; + typedef QValueList<TagIndex> TagIndexList; + + // preprocessed string + struct PString + { + QString data; + TagIndexList tags; + }; + + Tokenizer( PString &text ); + + static PString preprocess( const QString &richText ); + + static QString convertToRichText( const PString &ptext ); + + bool parseNextToken( Token &tok ); + +private: + void parseTag( const StringPtr &text, + StringPtr &tag, + AttributeMap &attributes ); + + static TagIndexList scanTagIndices( const QString &text ); + static void resolveEntities( QString &text, TagIndexList &tags ); + + enum TagParsingState { ScanForName, ScanForEqual, ScanForValue }; + + QString &m_text; + TagIndexList m_tags; + TagIndexList::ConstIterator m_lastTag; + bool m_textBeforeFirstTagProcessed; + bool m_done; + + Tokenizer( const Tokenizer & ); + Tokenizer &operator=( const Tokenizer & ); +}; + +class SelectionPoint; + +class TextLine +{ +public: + enum LayoutPolicy { NoUpdate, UpdateMaxHeight }; + + TextLine(); + // tranfers ownership of items! make sure that 'items' does not + // have autodeletion enabled! + TextLine( const QPtrList<Item> &items ); + + int maxHeight() const { return m_maxHeight; } + + QString updateSelection( const SelectionPoint &start, const SelectionPoint &end ); + void clearSelection(); + + // transfers ownership + void appendItem( Item *i, int layoutUpdatePolicy = NoUpdate ); + + bool isEmpty() const { return m_items.isEmpty(); } + + Item *resetLayout( QPtrList<Item> &remainingItems); + + void paint( QPainter &p, int y ); + + Item *itemAt( int px, SelectionPoint *selectionInfo, + Item::SelectionAccuracy accuracy = Item::SelectExact ); + + QPtrListIterator<Item> iterator() const { return QPtrListIterator<Item>( m_items ); } + + QString plainText() const; + + void fontChange( const QFont &newFont ); + +private: + QPtrList<Item> m_items; + int m_maxHeight; +}; + +class SelectionPoint; + +class TextParag +{ +public: + TextParag( TextView *textView, const QString &richText ); + + ~TextParag(); + + void layout( int width ); + + void paint( QPainter &p, int y, int maxY ); + + inline void setLayouted( bool l ) { m_layouted = l; } + inline bool isLayouted() const { return m_layouted; } + + inline int minWidth() const { return m_minWidth; } + inline int height() const { return m_height; } + + Item *itemAt( int px, int py, SelectionPoint *selectionInfo, + Item::SelectionAccuracy accuracy = Item::SelectExact ); + + TextView *textView() const { return m_textView; } + + QString updateSelection( const SelectionPoint &start, const SelectionPoint &end ); + + void clearSelection(); + + void setRichText( const QString &richText ); + + Tokenizer::PString processedRichText() const { return m_processedRichText; } + + QString plainText() const; + + void fontChange( const QFont &newFont ); + +private: + Tokenizer::PString m_processedRichText; + QPtrList<TextLine> m_lines; + bool m_layouted; + int m_height; + int m_minWidth; + TextView *m_textView; + + struct Tag + { + Tag() {} + Tag( const StringPtr &_name, const ItemProperties &_props ) + : name( _name ), props( _props ) {} + + StringPtr name; + ItemProperties props; + }; + + TextParag( const TextParag & ); + TextParag &operator=( const TextParag & ); +}; + +struct SelectionPoint +{ + SelectionPoint() : item( 0 ), line( 0 ), parag( 0 ), offset( 0 ) {} + Item *item; + TextLine *line; + TextParag *parag; + uint offset; + QPoint pos; +}; + +class TextParagIterator +{ + friend class TextView; +public: + TextParagIterator( const TextParagIterator &rhs ) + : m_paragIt( rhs.m_paragIt ) {} + TextParagIterator &operator=( const TextParagIterator &rhs ) + { m_paragIt = rhs.m_paragIt; return *this; } + + QString richText() const; + void setRichText( const QString &richText ); + + QString plainText() const; + + bool atEnd() const { return m_paragIt.current() == 0; } + + TextParagIterator &operator++() { ++m_paragIt; return *this; } + TextParagIterator &operator++( int steps ) { m_paragIt += steps; return *this; } + TextParagIterator &operator--() { --m_paragIt; return *this; } + TextParagIterator &operator--( int steps ) { m_paragIt -= steps; return *this; } + +protected: + TextParagIterator( const QPtrListIterator<TextParag> ¶gIt ) + : m_paragIt( paragIt ) {} + +private: + QPtrListIterator<TextParag> m_paragIt; +}; + +class ContentsPaintAlgorithm +{ +public: + ContentsPaintAlgorithm( const QPtrListIterator<TextParag> ¶gIt, + QWidget *viewport, QPixmap &paintBuffer, + QPainter &painter, int clipX, int clipY, int clipHeight ); + + void paint(); + +private: + int goToFirstVisibleParagraph(); + int paint( QPainter &bufferedPainter, int currentY ); + int adjustYAndIterator( int startY, int currentY, int nextY ); + + QPtrListIterator<TextParag> m_paragIt; + QWidget *m_viewport; + QPixmap &m_paintBuffer; + QPainter &m_painter; + int m_clipX, m_clipY, m_clipHeight; + int m_overshoot; +}; + +class TextView : public QScrollView +{ + Q_OBJECT + friend class Item; + friend class TextChunk; + friend class TextParag; + friend class TextParagIterator; +public: + TextView( QWidget *parent, const char *name = 0 ); + virtual ~TextView(); + + virtual void clear(); + + TextParagIterator appendParag( const QString &richText ); + + bool removeParag( const TextParagIterator ¶g ); + + void clearSelection( bool repaint = false ); // ### re-consider the repaint arg... + + QString selectedText() const { return m_selectedText; } + + TextParagIterator firstParag() const; + + QString plainText() const; + + QColor linkColor() const; + void setLinkColor( const QColor &linkColor ); + + void scrollToBottom( bool force = false ); + +signals: + void selectionChanged(); + void pasteReq(const QString&); + void linkClicked( const QMouseEvent *ev, const QString &url ); + +public slots: + void copy(); + +protected slots: + void scrolling(int value); + +protected: + virtual void viewportResizeEvent( QResizeEvent *ev ); + virtual void drawContents( QPainter *p, int cx, int cy, int cw, int ch ); + virtual void contentsMousePressEvent( QMouseEvent *ev ); + virtual void contentsMouseMoveEvent( QMouseEvent *ev ); + virtual void contentsMouseReleaseEvent( QMouseEvent *ev ); + virtual void fontChange( const QFont & ); + + virtual void startDrag(); + + virtual QDragObject *dragObject( const QString &dragURL ); + +private slots: + void autoScroll(); + +private: + void emitLinkClickedForMouseEvent( QMouseEvent *ev ); + + void startAutoScroll(); + + void stopAutoScroll(); + + void selectionOffsets( int &startOffset, int &endOffset ); + + void updateSelectionOrder(); + + QString updateSelection( const SelectionPoint &start, const SelectionPoint &end ); + + SelectionPoint *selectionStart(); + SelectionPoint *selectionEnd(); + + void layout( bool force = true ); + + Item *itemAt( const QPoint &pos, SelectionPoint *selectionInfo = 0, + Item::SelectionAccuracy accuracy = Item::SelectExact ); + + void clearSelectionInternal(); + + void contentsChange(int heightChange, bool force = false); + + QPtrList<TextParag> m_parags; + QPixmap m_paintBuffer; + + SelectionPoint m_selectionMaybeStart; + SelectionPoint m_selectionStart; + SelectionPoint m_selectionEnd; + bool m_selectionEndBeforeStart; + + QTimer *m_autoScrollTimer; + + QString m_selectedText; + + QPoint m_dragStartPos; + QString m_dragURL; + bool m_mousePressed : 1; + bool m_mmbPressed : 1; + QColor m_linkColor; + QColor m_selectionBackgroundColor; + + int m_height; + bool m_inScroll; + int m_lastScroll; +}; + +} // namespace KSirc + +#endif +/* + * vim: et sw=4 + */ |