new wxHtmlWindow::OnOpeningURL API
[wxWidgets.git] / src / html / m_image.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: m_image.cpp
3 // Purpose: wxHtml module for displaying images
4 // Author: Vaclav Slavik
5 // RCS-ID: $Id$
6 // Copyright: (c) 1999 Vaclav Slavik, Joel Lucsy
7 // Licence: wxWindows Licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation
12 #endif
13
14 #include "wx/wxprec.h"
15
16 #include "wx/defs.h"
17 #if wxUSE_HTML && wxUSE_STREAMS
18
19 #ifdef __BORDLANDC__
20 #pragma hdrstop
21 #endif
22
23 #ifndef WXPRECOMP
24 #include "wx/dc.h"
25 #include "wx/scrolwin.h"
26 #include "wx/timer.h"
27 #include "wx/dcmemory.h"
28 #endif
29
30 #include "wx/html/forcelnk.h"
31 #include "wx/html/m_templ.h"
32 #include "wx/html/htmlwin.h"
33
34 #include "wx/image.h"
35 #include "wx/gifdecod.h"
36 #include "wx/dynarray.h"
37
38 #include <math.h>
39 #include <float.h>
40
41 FORCE_LINK_ME(m_image)
42
43
44
45
46 WX_DECLARE_OBJARRAY(int, CoordArray);
47 #include "wx/arrimpl.cpp" // this is a magic incantation which must be done!
48 WX_DEFINE_OBJARRAY(CoordArray);
49
50
51 //--------------------------------------------------------------------------------
52 // wxHtmlImageMapAreaCell
53 // 0-width, 0-height cell that represents single area in imagemap
54 // (it's GetLink is called from wxHtmlImageCell's)
55 //--------------------------------------------------------------------------------
56
57 class wxHtmlImageMapAreaCell : public wxHtmlCell
58 {
59 public:
60 enum celltype { CIRCLE, RECT, POLY };
61 protected:
62 CoordArray coords;
63 celltype type;
64 int radius;
65 public:
66 wxHtmlImageMapAreaCell( celltype t, wxString &coords, double pixel_scale = 1.0);
67 virtual wxHtmlLinkInfo *GetLink( int x = 0, int y = 0 ) const;
68 };
69
70
71
72
73
74 wxHtmlImageMapAreaCell::wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell::celltype t, wxString &incoords, double pixel_scale )
75 {
76 int i;
77 wxString x = incoords, y;
78
79 type = t;
80 while ((i = x.Find( ',' )) != -1)
81 {
82 coords.Add( (int)(pixel_scale * (double)wxAtoi( x.Left( i ).c_str())) );
83 x = x.Mid( i + 1 );
84 }
85 coords.Add( (int)(pixel_scale * (double)wxAtoi( x.c_str())) );
86 }
87
88 wxHtmlLinkInfo *wxHtmlImageMapAreaCell::GetLink( int x, int y ) const
89 {
90 switch (type)
91 {
92 case RECT:
93 {
94 int l, t, r, b;
95
96 l = coords[ 0 ];
97 t = coords[ 1 ];
98 r = coords[ 2 ];
99 b = coords[ 3 ];
100 if (x >= l && x <= r && y >= t && y <= b)
101 {
102 return m_Link;
103 }
104 break;
105 }
106 case CIRCLE:
107 {
108 int l, t, r;
109 double d;
110
111 l = coords[ 0 ];
112 t = coords[ 1 ];
113 r = coords[ 2 ];
114 d = sqrt( (double) (((x - l) * (x - l)) + ((y - t) * (y - t))) );
115 if (d < (double)r)
116 {
117 return m_Link;
118 }
119 }
120 break;
121 case POLY:
122 {
123 if (coords.GetCount() >= 6)
124 {
125 int intersects = 0;
126 int wherex = x;
127 int wherey = y;
128 int totalv = coords.GetCount() / 2;
129 int totalc = totalv * 2;
130 int xval = coords[totalc - 2];
131 int yval = coords[totalc - 1];
132 int end = totalc;
133 int pointer = 1;
134
135 if ((yval >= wherey) != (coords[pointer] >= wherey))
136 {
137 if ((xval >= wherex) == (coords[0] >= wherex))
138 {
139 intersects += (xval >= wherex) ? 1 : 0;
140 }
141 else
142 {
143 intersects += ((xval - (yval - wherey) *
144 (coords[0] - xval) /
145 (coords[pointer] - yval)) >= wherex) ? 1 : 0;
146 }
147 }
148
149 while (pointer < end)
150 {
151 yval = coords[pointer];
152 pointer += 2;
153 if (yval >= wherey)
154 {
155 while ((pointer < end) && (coords[pointer] >= wherey))
156 {
157 pointer += 2;
158 }
159 if (pointer >= end)
160 {
161 break;
162 }
163 if ((coords[pointer - 3] >= wherex) ==
164 (coords[pointer - 1] >= wherex)) {
165 intersects += (coords[pointer - 3] >= wherex) ? 1 : 0;
166 }
167 else
168 {
169 intersects +=
170 ((coords[pointer - 3] - (coords[pointer - 2] - wherey) *
171 (coords[pointer - 1] - coords[pointer - 3]) /
172 (coords[pointer] - coords[pointer - 2])) >= wherex) ? 1 : 0;
173 }
174 }
175 else
176 {
177 while ((pointer < end) && (coords[pointer] < wherey))
178 {
179 pointer += 2;
180 }
181 if (pointer >= end)
182 {
183 break;
184 }
185 if ((coords[pointer - 3] >= wherex) ==
186 (coords[pointer - 1] >= wherex))
187 {
188 intersects += (coords[pointer - 3] >= wherex) ? 1 : 0;
189 }
190 else
191 {
192 intersects +=
193 ((coords[pointer - 3] - (coords[pointer - 2] - wherey) *
194 (coords[pointer - 1] - coords[pointer - 3]) /
195 (coords[pointer] - coords[pointer - 2])) >= wherex) ? 1 : 0;
196 }
197 }
198 }
199 if ((intersects & 1) != 0)
200 {
201 return m_Link;
202 }
203 }
204 }
205 break;
206 }
207
208 if (m_Next)
209 {
210 wxHtmlImageMapAreaCell *a = (wxHtmlImageMapAreaCell*)m_Next;
211 return a->GetLink( x, y );
212 }
213 return NULL;
214 }
215
216
217
218
219
220
221
222
223 //--------------------------------------------------------------------------------
224 // wxHtmlImageMapCell
225 // 0-width, 0-height cell that represents map from imagemaps
226 // it is always placed before wxHtmlImageMapAreaCells
227 // It responds to Find(wxHTML_COND_ISIMAGEMAP)
228 //--------------------------------------------------------------------------------
229
230
231 class wxHtmlImageMapCell : public wxHtmlCell
232 {
233 public:
234 wxHtmlImageMapCell( wxString &name );
235 protected:
236 wxString m_Name;
237 public:
238 virtual wxHtmlLinkInfo *GetLink( int x = 0, int y = 0 ) const;
239 virtual const wxHtmlCell *Find( int cond, const void *param ) const;
240 };
241
242
243 wxHtmlImageMapCell::wxHtmlImageMapCell( wxString &name )
244 {
245 m_Name = name ;
246 }
247
248 wxHtmlLinkInfo *wxHtmlImageMapCell::GetLink( int x, int y ) const
249 {
250 wxHtmlImageMapAreaCell *a = (wxHtmlImageMapAreaCell*)m_Next;
251 if (a)
252 return a->GetLink( x, y );
253 return wxHtmlCell::GetLink( x, y );
254 }
255
256 const wxHtmlCell *wxHtmlImageMapCell::Find( int cond, const void *param ) const
257 {
258 if (cond == wxHTML_COND_ISIMAGEMAP)
259 {
260 if (m_Name == *((wxString*)(param)))
261 return this;
262 }
263 return wxHtmlCell::Find(cond, param);
264 }
265
266
267
268
269
270 //--------------------------------------------------------------------------------
271 // wxHtmlImageCell
272 // Image/bitmap
273 //--------------------------------------------------------------------------------
274
275 class wxHtmlImageCell : public wxHtmlCell
276 {
277 public:
278 wxHtmlImageCell(wxWindow *window,
279 wxFSFile *input, int w = -1, int h = -1,
280 double scale = 1.0, int align = wxHTML_ALIGN_BOTTOM,
281 const wxString& mapname = wxEmptyString);
282 ~wxHtmlImageCell();
283 void Draw(wxDC& dc, int x, int y, int view_y1, int view_y2);
284 virtual wxHtmlLinkInfo *GetLink(int x = 0, int y = 0) const;
285
286 void SetImage(const wxImage& img);
287 #if wxUSE_GIF && wxUSE_TIMER
288 void AdvanceAnimation(wxTimer *timer);
289 virtual void Layout(int w);
290 #endif
291
292 private:
293 wxBitmap *m_bitmap;
294 int m_bmpW, m_bmpH;
295 bool m_showFrame:1;
296 wxScrolledWindow *m_window;
297 #if wxUSE_GIF && wxUSE_TIMER
298 wxGIFDecoder *m_gifDecoder;
299 wxTimer *m_gifTimer;
300 int m_physX, m_physY;
301 #endif
302 double m_scale;
303 wxHtmlImageMapCell *m_imageMap;
304 wxString m_mapName;
305 };
306
307 #if wxUSE_GIF && wxUSE_TIMER
308 class wxGIFTimer : public wxTimer
309 {
310 public:
311 wxGIFTimer(wxHtmlImageCell *cell) : m_cell(cell) {}
312 virtual void Notify()
313 {
314 m_cell->AdvanceAnimation(this);
315 }
316
317 private:
318 wxHtmlImageCell *m_cell;
319 };
320 #endif
321
322
323 //--------------------------------------------------------------------------------
324 // wxHtmlImageCell
325 //--------------------------------------------------------------------------------
326
327 /* XPM */
328 static const char * broken_image_xpm[] = {
329 "29 31 7 1",
330 " c None",
331 ". c #808080",
332 "+ c #FFFFFF",
333 "@ c #C0C0C0",
334 "# c #000000",
335 "$ c #333366",
336 "% c #B2B2B2",
337 "..................... ",
338 ".+++++++++++++++++++.. ",
339 ".+++++++++++++++++++.@. ",
340 ".++@@@@@@@@@@@@@@@@@.+@. ",
341 ".++@@@@@@@@@@@@@@@@@.++@. ",
342 ".++@@@@@.@@@@.@@@@@@.+++@. ",
343 ".++@@@@@@@@@@@@@@@@@.++++@. ",
344 ".++@@@@@@@@@@@@@@@@@.+++++@. ",
345 ".++@@.@@@@@@@@@@.@@@######## ",
346 ".++@@@@@@@@@@@@@@@@@@$$$$$$#.",
347 ".######@@@@@@@@@@@@@@@.....#.",
348 " ###@@@@@@@@@@@@@@@++#.",
349 " #####@@@@@@@@@@++#.",
350 " #@.@@@@@@@@++#.",
351 ".. ###@@@@@@@++#.",
352 ".+.... #@@@@@@++#.",
353 ".++@@@... ####@@++#.",
354 ".++@@@@@@.. #####.",
355 ".++@@@@@@@@... ",
356 ".++@@@@@@%%%%@. ",
357 ".++@@@@@@%%%%@@.... ",
358 ".++@@@@@@%%%%@@@@@@.... ",
359 ".++@@@@@@%%%%@@@@@@@@@@.... ",
360 ".++@@@@@@@@@@@@@@@@@@@@@@++#.",
361 ".++@@@@@@@@@@@@@@@@@@@@@@++#.",
362 ".++@@@@@@@@@@@@@@@@@@@@@@++#.",
363 ".++@@@@@@@@@@@@@@@@@@@@@@++#.",
364 ".++@@@@@@@@@@@@@@@@@@@@@@++#.",
365 ".++++++++++++++++++++++++++#.",
366 ".++++++++++++++++++++++++++#.",
367 "############################."};
368
369 wxHtmlImageCell::wxHtmlImageCell(wxWindow *window, wxFSFile *input,
370 int w, int h, double scale, int align,
371 const wxString& mapname) : wxHtmlCell()
372 {
373 m_window = window ? wxStaticCast(window, wxScrolledWindow) : NULL;
374 m_scale = scale;
375 m_showFrame = FALSE;
376 m_bitmap = NULL;
377 m_bmpW = w;
378 m_bmpH = h;
379 m_imageMap = NULL;
380 m_mapName = mapname;
381 SetCanLiveOnPagebreak(FALSE);
382 #if wxUSE_GIF && wxUSE_TIMER
383 m_gifDecoder = NULL;
384 m_gifTimer = NULL;
385 m_physX = m_physY = -1;
386 #endif
387
388 if ( input )
389 {
390 wxInputStream *s = input->GetStream();
391
392 if ( s )
393 {
394 bool readImg = TRUE;
395
396 #if wxUSE_GIF && wxUSE_TIMER
397 if ( (input->GetLocation().Matches(wxT("*.gif")) ||
398 input->GetLocation().Matches(wxT("*.GIF"))) && m_window )
399 {
400 m_gifDecoder = new wxGIFDecoder(s, TRUE);
401 if ( m_gifDecoder->ReadGIF() == wxGIF_OK )
402 {
403 wxImage img;
404 if ( m_gifDecoder->ConvertToImage(&img) )
405 SetImage(img);
406
407 readImg = FALSE;
408
409 if ( m_gifDecoder->IsAnimation() )
410 {
411 m_gifTimer = new wxGIFTimer(this);
412 m_gifTimer->Start(m_gifDecoder->GetDelay(), TRUE);
413 }
414 else
415 {
416 wxDELETE(m_gifDecoder);
417 }
418 }
419 else
420 {
421 wxDELETE(m_gifDecoder);
422 }
423 }
424
425 if ( readImg )
426 #endif
427 {
428 SetImage(wxImage(*s, wxBITMAP_TYPE_ANY));
429 }
430 }
431 }
432 else // input==NULL, use "broken image" bitmap
433 {
434 if ( m_bmpW == -1 && m_bmpH == -1 )
435 {
436 m_bmpW == 29, m_bmpH = 31;
437 }
438 else
439 {
440 m_showFrame = TRUE;
441 if ( m_bmpW == -1 ) m_bmpW = 31;
442 if ( m_bmpH == -1 ) m_bmpH = 33;
443 }
444 m_bitmap = new wxBitmap(broken_image_xpm);
445 }
446
447 m_Width = (int)(scale * (double)m_bmpW);
448 m_Height = (int)(scale * (double)m_bmpH);
449
450 switch (align)
451 {
452 case wxHTML_ALIGN_TOP :
453 m_Descent = m_Height;
454 break;
455 case wxHTML_ALIGN_CENTER :
456 m_Descent = m_Height / 2;
457 break;
458 case wxHTML_ALIGN_BOTTOM :
459 default :
460 m_Descent = 0;
461 break;
462 }
463 }
464
465 void wxHtmlImageCell::SetImage(const wxImage& img)
466 {
467 if ( img.Ok() )
468 {
469 delete m_bitmap;
470
471 int ww, hh;
472 ww = img.GetWidth();
473 hh = img.GetHeight();
474
475 if ( m_bmpW == -1 )
476 m_bmpW = ww;
477 if ( m_bmpH == -1 )
478 m_bmpH = hh;
479
480 if ((m_bmpW != ww) || (m_bmpH != hh))
481 {
482 wxImage img2 = img.Scale(m_bmpW, m_bmpH);
483 m_bitmap = new wxBitmap(img2);
484 }
485 else
486 m_bitmap = new wxBitmap(img);
487 }
488 }
489
490 #if wxUSE_GIF && wxUSE_TIMER
491 void wxHtmlImageCell::AdvanceAnimation(wxTimer *timer)
492 {
493 wxImage img;
494
495 m_gifDecoder->GoNextFrame(TRUE);
496
497 if ( m_physX == -1 )
498 {
499 m_physX = m_physY = 0;
500 for (wxHtmlCell *cell = this; cell; cell = cell->GetParent())
501 {
502 m_physX += cell->GetPosX();
503 m_physY += cell->GetPosY();
504 }
505 }
506
507 int x, y;
508 m_window->CalcScrolledPosition(m_physX, m_physY, &x, &y);
509 wxRect rect(x, y, m_Width, m_Height);
510
511 if ( m_window->GetClientRect().Intersects(rect) &&
512 m_gifDecoder->ConvertToImage(&img) )
513 {
514 if ( (int)m_gifDecoder->GetWidth() != m_Width ||
515 (int)m_gifDecoder->GetHeight() != m_Height ||
516 m_gifDecoder->GetLeft() != 0 || m_gifDecoder->GetTop() != 0 )
517 {
518 wxBitmap bmp(img);
519 wxMemoryDC dc;
520 dc.SelectObject(*m_bitmap);
521 dc.DrawBitmap(bmp, m_gifDecoder->GetLeft(), m_gifDecoder->GetTop());
522 }
523 else
524 SetImage(img);
525 m_window->Refresh(img.HasMask(), &rect);
526 }
527
528 timer->Start(m_gifDecoder->GetDelay(), TRUE);
529 }
530
531 void wxHtmlImageCell::Layout(int w)
532 {
533 wxHtmlCell::Layout(w);
534 m_physX = m_physY = -1;
535 }
536
537 #endif
538
539 wxHtmlImageCell::~wxHtmlImageCell()
540 {
541 delete m_bitmap;
542 #if wxUSE_GIF && wxUSE_TIMER
543 delete m_gifTimer;
544 delete m_gifDecoder;
545 #endif
546 }
547
548
549 void wxHtmlImageCell::Draw(wxDC& dc, int x, int y, int WXUNUSED(view_y1), int WXUNUSED(view_y2))
550 {
551 if ( m_showFrame )
552 {
553 dc.SetBrush(*wxTRANSPARENT_BRUSH);
554 dc.SetPen(*wxBLACK_PEN);
555 dc.DrawRectangle(x + m_PosX, y + m_PosY, m_Width, m_Height);
556 x++, y++;
557 }
558 if ( m_bitmap )
559 {
560 double us_x, us_y;
561 dc.GetUserScale(&us_x, &us_y);
562 dc.SetUserScale(us_x * m_scale, us_y * m_scale);
563
564 dc.DrawBitmap(*m_bitmap, (int) ((x + m_PosX) / m_scale),
565 (int) ((y + m_PosY) / m_scale), TRUE);
566 dc.SetUserScale(us_x, us_y);
567 }
568 }
569
570 wxHtmlLinkInfo *wxHtmlImageCell::GetLink( int x, int y ) const
571 {
572 if (m_mapName.IsEmpty())
573 return wxHtmlCell::GetLink( x, y );
574 if (!m_imageMap)
575 {
576 wxHtmlContainerCell *p, *op;
577 op = p = GetParent();
578 while (p)
579 {
580 op = p;
581 p = p->GetParent();
582 }
583 p = op;
584 wxHtmlCell *cell = (wxHtmlCell*)p->Find(wxHTML_COND_ISIMAGEMAP,
585 (const void*)(&m_mapName));
586 if (!cell)
587 {
588 ((wxString&)m_mapName).Clear();
589 return wxHtmlCell::GetLink( x, y );
590 }
591 { // dirty hack, ask Joel why he fills m_ImageMap in this place
592 // THE problem is that we're in const method and we can't modify m_ImageMap
593 wxHtmlImageMapCell **cx = (wxHtmlImageMapCell**)(&m_imageMap);
594 *cx = (wxHtmlImageMapCell*)cell;
595 }
596 }
597 return m_imageMap->GetLink(x, y);
598 }
599
600
601
602 //--------------------------------------------------------------------------------
603 // tag handler
604 //--------------------------------------------------------------------------------
605
606 TAG_HANDLER_BEGIN(IMG, "IMG,MAP,AREA")
607
608 TAG_HANDLER_PROC(tag)
609 {
610 if (tag.GetName() == wxT("IMG"))
611 {
612 if (tag.HasParam(wxT("SRC")))
613 {
614 int w = -1, h = -1;
615 int al;
616 wxFSFile *str;
617 wxString tmp = tag.GetParam(wxT("SRC"));
618 wxString mn = wxEmptyString;
619
620 str = m_WParser->OpenURL(wxHTML_URL_IMAGE, tmp);
621
622 if (tag.HasParam(wxT("WIDTH")))
623 tag.GetParamAsInt(wxT("WIDTH"), &w);
624 if (tag.HasParam(wxT("HEIGHT")))
625 tag.GetParamAsInt(wxT("HEIGHT"), &h);
626 al = wxHTML_ALIGN_BOTTOM;
627 if (tag.HasParam(wxT("ALIGN")))
628 {
629 wxString alstr = tag.GetParam(wxT("ALIGN"));
630 alstr.MakeUpper(); // for the case alignment was in ".."
631 if (alstr == wxT("TEXTTOP"))
632 al = wxHTML_ALIGN_TOP;
633 else if ((alstr == wxT("CENTER")) || (alstr == wxT("ABSCENTER")))
634 al = wxHTML_ALIGN_CENTER;
635 }
636 if (tag.HasParam(wxT("USEMAP")))
637 {
638 mn = tag.GetParam( wxT("USEMAP") );
639 if (mn.GetChar(0) == wxT('#'))
640 {
641 mn = mn.Mid( 1 );
642 }
643 }
644 wxHtmlImageCell *cel = new wxHtmlImageCell(
645 m_WParser->GetWindow(),
646 str, w, h,
647 m_WParser->GetPixelScale(),
648 al, mn);
649 cel->SetLink(m_WParser->GetLink());
650 cel->SetId(tag.GetParam(wxT("id"))); // may be empty
651 m_WParser->GetContainer()->InsertCell(cel);
652 if (str)
653 delete str;
654 }
655 }
656 if (tag.GetName() == wxT("MAP"))
657 {
658 m_WParser->CloseContainer();
659 m_WParser->OpenContainer();
660 if (tag.HasParam(wxT("NAME")))
661 {
662 wxString tmp = tag.GetParam(wxT("NAME"));
663 wxHtmlImageMapCell *cel = new wxHtmlImageMapCell( tmp );
664 m_WParser->GetContainer()->InsertCell( cel );
665 }
666 ParseInner( tag );
667 m_WParser->CloseContainer();
668 m_WParser->OpenContainer();
669 }
670 if (tag.GetName() == wxT("AREA"))
671 {
672 if (tag.HasParam(wxT("SHAPE")))
673 {
674 wxString tmp = tag.GetParam(wxT("SHAPE"));
675 wxString coords = wxEmptyString;
676 tmp.MakeUpper();
677 wxHtmlImageMapAreaCell *cel = NULL;
678 if (tag.HasParam(wxT("COORDS")))
679 {
680 coords = tag.GetParam(wxT("COORDS"));
681 }
682 if (tmp == wxT("POLY"))
683 {
684 cel = new wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell::POLY, coords, m_WParser->GetPixelScale() );
685 }
686 else if (tmp == wxT("CIRCLE"))
687 {
688 cel = new wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell::CIRCLE, coords, m_WParser->GetPixelScale() );
689 }
690 else if (tmp == wxT("RECT"))
691 {
692 cel = new wxHtmlImageMapAreaCell( wxHtmlImageMapAreaCell::RECT, coords, m_WParser->GetPixelScale() );
693 }
694 if (cel != NULL && tag.HasParam(wxT("HREF")))
695 {
696 wxString tmp = tag.GetParam(wxT("HREF"));
697 wxString target = wxEmptyString;
698 if (tag.HasParam(wxT("TARGET"))) target = tag.GetParam(wxT("TARGET"));
699 cel->SetLink( wxHtmlLinkInfo(tmp, target));
700 }
701 if (cel != NULL) m_WParser->GetContainer()->InsertCell( cel );
702 }
703 }
704
705 return FALSE;
706 }
707
708 TAG_HANDLER_END(IMG)
709
710
711
712 TAGS_MODULE_BEGIN(Image)
713
714 TAGS_MODULE_ADD(IMG)
715
716 TAGS_MODULE_END(Image)
717
718
719 #endif