1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/richtext/richtextformatdlg.cpp
3 // Purpose: Formatting dialog for wxRichTextCtrl
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // For compilers that support precompilation, includes "wx.h".
13 #include "wx/wxprec.h"
21 #include "wx/richtext/richtextformatdlg.h"
24 #include "wx/listbox.h"
25 #include "wx/combobox.h"
26 #include "wx/textctrl.h"
28 #include "wx/stattext.h"
29 #include "wx/statline.h"
30 #include "wx/radiobut.h"
32 #include "wx/bitmap.h"
33 #include "wx/dcclient.h"
35 #include "wx/checkbox.h"
36 #include "wx/button.h"
39 #include "wx/bookctrl.h"
40 #include "wx/colordlg.h"
41 #include "wx/settings.h"
42 #include "wx/module.h"
43 #include "wx/imaglist.h"
45 #include "wx/richtext/richtextctrl.h"
46 #include "wx/richtext/richtextstyles.h"
49 #include "../../src/richtext/richtextfontpage.cpp"
50 #include "../../src/richtext/richtextindentspage.cpp"
51 #include "../../src/richtext/richtexttabspage.cpp"
52 #include "../../src/richtext/richtextbulletspage.cpp"
53 #include "../../src/richtext/richtextstylepage.cpp"
54 #include "../../src/richtext/richtextliststylepage.cpp"
56 #include "richtextfontpage.cpp"
57 #include "richtextindentspage.cpp"
58 #include "richtexttabspage.cpp"
59 #include "richtextbulletspage.cpp"
60 // Digital Mars can't cope with this much code
62 #include "richtextliststylepage.cpp"
64 #include "richtextstylepage.cpp"
67 #if 0 // def __WXMAC__
68 #define wxRICHTEXT_USE_TOOLBOOK 1
70 #define wxRICHTEXT_USE_TOOLBOOK 0
73 bool wxRichTextFormattingDialog
::sm_showToolTips
= false;
75 IMPLEMENT_CLASS(wxRichTextFormattingDialog
, wxPropertySheetDialog
)
77 BEGIN_EVENT_TABLE(wxRichTextFormattingDialog
, wxPropertySheetDialog
)
78 EVT_BOOKCTRL_PAGE_CHANGED(wxID_ANY
, wxRichTextFormattingDialog
::OnTabChanged
)
81 wxRichTextFormattingDialogFactory
* wxRichTextFormattingDialog
::ms_FormattingDialogFactory
= NULL
;
83 void wxRichTextFormattingDialog
::Init()
86 m_styleDefinition
= NULL
;
90 wxRichTextFormattingDialog
::~wxRichTextFormattingDialog()
93 delete m_styleDefinition
;
96 bool wxRichTextFormattingDialog
::Create(long flags
, wxWindow
* parent
, const wxString
& title
, wxWindowID id
,
97 const wxPoint
& pos
, const wxSize
& sz
, long style
)
99 SetExtraStyle(wxDIALOG_EX_CONTEXTHELP
|wxWS_EX_VALIDATE_RECURSIVELY
);
101 int resizeBorder
= wxRESIZE_BORDER
;
103 GetFormattingDialogFactory()->SetSheetStyle(this);
105 wxPropertySheetDialog
::Create(parent
, id
, title
, pos
, sz
,
106 style
| (int)wxPlatform
::IfNot(wxOS_WINDOWS_CE
, resizeBorder
)
109 GetFormattingDialogFactory()->CreateButtons(this);
110 GetFormattingDialogFactory()->CreatePages(flags
, this);
117 /// Get attributes from the given range
118 bool wxRichTextFormattingDialog
::GetStyle(wxRichTextCtrl
* ctrl
, const wxRichTextRange
& range
)
120 if (ctrl
->GetBuffer().GetStyleForRange(range
.ToInternal(), m_attributes
))
121 return UpdateDisplay();
126 /// Apply attributes to the given range, only applying if necessary (wxRICHTEXT_SETSTYLE_OPTIMIZE)
127 bool wxRichTextFormattingDialog
::ApplyStyle(wxRichTextCtrl
* ctrl
, const wxRichTextRange
& range
, int flags
)
129 return ctrl
->SetStyleEx(range
, m_attributes
, flags
);
132 /// Set the attributes and optionally update the display
133 bool wxRichTextFormattingDialog
::SetStyle(const wxTextAttr
& style
, bool update
)
135 m_attributes
= style
;
141 /// Set the style definition and optionally update the display
142 bool wxRichTextFormattingDialog
::SetStyleDefinition(const wxRichTextStyleDefinition
& styleDef
, wxRichTextStyleSheet
* sheet
, bool update
)
144 m_styleSheet
= sheet
;
146 if (m_styleDefinition
)
147 delete m_styleDefinition
;
148 m_styleDefinition
= styleDef
.Clone();
150 return SetStyle(m_styleDefinition
->GetStyle(), update
);
153 /// Transfers the data and from to the window
154 bool wxRichTextFormattingDialog
::TransferDataToWindow()
156 if (m_styleDefinition
)
157 m_attributes
= m_styleDefinition
->GetStyle();
159 if (!wxPropertySheetDialog
::TransferDataToWindow())
165 bool wxRichTextFormattingDialog
::TransferDataFromWindow()
167 if (!wxPropertySheetDialog
::TransferDataFromWindow())
170 if (m_styleDefinition
)
171 m_styleDefinition
->GetStyle() = m_attributes
;
176 /// Update the display
177 bool wxRichTextFormattingDialog
::UpdateDisplay()
179 return TransferDataToWindow();
182 /// Apply the styles when a different tab is selected, so the previews are
184 void wxRichTextFormattingDialog
::OnTabChanged(wxBookCtrlEvent
& event
)
186 if (GetBookCtrl() != event
.GetEventObject())
192 int oldPageId
= event
.GetOldSelection();
195 wxWindow
* page
= GetBookCtrl()->GetPage(oldPageId
);
197 page
->TransferDataFromWindow();
200 int pageId
= event
.GetSelection();
203 wxWindow
* page
= GetBookCtrl()->GetPage(pageId
);
205 page
->TransferDataToWindow();
209 /// Respond to help command
210 void wxRichTextFormattingDialog
::OnHelp(wxCommandEvent
& event
)
212 int selPage
= GetBookCtrl()->GetSelection();
213 if (selPage
!= wxNOT_FOUND
)
215 int pageId
= m_pageIds
[selPage
];
216 if (!GetFormattingDialogFactory()->ShowHelp(pageId
, this))
221 void wxRichTextFormattingDialog
::SetFormattingDialogFactory(wxRichTextFormattingDialogFactory
* factory
)
223 if (ms_FormattingDialogFactory
)
224 delete ms_FormattingDialogFactory
;
225 ms_FormattingDialogFactory
= factory
;
229 * Factory for formatting dialog
232 /// Create all pages, under the dialog's book control, also calling AddPage
233 bool wxRichTextFormattingDialogFactory
::CreatePages(long pages
, wxRichTextFormattingDialog
* dialog
)
235 if (dialog
->GetImageList())
236 dialog
->GetBookCtrl()->SetImageList(dialog
->GetImageList());
238 int availablePageCount
= GetPageIdCount();
240 bool selected
= false;
241 for (i
= 0; i
< availablePageCount
; i
++)
243 int pageId
= GetPageId(i
);
244 if (pageId
!= -1 && (pages
& pageId
))
247 wxPanel
* panel
= CreatePage(pageId
, title
, dialog
);
248 wxASSERT( panel
!= NULL
);
251 int imageIndex
= GetPageImage(pageId
);
252 dialog
->GetBookCtrl()->AddPage(panel
, title
, !selected
, imageIndex
);
255 dialog
->AddPageId(pageId
);
263 /// Create a page, given a page identifier
264 wxPanel
* wxRichTextFormattingDialogFactory
::CreatePage(int page
, wxString
& title
, wxRichTextFormattingDialog
* dialog
)
266 if (page
== wxRICHTEXT_FORMAT_STYLE_EDITOR
)
268 wxRichTextStylePage
* page
= new wxRichTextStylePage(dialog
->GetBookCtrl(), wxID_ANY
);
272 else if (page
== wxRICHTEXT_FORMAT_FONT
)
274 wxRichTextFontPage
* page
= new wxRichTextFontPage(dialog
->GetBookCtrl(), wxID_ANY
);
278 else if (page
== wxRICHTEXT_FORMAT_INDENTS_SPACING
)
280 wxRichTextIndentsSpacingPage
* page
= new wxRichTextIndentsSpacingPage(dialog
->GetBookCtrl(), wxID_ANY
);
281 title
= _("Indents && Spacing");
284 else if (page
== wxRICHTEXT_FORMAT_TABS
)
286 wxRichTextTabsPage
* page
= new wxRichTextTabsPage(dialog
->GetBookCtrl(), wxID_ANY
);
290 else if (page
== wxRICHTEXT_FORMAT_BULLETS
)
292 wxRichTextBulletsPage
* page
= new wxRichTextBulletsPage(dialog
->GetBookCtrl(), wxID_ANY
);
293 title
= _("Bullets");
297 else if (page
== wxRICHTEXT_FORMAT_LIST_STYLE
)
299 wxRichTextListStylePage
* page
= new wxRichTextListStylePage(dialog
->GetBookCtrl(), wxID_ANY
);
300 title
= _("List Style");
308 /// Enumerate all available page identifiers
309 int wxRichTextFormattingDialogFactory
::GetPageId(int i
) const
312 wxRICHTEXT_FORMAT_STYLE_EDITOR
,
313 wxRICHTEXT_FORMAT_FONT
,
314 wxRICHTEXT_FORMAT_INDENTS_SPACING
,
315 wxRICHTEXT_FORMAT_BULLETS
,
316 wxRICHTEXT_FORMAT_TABS
,
317 wxRICHTEXT_FORMAT_LIST_STYLE
};
325 /// Get the number of available page identifiers
326 int wxRichTextFormattingDialogFactory
::GetPageIdCount() const
335 /// Set the sheet style, called at the start of wxRichTextFormattingDialog::Create
336 bool wxRichTextFormattingDialogFactory
::SetSheetStyle(wxRichTextFormattingDialog
* dialog
)
338 #if wxRICHTEXT_USE_TOOLBOOK
339 int sheetStyle
= wxPROPSHEET_SHRINKTOFIT
;
341 sheetStyle
|= wxPROPSHEET_BUTTONTOOLBOOK
;
343 sheetStyle
|= wxPROPSHEET_TOOLBOOK
;
346 dialog
->SetSheetStyle(sheetStyle
);
347 dialog
->SetSheetInnerBorder(0);
348 dialog
->SetSheetOuterBorder(0);
351 #endif // wxRICHTEXT_USE_TOOLBOOK
356 /// Create the main dialog buttons
357 bool wxRichTextFormattingDialogFactory
::CreateButtons(wxRichTextFormattingDialog
* dialog
)
359 int flags
= wxOK
|wxCANCEL
;
361 if (dialog
->GetWindowStyleFlag() & wxRICHTEXT_FORMAT_HELP_BUTTON
)
365 // If using a toolbook, also follow Mac style and don't create buttons
366 #if !wxRICHTEXT_USE_TOOLBOOK
367 dialog
->CreateButtons(flags
);
374 * Module to initialise and clean up handlers
377 class wxRichTextFormattingDialogModule
: public wxModule
379 DECLARE_DYNAMIC_CLASS(wxRichTextFormattingDialogModule
)
381 wxRichTextFormattingDialogModule() {}
382 bool OnInit() { wxRichTextFormattingDialog
::SetFormattingDialogFactory(new wxRichTextFormattingDialogFactory
); return true; }
383 void OnExit() { wxRichTextFormattingDialog
::SetFormattingDialogFactory(NULL
); }
386 IMPLEMENT_DYNAMIC_CLASS(wxRichTextFormattingDialogModule
, wxModule
)
389 * Font preview control
392 BEGIN_EVENT_TABLE(wxRichTextFontPreviewCtrl
, wxWindow
)
393 EVT_PAINT(wxRichTextFontPreviewCtrl
::OnPaint
)
396 wxRichTextFontPreviewCtrl
::wxRichTextFontPreviewCtrl(wxWindow
*parent
, wxWindowID id
, const wxPoint
& pos
, const wxSize
& sz
, long style
)
398 if ((style
& wxBORDER_MASK
) == wxBORDER_DEFAULT
)
399 style
|= wxBORDER_THEME
;
401 wxWindow
::Create(parent
, id
, pos
, sz
, style
);
403 SetBackgroundColour(*wxWHITE
);
407 void wxRichTextFontPreviewCtrl
::OnPaint(wxPaintEvent
& WXUNUSED(event
))
411 wxSize size
= GetSize();
412 wxFont font
= GetFont();
414 if ((GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
) || (GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
))
416 double size
= static_cast<double>(font
.GetPointSize()) / wxSCRIPT_MUL_FACTOR
;
417 font
.SetPointSize( static_cast<int>(size
) );
423 // Calculate vertical and horizontal centre
424 wxCoord w
= 0, h
= 0;
426 wxString
text(_("ABCDEFGabcdefg12345"));
427 if (GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS
)
430 dc
.GetTextExtent( text
, &w
, &h
);
431 int cx
= wxMax(2, (size
.x
/2) - (w
/2));
432 int cy
= wxMax(2, (size
.y
/2) - (h
/2));
434 if ( GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT
)
436 if ( GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT
)
439 dc
.SetTextForeground(GetForegroundColour());
440 dc
.SetClippingRegion(2, 2, size
.x
-4, size
.y
-4);
441 dc
.DrawText(text
, cx
, cy
);
443 if (GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH
)
445 dc
.SetPen(wxPen(GetForegroundColour(), 1));
446 dc
.DrawLine(cx
, (int) (cy
+ h
/2 + 0.5), cx
+ w
, (int) (cy
+ h
/2 + 0.5));
449 dc
.DestroyClippingRegion();
453 // Helper for pages to get the top-level dialog
454 wxRichTextFormattingDialog
* wxRichTextFormattingDialog
::GetDialog(wxWindow
* win
)
456 wxWindow
* p
= win
->GetParent();
457 while (p
&& !p
->IsKindOf(CLASSINFO(wxRichTextFormattingDialog
)))
459 wxRichTextFormattingDialog
* dialog
= wxDynamicCast(p
, wxRichTextFormattingDialog
);
464 // Helper for pages to get the attributes
465 wxTextAttr
* wxRichTextFormattingDialog
::GetDialogAttributes(wxWindow
* win
)
467 wxRichTextFormattingDialog
* dialog
= GetDialog(win
);
469 return & dialog
->GetAttributes();
474 // Helper for pages to get the style
475 wxRichTextStyleDefinition
* wxRichTextFormattingDialog
::GetDialogStyleDefinition(wxWindow
* win
)
477 wxRichTextFormattingDialog
* dialog
= GetDialog(win
);
479 return dialog
->GetStyleDefinition();
485 * A control for displaying a small preview of a colour or bitmap
488 BEGIN_EVENT_TABLE(wxRichTextColourSwatchCtrl
, wxControl
)
489 EVT_MOUSE_EVENTS(wxRichTextColourSwatchCtrl
::OnMouseEvent
)
492 IMPLEMENT_CLASS(wxRichTextColourSwatchCtrl
, wxControl
)
494 wxRichTextColourSwatchCtrl
::wxRichTextColourSwatchCtrl(wxWindow
* parent
, wxWindowID id
, const wxPoint
& pos
, const wxSize
& size
, long style
)
496 if ((style
& wxBORDER_MASK
) == wxBORDER_DEFAULT
)
497 style
|= wxBORDER_THEME
;
499 wxControl
::Create(parent
, id
, pos
, size
, style
);
501 SetColour(wxSystemSettings
::GetColour(wxSYS_COLOUR_WINDOW
));
502 SetBackgroundStyle(wxBG_STYLE_COLOUR
);
505 wxRichTextColourSwatchCtrl
::~wxRichTextColourSwatchCtrl()
509 void wxRichTextColourSwatchCtrl
::OnMouseEvent(wxMouseEvent
& event
)
511 if (event
.LeftDown())
513 wxWindow
* parent
= GetParent();
514 while (parent
!= NULL
&& !parent
->IsKindOf(CLASSINFO(wxDialog
)) && !parent
->IsKindOf(CLASSINFO(wxFrame
)))
515 parent
= parent
->GetParent();
518 data
.SetChooseFull(true);
519 data
.SetColour(m_colour
);
521 wxColourDialog
*dialog
= new wxColourDialog(parent
, &data
);
522 // Crashes on wxMac (no m_peer)
524 dialog
->SetTitle(_("Colour"));
526 if (dialog
->ShowModal() == wxID_OK
)
528 wxColourData retData
= dialog
->GetColourData();
529 m_colour
= retData
.GetColour();
530 SetBackgroundColour(m_colour
);
533 #endif // wxUSE_COLOURDLG
536 wxCommandEvent
event(wxEVT_COMMAND_BUTTON_CLICKED
, GetId());
537 GetEventHandler()->ProcessEvent(event
);
544 * wxRichTextFontListBox class declaration
545 * A listbox to display styles.
548 IMPLEMENT_CLASS(wxRichTextFontListBox
, wxHtmlListBox
)
550 BEGIN_EVENT_TABLE(wxRichTextFontListBox
, wxHtmlListBox
)
553 wxRichTextFontListBox
::wxRichTextFontListBox(wxWindow
* parent
, wxWindowID id
, const wxPoint
& pos
,
554 const wxSize
& size
, long style
)
557 Create(parent
, id
, pos
, size
, style
);
560 bool wxRichTextFontListBox
::Create(wxWindow
* parent
, wxWindowID id
, const wxPoint
& pos
,
561 const wxSize
& size
, long style
)
563 if ((style
& wxBORDER_MASK
) == wxBORDER_DEFAULT
)
564 style
|= wxBORDER_THEME
;
566 return wxHtmlListBox
::Create(parent
, id
, pos
, size
, style
);
569 wxRichTextFontListBox
::~wxRichTextFontListBox()
573 /// Returns the HTML for this item
574 wxString wxRichTextFontListBox
::OnGetItem(size_t n
) const
576 if (m_faceNames
.GetCount() == 0)
577 return wxEmptyString
;
579 wxString str
= CreateHTML(m_faceNames
[n
]);
583 /// Get font name for index
584 wxString wxRichTextFontListBox
::GetFaceName(size_t i
) const
586 return m_faceNames
[i
];
589 /// Set selection for string, returning the index.
590 int wxRichTextFontListBox
::SetFaceNameSelection(const wxString
& name
)
592 int i
= m_faceNames
.Index(name
);
598 /// Updates the font list
599 void wxRichTextFontListBox
::UpdateFonts()
601 wxArrayString facenames
= wxRichTextCtrl
::GetAvailableFontNames();
602 m_faceNames
= facenames
;
605 SetItemCount(m_faceNames
.GetCount());
610 // Convert a colour to a 6-digit hex string
611 static wxString
ColourToHexString(const wxColour
& col
)
615 hex
+= wxDecToHex(col
.Red());
616 hex
+= wxDecToHex(col
.Green());
617 hex
+= wxDecToHex(col
.Blue());
623 /// Creates a suitable HTML fragment for a definition
624 wxString wxRichTextFontListBox
::CreateHTML(const wxString
& facename
) const
626 wxString str
= wxT("<font");
628 str
<< wxT(" size=\"+2\"");;
630 if (!facename
.IsEmpty() && facename
!= _("(none)"))
631 str
<< wxT(" face=\"") << facename
<< wxT("\"");
633 if (def->GetStyle().GetTextColour().Ok())
634 str << wxT(" color=\"#") << ColourToHexString(def->GetStyle().GetTextColour()) << wxT("\"");
639 bool hasBold
= false;
649 str
<< wxT("</font>");