JAL-3446 from applet -- reload; also fixes some repaint issues
[jalview.git] / src / jalview / gui / PaintRefresher.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 package jalview.gui;
22
23 import jalview.datamodel.AlignmentI;
24 import jalview.datamodel.SequenceI;
25
26 import java.awt.Component;
27 import java.util.ArrayList;
28 import java.util.HashMap;
29 import java.util.Iterator;
30 import java.util.List;
31 import java.util.Map;
32
33 /**
34  * Route datamodel/view update events for a sequence set to any display
35  * components involved TODO: JV3 refactor to abstract gui/view package
36  * 
37  * @author $author$
38  * @version $Revision$
39  */
40 public class PaintRefresher
41 {
42   private static final int ALIGNMENT_CHANGED = 1 << 0;
43   private static final int VALIDATE_SEQUENCES = 1 << 1;
44   
45   static Map<String, List<Component>> components = new HashMap<>();
46
47   /**
48    * Add the given component to those registered under the given sequence set
49    * id. Does nothing if already added.
50    * 
51    * @param comp
52    * @param al
53    */
54   public static void Register(Component comp, String seqSetId)
55   {
56     if (components.containsKey(seqSetId))
57     {
58       List<Component> comps = components.get(seqSetId);
59       if (!comps.contains(comp))
60       {
61         comps.add(comp);
62       }
63     }
64     else
65     {
66       List<Component> vcoms = new ArrayList<>();
67       vcoms.add(comp);
68       components.put(seqSetId, vcoms);
69     }
70   }
71
72   /**
73    * Remove this component from all registrations. Also removes a registered
74    * sequence set id if there are no remaining components registered against it.
75    * 
76    * @param comp
77    */
78   public static void RemoveComponent(Component comp)
79   {
80     if (components == null)
81     {
82       return;
83     }
84
85     Iterator<String> it = components.keySet().iterator();
86     while (it.hasNext())
87     {
88       List<Component> comps = components.get(it.next());
89       comps.remove(comp);
90       if (comps.isEmpty())
91       {
92         it.remove();
93       }
94     }
95   }
96
97   public static void Refresh(Component source, String id)
98   {
99     Refresh(source, id, false, false);
100   }
101
102   public static void Refresh(Component source, String id,
103           boolean alignmentChanged, boolean validateSequences)
104   {
105     List<Component> comps = components.get(id);
106
107     int mode = (alignmentChanged ? ALIGNMENT_CHANGED : 0) | (validateSequences ? VALIDATE_SEQUENCES : 0);
108     if (comps == null)
109     {
110       return;
111     }
112     repaintComponents(source, mode, comps.toArray(new Component[comps.size()]));
113   }
114
115   public static void repaintComponents(Component source, int mode,
116           Component... comps)
117   {
118     for (int i = 0; i < comps.length; i++)
119     {
120       Component comp = comps[i];
121       if (comp == null)
122       {
123         continue;
124       }
125       if (comp instanceof AlignmentPanel)
126       {
127         if ((mode & VALIDATE_SEQUENCES) != 0 && source instanceof AlignmentPanel)
128         {
129           validateSequences(((AlignmentPanel) source).av.getAlignment(),
130                   ((AlignmentPanel) comp).av.getAlignment());
131         }
132         if ((mode & ALIGNMENT_CHANGED) != 0)
133         {
134           ((AlignmentPanel) comp).alignmentChanged();
135         }
136       }
137       else if (comp instanceof IdCanvas)
138       {
139         // BH 2019.04.22 fixes JS problem of repaint() consolidation
140         // that occurs in JavaScript but not Java [JAL-3226]
141         ((IdCanvas) comp).setNoFastPaint();
142       }
143       else if (comp instanceof SeqCanvas)
144       {
145         // BH 2019.04.22 fixes JS problem of repaint() consolidation
146         // that occurs in JavaScript but not Java [JAL-3226]
147         ((SeqCanvas) comp).setNoFastPaint();
148       }
149       comp.repaint();
150     }
151   }
152
153   static void validateSequences(AlignmentI source, AlignmentI comp)
154   {
155     SequenceI[] a1;
156     if (source.getHiddenSequences().getSize() > 0)
157     {
158       a1 = source.getHiddenSequences().getFullAlignment()
159               .getSequencesArray();
160     }
161     else
162     {
163       a1 = source.getSequencesArray();
164     }
165
166     SequenceI[] a2;
167     if (comp.getHiddenSequences().getSize() > 0)
168     {
169       a2 = comp.getHiddenSequences().getFullAlignment().getSequencesArray();
170     }
171     else
172     {
173       a2 = comp.getSequencesArray();
174     }
175
176     int i, iSize = a1.length, j, jSize = a2.length;
177
178     if (iSize == jSize)
179     {
180       return;
181     }
182
183     boolean exists = false;
184     for (i = 0; i < iSize; i++)
185     {
186       exists = false;
187
188       for (j = 0; j < jSize; j++)
189       {
190         if (a2[j] == a1[i])
191         {
192           exists = true;
193           break;
194         }
195       }
196
197       if (!exists)
198       {
199         if (i < comp.getHeight())
200         {
201           // TODO: the following does not trigger any recalculation of
202           // height/etc, or maintain the dataset
203           if (comp.getDataset() != source.getDataset())
204           {
205             // raise an implementation warning here - not sure if this situation
206             // will ever occur
207             System.err.println(
208                     "IMPLEMENTATION PROBLEM: DATASET out of sync due to an insert whilst calling PaintRefresher.validateSequences(AlignmentI, ALignmentI)");
209           }
210           List<SequenceI> alsq = comp.getSequences();
211           synchronized (alsq)
212           {
213             alsq.add(i, a1[i]);
214           }
215         }
216         else
217         {
218           comp.addSequence(a1[i]);
219         }
220
221         if (comp.getHiddenSequences().getSize() > 0)
222         {
223           a2 = comp.getHiddenSequences().getFullAlignment()
224                   .getSequencesArray();
225         }
226         else
227         {
228           a2 = comp.getSequencesArray();
229         }
230
231         jSize = a2.length;
232       }
233     }
234
235     iSize = a1.length;
236     jSize = a2.length;
237
238     for (j = 0; j < jSize; j++)
239     {
240       exists = false;
241       for (i = 0; i < iSize; i++)
242       {
243         if (a2[j] == a1[i])
244         {
245           exists = true;
246           break;
247         }
248       }
249
250       if (!exists)
251       {
252         comp.deleteSequence(a2[j]);
253       }
254     }
255   }
256
257   static AlignmentPanel[] getAssociatedPanels(String id)
258   {
259     List<Component> comps = components.get(id);
260     if (comps == null)
261     {
262       return new AlignmentPanel[0];
263     }
264     List<AlignmentPanel> tmp = new ArrayList<>();
265     for (Component comp : comps)
266     {
267       if (comp instanceof AlignmentPanel)
268       {
269         tmp.add((AlignmentPanel) comp);
270       }
271     }
272     return tmp.toArray(new AlignmentPanel[tmp.size()]);
273   }
274
275   
276 }