]> git.saurik.com Git - wxWidgets.git/blame - src/html/htmltag.cpp
doubleclick selects word
[wxWidgets.git] / src / html / htmltag.cpp
CommitLineData
5526e819
VS
1/////////////////////////////////////////////////////////////////////////////
2// Name: htmltag.cpp
3// Purpose: wxHtmlTag class (represents single tag)
4// Author: Vaclav Slavik
69941f05 5// RCS-ID: $Id$
5526e819
VS
6// Copyright: (c) 1999 Vaclav Slavik
7// Licence: wxWindows Licence
8/////////////////////////////////////////////////////////////////////////////
9
10
11#ifdef __GNUG__
1aedb1dd 12#pragma implementation "htmltag.h"
5526e819
VS
13#endif
14
3096bd2f 15#include "wx/wxprec.h"
5526e819
VS
16
17#include "wx/defs.h"
18#if wxUSE_HTML
19
2b5f62a0 20#ifdef __BORLANDC__
5526e819
VS
21#pragma hdrstop
22#endif
23
24#ifndef WXPRECOMP
5526e819
VS
25#endif
26
69941f05 27#include "wx/html/htmltag.h"
daa616fc 28#include "wx/html/htmlpars.h"
fc1f2125 29#include "wx/colour.h"
7e1e0960 30#include <stdio.h> // for vsscanf
5526e819
VS
31#include <stdarg.h>
32
33
5526e819
VS
34//-----------------------------------------------------------------------------
35// wxHtmlTagsCache
36//-----------------------------------------------------------------------------
37
5e8e25e7
VS
38struct 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
5526e819
VS
56IMPLEMENT_CLASS(wxHtmlTagsCache,wxObject)
57
58#define CACHE_INCREMENT 64
59
07cc7ddc 60bool wxIsCDATAElement(const wxChar *tag)
7c6cd4a8
VS
61{
62 return (wxStrcmp(tag, _T("SCRIPT")) == 0) ||
63 (wxStrcmp(tag, _T("STYLE")) == 0);
64}
65
5526e819
VS
66wxHtmlTagsCache::wxHtmlTagsCache(const wxString& source)
67{
66a77a74 68 const wxChar *src = source.c_str();
14d36de8 69 int tg, stpos;
5526e819 70 int lng = source.Length();
8cd82622 71 wxChar tagBuffer[256];
5526e819
VS
72
73 m_Cache = NULL;
74 m_CacheSize = 0;
75 m_CachePos = 0;
76
14d36de8 77 int pos = 0;
8cd82622 78 while (pos < lng)
4f9297b0
VS
79 {
80 if (src[pos] == wxT('<')) // tag found:
a914db0f 81 {
5526e819 82 if (m_CacheSize % CACHE_INCREMENT == 0)
5e8e25e7 83 m_Cache = (wxHtmlCacheItem*) realloc(m_Cache, (m_CacheSize + CACHE_INCREMENT) * sizeof(wxHtmlCacheItem));
5526e819
VS
84 tg = m_CacheSize++;
85 m_Cache[tg].Key = stpos = pos++;
8cd82622 86
4f22f506 87 int i;
8cd82622 88 for ( i = 0;
4f22f506 89 pos < lng && i < (int)WXSIZEOF(tagBuffer) - 1 &&
8cd82622
VZ
90 src[pos] != wxT('>') && !wxIsspace(src[pos]);
91 i++, pos++ )
a914db0f 92 {
8cd82622 93 tagBuffer[i] = wxToupper(src[pos]);
5526e819 94 }
8cd82622
VZ
95 tagBuffer[i] = _T('\0');
96
66a77a74 97 m_Cache[tg].Name = new wxChar[i+1];
8cd82622 98 memcpy(m_Cache[tg].Name, tagBuffer, (i+1)*sizeof(wxChar));
5526e819 99
15db3cf5 100 while (pos < lng && src[pos] != wxT('>')) pos++;
5526e819 101
4f9297b0 102 if (src[stpos+1] == wxT('/')) // ending tag:
a914db0f 103 {
5526e819
VS
104 m_Cache[tg].End1 = m_Cache[tg].End2 = -2;
105 // find matching begin tag:
106 for (i = tg; i >= 0; i--)
8cd82622 107 if ((m_Cache[i].End1 == -1) && (wxStrcmp(m_Cache[i].Name, tagBuffer+1) == 0))
a914db0f 108 {
5526e819
VS
109 m_Cache[i].End1 = stpos;
110 m_Cache[i].End2 = pos + 1;
111 break;
112 }
113 }
8cd82622 114 else
a914db0f 115 {
5526e819 116 m_Cache[tg].End1 = m_Cache[tg].End2 = -1;
7c6cd4a8
VS
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 }
5526e819
VS
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:
14d36de8 165 for (int i = 0; i < m_CacheSize; i++)
4f9297b0 166 {
2776d7c3 167 delete[] m_Cache[i].Name;
5526e819
VS
168 m_Cache[i].Name = NULL;
169 }
170}
171
5526e819
VS
172void wxHtmlTagsCache::QueryTag(int at, int* end1, int* end2)
173{
174 if (m_Cache == NULL) return;
8cd82622 175 if (m_Cache[m_CachePos].Key != at)
4f9297b0 176 {
5526e819 177 int delta = (at < m_Cache[m_CachePos].Key) ? -1 : 1;
8cd82622
VZ
178 do
179 {
180 m_CachePos += delta;
daa616fc
VS
181 }
182 while (m_Cache[m_CachePos].Key != at);
5526e819
VS
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
195IMPLEMENT_CLASS(wxHtmlTag,wxObject)
196
211dfedd 197wxHtmlTag::wxHtmlTag(wxHtmlTag *parent,
8cd82622 198 const wxString& source, int pos, int end_pos,
daa616fc
VS
199 wxHtmlTagsCache *cache,
200 wxHtmlEntitiesParser *entParser) : wxObject()
5526e819 201{
211dfedd
VS
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: */
8cd82622 220
5526e819 221 int i;
daa616fc 222 wxChar c;
5526e819
VS
223
224 // fill-in name, params and begin pos:
5526e819 225 i = pos+1;
5526e819 226
b076dc01 227 // find tag's name and convert it to uppercase:
8cd82622
VZ
228 while ((i < end_pos) &&
229 ((c = source[i++]) != wxT(' ') && c != wxT('\r') &&
daa616fc 230 c != wxT('\n') && c != wxT('\t') &&
8cd82622 231 c != wxT('>')))
a914db0f 232 {
8cd82622 233 if ((c >= wxT('a')) && (c <= wxT('z')))
daa616fc
VS
234 c -= (wxT('a') - wxT('A'));
235 m_Name << c;
5526e819
VS
236 }
237
b076dc01 238 // if the tag has parameters, read them and "normalize" them,
8cd82622 239 // i.e. convert to uppercase, replace whitespaces by spaces and
b076dc01 240 // remove whitespaces around '=':
c9893146 241 if (source[i-1] != wxT('>'))
daa616fc
VS
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;
8cd82622 247 enum
a914db0f 248 {
8cd82622 249 ST_BEFORE_NAME = 1,
daa616fc
VS
250 ST_NAME,
251 ST_BEFORE_EQ,
252 ST_BEFORE_VALUE,
253 ST_VALUE
254 } state;
8cd82622 255
daa616fc
VS
256 quote = 0;
257 state = ST_BEFORE_NAME;
258 while (i < end_pos)
259 {
260 c = source[i++];
261
8cd82622 262 if (c == wxT('>') && !(state == ST_VALUE && quote != 0))
a914db0f 263 {
daa616fc 264 if (state == ST_BEFORE_EQ || state == ST_NAME)
b076dc01 265 {
daa616fc
VS
266 m_ParamNames.Add(pname);
267 m_ParamValues.Add(wxEmptyString);
b076dc01 268 }
daa616fc
VS
269 else if (state == ST_VALUE && quote == 0)
270 {
271 m_ParamNames.Add(pname);
367c84b9
VS
272 if (entParser)
273 m_ParamValues.Add(entParser->Parse(pvalue));
274 else
275 m_ParamValues.Add(pvalue);
daa616fc
VS
276 }
277 break;
5526e819 278 }
daa616fc 279 switch (state)
a914db0f 280 {
daa616fc
VS
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 }
367c84b9
VS
328 if (entParser)
329 m_ParamValues.Add(entParser->Parse(pvalue));
330 else
331 m_ParamValues.Add(pvalue);
daa616fc
VS
332 state = ST_BEFORE_NAME;
333 }
334 else
335 pvalue << c;
336 break;
72aa4a98 337 }
5526e819 338 }
8cd82622 339
daa616fc
VS
340 #undef IS_WHITE
341 }
5526e819
VS
342 m_Begin = i;
343
4f9297b0 344 cache->QueryTag(pos, &m_End1, &m_End2);
5526e819
VS
345 if (m_End1 > end_pos) m_End1 = end_pos;
346 if (m_End2 > end_pos) m_End2 = end_pos;
347}
348
211dfedd
VS
349wxHtmlTag::~wxHtmlTag()
350{
0d58bb65
VS
351 wxHtmlTag *t1, *t2;
352 t1 = m_FirstChild;
353 while (t1)
354 {
355 t2 = t1->GetNextSibling();
356 delete t1;
357 t1 = t2;
358 }
211dfedd
VS
359}
360
5526e819
VS
361bool wxHtmlTag::HasParam(const wxString& par) const
362{
daa616fc 363 return (m_ParamNames.Index(par, FALSE) != wxNOT_FOUND);
5526e819
VS
364}
365
5526e819
VS
366wxString wxHtmlTag::GetParam(const wxString& par, bool with_commas) const
367{
daa616fc
VS
368 int index = m_ParamNames.Index(par, FALSE);
369 if (index == wxNOT_FOUND)
370 return wxEmptyString;
371 if (with_commas)
4f9297b0 372 {
daa616fc
VS
373 // VS: backward compatibility, seems to be never used by wxHTML...
374 wxString s;
375 s << wxT('"') << m_ParamValues[index] << wxT('"');
376 return s;
5526e819 377 }
daa616fc
VS
378 else
379 return m_ParamValues[index];
5526e819
VS
380}
381
90350682
VZ
382int wxHtmlTag::ScanParam(const wxString& par,
383 const wxChar *format,
384 void *param) const
5526e819 385{
5526e819 386 wxString parval = GetParam(par);
161f4f73 387 return wxSscanf(parval, format, param);
5526e819
VS
388}
389
8bd72d90
VS
390bool wxHtmlTag::GetParamAsColour(const wxString& par, wxColour *clr) const
391{
392 wxString str = GetParam(par);
8cd82622 393
8bd72d90
VS
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
8bd72d90 428 }
5716a1ab
VZ
429
430 return FALSE;
8bd72d90
VS
431}
432
433bool 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
daa616fc
VS
442wxString wxHtmlTag::GetAllParams() const
443{
8cd82622 444 // VS: this function is for backward compatiblity only,
daa616fc
VS
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
211dfedd
VS
460wxHtmlTag *wxHtmlTag::GetFirstSibling() const
461{
462 if (m_Parent)
463 return m_Parent->m_FirstChild;
464 else
465 {
466 wxHtmlTag *cur = (wxHtmlTag*)this;
8cd82622 467 while (cur->m_Prev)
211dfedd
VS
468 cur = cur->m_Prev;
469 return cur;
470 }
471}
472
473wxHtmlTag *wxHtmlTag::GetLastSibling() const
474{
475 if (m_Parent)
476 return m_Parent->m_LastChild;
477 else
478 {
479 wxHtmlTag *cur = (wxHtmlTag*)this;
8cd82622 480 while (cur->m_Next)
211dfedd
VS
481 cur = cur->m_Next;
482 return cur;
483 }
484}
485
486wxHtmlTag *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;
8cd82622 492 while (cur->m_Parent && !cur->m_Next)
211dfedd
VS
493 cur = cur->m_Parent;
494 return cur->m_Next;
495}
496
4d223b67 497#endif