1///////////////////////////////////////////////////////////////////////////// 2// Name: src/richtext/richtexthtml.cpp 3// Purpose: HTML I/O for wxRichTextCtrl 4// Author: Julian Smart 5// Modified by: 6// Created: 2005-09-30 7// RCS-ID: $Id: richtexthtml.cpp 64162 2010-04-27 16:10:27Z 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/richtexthtml.h" 22#include "wx/richtext/richtextstyles.h" 23 24#ifndef WX_PRECOMP 25#endif 26 27#include "wx/filename.h" 28#include "wx/wfstream.h" 29#include "wx/txtstrm.h" 30 31#if wxUSE_FILESYSTEM 32#include "wx/filesys.h" 33#include "wx/fs_mem.h" 34#endif 35 36IMPLEMENT_DYNAMIC_CLASS(wxRichTextHTMLHandler, wxRichTextFileHandler) 37 38int wxRichTextHTMLHandler::sm_fileCounter = 1; 39 40wxRichTextHTMLHandler::wxRichTextHTMLHandler(const wxString& name, const wxString& ext, int type) 41 : wxRichTextFileHandler(name, ext, type), m_buffer(NULL), m_font(false), m_inTable(false) 42{ 43 m_fontSizeMapping.Add(8); 44 m_fontSizeMapping.Add(10); 45 m_fontSizeMapping.Add(13); 46 m_fontSizeMapping.Add(17); 47 m_fontSizeMapping.Add(22); 48 m_fontSizeMapping.Add(30); 49 m_fontSizeMapping.Add(100); 50} 51 52/// Can we handle this filename (if using files)? By default, checks the extension. 53bool wxRichTextHTMLHandler::CanHandle(const wxString& filename) const 54{ 55 wxString path, file, ext; 56 wxSplitPath(filename, & path, & file, & ext); 57 58 return (ext.Lower() == wxT("html") || ext.Lower() == wxT("htm")); 59} 60 61 62#if wxUSE_STREAMS 63bool wxRichTextHTMLHandler::DoLoadFile(wxRichTextBuffer *WXUNUSED(buffer), wxInputStream& WXUNUSED(stream)) 64{ 65 return false; 66} 67 68/* 69 * We need to output only _changes_ in character formatting. 70 */ 71 72bool wxRichTextHTMLHandler::DoSaveFile(wxRichTextBuffer *buffer, wxOutputStream& stream) 73{ 74 m_buffer = buffer; 75 76 ClearTemporaryImageLocations(); 77 78 buffer->Defragment(); 79 80#if wxUSE_UNICODE 81 wxCSConv* customEncoding = NULL; 82 wxMBConv* conv = NULL; 83 if (!GetEncoding().IsEmpty()) 84 { 85 customEncoding = new wxCSConv(GetEncoding()); 86 if (!customEncoding->IsOk()) 87 { 88 delete customEncoding; 89 customEncoding = NULL; 90 } 91 } 92 if (customEncoding) 93 conv = customEncoding; 94 else 95 conv = & wxConvUTF8; 96#endif 97 98 { 99#if wxUSE_UNICODE 100 wxTextOutputStream str(stream, wxEOL_NATIVE, *conv); 101#else 102 wxTextOutputStream str(stream, wxEOL_NATIVE); 103#endif 104 105 wxTextAttrEx currentParaStyle = buffer->GetAttributes(); 106 wxTextAttrEx currentCharStyle = buffer->GetAttributes(); 107 108 if ((GetFlags() & wxRICHTEXT_HANDLER_NO_HEADER_FOOTER) == 0) 109 str << wxT("<html><head></head><body>\n"); 110 111 OutputFont(currentParaStyle, str); 112 113 m_font = false; 114 m_inTable = false; 115 116 m_indents.Clear(); 117 m_listTypes.Clear(); 118 119 wxRichTextObjectList::compatibility_iterator node = buffer->GetChildren().GetFirst(); 120 while (node) 121 { 122 wxRichTextParagraph* para = wxDynamicCast(node->GetData(), wxRichTextParagraph); 123 wxASSERT (para != NULL); 124 125 if (para) 126 { 127 wxTextAttrEx paraStyle(para->GetCombinedAttributes()); 128 129 BeginParagraphFormatting(currentParaStyle, paraStyle, str); 130 131 wxRichTextObjectList::compatibility_iterator node2 = para->GetChildren().GetFirst(); 132 while (node2) 133 { 134 wxRichTextObject* obj = node2->GetData(); 135 wxRichTextPlainText* textObj = wxDynamicCast(obj, wxRichTextPlainText); 136 if (textObj && !textObj->IsEmpty()) 137 { 138 wxTextAttrEx charStyle(para->GetCombinedAttributes(obj->GetAttributes())); 139 BeginCharacterFormatting(currentCharStyle, charStyle, paraStyle, str); 140 141 wxString text = textObj->GetText(); 142 143 if (charStyle.HasTextEffects() && (charStyle.GetTextEffects() & wxTEXT_ATTR_EFFECT_CAPITALS)) 144 text.MakeUpper(); 145 146 wxString toReplace = wxRichTextLineBreakChar; 147 text.Replace(toReplace, wxT("<br>")); 148 149 str << text; 150 151 EndCharacterFormatting(currentCharStyle, charStyle, paraStyle, str); 152 } 153 154 wxRichTextImage* image = wxDynamicCast(obj, wxRichTextImage); 155 if( image && (!image->IsEmpty() || image->GetImageBlock().GetData())) 156 WriteImage( image, stream ); 157 158 node2 = node2->GetNext(); 159 } 160 161 EndParagraphFormatting(currentParaStyle, paraStyle, str); 162 163 str << wxT("\n"); 164 } 165 node = node->GetNext(); 166 } 167 168 CloseLists(-1, str); 169 170 str << wxT("</font>"); 171 172 if ((GetFlags() & wxRICHTEXT_HANDLER_NO_HEADER_FOOTER) == 0) 173 str << wxT("</body></html>"); 174 175 str << wxT("\n"); 176 } 177 178#if wxUSE_UNICODE 179 if (customEncoding) 180 delete customEncoding; 181#endif 182 183 m_buffer = NULL; 184 185 return true; 186} 187 188void wxRichTextHTMLHandler::BeginCharacterFormatting(const wxTextAttrEx& currentStyle, const wxTextAttrEx& thisStyle, const wxTextAttrEx& WXUNUSED(paraStyle), wxTextOutputStream& str) 189{ 190 wxString style; 191 192 // Is there any change in the font properties of the item? 193 if (thisStyle.GetFont().GetFaceName() != currentStyle.GetFont().GetFaceName()) 194 { 195 wxString faceName(thisStyle.GetFont().GetFaceName()); 196 style += wxString::Format(wxT(" face=\"%s\""), faceName.c_str()); 197 } 198 if (thisStyle.GetFont().GetPointSize() != currentStyle.GetFont().GetPointSize()) 199 style += wxString::Format(wxT(" size=\"%ld\""), PtToSize(thisStyle.GetFont().GetPointSize())); 200 if (thisStyle.GetTextColour() != currentStyle.GetTextColour() ) 201 { 202 wxString color(thisStyle.GetTextColour().GetAsString(wxC2S_HTML_SYNTAX)); 203 style += wxString::Format(wxT(" color=\"%s\""), color.c_str()); 204 } 205 206 if (style.size()) 207 { 208 str << wxString::Format(wxT("<font %s >"), style.c_str()); 209 m_font = true; 210 } 211 212 if (thisStyle.GetFont().GetWeight() == wxBOLD) 213 str << wxT("<b>"); 214 if (thisStyle.GetFont().GetStyle() == wxITALIC) 215 str << wxT("<i>"); 216 if (thisStyle.GetFont().GetUnderlined()) 217 str << wxT("<u>"); 218 219 if (thisStyle.HasURL()) 220 str << wxT("<a href=\"") << thisStyle.GetURL() << wxT("\">"); 221} 222 223void wxRichTextHTMLHandler::EndCharacterFormatting(const wxTextAttrEx& WXUNUSED(currentStyle), const wxTextAttrEx& thisStyle, const wxTextAttrEx& WXUNUSED(paraStyle), wxTextOutputStream& stream) 224{ 225 if (thisStyle.HasURL()) 226 stream << wxT("</a>"); 227 228 if (thisStyle.GetFont().GetUnderlined()) 229 stream << wxT("</u>"); 230 if (thisStyle.GetFont().GetStyle() == wxITALIC) 231 stream << wxT("</i>"); 232 if (thisStyle.GetFont().GetWeight() == wxBOLD) 233 stream << wxT("</b>"); 234 235 if (m_font) 236 { 237 m_font = false; 238 stream << wxT("</font>"); 239 } 240} 241 242/// Begin paragraph formatting 243void wxRichTextHTMLHandler::BeginParagraphFormatting(const wxTextAttrEx& WXUNUSED(currentStyle), const wxTextAttrEx& thisStyle, wxTextOutputStream& str) 244{ 245 if (thisStyle.HasPageBreak()) 246 { 247 str << wxT("<div style=\"page-break-after:always\"></div>\n"); 248 } 249 250 if (thisStyle.HasLeftIndent() && thisStyle.GetLeftIndent() != 0) 251 { 252 if (thisStyle.HasBulletStyle()) 253 { 254 int indent = thisStyle.GetLeftIndent(); 255 256 // Close levels high than this 257 CloseLists(indent, str); 258 259 if (m_indents.GetCount() > 0 && indent == m_indents.Last()) 260 { 261 // Same level, no need to start a new list 262 } 263 else if (m_indents.GetCount() == 0 || indent > m_indents.Last()) 264 { 265 m_indents.Add(indent); 266 267 wxString tag; 268 int listType = TypeOfList(thisStyle, tag); 269 m_listTypes.Add(listType); 270 271 // wxHTML needs an extra <p> before a list when using <p> ... </p> in previous paragraphs. 272 // TODO: pass a flag that indicates we're using wxHTML. 273 str << wxT("<p>\n"); 274 275 str << tag; 276 } 277 278 str << wxT("<li> "); 279 } 280 else 281 { 282 CloseLists(-1, str); 283 284 wxString align = GetAlignment(thisStyle); 285 str << wxString::Format(wxT("<p align=\"%s\""), align.c_str()); 286 287 wxString styleStr; 288 289 if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) && thisStyle.HasParagraphSpacingBefore()) 290 { 291 float spacingBeforeMM = thisStyle.GetParagraphSpacingBefore() / 10.0; 292 293 styleStr += wxString::Format(wxT("margin-top: %.2fmm; "), spacingBeforeMM); 294 } 295 if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) && thisStyle.HasParagraphSpacingAfter()) 296 { 297 float spacingAfterMM = thisStyle.GetParagraphSpacingAfter() / 10.0; 298 299 styleStr += wxString::Format(wxT("margin-bottom: %.2fmm; "), spacingAfterMM); 300 } 301 302 float indentLeftMM = (thisStyle.GetLeftIndent() + thisStyle.GetLeftSubIndent())/10.0; 303 if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) && (indentLeftMM > 0.0)) 304 { 305 styleStr += wxString::Format(wxT("margin-left: %.2fmm; "), indentLeftMM); 306 } 307 float indentRightMM = thisStyle.GetRightIndent()/10.0; 308 if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) && thisStyle.HasRightIndent() && (indentRightMM > 0.0)) 309 { 310 styleStr += wxString::Format(wxT("margin-right: %.2fmm; "), indentRightMM); 311 } 312 // First line indentation 313 float firstLineIndentMM = - thisStyle.GetLeftSubIndent() / 10.0; 314 if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) && (firstLineIndentMM > 0.0)) 315 { 316 styleStr += wxString::Format(wxT("text-indent: %.2fmm; "), firstLineIndentMM); 317 } 318 319 if (!styleStr.IsEmpty()) 320 str << wxT(" style=\"") << styleStr << wxT("\""); 321 322 str << wxT(">"); 323 324 // TODO: convert to pixels 325 int indentPixels = indentLeftMM*10/4; 326 327 if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) == 0) 328 { 329 // Use a table to do indenting if we don't have CSS 330 str << wxString::Format(wxT("<table border=0 cellpadding=0 cellspacing=0><tr><td width=\"%d\"></td><td>"), indentPixels); 331 m_inTable = true; 332 } 333 334 if (((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) == 0) && (thisStyle.GetLeftSubIndent() < 0)) 335 { 336 str << SymbolicIndent( - thisStyle.GetLeftSubIndent()); 337 } 338 } 339 } 340 else 341 { 342 CloseLists(-1, str); 343 344 wxString align = GetAlignment(thisStyle); 345 str << wxString::Format(wxT("<p align=\"%s\""), align.c_str()); 346 347 wxString styleStr; 348 349 if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) && thisStyle.HasParagraphSpacingBefore()) 350 { 351 float spacingBeforeMM = thisStyle.GetParagraphSpacingBefore() / 10.0; 352 353 styleStr += wxString::Format(wxT("margin-top: %.2fmm; "), spacingBeforeMM); 354 } 355 if ((GetFlags() & wxRICHTEXT_HANDLER_USE_CSS) && thisStyle.HasParagraphSpacingAfter()) 356 { 357 float spacingAfterMM = thisStyle.GetParagraphSpacingAfter() / 10.0; 358 359 styleStr += wxString::Format(wxT("margin-bottom: %.2fmm; "), spacingAfterMM); 360 } 361 362 if (!styleStr.IsEmpty()) 363 str << wxT(" style=\"") << styleStr << wxT("\""); 364 365 str << wxT(">"); 366 } 367 OutputFont(thisStyle, str); 368} 369 370/// End paragraph formatting 371void wxRichTextHTMLHandler::EndParagraphFormatting(const wxTextAttrEx& WXUNUSED(currentStyle), const wxTextAttrEx& thisStyle, wxTextOutputStream& stream) 372{ 373 if (thisStyle.HasFont()) 374 stream << wxT("</font>"); 375 376 if (m_inTable) 377 { 378 stream << wxT("</td></tr></table></p>\n"); 379 m_inTable = false; 380 } 381 else if (!thisStyle.HasBulletStyle()) 382 stream << wxT("</p>\n"); 383} 384 385/// Closes lists to level (-1 means close all) 386void wxRichTextHTMLHandler::CloseLists(int level, wxTextOutputStream& str) 387{ 388 // Close levels high than this 389 int i = m_indents.GetCount()-1; 390 while (i >= 0) 391 { 392 int l = m_indents[i]; 393 if (l > level) 394 { 395 if (m_listTypes[i] == 0) 396 str << wxT("</ol>"); 397 else 398 str << wxT("</ul>"); 399 m_indents.RemoveAt(i); 400 m_listTypes.RemoveAt(i); 401 } 402 else 403 break; 404 i --; 405 } 406} 407 408/// Output font tag 409void wxRichTextHTMLHandler::OutputFont(const wxTextAttrEx& style, wxTextOutputStream& stream) 410{ 411 if (style.HasFont()) 412 { 413 stream << wxString::Format(wxT("<font face=\"%s\" size=\"%ld\""), style.GetFont().GetFaceName().c_str(), PtToSize(style.GetFont().GetPointSize())); 414 if (style.HasTextColour()) 415 stream << wxString::Format(wxT(" color=\"%s\""), style.GetTextColour().GetAsString(wxC2S_HTML_SYNTAX).c_str()); 416 stream << wxT(" >"); 417 } 418} 419 420int wxRichTextHTMLHandler::TypeOfList( const wxTextAttrEx& thisStyle, wxString& tag ) 421{ 422 // We can use number attribute of li tag but not all the browsers support it. 423 // also wxHtmlWindow doesn't support type attribute. 424 425 bool m_is_ul = false; 426 if (thisStyle.GetBulletStyle() == (wxTEXT_ATTR_BULLET_STYLE_ARABIC|wxTEXT_ATTR_BULLET_STYLE_PERIOD)) 427 tag = wxT("<ol type=\"1\">"); 428 else if (thisStyle.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_LETTERS_UPPER) 429 tag = wxT("<ol type=\"A\">"); 430 else if (thisStyle.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_LETTERS_LOWER) 431 tag = wxT("<ol type=\"a\">"); 432 else if (thisStyle.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_ROMAN_UPPER) 433 tag = wxT("<ol type=\"I\">"); 434 else if (thisStyle.GetBulletStyle() == wxTEXT_ATTR_BULLET_STYLE_ROMAN_LOWER) 435 tag = wxT("<ol type=\"i\">"); 436 else 437 { 438 tag = wxT("<ul>"); 439 m_is_ul = true; 440 } 441 442 if (m_is_ul) 443 return 1; 444 else 445 return 0; 446} 447 448wxString wxRichTextHTMLHandler::GetAlignment( const wxTextAttrEx& thisStyle ) 449{ 450 switch( thisStyle.GetAlignment() ) 451 { 452 case wxTEXT_ALIGNMENT_LEFT: 453 return wxT("left"); 454 case wxTEXT_ALIGNMENT_RIGHT: 455 return wxT("right"); 456 case wxTEXT_ALIGNMENT_CENTER: 457 return wxT("center"); 458 case wxTEXT_ALIGNMENT_JUSTIFIED: 459 return wxT("justify"); 460 default: 461 return wxT("left"); 462 } 463} 464 465void wxRichTextHTMLHandler::WriteImage(wxRichTextImage* image, wxOutputStream& stream) 466{ 467 wxTextOutputStream str(stream); 468 469 str << wxT("<img src=\""); 470 471#if wxUSE_FILESYSTEM 472 if (GetFlags() & wxRICHTEXT_HANDLER_SAVE_IMAGES_TO_MEMORY) 473 { 474 if (!image->GetImage().Ok() && image->GetImageBlock().GetData()) 475 image->LoadFromBlock(); 476 if (image->GetImage().Ok() && !image->GetImageBlock().GetData()) 477 image->MakeBlock(); 478 479 if (image->GetImage().Ok()) 480 { 481 wxString ext(image->GetImageBlock().GetExtension()); 482 wxString tempFilename(wxString::Format(wxT("image%d.%s"), sm_fileCounter, (const wxChar*) ext)); 483 wxMemoryFSHandler::AddFile(tempFilename, image->GetImage(), image->GetImageBlock().GetImageType()); 484 485 m_imageLocations.Add(tempFilename); 486 487 str << wxT("memory:") << tempFilename; 488 } 489 else 490 str << wxT("memory:?"); 491 492 sm_fileCounter ++; 493 } 494 else if (GetFlags() & wxRICHTEXT_HANDLER_SAVE_IMAGES_TO_FILES) 495 { 496 if (!image->GetImage().Ok() && image->GetImageBlock().GetData()) 497 image->LoadFromBlock(); 498 if (image->GetImage().Ok() && !image->GetImageBlock().GetData()) 499 image->MakeBlock(); 500 501 if (image->GetImage().Ok()) 502 { 503 wxString tempDir(GetTempDir()); 504 if (tempDir.IsEmpty()) 505 tempDir = wxFileName::GetTempDir(); 506 507 wxString ext(image->GetImageBlock().GetExtension()); 508 wxString tempFilename(wxString::Format(wxT("%s/image%d.%s"), (const wxChar*) tempDir, sm_fileCounter, (const wxChar*) ext)); 509 image->GetImageBlock().Write(tempFilename); 510 511 m_imageLocations.Add(tempFilename); 512 513 str << wxFileSystem::FileNameToURL(tempFilename); 514 } 515 else 516 str << wxT("file:?"); 517 518 sm_fileCounter ++; 519 } 520 else // if (GetFlags() & wxRICHTEXT_HANDLER_SAVE_IMAGES_TO_BASE64) // this is implied 521#endif 522 { 523 str << wxT("data:"); 524 str << GetMimeType(image->GetImageBlock().GetImageType()); 525 str << wxT(";base64,"); 526 527 if (image->GetImage().Ok() && !image->GetImageBlock().GetData()) 528 image->MakeBlock(); 529 530 wxChar* data = b64enc( image->GetImageBlock().GetData(), image->GetImageBlock().GetDataSize() ); 531 str << data; 532 533 delete[] data; 534 } 535 536 str << wxT("\" />"); 537} 538 539long wxRichTextHTMLHandler::PtToSize(long size) 540{ 541 int i; 542 int len = m_fontSizeMapping.GetCount(); 543 for (i = 0; i < len; i++) 544 if (size <= m_fontSizeMapping[i]) 545 return i+1; 546 return 7; 547} 548 549wxString wxRichTextHTMLHandler::SymbolicIndent(long indent) 550{ 551 wxString in; 552 for(;indent > 0; indent -= 20) 553 in.Append( wxT(" ") ); 554 return in; 555} 556 557const wxChar* wxRichTextHTMLHandler::GetMimeType(int imageType) 558{ 559 switch(imageType) 560 { 561 case wxBITMAP_TYPE_BMP: 562 return wxT("image/bmp"); 563 case wxBITMAP_TYPE_TIF: 564 return wxT("image/tiff"); 565 case wxBITMAP_TYPE_GIF: 566 return wxT("image/gif"); 567 case wxBITMAP_TYPE_PNG: 568 return wxT("image/png"); 569 case wxBITMAP_TYPE_JPEG: 570 return wxT("image/jpeg"); 571 default: 572 return wxT("image/unknown"); 573 } 574} 575 576// exim-style base64 encoder 577wxChar* wxRichTextHTMLHandler::b64enc( unsigned char* input, size_t in_len ) 578{ 579 // elements of enc64 array must be 8 bit values 580 // otherwise encoder will fail 581 // hmmm.. Does wxT macro define a char as 16 bit value 582 // when compiling with UNICODE option? 583 static const wxChar enc64[] = wxT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); 584 wxChar* output = new wxChar[4*((in_len+2)/3)+1]; 585 wxChar* p = output; 586 587 while( in_len-- > 0 ) 588 { 589 register wxChar a, b; 590 591 a = *input++; 592 593 *p++ = enc64[ (a >> 2) & 0x3f ]; 594 595 if( in_len-- == 0 ) 596 { 597 *p++ = enc64[ (a << 4 ) & 0x30 ]; 598 *p++ = '='; 599 *p++ = '='; 600 break; 601 } 602 603 b = *input++; 604 605 *p++ = enc64[(( a << 4 ) | ((b >> 4) &0xf )) & 0x3f]; 606 607 if( in_len-- == 0 ) 608 { 609 *p++ = enc64[ (b << 2) & 0x3f ]; 610 *p++ = '='; 611 break; 612 } 613 614 a = *input++; 615 616 *p++ = enc64[ ((( b << 2 ) & 0x3f ) | ((a >> 6)& 0x3)) & 0x3f ]; 617 618 *p++ = enc64[ a & 0x3f ]; 619 } 620 *p = 0; 621 622 return output; 623} 624#endif 625// wxUSE_STREAMS 626 627/// Delete the in-memory or temporary files generated by the last operation 628bool wxRichTextHTMLHandler::DeleteTemporaryImages() 629{ 630 return DeleteTemporaryImages(GetFlags(), m_imageLocations); 631} 632 633/// Delete the in-memory or temporary files generated by the last operation 634bool wxRichTextHTMLHandler::DeleteTemporaryImages(int flags, const wxArrayString& imageLocations) 635{ 636 size_t i; 637 for (i = 0; i < imageLocations.GetCount(); i++) 638 { 639 wxString location = imageLocations[i]; 640 641 if (flags & wxRICHTEXT_HANDLER_SAVE_IMAGES_TO_MEMORY) 642 { 643#if wxUSE_FILESYSTEM 644 wxMemoryFSHandler::RemoveFile(location); 645#endif 646 } 647 else if (flags & wxRICHTEXT_HANDLER_SAVE_IMAGES_TO_FILES) 648 { 649 if (wxFileExists(location)) 650 wxRemoveFile(location); 651 } 652 } 653 654 return true; 655} 656 657 658#endif 659// wxUSE_RICHTEXT 660 661