You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdenetwork/ksirc/kstextview.h

578 lines
14 KiB

/* 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., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, 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> &paragIt )
: m_paragIt( paragIt ) {}
private:
QPtrListIterator<TextParag> m_paragIt;
};
class ContentsPaintAlgorithm
{
public:
ContentsPaintAlgorithm( const QPtrListIterator<TextParag> &paragIt,
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 &parag );
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
*/