source: trunk/poppler/mypoppler/poppler/Link.cc @ 254

Last change on this file since 254 was 254, checked in by Eugene Romanenko, 13 years ago

PDF plugin: Poppler library updated to version 0.8.6

File size: 20.3 KB
Line 
1//========================================================================
2//
3// Link.cc
4//
5// Copyright 1996-2003 Glyph & Cog, LLC
6//
7//========================================================================
8
9#include <config.h>
10
11#ifdef USE_GCC_PRAGMAS
12#pragma implementation
13#endif
14
15#include <stddef.h>
16#include <string.h>
17#include "goo/gmem.h"
18#include "goo/GooString.h"
19#include "Error.h"
20#include "Object.h"
21#include "Array.h"
22#include "Dict.h"
23#include "Link.h"
24#include "Sound.h"
25#include "Movie.h"
26
27//------------------------------------------------------------------------
28// LinkAction
29//------------------------------------------------------------------------
30
31LinkAction *LinkAction::parseDest(Object *obj) {
32  LinkAction *action;
33
34  action = new LinkGoTo(obj);
35  if (!action->isOk()) {
36    delete action;
37    return NULL;
38  }
39  return action;
40}
41
42LinkAction *LinkAction::parseAction(Object *obj, GooString *baseURI) {
43  LinkAction *action;
44  Object obj2, obj3, obj4;
45
46  if (!obj->isDict()) {
47      error(-1, "parseAction: Bad annotation action for URI '%s'",
48            baseURI ? baseURI->getCString() : "NULL");
49      return NULL;
50  }
51
52  obj->dictLookup("S", &obj2);
53
54  // GoTo action
55  if (obj2.isName("GoTo")) {
56    obj->dictLookup("D", &obj3);
57    action = new LinkGoTo(&obj3);
58    obj3.free();
59
60  // GoToR action
61  } else if (obj2.isName("GoToR")) {
62    obj->dictLookup("F", &obj3);
63    obj->dictLookup("D", &obj4);
64    action = new LinkGoToR(&obj3, &obj4);
65    obj3.free();
66    obj4.free();
67
68  // Launch action
69  } else if (obj2.isName("Launch")) {
70    action = new LinkLaunch(obj);
71
72  // URI action
73  } else if (obj2.isName("URI")) {
74    obj->dictLookup("URI", &obj3);
75    action = new LinkURI(&obj3, baseURI);
76    obj3.free();
77
78  // Named action
79  } else if (obj2.isName("Named")) {
80    obj->dictLookup("N", &obj3);
81    action = new LinkNamed(&obj3);
82    obj3.free();
83
84  // Movie action
85  } else if (obj2.isName("Movie")) {
86    action = new LinkMovie(obj);
87
88  // Rendition action
89  } else if (obj2.isName("Rendition")) {
90    action = new LinkRendition(obj);
91
92  // Sound action
93  } else if (obj2.isName("Sound")) {
94    action = new LinkSound(obj);
95
96  // unknown action
97  } else if (obj2.isName()) {
98    action = new LinkUnknown(obj2.getName());
99
100  // action is missing or wrong type
101  } else {
102    error(-1, "parseAction: Unknown annotation action object: URI = '%s'",
103          baseURI ? baseURI->getCString() : "NULL");
104    action = NULL;
105  }
106
107  obj2.free();
108
109  if (action && !action->isOk()) {
110    delete action;
111    return NULL;
112  }
113  return action;
114}
115
116GooString *LinkAction::getFileSpecName(Object *fileSpecObj) {
117  GooString *name;
118  Object obj1;
119
120  name = NULL;
121
122  // string
123  if (fileSpecObj->isString()) {
124    name = fileSpecObj->getString()->copy();
125
126  // dictionary
127  } else if (fileSpecObj->isDict()) {
128#ifdef WIN32
129    if (!fileSpecObj->dictLookup("DOS", &obj1)->isString()) {
130#else
131    if (!fileSpecObj->dictLookup("Unix", &obj1)->isString()) {
132#endif
133      obj1.free();
134      fileSpecObj->dictLookup("F", &obj1);
135    }
136    if (obj1.isString()) {
137      name = obj1.getString()->copy();
138    } else {
139      error(-1, "Illegal file spec in link");
140    }
141    obj1.free();
142
143  // error
144  } else {
145    error(-1, "Illegal file spec in link");
146  }
147
148  // system-dependent path manipulation
149  if (name) {
150#ifdef WIN32
151    int i, j;
152
153    // "//...."             --> "\...."
154    // "/x/...."            --> "x:\...."
155    // "/server/share/...." --> "\\server\share\...."
156    // convert escaped slashes to slashes and unescaped slashes to backslashes
157    i = 0;
158    if (name->getChar(0) == '/') {
159      if (name->getLength() >= 2 && name->getChar(1) == '/') {
160        name->del(0);
161        i = 0;
162      } else if (name->getLength() >= 2 &&
163                 ((name->getChar(1) >= 'a' && name->getChar(1) <= 'z') ||
164                  (name->getChar(1) >= 'A' && name->getChar(1) <= 'Z')) &&
165                 (name->getLength() == 2 || name->getChar(2) == '/')) {
166        name->setChar(0, name->getChar(1));
167        name->setChar(1, ':');
168        i = 2;
169      } else {
170        for (j = 2; j < name->getLength(); ++j) {
171          if (name->getChar(j-1) != '\\' &&
172              name->getChar(j) == '/') {
173            break;
174          }
175        }
176        if (j < name->getLength()) {
177          name->setChar(0, '\\');
178          name->insert(0, '\\');
179          i = 2;
180        }
181      }
182    }
183    for (; i < name->getLength(); ++i) {
184      if (name->getChar(i) == '/') {
185        name->setChar(i, '\\');
186      } else if (name->getChar(i) == '\\' &&
187                 i+1 < name->getLength() &&
188                 name->getChar(i+1) == '/') {
189        name->del(i);
190      }
191    }
192#else
193    // no manipulation needed for Unix
194#endif
195  }
196
197  return name;
198}
199
200//------------------------------------------------------------------------
201// LinkDest
202//------------------------------------------------------------------------
203
204LinkDest::LinkDest(Array *a) {
205  Object obj1, obj2;
206
207  // initialize fields
208  left = bottom = right = top = zoom = 0;
209  changeLeft = changeTop = changeZoom = gFalse;
210  ok = gFalse;
211
212  // get page
213  if (a->getLength() < 2) {
214    error(-1, "Annotation destination array is too short");
215    return;
216  }
217  a->getNF(0, &obj1);
218  if (obj1.isInt()) {
219    pageNum = obj1.getInt() + 1;
220    pageIsRef = gFalse;
221  } else if (obj1.isRef()) {
222    pageRef.num = obj1.getRefNum();
223    pageRef.gen = obj1.getRefGen();
224    pageIsRef = gTrue;
225  } else {
226    error(-1, "Bad annotation destination");
227    goto err2;
228  }
229  obj1.free();
230
231  // get destination type
232  a->get(1, &obj1);
233
234  // XYZ link
235  if (obj1.isName("XYZ")) {
236    kind = destXYZ;
237    if (a->getLength() < 3) {
238      changeLeft = gFalse;
239    } else {
240      a->get(2, &obj2);
241      if (obj2.isNull()) {
242        changeLeft = gFalse;
243      } else if (obj2.isNum()) {
244        changeLeft = gTrue;
245        left = obj2.getNum();
246      } else {
247        error(-1, "Bad annotation destination position");
248        goto err1;
249      }
250      obj2.free();
251    }
252    if (a->getLength() < 4) {
253      changeTop = gFalse;
254    } else {
255      a->get(3, &obj2);
256      if (obj2.isNull()) {
257        changeTop = gFalse;
258      } else if (obj2.isNum()) {
259        changeTop = gTrue;
260        top = obj2.getNum();
261      } else {
262        error(-1, "Bad annotation destination position");
263        goto err1;
264      }
265      obj2.free();
266    }
267    if (a->getLength() < 5) {
268      changeZoom = gFalse;
269    } else {
270      a->get(4, &obj2);
271      if (obj2.isNull()) {
272        changeZoom = gFalse;
273      } else if (obj2.isNum()) {
274        changeZoom = gTrue;
275        zoom = obj2.getNum();
276      } else {
277        error(-1, "Bad annotation destination position");
278        goto err1;
279      }
280      obj2.free();
281    }
282
283  // Fit link
284  } else if (obj1.isName("Fit")) {
285    if (a->getLength() < 2) {
286      error(-1, "Annotation destination array is too short");
287      goto err2;
288    }
289    kind = destFit;
290
291  // FitH link
292  } else if (obj1.isName("FitH")) {
293    if (a->getLength() < 3) {
294      error(-1, "Annotation destination array is too short");
295      goto err2;
296    }
297    kind = destFitH;
298    a->get(2, &obj2);
299    if (obj2.isNull()) {
300      changeTop = gFalse;
301    } else if (obj2.isNum()) {
302      changeTop = gTrue;
303      top = obj2.getNum();
304    } else {
305      error(-1, "Bad annotation destination position");
306      kind = destFit;
307    }
308    obj2.free();
309
310  // FitV link
311  } else if (obj1.isName("FitV")) {
312    if (a->getLength() < 3) {
313      error(-1, "Annotation destination array is too short");
314      goto err2;
315    }
316    kind = destFitV;
317    a->get(2, &obj2);
318    if (obj2.isNull()) {
319      changeLeft = gFalse;
320    } else if (obj2.isNum()) {
321      changeLeft = gTrue;
322      left = obj2.getNum();
323    } else {
324      error(-1, "Bad annotation destination position");
325      kind = destFit;
326    }
327    obj2.free();
328
329  // FitR link
330  } else if (obj1.isName("FitR")) {
331    if (a->getLength() < 6) {
332      error(-1, "Annotation destination array is too short");
333      goto err2;
334    }
335    kind = destFitR;
336    if (!a->get(2, &obj2)->isNum()) {
337      error(-1, "Bad annotation destination position");
338      kind = destFit;
339    }
340    left = obj2.getNum();
341    obj2.free();
342    if (!a->get(3, &obj2)->isNum()) {
343      error(-1, "Bad annotation destination position");
344      kind = destFit;
345    }
346    bottom = obj2.getNum();
347    obj2.free();
348    if (!a->get(4, &obj2)->isNum()) {
349      error(-1, "Bad annotation destination position");
350      kind = destFit;
351    }
352    right = obj2.getNum();
353    obj2.free();
354    if (!a->get(5, &obj2)->isNum()) {
355      error(-1, "Bad annotation destination position");
356      kind = destFit;
357    }
358    top = obj2.getNum();
359    obj2.free();
360
361  // FitB link
362  } else if (obj1.isName("FitB")) {
363    if (a->getLength() < 2) {
364      error(-1, "Annotation destination array is too short");
365      goto err2;
366    }
367    kind = destFitB;
368
369  // FitBH link
370  } else if (obj1.isName("FitBH")) {
371    if (a->getLength() < 3) {
372      error(-1, "Annotation destination array is too short");
373      goto err2;
374    }
375    kind = destFitBH;
376    a->get(2, &obj2);
377    if (obj2.isNull()) {
378      changeTop = gFalse;
379    } else if (obj2.isNum()) {
380      changeTop = gTrue;
381      top = obj2.getNum();
382    } else {
383      error(-1, "Bad annotation destination position");
384      kind = destFit;
385    }
386    obj2.free();
387
388  // FitBV link
389  } else if (obj1.isName("FitBV")) {
390    if (a->getLength() < 3) {
391      error(-1, "Annotation destination array is too short");
392      goto err2;
393    }
394    kind = destFitBV;
395    a->get(2, &obj2);
396    if (obj2.isNull()) {
397      changeLeft = gFalse;
398    } else if (obj2.isNum()) {
399      changeLeft = gTrue;
400      left = obj2.getNum();
401    } else {
402      error(-1, "Bad annotation destination position");
403      kind = destFit;
404    }
405    obj2.free();
406
407  // unknown link kind
408  } else {
409    error(-1, "Unknown annotation destination type");
410    goto err2;
411  }
412
413  obj1.free();
414  ok = gTrue;
415  return;
416
417 err1:
418  obj2.free();
419 err2:
420  obj1.free();
421}
422
423LinkDest::LinkDest(LinkDest *dest) {
424  kind = dest->kind;
425  pageIsRef = dest->pageIsRef;
426  if (pageIsRef)
427    pageRef = dest->pageRef;
428  else
429    pageNum = dest->pageNum;
430  left = dest->left;
431  bottom = dest->bottom;
432  right = dest->right;
433  top = dest->top;
434  zoom = dest->zoom;
435  changeLeft = dest->changeLeft;
436  changeTop = dest->changeTop;
437  changeZoom = dest->changeZoom;
438  ok = gTrue;
439}
440
441//------------------------------------------------------------------------
442// LinkGoTo
443//------------------------------------------------------------------------
444
445LinkGoTo::LinkGoTo(Object *destObj) {
446  dest = NULL;
447  namedDest = NULL;
448
449  // named destination
450  if (destObj->isName()) {
451    namedDest = new GooString(destObj->getName());
452  } else if (destObj->isString()) {
453    namedDest = destObj->getString()->copy();
454
455  // destination dictionary
456  } else if (destObj->isArray()) {
457    dest = new LinkDest(destObj->getArray());
458    if (!dest->isOk()) {
459      delete dest;
460      dest = NULL;
461    }
462
463  // error
464  } else {
465    error(-1, "Illegal annotation destination");
466  }
467}
468
469LinkGoTo::~LinkGoTo() {
470  if (dest)
471    delete dest;
472  if (namedDest)
473    delete namedDest;
474}
475
476//------------------------------------------------------------------------
477// LinkGoToR
478//------------------------------------------------------------------------
479
480LinkGoToR::LinkGoToR(Object *fileSpecObj, Object *destObj) {
481  dest = NULL;
482  namedDest = NULL;
483
484  // get file name
485  fileName = getFileSpecName(fileSpecObj);
486
487  // named destination
488  if (destObj->isName()) {
489    namedDest = new GooString(destObj->getName());
490  } else if (destObj->isString()) {
491    namedDest = destObj->getString()->copy();
492
493  // destination dictionary
494  } else if (destObj->isArray()) {
495    dest = new LinkDest(destObj->getArray());
496    if (!dest->isOk()) {
497      delete dest;
498      dest = NULL;
499    }
500
501  // error
502  } else {
503    error(-1, "Illegal annotation destination");
504  }
505}
506
507LinkGoToR::~LinkGoToR() {
508  if (fileName)
509    delete fileName;
510  if (dest)
511    delete dest;
512  if (namedDest)
513    delete namedDest;
514}
515
516
517//------------------------------------------------------------------------
518// LinkLaunch
519//------------------------------------------------------------------------
520
521LinkLaunch::LinkLaunch(Object *actionObj) {
522  Object obj1, obj2;
523
524  fileName = NULL;
525  params = NULL;
526
527  if (actionObj->isDict()) {
528    if (!actionObj->dictLookup("F", &obj1)->isNull()) {
529      fileName = getFileSpecName(&obj1);
530    } else {
531      obj1.free();
532#ifdef WIN32
533      if (actionObj->dictLookup("Win", &obj1)->isDict()) {
534        obj1.dictLookup("F", &obj2);
535        fileName = getFileSpecName(&obj2);
536        obj2.free();
537        if (obj1.dictLookup("P", &obj2)->isString()) {
538          params = obj2.getString()->copy();
539        }
540        obj2.free();
541      } else {
542        error(-1, "Bad launch-type link action");
543      }
544#else
545      //~ This hasn't been defined by Adobe yet, so assume it looks
546      //~ just like the Win dictionary until they say otherwise.
547      if (actionObj->dictLookup("Unix", &obj1)->isDict()) {
548        obj1.dictLookup("F", &obj2);
549        fileName = getFileSpecName(&obj2);
550        obj2.free();
551        if (obj1.dictLookup("P", &obj2)->isString()) {
552          params = obj2.getString()->copy();
553        }
554        obj2.free();
555      } else {
556        error(-1, "Bad launch-type link action");
557      }
558#endif
559    }
560    obj1.free();
561  }
562}
563
564LinkLaunch::~LinkLaunch() {
565  if (fileName)
566    delete fileName;
567  if (params)
568    delete params;
569}
570
571//------------------------------------------------------------------------
572// LinkURI
573//------------------------------------------------------------------------
574
575LinkURI::LinkURI(Object *uriObj, GooString *baseURI) {
576  GooString *uri2;
577  int n;
578  char c;
579
580  uri = NULL;
581  if (uriObj->isString()) {
582    uri2 = uriObj->getString()->copy();
583    if (baseURI && baseURI->getLength() > 0) {
584      n = strcspn(uri2->getCString(), "/:");
585      if (n == uri2->getLength() || uri2->getChar(n) == '/') {
586        uri = baseURI->copy();
587        c = uri->getChar(uri->getLength() - 1);
588        if (c == '/' || c == '?') {
589          if (uri2->getChar(0) == '/') {
590            uri2->del(0);
591          }
592        } else {
593          if (uri2->getChar(0) != '/') {
594            uri->append('/');
595          }
596        }
597        uri->append(uri2);
598        delete uri2;
599      } else {
600        uri = uri2;
601      }
602    } else {
603      uri = uri2;
604    }
605  } else {
606    error(-1, "Illegal URI-type link");
607  }
608}
609
610LinkURI::~LinkURI() {
611  if (uri)
612    delete uri;
613}
614
615//------------------------------------------------------------------------
616// LinkNamed
617//------------------------------------------------------------------------
618
619LinkNamed::LinkNamed(Object *nameObj) {
620  name = NULL;
621  if (nameObj->isName()) {
622    name = new GooString(nameObj->getName());
623  }
624}
625
626LinkNamed::~LinkNamed() {
627  if (name) {
628    delete name;
629  }
630}
631
632//------------------------------------------------------------------------
633// LinkMovie
634//------------------------------------------------------------------------
635
636LinkMovie::LinkMovie(Object *obj) {
637  annotRef.num = -1;
638  annotTitle = NULL;
639
640  Object tmp;
641  if (obj->dictLookupNF("Annotation", &tmp)->isRef()) {
642    annotRef = tmp.getRef();
643  }
644  tmp.free();
645
646  if (obj->dictLookup("T", &tmp)->isString()) {
647    annotTitle = tmp.getString()->copy();
648  }
649  tmp.free();
650
651  if ((annotTitle == NULL) && (annotRef.num == -1)) {
652    error(-1, "Movie action is missing both the Annot and T keys");
653  }
654
655  if (obj->dictLookup("Operation", &tmp)->isName()) {
656    char *name = tmp.getName();
657   
658    if (!strcmp(name, "Play")) {
659      operation = operationTypePlay;
660    }
661    else if (!strcmp(name, "Stop")) {
662      operation = operationTypeStop;
663    }
664    else if (!strcmp(name, "Pause")) {
665      operation = operationTypePause;
666    }
667    else if (!strcmp(name, "Resume")) {
668      operation = operationTypeResume;
669    }
670  }
671  tmp.free();
672}
673
674LinkMovie::~LinkMovie() {
675  if (annotTitle) {
676    delete annotTitle;
677  }
678}
679
680//------------------------------------------------------------------------
681// LinkSound
682//------------------------------------------------------------------------
683
684LinkSound::LinkSound(Object *soundObj) {
685  volume = 1.0;
686  sync = gFalse;
687  repeat = gFalse;
688  mix = gFalse;
689  sound = NULL;
690  if (soundObj->isDict())
691  {
692    Object tmp;
693    // volume
694    soundObj->dictLookup("Volume", &tmp);
695    if (tmp.isNum()) {
696      volume = tmp.getNum();
697    }
698    tmp.free();
699    // sync
700    soundObj->dictLookup("Synchronous", &tmp);
701    if (tmp.isBool()) {
702      sync = tmp.getBool();
703    }
704    tmp.free();
705    // repeat
706    soundObj->dictLookup("Repeat", &tmp);
707    if (tmp.isBool()) {
708      repeat = tmp.getBool();
709    }
710    tmp.free();
711    // mix
712    soundObj->dictLookup("Mix", &tmp);
713    if (tmp.isBool()) {
714      mix = tmp.getBool();
715    }
716    tmp.free();
717    // 'Sound' object
718    soundObj->dictLookup("Sound", &tmp);
719    sound = Sound::parseSound(&tmp);
720    tmp.free();
721  }
722}
723
724LinkSound::~LinkSound() {
725  delete sound;
726}
727
728//------------------------------------------------------------------------
729// LinkRendition
730//------------------------------------------------------------------------
731
732LinkRendition::LinkRendition(Object *Obj) {
733  operation = -1;
734  movie = NULL;
735  screenRef.num = -1;
736
737  if (Obj->isDict())
738  {
739    Object tmp;
740
741    if (Obj->dictLookup("OP", &tmp)->isNull()) {
742      error(-1, "Rendition action : no /OP field defined");
743      tmp.free();
744    } else {
745   
746      operation = tmp.getInt();
747      tmp.free();
748
749      // screen annotation reference
750      Obj->dictLookupNF("AN", &tmp);
751      if (tmp.isRef()) {
752        screenRef = tmp.getRef();
753      }
754      tmp.free();
755
756      // retrieve rendition object
757      Obj->dictLookup("R", &renditionObj);
758      if (renditionObj.isDict()) {
759
760        movie = new Movie();
761        movie->parseMediaRendition(&renditionObj);
762       
763        if (screenRef.num == -1) {
764          error(-1, "Action Rendition : Rendition without Screen Annotation !");
765        }
766      }     
767
768    }
769  }
770
771}
772
773LinkRendition::~LinkRendition() {
774  renditionObj.free();
775
776  if (movie)
777    delete movie;
778}
779
780
781//------------------------------------------------------------------------
782// LinkUnknown
783//------------------------------------------------------------------------
784
785LinkUnknown::LinkUnknown(char *actionA) {
786  action = new GooString(actionA);
787}
788
789LinkUnknown::~LinkUnknown() {
790  delete action;
791}
792
793//------------------------------------------------------------------------
794// Link
795//------------------------------------------------------------------------
796
797Link::Link(Dict *dict, GooString *baseURI) {
798  Object obj1, obj2;
799  double t;
800
801  action = NULL;
802  ok = gFalse;
803
804  // get rectangle
805  if (!dict->lookup("Rect", &obj1)->isArray()) {
806    error(-1, "Annotation rectangle is wrong type");
807    goto err2;
808  }
809  if (!obj1.arrayGet(0, &obj2)->isNum()) {
810    error(-1, "Bad annotation rectangle");
811    goto err1;
812  }
813  x1 = obj2.getNum();
814  obj2.free();
815  if (!obj1.arrayGet(1, &obj2)->isNum()) {
816    error(-1, "Bad annotation rectangle");
817    goto err1;
818  }
819  y1 = obj2.getNum();
820  obj2.free();
821  if (!obj1.arrayGet(2, &obj2)->isNum()) {
822    error(-1, "Bad annotation rectangle");
823    goto err1;
824  }
825  x2 = obj2.getNum();
826  obj2.free();
827  if (!obj1.arrayGet(3, &obj2)->isNum()) {
828    error(-1, "Bad annotation rectangle");
829    goto err1;
830  }
831  y2 = obj2.getNum();
832  obj2.free();
833  obj1.free();
834  if (x1 > x2) {
835    t = x1;
836    x1 = x2;
837    x2 = t;
838  }
839  if (y1 > y2) {
840    t = y1;
841    y1 = y2;
842    y2 = t;
843  }
844
845  // look for destination
846  if (!dict->lookup("Dest", &obj1)->isNull()) {
847    action = LinkAction::parseDest(&obj1);
848
849  // look for action
850  } else {
851    obj1.free();
852    if (dict->lookup("A", &obj1)->isDict()) {
853      action = LinkAction::parseAction(&obj1, baseURI);
854    }
855  }
856  obj1.free();
857
858  // check for bad action
859  if (action) {
860    ok = gTrue;
861  }
862
863  return;
864
865 err1:
866  obj2.free();
867 err2:
868  obj1.free();
869}
870
871Link::~Link() {
872  if (action) {
873    delete action;
874  }
875}
876
877//------------------------------------------------------------------------
878// Links
879//------------------------------------------------------------------------
880
881Links::Links(Object *annots, GooString *baseURI) {
882  Link *link;
883  Object obj1, obj2;
884  int size;
885  int i;
886
887  links = NULL;
888  size = 0;
889  numLinks = 0;
890
891  if (annots->isArray()) {
892    for (i = 0; i < annots->arrayGetLength(); ++i) {
893      if (annots->arrayGet(i, &obj1)->isDict()) {
894        if (obj1.dictLookup("Subtype", &obj2)->isName("Link")) {
895          link = new Link(obj1.getDict(), baseURI);
896          if (link->isOk()) {
897            if (numLinks >= size) {
898              size += 16;
899              links = (Link **)greallocn(links, size, sizeof(Link *));
900            }
901            links[numLinks++] = link;
902          } else {
903            delete link;
904          }
905        }
906        obj2.free();
907      }
908      obj1.free();
909    }
910  }
911}
912
913Links::~Links() {
914  int i;
915
916  for (i = 0; i < numLinks; ++i)
917    delete links[i];
918  gfree(links);
919}
920
921LinkAction *Links::find(double x, double y) const {
922  int i;
923
924  for (i = numLinks - 1; i >= 0; --i) {
925    if (links[i]->inRect(x, y)) {
926      return links[i]->getAction();
927    }
928  }
929  return NULL;
930}
931
932GBool Links::onLink(double x, double y) const {
933  int i;
934
935  for (i = 0; i < numLinks; ++i) {
936    if (links[i]->inRect(x, y))
937      return gTrue;
938  }
939  return gFalse;
940}
Note: See TracBrowser for help on using the repository browser.