diff --git a/include/wx/richtext/richtextbuffer.h b/include/wx/richtext/richtextbuffer.h index 45eb308cff..d8fa8a67f3 100644 --- a/include/wx/richtext/richtextbuffer.h +++ b/include/wx/richtext/richtextbuffer.h @@ -102,6 +102,10 @@ #include "wx/cmdproc.h" #include "wx/txtstrm.h" +#if wxUSE_DATAOBJ +#include "wx/dataobj.h" +#endif + // Experimental dynamic styles to avoid user-specific character styles from being // overwritten by paragraph styles. #define wxRICHTEXT_USE_DYNAMIC_STYLES 1 @@ -127,7 +131,6 @@ class WXDLLIMPEXP_RICHTEXT wxRichTextCacheObject; class WXDLLIMPEXP_RICHTEXT wxRichTextObjectList; class WXDLLIMPEXP_RICHTEXT wxRichTextLine; class WXDLLIMPEXP_RICHTEXT wxRichTextParagraph; -class WXDLLIMPEXP_RICHTEXT wxRichTextFragment; class WXDLLIMPEXP_RICHTEXT wxRichTextFileHandler; class WXDLLIMPEXP_RICHTEXT wxRichTextStyleSheet; class WXDLLIMPEXP_RICHTEXT wxTextAttrEx; @@ -757,6 +760,9 @@ public: /// Copy void Copy(const wxRichTextCompositeObject& obj); + /// Assignment + void operator= (const wxRichTextCompositeObject& obj) { Copy(obj); } + /// Append a child, returning the position size_t AppendChild(wxRichTextObject* child) ; @@ -827,7 +833,7 @@ public: // Constructors wxRichTextParagraphLayoutBox(wxRichTextObject* parent = NULL); - wxRichTextParagraphLayoutBox(const wxRichTextParagraphLayoutBox& obj):wxRichTextBox() { Init(); Copy(obj); } + wxRichTextParagraphLayoutBox(const wxRichTextParagraphLayoutBox& obj): wxRichTextBox() { Init(); Copy(obj); } // Overrideables @@ -855,6 +861,10 @@ public: /// Get the associated control. wxRichTextCtrl* GetRichTextCtrl() const { return m_ctrl; } + /// Get/set whether the last paragraph is partial or complete + void SetPartialParagraph(bool partialPara) { m_partialParagraph = partialPara; } + bool GetPartialParagraph() const { return m_partialParagraph; } + // Operations /// Initialize the object. @@ -962,10 +972,10 @@ public: /// Insert fragment into this box at the given position. If partialParagraph is true, /// it is assumed that the last (or only) paragraph is just a piece of data with no paragraph /// marker. - virtual bool InsertFragment(long position, wxRichTextFragment& fragment); + virtual bool InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment); /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'. - virtual bool CopyFragment(const wxRichTextRange& range, wxRichTextFragment& fragment); + virtual bool CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment); /// Apply the style sheet to the buffer, for example if the styles have changed. virtual bool ApplyStyleSheet(wxRichTextStyleSheet* styleSheet); @@ -973,6 +983,9 @@ public: /// Copy void Copy(const wxRichTextParagraphLayoutBox& obj); + /// Assignment + void operator= (const wxRichTextParagraphLayoutBox& obj) { Copy(obj); } + /// Calculate ranges virtual void UpdateRanges() { long end; CalculateRange(0, end); } @@ -1004,47 +1017,10 @@ protected: wxTextAttrEx m_defaultAttributes; /// The invalidated range that will need full layout - wxRichTextRange m_invalidRange; -}; - -/*! - * wxRichTextFragment class declaration - * This is a lind of paragraph layout box used for storing - * paragraphs for Undo/Redo, for example. - */ - -class WXDLLIMPEXP_RICHTEXT wxRichTextFragment: public wxRichTextParagraphLayoutBox -{ - DECLARE_DYNAMIC_CLASS(wxRichTextFragment) -public: -// Constructors - - wxRichTextFragment() { Init(); } - wxRichTextFragment(const wxRichTextFragment& obj):wxRichTextParagraphLayoutBox() { Init(); Copy(obj); } - -// Accessors - - /// Get/set whether the last paragraph is partial or complete - void SetPartialParagraph(bool partialPara) { m_partialParagraph = partialPara; } - bool GetPartialParagraph() const { return m_partialParagraph; } - -// Overrideables - -// Operations - - /// Initialise - void Init(); - - /// Copy - void Copy(const wxRichTextFragment& obj); - - /// Clone - virtual wxRichTextObject* Clone() const { return new wxRichTextFragment(*this); } - -protected: + wxRichTextRange m_invalidRange; // Is the last paragraph partial or complete? - bool m_partialParagraph; + bool m_partialParagraph; }; /*! @@ -1143,7 +1119,7 @@ public: wxRichTextParagraph(wxRichTextObject* parent = NULL, wxTextAttrEx* style = NULL); wxRichTextParagraph(const wxString& text, wxRichTextObject* parent = NULL, wxTextAttrEx* style = NULL); virtual ~wxRichTextParagraph(); - wxRichTextParagraph(const wxRichTextParagraph& obj):wxRichTextBox() { Copy(obj); } + wxRichTextParagraph(const wxRichTextParagraph& obj): wxRichTextBox() { Copy(obj); } // Overrideables @@ -1245,7 +1221,7 @@ public: // Constructors wxRichTextPlainText(const wxString& text = wxEmptyString, wxRichTextObject* parent = NULL, wxTextAttrEx* style = NULL); - wxRichTextPlainText(const wxRichTextPlainText& obj):wxRichTextObject() { Copy(obj); } + wxRichTextPlainText(const wxRichTextPlainText& obj): wxRichTextObject() { Copy(obj); } // Overrideables @@ -1397,10 +1373,10 @@ class WXDLLIMPEXP_RICHTEXT wxRichTextImage: public wxRichTextObject public: // Constructors - wxRichTextImage(wxRichTextObject* parent = NULL):wxRichTextObject(parent) { } + wxRichTextImage(wxRichTextObject* parent = NULL): wxRichTextObject(parent) { } wxRichTextImage(const wxImage& image, wxRichTextObject* parent = NULL); wxRichTextImage(const wxRichTextImageBlock& imageBlock, wxRichTextObject* parent = NULL); - wxRichTextImage(const wxRichTextImage& obj):wxRichTextObject() { Copy(obj); } + wxRichTextImage(const wxRichTextImage& obj): wxRichTextObject() { Copy(obj); } // Overrideables @@ -1465,7 +1441,7 @@ public: // Constructors wxRichTextBuffer() { Init(); } - wxRichTextBuffer(const wxRichTextBuffer& obj):wxRichTextParagraphLayoutBox() { Init(); Copy(obj); } + wxRichTextBuffer(const wxRichTextBuffer& obj): wxRichTextParagraphLayoutBox() { Init(); Copy(obj); } virtual ~wxRichTextBuffer() ; // Accessors @@ -1501,7 +1477,7 @@ public: virtual bool SaveFile(wxOutputStream& stream, int type = wxRICHTEXT_TYPE_ANY); /// Convenience function to add a paragraph of text - virtual wxRichTextRange AddParagraph(const wxString& text) { Modify(); return wxRichTextParagraphLayoutBox::AddParagraph(text); } + virtual wxRichTextRange AddParagraph(const wxString& text, wxTextAttrEx* paraStyle = NULL) { Modify(); return wxRichTextParagraphLayoutBox::AddParagraph(text, paraStyle); } /// Begin collapsing undo/redo commands. Note that this may not work properly /// if combining commands that delete or insert content, changing ranges for @@ -1649,11 +1625,14 @@ public: // Implementation /// Copy - void Copy(const wxRichTextBuffer& obj) { wxRichTextBox::Copy(obj); } + void Copy(const wxRichTextBuffer& obj); /// Clone virtual wxRichTextObject* Clone() const { return new wxRichTextBuffer(*this); } + /// Submit command to insert paragraphs + bool InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags = 0); + /// Submit command to insert the given text bool InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags = 0); @@ -1807,11 +1786,11 @@ public: void UpdateAppearance(long caretPosition, bool sendUpdateEvent = false); /// Replace the buffer paragraphs with the given fragment. - void ApplyParagraphs(const wxRichTextFragment& fragment); + void ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment); /// Get the fragments - wxRichTextFragment& GetNewParagraphs() { return m_newParagraphs; } - wxRichTextFragment& GetOldParagraphs() { return m_oldParagraphs; } + wxRichTextParagraphLayoutBox& GetNewParagraphs() { return m_newParagraphs; } + wxRichTextParagraphLayoutBox& GetOldParagraphs() { return m_oldParagraphs; } /// Set/get the position used for e.g. insertion void SetPosition(long pos) { m_position = pos; } @@ -1835,10 +1814,10 @@ protected: wxRichTextCtrl* m_ctrl; // Stores the new paragraphs - wxRichTextFragment m_newParagraphs; + wxRichTextParagraphLayoutBox m_newParagraphs; // Stores the old paragraphs - wxRichTextFragment m_oldParagraphs; + wxRichTextParagraphLayoutBox m_oldParagraphs; // The affected range wxRichTextRange m_range; @@ -1947,6 +1926,48 @@ protected: }; +#if wxUSE_DATAOBJ + +/*! + * The data object for a wxRichTextBuffer + */ + +class wxRichTextBufferDataObject: public wxDataObjectSimple +{ +public: + // ctor doesn't copy the pointer, so it shouldn't go away while this object + // is alive + wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer = (wxRichTextBuffer*) NULL); + virtual ~wxRichTextBufferDataObject(); + + // after a call to this function, the buffer is owned by the caller and it + // is responsible for deleting it + wxRichTextBuffer* GetRichTextBuffer(); + + // Returns the id for the new data format + static const wxChar* GetRichTextBufferFormatId() { return ms_richTextBufferFormatId; } + + // base class pure virtuals + + virtual wxDataFormat GetPreferredFormat(Direction dir) const; + virtual size_t GetDataSize() const; + virtual bool GetDataHere(void *pBuf) const; + virtual bool SetData(size_t len, const void *buf); + + // prevent warnings + + virtual size_t GetDataSize(const wxDataFormat&) const { return GetDataSize(); } + virtual bool GetDataHere(const wxDataFormat&, void *buf) const { return GetDataHere(buf); } + virtual bool SetData(const wxDataFormat&, size_t len, const void *buf) { return SetData(len, buf); } + +private: + wxDataFormat m_formatRichTextBuffer; // our custom format + wxRichTextBuffer* m_richTextBuffer; // our data + static const wxChar* ms_richTextBufferFormatId; // our format id +}; + +#endif + /*! * Utilities * diff --git a/include/wx/richtext/richtextctrl.h b/include/wx/richtext/richtextctrl.h index bf1e62bafd..c17fbacfa6 100644 --- a/include/wx/richtext/richtextctrl.h +++ b/include/wx/richtext/richtextctrl.h @@ -390,6 +390,9 @@ public: /// Clear the selection virtual void SelectNone(); + /// Select the word at the given character position + virtual bool SelectWord(long position); + /// Get/set the selection range in character positions. -1, -1 means no selection. /// The range is in API convention, i.e. a single character selection is denoted /// by (n, n+1) diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index ce8a05534d..daf5f9944d 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -33,6 +33,7 @@ #include "wx/wfstream.h" #include "wx/mstream.h" #include "wx/sstream.h" +#include "wx/textfile.h" #include "wx/richtext/richtextctrl.h" #include "wx/richtext/richtextstyles.h" @@ -491,6 +492,7 @@ void wxRichTextParagraphLayoutBox::Init() m_rightMargin = 4; m_topMargin = 4; m_bottomMargin = 4; + m_partialParagraph = false; } /// Draw the item @@ -659,6 +661,8 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int styl void wxRichTextParagraphLayoutBox::Copy(const wxRichTextParagraphLayoutBox& obj) { wxRichTextBox::Copy(obj); + + m_partialParagraph = obj.m_partialParagraph; } /// Get/set the size for the given range. @@ -924,21 +928,36 @@ wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text wxRichTextParagraph* lastPara = NULL; wxRichTextRange range(-1, -1); + size_t i = 0; size_t len = text.length(); wxString line; + wxRichTextParagraph* para = new wxRichTextParagraph(wxT(""), this, & style); + if (paraStyle) + para->SetAttributes(*paraStyle); + + AppendChild(para); + + firstPara = para; + lastPara = para; + while (i < len) { wxChar ch = text[i]; if (ch == wxT('\n') || ch == wxT('\r')) { - wxRichTextParagraph* para = new wxRichTextParagraph(line, this, & style); + wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData(); + plainText->SetText(line); + + para = new wxRichTextParagraph(wxT(""), this, & style); if (paraStyle) para->SetAttributes(*paraStyle); AppendChild(para); - if (!firstPara) - firstPara = para; + + //if (!firstPara) + // firstPara = para; + lastPara = para; line = wxEmptyString; } @@ -947,16 +966,14 @@ wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text i ++; } + if (!line.empty()) { - lastPara = new wxRichTextParagraph(line, this, & style); - if (paraStyle) - lastPara->SetAttributes(*paraStyle); - - //wxLogDebug("Para Face = %s", lastPara->GetAttributes().GetFont().GetFaceName()); - AppendChild(lastPara); + wxRichTextPlainText* plainText = (wxRichTextPlainText*) para->GetChildren().GetFirst()->GetData(); + plainText->SetText(line); } +/* if (firstPara) range.SetStart(firstPara->GetRange().GetStart()); else if (lastPara) @@ -966,11 +983,13 @@ wxRichTextRange wxRichTextParagraphLayoutBox::AddParagraphs(const wxString& text range.SetEnd(lastPara->GetRange().GetEnd()); else if (firstPara) range.SetEnd(firstPara->GetRange().GetEnd()); +*/ UpdateRanges(); + SetDirty(false); - return GetRange(); + return wxRichTextRange(firstPara->GetRange().GetStart(), lastPara->GetRange().GetEnd()); } /// Convenience function to add an image @@ -1009,7 +1028,7 @@ wxRichTextRange wxRichTextParagraphLayoutBox::AddImage(const wxImage& image, wxT /// TODO: if fragment is inserted inside styled fragment, must apply that style to /// to the data (if it has a default style, anyway). -bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextFragment& fragment) +bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextParagraphLayoutBox& fragment) { SetDirty(true); @@ -1174,7 +1193,7 @@ bool wxRichTextParagraphLayoutBox::InsertFragment(long position, wxRichTextFragm /// Make a copy of the fragment corresponding to the given range, putting it in 'fragment'. /// If there was an incomplete paragraph at the end, partialParagraph is set to true. -bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextFragment& fragment) +bool wxRichTextParagraphLayoutBox::CopyFragment(const wxRichTextRange& range, wxRichTextParagraphLayoutBox& fragment) { wxRichTextObjectList::compatibility_iterator i = GetChildren().GetFirst(); while (i) @@ -1977,29 +1996,6 @@ bool wxRichTextParagraphLayoutBox::ApplyStyleSheet(wxRichTextStyleSheet* styleSh return foundCount != 0; } - -/*! - * wxRichTextFragment class declaration - * This is a lind of paragraph layout box used for storing - * paragraphs for Undo/Redo, for example. - */ - -IMPLEMENT_DYNAMIC_CLASS(wxRichTextFragment, wxRichTextParagraphLayoutBox) - -/// Initialise -void wxRichTextFragment::Init() -{ - m_partialParagraph = false; -} - -/// Copy -void wxRichTextFragment::Copy(const wxRichTextFragment& obj) -{ - wxRichTextParagraphLayoutBox::Copy(obj); - - m_partialParagraph = obj.m_partialParagraph; -} - /*! * wxRichTextParagraph * This object represents a single paragraph (or in a straight text editor, a line). @@ -2586,8 +2582,6 @@ bool wxRichTextParagraph::FindPosition(wxDC& dc, long index, wxPoint& pt, int* h if (line) pt = pt + line->GetPosition(); - *height = dc.GetCharHeight(); - return true; } @@ -3543,6 +3537,49 @@ void wxRichTextBuffer::Reset() Invalidate(wxRICHTEXT_ALL); } +void wxRichTextBuffer::Copy(const wxRichTextBuffer& obj) +{ + wxRichTextParagraphLayoutBox::Copy(obj); + + m_styleSheet = obj.m_styleSheet; + m_modified = obj.m_modified; + m_batchedCommandDepth = obj.m_batchedCommandDepth; + m_batchedCommand = obj.m_batchedCommand; + m_suppressUndo = obj.m_suppressUndo; +} + +/// Submit command to insert paragraphs +bool wxRichTextBuffer::InsertParagraphsWithUndo(long pos, const wxRichTextParagraphLayoutBox& paragraphs, wxRichTextCtrl* ctrl, int flags) +{ + wxRichTextAction* action = new wxRichTextAction(NULL, _("Insert Text"), wxRICHTEXT_INSERT, this, ctrl, false); + + wxTextAttrEx* p = NULL; + wxTextAttrEx paraAttr; + if (flags & wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE) + { + paraAttr = GetStyleForNewParagraph(pos); + if (!paraAttr.IsDefault()) + p = & paraAttr; + } + +#if wxRICHTEXT_USE_DYNAMIC_STYLES + wxTextAttrEx attr(GetDefaultStyle()); +#else + wxTextAttrEx attr(GetBasicStyle()); + wxRichTextApplyStyle(attr, GetDefaultStyle()); +#endif + + action->GetNewParagraphs() = paragraphs; + action->SetPosition(pos); + + // Set the range we'll need to delete in Undo + action->SetRange(wxRichTextRange(pos, pos + paragraphs.GetRange().GetEnd() - 1)); + + SubmitAction(action); + + return true; +} + /// Submit command to insert the given text bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRichTextCtrl* ctrl, int flags) { @@ -3565,13 +3602,20 @@ bool wxRichTextBuffer::InsertTextWithUndo(long pos, const wxString& text, wxRich #endif action->GetNewParagraphs().AddParagraphs(text, p); - if (action->GetNewParagraphs().GetChildCount() == 1 && text.Find(wxT("\n")) == wxNOT_FOUND) + + int length = action->GetNewParagraphs().GetRange().GetLength(); + + if (text.length() > 0 && text.Last() != wxT('\n')) + { + // Don't count the newline when undoing + length --; action->GetNewParagraphs().SetPartialParagraph(true); + } action->SetPosition(pos); // Set the range we'll need to delete in Undo - action->SetRange(wxRichTextRange(pos, pos + text.length() - 1)); + action->SetRange(wxRichTextRange(pos, pos + length - 1)); SubmitAction(action); @@ -4062,12 +4106,14 @@ wxRichTextFileHandler *wxRichTextBuffer::FindHandlerFilenameOrType(const wxStrin { if (imageType != wxRICHTEXT_TYPE_ANY) return FindHandler(imageType); - else + else if (!filename.IsEmpty()) { wxString path, file, ext; wxSplitPath(filename, & path, & file, & ext); return FindHandler(ext, imageType); } + else + return NULL; } @@ -4234,12 +4280,41 @@ bool wxRichTextBuffer::CopyToClipboard(const wxRichTextRange& range) { bool success = false; #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ - wxString text = GetTextForRange(range); + if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open()) { - success = wxTheClipboard->SetData(new wxTextDataObject(text)); + wxTheClipboard->Clear(); + + // Add composite object + + wxDataObjectComposite* compositeObject = new wxDataObjectComposite(); + + { + wxString text = GetTextForRange(range); + +#ifdef __WXMSW__ + text = wxTextFile::Translate(text, wxTextFileType_Dos); +#endif + + compositeObject->Add(new wxTextDataObject(text), false /* not preferred */); + } + + // Add rich text buffer data object. This needs the XML handler to be present. + + if (FindHandler(wxRICHTEXT_TYPE_XML)) + { + wxRichTextBuffer* richTextBuf = new wxRichTextBuffer; + CopyFragment(range, *richTextBuf); + + compositeObject->Add(new wxRichTextBufferDataObject(richTextBuf), true /* preferred */); + } + + if (wxTheClipboard->SetData(compositeObject)) + success = true; + wxTheClipboard->Close(); } + #else wxUnusedVar(range); #endif @@ -4255,7 +4330,18 @@ bool wxRichTextBuffer::PasteFromClipboard(long position) { if (wxTheClipboard->Open()) { - if (wxTheClipboard->IsSupported(wxDF_TEXT)) + if (wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId()))) + { + wxRichTextBufferDataObject data; + wxTheClipboard->GetData(data); + wxRichTextBuffer* richTextBuffer = data.GetRichTextBuffer(); + if (richTextBuffer) + { + InsertParagraphsWithUndo(position+1, *richTextBuffer, GetRichTextCtrl(), wxRICHTEXT_INSERT_WITH_PREVIOUS_PARAGRAPH_STYLE); + delete richTextBuffer; + } + } + else if (wxTheClipboard->IsSupported(wxDF_TEXT) || wxTheClipboard->IsSupported(wxDF_UNICODETEXT)) { wxTextDataObject data; wxTheClipboard->GetData(data); @@ -4305,7 +4391,9 @@ bool wxRichTextBuffer::CanPasteFromClipboard() const #if wxUSE_CLIPBOARD && wxUSE_DATAOBJ if (!wxTheClipboard->IsOpened() && wxTheClipboard->Open()) { - if (wxTheClipboard->IsSupported(wxDF_TEXT) || wxTheClipboard->IsSupported(wxDF_BITMAP)) + if (wxTheClipboard->IsSupported(wxDF_TEXT) || wxTheClipboard->IsSupported(wxDF_UNICODETEXT) || + wxTheClipboard->IsSupported(wxDataFormat(wxRichTextBufferDataObject::GetRichTextBufferFormatId())) || + wxTheClipboard->IsSupported(wxDF_BITMAP)) { canPaste = true; } @@ -4434,10 +4522,17 @@ bool wxRichTextAction::Do() m_buffer->UpdateRanges(); m_buffer->Invalidate(GetRange()); - long newCaretPosition = GetPosition() + m_newParagraphs.GetRange().GetLength() - 1; + long newCaretPosition = GetPosition() + m_newParagraphs.GetRange().GetLength(); + + // Character position to caret position + newCaretPosition --; + + // Don't take into account the last newline if (m_newParagraphs.GetPartialParagraph()) newCaretPosition --; + newCaretPosition = wxMin(newCaretPosition, (m_buffer->GetRange().GetEnd()-1)); + UpdateAppearance(newCaretPosition, true /* send update event */); break; @@ -4533,7 +4628,7 @@ void wxRichTextAction::UpdateAppearance(long caretPosition, bool sendUpdateEvent } /// Replace the buffer paragraphs with the new ones. -void wxRichTextAction::ApplyParagraphs(const wxRichTextFragment& fragment) +void wxRichTextAction::ApplyParagraphs(const wxRichTextParagraphLayoutBox& fragment) { wxRichTextObjectList::compatibility_iterator node = fragment.GetChildren().GetFirst(); while (node) @@ -5817,7 +5912,7 @@ bool wxRichTextImageBlock::Load(wxImage& image) return false; // Read in the image. -#if 1 +#if wxUSE_STREAMS wxMemoryInputStream mstream(m_data, m_dataSize); bool success = image.LoadFile(mstream, GetImageType()); #else @@ -5877,7 +5972,6 @@ bool wxRichTextImageBlock::ReadHex(wxInputStream& stream, int length, int imageT return true; } - // Allocate and read from stream as a block of memory unsigned char* wxRichTextImageBlock::ReadBlock(wxInputStream& stream, size_t size) { @@ -5917,5 +6011,123 @@ bool wxRichTextImageBlock::WriteBlock(const wxString& filename, unsigned char* b return WriteBlock(outStream, block, size); } +#if wxUSE_DATAOBJ + +/*! + * The data object for a wxRichTextBuffer + */ + +const wxChar *wxRichTextBufferDataObject::ms_richTextBufferFormatId = wxT("wxShape"); + +wxRichTextBufferDataObject::wxRichTextBufferDataObject(wxRichTextBuffer* richTextBuffer) +{ + m_richTextBuffer = richTextBuffer; + + // this string should uniquely identify our format, but is otherwise + // arbitrary + m_formatRichTextBuffer.SetId(GetRichTextBufferFormatId()); + + SetFormat(m_formatRichTextBuffer); +} + +wxRichTextBufferDataObject::~wxRichTextBufferDataObject() +{ + delete m_richTextBuffer; +} + +// after a call to this function, the richTextBuffer is owned by the caller and it +// is responsible for deleting it! +wxRichTextBuffer* wxRichTextBufferDataObject::GetRichTextBuffer() +{ + wxRichTextBuffer* richTextBuffer = m_richTextBuffer; + m_richTextBuffer = NULL; + + return richTextBuffer; +} + +wxDataFormat wxRichTextBufferDataObject::GetPreferredFormat(Direction WXUNUSED(dir)) const +{ + return m_formatRichTextBuffer; +} + +size_t wxRichTextBufferDataObject::GetDataSize() const +{ + if (!m_richTextBuffer) + return 0; + + wxString bufXML; + + { + wxStringOutputStream stream(& bufXML); + if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML)) + { + wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler.")); + return 0; + } + } + +#if wxUSE_UNICODE + wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8); + return strlen(buffer) + 1; +#else + return bufXML.Length()+1; +#endif +} + +bool wxRichTextBufferDataObject::GetDataHere(void *pBuf) const +{ + if (!pBuf || !m_richTextBuffer) + return false; + + wxString bufXML; + + { + wxStringOutputStream stream(& bufXML); + if (!m_richTextBuffer->SaveFile(stream, wxRICHTEXT_TYPE_XML)) + { + wxLogError(wxT("Could not write the buffer to an XML stream.\nYou may have forgotten to add the XML file handler.")); + return 0; + } + } + +#if wxUSE_UNICODE + wxCharBuffer buffer = bufXML.mb_str(wxConvUTF8); + size_t len = strlen(buffer); + memcpy((char*) pBuf, (const char*) buffer, len); + ((char*) pBuf)[len] = 0; +#else + size_t len = bufXML.Length(); + memcpy((char*) pBuf, (const char*) bufXML.c_str(), len); + ((char*) pBuf)[len] = 0; +#endif + + return true; +} + +bool wxRichTextBufferDataObject::SetData(size_t WXUNUSED(len), const void *buf) +{ + delete m_richTextBuffer; + m_richTextBuffer = NULL; + + wxString bufXML((const char*) buf, wxConvUTF8); + + m_richTextBuffer = new wxRichTextBuffer; + + wxStringInputStream stream(bufXML); + if (!m_richTextBuffer->LoadFile(stream, wxRICHTEXT_TYPE_XML)) + { + wxLogError(wxT("Could not read the buffer from an XML stream.\nYou may have forgotten to add the XML file handler.")); + + delete m_richTextBuffer; + m_richTextBuffer = NULL; + + return false; + } + return true; +} + +#endif + // wxUSE_DATAOBJ + #endif // wxUSE_RICHTEXT diff --git a/src/richtext/richtextctrl.cpp b/src/richtext/richtextctrl.cpp index b77d1a2fc1..a795c052f7 100644 --- a/src/richtext/richtextctrl.cpp +++ b/src/richtext/richtextctrl.cpp @@ -329,19 +329,6 @@ void wxRichTextCtrl::OnLeftClick(wxMouseEvent& event) MoveCaret(position, caretAtLineStart); SetDefaultStyleToCursorStyle(); - -#if 0 - wxWindow* p = GetParent(); - while (p && !p->IsKindOf(CLASSINFO(wxFrame))) - p = p->GetParent(); - - wxFrame* frame = wxDynamicCast(p, wxFrame); - if (frame) - { - wxString msg = wxString::Format(wxT("Found position %ld"), position); - frame->SetStatusText(msg, 1); - } -#endif } event.Skip(); @@ -417,6 +404,7 @@ void wxRichTextCtrl::OnRightClick(wxMouseEvent& event) /// Left-double-click void wxRichTextCtrl::OnLeftDClick(wxMouseEvent& event) { + SelectWord(GetCaretPosition()+1); event.Skip(); } @@ -1620,6 +1608,60 @@ void wxRichTextCtrl::SelectNone() m_selectionAnchor = -2; } +static bool wxIsWordDelimiter(const wxString& text) +{ + static wxString delimiters = wxT(" ,.:;!?-\"'~£$%^&*()_+-=`¬{}[]@#<>/\\|"); + return !text.IsEmpty() && delimiters.Find(text) != wxNOT_FOUND; +} + +/// Select the word at the given character position +bool wxRichTextCtrl::SelectWord(long position) +{ + if (position < 0 || position > GetBuffer().GetRange().GetEnd()) + return false; + + wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(position); + if (!para) + return false; + + long positionStart = position; + long positionEnd = position; + + for (positionStart = position; positionStart >= para->GetRange().GetStart(); positionStart --) + { + wxString text = GetBuffer().GetTextForRange(wxRichTextRange(positionStart, positionStart)); + if (wxIsWordDelimiter(text)) + { + positionStart ++; + break; + } + } + if (positionStart < para->GetRange().GetStart()) + positionStart = para->GetRange().GetStart(); + + for (positionEnd = position; positionEnd < para->GetRange().GetEnd(); positionEnd ++) + { + wxString text = GetBuffer().GetTextForRange(wxRichTextRange(positionEnd, positionEnd)); + if (wxIsWordDelimiter(text)) + { + positionEnd --; + break; + } + } + if (positionEnd >= para->GetRange().GetEnd()) + positionEnd = para->GetRange().GetEnd(); + + SetSelection(positionStart, positionEnd+1); + + if (positionStart >= 0) + { + MoveCaret(positionStart-1, true); + SetDefaultStyleToCursorStyle(); + } + + return true; +} + wxString wxRichTextCtrl::GetStringSelection() const { long from, to; @@ -1743,9 +1785,9 @@ void wxRichTextCtrl::WriteText(const wxString& value) void wxRichTextCtrl::DoWriteText(const wxString& value, bool WXUNUSED(selectionOnly)) { - wxString valueDos = wxTextFile::Translate(value, wxTextFileType_Unix); + wxString valueUnix = wxTextFile::Translate(value, wxTextFileType_Unix); - GetBuffer().InsertTextWithUndo(m_caretPosition+1, valueDos, this); + GetBuffer().InsertTextWithUndo(m_caretPosition+1, valueUnix, this); } void wxRichTextCtrl::AppendText(const wxString& text) @@ -1951,6 +1993,7 @@ void wxRichTextCtrl::DoSetSelection(long from, long to, bool WXUNUSED(scrollCare { m_selectionAnchor = from; m_selectionRange.SetRange(from, to-1); + Refresh(false); PositionCaret(); } @@ -2598,22 +2641,16 @@ bool wxRichTextCtrl::ApplyUnderlineToSelection() /// Is all of the selection aligned according to the specified flag? bool wxRichTextCtrl::IsSelectionAligned(wxTextAttrAlignment alignment) { + wxRichTextRange range; if (HasSelection()) - { - wxRichTextRange range = GetInternalSelectionRange(); - wxRichTextAttr attr; - attr.SetAlignment(alignment); - - return HasParagraphAttributes(range, attr); - } + range = GetInternalSelectionRange(); else - { - // If no selection, then we need to get information from the current paragraph. - wxRichTextParagraph* para = GetBuffer().GetParagraphAtPosition(GetCaretPosition()+1); - if (para) - return para->GetAttributes().GetAlignment() == alignment; - } - return false; + range = wxRichTextRange(GetCaretPosition()+1, GetCaretPosition()+1); + + wxRichTextAttr attr; + attr.SetAlignment(alignment); + + return HasParagraphAttributes(range, attr); } /// Apply alignment to the selection diff --git a/src/richtext/richtextxml.cpp b/src/richtext/richtextxml.cpp index b2205170ed..4bdbf89826 100644 --- a/src/richtext/richtextxml.cpp +++ b/src/richtext/richtextxml.cpp @@ -30,6 +30,7 @@ #include "wx/wfstream.h" #include "wx/sstream.h" #include "wx/txtstrm.h" +#include "wx/tokenzr.h" #include "wx/xml/xml.h" IMPLEMENT_DYNAMIC_CLASS(wxRichTextXMLHandler, wxRichTextFileHandler) @@ -98,6 +99,9 @@ bool wxRichTextXMLHandler::ImportXML(wxRichTextBuffer* buffer, wxXmlNode* node) if (name == wxT("paragraphlayout")) { + wxString partial = node->GetPropVal(wxT("partialparagraph"), wxEmptyString); + if (partial == wxT("true")) + buffer->SetPartialParagraph(true); } else if (name == wxT("paragraph")) { @@ -506,6 +510,9 @@ bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxMBConv* convMem, wxString style = CreateStyle(obj.GetAttributes(), isPara); + if (objectName == wxT("paragraphlayout") && ((wxRichTextParagraphLayoutBox&) obj).GetPartialParagraph()) + style << wxT(" partialparagraph=\"true\""); + OutputString(stream, style + wxT(">"), convMem, convFile); wxRichTextCompositeObject& composite = (wxRichTextCompositeObject&) obj; @@ -529,23 +536,34 @@ bool wxRichTextXMLHandler::ExportXML(wxOutputStream& stream, wxMBConv* convMem, wxString wxRichTextXMLHandler::CreateStyle(const wxTextAttrEx& attr, bool isPara) { wxString str; - if (attr.GetTextColour().Ok()) + if (attr.HasTextColour() && attr.GetTextColour().Ok()) { str << wxT(" textcolor=\"#") << ColourToHexString(attr.GetTextColour()) << wxT("\""); } - if (attr.GetBackgroundColour().Ok()) + if (attr.HasBackgroundColour() && attr.GetBackgroundColour().Ok()) { str << wxT(" bgcolor=\"#") << ColourToHexString(attr.GetBackgroundColour()) << wxT("\""); } if (attr.GetFont().Ok()) { - str << wxT(" fontsize=\"") << attr.GetFont().GetPointSize() << wxT("\""); - str << wxT(" fontfamily=\"") << attr.GetFont().GetFamily() << wxT("\""); - str << wxT(" fontstyle=\"") << attr.GetFont().GetStyle() << wxT("\""); - str << wxT(" fontweight=\"") << attr.GetFont().GetWeight() << wxT("\""); - str << wxT(" fontunderlined=\"") << (int) attr.GetFont().GetUnderlined() << wxT("\""); - str << wxT(" fontface=\"") << attr.GetFont().GetFaceName() << wxT("\""); + if (attr.HasSize()) + str << wxT(" fontsize=\"") << attr.GetFont().GetPointSize() << wxT("\""); + + //if (attr.HasFamily()) + // str << wxT(" fontfamily=\"") << attr.GetFont().GetFamily() << wxT("\""); + + if (attr.HasItalic()) + str << wxT(" fontstyle=\"") << attr.GetFont().GetStyle() << wxT("\""); + + if (attr.HasWeight()) + str << wxT(" fontweight=\"") << attr.GetFont().GetWeight() << wxT("\""); + + if (attr.HasUnderlined()) + str << wxT(" fontunderlined=\"") << (int) attr.GetFont().GetUnderlined() << wxT("\""); + + if (attr.HasFaceName()) + str << wxT(" fontface=\"") << attr.GetFont().GetFaceName() << wxT("\""); } if (!attr.GetCharacterStyleName().empty()) @@ -553,19 +571,51 @@ wxString wxRichTextXMLHandler::CreateStyle(const wxTextAttrEx& attr, bool isPara if (isPara) { - str << wxT(" alignment=\"") << (int) attr.GetAlignment() << wxT("\""); - str << wxT(" leftindent=\"") << (int) attr.GetLeftIndent() << wxT("\""); - str << wxT(" leftsubindent=\"") << (int) attr.GetLeftSubIndent() << wxT("\""); - str << wxT(" rightindent=\"") << (int) attr.GetRightIndent() << wxT("\""); - str << wxT(" parspacingafter=\"") << (int) attr.GetParagraphSpacingAfter() << wxT("\""); - str << wxT(" parspacingbefore=\"") << (int) attr.GetParagraphSpacingBefore() << wxT("\""); - str << wxT(" linespacing=\"") << (int) attr.GetLineSpacing() << wxT("\""); - str << wxT(" bulletstyle=\"") << (int) attr.GetBulletStyle() << wxT("\""); - str << wxT(" bulletnumber=\"") << (int) attr.GetBulletNumber() << wxT("\""); - str << wxT(" bulletsymbol=\"") << wxString(attr.GetBulletSymbol()) << wxT("\""); + if (attr.HasAlignment()) + str << wxT(" alignment=\"") << (int) attr.GetAlignment() << wxT("\""); + + if (attr.HasLeftIndent()) + { + str << wxT(" leftindent=\"") << (int) attr.GetLeftIndent() << wxT("\""); + str << wxT(" leftsubindent=\"") << (int) attr.GetLeftSubIndent() << wxT("\""); + } + + if (attr.HasRightIndent()) + str << wxT(" rightindent=\"") << (int) attr.GetRightIndent() << wxT("\""); + + if (attr.HasParagraphSpacingAfter()) + str << wxT(" parspacingafter=\"") << (int) attr.GetParagraphSpacingAfter() << wxT("\""); + + if (attr.HasParagraphSpacingBefore()) + str << wxT(" parspacingbefore=\"") << (int) attr.GetParagraphSpacingBefore() << wxT("\""); + + if (attr.HasLineSpacing()) + str << wxT(" linespacing=\"") << (int) attr.GetLineSpacing() << wxT("\""); + + if (attr.HasBulletStyle()) + str << wxT(" bulletstyle=\"") << (int) attr.GetBulletStyle() << wxT("\""); + + if (attr.HasBulletNumber()) + str << wxT(" bulletnumber=\"") << (int) attr.GetBulletNumber() << wxT("\""); + + if (attr.HasBulletSymbol()) + str << wxT(" bulletsymbol=\"") << wxString(attr.GetBulletSymbol()) << wxT("\""); if (!attr.GetParagraphStyleName().empty()) str << wxT(" parstyle=\"") << wxString(attr.GetParagraphStyleName()) << wxT("\""); + + if (attr.HasTabs()) + { + str << wxT(" tabs=\""); + size_t i; + for (i = 0; i < attr.GetTabs().GetCount(); i++) + { + if (i > 0) + str << wxT(","); + str << attr.GetTabs()[i]; + } + str << wxT("\""); + } } return str; @@ -581,29 +631,52 @@ bool wxRichTextXMLHandler::GetStyle(wxTextAttrEx& attr, wxXmlNode* node, bool is int fontStyle = wxNORMAL; bool fontUnderlined = false; - fontFacename = node->GetPropVal(wxT("fontface"), wxEmptyString); + int fontFlags = 0; - wxString value = node->GetPropVal(wxT("fontfamily"), wxEmptyString); - if (!value.empty()) - fontFamily = wxAtoi(value); + fontFacename = node->GetPropVal(wxT("fontface"), wxEmptyString); + if (!fontFacename.IsEmpty()) + fontFlags |= wxTEXT_ATTR_FONT_FACE; + + wxString value; + //value = node->GetPropVal(wxT("fontfamily"), wxEmptyString); + //if (!value.empty()) + // fontFamily = wxAtoi(value); value = node->GetPropVal(wxT("fontstyle"), wxEmptyString); if (!value.empty()) + { fontStyle = wxAtoi(value); + fontFlags |= wxTEXT_ATTR_FONT_ITALIC; + } value = node->GetPropVal(wxT("fontsize"), wxEmptyString); if (!value.empty()) + { fontSize = wxAtoi(value); + fontFlags |= wxTEXT_ATTR_FONT_SIZE; + } value = node->GetPropVal(wxT("fontweight"), wxEmptyString); if (!value.empty()) + { fontWeight = wxAtoi(value); + fontFlags |= wxTEXT_ATTR_FONT_WEIGHT; + } value = node->GetPropVal(wxT("fontunderlined"), wxEmptyString); if (!value.empty()) + { fontUnderlined = wxAtoi(value) != 0; + fontFlags |= wxTEXT_ATTR_FONT_UNDERLINE; + } - attr.SetFont(* wxTheFontList->FindOrCreateFont(fontSize, fontFamily, fontStyle, fontWeight, fontUnderlined, fontFacename)); + attr.SetFlags(fontFlags); + + if (attr.HasFlag(wxTEXT_ATTR_FONT)) + attr.SetFont(* wxTheFontList->FindOrCreateFont(fontSize, fontFamily, fontStyle, fontWeight, fontUnderlined, fontFacename)); + + // Restore correct font flags + attr.SetFlags(fontFlags); value = node->GetPropVal(wxT("textcolor"), wxEmptyString); if (!value.empty()) @@ -636,13 +709,24 @@ bool wxRichTextXMLHandler::GetStyle(wxTextAttrEx& attr, wxXmlNode* node, bool is int leftSubIndent = 0; int leftIndent = 0; + bool hasLeftIndent = false; + value = node->GetPropVal(wxT("leftindent"), wxEmptyString); if (!value.empty()) + { leftIndent = wxAtoi(value); + hasLeftIndent = true; + } + value = node->GetPropVal(wxT("leftsubindent"), wxEmptyString); if (!value.empty()) + { leftSubIndent = wxAtoi(value); - attr.SetLeftIndent(leftIndent, leftSubIndent); + hasLeftIndent = true; + } + + if (hasLeftIndent) + attr.SetLeftIndent(leftIndent, leftSubIndent); value = node->GetPropVal(wxT("rightindent"), wxEmptyString); if (!value.empty()) @@ -675,6 +759,19 @@ bool wxRichTextXMLHandler::GetStyle(wxTextAttrEx& attr, wxXmlNode* node, bool is value = node->GetPropVal(wxT("parstyle"), wxEmptyString); if (!value.empty()) attr.SetParagraphStyleName(value); + + value = node->GetPropVal(wxT("tabs"), wxEmptyString); + if (!value.empty()) + { + wxArrayInt tabs; + wxStringTokenizer tkz(value, wxT(",")); + while (tkz.HasMoreTokens()) + { + wxString token = tkz.GetNextToken(); + tabs.Add(wxAtoi(token)); + } + attr.SetTabs(tabs); + } } return true;