Dillo v3.1.1-46-g8a360e32
Loading...
Searching...
No Matches
image.cc
Go to the documentation of this file.
1/*
2 * Dillo Widget
3 *
4 * Copyright 2005-2007 Sebastian Geerken <sgeerken@dillo.org>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20
21
22#include "image.hh"
23#include "dlib/dlib.h"
24#include "../lout/msg.h"
25#include "../lout/misc.hh"
26#include "../lout/debug.hh"
27
28namespace dw {
29
30using namespace lout;
31
33{
34 shapesAndLinks = new container::typed::List <ShapeAndLink> (true);
35 defaultLink = -1;
36}
37
39{
40 delete shapesAndLinks;
41}
42
44 int x, int y)
45{
46 container::typed::Iterator <ShapeAndLink> it;
47
48 for (it = shapesAndLinks->iterator (); it.hasNext (); ) {
49 ShapeAndLink *shapeAndLink = it.getNext ();
50
51 shapeAndLink->shape->draw(view, style, x, y);
52 }
53}
54
56 ShapeAndLink *shapeAndLink = new ShapeAndLink ();
57 shapeAndLink->shape = shape;
58 shapeAndLink->link = link;
59 shapesAndLinks->append (shapeAndLink);
60}
61
63 container::typed::Iterator <ShapeAndLink> it;
64 int link = defaultLink;
65
66 for (it = shapesAndLinks->iterator (); it.hasNext (); ) {
67 ShapeAndLink *shapeAndLink = it.getNext ();
68
69 if (shapeAndLink->shape->isPointWithin (x, y)) {
70 link = shapeAndLink->link;
71 break;
72 }
73 }
74
75 return link;
76}
77
79{
80 imageMaps = new container::typed::HashTable <object::Object, ImageMap>
81 (true, true);
82 currentMap = NULL;
83}
84
89
98{
99 currentMap = new ImageMap ();
100 imageMaps->put (key, currentMap);
101}
102
110{
111 currentMap->add (shape, link);
112}
113
121
123 core::style::Style *style, int x, int y)
124{
125 ImageMap *map = imageMaps->get (key);
126
127 if (map)
128 map->draw(view, style, x, y);
129}
130
131int ImageMapsList::link (object::Object *key, int x, int y)
132{
133 int link = -1;
134 ImageMap *map = imageMaps->get (key);
135
136 if (map)
137 link = map->link (x, y);
138
139 return link;
140}
141
142// ----------------------------------------------------------------------
143
144int Image::CLASS_ID = -1;
145
146Image::Image(const char *altText)
147{
148 DBG_OBJ_CREATE ("dw::Image");
149 registerName ("dw::Image", &CLASS_ID);
150 this->altText = altText ? dStrdup (altText) : NULL;
151 altTextWidth = -1; // not yet calculated
152 buffer = NULL;
153 bufWidth = bufHeight = -1;
154 clicking = false;
155 currLink = -1;
156 mapList = NULL;
157 mapKey = NULL;
158 isMap = false;
159
160 DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
161 DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
162}
163
165{
166 if (altText)
167 free(altText);
168 if (buffer)
169 buffer->unref ();
170 if (mapKey)
171 delete mapKey;
172
174}
175
177{
178 DBG_OBJ_ENTER0 ("resize", 0, "sizeRequestImpl");
179
180 if (buffer) {
181 requisition->width = buffer->getRootWidth ();
182 requisition->ascent = buffer->getRootHeight ();
183 requisition->descent = 0;
184 } else {
185 if (altText && altText[0]) {
186 if (altTextWidth == -1)
187 altTextWidth =
188 layout->textWidth (getStyle()->font, altText, strlen (altText));
189
190 requisition->width = altTextWidth;
191 requisition->ascent = getStyle()->font->ascent;
192 requisition->descent = getStyle()->font->descent;
193 } else {
194 requisition->width = 0;
195 requisition->ascent = 0;
196 requisition->descent = 0;
197 }
198 }
199
200 requisition->width += boxDiffWidth ();
201 requisition->ascent += boxOffsetY ();
202 requisition->descent += boxRestHeight ();
203
204 correctRequisition (requisition, core::splitHeightPreserveDescent, true,
205 true);
206
207 if (buffer) {
208 // If one dimension is set, preserve the aspect ratio (without
209 // extraSpace/margin/border/padding). Notice that
210 // requisition->descent could have been changed in
211 // core::splitHeightPreserveDescent, so we do not make any
212 // assumtions here about it (and requisition->ascent).
213
214 // TODO Check again possible overflows. (Aren't buffer
215 // dimensions limited to 2^15?)
216
217 bool widthSpecified = getStyle()->width != core::style::LENGTH_AUTO ||
218 getStyle()->minWidth != core::style::LENGTH_AUTO ||
219 getStyle()->maxWidth != core::style::LENGTH_AUTO;
220 bool heightSpecified = getStyle()->height != core::style::LENGTH_AUTO ||
221 getStyle()->minHeight != core::style::LENGTH_AUTO ||
222 getStyle()->maxHeight != core::style::LENGTH_AUTO;
223
224 if (!widthSpecified && heightSpecified)
225 requisition->width =
226 (requisition->ascent + requisition->descent - boxDiffHeight ())
227 * buffer->getRootWidth () / buffer->getRootHeight ()
228 + boxDiffWidth ();
229 else if (widthSpecified && !heightSpecified) {
230 requisition->ascent = (requisition->width + boxDiffWidth ())
231 * buffer->getRootHeight () / buffer->getRootWidth ()
232 + boxOffsetY ();
233 requisition->descent = boxRestHeight ();
234 }
235 }
236
237 DBG_OBJ_MSGF ("resize", 1, "=> %d * (%d + %d)",
238 requisition->width, requisition->ascent, requisition->descent);
239 DBG_OBJ_LEAVE ();
240}
241
243{
244 int contentWidth;
245 if (buffer)
246 contentWidth = buffer->getRootWidth ();
247 else {
248 if (altText && altText[0]) {
249 if (altTextWidth == -1)
250 altTextWidth =
251 layout->textWidth (getStyle()->font, altText, strlen (altText));
252 contentWidth = altTextWidth;
253 } else
254 contentWidth = 0;
255 }
256
257 int width = contentWidth + boxDiffWidth ();
258
259 // With percentage width, the image may be narrower than the buffer.
260 extremes->minWidth =
261 core::style::isPerLength (getStyle()->width) ? boxDiffWidth () : width;
262
263 // (We ignore the same effect for the maximal width.)
264 extremes->maxWidth = width;
265
266 extremes->minWidthIntrinsic = extremes->minWidth;
267 extremes->maxWidthIntrinsic = extremes->maxWidth;
268
269 correctExtremes (extremes, false);
270
271 extremes->adjustmentWidth =
272 misc::min (extremes->minWidthIntrinsic, extremes->minWidth);
273}
274
276{
277 DBG_OBJ_ENTER ("resize", 0, "sizeAllocateImpl", "%d, %d; %d * (%d + %d)",
278 allocation->x, allocation->y, allocation->width,
279 allocation->ascent, allocation->descent);
280
281
282 int newBufWidth = allocation->width - boxDiffWidth ();
283 int newBufHeight =
284 allocation->ascent + allocation->descent - boxDiffHeight ();
285
286 if (buffer && newBufWidth > 0 && newBufHeight > 0 &&
287 // Save some time when size did not change:
288 (newBufWidth != bufWidth || newBufHeight != bufHeight)) {
289 DBG_OBJ_MSG ("resize", 1, "replacing buffer");
290
291 core::Imgbuf *oldBuffer = buffer;
292 buffer = oldBuffer->getScaledBuf (newBufWidth, newBufHeight);
293 oldBuffer->unref ();
294
295 bufWidth = newBufWidth;
296 bufHeight = newBufHeight;
297
298 DBG_OBJ_ASSOC_CHILD (this->buffer);
299 DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
300 DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
301 }
302
303 DBG_OBJ_LEAVE ();
304}
305
307{
308 DBG_OBJ_ENTER0 ("resize", 0, "containerSizeChangedForChildren");
309 // Nothing to do.
310 DBG_OBJ_LEAVE ();
311}
312
314{
315 // BUG: this is wrong for image maps, but the cursor position is unknown.
316 currLink = getStyle()->x_link;
317
318 if (currLink != -1) {
319 (void) layout->emitLinkEnter (this, currLink, -1, -1, -1);
320 }
321 Widget::enterNotifyImpl(event);
322}
323
325{
326 clicking = false;
327
328 if (currLink != -1) {
329 currLink = -1;
330 (void) layout->emitLinkEnter (this, -1, -1, -1, -1);
331 }
332 Widget::leaveNotifyImpl(event);
333}
334
335/*
336 * Return the coordinate relative to the contents.
337 * If the event occurred in the surrounding box, return the value at the
338 * edge of the contents instead.
339 */
341{
342 int ret = event->xWidget - boxOffsetX();
343
344 ret = misc::min(getContentWidth(), misc::max(ret, 0));
345 return ret;
346}
347
349{
350 int ret = event->yWidget - boxOffsetY();
351
352 ret = misc::min(getContentHeight(), misc::max(ret, 0));
353 return ret;
354}
355
357{
358 if (mapList || isMap) {
359 int x = contentX(event);
360 int y = contentY(event);
361
362 if (mapList) {
363 /* client-side image map */
364 int newLink = mapList->link (mapKey, x, y);
365 if (newLink != currLink) {
366 currLink = newLink;
367 clicking = false;
368 /* \todo Using MAP/AREA styles would probably be best */
369 setCursor(newLink == -1 ? getStyle()->cursor :
371 (void) layout->emitLinkEnter (this, newLink, -1, -1, -1);
372 }
373 } else if (isMap && currLink != -1) {
374 /* server-side image map */
375 (void) layout->emitLinkEnter (this, currLink, -1, x, y);
376 }
377 }
378 return true;
379}
380
382{
383 bool ret = false;
384
385 currLink = mapList ? mapList->link(mapKey,contentX(event),contentY(event)) :
386 getStyle()->x_link;
387 if (event->button == 3){
388 (void)layout->emitLinkPress(this, currLink, getStyle()->x_img, -1, -1,
389 event);
390 ret = true;
391 } else if (event->button == 1 || currLink != -1){
392 clicking = true;
393 ret = true;
394 }
395 return ret;
396}
397
399{
400 currLink = mapList ? mapList->link(mapKey,contentX(event),contentY(event)) :
401 getStyle()->x_link;
402 if (clicking) {
403 int x = isMap ? contentX(event) : -1;
404 int y = isMap ? contentY(event) : -1;
405 clicking = false;
406 layout->emitLinkClick (this, currLink, getStyle()->x_img, x, y, event);
407 return true;
408 }
409 return false;
410}
411
413 core::DrawingContext *context)
414{
415 int dx, dy;
416 core::Rectangle content, intersection;
417
418 drawWidgetBox (view, area, false);
419
420 if (buffer) {
421 dx = boxOffsetX ();
422 dy = boxOffsetY ();
423 content.x = dx;
424 content.y = dy;
425 content.width = getContentWidth ();
426 content.height = getContentHeight ();
427
428 if (area->intersectsWith (&content, &intersection))
429 view->drawImage (buffer,
430 allocation.x + dx, allocation.y + dy,
431 intersection.x - dx, intersection.y - dy,
432 intersection.width, intersection.height);
433 } else {
434 core::View *clippingView;
435
436 if (altText && altText[0]) {
437 core::View *usedView = view;
438
439 clippingView = NULL;
440
441 if (altTextWidth == -1)
442 altTextWidth =
443 layout->textWidth (getStyle()->font, altText, strlen (altText));
444
445 if ((getContentWidth() < altTextWidth) ||
446 (getContentHeight() <
447 getStyle()->font->ascent + getStyle()->font->descent)) {
448 clippingView = usedView =
449 view->getClippingView (allocation.x + boxOffsetX (),
450 allocation.y + boxOffsetY (),
451 getContentWidth(),
452 getContentHeight());
453 }
454
455 usedView->drawSimpleWrappedText (getStyle()->font, getStyle()->color,
457 allocation.x + boxOffsetX (),
458 allocation.y + boxOffsetY (),
459 getContentWidth(), getContentHeight(), altText);
460
461 if (clippingView)
462 view->mergeClippingView (clippingView);
463 }
464 if (mapKey) {
465 clippingView = view->getClippingView (allocation.x + boxOffsetX (),
466 allocation.y + boxOffsetY (),
467 getContentWidth(),
468 getContentHeight());
469 mapList->drawMap(mapKey, clippingView, getStyle(),
470 allocation.x + boxOffsetX (),
471 allocation.y + boxOffsetY ());
472 view->mergeClippingView (clippingView);
473 }
474 }
475
477}
478
480{
481 //return new core::TextIterator (this, mask, atEnd, altText);
483 return new core::EmptyIterator (this, mask, atEnd);
484}
485
486void Image::setBuffer (core::Imgbuf *buffer, bool resize)
487{
488 core::Imgbuf *oldBuf = this->buffer;
489
490 if (wasAllocated () && needsResize () &&
491 getContentWidth () > 0 && getContentHeight () > 0) {
492 // Don't create a new buffer for the transition from alt text to img,
493 // and only scale when both dimensions are known.
494
495 bufWidth = getContentWidth ();
496 bufHeight = getContentHeight ();
497 this->buffer = buffer->getScaledBuf (bufWidth, bufHeight);
498 } else {
499 this->buffer = buffer;
500 bufWidth = buffer->getRootWidth ();
501 bufHeight = buffer->getRootHeight ();
502 buffer->ref ();
503 }
504 queueResize (0, true);
505
506 DBG_OBJ_ASSOC_CHILD (this->buffer);
507 DBG_OBJ_SET_NUM ("bufWidth", bufWidth);
508 DBG_OBJ_SET_NUM ("bufHeight", bufHeight);
509
510 if (oldBuf)
511 oldBuf->unref ();
512}
513
514void Image::drawRow (int row)
515{
516 core::Rectangle area;
517
518 assert (buffer != NULL);
519
520 buffer->getRowArea (row, &area);
521 if (area.width && area.height)
522 queueDrawArea (area.x + boxOffsetX (), area.y + boxOffsetY (), area.width,
523 area.height);
524}
525
527{
528 // Nothing to do; images are always drawn line by line.
529}
530
532{
533 // Could display an error.
534}
535
536
541{
542 isMap = true;
543}
544
545
554{
555 mapList = list;
556 if (mapKey && mapKey != key)
557 delete mapKey;
558 mapKey = key;
559}
560
561} // namespace dw
void setDefaultLink(int link)
Definition image.hh:44
void add(core::Shape *shape, int link)
Definition image.cc:55
int link(int x, int y)
Definition image.cc:62
void draw(core::View *view, core::style::Style *style, int x, int y)
Definition image.cc:43
lout::container::typed::List< ShapeAndLink > * shapesAndLinks
Definition image.hh:36
Represents a list of client-side image maps.
Definition image.hh:24
void drawMap(lout::object::Object *key, core::View *view, core::style::Style *style, int x, int y)
Definition image.cc:122
ImageMap * currentMap
Definition image.hh:50
void addShapeToCurrentMap(core::Shape *shape, int link)
Add a shape to the current map-.
Definition image.cc:109
void startNewMap(lout::object::Object *key)
Start a new map and make it the current one.
Definition image.cc:97
lout::container::typed::HashTable< lout::object::Object, ImageMap > * imageMaps
Definition image.hh:49
void setCurrentMapDefaultLink(int link)
Set default link for current map-.
Definition image.cc:117
int link(lout::object::Object *key, int x, int y)
Definition image.cc:131
void setIsMap()
Sets image as server side image map.
Definition image.cc:540
void setUseMap(ImageMapsList *list, Object *key)
Sets image as client side image map.
Definition image.cc:553
core::Iterator * iterator(core::Content::Type mask, bool atEnd)
Return an iterator for this widget.
Definition image.cc:479
int contentY(core::MousePositionEvent *event)
Definition image.cc:348
void finish()
Called, when all image data has been retrieved.
Definition image.cc:526
void drawRow(int row)
Called, when data from a row is available and has been copied into the image buffer.
Definition image.cc:514
Image(const char *altText)
Definition image.cc:146
void sizeAllocateImpl(core::Allocation *allocation)
See Sizes of Dillo Widgets.
Definition image.cc:275
bool buttonReleaseImpl(core::EventButton *event)
Definition image.cc:398
void sizeRequestSimpl(core::Requisition *requisition)
Simple variant, to be implemented by widgets with sizes not depending on positions.
Definition image.cc:176
static int CLASS_ID
Definition image.hh:152
void containerSizeChangedForChildren()
Definition image.cc:306
bool buttonPressImpl(core::EventButton *event)
Definition image.cc:381
void fatal()
Called, when there are problems with the retrieval of image data.
Definition image.cc:531
int contentX(core::MousePositionEvent *event)
Definition image.cc:340
void getExtremesSimpl(core::Extremes *extremes)
Simple variant, to be implemented by widgets with extremes not depending on positions.
Definition image.cc:242
void setBuffer(core::Imgbuf *buffer, bool resize=false)
Called, when an image buffer is attached.
Definition image.cc:486
void enterNotifyImpl(core::EventCrossing *event)
Definition image.cc:313
void leaveNotifyImpl(core::EventCrossing *event)
Definition image.cc:324
bool motionNotifyImpl(core::EventMotion *event)
Definition image.cc:356
void draw(core::View *view, core::Rectangle *area, core::DrawingContext *context)
Area is given in widget coordinates.
Definition image.cc:412
Set at the top when drawing.
Definition types.hh:295
This implementation of dw::core::Iterator can be used by widgets with no contents.
Definition iterator.hh:97
Represents a button press or release event.
Definition events.hh:58
Represents a enter or leave notify event.
Definition events.hh:75
Represents a mouse motion event.
Definition events.hh:68
The platform independent interface for image buffers.
Definition imgbuf.hh:162
virtual int getRootWidth()=0
virtual void ref()=0
virtual Imgbuf * getScaledBuf(int width, int height)=0
virtual void unref()=0
virtual int getRootHeight()=0
Iterators are used to iterate through the contents of a widget.
Definition iterator.hh:20
Base class for all mouse events related to a specific position.
Definition events.hh:49
dw::core::Shape implemtation for simple rectangles.
Definition types.hh:70
bool intersectsWith(Rectangle *otherRect, Rectangle *dest)
Return whether this rectangle and otherRect intersect.
Definition types.cc:53
Abstract interface for different shapes.
Definition types.hh:59
virtual bool isPointWithin(int x, int y)=0
virtual void draw(core::View *view, core::style::Style *style, int x, int y)=0
An interface to encapsulate platform dependent drawing.
Definition view.hh:17
virtual void mergeClippingView(View *clippingView)=0
virtual void drawImage(Imgbuf *imgbuf, int xRoot, int yRoot, int x, int y, int width, int height)=0
virtual void drawSimpleWrappedText(style::Font *font, style::Color *color, style::Color::Shading shading, int x, int y, int w, int h, const char *text)=0
virtual View * getClippingView(int x, int y, int width, int height)=0
This is the base class for many other classes, which defines very common virtual methods.
Definition object.hh:25
#define DBG_OBJ_ENTER0(aspect, prio, funname)
#define DBG_OBJ_DELETE()
#define DBG_OBJ_CREATE(klass)
#define DBG_OBJ_MSGF(aspect, prio, fmt,...)
#define DBG_OBJ_SET_NUM(var, val)
#define DBG_OBJ_MSG(aspect, prio, msg)
#define DBG_OBJ_ENTER(aspect, prio, funname, fmt,...)
#define DBG_OBJ_LEAVE()
#define DBG_OBJ_ASSOC_CHILD(child)
char * dStrdup(const char *s)
Definition dlib.c:77
static Layout * layout
bool isPerLength(Length l)
Returns true if l is a percentage.
Definition style.hh:445
@ LENGTH_AUTO
Represents "auto" lengths.
Definition style.hh:494
void splitHeightPreserveDescent(int height, int *ascent, int *descent)
Definition widget.cc:2041
Dw is in this namespace, or sub namespaces of this one.
T min(T a, T b)
Definition misc.hh:20
T max(T a, T b)
Definition misc.hh:21
The DilloImage data-structure and methods.
Represents the allocation, i.e.
Definition types.hh:164