001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.swing.tabcontrol;
043:
044: import java.util.ArrayList;
045: import java.util.Arrays;
046: import java.util.Collections;
047: import java.util.HashMap;
048: import java.util.HashSet;
049: import java.util.List;
050: import java.util.Map;
051: import java.util.Set;
052: import javax.swing.Icon;
053: import javax.swing.event.ChangeListener;
054: import javax.swing.event.ListDataEvent;
055: import org.netbeans.swing.tabcontrol.event.ComplexListDataEvent;
056: import org.netbeans.swing.tabcontrol.event.ComplexListDataListener;
057: import org.netbeans.swing.tabcontrol.event.VeryComplexListDataEvent;
058: import org.openide.util.ChangeSupport;
059:
060: /**
061: * Default implementation of TabDataModel.
062: *
063: * @author Tim Boudreau
064: */
065: public class DefaultTabDataModel implements TabDataModel {
066: /**
067: * Utility field holding list of ChangeListeners.
068: */
069: private transient ArrayList<ComplexListDataListener> listenerList;
070:
071: private class L extends ArrayList<TabData> {
072: public void removeRange(int fromIndex, int toIndex) {
073: super .removeRange(fromIndex, toIndex);
074: }
075: }
076:
077: private L list = new L();
078:
079: private final ChangeSupport cs = new ChangeSupport(this );
080:
081: /**
082: * Creates a new instance of DefaultTabDataModel
083: */
084: public DefaultTabDataModel() {
085: }
086:
087: /**
088: * Testing constructor
089: */
090: public DefaultTabDataModel(TabData[] data) {
091: list.addAll(Arrays.asList(data));
092: }
093:
094: public java.util.List<TabData> getTabs() {
095: return Collections.unmodifiableList(list);
096: }
097:
098: public TabData getTab(int index) {
099: return (TabData) list.get(index);
100: }
101:
102: public void setTabs(TabData[] data) {
103:
104: TabData[] oldContents = new TabData[list.size()];
105: oldContents = (TabData[]) list.toArray(oldContents);
106:
107: //No change, Peter says this will be the typical case
108: if (Arrays.equals(data, oldContents)) {
109: return;
110: }
111:
112: List newContents = Arrays.asList(data);
113:
114: list.clear();
115: list.addAll(Arrays.asList(data));
116:
117: VeryComplexListDataEvent vclde = new VeryComplexListDataEvent(
118: this , oldContents, data);
119: fireIndicesChanged(vclde);
120: }
121:
122: public void setIcon(int index, Icon i) {
123: boolean[] widthChanged = new boolean[] { false };
124:
125: boolean fireChange = _setIcon(index, i, widthChanged);
126: if (fireChange) {
127: ComplexListDataEvent clde = new ComplexListDataEvent(this ,
128: ComplexListDataEvent.CONTENTS_CHANGED, index,
129: index, widthChanged[0]);
130: fireContentsChanged(clde);
131: }
132: }
133:
134: private boolean _setIcon(int index, Icon i,
135: final boolean[] widthChanged) {
136: if (i == null) {
137: i = TabData.NO_ICON;
138: }
139: TabData data = getTab(index);
140: if (i != data.getIcon()) {
141: widthChanged[0] = data.getIcon().getIconWidth() != i
142: .getIconWidth();
143: data.icon = i;
144: return true;
145: } else {
146: return false;
147: }
148: }
149:
150: public void setText(int index, String txt) {
151: boolean[] widthChanged = new boolean[] { false };
152: boolean fireChange = _setText(index, txt, widthChanged);
153: if (fireChange) {
154: ComplexListDataEvent clde = new ComplexListDataEvent(this ,
155: ComplexListDataEvent.CONTENTS_CHANGED, index,
156: index, widthChanged[0]);
157: fireContentsChanged(clde);
158: }
159: }
160:
161: private int[] _setText(int[] indices, String[] txt,
162: final boolean[] widthChanged) {
163: widthChanged[0] = false;
164: boolean fireChange = false;
165: boolean[] changed = new boolean[indices.length];
166: int changedCount = 0;
167: Arrays.fill(changed, false);
168: for (int i = 0; i < indices.length; i++) {
169: boolean[] currWidthChanged = new boolean[] { false };
170: fireChange |= _setText(indices[i], txt[i], currWidthChanged);
171: widthChanged[0] |= currWidthChanged[0];
172: if (currWidthChanged[0])
173: changedCount++;
174: changed[i] = currWidthChanged[0];
175: }
176: int[] toFire;
177: if (widthChanged[0] || fireChange) {
178: if (changedCount == indices.length) {
179: toFire = indices;
180: } else {
181: toFire = new int[changedCount];
182: int idx = 0;
183: for (int i = 0; i < indices.length; i++) {
184: if (changed[i]) {
185: toFire[idx] = indices[i];
186: idx++;
187: }
188: }
189: }
190: return toFire;
191: }
192: return null;
193: }
194:
195: private int[] _setIcon(int[] indices, Icon[] icons,
196: final boolean[] widthChanged) {
197: widthChanged[0] = false;
198: boolean fireChange = false;
199: boolean[] changed = new boolean[indices.length];
200: int changedCount = 0;
201: Arrays.fill(changed, false);
202: boolean[] currWidthChanged = new boolean[] { false };
203: boolean currChanged = false;
204: for (int i = 0; i < indices.length; i++) {
205: currChanged = _setIcon(indices[i], icons[i],
206: currWidthChanged);
207: fireChange |= currChanged;
208: widthChanged[0] |= currWidthChanged[0];
209: if (currChanged)
210: changedCount++;
211: changed[i] = currChanged;
212: }
213: int[] toFire;
214: if (widthChanged[0] || fireChange) {
215: if (changedCount == indices.length) {
216: toFire = indices;
217: } else {
218: toFire = new int[changedCount];
219: int idx = 0;
220: for (int i = 0; i < indices.length; i++) {
221: if (changed[i]) {
222: toFire[idx] = indices[i];
223: idx++;
224: }
225: }
226: }
227: return toFire;
228: }
229: return null;
230: }
231:
232: public void setIconsAndText(int[] indices, String[] txt,
233: Icon[] icons) {
234: boolean[] iconWidthsChanged = new boolean[] { false };
235: boolean[] txtWidthsChanged = new boolean[] { false };
236: int[] iconsToFire = _setIcon(indices, icons, iconWidthsChanged);
237: int[] txtToFire = _setText(indices, txt, txtWidthsChanged);
238: boolean widthChanged = iconWidthsChanged[0]
239: || txtWidthsChanged[0];
240: boolean fire = widthChanged || iconsToFire != null
241: || txtToFire != null;
242: if (fire) {
243: if ((indices == iconsToFire) && (indices == txtToFire)) {
244: //if all icons/txt changed, optimize and don't calculate a merge
245: ComplexListDataEvent clde = new ComplexListDataEvent(
246: this , ComplexListDataEvent.CONTENTS_CHANGED,
247: indices, widthChanged);
248: fireContentsChanged(clde);
249: } else {
250: //okay, there are differences in what was set to what. Build a
251: //merge of the change data and fire that
252: int size = (iconsToFire != null ? iconsToFire.length
253: : 0)
254: + (txtToFire != null ? txtToFire.length : 0);
255: Set<Integer> allIndicesToFire = new HashSet<Integer>(
256: size);
257: Integer[] o;
258: if (iconsToFire != null) {
259: o = toObjectArray(iconsToFire);
260: allIndicesToFire.addAll(Arrays.asList(o));
261: }
262: if (txtToFire != null) {
263: o = toObjectArray(txtToFire);
264: allIndicesToFire.addAll(Arrays.asList(o));
265: }
266: Integer[] all = new Integer[allIndicesToFire.size()];
267: all = (Integer[]) allIndicesToFire.toArray(all);
268: int[] allPrimitive = toPrimitiveArray(all);
269: ComplexListDataEvent clde = new ComplexListDataEvent(
270: this , ComplexListDataEvent.CONTENTS_CHANGED,
271: allPrimitive, widthChanged);
272: fireContentsChanged(clde);
273: }
274: }
275: }
276:
277: public void setIcon(int[] indices, Icon[] icons) {
278: boolean[] widthChanged = new boolean[] { false };
279: int[] toFire = _setIcon(indices, icons, widthChanged);
280: if (toFire != null) {
281: ComplexListDataEvent clde = new ComplexListDataEvent(this ,
282: ComplexListDataEvent.CONTENTS_CHANGED, toFire,
283: widthChanged[0]);
284: fireContentsChanged(clde);
285: }
286: }
287:
288: public void setText(int[] indices, String[] txt) {
289: boolean[] widthChanged = new boolean[] { false };
290: int[] toFire = _setText(indices, txt, widthChanged);
291: if (toFire != null) {
292: ComplexListDataEvent clde = new ComplexListDataEvent(this ,
293: ComplexListDataEvent.CONTENTS_CHANGED, toFire,
294: widthChanged[0]);
295: fireContentsChanged(clde);
296: }
297: }
298:
299: private boolean _setText(int index, String txt,
300: final boolean[] widthChanged) {
301: TabData data = getTab(index);
302: if (txt != data.txt) {
303: widthChanged[0] = data.getText() != txt;
304: data.txt = txt;
305: return true;
306: } else {
307: return false;
308: }
309: }
310:
311: public void setTab(int index, TabData data) {
312: if (!data.equals(getTab(index))) {
313: TabData olddata = getTab(index);
314: boolean txtChg = data.getText().equals(olddata.getText());
315: boolean compChg = data.getUserObject() != olddata
316: .getUserObject();
317: list.set(index, data);
318: ComplexListDataEvent lde = new ComplexListDataEvent(this ,
319: ListDataEvent.CONTENTS_CHANGED, index, index,
320: txtChg, compChg);
321: lde.setAffectedItems(new TabData[] { data });
322: fireContentsChanged(lde);
323: }
324: }
325:
326: public void addTab(int index, TabData data) {
327: list.add(index, data);
328: ComplexListDataEvent lde = new ComplexListDataEvent(this ,
329: ComplexListDataEvent.INTERVAL_ADDED, index, index, true);
330: lde.setAffectedItems(new TabData[] { data });
331: fireIntervalAdded(lde);
332: }
333:
334: public void addTabs(int start, TabData[] data) {
335: list.addAll(start, Arrays.asList(data));
336: ComplexListDataEvent lde = new ComplexListDataEvent(this ,
337: ListDataEvent.INTERVAL_ADDED, start, start
338: + data.length - 1, true);
339: lde.setAffectedItems(data);
340: fireIntervalAdded(lde);
341: }
342:
343: public void removeTab(int index) {
344: TabData[] td = new TabData[] { (TabData) list.get(index) };
345: list.remove(index);
346: ComplexListDataEvent lde = new ComplexListDataEvent(this ,
347: ListDataEvent.INTERVAL_REMOVED, index, index);
348: lde.setAffectedItems(td);
349: fireIntervalRemoved(lde);
350: }
351:
352: /**
353: * Remove a range of tabs from <code>start</code> up to <i>and including</i>
354: * <code>finish</code>.
355: */
356: public void removeTabs(int start, int end) {
357: java.util.List affected = list.subList(start, end);
358: if (start == end) {
359: list.remove(start);
360: } else {
361: list.removeRange(start, end + 1);
362: }
363: ComplexListDataEvent lde = new ComplexListDataEvent(this ,
364: ListDataEvent.INTERVAL_REMOVED, start, end);
365: lde.setAffectedItems((TabData[]) list.toArray(new TabData[0]));
366: fireIntervalRemoved(lde);
367: }
368:
369: public void addTabs(int[] indices, TabData[] data) {
370: Map<Integer, TabData> m = new HashMap<Integer, TabData>(
371: data.length);
372: for (int i = 0; i < data.length; i++) {
373: m.put(new Integer(indices[i]), data[i]);
374: }
375: Arrays.sort(indices);
376: for (int i = 0; i < indices.length; i++) {
377: Integer key = new Integer(indices[i]);
378: TabData currData = m.get(key);
379: list.add(indices[i], currData);
380: }
381: ComplexListDataEvent clde = new ComplexListDataEvent(this ,
382: ComplexListDataEvent.ITEMS_ADDED, indices, true);
383: clde.setAffectedItems(data);
384: fireIndicesAdded(clde);
385: }
386:
387: public void removeTabs(int[] indices) {
388: Arrays.sort(indices);
389: TabData[] affected = new TabData[indices.length];
390: for (int i = indices.length - 1; i >= 0; i--) {
391: affected[i] = (TabData) list.remove(indices[i]);
392: }
393: ComplexListDataEvent clde = new ComplexListDataEvent(this ,
394: ComplexListDataEvent.ITEMS_REMOVED, indices, true);
395: clde.setAffectedItems(affected);
396: fireIndicesRemoved(clde);
397: }
398:
399: public int size() {
400: return list.size();
401: }
402:
403: public synchronized void addComplexListDataListener(
404: ComplexListDataListener listener) {
405: if (listenerList == null) {
406: listenerList = new ArrayList<ComplexListDataListener>();
407: }
408: listenerList.add(listener);
409: }
410:
411: public synchronized void removeComplexListDataListener(
412: ComplexListDataListener listener) {
413: listenerList.remove(listener);
414: }
415:
416: private void fireIntervalAdded(ListDataEvent event) {
417: if (listenerList == null) {
418: return;
419: }
420: int max = listenerList.size();
421: for (int i = 0; i < max; i++) {
422: ComplexListDataListener l = (ComplexListDataListener) listenerList
423: .get(i);
424: l.intervalAdded(event);
425: }
426: cs.fireChange();
427: }
428:
429: private void fireIntervalRemoved(ListDataEvent event) {
430: if (listenerList == null)
431: return;
432: int max = listenerList.size();
433: for (int i = 0; i < max; i++) {
434: ComplexListDataListener l = (ComplexListDataListener) listenerList
435: .get(i);
436: l.intervalRemoved(event);
437: }
438: cs.fireChange();
439: }
440:
441: private void fireContentsChanged(ListDataEvent event) {
442: if (listenerList == null)
443: return;
444: int max = listenerList.size();
445: for (int i = 0; i < max; i++) {
446: ComplexListDataListener l = (ComplexListDataListener) listenerList
447: .get(i);
448: l.contentsChanged(event);
449: }
450: cs.fireChange();
451: }
452:
453: private void fireIndicesAdded(ComplexListDataEvent event) {
454: if (listenerList == null)
455: return;
456: int max = listenerList.size();
457: for (int i = 0; i < max; i++) {
458: ComplexListDataListener l = (ComplexListDataListener) listenerList
459: .get(i);
460: l.indicesAdded(event);
461: }
462: cs.fireChange();
463: }
464:
465: private void fireIndicesRemoved(ComplexListDataEvent event) {
466: if (listenerList == null)
467: return;
468: int max = listenerList.size();
469: for (int i = 0; i < max; i++) {
470: ComplexListDataListener l = (ComplexListDataListener) listenerList
471: .get(i);
472: l.indicesRemoved(event);
473: }
474: cs.fireChange();
475: }
476:
477: private void fireIndicesChanged(ComplexListDataEvent event) {
478: if (listenerList == null)
479: return;
480: int max = listenerList.size();
481: for (int i = 0; i < max; i++) {
482: ComplexListDataListener l = (ComplexListDataListener) listenerList
483: .get(i);
484: l.indicesChanged(event);
485: }
486: cs.fireChange();
487: }
488:
489: public String toString() {
490: StringBuffer out = new StringBuffer(getClass().getName());
491: out.append(" size =");
492: int max = size();
493: out.append(max);
494: out.append(" - ");
495: for (int i = 0; i < max; i++) {
496: TabData td = getTab(i);
497: out.append(td.toString());
498: if (i != max - 1) {
499: out.append(',');
500: }
501: }
502: return out.toString();
503: }
504:
505: //===========================
506: //XXX remove ChangeListener support and handle the ComplexNNN events so nothing is repainted
507: //if not displayed on screen!
508:
509: /**
510: * Registers ChangeListener to receive events.
511: *
512: * @param listener The listener to register.
513: */
514: public void addChangeListener(ChangeListener listener) {
515: cs.addChangeListener(listener);
516: }
517:
518: /**
519: * Removes ChangeListener from the list of listeners.
520: *
521: * @param listener The listener to remove.
522: */
523: public void removeChangeListener(ChangeListener listener) {
524: cs.removeChangeListener(listener);
525: }
526:
527: public int indexOf(TabData td) {
528: return list.indexOf(td);
529: }
530:
531: private Integer[] toObjectArray(int[] o) {
532: Integer[] result = new Integer[o.length];
533: for (int i = 0; i < o.length; i++) {
534: result[i] = new Integer(o[i]);
535: }
536: return result;
537: }
538:
539: private int[] toPrimitiveArray(Integer[] o) {
540: int[] result = new int[o.length];
541: for (int i = 0; i < o.length; i++) {
542: result[i] = o[i].intValue();
543: }
544: return result;
545: }
546:
547: }
|