compilation fix for optimizing compilers
[wxWidgets.git] / src / html / htmltag.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: htmltag.cpp
3 // Purpose: wxHtmlTag class (represents single tag)
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 "htmltag.h"
13 #endif
14
15 #include "wx/wxprec.h"
16
17 #include "wx/defs.h"
18 #if wxUSE_HTML
19
20 #ifdef __BORLANDC__
21 #pragma hdrstop
22 #endif
23
24 #ifndef WXPRECOMP
25 #endif
26
27 #include "wx/html/htmltag.h"
28 #include "wx/html/htmlpars.h"
29 #include "wx/colour.h"
30 #include <stdio.h> // for vsscanf
31 #include <stdarg.h>
32
33
34 //-----------------------------------------------------------------------------
35 // wxHtmlTagsCache
36 //-----------------------------------------------------------------------------
37
38 struct wxHtmlCacheItem
39 {
40 // this is "pos" value passed to wxHtmlTag's constructor.
41 // it is position of '<' character of the tag
42 int Key;
43
44 // end positions for the tag:
45 // end1 is '<' of ending tag,
46 // end2 is '>' or both are
47 // -1 if there is no ending tag for this one...
48 // or -2 if this is ending tag </...>
49 int End1, End2;
50
51 // name of this tag
52 wxChar *Name;
53 };
54
55
56 IMPLEMENT_CLASS(wxHtmlTagsCache,wxObject)
57
58 #define CACHE_INCREMENT 64
59
60 bool wxIsCDATAElement(const wxChar *tag)
61 {
62 return (wxStrcmp(tag, _T("SCRIPT")) == 0) ||
63 (wxStrcmp(tag, _T("STYLE")) == 0);
64 }
65
66 wxHtmlTagsCache::wxHtmlTagsCache(const wxString& source)
67 {
68 const wxChar *src = source.c_str();
69 int tg, stpos;
70 int lng = source.Length();
71 wxChar tagBuffer[256];
72
73 m_Cache = NULL;
74 m_CacheSize = 0;
75 m_CachePos = 0;
76
77 int pos = 0;
78 while (pos < lng)
79 {
80 if (src[pos] == wxT('<')) // tag found:
81 {
82 if (m_CacheSize % CACHE_INCREMENT == 0)
83 m_Cache = (wxHtmlCacheItem*) realloc(m_Cache, (m_CacheSize + CACHE_INCREMENT) * sizeof(wxHtmlCacheItem));
84 tg = m_CacheSize++;
85 m_Cache[tg].Key = stpos = pos++;
86
87 int i;
88 for ( i = 0;
89 pos < lng && i < (int)WXSIZEOF(tagBuffer) - 1 &&
90 src[pos] != wxT('>') && !wxIsspace(src[pos]);
91 i++, pos++ )
92 {
93 tagBuffer[i] = wxToupper(src[pos]);
94 }
95 tagBuffer[i] = _T('\0');
96
97 m_Cache[tg].Name = new wxChar[i+1];
98 memcpy(m_Cache[tg].Name, tagBuffer, (i+1)*sizeof(wxChar));
99
100 while (pos < lng && src[pos] != wxT('>')) pos++;
101
102 if (src[stpos+1] == wxT('/')) // ending tag:
103 {
104 m_Cache[tg].End1 = m_Cache[tg].End2 = -2;
105 // find matching begin tag:
106 for (i = tg; i >= 0; i--)
107 if ((m_Cache[i].End1 == -1) && (wxStrcmp(m_Cache[i].Name, tagBuffer+1) == 0))
108 {
109 m_Cache[i].End1 = stpos;
110 m_Cache[i].End2 = pos + 1;
111 break;
112 }
113 }
114 else
115 {
116 m_Cache[tg].End1 = m_Cache[tg].End2 = -1;
117
118 if (wxIsCDATAElement(tagBuffer))
119 {
120 // find next matching tag
121 int tag_len = wxStrlen(tagBuffer);
122 while (pos < lng)
123 {
124 // find the ending tag
125 while (pos + 1 < lng &&
126 (src[pos] != '<' || src[pos+1] != '/'))
127 ++pos;
128 if (src[pos] == '<')
129 ++pos;
130
131 // see if it matches
132 int match_pos = 0;
133 while (pos < lng && match_pos < tag_len && src[pos] != '>' && src[pos] != '<') {
134 if (wxToupper(src[pos]) == tagBuffer[match_pos]) {
135 ++match_pos;
136 }
137 else if (src[pos] == wxT(' ') || src[pos] == wxT('\n') ||
138 src[pos] == wxT('\r') || src[pos] == wxT('\t')) {
139 // need to skip over these
140 }
141 else {
142 match_pos = 0;
143 }
144 ++pos;
145 }
146
147 // found a match
148 if (match_pos == tag_len) {
149 pos = pos - tag_len - 3;
150 stpos = pos;
151 break;
152 }
153 else {
154 ++pos;
155 }
156 }
157 }
158 }
159 }
160
161 pos++;
162 }
163
164 // ok, we're done, now we'll free .Name members of cache - we don't need it anymore:
165 for (int i = 0; i < m_CacheSize; i++)
166 {
167 delete[] m_Cache[i].Name;
168 m_Cache[i].Name = NULL;
169 }
170 }
171
172 void wxHtmlTagsCache::QueryTag(int at, int* end1, int* end2)
173 {
174 if (m_Cache == NULL) return;
175 if (m_Cache[m_CachePos].Key != at)
176 {
177 int delta = (at < m_Cache[m_CachePos].Key) ? -1 : 1;
178 do
179 {
180 m_CachePos += delta;
181 }
182 while (m_Cache[m_CachePos].Key != at);
183 }
184 *end1 = m_Cache[m_CachePos].End1;
185 *end2 = m_Cache[m_CachePos].End2;
186 }
187
188
189
190
191 //-----------------------------------------------------------------------------
192 // wxHtmlTag
193 //-----------------------------------------------------------------------------
194
195 IMPLEMENT_CLASS(wxHtmlTag,wxObject)
196
197 wxHtmlTag::wxHtmlTag(wxHtmlTag *parent,
198 const wxString& source, int pos, int end_pos,
199 wxHtmlTagsCache *cache,
200 wxHtmlEntitiesParser *entParser) : wxObject()
201 {
202 /* Setup DOM relations */
203
204 m_Next = NULL;
205 m_FirstChild = m_LastChild = NULL;
206 m_Parent = parent;
207 if (parent)
208 {
209 m_Prev = m_Parent->m_LastChild;
210 if (m_Prev == NULL)
211 m_Parent->m_FirstChild = this;
212 else
213 m_Prev->m_Next = this;
214 m_Parent->m_LastChild = this;
215 }
216 else
217 m_Prev = NULL;
218
219 /* Find parameters and their values: */
220
221 int i;
222 wxChar c;
223
224 // fill-in name, params and begin pos:
225 i = pos+1;
226
227 // find tag's name and convert it to uppercase:
228 while ((i < end_pos) &&
229 ((c = source[i++]) != wxT(' ') && c != wxT('\r') &&
230 c != wxT('\n') && c != wxT('\t') &&
231 c != wxT('>')))
232 {
233 if ((c >= wxT('a')) && (c <= wxT('z')))
234 c -= (wxT('a') - wxT('A'));
235 m_Name << c;
236 }
237
238 // if the tag has parameters, read them and "normalize" them,
239 // i.e. convert to uppercase, replace whitespaces by spaces and
240 // remove whitespaces around '=':
241 if (source[i-1] != wxT('>'))
242 {
243 #define IS_WHITE(c) (c == wxT(' ') || c == wxT('\r') || \
244 c == wxT('\n') || c == wxT('\t'))
245 wxString pname, pvalue;
246 wxChar quote;
247 enum
248 {
249 ST_BEFORE_NAME = 1,
250 ST_NAME,
251 ST_BEFORE_EQ,
252 ST_BEFORE_VALUE,
253 ST_VALUE
254 } state;
255
256 quote = 0;
257 state = ST_BEFORE_NAME;
258 while (i < end_pos)
259 {
260 c = source[i++];
261
262 if (c == wxT('>') && !(state == ST_VALUE && quote != 0))
263 {
264 if (state == ST_BEFORE_EQ || state == ST_NAME)
265 {
266 m_ParamNames.Add(pname);
267 m_ParamValues.Add(wxEmptyString);
268 }
269 else if (state == ST_VALUE && quote == 0)
270 {
271 m_ParamNames.Add(pname);
272 if (entParser)
273 m_ParamValues.Add(entParser->Parse(pvalue));
274 else
275 m_ParamValues.Add(pvalue);
276 }
277 break;
278 }
279 switch (state)
280 {
281 case ST_BEFORE_NAME:
282 if (!IS_WHITE(c))
283 {
284 pname = c;
285 state = ST_NAME;
286 }
287 break;
288 case ST_NAME:
289 if (IS_WHITE(c))
290 state = ST_BEFORE_EQ;
291 else if (c == wxT('='))
292 state = ST_BEFORE_VALUE;
293 else
294 pname << c;
295 break;
296 case ST_BEFORE_EQ:
297 if (c == wxT('='))
298 state = ST_BEFORE_VALUE;
299 else if (!IS_WHITE(c))
300 {
301 m_ParamNames.Add(pname);
302 m_ParamValues.Add(wxEmptyString);
303 pname = c;
304 state = ST_NAME;
305 }
306 break;
307 case ST_BEFORE_VALUE:
308 if (!IS_WHITE(c))
309 {
310 if (c == wxT('"') || c == wxT('\''))
311 quote = c, pvalue = wxEmptyString;
312 else
313 quote = 0, pvalue = c;
314 state = ST_VALUE;
315 }
316 break;
317 case ST_VALUE:
318 if ((quote != 0 && c == quote) ||
319 (quote == 0 && IS_WHITE(c)))
320 {
321 m_ParamNames.Add(pname);
322 if (quote == 0)
323 {
324 // VS: backward compatibility, no real reason,
325 // but wxHTML code relies on this... :(
326 pvalue.MakeUpper();
327 }
328 if (entParser)
329 m_ParamValues.Add(entParser->Parse(pvalue));
330 else
331 m_ParamValues.Add(pvalue);
332 state = ST_BEFORE_NAME;
333 }
334 else
335 pvalue << c;
336 break;
337 }
338 }
339
340 #undef IS_WHITE
341 }
342 m_Begin = i;
343
344 cache->QueryTag(pos, &m_End1, &m_End2);
345 if (m_End1 > end_pos) m_End1 = end_pos;
346 if (m_End2 > end_pos) m_End2 = end_pos;
347 }
348
349 wxHtmlTag::~wxHtmlTag()
350 {
351 wxHtmlTag *t1, *t2;
352 t1 = m_FirstChild;
353 while (t1)
354 {
355 t2 = t1->GetNextSibling();
356 delete t1;
357 t1 = t2;
358 }
359 }
360
361 bool wxHtmlTag::HasParam(const wxString& par) const
362 {
363 return (m_ParamNames.Index(par, FALSE) != wxNOT_FOUND);
364 }
365
366 wxString wxHtmlTag::GetParam(const wxString& par, bool with_commas) const
367 {
368 int index = m_ParamNames.Index(par, FALSE);
369 if (index == wxNOT_FOUND)
370 return wxEmptyString;
371 if (with_commas)
372 {
373 // VS: backward compatibility, seems to be never used by wxHTML...
374 wxString s;
375 s << wxT('"') << m_ParamValues[index] << wxT('"');
376 return s;
377 }
378 else
379 return m_ParamValues[index];
380 }
381
382 int wxHtmlTag::ScanParam(const wxString& par,
383 const wxChar *format,
384 void *param) const
385 {
386 wxString parval = GetParam(par);
387 return wxSscanf(parval, format, param);
388 }
389
390 bool wxHtmlTag::GetParamAsColour(const wxString& par, wxColour *clr) const
391 {
392 wxString str = GetParam(par);
393
394 if (str.IsEmpty()) return FALSE;
395 if (str.GetChar(0) == wxT('#'))
396 {
397 unsigned long tmp;
398 if (ScanParam(par, wxT("#%lX"), &tmp) != 1)
399 return FALSE;
400 *clr = wxColour((unsigned char)((tmp & 0xFF0000) >> 16),
401 (unsigned char)((tmp & 0x00FF00) >> 8),
402 (unsigned char)(tmp & 0x0000FF));
403 return TRUE;
404 }
405 else
406 {
407 // Handle colours defined in HTML 4.0:
408 #define HTML_COLOUR(name,r,g,b) \
409 if (str.IsSameAs(wxT(name), FALSE)) \
410 { *clr = wxColour(r,g,b); return TRUE; }
411 HTML_COLOUR("black", 0x00,0x00,0x00)
412 HTML_COLOUR("silver", 0xC0,0xC0,0xC0)
413 HTML_COLOUR("gray", 0x80,0x80,0x80)
414 HTML_COLOUR("white", 0xFF,0xFF,0xFF)
415 HTML_COLOUR("maroon", 0x80,0x00,0x00)
416 HTML_COLOUR("red", 0xFF,0x00,0x00)
417 HTML_COLOUR("purple", 0x80,0x00,0x80)
418 HTML_COLOUR("fuchsia", 0xFF,0x00,0xFF)
419 HTML_COLOUR("green", 0x00,0x80,0x00)
420 HTML_COLOUR("lime", 0x00,0xFF,0x00)
421 HTML_COLOUR("olive", 0x80,0x80,0x00)
422 HTML_COLOUR("yellow", 0xFF,0xFF,0x00)
423 HTML_COLOUR("navy", 0x00,0x00,0x80)
424 HTML_COLOUR("blue", 0x00,0x00,0xFF)
425 HTML_COLOUR("teal", 0x00,0x80,0x80)
426 HTML_COLOUR("aqua", 0x00,0xFF,0xFF)
427 #undef HTML_COLOUR
428 }
429
430 return FALSE;
431 }
432
433 bool wxHtmlTag::GetParamAsInt(const wxString& par, int *clr) const
434 {
435 if (!HasParam(par)) return FALSE;
436 long i;
437 bool succ = GetParam(par).ToLong(&i);
438 *clr = (int)i;
439 return succ;
440 }
441
442 wxString wxHtmlTag::GetAllParams() const
443 {
444 // VS: this function is for backward compatiblity only,
445 // never used by wxHTML
446 wxString s;
447 size_t cnt = m_ParamNames.GetCount();
448 for (size_t i = 0; i < cnt; i++)
449 {
450 s << m_ParamNames[i];
451 s << wxT('=');
452 if (m_ParamValues[i].Find(wxT('"')) != wxNOT_FOUND)
453 s << wxT('\'') << m_ParamValues[i] << wxT('\'');
454 else
455 s << wxT('"') << m_ParamValues[i] << wxT('"');
456 }
457 return s;
458 }
459
460 wxHtmlTag *wxHtmlTag::GetFirstSibling() const
461 {
462 if (m_Parent)
463 return m_Parent->m_FirstChild;
464 else
465 {
466 wxHtmlTag *cur = (wxHtmlTag*)this;
467 while (cur->m_Prev)
468 cur = cur->m_Prev;
469 return cur;
470 }
471 }
472
473 wxHtmlTag *wxHtmlTag::GetLastSibling() const
474 {
475 if (m_Parent)
476 return m_Parent->m_LastChild;
477 else
478 {
479 wxHtmlTag *cur = (wxHtmlTag*)this;
480 while (cur->m_Next)
481 cur = cur->m_Next;
482 return cur;
483 }
484 }
485
486 wxHtmlTag *wxHtmlTag::GetNextTag() const
487 {
488 if (m_FirstChild) return m_FirstChild;
489 if (m_Next) return m_Next;
490 wxHtmlTag *cur = m_Parent;
491 if (!cur) return NULL;
492 while (cur->m_Parent && !cur->m_Next)
493 cur = cur->m_Parent;
494 return cur->m_Next;
495 }
496
497 #endif