1///////////////////////////////////////////////////////////////////////////// 2// Name: src/richtext/richtextformatdlg.cpp 3// Purpose: Formatting dialog for wxRichTextCtrl 4// Author: Julian Smart 5// Modified by: 6// Created: 2006-10-01 7// RCS-ID: $Id: richtextformatdlg.cpp 62010 2009-09-22 10:03:45Z JS $ 8// Copyright: (c) Julian Smart 9// Licence: wxWindows licence 10///////////////////////////////////////////////////////////////////////////// 11 12// For compilers that support precompilation, includes "wx.h". 13#include "wx/wxprec.h" 14 15#ifdef __BORLANDC__ 16 #pragma hdrstop 17#endif 18 19#if wxUSE_RICHTEXT 20 21#include "wx/richtext/richtextformatdlg.h" 22 23#ifndef WX_PRECOMP 24 #include "wx/listbox.h" 25 #include "wx/combobox.h" 26 #include "wx/textctrl.h" 27 #include "wx/sizer.h" 28 #include "wx/stattext.h" 29 #include "wx/statline.h" 30 #include "wx/radiobut.h" 31 #include "wx/icon.h" 32 #include "wx/bitmap.h" 33 #include "wx/dcclient.h" 34 #include "wx/frame.h" 35 #include "wx/checkbox.h" 36 #include "wx/button.h" 37#endif // WX_PRECOMP 38 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" 44 45#include "wx/richtext/richtextctrl.h" 46#include "wx/richtext/richtextstyles.h" 47 48#ifdef __WXMAC__ 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" 55#else 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 61#ifndef __DMC__ 62 #include "richtextliststylepage.cpp" 63#endif 64#include "richtextstylepage.cpp" 65#endif 66 67#if 0 // def __WXMAC__ 68#define wxRICHTEXT_USE_TOOLBOOK true 69#else 70#define wxRICHTEXT_USE_TOOLBOOK false 71#endif 72 73bool wxRichTextFormattingDialog::sm_showToolTips = false; 74 75IMPLEMENT_CLASS(wxRichTextFormattingDialog, wxPropertySheetDialog) 76 77BEGIN_EVENT_TABLE(wxRichTextFormattingDialog, wxPropertySheetDialog) 78 EVT_BOOKCTRL_PAGE_CHANGED(wxID_ANY, wxRichTextFormattingDialog::OnTabChanged) 79END_EVENT_TABLE() 80 81wxRichTextFormattingDialogFactory* wxRichTextFormattingDialog::ms_FormattingDialogFactory = NULL; 82 83void wxRichTextFormattingDialog::Init() 84{ 85 m_imageList = NULL; 86 m_styleDefinition = NULL; 87 m_styleSheet = NULL; 88} 89 90wxRichTextFormattingDialog::~wxRichTextFormattingDialog() 91{ 92 delete m_imageList; 93 delete m_styleDefinition; 94} 95 96bool wxRichTextFormattingDialog::Create(long flags, wxWindow* parent, const wxString& title, wxWindowID id, 97 const wxPoint& pos, const wxSize& sz, long style) 98{ 99 SetExtraStyle(wxDIALOG_EX_CONTEXTHELP|wxWS_EX_VALIDATE_RECURSIVELY); 100 101 int resizeBorder = wxRESIZE_BORDER; 102 103 GetFormattingDialogFactory()->SetSheetStyle(this); 104 105#ifdef __WXMAC__ 106 SetWindowVariant(wxWINDOW_VARIANT_SMALL); 107#endif 108 109 wxPropertySheetDialog::Create(parent, id, title, pos, sz, 110 style | (int)wxPlatform::IfNot(wxOS_WINDOWS_CE, resizeBorder) 111 ); 112 113 GetFormattingDialogFactory()->CreateButtons(this); 114 GetFormattingDialogFactory()->CreatePages(flags, this); 115 116 LayoutDialog(); 117 118 return true; 119} 120 121/// Get attributes from the given range 122bool wxRichTextFormattingDialog::GetStyle(wxRichTextCtrl* ctrl, const wxRichTextRange& range) 123{ 124 if (ctrl->GetBuffer().GetStyleForRange(range.ToInternal(), m_attributes)) 125 return UpdateDisplay(); 126 else 127 return false; 128} 129 130/// Apply attributes to the given range, only applying if necessary (wxRICHTEXT_SETSTYLE_OPTIMIZE) 131bool wxRichTextFormattingDialog::ApplyStyle(wxRichTextCtrl* ctrl, const wxRichTextRange& range, int flags) 132{ 133 return ctrl->SetStyleEx(range, m_attributes, flags); 134} 135 136/// Set the attributes and optionally update the display 137bool wxRichTextFormattingDialog::SetStyle(const wxTextAttrEx& style, bool update) 138{ 139 m_attributes = style; 140 if (update) 141 UpdateDisplay(); 142 return true; 143} 144 145/// Set the style definition and optionally update the display 146bool wxRichTextFormattingDialog::SetStyleDefinition(const wxRichTextStyleDefinition& styleDef, wxRichTextStyleSheet* sheet, bool update) 147{ 148 m_styleSheet = sheet; 149 150 if (m_styleDefinition) 151 delete m_styleDefinition; 152 m_styleDefinition = styleDef.Clone(); 153 154 return SetStyle(m_styleDefinition->GetStyle(), update); 155} 156 157/// Transfers the data and from to the window 158bool wxRichTextFormattingDialog::TransferDataToWindow() 159{ 160 if (m_styleDefinition) 161 m_attributes = m_styleDefinition->GetStyle(); 162 163 if (!wxPropertySheetDialog::TransferDataToWindow()) 164 return false; 165 166 return true; 167} 168 169bool wxRichTextFormattingDialog::TransferDataFromWindow() 170{ 171 if (!wxPropertySheetDialog::TransferDataFromWindow()) 172 return false; 173 174 if (m_styleDefinition) 175 m_styleDefinition->GetStyle() = m_attributes; 176 177 return true; 178} 179 180/// Update the display 181bool wxRichTextFormattingDialog::UpdateDisplay() 182{ 183 return TransferDataToWindow(); 184} 185 186/// Apply the styles when a different tab is selected, so the previews are 187/// up to date 188void wxRichTextFormattingDialog::OnTabChanged(wxBookCtrlEvent& event) 189{ 190 if (GetBookCtrl() != event.GetEventObject()) 191 { 192 event.Skip(); 193 return; 194 } 195 196 int oldPageId = event.GetOldSelection(); 197 if (oldPageId != -1) 198 { 199 wxWindow* page = GetBookCtrl()->GetPage(oldPageId); 200 if (page) 201 page->TransferDataFromWindow(); 202 } 203 204 int pageId = event.GetSelection(); 205 if (pageId != -1) 206 { 207 wxWindow* page = GetBookCtrl()->GetPage(pageId); 208 if (page) 209 page->TransferDataToWindow(); 210 } 211} 212 213/// Respond to help command 214void wxRichTextFormattingDialog::OnHelp(wxCommandEvent& event) 215{ 216 int selPage = GetBookCtrl()->GetSelection(); 217 if (selPage != wxNOT_FOUND) 218 { 219 int pageId = m_pageIds[selPage]; 220 if (!GetFormattingDialogFactory()->ShowHelp(pageId, this)) 221 event.Skip(); 222 } 223} 224 225void wxRichTextFormattingDialog::SetFormattingDialogFactory(wxRichTextFormattingDialogFactory* factory) 226{ 227 if (ms_FormattingDialogFactory) 228 delete ms_FormattingDialogFactory; 229 ms_FormattingDialogFactory = factory; 230} 231 232/*! 233 * Factory for formatting dialog 234 */ 235 236/// Create all pages, under the dialog's book control, also calling AddPage 237bool wxRichTextFormattingDialogFactory::CreatePages(long pages, wxRichTextFormattingDialog* dialog) 238{ 239 if (dialog->GetImageList()) 240 dialog->GetBookCtrl()->SetImageList(dialog->GetImageList()); 241 242 int availablePageCount = GetPageIdCount(); 243 int i; 244 bool selected = false; 245 for (i = 0; i < availablePageCount; i ++) 246 { 247 int pageId = GetPageId(i); 248 if (pageId != -1 && (pages & pageId)) 249 { 250 wxString title; 251 wxPanel* panel = CreatePage(pageId, title, dialog); 252 wxASSERT( panel != NULL ); 253 if (panel) 254 { 255 int imageIndex = GetPageImage(pageId); 256 dialog->GetBookCtrl()->AddPage(panel, title, !selected, imageIndex); 257 selected = true; 258 259 dialog->AddPageId(pageId); 260 } 261 } 262 } 263 264 return true; 265} 266 267/// Create a page, given a page identifier 268wxPanel* wxRichTextFormattingDialogFactory::CreatePage(int page, wxString& title, wxRichTextFormattingDialog* dialog) 269{ 270 if (page == wxRICHTEXT_FORMAT_STYLE_EDITOR) 271 { 272 wxRichTextStylePage* page = new wxRichTextStylePage(dialog->GetBookCtrl(), wxID_ANY); 273 title = _("Style"); 274 return page; 275 } 276 else if (page == wxRICHTEXT_FORMAT_FONT) 277 { 278 wxRichTextFontPage* page = new wxRichTextFontPage(dialog->GetBookCtrl(), wxID_ANY); 279 title = _("Font"); 280 return page; 281 } 282 else if (page == wxRICHTEXT_FORMAT_INDENTS_SPACING) 283 { 284 wxRichTextIndentsSpacingPage* page = new wxRichTextIndentsSpacingPage(dialog->GetBookCtrl(), wxID_ANY); 285 title = _("Indents && Spacing"); 286 return page; 287 } 288 else if (page == wxRICHTEXT_FORMAT_TABS) 289 { 290 wxRichTextTabsPage* page = new wxRichTextTabsPage(dialog->GetBookCtrl(), wxID_ANY); 291 title = _("Tabs"); 292 return page; 293 } 294 else if (page == wxRICHTEXT_FORMAT_BULLETS) 295 { 296 wxRichTextBulletsPage* page = new wxRichTextBulletsPage(dialog->GetBookCtrl(), wxID_ANY); 297 title = _("Bullets"); 298 return page; 299 } 300#ifndef __DMC__ 301 else if (page == wxRICHTEXT_FORMAT_LIST_STYLE) 302 { 303 wxRichTextListStylePage* page = new wxRichTextListStylePage(dialog->GetBookCtrl(), wxID_ANY); 304 title = _("List Style"); 305 return page; 306 } 307#endif 308 else 309 return NULL; 310} 311 312/// Enumerate all available page identifiers 313int wxRichTextFormattingDialogFactory::GetPageId(int i) const 314{ 315 int pages[] = { 316 wxRICHTEXT_FORMAT_STYLE_EDITOR, 317 wxRICHTEXT_FORMAT_FONT, 318 wxRICHTEXT_FORMAT_INDENTS_SPACING, 319 wxRICHTEXT_FORMAT_BULLETS, 320 wxRICHTEXT_FORMAT_TABS, 321 wxRICHTEXT_FORMAT_LIST_STYLE }; 322 323 if (i < 0 || i > 5) 324 return -1; 325 326 return pages[i]; 327} 328 329/// Get the number of available page identifiers 330int wxRichTextFormattingDialogFactory::GetPageIdCount() const 331{ 332#ifdef __DMC__ 333 return 5; 334#else 335 return 6; 336#endif 337} 338 339/// Set the sheet style, called at the start of wxRichTextFormattingDialog::Create 340bool wxRichTextFormattingDialogFactory::SetSheetStyle(wxRichTextFormattingDialog* dialog) 341{ 342 bool useToolBook = wxRICHTEXT_USE_TOOLBOOK; 343 if (useToolBook) 344 { 345 int sheetStyle = wxPROPSHEET_SHRINKTOFIT; 346#ifdef __WXMAC__ 347 sheetStyle |= wxPROPSHEET_BUTTONTOOLBOOK; 348#else 349 sheetStyle |= wxPROPSHEET_TOOLBOOK; 350#endif 351 352 dialog->SetSheetStyle(sheetStyle); 353 dialog->SetSheetInnerBorder(0); 354 dialog->SetSheetOuterBorder(0); 355 } 356 357 return true; 358} 359 360/// Create the main dialog buttons 361bool wxRichTextFormattingDialogFactory::CreateButtons(wxRichTextFormattingDialog* dialog) 362{ 363 bool useToolBook = wxRICHTEXT_USE_TOOLBOOK; 364 365 // If using a toolbook, also follow Mac style and don't create buttons 366 int flags = wxOK|wxCANCEL; 367#ifndef __WXWINCE__ 368 if (dialog->GetWindowStyleFlag() & wxRICHTEXT_FORMAT_HELP_BUTTON) 369 flags |= wxHELP; 370#endif 371 372 if (!useToolBook) 373 dialog->CreateButtons(flags); 374 375 return true; 376} 377 378/* 379 * Module to initialise and clean up handlers 380 */ 381 382class wxRichTextFormattingDialogModule: public wxModule 383{ 384DECLARE_DYNAMIC_CLASS(wxRichTextFormattingDialogModule) 385public: 386 wxRichTextFormattingDialogModule() {} 387 bool OnInit() { wxRichTextFormattingDialog::SetFormattingDialogFactory(new wxRichTextFormattingDialogFactory); return true; } 388 void OnExit() { wxRichTextFormattingDialog::SetFormattingDialogFactory(NULL); } 389}; 390 391IMPLEMENT_DYNAMIC_CLASS(wxRichTextFormattingDialogModule, wxModule) 392 393/* 394 * Font preview control 395 */ 396 397BEGIN_EVENT_TABLE(wxRichTextFontPreviewCtrl, wxWindow) 398 EVT_PAINT(wxRichTextFontPreviewCtrl::OnPaint) 399END_EVENT_TABLE() 400 401void wxRichTextFontPreviewCtrl::OnPaint(wxPaintEvent& WXUNUSED(event)) 402{ 403 wxPaintDC dc(this); 404 405 wxSize size = GetSize(); 406 wxFont font = GetFont(); 407 408 if ((GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT) || (GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT)) 409 { 410 double size = static_cast<double>(font.GetPointSize()) / wxSCRIPT_MUL_FACTOR; 411 font.SetPointSize( static_cast<int>(size) ); 412 } 413 414 if ( font.Ok() ) 415 { 416 dc.SetFont(font); 417 // Calculate vertical and horizontal centre 418 long w = 0, h = 0; 419 420 wxString text(_("ABCDEFGabcdefg12345")); 421 if (GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS) 422 text.MakeUpper(); 423 424 dc.GetTextExtent( text, &w, &h); 425 int cx = wxMax(2, (size.x/2) - (w/2)); 426 int cy = wxMax(2, (size.y/2) - (h/2)); 427 428 if ( GetTextEffects() & wxTEXT_ATTR_EFFECT_SUPERSCRIPT ) 429 cy -= h/2; 430 if ( GetTextEffects() & wxTEXT_ATTR_EFFECT_SUBSCRIPT ) 431 cy += h/2; 432 433 dc.SetTextForeground(GetForegroundColour()); 434 dc.SetClippingRegion(2, 2, size.x-4, size.y-4); 435 dc.DrawText(text, cx, cy); 436 437 if (GetTextEffects() & wxTEXT_ATTR_EFFECT_STRIKETHROUGH) 438 { 439 dc.SetPen(wxPen(GetForegroundColour(), 1)); 440 dc.DrawLine(cx, (int) (cy + h/2 + 0.5), cx + w, (int) (cy + h/2 + 0.5)); 441 } 442 443 dc.DestroyClippingRegion(); 444 } 445} 446 447// Helper for pages to get the top-level dialog 448wxRichTextFormattingDialog* wxRichTextFormattingDialog::GetDialog(wxWindow* win) 449{ 450 wxWindow* p = win->GetParent(); 451 while (p && !p->IsKindOf(CLASSINFO(wxRichTextFormattingDialog))) 452 p = p->GetParent(); 453 wxRichTextFormattingDialog* dialog = wxDynamicCast(p, wxRichTextFormattingDialog); 454 return dialog; 455} 456 457 458// Helper for pages to get the attributes 459wxTextAttrEx* wxRichTextFormattingDialog::GetDialogAttributes(wxWindow* win) 460{ 461 wxRichTextFormattingDialog* dialog = GetDialog(win); 462 if (dialog) 463 return & dialog->GetAttributes(); 464 else 465 return NULL; 466} 467 468// Helper for pages to get the style 469wxRichTextStyleDefinition* wxRichTextFormattingDialog::GetDialogStyleDefinition(wxWindow* win) 470{ 471 wxRichTextFormattingDialog* dialog = GetDialog(win); 472 if (dialog) 473 return dialog->GetStyleDefinition(); 474 else 475 return NULL; 476} 477 478/* 479 * A control for displaying a small preview of a colour or bitmap 480 */ 481 482BEGIN_EVENT_TABLE(wxRichTextColourSwatchCtrl, wxControl) 483 EVT_MOUSE_EVENTS(wxRichTextColourSwatchCtrl::OnMouseEvent) 484END_EVENT_TABLE() 485 486IMPLEMENT_CLASS(wxRichTextColourSwatchCtrl, wxControl) 487 488wxRichTextColourSwatchCtrl::wxRichTextColourSwatchCtrl(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style) 489{ 490 if ((style & wxBORDER_MASK) == wxBORDER_DEFAULT) 491#ifdef __WXMSW__ 492 style |= GetThemedBorderStyle(); 493#else 494 style |= wxBORDER_SUNKEN; 495#endif 496 wxControl::Create(parent, id, pos, size, style); 497 498 SetColour(wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)); 499} 500 501wxRichTextColourSwatchCtrl::~wxRichTextColourSwatchCtrl() 502{ 503} 504 505void wxRichTextColourSwatchCtrl::OnMouseEvent(wxMouseEvent& event) 506{ 507 if (event.LeftDown()) 508 { 509 wxWindow* parent = GetParent(); 510 while (parent != NULL && !parent->IsKindOf(CLASSINFO(wxDialog)) && !parent->IsKindOf(CLASSINFO(wxFrame))) 511 parent = parent->GetParent(); 512 513 wxColourData data; 514 data.SetChooseFull(true); 515 data.SetColour(m_colour); 516#if wxUSE_COLOURDLG 517 wxColourDialog *dialog = new wxColourDialog(parent, &data); 518 // Crashes on wxMac (no m_peer) 519#ifndef __WXMAC__ 520 dialog->SetTitle(_("Colour")); 521#endif 522 if (dialog->ShowModal() == wxID_OK) 523 { 524 wxColourData retData = dialog->GetColourData(); 525 m_colour = retData.GetColour(); 526 SetBackgroundColour(m_colour); 527 } 528 dialog->Destroy(); 529#endif // wxUSE_COLOURDLG 530 Refresh(); 531 532 wxCommandEvent event(wxEVT_COMMAND_BUTTON_CLICKED, GetId()); 533 GetEventHandler()->ProcessEvent(event); 534 } 535} 536 537#if wxUSE_HTML 538 539/*! 540 * wxRichTextFontListBox class declaration 541 * A listbox to display styles. 542 */ 543 544IMPLEMENT_CLASS(wxRichTextFontListBox, wxHtmlListBox) 545 546BEGIN_EVENT_TABLE(wxRichTextFontListBox, wxHtmlListBox) 547END_EVENT_TABLE() 548 549wxRichTextFontListBox::wxRichTextFontListBox(wxWindow* parent, wxWindowID id, const wxPoint& pos, 550 const wxSize& size, long style) 551{ 552 if ((style & wxBORDER_MASK) == wxBORDER_DEFAULT) 553#ifdef __WXMSW__ 554 style |= GetThemedBorderStyle(); 555#else 556 style |= wxBORDER_SUNKEN; 557#endif 558 559 Init(); 560 Create(parent, id, pos, size, style); 561} 562 563bool wxRichTextFontListBox::Create(wxWindow* parent, wxWindowID id, const wxPoint& pos, 564 const wxSize& size, long style) 565{ 566 return wxHtmlListBox::Create(parent, id, pos, size, style); 567} 568 569wxRichTextFontListBox::~wxRichTextFontListBox() 570{ 571} 572 573/// Returns the HTML for this item 574wxString wxRichTextFontListBox::OnGetItem(size_t n) const 575{ 576 if (m_faceNames.GetCount() == 0) 577 return wxEmptyString; 578 579 wxString str = CreateHTML(m_faceNames[n]); 580 return str; 581} 582 583/// Get font name for index 584wxString wxRichTextFontListBox::GetFaceName(size_t i) const 585{ 586 return m_faceNames[i]; 587} 588 589/// Set selection for string, returning the index. 590int wxRichTextFontListBox::SetFaceNameSelection(const wxString& name) 591{ 592 int i = m_faceNames.Index(name); 593 SetSelection(i); 594 595 return i; 596} 597 598/// Updates the font list 599void wxRichTextFontListBox::UpdateFonts() 600{ 601 wxArrayString facenames = wxRichTextCtrl::GetAvailableFontNames(); 602 m_faceNames = facenames; 603 m_faceNames.Sort(); 604 605 SetItemCount(m_faceNames.GetCount()); 606 Refresh(); 607} 608 609#if 0 610// Convert a colour to a 6-digit hex string 611static wxString ColourToHexString(const wxColour& col) 612{ 613 wxString hex; 614 615 hex += wxDecToHex(col.Red()); 616 hex += wxDecToHex(col.Green()); 617 hex += wxDecToHex(col.Blue()); 618 619 return hex; 620} 621#endif 622 623/// Creates a suitable HTML fragment for a definition 624wxString wxRichTextFontListBox::CreateHTML(const wxString& facename) const 625{ 626 wxString str = wxT("<font"); 627 628 str << wxT(" size=\"+2\"");; 629 630 if (!facename.IsEmpty() && facename != _("(none)")) 631 str << wxT(" face=\"") << facename << wxT("\""); 632/* 633 if (def->GetStyle().GetTextColour().Ok()) 634 str << wxT(" color=\"#") << ColourToHexString(def->GetStyle().GetTextColour()) << wxT("\""); 635*/ 636 637 str << wxT(">"); 638 639 bool hasBold = false; 640 641 if (hasBold) 642 str << wxT("<b>"); 643 644 str += facename; 645 646 if (hasBold) 647 str << wxT("</b>"); 648 649 str << wxT("</font>"); 650 651 return str; 652} 653 654#endif 655 // wxUSE_HTML 656 657 658#endif 659 // wxUSE_RICHTEXT 660