Added
Link Here
|
1 |
/* |
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
3 |
* |
4 |
* Copyright 2010 Sun Microsystems, Inc. All rights reserved. |
5 |
* |
6 |
* The contents of this file are subject to the terms of either the GNU |
7 |
* General Public License Version 2 only ("GPL") or the Common |
8 |
* Development and Distribution License("CDDL") (collectively, the |
9 |
* "License"). You may not use this file except in compliance with the |
10 |
* License. You can obtain a copy of the License at |
11 |
* http://www.netbeans.org/cddl-gplv2.html |
12 |
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the |
13 |
* specific language governing permissions and limitations under the |
14 |
* License. When distributing the software, include this License Header |
15 |
* Notice in each file and include the License file at |
16 |
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this |
17 |
* particular file as subject to the "Classpath" exception as provided |
18 |
* by Sun in the GPL Version 2 section of the License file that |
19 |
* accompanied this code. If applicable, add the following below the |
20 |
* License Header, with the fields enclosed by brackets [] replaced by |
21 |
* your own identifying information: |
22 |
* "Portions Copyrighted [year] [name of copyright owner]" |
23 |
* |
24 |
* If you wish your version of this file to be governed by only the CDDL |
25 |
* or only the GPL Version 2, indicate your decision by adding |
26 |
* "[Contributor] elects to include this software in this distribution |
27 |
* under the [CDDL or GPL Version 2] license." If you do not indicate a |
28 |
* single choice of license, a recipient has the option to distribute |
29 |
* your version of this file under either the CDDL, the GPL Version 2 or |
30 |
* to extend the choice of license to its licensees as provided above. |
31 |
* However, if you add GPL Version 2 code and therefore, elected the GPL |
32 |
* Version 2 license, then the option applies only if the new code is |
33 |
* made subject to such option by the copyright holder. |
34 |
* |
35 |
* Contributor(s): |
36 |
* |
37 |
* Portions Copyrighted 2010 Sun Microsystems, Inc. |
38 |
*/ |
39 |
package org.netbeans.core.menu; |
40 |
|
41 |
import java.awt.Component; |
42 |
import java.awt.EventQueue; |
43 |
import java.io.IOException; |
44 |
import java.util.ArrayList; |
45 |
import java.util.Arrays; |
46 |
import java.util.HashMap; |
47 |
import java.util.HashSet; |
48 |
import java.util.Iterator; |
49 |
import java.util.LinkedList; |
50 |
import java.util.List; |
51 |
import java.util.Map; |
52 |
import java.util.Set; |
53 |
import javax.swing.Action; |
54 |
import javax.swing.JComponent; |
55 |
import javax.swing.JSeparator; |
56 |
import org.openide.awt.DynamicMenuContent; |
57 |
import org.openide.cookies.InstanceCookie; |
58 |
import org.openide.loaders.DataFolder; |
59 |
import org.openide.loaders.DataObject; |
60 |
import org.openide.util.Exceptions; |
61 |
import org.openide.util.Parameters; |
62 |
import org.openide.util.actions.Presenter; |
63 |
|
64 |
/** |
65 |
* |
66 |
* @author Tim Boudreau |
67 |
*/ |
68 |
abstract class FolderModelEntry<CompType extends JComponent> extends ModelEntry<CompType, DataFolder> implements Callback { |
69 |
|
70 |
private final List<ModelEntry<?, ?>> children = new ArrayList<ModelEntry<?, ?>>(15); |
71 |
private final Object childLock = new Object(); |
72 |
private final Object mapLock = new Object(); |
73 |
private final Map<ModelEntry<?, ?>, ComponentList> comps4model = |
74 |
new HashMap<ModelEntry<?, ?>, ComponentList>(); |
75 |
|
76 |
FolderModelEntry(DataFolder fld, Class<CompType> compType) { |
77 |
super(fld, DataFolder.class, compType); |
78 |
} |
79 |
|
80 |
boolean setChildren(List<ModelEntry<?, ?>> l) { |
81 |
boolean result; |
82 |
synchronized (childLock) { |
83 |
result = !children.equals(l); |
84 |
if (result) { |
85 |
children.clear(); |
86 |
children.addAll(l); |
87 |
} |
88 |
} |
89 |
synchronized (mapLock) { |
90 |
if (!comps4model.isEmpty()) { |
91 |
Set<ModelEntry<?,?>> known = new HashSet<ModelEntry<?, ?>>(comps4model.keySet()); |
92 |
known.removeAll(l); |
93 |
for (ModelEntry<?,?> m : known) { |
94 |
comps4model.remove(m); |
95 |
} |
96 |
} |
97 |
} |
98 |
return result; |
99 |
} |
100 |
|
101 |
@Override |
102 |
public final void onHide(ModelEntry<?, ?> entry) { |
103 |
synchronized (mapLock) { |
104 |
for (Map.Entry<ModelEntry<?,?>, ComponentList> e : comps4model.entrySet()) { |
105 |
e.getValue().removeNotify(); |
106 |
} |
107 |
} |
108 |
} |
109 |
|
110 |
@Override |
111 |
public final void onShow(ModelEntry<?, ?> entry) { |
112 |
synchronized (mapLock) { |
113 |
for (Map.Entry<ModelEntry<?,?>, ComponentList> e : comps4model.entrySet()) { |
114 |
e.getValue().addNotify(); |
115 |
} |
116 |
} |
117 |
} |
118 |
|
119 |
private ComponentList components(ModelEntry<?,?> e) { |
120 |
Parameters.notNull("e", e); |
121 |
synchronized (mapLock) { |
122 |
return comps4model.get(e); |
123 |
} |
124 |
} |
125 |
|
126 |
private void setComponents (ModelEntry<?,?> e, ComponentList comps) { |
127 |
Parameters.notNull("comps", comps); |
128 |
Parameters.notNull("e", e); |
129 |
synchronized (mapLock) { |
130 |
if (comps == null) { |
131 |
comps4model.remove(e); |
132 |
} else { |
133 |
comps4model.put(e, comps); |
134 |
} |
135 |
} |
136 |
} |
137 |
|
138 |
List<ModelEntry<?, ?>> children() { |
139 |
synchronized (childLock) { |
140 |
return new ArrayList<ModelEntry<?, ?>>(children); |
141 |
} |
142 |
} |
143 |
|
144 |
protected void onInitBackground(List<? super ModelEntry<?, ?>> children) { |
145 |
//do nothing - for subclasses to force init |
146 |
} |
147 |
|
148 |
@Override |
149 |
protected boolean initBackground(List<? super ModelEntry<?, ?>> children) { |
150 |
// System.err.println("initBackground " + getName()); |
151 |
DataFolder fld = file(); |
152 |
boolean changed = true; |
153 |
Set<ModelEntry<?, ?>> created = new HashSet<ModelEntry<?, ?>>(); |
154 |
try { |
155 |
DataObject[] ch = fld.getChildren(); |
156 |
if (ch != null) { //can be null |
157 |
outer: |
158 |
for (DataObject dob : ch) { |
159 |
try { |
160 |
for (Types t : Types.values()) { |
161 |
if (t.accept(dob)) { |
162 |
ModelEntry<?, ?> e = t.createEntry(dob, this); |
163 |
created.add(e); |
164 |
children.add(e); |
165 |
//XXX defer or batch afterward? |
166 |
List<ModelEntry<?, ?>> kids = new ArrayList<ModelEntry<?, ?>>(); |
167 |
boolean hasKids = e.initBackground(kids); |
168 |
changed |= hasKids; |
169 |
if (hasKids) { |
170 |
if (e instanceof FolderModelEntry) { |
171 |
FolderModelEntry<?> fme = FolderModelEntry.class.cast(e); |
172 |
changed |= fme.setChildren(kids); |
173 |
} |
174 |
} |
175 |
continue outer; |
176 |
} |
177 |
} |
178 |
// System.err.println("No type accepted " + dob.getPrimaryFile().getPath() + " : " + ic(dob)); |
179 |
} catch (IOException ex) { |
180 |
Exceptions.printStackTrace(ex); |
181 |
} catch (ClassNotFoundException ex) { |
182 |
Exceptions.printStackTrace(ex); |
183 |
} |
184 |
} |
185 |
} |
186 |
} finally { |
187 |
onInitBackground(children); |
188 |
} |
189 |
return changed; |
190 |
} |
191 |
|
192 |
// private static String ic(DataObject dob) throws ClassNotFoundException, IOException { |
193 |
// InstanceCookie ck = dob.getCookie(InstanceCookie.class); |
194 |
// return ck == null ? " [no ic]" : ck.instanceClass().getName(); |
195 |
// } |
196 |
@Override |
197 |
protected CompType refreshComponent(CompType old) { |
198 |
if (!EventQueue.isDispatchThread()) { |
199 |
throw new IllegalStateException("Not on event thread"); |
200 |
} |
201 |
// long start = System.currentTimeMillis(); |
202 |
Parameters.notNull("old", old); |
203 |
old.removeAll(); |
204 |
List<JComponent> all = new LinkedList<JComponent>(); |
205 |
List<ModelEntry<?, ?>> kids = children(); |
206 |
Boolean expectHasIcon = false; |
207 |
for (ModelEntry<?, ?> e : kids) { |
208 |
ComponentList c = components(e); |
209 |
JComponent[] comps; |
210 |
if (c == null) { |
211 |
comps = e.getComponents(); |
212 |
if (comps != null) { |
213 |
all.addAll(Arrays.asList(comps)); |
214 |
setComponents(e, c = new ComponentList(comps)); |
215 |
} |
216 |
} else { |
217 |
boolean allStillAlive = c.addNotify(); |
218 |
if (allStillAlive) { |
219 |
comps = e.refresh(c.getComponents()); |
220 |
ComponentList newComps; |
221 |
if (comps != null && !((newComps = new ComponentList(comps)).equals(c))) { |
222 |
setComponents(e, newComps); |
223 |
c = newComps; |
224 |
} |
225 |
} else { |
226 |
comps = e.findComponents(); |
227 |
boolean fixed = c.repair(comps); |
228 |
if (!fixed) { |
229 |
setComponents(e, c = new ComponentList(comps)); |
230 |
} |
231 |
} |
232 |
} |
233 |
if (comps != null) { |
234 |
for (JComponent comp : comps) { |
235 |
expectHasIcon = ModelEntry.defaultHasIcon(comp, expectHasIcon); |
236 |
} |
237 |
} |
238 |
if (c != null) { |
239 |
all.addAll(Arrays.asList(c.getComponents())); |
240 |
} |
241 |
} |
242 |
pruneSeparatorsAndFixIcons(all, expectHasIcon); |
243 |
installComponents(all, old); |
244 |
// long dur = System.currentTimeMillis() - start; |
245 |
// System.err.println("Refresh " + getName() + " took " + dur + " milliseconds"); |
246 |
return old; |
247 |
} |
248 |
|
249 |
protected void installComponents(List<? extends JComponent> components, CompType into) { |
250 |
for (Component c : components) { |
251 |
if (c == null) { |
252 |
c = new JSeparator(); |
253 |
} |
254 |
into.add(c); |
255 |
} |
256 |
} |
257 |
|
258 |
private void pruneSeparatorsAndFixIcons(List<JComponent> all, Boolean icons) { |
259 |
boolean lastWasSep = true; |
260 |
for (Iterator<JComponent> i = all.iterator(); i.hasNext();) { |
261 |
JComponent c = i.next(); |
262 |
boolean isSep = c instanceof JSeparator; |
263 |
if ((lastWasSep && isSep) || (!i.hasNext() && isSep)) { |
264 |
i.remove(); |
265 |
} |
266 |
lastWasSep = isSep; |
267 |
if (icons != null) { |
268 |
ModelEntry.configureIcon(c, icons); |
269 |
} |
270 |
} |
271 |
} |
272 |
|
273 |
private static enum Types { |
274 |
|
275 |
SEPARATOR(JSeparator.class), |
276 |
DYNAMIC_MENU_CONTENT(DynamicMenuContent.class), |
277 |
PRESENTER(Presenter.Menu.class), |
278 |
MENU, |
279 |
COMPONENT(Component.class), |
280 |
ACTION(Action.class); |
281 |
private final Class<?> type; |
282 |
|
283 |
Types(Class<?> type) { |
284 |
this.type = type; |
285 |
} |
286 |
|
287 |
Types() { |
288 |
this(Object.class); |
289 |
} |
290 |
|
291 |
boolean accept(DataObject dob) throws IOException, ClassNotFoundException { |
292 |
if (this == MENU) { |
293 |
DataFolder fld = dob instanceof DataFolder ? (DataFolder) dob |
294 |
: dob.getLookup().lookup(DataFolder.class); |
295 |
return fld != null; |
296 |
} |
297 |
InstanceCookie ck = dob.getLookup().lookup(InstanceCookie.class); |
298 |
return ck instanceof InstanceCookie.Of ? ((InstanceCookie.Of) ck).instanceOf(type) |
299 |
: ck == null ? false : type.isAssignableFrom(ck.instanceClass()); |
300 |
} |
301 |
|
302 |
ModelEntry<?, ?> createEntry(DataObject ob, Callback callback) { |
303 |
// System.err.println("Create " + name() + " entry for " + ob.getPrimaryFile().getPath()); |
304 |
switch (this) { |
305 |
case SEPARATOR: |
306 |
return new SeparatorModelEntry(ob); |
307 |
case ACTION: |
308 |
InstanceCookie ic = ob.getLookup().lookup(InstanceCookie.class); |
309 |
try { |
310 |
//This is a bit of a mess - some InstanceCookies lie |
311 |
if (ic.instanceCreate() instanceof Presenter.Menu) { |
312 |
return new PresenterEntry(ob); |
313 |
} |
314 |
} catch (IOException ex) { |
315 |
Exceptions.printStackTrace(ex); |
316 |
} catch (ClassNotFoundException ex) { |
317 |
Exceptions.printStackTrace(ex); |
318 |
} |
319 |
return new ActionModelEntry(ob); |
320 |
case DYNAMIC_MENU_CONTENT: |
321 |
return new DynamicModelEntry(ob); |
322 |
case PRESENTER: |
323 |
return new PresenterEntry(ob); |
324 |
case MENU: |
325 |
DataFolder fld = ob instanceof DataFolder ? (DataFolder) ob |
326 |
: ob.getLookup().lookup(DataFolder.class); |
327 |
Parameters.notNull("folder was non-null but is now null", fld); |
328 |
return new MenuEntry(fld, null); |
329 |
case COMPONENT: |
330 |
return new ComponentModelEntry(ob); |
331 |
default: |
332 |
throw new AssertionError(this + ""); |
333 |
} |
334 |
} |
335 |
} |
336 |
} |