From: Julian Smart Date: Fri, 27 Oct 2006 08:43:44 +0000 (+0000) Subject: First cut at printing support for wxRichTextCtrl X-Git-Url: https://git.saurik.com/wxWidgets.git/commitdiff_plain/44219ff04fedc5ede9842494fa29fbcb1fec04b0 First cut at printing support for wxRichTextCtrl git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@42494 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775 --- diff --git a/include/wx/richtext/richtextbuffer.h b/include/wx/richtext/richtextbuffer.h index 6d1a07395f..8ea8b0bbd7 100644 --- a/include/wx/richtext/richtextbuffer.h +++ b/include/wx/richtext/richtextbuffer.h @@ -101,6 +101,7 @@ class WXDLLIMPEXP_RICHTEXT wxTextAttrEx; class WXDLLIMPEXP_RICHTEXT wxRichTextListStyleDefinition; class WXDLLIMPEXP_RICHTEXT wxRichTextEvent; class WXDLLIMPEXP_RICHTEXT wxRichTextRenderer; +class WXDLLIMPEXP_RICHTEXT wxRichTextBuffer; /*! * Flags determining the available space, passed to Layout @@ -115,6 +116,15 @@ class WXDLLIMPEXP_RICHTEXT wxRichTextRenderer; // the rect passed to Layout. #define wxRICHTEXT_LAYOUT_SPECIFIED_RECT 0x10 +/*! + * Flags to pass to Draw + */ + +// Ignore paragraph cache optimization, e.g. for printing purposes +// where one line may be drawn higher (on the next page) compared +// with the previous line +#define wxRICHTEXT_DRAW_IGNORE_CACHE 0x01 + /*! * Flags returned from hit-testing */ @@ -714,6 +724,9 @@ public: void SetDescent(int descent) { m_descent = descent; } int GetDescent() const { return m_descent; } + /// Gets the containing buffer + wxRichTextBuffer* GetBuffer() const; + // Operations /// Clone the object @@ -727,8 +740,9 @@ public: void Reference() { m_refCount ++; } void Dereference(); - /// Convert units in tends of a millimetre to device units - static int ConvertTenthsMMToPixels(wxDC& dc, int units); + /// Convert units in tenths of a millimetre to device units + int ConvertTenthsMMToPixels(wxDC& dc, int units); + static int ConvertTenthsMMToPixels(int ppi, int units); protected: wxSize m_size; @@ -1861,6 +1875,11 @@ public: /// Factor to multiply by character height to get a reasonable bullet size static float GetBulletProportion() { return sm_bulletProportion; } static void SetBulletProportion(float prop) { sm_bulletProportion = prop; } + + /// Scale factor for calculating dimensions + double GetScale() const { return m_scale; } + void SetScale(double scale) { m_scale = scale; } + protected: /// Command processor @@ -1904,6 +1923,9 @@ protected: /// Factor to multiply by character height to get a reasonable bullet size static float sm_bulletProportion; + + /// Scaling factor in use: needed to calculate correct dimensions when printing + double m_scale; }; /*! diff --git a/include/wx/richtext/richtextprint.h b/include/wx/richtext/richtextprint.h new file mode 100644 index 0000000000..69d1a43e2e --- /dev/null +++ b/include/wx/richtext/richtextprint.h @@ -0,0 +1,237 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: wx/richtext/richtextprint.h +// Purpose: Rich text printing classes +// Author: Julian Smart +// Created: 2006-10-23 +// RCS-ID: $Id$ +// Copyright: (c) Julian Smart +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +#ifndef _WX_RICHTEXTPRINT_H_ +#define _WX_RICHTEXTPRINT_H_ + +#include "wx/defs.h" + +#if wxUSE_RICHTEXT & wxUSE_PRINTING_ARCHITECTURE + +#include "wx/richtext/richtextbuffer.h" + +#include "wx/print.h" +#include "wx/printdlg.h" + +#define wxRICHTEXT_PRINT_MAX_PAGES 99999 + +// Header/footer page identifiers +enum wxRichTextOddEvenPage { + wxRICHTEXT_PAGE_ODD, + wxRICHTEXT_PAGE_EVEN, + wxRICHTEXT_PAGE_ALL, +}; + +// Header/footer text locations +enum wxRichTextPageLocation { + wxRICHTEXT_PAGE_LEFT, + wxRICHTEXT_PAGE_CENTRE, + wxRICHTEXT_PAGE_RIGHT +}; + +/*! + * Header/footer data + */ + +class WXDLLIMPEXP_RICHTEXT wxRichTextHeaderFooterData: public wxObject +{ +public: + wxRichTextHeaderFooterData() { Init(); } + wxRichTextHeaderFooterData(const wxRichTextHeaderFooterData& data) { Copy(data); } + + /// Initialise + void Init() { m_headerMargin = 20; m_footerMargin = 20; m_showOnFirstPage = true; } + + /// Copy + void Copy(const wxRichTextHeaderFooterData& data); + + /// Assignment + void operator= (const wxRichTextHeaderFooterData& data) { Copy(data); } + + /// Set/get header text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT + void SetHeaderText(const wxString& text, wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_ALL, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE); + wxString GetHeaderText(wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_EVEN, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE) const; + + /// Set/get footer text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT + void SetFooterText(const wxString& text, wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_ALL, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE); + wxString GetFooterText(wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_EVEN, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE) const; + + /// Set/get text + void SetText(const wxString& text, int headerFooter, wxRichTextOddEvenPage page, wxRichTextPageLocation location); + wxString GetText(int headerFooter, wxRichTextOddEvenPage page, wxRichTextPageLocation location) const; + + /// Set/get margins between text and header or footer, in tenths of a millimeter + void SetMargins(int headerMargin, int footerMargin) { m_headerMargin = headerMargin; m_footerMargin = footerMargin; } + int GetHeaderMargin() const { return m_headerMargin; } + int GetFooterMargin() const { return m_footerMargin; } + + /// Set/get whether to show header or footer on first page + void SetShowOnFirstPage(bool showOnFirstPage) { m_showOnFirstPage = showOnFirstPage; } + bool GetShowOnFirstPage() const { return m_showOnFirstPage; } + + /// Clear all text + void Clear(); + + /// Set/get font + void SetFont(const wxFont& font) { m_font = font; } + const wxFont& GetFont() const { return m_font; } + + /// Set/get colour + void SetTextColour(const wxColour& col) { m_colour = col; } + const wxColour& GetTextColour() const { return m_colour; } + + DECLARE_CLASS(wxRichTextHeaderFooterData) + +private: + + // Strings for left, centre, right, top, bottom, odd, even + wxString m_text[12]; + wxFont m_font; + wxColour m_colour; + int m_headerMargin; + int m_footerMargin; + bool m_showOnFirstPage; +}; + +/*! + * wxRichTextPrintout + */ + +class WXDLLIMPEXP_RICHTEXT wxRichTextPrintout : public wxPrintout +{ +public: + wxRichTextPrintout(const wxString& title = wxT("Printout")); + virtual ~wxRichTextPrintout(); + + /// The buffer to print + void SetRichTextBuffer(wxRichTextBuffer* buffer) { m_richTextBuffer = buffer; } + wxRichTextBuffer* GetRichTextBuffer() const { return m_richTextBuffer; } + + /// Set/get header/footer data + void SetHeaderFooterData(const wxRichTextHeaderFooterData& data) { m_headerFooterData = data; } + const wxRichTextHeaderFooterData& GetHeaderFooterData() const { return m_headerFooterData; } + + /// Sets margins in 10ths of millimetre. Defaults to 1 inch for margins. + void SetMargins(int top = 252, int bottom = 252, int left = 252, int right = 252); + + /// Calculate scaling and rectangles, setting the device context scaling + void CalculateScaling(wxDC* dc, wxRect& textRect, wxRect& headerRect, wxRect& footerRect); + + // wxPrintout virtual functions + virtual bool OnPrintPage(int page); + virtual bool HasPage(int page); + virtual void GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo); + virtual bool OnBeginDocument(int startPage, int endPage); + virtual void OnPreparePrinting(); + +private: + + /// Renders one page into dc + void RenderPage(wxDC *dc, int page); + + /// Substitute keywords + static bool SubstituteKeywords(wxString& str, const wxString& title, int pageNum, int pageCount); + +private: + + wxRichTextBuffer* m_richTextBuffer; + int m_numPages; + wxArrayInt m_pageBreaksStart; + wxArrayInt m_pageBreaksEnd; + int m_marginLeft, m_marginTop, m_marginRight, m_marginBottom; + + wxRichTextHeaderFooterData m_headerFooterData; + + DECLARE_NO_COPY_CLASS(wxRichTextPrintout) +}; + +/* + *! wxRichTextPrinting + * A simple interface to perform wxRichTextBuffer printing. + */ + +class WXDLLIMPEXP_RICHTEXT wxRichTextPrinting : public wxObject +{ +public: + wxRichTextPrinting(const wxString& name = wxT("Printing"), wxWindow *parentWindow = NULL); + virtual ~wxRichTextPrinting(); + + /// Preview the file or buffer + bool PreviewFile(const wxString& richTextFile); + bool PreviewBuffer(const wxRichTextBuffer& buffer); + + /// Print the file or buffer + bool PrintFile(const wxString& richTextFile); + bool PrintBuffer(const wxRichTextBuffer& buffer); + + /// Shows page setup dialog + void PageSetup(); + + /// Set/get header/footer data + void SetHeaderFooterData(const wxRichTextHeaderFooterData& data) { m_headerFooterData = data; } + const wxRichTextHeaderFooterData& GetHeaderFooterData() const { return m_headerFooterData; } + + /// Set/get header text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT + void SetHeaderText(const wxString& text, wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_ALL, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE); + wxString GetHeaderText(wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_EVEN, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE) const; + + /// Set/get footer text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT + void SetFooterText(const wxString& text, wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_ALL, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE); + wxString GetFooterText(wxRichTextOddEvenPage page = wxRICHTEXT_PAGE_EVEN, wxRichTextPageLocation location = wxRICHTEXT_PAGE_CENTRE) const; + + /// Show header/footer on first page, or not + bool SetShowOnFirstPage(bool show) { m_headerFooterData.SetShowOnFirstPage(show); } + + /// Get print and page setup data + wxPrintData *GetPrintData(); + wxPageSetupDialogData *GetPageSetupData() { return m_pageSetupData; } + + /// Set the rich text buffer pointer, deleting the existing object if present + void SetRichTextBufferPreview(wxRichTextBuffer* buf); + wxRichTextBuffer* GetRichTextBufferPreview() const { return m_richTextBufferPreview; } + + void SetRichTextBufferPrinting(wxRichTextBuffer* buf); + wxRichTextBuffer* GetRichTextBufferPrinting() const { return m_richTextBufferPrinting; } + + /// Set/get the parent window + void SetParentWindow(wxWindow* parent) { m_parentWindow = parent; } + wxWindow* GetParentWindow() const { return m_parentWindow; } + + /// Set/get the title + void SetTitle(const wxString& title) { m_title = title; } + const wxString& GetTitle() const { return m_title; } + + /// Set/get the preview rect + void SetPreviewRect(const wxRect& rect) { m_previewRect = rect; } + const wxRect& GetPreviewRect() const { return m_previewRect; } + +protected: + virtual wxRichTextPrintout *CreatePrintout(); + virtual bool DoPreview(wxRichTextPrintout *printout1, wxRichTextPrintout *printout2); + virtual bool DoPrint(wxRichTextPrintout *printout); + +private: + wxPrintData* m_printData; + wxPageSetupDialogData* m_pageSetupData; + + wxRichTextHeaderFooterData m_headerFooterData; + wxString m_title; + wxWindow* m_parentWindow; + wxRichTextBuffer* m_richTextBufferPreview; + wxRichTextBuffer* m_richTextBufferPrinting; + wxRect m_previewRect; + + DECLARE_NO_COPY_CLASS(wxRichTextPrinting) +}; + +#endif // wxUSE_RICHTEXT & wxUSE_PRINTING_ARCHITECTURE + +#endif // _WX_RICHTEXTPRINT_H_ + diff --git a/src/richtext/richtextbuffer.cpp b/src/richtext/richtextbuffer.cpp index d250a15a0e..2c03c59d5f 100644 --- a/src/richtext/richtextbuffer.cpp +++ b/src/richtext/richtextbuffer.cpp @@ -103,11 +103,21 @@ void wxRichTextObject::SetMargins(int leftMargin, int rightMargin, int topMargin m_bottomMargin = bottomMargin; } -// Convert units in tends of a millimetre to device units +// Convert units in tenths of a millimetre to device units int wxRichTextObject::ConvertTenthsMMToPixels(wxDC& dc, int units) { - int ppi = dc.GetPPI().x; + int p = ConvertTenthsMMToPixels(dc.GetPPI().x, units); + // Unscale + wxRichTextBuffer* buffer = GetBuffer(); + if (buffer) + p = (int) ((double)p / buffer->GetScale()); + return p; +} + +// Convert units in tenths of a millimetre to device units +int wxRichTextObject::ConvertTenthsMMToPixels(int ppi, int units) +{ // There are ppi pixels in 254.1 "1/10 mm" double pixels = ((double) units * (double)ppi) / 254.1; @@ -123,6 +133,14 @@ void wxRichTextObject::Dump(wxTextOutputStream& stream) stream << wxString::Format(wxT("Text colour: %d,%d,%d."), (int) m_attributes.GetTextColour().Red(), (int) m_attributes.GetTextColour().Green(), (int) m_attributes.GetTextColour().Blue()) << wxT("\n"); } +/// Gets the containing buffer +wxRichTextBuffer* wxRichTextObject::GetBuffer() const +{ + const wxRichTextObject* obj = this; + while (obj && !obj->IsKindOf(CLASSINFO(wxRichTextBuffer))) + obj = obj->GetParent(); + return wxDynamicCast(obj, wxRichTextBuffer); +} /*! * wxRichTextCompositeObject @@ -508,7 +526,7 @@ bool wxRichTextParagraphLayoutBox::Draw(wxDC& dc, const wxRichTextRange& range, { wxRect childRect(child->GetPosition(), child->GetCachedSize()); - if (childRect.GetTop() > rect.GetBottom() || childRect.GetBottom() < rect.GetTop()) + if (((style & wxRICHTEXT_DRAW_IGNORE_CACHE) == 0) && childRect.GetTop() > rect.GetBottom() || childRect.GetBottom() < rect.GetTop()) { // Skip } @@ -528,7 +546,10 @@ bool wxRichTextParagraphLayoutBox::Layout(wxDC& dc, const wxRect& rect, int styl bool formatRect = (style & wxRICHTEXT_LAYOUT_SPECIFIED_RECT) == wxRICHTEXT_LAYOUT_SPECIFIED_RECT; // If only laying out a specific area, the passed rect has a different meaning: - // the visible part of the buffer. + // the visible part of the buffer. This is used in wxRichTextCtrl::OnSize, + // so that during a size, only the visible part will be relaid out, or + // it would take too long causing flicker. As an approximation, we assume that + // everything up to the start of the visible area is laid out correctly. if (formatRect) { availableSpace = wxRect(0 + m_leftMargin, @@ -3328,7 +3349,7 @@ void wxRichTextParagraph::ApplyParagraphStyle(const wxTextAttrEx& attr, const wx } else if (attr.HasAlignment() && GetAttributes().GetAlignment() == wxTEXT_ALIGNMENT_RIGHT) { - pos.x = rect.GetRight() - size.x; + pos.x = pos.x + rect.GetWidth() - size.x; line->SetPosition(pos); } @@ -4510,6 +4531,7 @@ void wxRichTextBuffer::Init() m_batchedCommand = NULL; m_suppressUndo = 0; m_handlerFlags = 0; + m_scale = 1.0; } /// Initialisation @@ -5629,7 +5651,7 @@ void wxRichTextBuffer::SetRenderer(wxRichTextRenderer* renderer) sm_renderer = renderer; } -bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& dc, const wxTextAttrEx& bulletAttr, const wxRect& rect) +bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxTextAttrEx& bulletAttr, const wxRect& rect) { if (bulletAttr.GetTextColour().Ok()) { @@ -5664,7 +5686,7 @@ bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* WXUNUSED(par y = y + (charHeight+1)/2 - (bulletHeight+1)/2; // The margin between a bullet and text. - int margin = wxRichTextObject::ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin()); + int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin()); if (bulletAttr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT) x = rect.x + rect.width - bulletWidth - margin; @@ -5702,7 +5724,7 @@ bool wxRichTextStdRenderer::DrawStandardBullet(wxRichTextParagraph* WXUNUSED(par return true; } -bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* WXUNUSED(paragraph), wxDC& dc, const wxTextAttrEx& attr, const wxRect& rect, const wxString& text) +bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* paragraph, wxDC& dc, const wxTextAttrEx& attr, const wxRect& rect, const wxString& text) { if (!text.empty()) { @@ -5735,7 +5757,7 @@ bool wxRichTextStdRenderer::DrawTextBullet(wxRichTextParagraph* WXUNUSED(paragra int y = rect.y + (rect.height - charHeight); // The margin between a bullet and text. - int margin = wxRichTextObject::ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin()); + int margin = paragraph->ConvertTenthsMMToPixels(dc, wxRichTextBuffer::GetBulletRightMargin()); if (attr.GetBulletStyle() & wxTEXT_ATTR_BULLET_STYLE_ALIGN_RIGHT) x = (rect.x + rect.width) - tw - margin; diff --git a/src/richtext/richtextprint.cpp b/src/richtext/richtextprint.cpp new file mode 100644 index 0000000000..0032487f4c --- /dev/null +++ b/src/richtext/richtextprint.cpp @@ -0,0 +1,693 @@ +///////////////////////////////////////////////////////////////////////////// +// Name: src/richtext/richtextprint.cpp +// Purpose: Rich text printing classes +// Author: Julian Smart +// Created: 2006-10-24 +// RCS-ID: $Id$ +// Copyright: (c) Julian Smart +// Licence: wxWindows licence +///////////////////////////////////////////////////////////////////////////// + +// For compilers that support precompilation, includes "wx/wx.h". +#include "wx/wxprec.h" + +#ifdef __BORLANDC__ + #pragma hdrstop +#endif + +#if wxUSE_RICHTEXT && wxUSE_PRINTING_ARCHITECTURE && wxUSE_STREAMS + +#ifndef WX_PRECOMP + #include "wx/log.h" + #include "wx/intl.h" + #include "wx/dc.h" + #include "wx/settings.h" + #include "wx/msgdlg.h" +#endif + +#include "wx/datetime.h" +#include "wx/print.h" +#include "wx/printdlg.h" +#include "wx/richtext/richtextprint.h" +#include "wx/wfstream.h" + +/*! + * wxRichTextPrintout + */ + +wxRichTextPrintout::wxRichTextPrintout(const wxString& title) : wxPrintout(title) +{ + m_numPages = wxRICHTEXT_PRINT_MAX_PAGES; + + SetMargins(); // to default values +} + +wxRichTextPrintout::~wxRichTextPrintout() +{ +} + +void wxRichTextPrintout::OnPreparePrinting() +{ + wxBusyCursor wait; + + m_numPages = 0; + + m_pageBreaksStart.Clear(); + m_pageBreaksEnd.Clear(); + + int lastStartPos = 0; + + wxRect rect, headerRect, footerRect; + + /// Sets the DC scaling and returns important page rectangles + CalculateScaling(GetDC(), rect, headerRect, footerRect); + + if (GetRichTextBuffer()) + { + GetRichTextBuffer()->Invalidate(wxRICHTEXT_ALL); + + GetRichTextBuffer()->Layout(*GetDC(), rect, wxRICHTEXT_FIXED_WIDTH|wxRICHTEXT_VARIABLE_HEIGHT); + + // Now calculate the page breaks + + int yOffset = 0; + + wxRichTextLine* lastLine = NULL; + + wxRichTextObjectList::compatibility_iterator node = GetRichTextBuffer()->GetChildren().GetFirst(); + while (node) + { + // child is a paragraph + wxRichTextParagraph* child = wxDynamicCast(node->GetData(), wxRichTextParagraph); + wxASSERT (child != NULL); + + wxRichTextLineList::compatibility_iterator node2 = child->GetLines().GetFirst(); + while (node2) + { + wxRichTextLine* line = node2->GetData(); + + // Set the line to the page-adjusted position + line->SetPosition(wxPoint(line->GetPosition().x, line->GetPosition().y - yOffset)); + + int lineY = child->GetPosition().y + line->GetPosition().y; + + if ((lineY + line->GetSize().y) > rect.GetBottom()) + { + // New page starting at this line + int newY = rect.y; + + // We increase the offset by the difference between new and old positions + + int increaseOffsetBy = lineY - newY; + yOffset += increaseOffsetBy; + + line->SetPosition(wxPoint(line->GetPosition().x, newY - child->GetPosition().y)); + + if (!lastLine) + lastLine = line; + + m_pageBreaksStart.Add(lastStartPos); + m_pageBreaksEnd.Add(lastLine->GetAbsoluteRange().GetEnd()); + + lastStartPos = line->GetAbsoluteRange().GetStart(); + + m_numPages ++; + } + + lastLine = line; + + node2 = node2->GetNext(); + } + + node = node->GetNext(); + } + + // Closing page break + if (m_pageBreaksStart.GetCount() > 0 && (m_pageBreaksEnd[m_pageBreaksEnd.GetCount()-1] < (GetRichTextBuffer()->GetRange().GetEnd()-1))) + { + m_pageBreaksStart.Add(lastStartPos); + m_pageBreaksEnd.Add(GetRichTextBuffer()->GetRange().GetEnd()); + m_numPages ++; + } + } +} + +bool wxRichTextPrintout::OnBeginDocument(int startPage, int endPage) +{ + if (!wxPrintout::OnBeginDocument(startPage, endPage)) return false; + + return true; +} + +bool wxRichTextPrintout::OnPrintPage(int page) +{ + wxDC *dc = GetDC(); + if (dc) + { + if (HasPage(page)) + RenderPage(dc, page); + return true; + } + else return false; +} + +void wxRichTextPrintout::GetPageInfo(int *minPage, int *maxPage, int *selPageFrom, int *selPageTo) +{ + *minPage = 1; + *maxPage = m_numPages; + *selPageFrom = 1; + *selPageTo = m_numPages; +} + +bool wxRichTextPrintout::HasPage(int pageNum) +{ + return pageNum > 0 && pageNum <= m_numPages; +} + +void wxRichTextPrintout::RenderPage(wxDC *dc, int page) +{ + if (!GetRichTextBuffer()) + return; + + wxBusyCursor wait; + + wxRect textRect, headerRect, footerRect; + + /// Sets the DC scaling and returns important page rectangles + CalculateScaling(dc, textRect, headerRect, footerRect); + + if (page > 1 || m_headerFooterData.GetShowOnFirstPage()) + { + if (m_headerFooterData.GetFont().Ok()) + dc->SetFont(m_headerFooterData.GetFont()); + else + dc->SetFont(*wxNORMAL_FONT); + if (m_headerFooterData.GetTextColour().Ok()) + dc->SetTextForeground(m_headerFooterData.GetTextColour()); + else + dc->SetTextForeground(*wxBLACK); + dc->SetBackgroundMode(wxTRANSPARENT); + + // Draw header, if any + wxRichTextOddEvenPage oddEven = ((page % 2) == 1) ? wxRICHTEXT_PAGE_ODD : wxRICHTEXT_PAGE_EVEN; + + wxString headerTextCentre = m_headerFooterData.GetHeaderText(oddEven, wxRICHTEXT_PAGE_CENTRE); + wxString headerTextLeft = m_headerFooterData.GetHeaderText(oddEven, wxRICHTEXT_PAGE_LEFT); + wxString headerTextRight = m_headerFooterData.GetHeaderText(oddEven, wxRICHTEXT_PAGE_RIGHT); + + if (!headerTextLeft.IsEmpty()) + { + SubstituteKeywords(headerTextLeft, GetTitle(), page, m_numPages); + + //int tx, ty; + //dc->GetTextExtent(headerTextLeft, & tx, & ty); + + int x = headerRect.GetLeft(); + int y = headerRect.GetX(); + dc->DrawText(headerTextLeft, x, y); + } + if (!headerTextCentre.IsEmpty()) + { + SubstituteKeywords(headerTextCentre, GetTitle(), page, m_numPages); + + int tx, ty; + dc->GetTextExtent(headerTextCentre, & tx, & ty); + + int x = headerRect.GetWidth()/2 - tx/2 + headerRect.GetLeft(); + int y = headerRect.GetY(); + dc->DrawText(headerTextCentre, x, y); + } + if (!headerTextRight.IsEmpty()) + { + SubstituteKeywords(headerTextRight, GetTitle(), page, m_numPages); + + int tx, ty; + dc->GetTextExtent(headerTextRight, & tx, & ty); + + int x = headerRect.GetRight() - tx; + int y = headerRect.GetY(); + dc->DrawText(headerTextRight, x, y); + } + + // Draw footer, if any + wxString footerTextCentre = m_headerFooterData.GetFooterText(oddEven, wxRICHTEXT_PAGE_CENTRE); + wxString footerTextLeft = m_headerFooterData.GetFooterText(oddEven, wxRICHTEXT_PAGE_LEFT); + wxString footerTextRight = m_headerFooterData.GetFooterText(oddEven, wxRICHTEXT_PAGE_RIGHT); + + if (!footerTextLeft.IsEmpty()) + { + SubstituteKeywords(footerTextLeft, GetTitle(), page, m_numPages); + + int tx, ty; + dc->GetTextExtent(footerTextLeft, & tx, & ty); + + int x = footerRect.GetLeft(); + int y = footerRect.GetBottom() - ty; + dc->DrawText(footerTextLeft, x, y); + } + if (!footerTextCentre.IsEmpty()) + { + SubstituteKeywords(footerTextCentre, GetTitle(), page, m_numPages); + + int tx, ty; + dc->GetTextExtent(footerTextCentre, & tx, & ty); + + int x = footerRect.GetWidth()/2 - tx/2 + footerRect.GetLeft(); + int y = footerRect.GetBottom() - ty; + dc->DrawText(footerTextCentre, x, y); + } + if (!footerTextRight.IsEmpty()) + { + SubstituteKeywords(footerTextRight, GetTitle(), page, m_numPages); + + int tx, ty; + dc->GetTextExtent(footerTextRight, & tx, & ty); + + int x = footerRect.GetRight() - tx; + int y = footerRect.GetBottom() - ty; + dc->DrawText(footerTextRight, x, y); + } + } + + wxRichTextRange rangeToDraw(m_pageBreaksStart[page-1], m_pageBreaksEnd[page-1]); + + GetRichTextBuffer()->Draw(*dc, rangeToDraw, wxRichTextRange(-1,-1), textRect, 0 /* descent */, wxRICHTEXT_DRAW_IGNORE_CACHE /* flags */); +} + +void wxRichTextPrintout::SetMargins(int top, int bottom, int left, int right) +{ + m_marginTop = top; + m_marginBottom = bottom; + m_marginLeft = left; + m_marginRight = right; +} + +/// Calculate scaling and rectangles, setting the device context scaling +void wxRichTextPrintout::CalculateScaling(wxDC* dc, wxRect& textRect, wxRect& headerRect, wxRect& footerRect) +{ + // Get the logical pixels per inch of screen and printer + int ppiScreenX, ppiScreenY; + GetPPIScreen(&ppiScreenX, &ppiScreenY); + int ppiPrinterX, ppiPrinterY; + GetPPIPrinter(&ppiPrinterX, &ppiPrinterY); + + // This scales the DC so that the printout roughly represents the + // the screen scaling. + float scale = (float)((float)ppiPrinterX/(float)ppiScreenX); + + // Now we have to check in case our real page size is reduced + // (e.g. because we're drawing to a print preview memory DC) + int pageWidth, pageHeight; + int w, h; + dc->GetSize(&w, &h); + GetPageSizePixels(&pageWidth, &pageHeight); + + // If printer pageWidth == current DC width, then this doesn't + // change. But w might be the preview bitmap width, so scale down. + float previewScale = (float)(w/(float)pageWidth); + float overallScale = scale * previewScale; + + // The dimensions used for indentation etc. have to be unscaled + // during printing to be correct when scaling is applied. + if (!IsPreview()) + m_richTextBuffer->SetScale(scale); + + // Calculate margins + int marginLeft = wxRichTextObject::ConvertTenthsMMToPixels(ppiPrinterX, m_marginLeft); + int marginTop = wxRichTextObject::ConvertTenthsMMToPixels(ppiPrinterX, m_marginTop); + int marginRight = wxRichTextObject::ConvertTenthsMMToPixels(ppiPrinterX, m_marginRight); + int marginBottom = wxRichTextObject::ConvertTenthsMMToPixels(ppiPrinterX, m_marginBottom); + + // Header and footer margins + int headerMargin = wxRichTextObject::ConvertTenthsMMToPixels(ppiPrinterX, m_headerFooterData.GetHeaderMargin()); + int footerMargin = wxRichTextObject::ConvertTenthsMMToPixels(ppiPrinterX, m_headerFooterData.GetFooterMargin()); + + dc->SetUserScale(overallScale, overallScale); + + wxRect rect(marginLeft/scale, marginTop/scale, + (pageWidth - marginLeft - marginRight)/scale, (pageHeight - marginTop - marginBottom)/scale); + + headerRect = wxRect(0, 0, 0, 0); + + if (!m_headerFooterData.GetHeaderText(wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT).IsEmpty() || + !m_headerFooterData.GetHeaderText(wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_CENTRE).IsEmpty() || + !m_headerFooterData.GetHeaderText(wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_RIGHT).IsEmpty() || + + !m_headerFooterData.GetHeaderText(wxRICHTEXT_PAGE_EVEN, wxRICHTEXT_PAGE_LEFT).IsEmpty() || + !m_headerFooterData.GetHeaderText(wxRICHTEXT_PAGE_EVEN, wxRICHTEXT_PAGE_CENTRE).IsEmpty() || + !m_headerFooterData.GetHeaderText(wxRICHTEXT_PAGE_EVEN, wxRICHTEXT_PAGE_RIGHT).IsEmpty()) + { + if (m_headerFooterData.GetFont().Ok()) + dc->SetFont(m_headerFooterData.GetFont()); + else + dc->SetFont(*wxNORMAL_FONT); + + int charHeight = dc->GetCharHeight(); + + int headerHeight = charHeight + headerMargin/scale; + + headerRect = wxRect(rect.x, rect.y, rect.width, headerHeight); + + rect.y += headerHeight; + rect.height -= headerHeight; + } + + footerRect = wxRect(0, 0, 0, 0); + + if (!m_headerFooterData.GetFooterText(wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT).IsEmpty() || + !m_headerFooterData.GetFooterText(wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_CENTRE).IsEmpty() || + !m_headerFooterData.GetFooterText(wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_RIGHT).IsEmpty() || + + !m_headerFooterData.GetFooterText(wxRICHTEXT_PAGE_EVEN, wxRICHTEXT_PAGE_LEFT).IsEmpty() || + !m_headerFooterData.GetFooterText(wxRICHTEXT_PAGE_EVEN, wxRICHTEXT_PAGE_CENTRE).IsEmpty() || + !m_headerFooterData.GetFooterText(wxRICHTEXT_PAGE_EVEN, wxRICHTEXT_PAGE_RIGHT).IsEmpty()) + { + if (m_headerFooterData.GetFont().Ok()) + dc->SetFont(m_headerFooterData.GetFont()); + else + dc->SetFont(*wxNORMAL_FONT); + + int charHeight = dc->GetCharHeight(); + + int footerHeight = charHeight + footerMargin/scale; + + footerRect = wxRect(rect.x, rect.y + rect.height, rect.width, footerHeight); + + rect.height -= footerHeight; + } + + textRect = rect; +} + +bool wxRichTextPrintout::SubstituteKeywords(wxString& str, const wxString& title, int pageNum, int pageCount) +{ + wxString num; + + num.Printf(wxT("%i"), pageNum); + str.Replace(wxT("@PAGENUM@"), num); + + num.Printf(wxT("%lu"), (unsigned long) pageCount); + str.Replace(wxT("@PAGESCNT@"), num); + + wxDateTime now = wxDateTime::Now(); + + str.Replace(wxT("@DATE@"), now.FormatDate()); + str.Replace(wxT("@TIME@"), now.FormatTime()); + + str.Replace(wxT("@TITLE@"), title); + + return true; +} + +/*! + * wxRichTextPrinting + */ + +wxRichTextPrinting::wxRichTextPrinting(const wxString& name, wxWindow *parentWindow) +{ + m_richTextBufferPrinting = NULL; + m_richTextBufferPreview = NULL; + + m_parentWindow = parentWindow; + m_title = name; + m_printData = NULL; + + m_previewRect = wxRect(wxPoint(100, 100), wxSize(800, 800)); + + m_pageSetupData = new wxPageSetupDialogData; + m_pageSetupData->EnableMargins(true); + m_pageSetupData->SetMarginTopLeft(wxPoint(25, 25)); + m_pageSetupData->SetMarginBottomRight(wxPoint(25, 25)); +} + +wxRichTextPrinting::~wxRichTextPrinting() +{ + delete m_printData; + delete m_pageSetupData; + delete m_richTextBufferPrinting; + delete m_richTextBufferPreview; +} + +wxPrintData *wxRichTextPrinting::GetPrintData() +{ + if (m_printData == NULL) + m_printData = new wxPrintData(); + return m_printData; +} + +/// Set the rich text buffer pointer, deleting the existing object if present +void wxRichTextPrinting::SetRichTextBufferPrinting(wxRichTextBuffer* buf) +{ + if (m_richTextBufferPrinting) + { + delete m_richTextBufferPrinting; + m_richTextBufferPrinting = NULL; + } + m_richTextBufferPrinting = buf; +} + +void wxRichTextPrinting::SetRichTextBufferPreview(wxRichTextBuffer* buf) +{ + if (m_richTextBufferPreview) + { + delete m_richTextBufferPreview; + m_richTextBufferPreview = NULL; + } + m_richTextBufferPreview = buf; +} + +bool wxRichTextPrinting::PreviewFile(const wxString& richTextFile) +{ + SetRichTextBufferPreview(new wxRichTextBuffer); + + if (!m_richTextBufferPreview->LoadFile(richTextFile)) + { + SetRichTextBufferPreview(NULL); + return false; + } + else + SetRichTextBufferPrinting(new wxRichTextBuffer(*m_richTextBufferPreview)); + + wxRichTextPrintout *p1 = CreatePrintout(); + p1->SetRichTextBuffer(m_richTextBufferPreview); + + wxRichTextPrintout *p2 = CreatePrintout(); + p2->SetRichTextBuffer(m_richTextBufferPrinting); + return DoPreview(p1, p2); +} + +bool wxRichTextPrinting::PreviewBuffer(const wxRichTextBuffer& buffer) +{ + SetRichTextBufferPreview(new wxRichTextBuffer(buffer)); + SetRichTextBufferPrinting(new wxRichTextBuffer(buffer)); + + wxRichTextPrintout *p1 = CreatePrintout(); + p1->SetRichTextBuffer(m_richTextBufferPreview); + + wxRichTextPrintout *p2 = CreatePrintout(); + p2->SetRichTextBuffer(m_richTextBufferPrinting); + + return DoPreview(p1, p2); +} + +bool wxRichTextPrinting::PrintFile(const wxString& richTextFile) +{ + SetRichTextBufferPrinting(new wxRichTextBuffer); + + if (!m_richTextBufferPrinting->LoadFile(richTextFile)) + { + SetRichTextBufferPrinting(NULL); + return false; + } + + wxRichTextPrintout *p = CreatePrintout(); + p->SetRichTextBuffer(m_richTextBufferPrinting); + + bool ret = DoPrint(p); + delete p; + return ret; +} + +bool wxRichTextPrinting::PrintBuffer(const wxRichTextBuffer& buffer) +{ + SetRichTextBufferPrinting(new wxRichTextBuffer(buffer)); + + wxRichTextPrintout *p = CreatePrintout(); + p->SetRichTextBuffer(m_richTextBufferPrinting); + + bool ret = DoPrint(p); + delete p; + return ret; +} + +bool wxRichTextPrinting::DoPreview(wxRichTextPrintout *printout1, wxRichTextPrintout *printout2) +{ + // Pass two printout objects: for preview, and possible printing. + wxPrintDialogData printDialogData(*GetPrintData()); + wxPrintPreview *preview = new wxPrintPreview(printout1, printout2, &printDialogData); + if (!preview->Ok()) + { + delete preview; + return false; + } + + wxPreviewFrame *frame = new wxPreviewFrame(preview, m_parentWindow, + m_title + _(" Preview"), + m_previewRect.GetPosition(), m_previewRect.GetSize()); + frame->Centre(wxBOTH); + frame->Initialize(); + frame->Show(true); + return true; +} + +bool wxRichTextPrinting::DoPrint(wxRichTextPrintout *printout) +{ + wxPrintDialogData printDialogData(*GetPrintData()); + wxPrinter printer(&printDialogData); + + if (!printer.Print(m_parentWindow, printout, true)) + { + return false; + } + + (*GetPrintData()) = printer.GetPrintDialogData().GetPrintData(); + return true; +} + +void wxRichTextPrinting::PageSetup() +{ + if (!GetPrintData()->Ok()) + { + wxLogError(_("There was a problem during page setup: you may need to set a default printer.")); + return; + } + + m_pageSetupData->SetPrintData(*GetPrintData()); + wxPageSetupDialog pageSetupDialog(m_parentWindow, m_pageSetupData); + + if (pageSetupDialog.ShowModal() == wxID_OK) + { + (*GetPrintData()) = pageSetupDialog.GetPageSetupData().GetPrintData(); + (*m_pageSetupData) = pageSetupDialog.GetPageSetupData(); + } +} + +wxRichTextPrintout *wxRichTextPrinting::CreatePrintout() +{ + wxRichTextPrintout *p = new wxRichTextPrintout(m_title); + + p->SetHeaderFooterData(GetHeaderFooterData()); + p->SetMargins(10*m_pageSetupData->GetMarginTopLeft().y, + 10*m_pageSetupData->GetMarginBottomRight().y, + 10*m_pageSetupData->GetMarginTopLeft().x, + 10*m_pageSetupData->GetMarginBottomRight().x); + + return p; +} + +/// Set/get header text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT +void wxRichTextPrinting::SetHeaderText(const wxString& text, wxRichTextOddEvenPage page, wxRichTextPageLocation location) +{ + m_headerFooterData.SetHeaderText(text, page, location); +} + +wxString wxRichTextPrinting::GetHeaderText(wxRichTextOddEvenPage page, wxRichTextPageLocation location) const +{ + return m_headerFooterData.GetHeaderText(page, location); +} + +/// Set/get footer text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT +void wxRichTextPrinting::SetFooterText(const wxString& text, wxRichTextOddEvenPage page, wxRichTextPageLocation location) +{ + m_headerFooterData.SetFooterText(text, page, location); +} + +wxString wxRichTextPrinting::GetFooterText(wxRichTextOddEvenPage page, wxRichTextPageLocation location) const +{ + return m_headerFooterData.GetFooterText(page, location); +} + +/*! + * Header/footer data + */ + +IMPLEMENT_CLASS(wxRichTextHeaderFooterData, wxObject) + +/// Copy +void wxRichTextHeaderFooterData::Copy(const wxRichTextHeaderFooterData& data) +{ + int i; + for (i = 0; i < 12; i++) + m_text[i] = data.m_text[i]; + m_font = data.m_font; + m_colour = data.m_colour; + m_headerMargin = data.m_headerMargin; + m_footerMargin = data.m_footerMargin; + m_showOnFirstPage = data.m_showOnFirstPage; +} + +/// Set/get text +void wxRichTextHeaderFooterData::SetText(const wxString& text, int headerFooter, wxRichTextOddEvenPage page, wxRichTextPageLocation location) +{ + int idx = headerFooter + (2 * (int) page) + (4 * (int) location); + wxASSERT( idx >= 0 && idx < 12 ); + + if (idx >= 0 && idx < 12) + m_text[idx] = text; +} + +wxString wxRichTextHeaderFooterData::GetText(int headerFooter, wxRichTextOddEvenPage page, wxRichTextPageLocation location) const +{ + int idx = headerFooter + (2 * (int) page) + (4 * (int) location); + wxASSERT( idx >= 0 && idx < 12 ); + + if (idx >= 0 && idx < 12) + return m_text[idx]; + else + return wxEmptyString; +} + +/// Set/get header text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT +void wxRichTextHeaderFooterData::SetHeaderText(const wxString& text, wxRichTextOddEvenPage page, wxRichTextPageLocation location) +{ + if (page == wxRICHTEXT_PAGE_ALL) + { + SetText(text, 0, wxRICHTEXT_PAGE_ODD, location); + SetText(text, 0, wxRICHTEXT_PAGE_EVEN, location); + } + else + SetText(text, 0, page, location); +} + +wxString wxRichTextHeaderFooterData::GetHeaderText(wxRichTextOddEvenPage page, wxRichTextPageLocation location) const +{ + return GetText(0, page, location); +} + +/// Set/get footer text, e.g. wxRICHTEXT_PAGE_ODD, wxRICHTEXT_PAGE_LEFT +void wxRichTextHeaderFooterData::SetFooterText(const wxString& text, wxRichTextOddEvenPage page, wxRichTextPageLocation location) +{ + if (page == wxRICHTEXT_PAGE_ALL) + { + SetText(text, 1, wxRICHTEXT_PAGE_ODD, location); + SetText(text, 1, wxRICHTEXT_PAGE_EVEN, location); + } + else + SetText(text, 1, page, location); +} + +wxString wxRichTextHeaderFooterData::GetFooterText(wxRichTextOddEvenPage page, wxRichTextPageLocation location) const +{ + return GetText(1, page, location); +} + +/// Clear all text +void wxRichTextHeaderFooterData::Clear() +{ + int i; + for (i = 0; i < 12; i++) + m_text[i] = wxEmptyString; +} + +#endif // wxUSE_RICHTEXT & wxUSE_PRINTING_ARCHITECTURE +