1    /*
2    Copyright 2000 by Ralph Hartley
3    This software is licenced under the terms of the
4    Gnu Public Licence
5    */
6    import java.util.*;
7    import java.awt.*;
8    import java.awt.geom.*;
9    import javax.swing.*;
10   
11   public class Comp extends Symbol implements Editable,Region,Modable {
12   
13     private static final long serialVersionUID = Version.getSUID();
14   
15     public static int TOP = 0;
16     public static int BOTTOM = 1;
17     public static int ABOVE = 2;
18     public static int BELOW = 3;
19   
20     Vector members = new Vector();
21     boolean survey = false;
22     boolean isVertical = false;
23   
24     boolean showall = true;
25     Layer visible = Layer.all;
26     Layer showin = Layer.none;
27     Layer showout = Layer.none;
28   //  Layer showmodify = (Layer)Layer.lookup(Layer.class,Curve.class.toString());
29   
30     public Map regions = new HashMap();
31   
32     public Stacking stack = new Stacking();
33   
34     public static Color defbackground = Color.WHITE;
35   
36     public static Color passagecolor = Color.BLUE;
37     public static Color passagelinkcolor = Color.BLUE;
38     public static Color alignercolor = Color.RED;
39     public static Color alignlinkcolor = Color.PINK;
40   
41     Color background = defbackground;
42     Fill fill = null;
43   
44   //  transient Rectangle2D bounds = null;
45   
46   //  transient CompPane curpane =null;
47   //  transient public boolean dirty = false;
48   
49     public Comp(Point2D pos,View view,Object arg) {
50       this();
51     }
52   
53     public Comp() {
54       members = new Vector();
55     }
56   
57     public Comp(String name,Survey survey,boolean isVertical) {
58       super(name);
59       this.isVertical = isVertical;
60       if (survey!=null) {
61         add(survey);
62         this.survey = true;
63       }
64       else this.survey = false;
65   
66       showin = (Layer)Layer.lookup(Layer.class,"In");
67       showout = (Layer)Layer.lookup(Layer.class,"Out");
68     }
69   
70     public Symbol importSym(Carto dest,Carto source) {
71   
72       bounds = null;
73   
74       if (survey && dest.survey==null) dest.survey = source.survey;
75   
76       if (source.complist.contains(this))
77         dest.complist.add(this);
78   
79       Vector newmembers = new Vector();
80       for (Iterator it = members.iterator();it.hasNext();)
81         newmembers.add(((Symbol)it.next()).importSym(dest,source));
82       
83       members = newmembers;
84       
85       return(this);
86     }
87   
88     public void clean(Vector segments) {
89       HashSet seen = new HashSet();
90       for (Iterator it = members.iterator();it.hasNext();) {
91         Symbol item = (Symbol) it.next();
92         if (item instanceof Curve && ((Curve)item).points.size()==0) it.remove();
93         else if (item instanceof Segment) {
94           if ((!segments.contains(item))||(seen.contains(item))) it.remove();
95           else seen.add(item);
96         }
97         else if ((item instanceof Text) && ((Text)item).contents.length()==0) it.remove(); 
98         // I have seen corruption like this, I don't know if the bug that produced it still exists.
99         else if ((item instanceof Sub) &&
100                 (((Sub)item).anchorpoint.position==null ||
101          	Double.isNaN(((Sub)item).anchorpoint.position.getScaleX())))
102          it.remove();
103  //        System.out.println("item = "+item+" anchor = "+((Sub)item).anchorpoint.position);
104      }
105    }
106  
107    /**
108     * Prepare all members for survey update.
109     */
110    public void prepareForSurveyUpdate(java.util.List segments) {
111      if (name.compareTo("drywater")==0) { System.out.println("Comp prep");
112  //    new Throwable().printStackTrace();
113  }
114  
115      java.util.List segs = new ArrayList();
116      for (Iterator it = members.iterator();it.hasNext();) {
117        Symbol item = (Symbol)it.next();
118        if (item instanceof Segment)
119          segs.add(item);
120      }
121  //    System.out.println("comp items prep segs = "+segs);
122  
123      for (Iterator it = members.iterator();it.hasNext();) {
124        Symbol sym = (Symbol)it.next();
125        // If it is a Comp it gets done from Carto.prepareForSurveyUpdate()
126        if (! (sym instanceof Comp)) sym.prepareForSurveyUpdate(segs);
127      }
128    }
129  
130    /**
131     * Tell all members to use a survey update that just happened.
132     */
133    public void useSurveyUpdate() {
134      for (Iterator it = members.iterator();it.hasNext();) {
135        Symbol sym = (Symbol)it.next();
136        // If it is a Comp it gets done from Carto.useSurveyUpdate()
137        if (! (sym instanceof Comp)) sym.useSurveyUpdate();
138      }
139      bounds = null;
140    }
141  
142    public Set getPages() {
143      Set res = new HashSet();
144      for (Iterator it = members.iterator();it.hasNext();) {
145        Symbol item = (Symbol)it.next();
146        if (item instanceof Page) res.add(item);
147      }
148      return(res);
149    }
150   
151    public int getLevel() {return(super.getLevel()+2);}
152  
153    transient ColorBox colorbox = null;
154    transient JCheckBox viewall = null;
155    transient JComboBox showbox = null;
156    transient JComboBox showinbox = null;
157    transient JComboBox showoutbox = null;
158  
159  //  transient JComboBox modbox = null;
160  
161    public void getPropertyEdit(Object[] edits,int slot,Set sub,Symbol parent) {
162      Container res = new javax.swing.Box(BoxLayout.Y_AXIS);
163  
164      edits[slot] = res;
165      res.setName("Plan");
166      viewall = new JCheckBox(CompEditor.VIEWALL);
167      viewall.setSelected(showall);
168      res.add(viewall);
169  
170      showbox = new JComboBox(Layer.getAll(Layer.class));
171      showbox.setSelectedItem(visible);
172      Container row = new javax.swing.Box(BoxLayout.X_AXIS);
173      row.add(new JLabel("Filter Visible"));
174      row.add(showbox);
175      res.add(row);
176  
177      showinbox = new JComboBox(Layer.getAll(Layer.class));
178      showinbox.setSelectedItem(showin);
179      row = new javax.swing.Box(BoxLayout.X_AXIS);
180      row.add(new JLabel("Filter In"));
181      row.add(showinbox);
182      res.add(row);
183  
184      showoutbox = new JComboBox(Layer.getAll(Layer.class));
185      showoutbox.setSelectedItem(showout);
186      row = new javax.swing.Box(BoxLayout.X_AXIS);
187      row.add(new JLabel("Filter Out"));
188      row.add(showoutbox);
189      res.add(row);
190  
191  
192  //    res.add(new JCheckBox("survey",survey));
193      res.add(new JLabel("background colors (below) not implemented yet"));
194      res.add(colorbox = new ColorBox(background,"Background"));
195  
196      if (stack==null) stack = new Stacking();
197      res = stack.getPane(this);
198  
199      edits[slot-1] = res;
200      res.setName("Stacking");
201  
202      super.getPropertyEdit(edits,slot-2,sub,parent);
203    }
204  
205    public void acceptPropertyEdit() {
206      if (colorbox!=null) {
207        background = colorbox.color;
208  
209        showall = viewall.isSelected();
210        viewall = null;
211  
212        visible = (Layer)showbox.getSelectedItem();
213        showbox = null;
214  
215        showin = (Layer)showinbox.getSelectedItem();
216        showinbox = null;
217  
218        showout = (Layer)showoutbox.getSelectedItem();
219        showoutbox = null;
220  
221        stack.save();
222        stack.clear();
223  
224  //      showmodify = (Layer)modbox.getSelectedItem();
225  //      modbox = null;
226  
227        if (background == null) background = Color.WHITE;
228        namefield = null;
229      }
230      super.acceptPropertyEdit();
231    }
232  
233    public void abandonPropertyEdit() {
234      colorbox = null;
235      viewall = null;
236      showbox = null;
237      showinbox = null;
238      showoutbox = null;
239      stack.clear();
240  //    modbox = null;
241      super.abandonPropertyEdit();
242    }
243  
244    public Vector sortSet(Set s) {
245      Vector res = new Vector();
246      for (Iterator it = members.iterator();it.hasNext();) {
247        Symbol item = (Symbol)it.next();
248        if (s.contains(item)) res.add(item);
249      }
250      return(res);
251    }
252  
253    public Editor getEditor(CartoFrame frame) {
254      return(new CompEditor(this,frame));
255    }
256  
257    public Area getIn(View view) {
258      Area res = new Area();
259  
260      for (Iterator it = members.iterator(); it.hasNext();) {
261        Symbol item = (Symbol)it.next();
262  
263        if (item instanceof Region) {
264          Integer flag = (Integer)regions.get(item);
265  
266          if (flag!=null) {
267  
268            if (flag.intValue() == IN){
269              res.add(((Region)item).getIn(view));
270            }
271  
272            else if (flag.intValue() == OUT) {
273              res.subtract(((Region)item).getIn(view));
274            }
275          }
276        }
277      }
278  
279      return(res);
280    }
281  
282    transient Rectangle2D bounds = null;
283  
284    public Rectangle2D getApproxBounds(AffineTransform trans) {
285      if (bounds==null)
286        bounds = getBounds(new AffineTransform());
287      return (trans.createTransformedShape(bounds).getBounds2D());
288    }
289  
290    public Rectangle2D getBounds(AffineTransform trans) {
291  
292      if (regions != null && regions.size()!=0) {
293        Rectangle2D res = null;
294        for (Iterator it = regions.keySet().iterator(); it.hasNext();) {
295          if (res == null) res = ((Symbol)it.next()).getBounds(trans);
296          else res.add(((Symbol)it.next()).getBounds(trans));
297        }
298  //      bounds = res;
299        return(res);
300      }
301  
302      if (members.size()==0) return(new Rectangle2D.Double(0.5,0.5,1.0,1.0));
303      Rectangle2D res = null;
304      Symbol item = null;
305      Rectangle2D newrect = null;
306      for (Iterator it = members.iterator();it.hasNext();) {
307        item = (Symbol)it.next();
308        if (item==null) {
309          it.remove();
310          continue;
311        }
312        newrect = item.getBounds(trans);
313  /*      if ((item instanceof Segment) &&// (!Segment.removeunmorphed) &&
314  //          !(Segment.cookinprogress) &&
315            !((Segment)item).cooking &&
316            !((Segment)item).isCooked() &&
317            Segment.removeunmorphed &&
318            JOptionPane.YES_OPTION == 
319            JOptionPane.showConfirmDialog(null,
320          				"Remove unmorphed segment "+item.name+
321          				" from composite "+name+"?",
322          				"Unmorphed",
323          				JOptionPane.YES_NO_OPTION))
324          it.remove();
325  */
326        if (res==null) res = newrect;
327        else if (newrect!=null) res.add(newrect);
328      }
329      if (res==null) return(new Rectangle2D.Double(0.5,0.5,1.0,1.0));
330  
331  //    bounds = res;
332      return(res);
333    }
334  
335    public Collection getSelectable(){
336      return(members);
337    }
338  
339    public boolean recursiveDelete(Symbol old){
340      bounds = null;
341      for (Iterator it=members.iterator();it.hasNext();) {
342        Symbol member = (Symbol)it.next();
343        if (member==old || member.recursiveDelete(old))
344          it.remove();
345      }
346      return(members.size()==0);
347    }
348  
349    public Class addType() {
350      return(Symbol.class);
351    }
352  
353    public void add(Symbol newmember,View v) {
354      bounds = null;
355      if (newmember==null) throw new NullPointerException("Attempt to add null to a Comp");
356      members.add(newmember);
357    }
358  
359    public void add(Symbol newmember) {
360      bounds = null;
361      members.add(newmember);
362    }
363  
364    public void delete(Symbol oldmember) {
365      bounds = null;
366      members.remove(oldmember);
367    }
368  
369    public void add(Symbol newmember, int level) {
370      add(newmember,level,null);
371    }
372  
373    public void add(Symbol newmember, int level,Set relativeset) {
374      bounds = null;
375      int place;
376      if (relativeset==null && level==ABOVE) level=TOP;
377      if (relativeset==null && level==BELOW) level=BOTTOM;
378      if (level==BOTTOM || level == ABOVE) place = 0;
379      else place = members.size();
380      if (relativeset!=null)
381        for (Iterator it = relativeset.iterator();it.hasNext();) {
382          int i = members.indexOf(it.next());
383          if (level == BELOW && i>place) i = place;
384          else if (level == ABOVE && i<=place) place = i + 1;
385        }
386      members.add(place,newmember);
387    }
388  
389    public Glyph group(Set parts) {
390      return(null);
391  /*
392      if (parts.size()==0) return(null);
393  
394      Glyph whole = new Glyph();
395      boolean added = false;
396  
397      for (int i = 0;i<members.size();i++) {
398        Symbol symb = (Symbol) it.elementAt(i);
399  
400        if (parts.contains(symb)) {
401          members.remove(i);
402          if (!added) members.add(i,whole);
403          whole.contents.add(symb);
404        }
405      }
406  
407      whole.findCenter();
408      return(whole);
409  */
410    }
411  
412    public Set unGroup(Glyph whole) {
413      return(null);
414  /*
415      Set parts = whole.transformedMembers();;
416      int place = members.indexOf(whole);
417      members.remove(whole);
418      for (Iterator it = parts.iterator;it.hasNext();) {
419        members.add(place++,it.next());
420      }
421      return(parts);
422  */
423    }
424  
425    public void change_level(Set targets,int level) {
426      int dir;
427      int i,j;
428      Object temp;
429  
430      if (level == TOP || level == ABOVE) {
431        i = members.size() - 1;
432        dir = -1;
433      }
434      else {
435        i = 0;
436        dir = 1;
437      }
438  
439      for (;i>=0 && i<members.size() && targets.contains(members.elementAt(i));i+=dir);
440      j = i;
441  
442      for (;i>=0 && i<members.size();i+=dir)
443        if (targets.contains(members.elementAt(i))) {
444          if (level == ABOVE || level == BELOW) j = i - dir;
445          temp = members.elementAt(i);
446          members.remove(i);
447          members.add(j,temp);
448          j += dir;
449        }
450    }
451    
452    public boolean selectProbe(Point2D pos,View view){
453      Rectangle2D rect = getBounds(new java.awt.geom.AffineTransform());
454      double BOXSIZE = Size.handle.getSize(view.mapscale);
455  
456      if (pos.distance(rect.getX(),rect.getY())<BOXSIZE/view.scale) return(true);
457      if (pos.distance(rect.getX()+rect.getWidth(),rect.getY())<BOXSIZE/view.scale) return(true);
458      if (pos.distance(rect.getX(),rect.getY()+rect.getHeight())<BOXSIZE/view.scale) return(true);
459      if (pos.distance(rect.getX()+rect.getWidth(),rect.getY()+rect.getHeight())<BOXSIZE/view.scale) return(true);
460  //    if (pos.distance(rect.getX(),rect.getY())<BOXSIZE/view.scale) return(true);
461  //    if (pos.distance(rect.getX()+rect.getWidth(),rect.getY())<BOXSIZE/view.scale) return(true);
462  //    if (pos.distance(rect.getX(),rect.getY()+rect.getHeight())<BOXSIZE/view.scale) return(true);
463  //    if (pos.distance(rect.getX()+rect.getWidth(),rect.getY()+rect.getHeight())<BOXSIZE/view.scale) return(true);
464  
465      return(false);
466    }
467  
468    public void showSelected(View view) {
469      int BOXSIZE = (int)Size.handle.getSize(view.mapscale);
470      Rectangle2D rect = getBounds(view.trans);
471      view.draw.draw(rect);
472      view.draw.drawRect((int)rect.getX()                     -BOXSIZE,(int)rect.getY()                      -BOXSIZE,
473          	       2*BOXSIZE,2*BOXSIZE);
474      view.draw.drawRect((int)rect.getX()+(int)rect.getWidth()-BOXSIZE,(int)rect.getY()                      -BOXSIZE,
475          	       2*BOXSIZE,2*BOXSIZE);
476      view.draw.drawRect((int)rect.getX()                     -BOXSIZE,(int)rect.getY()+(int)rect.getHeight()-BOXSIZE,
477          	       2*BOXSIZE,2*BOXSIZE);
478      view.draw.drawRect((int)rect.getX()+(int)rect.getWidth()-BOXSIZE,(int)rect.getY()+(int)rect.getHeight()-BOXSIZE,
479          	       2*BOXSIZE,2*BOXSIZE);
480    }
481  
482    public Survey getSurvey() {
483      for (Iterator it = members.iterator(); it.hasNext();) {
484        Symbol sym = (Symbol)it.next();
485        if (sym instanceof Survey) return((Survey)sym);
486      }
487      return(new Survey());
488    }
489  
490    public void paint(View baseview) {
491      if (!baseview.visible.isMember(this)) return;
492  
493      if (!getApproxBounds(baseview.trans).intersects(baseview.getClipBounds())) return;
494  
495      int n = members.size();
496  
497      Comp oldcomp = baseview.composite;
498      baseview.composite = this;
499  
500      Area in = null;
501      Area out = null;
502  
503      View inview = null;
504      View outview = null;
505  
506      if (!baseview.showall &&
507          regions!=null && regions.size()>0 &&
508          ((showin!=null && showin!=Layer.none) ||
509           (showout!=null && showout!=Layer.none))) {
510        in = getIn(baseview);
511        if (in!=null) {
512          inview = (View)baseview.clone();
513          inview.intersectClip(in);
514        }
515  
516        out = new Area(baseview.getClip());
517        out.subtract(in);
518        if (in!=null) {
519          outview = (View)baseview.clone();
520          outview.intersectClip(out);
521        }
522      }
523  
524      for (int i=0;i<n;i++) {
525        Symbol sym = (Symbol)members.elementAt(i);
526  
527        if (sym instanceof Survey) continue;
528  
529        View view = baseview;
530  
531        if (regions!=null && !regions.containsKey(sym)) {
532  
533          if (outview!=null && !showin.isMember(sym))
534            view = outview;
535          
536          if (inview!=null && !showout.isMember(sym))
537            view = inview;
538        }
539  
540        Shape clip = view.getClip();
541  
542        if (view.stacking!=null) {
543          view.stacking.getOverUnder(sym,view);
544          Area over = view.stacking.over;
545          Area under = view.stacking.under;
546          Area hide = view.stacking.hide;
547  
548          if (over!=null || under!=null || hide!=null) {
549            Area natclip = new Area(view.getClip());
550  
551            if (hide != null) {
552              natclip.subtract(hide);
553              view.setClip(natclip);
554            }
555  
556            if (over!=null) {
557              if (sym instanceof Modable) {
558                int modsave = view.mod;
559                view.mod = Stacking.OVER;
560                view.intersectClip(over);
561                sym.paint(view);
562                view.mod = modsave;
563              }
564              natclip.subtract(over);
565              view.setClip(natclip);
566            }
567  
568            if (under!=null) {
569              if (sym instanceof Modable) {
570                int modsave = view.mod;
571                view.mod = Stacking.UNDER;
572                view.intersectClip(under);
573                sym.paint(view);
574                view.mod = modsave;
575              }
576              natclip.subtract(under);
577              view.setClip(natclip);
578            }
579          }
580        }
581        if (view.mod == 0 || sym instanceof Modable) sym.paint(view);
582        
583        view.setClip(clip);
584      }
585  
586      if (inview!=null) inview.draw.dispose();
587      if (outview!=null) outview.draw.dispose();
588  
589      baseview.composite = oldcomp;
590      oldcomp=null;    
591    }
592  
593    public boolean stripSurveys() {
594      survey = false;
595  
596      for (Iterator it =members.iterator();it.hasNext();) {
597        Symbol cur = (Symbol)it.next();
598        if (cur instanceof Survey) it.remove();
599        else if (!cur.stripSurveys()) it.remove();
600      }
601  
602      return(members.size()>0);
603    }
604  
605    private void readObject(java.io.ObjectInputStream stream)
606        throws java.io.IOException,java.lang.ClassNotFoundException {
607      stream.defaultReadObject();
608      if (visible == null) {
609        visible = Layer.all;
610        showall = true;
611      }
612      if (background == null) background = Color.WHITE;
613  //    if (passpriority==null)
614  //      passpriority = new HashMap();
615    }
616  //  public boolean isDirty() {return(dirty);}
617  }
618  
619