don't break lines in the middle of word
[wxWidgets.git] / src / html / winpars.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: winpars.cpp
3 // Purpose: wxHtmlParser class (generic parser)
4 // Author: Vaclav Slavik
5 // RCS-ID: $Id$
6 // Copyright: (c) 1999 Vaclav Slavik
7 // Licence: wxWindows Licence
8 /////////////////////////////////////////////////////////////////////////////
9
10
11 #ifdef __GNUG__
12 #pragma implementation "winpars.h"
13 #endif
14
15 #include "wx/wxprec.h"
16
17 #include "wx/defs.h"
18 #if wxUSE_HTML && wxUSE_STREAMS
19
20 #ifdef __BORLANDC__
21 #pragma hdrstop
22 #endif
23
24 #ifndef WXPRECOMP
25 #include "wx/intl.h"
26 #include "wx/dc.h"
27 #endif
28
29 #include "wx/html/htmldefs.h"
30 #include "wx/html/winpars.h"
31 #include "wx/html/htmlwin.h"
32 #include "wx/fontmap.h"
33 #include "wx/log.h"
34
35
36 //-----------------------------------------------------------------------------
37 // wxHtmlWinParser
38 //-----------------------------------------------------------------------------
39
40
41 wxList wxHtmlWinParser::m_Modules;
42
43 wxHtmlWinParser::wxHtmlWinParser(wxHtmlWindow *wnd) : wxHtmlParser()
44 {
45 m_tmpStrBuf = NULL;
46 m_tmpStrBufSize = 0;
47 m_Window = wnd;
48 m_Container = NULL;
49 m_DC = NULL;
50 m_CharHeight = m_CharWidth = 0;
51 m_UseLink = FALSE;
52 #if !wxUSE_UNICODE
53 m_EncConv = NULL;
54 m_InputEnc = wxFONTENCODING_ISO8859_1;
55 m_OutputEnc = wxFONTENCODING_DEFAULT;
56 #endif
57 m_lastWordCell = NULL;
58
59 {
60 int i, j, k, l, m;
61 for (i = 0; i < 2; i++)
62 for (j = 0; j < 2; j++)
63 for (k = 0; k < 2; k++)
64 for (l = 0; l < 2; l++)
65 for (m = 0; m < 7; m++)
66 {
67 m_FontsTable[i][j][k][l][m] = NULL;
68 m_FontsFacesTable[i][j][k][l][m] = wxEmptyString;
69 #if !wxUSE_UNICODE
70 m_FontsEncTable[i][j][k][l][m] = wxFONTENCODING_DEFAULT;
71 #endif
72 }
73
74 SetFonts(wxEmptyString, wxEmptyString, NULL);
75 }
76
77 // fill in wxHtmlParser's tables:
78 wxList::compatibility_iterator node = m_Modules.GetFirst();
79 while (node)
80 {
81 wxHtmlTagsModule *mod = (wxHtmlTagsModule*) node->GetData();
82 mod->FillHandlersTable(this);
83 node = node->GetNext();
84 }
85 }
86
87 wxHtmlWinParser::~wxHtmlWinParser()
88 {
89 int i, j, k, l, m;
90
91 for (i = 0; i < 2; i++)
92 for (j = 0; j < 2; j++)
93 for (k = 0; k < 2; k++)
94 for (l = 0; l < 2; l++)
95 for (m = 0; m < 7; m++)
96 {
97 if (m_FontsTable[i][j][k][l][m] != NULL)
98 delete m_FontsTable[i][j][k][l][m];
99 }
100 #if !wxUSE_UNICODE
101 delete m_EncConv;
102 #endif
103 delete[] m_tmpStrBuf;
104 }
105
106 void wxHtmlWinParser::AddModule(wxHtmlTagsModule *module)
107 {
108 m_Modules.Append(module);
109 }
110
111 void wxHtmlWinParser::RemoveModule(wxHtmlTagsModule *module)
112 {
113 m_Modules.DeleteObject(module);
114 }
115
116 void wxHtmlWinParser::SetFonts(wxString normal_face, wxString fixed_face,
117 const int *sizes)
118 {
119 static int default_sizes[7] =
120 {
121 wxHTML_FONT_SIZE_1,
122 wxHTML_FONT_SIZE_2,
123 wxHTML_FONT_SIZE_3,
124 wxHTML_FONT_SIZE_4,
125 wxHTML_FONT_SIZE_5,
126 wxHTML_FONT_SIZE_6,
127 wxHTML_FONT_SIZE_7
128 };
129
130 if (sizes == NULL) sizes = default_sizes;
131
132 int i, j, k, l, m;
133
134 for (i = 0; i < 7; i++) m_FontsSizes[i] = sizes[i];
135 m_FontFaceFixed = fixed_face;
136 m_FontFaceNormal = normal_face;
137
138 #if !wxUSE_UNICODE
139 SetInputEncoding(m_InputEnc);
140 #endif
141
142 for (i = 0; i < 2; i++)
143 for (j = 0; j < 2; j++)
144 for (k = 0; k < 2; k++)
145 for (l = 0; l < 2; l++)
146 for (m = 0; m < 7; m++) {
147 if (m_FontsTable[i][j][k][l][m] != NULL)
148 {
149 delete m_FontsTable[i][j][k][l][m];
150 m_FontsTable[i][j][k][l][m] = NULL;
151 }
152 }
153 }
154
155 void wxHtmlWinParser::InitParser(const wxString& source)
156 {
157 wxHtmlParser::InitParser(source);
158 wxASSERT_MSG(m_DC != NULL, wxT("no DC assigned to wxHtmlWinParser!!"));
159
160 m_FontBold = m_FontItalic = m_FontUnderlined = m_FontFixed = FALSE;
161 m_FontSize = 3; //default one
162 CreateCurrentFont(); // we're selecting default font into
163 m_DC->GetTextExtent( wxT("H"), &m_CharWidth, &m_CharHeight);
164 /* NOTE : we're not using GetCharWidth/Height() because
165 of differences under X and win
166 */
167
168 m_UseLink = FALSE;
169 m_Link = wxHtmlLinkInfo( wxT(""), wxT("") );
170 m_LinkColor.Set(0, 0, 0xFF);
171 m_ActualColor.Set(0, 0, 0);
172 m_Align = wxHTML_ALIGN_LEFT;
173 m_tmpLastWasSpace = FALSE;
174 m_lastWordCell = NULL;
175
176 OpenContainer();
177 OpenContainer();
178
179 #if !wxUSE_UNICODE
180 wxString charset = ExtractCharsetInformation(source);
181 if (!charset.empty())
182 {
183 wxFontEncoding enc = wxFontMapper::Get()->CharsetToEncoding(charset);
184 if (enc != wxFONTENCODING_SYSTEM)
185 SetInputEncoding(enc);
186 }
187 #endif
188
189 m_Container->InsertCell(new wxHtmlColourCell(m_ActualColor));
190 m_Container->InsertCell(new wxHtmlFontCell(CreateCurrentFont()));
191 }
192
193 void wxHtmlWinParser::DoneParser()
194 {
195 m_Container = NULL;
196 #if !wxUSE_UNICODE
197 SetInputEncoding(wxFONTENCODING_ISO8859_1); // for next call
198 #endif
199 wxHtmlParser::DoneParser();
200 }
201
202 wxObject* wxHtmlWinParser::GetProduct()
203 {
204 wxHtmlContainerCell *top;
205
206 CloseContainer();
207 OpenContainer();
208
209 top = m_Container;
210 while (top->GetParent()) top = top->GetParent();
211 return top;
212 }
213
214 wxFSFile *wxHtmlWinParser::OpenURL(wxHtmlURLType type,
215 const wxString& url) const
216 {
217 // FIXME - normalize the URL to full path before passing to
218 // OnOpeningURL!!
219 if ( m_Window )
220 {
221 wxString myurl(url);
222 wxHtmlOpeningStatus status;
223 for (;;)
224 {
225 wxString redirect;
226 status = m_Window->OnOpeningURL(type, myurl, &redirect);
227 if ( status != wxHTML_REDIRECT )
228 break;
229
230 myurl = redirect;
231 }
232
233 if ( status == wxHTML_BLOCK )
234 return NULL;
235
236 return GetFS()->OpenFile(myurl);
237 }
238
239 return wxHtmlParser::OpenURL(type, url);
240 }
241
242 void wxHtmlWinParser::AddText(const wxChar* txt)
243 {
244 wxHtmlCell *c;
245 size_t i = 0,
246 x,
247 lng = wxStrlen(txt);
248 register wxChar d;
249 int templen = 0;
250 wxChar nbsp = GetEntitiesParser()->GetCharForCode(160 /* nbsp */);
251
252 if (lng+1 > m_tmpStrBufSize)
253 {
254 delete[] m_tmpStrBuf;
255 m_tmpStrBuf = new wxChar[lng+1];
256 m_tmpStrBufSize = lng+1;
257 }
258 wxChar *temp = m_tmpStrBuf;
259
260 if (m_tmpLastWasSpace)
261 {
262 while ((i < lng) &&
263 ((txt[i] == wxT('\n')) || (txt[i] == wxT('\r')) || (txt[i] == wxT(' ')) ||
264 (txt[i] == wxT('\t')))) i++;
265 }
266
267 while (i < lng)
268 {
269 x = 0;
270 d = temp[templen++] = txt[i];
271 if ((d == wxT('\n')) || (d == wxT('\r')) || (d == wxT(' ')) || (d == wxT('\t')))
272 {
273 i++, x++;
274 while ((i < lng) && ((txt[i] == wxT('\n')) || (txt[i] == wxT('\r')) ||
275 (txt[i] == wxT(' ')) || (txt[i] == wxT('\t')))) i++, x++;
276 }
277 else i++;
278
279 if (x)
280 {
281 temp[templen-1] = wxT(' ');
282 temp[templen] = 0;
283 templen = 0;
284 #if !wxUSE_UNICODE
285 if (m_EncConv)
286 m_EncConv->Convert(temp);
287 #endif
288 size_t len = wxStrlen(temp);
289 for (size_t j = 0; j < len; j++)
290 if (temp[j] == nbsp)
291 temp[j] = wxT(' ');
292 c = new wxHtmlWordCell(temp, *(GetDC()));
293 if (m_UseLink)
294 c->SetLink(m_Link);
295 m_Container->InsertCell(c);
296 ((wxHtmlWordCell*)c)->SetPreviousWord(m_lastWordCell);
297 m_lastWordCell = (wxHtmlWordCell*)c;
298 m_tmpLastWasSpace = TRUE;
299 }
300 }
301
302 if (templen && (templen > 1 || temp[0] != wxT(' ')))
303 {
304 temp[templen] = 0;
305 #if !wxUSE_UNICODE
306 if (m_EncConv)
307 m_EncConv->Convert(temp);
308 #endif
309 size_t len = wxStrlen(temp);
310 for (size_t j = 0; j < len; j++)
311 if (temp[j] == nbsp)
312 temp[j] = wxT(' ');
313 c = new wxHtmlWordCell(temp, *(GetDC()));
314 if (m_UseLink)
315 c->SetLink(m_Link);
316 m_Container->InsertCell(c);
317 ((wxHtmlWordCell*)c)->SetPreviousWord(m_lastWordCell);
318 m_lastWordCell = (wxHtmlWordCell*)c;
319 m_tmpLastWasSpace = FALSE;
320 }
321 }
322
323
324
325 wxHtmlContainerCell* wxHtmlWinParser::OpenContainer()
326 {
327 m_Container = new wxHtmlContainerCell(m_Container);
328 m_Container->SetAlignHor(m_Align);
329 m_tmpLastWasSpace = TRUE;
330 /* to avoid space being first character in paragraph */
331 return m_Container;
332 }
333
334
335
336 wxHtmlContainerCell* wxHtmlWinParser::SetContainer(wxHtmlContainerCell *c)
337 {
338 m_tmpLastWasSpace = TRUE;
339 /* to avoid space being first character in paragraph */
340 return m_Container = c;
341 }
342
343
344
345 wxHtmlContainerCell* wxHtmlWinParser::CloseContainer()
346 {
347 m_Container = m_Container->GetParent();
348 return m_Container;
349 }
350
351
352 void wxHtmlWinParser::SetFontSize(int s)
353 {
354 if (s < 1) s = 1;
355 else if (s > 7) s = 7;
356 m_FontSize = s;
357 }
358
359
360
361 wxFont* wxHtmlWinParser::CreateCurrentFont()
362 {
363 int fb = GetFontBold(),
364 fi = GetFontItalic(),
365 fu = GetFontUnderlined(),
366 ff = GetFontFixed(),
367 fs = GetFontSize() - 1 /*remap from <1;7> to <0;6>*/ ;
368
369 wxString face = ff ? m_FontFaceFixed : m_FontFaceNormal;
370 wxString *faceptr = &(m_FontsFacesTable[fb][fi][fu][ff][fs]);
371 wxFont **fontptr = &(m_FontsTable[fb][fi][fu][ff][fs]);
372 #if !wxUSE_UNICODE
373 wxFontEncoding *encptr = &(m_FontsEncTable[fb][fi][fu][ff][fs]);
374 #endif
375
376 if (*fontptr != NULL && (*faceptr != face
377 #if !wxUSE_UNICODE
378 || *encptr != m_OutputEnc
379 #endif
380 ))
381 {
382 delete *fontptr;
383 *fontptr = NULL;
384 }
385
386 if (*fontptr == NULL)
387 {
388 *faceptr = face;
389 *fontptr = new wxFont(
390 (int) (m_FontsSizes[fs] * m_PixelScale),
391 ff ? wxMODERN : wxSWISS,
392 fi ? wxITALIC : wxNORMAL,
393 fb ? wxBOLD : wxNORMAL,
394 fu ? TRUE : FALSE, face
395 #if wxUSE_UNICODE
396 );
397 #else
398 , m_OutputEnc);
399 *encptr = m_OutputEnc;
400 #endif
401 }
402 m_DC->SetFont(**fontptr);
403 return (*fontptr);
404 }
405
406
407
408 void wxHtmlWinParser::SetLink(const wxHtmlLinkInfo& link)
409 {
410 m_Link = link;
411 m_UseLink = (link.GetHref() != wxEmptyString);
412 }
413
414
415 void wxHtmlWinParser::SetFontFace(const wxString& face)
416 {
417 if (GetFontFixed()) m_FontFaceFixed = face;
418 else m_FontFaceNormal = face;
419
420 #if !wxUSE_UNICODE
421 if (m_InputEnc != wxFONTENCODING_DEFAULT)
422 SetInputEncoding(m_InputEnc);
423 #endif
424 }
425
426
427
428 #if !wxUSE_UNICODE
429 void wxHtmlWinParser::SetInputEncoding(wxFontEncoding enc)
430 {
431 m_InputEnc = m_OutputEnc = wxFONTENCODING_DEFAULT;
432 if (m_EncConv)
433 {
434 delete m_EncConv;
435 m_EncConv = NULL;
436 }
437
438 if (enc == wxFONTENCODING_DEFAULT) return;
439
440 wxFontEncoding altfix, altnorm;
441 bool availfix, availnorm;
442
443 // exact match?
444 availnorm = wxFontMapper::Get()->IsEncodingAvailable(enc, m_FontFaceNormal);
445 availfix = wxFontMapper::Get()->IsEncodingAvailable(enc, m_FontFaceFixed);
446 if (availnorm && availfix)
447 m_OutputEnc = enc;
448
449 // alternatives?
450 else if (wxFontMapper::Get()->GetAltForEncoding(enc, &altnorm, m_FontFaceNormal, FALSE) &&
451 wxFontMapper::Get()->GetAltForEncoding(enc, &altfix, m_FontFaceFixed, FALSE) &&
452 altnorm == altfix)
453 m_OutputEnc = altnorm;
454
455 // at least normal face?
456 else if (availnorm)
457 m_OutputEnc = enc;
458 else if (wxFontMapper::Get()->GetAltForEncoding(enc, &altnorm, m_FontFaceNormal, FALSE))
459 m_OutputEnc = altnorm;
460
461 // okay, let convert to ISO_8859-1, available always
462 else
463 m_OutputEnc = wxFONTENCODING_DEFAULT;
464
465 m_InputEnc = enc;
466 if (m_OutputEnc == wxFONTENCODING_DEFAULT)
467 GetEntitiesParser()->SetEncoding(wxFONTENCODING_SYSTEM);
468 else
469 GetEntitiesParser()->SetEncoding(m_OutputEnc);
470
471 if (m_InputEnc == m_OutputEnc) return;
472
473 m_EncConv = new wxEncodingConverter();
474 if (!m_EncConv->Init(m_InputEnc,
475 (m_OutputEnc == wxFONTENCODING_DEFAULT) ?
476 wxFONTENCODING_ISO8859_1 : m_OutputEnc,
477 wxCONVERT_SUBSTITUTE))
478 { // total failture :-(
479 wxLogError(_("Failed to display HTML document in %s encoding"),
480 wxFontMapper::GetEncodingName(enc).c_str());
481 m_InputEnc = m_OutputEnc = wxFONTENCODING_DEFAULT;
482 delete m_EncConv;
483 m_EncConv = NULL;
484 }
485 }
486 #endif
487
488
489
490
491 //-----------------------------------------------------------------------------
492 // wxHtmlWinTagHandler
493 //-----------------------------------------------------------------------------
494
495 IMPLEMENT_ABSTRACT_CLASS(wxHtmlWinTagHandler, wxHtmlTagHandler)
496
497 //-----------------------------------------------------------------------------
498 // wxHtmlTagsModule
499 //-----------------------------------------------------------------------------
500
501 // NB: This is *NOT* winpars.cpp's initialization and shutdown code!!
502 // This module is an ancestor for tag handlers modules defined
503 // in m_*.cpp files with TAGS_MODULE_BEGIN...TAGS_MODULE_END construct.
504 //
505 // Do not add any winpars.cpp shutdown or initialization code to it,
506 // create a new module instead!
507
508 IMPLEMENT_DYNAMIC_CLASS(wxHtmlTagsModule, wxModule)
509
510 bool wxHtmlTagsModule::OnInit()
511 {
512 wxHtmlWinParser::AddModule(this);
513 return TRUE;
514 }
515
516 void wxHtmlTagsModule::OnExit()
517 {
518 wxHtmlWinParser::RemoveModule(this);
519 }
520
521 #endif
522