Added wxUnusedVar for Borland and fixed 2 release mode warnings for Borland (patch...
[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 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
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 lng = source.Length();
70 wxChar tagBuffer[256];
71
72 m_Cache = NULL;
73 m_CacheSize = 0;
74 m_CachePos = 0;
75
76 int pos = 0;
77 while (pos < lng)
78 {
79 if (src[pos] == wxT('<')) // tag found:
80 {
81 if (m_CacheSize % CACHE_INCREMENT == 0)
82 m_Cache = (wxHtmlCacheItem*) realloc(m_Cache, (m_CacheSize + CACHE_INCREMENT) * sizeof(wxHtmlCacheItem));
83 int tg = m_CacheSize++;
84 int stpos = pos++;
85 m_Cache[tg].Key = stpos;
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 // cast to wxChar needed to suppress warning in
135 // Unicode build
136 if ((wxChar)wxToupper(src[pos]) == tagBuffer[match_pos]) {
137 ++match_pos;
138 }
139 else if (src[pos] == wxT(' ') || src[pos] == wxT('\n') ||
140 src[pos] == wxT('\r') || src[pos] == wxT('\t')) {
141 // need to skip over these
142 }
143 else {
144 match_pos = 0;
145 }
146 ++pos;
147 }
148
149 // found a match
150 if (match_pos == tag_len) {
151 // These 2 lines commented:
152 //pos = pos - tag_len - 3; pos is not used outside of while loop
153 //stpos = pos; Value is local to while loop, no need to set it
154 break;
155 }
156 else {
157 ++pos;
158 }
159 }
160 }
161 }
162 }
163
164 pos++;
165 }
166
167 // ok, we're done, now we'll free .Name members of cache - we don't need it anymore:
168 for (int i = 0; i < m_CacheSize; i++)
169 {
170 delete[] m_Cache[i].Name;
171 m_Cache[i].Name = NULL;
172 }
173 }
174
175 void wxHtmlTagsCache::QueryTag(int at, int* end1, int* end2)
176 {
177 if (m_Cache == NULL) return;
178 if (m_Cache[m_CachePos].Key != at)
179 {
180 int delta = (at < m_Cache[m_CachePos].Key) ? -1 : 1;
181 do
182 {
183 m_CachePos += delta;
184 }
185 while (m_Cache[m_CachePos].Key != at);
186 }
187 *end1 = m_Cache[m_CachePos].End1;
188 *end2 = m_Cache[m_CachePos].End2;
189 }
190
191
192
193
194 //-----------------------------------------------------------------------------
195 // wxHtmlTag
196 //-----------------------------------------------------------------------------
197
198 IMPLEMENT_CLASS(wxHtmlTag,wxObject)
199
200 wxHtmlTag::wxHtmlTag(wxHtmlTag *parent,
201 const wxString& source, int pos, int end_pos,
202 wxHtmlTagsCache *cache,
203 wxHtmlEntitiesParser *entParser) : wxObject()
204 {
205 /* Setup DOM relations */
206
207 m_Next = NULL;
208 m_FirstChild = m_LastChild = NULL;
209 m_Parent = parent;
210 if (parent)
211 {
212 m_Prev = m_Parent->m_LastChild;
213 if (m_Prev == NULL)
214 m_Parent->m_FirstChild = this;
215 else
216 m_Prev->m_Next = this;
217 m_Parent->m_LastChild = this;
218 }
219 else
220 m_Prev = NULL;
221
222 /* Find parameters and their values: */
223
224 int i;
225 wxChar c;
226
227 // fill-in name, params and begin pos:
228 i = pos+1;
229
230 // find tag's name and convert it to uppercase:
231 while ((i < end_pos) &&
232 ((c = source[i++]) != wxT(' ') && c != wxT('\r') &&
233 c != wxT('\n') && c != wxT('\t') &&
234 c != wxT('>')))
235 {
236 if ((c >= wxT('a')) && (c <= wxT('z')))
237 c -= (wxT('a') - wxT('A'));
238 m_Name << c;
239 }
240
241 // if the tag has parameters, read them and "normalize" them,
242 // i.e. convert to uppercase, replace whitespaces by spaces and
243 // remove whitespaces around '=':
244 if (source[i-1] != wxT('>'))
245 {
246 #define IS_WHITE(c) (c == wxT(' ') || c == wxT('\r') || \
247 c == wxT('\n') || c == wxT('\t'))
248 wxString pname, pvalue;
249 wxChar quote;
250 enum
251 {
252 ST_BEFORE_NAME = 1,
253 ST_NAME,
254 ST_BEFORE_EQ,
255 ST_BEFORE_VALUE,
256 ST_VALUE
257 } state;
258
259 quote = 0;
260 state = ST_BEFORE_NAME;
261 while (i < end_pos)
262 {
263 c = source[i++];
264
265 if (c == wxT('>') && !(state == ST_VALUE && quote != 0))
266 {
267 if (state == ST_BEFORE_EQ || state == ST_NAME)
268 {
269 m_ParamNames.Add(pname);
270 m_ParamValues.Add(wxEmptyString);
271 }
272 else if (state == ST_VALUE && quote == 0)
273 {
274 m_ParamNames.Add(pname);
275 if (entParser)
276 m_ParamValues.Add(entParser->Parse(pvalue));
277 else
278 m_ParamValues.Add(pvalue);
279 }
280 break;
281 }
282 switch (state)
283 {
284 case ST_BEFORE_NAME:
285 if (!IS_WHITE(c))
286 {
287 pname = c;
288 state = ST_NAME;
289 }
290 break;
291 case ST_NAME:
292 if (IS_WHITE(c))
293 state = ST_BEFORE_EQ;
294 else if (c == wxT('='))
295 state = ST_BEFORE_VALUE;
296 else
297 pname << c;
298 break;
299 case ST_BEFORE_EQ:
300 if (c == wxT('='))
301 state = ST_BEFORE_VALUE;
302 else if (!IS_WHITE(c))
303 {
304 m_ParamNames.Add(pname);
305 m_ParamValues.Add(wxEmptyString);
306 pname = c;
307 state = ST_NAME;
308 }
309 break;
310 case ST_BEFORE_VALUE:
311 if (!IS_WHITE(c))
312 {
313 if (c == wxT('"') || c == wxT('\''))
314 quote = c, pvalue = wxEmptyString;
315 else
316 quote = 0, pvalue = c;
317 state = ST_VALUE;
318 }
319 break;
320 case ST_VALUE:
321 if ((quote != 0 && c == quote) ||
322 (quote == 0 && IS_WHITE(c)))
323 {
324 m_ParamNames.Add(pname);
325 if (quote == 0)
326 {
327 // VS: backward compatibility, no real reason,
328 // but wxHTML code relies on this... :(
329 pvalue.MakeUpper();
330 }
331 if (entParser)
332 m_ParamValues.Add(entParser->Parse(pvalue));
333 else
334 m_ParamValues.Add(pvalue);
335 state = ST_BEFORE_NAME;
336 }
337 else
338 pvalue << c;
339 break;
340 }
341 }
342
343 #undef IS_WHITE
344 }
345 m_Begin = i;
346
347 cache->QueryTag(pos, &m_End1, &m_End2);
348 if (m_End1 > end_pos) m_End1 = end_pos;
349 if (m_End2 > end_pos) m_End2 = end_pos;
350 }
351
352 wxHtmlTag::~wxHtmlTag()
353 {
354 wxHtmlTag *t1, *t2;
355 t1 = m_FirstChild;
356 while (t1)
357 {
358 t2 = t1->GetNextSibling();
359 delete t1;
360 t1 = t2;
361 }
362 }
363
364 bool wxHtmlTag::HasParam(const wxString& par) const
365 {
366 return (m_ParamNames.Index(par, false) != wxNOT_FOUND);
367 }
368
369 wxString wxHtmlTag::GetParam(const wxString& par, bool with_commas) const
370 {
371 int index = m_ParamNames.Index(par, false);
372 if (index == wxNOT_FOUND)
373 return wxEmptyString;
374 if (with_commas)
375 {
376 // VS: backward compatibility, seems to be never used by wxHTML...
377 wxString s;
378 s << wxT('"') << m_ParamValues[index] << wxT('"');
379 return s;
380 }
381 else
382 return m_ParamValues[index];
383 }
384
385 int wxHtmlTag::ScanParam(const wxString& par,
386 const wxChar *format,
387 void *param) const
388 {
389 wxString parval = GetParam(par);
390 return wxSscanf(parval, format, param);
391 }
392
393 bool wxHtmlTag::GetParamAsColour(const wxString& par, wxColour *clr) const
394 {
395 wxString str = GetParam(par);
396
397 if (str.IsEmpty()) return false;
398 if (str.GetChar(0) == wxT('#'))
399 {
400 unsigned long tmp;
401 if (ScanParam(par, wxT("#%lX"), &tmp) != 1)
402 return false;
403 *clr = wxColour((unsigned char)((tmp & 0xFF0000) >> 16),
404 (unsigned char)((tmp & 0x00FF00) >> 8),
405 (unsigned char)(tmp & 0x0000FF));
406 return true;
407 }
408 else
409 {
410 // Handle colours defined in HTML 4.0:
411 #define HTML_COLOUR(name,r,g,b) \
412 if (str.IsSameAs(wxT(name), false)) \
413 { *clr = wxColour(r,g,b); return true; }
414 HTML_COLOUR("black", 0x00,0x00,0x00)
415 HTML_COLOUR("silver", 0xC0,0xC0,0xC0)
416 HTML_COLOUR("gray", 0x80,0x80,0x80)
417 HTML_COLOUR("white", 0xFF,0xFF,0xFF)
418 HTML_COLOUR("maroon", 0x80,0x00,0x00)
419 HTML_COLOUR("red", 0xFF,0x00,0x00)
420 HTML_COLOUR("purple", 0x80,0x00,0x80)
421 HTML_COLOUR("fuchsia", 0xFF,0x00,0xFF)
422 HTML_COLOUR("green", 0x00,0x80,0x00)
423 HTML_COLOUR("lime", 0x00,0xFF,0x00)
424 HTML_COLOUR("olive", 0x80,0x80,0x00)
425 HTML_COLOUR("yellow", 0xFF,0xFF,0x00)
426 HTML_COLOUR("navy", 0x00,0x00,0x80)
427 HTML_COLOUR("blue", 0x00,0x00,0xFF)
428 HTML_COLOUR("teal", 0x00,0x80,0x80)
429 HTML_COLOUR("aqua", 0x00,0xFF,0xFF)
430 #undef HTML_COLOUR
431 }
432
433 return false;
434 }
435
436 bool wxHtmlTag::GetParamAsInt(const wxString& par, int *clr) const
437 {
438 if (!HasParam(par)) return false;
439 long i;
440 bool succ = GetParam(par).ToLong(&i);
441 *clr = (int)i;
442 return succ;
443 }
444
445 wxString wxHtmlTag::GetAllParams() const
446 {
447 // VS: this function is for backward compatiblity only,
448 // never used by wxHTML
449 wxString s;
450 size_t cnt = m_ParamNames.GetCount();
451 for (size_t i = 0; i < cnt; i++)
452 {
453 s << m_ParamNames[i];
454 s << wxT('=');
455 if (m_ParamValues[i].Find(wxT('"')) != wxNOT_FOUND)
456 s << wxT('\'') << m_ParamValues[i] << wxT('\'');
457 else
458 s << wxT('"') << m_ParamValues[i] << wxT('"');
459 }
460 return s;
461 }
462
463 wxHtmlTag *wxHtmlTag::GetFirstSibling() const
464 {
465 if (m_Parent)
466 return m_Parent->m_FirstChild;
467 else
468 {
469 wxHtmlTag *cur = (wxHtmlTag*)this;
470 while (cur->m_Prev)
471 cur = cur->m_Prev;
472 return cur;
473 }
474 }
475
476 wxHtmlTag *wxHtmlTag::GetLastSibling() const
477 {
478 if (m_Parent)
479 return m_Parent->m_LastChild;
480 else
481 {
482 wxHtmlTag *cur = (wxHtmlTag*)this;
483 while (cur->m_Next)
484 cur = cur->m_Next;
485 return cur;
486 }
487 }
488
489 wxHtmlTag *wxHtmlTag::GetNextTag() const
490 {
491 if (m_FirstChild) return m_FirstChild;
492 if (m_Next) return m_Next;
493 wxHtmlTag *cur = m_Parent;
494 if (!cur) return NULL;
495 while (cur->m_Parent && !cur->m_Next)
496 cur = cur->m_Parent;
497 return cur->m_Next;
498 }
499
500 #endif