001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2008 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-2008 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.modules.autoupdate.ui;
043:
044: import java.util.ArrayList;
045: import java.util.Collection;
046: import java.util.Collections;
047: import java.util.Comparator;
048: import java.util.HashMap;
049: import java.util.List;
050: import java.util.Map;
051: import javax.swing.SwingUtilities;
052: import javax.swing.table.AbstractTableModel;
053: import javax.swing.table.JTableHeader;
054: import org.netbeans.api.autoupdate.UpdateManager;
055: import org.netbeans.api.autoupdate.UpdateUnit;
056: import org.openide.util.NbBundle;
057: import org.openide.util.NbPreferences;
058: import org.openide.util.RequestProcessor;
059:
060: /**
061: *
062: * @author Jiri Rechtacek, Radek Matous
063: */
064: public abstract class UnitCategoryTableModel extends AbstractTableModel {
065: private static final String EXPAND_STATE = "ExpandState";//NOI18N
066: private List<Unit> unitData = Collections.emptyList();
067: private List<Unit> featuretData = Collections.emptyList();
068: private List<Unit> standAloneModules = Collections.emptyList();
069:
070: private List<UpdateUnitListener> listeners = new ArrayList<UpdateUnitListener>();
071: private String filter = "";//NOI18N
072: private Comparator<Unit> unitCmp;
073:
074: //null == NOT_EXPANDABLE, true == EXPANDED, false == COLLAPSED
075: private Boolean isExpanded = null;
076:
077: public static enum Type {
078: INSTALLED, UPDATE, AVAILABLE, LOCAL
079: }
080:
081: /** Creates a new instance of CategoryTableModel */
082: public UnitCategoryTableModel() {
083: }
084:
085: List<Unit> getUnits() {
086: return unitData;
087: }
088:
089: static Map<String, Boolean> captureState(List<Unit> units) {
090: Map<String, Boolean> retval = new HashMap<String, Boolean>();
091: for (Unit unit : units) {
092: retval.put(unit.updateUnit.getCodeName(), unit.isMarked());
093: }
094: return retval;
095: }
096:
097: static void restoreState(List<Unit> newUnits,
098: Map<String, Boolean> capturedState,
099: boolean isMarkedAsDefault) {
100: for (Unit unit : newUnits) {
101: Boolean isChecked = capturedState.get(unit.updateUnit
102: .getCodeName());
103: if (isChecked != null) {
104: if (isChecked.booleanValue() && !unit.isMarked()
105: && unit.canBeMarked()) {
106: unit.setMarked(true);
107: } else if (!isChecked.booleanValue() && unit.isMarked()
108: && unit.canBeMarked()) {
109: unit.setMarked(false);
110: }
111: } else if (isMarkedAsDefault && !unit.isMarked()
112: && unit.canBeMarked()) {
113: unit.setMarked(true);
114: }
115: }
116: }
117:
118: public static boolean isMarkedAsDefault(Type type) {
119: return (type.equals(Type.LOCAL) || type.equals(Type.UPDATE)) ? true
120: : false;
121: }
122:
123: protected final boolean isMarkedAsDefault() {
124: return isMarkedAsDefault(getType());
125: }
126:
127: public abstract Object getValueAt(int row, int col);
128:
129: @Override
130: public abstract Class getColumnClass(int c);
131:
132: public abstract Type getType();
133:
134: public abstract boolean isSortAllowed(Object columnIdentifier);
135:
136: public abstract int getDownloadSize();
137:
138: public abstract boolean needsRestart();
139:
140: protected abstract Comparator<Unit> getComparator(
141: final Object columnIdentifier, final boolean sortAscending);
142:
143: public abstract void setUnits(List<UpdateUnit> units);
144:
145: public String getTabTooltipText() {
146: return null;
147: }
148:
149: public abstract String getTabTitle();
150:
151: public final String getDecoratedTabTitle() {
152: int count = getItemCount();
153: int rawCount = getRawItemCount();
154: String countInfo = (count == rawCount) ? String
155: .valueOf(rawCount) : NbBundle.getMessage(
156: PluginManagerUI.class,
157: "PluginManagerUI_Tabs_CountFormat", count, rawCount);
158: String newName = NbBundle.getMessage(PluginManagerUI.class,
159: "PluginManagerUI_Tabs_NameFormat", getTabTitle(),
160: countInfo);
161: int index = getTabIndex();
162: return (rawCount == 0) ? getTabTitle() : newName;
163: }
164:
165: public boolean canBePrimaryTab() {
166: return true;
167: }
168:
169: public abstract int getTabIndex();
170:
171: public boolean isTabEnabled() {
172: return true;
173: }
174:
175: public String getToolTipText(int row, int col) {
176: String retval = null;
177: if (col == 0) {
178: retval = getTooltipForCheckBox(row);
179: } else if (col == 1) {
180: retval = (String) getValueAt(row, 1);
181: }
182: return retval;
183: }
184:
185: public int getMinWidth(JTableHeader header, int col) {
186: return header.getHeaderRect(col).width;
187: }
188:
189: public abstract int getPreferredWidth(JTableHeader header, int col);
190:
191: protected Comparator<Unit> getDefaultComparator() {
192: return new Comparator<Unit>() {
193: public int compare(Unit o1, Unit o2) {
194: return Unit.compareCategories(o1, o2);
195: }
196: };
197: }
198:
199: public final void sort(Object columnIdentifier,
200: boolean sortAscending) {
201: if (columnIdentifier == null) {
202: setUnitComparator(getDefaultComparator());
203: } else {
204: setUnitComparator(getComparator(columnIdentifier,
205: sortAscending));
206: }
207: fireTableDataChanged();
208: }
209:
210: private String getTooltipForCheckBox(int row) {
211: String key0 = null;
212: switch (getType()) {
213: case INSTALLED:
214: key0 = "UnitTab_TooltipCheckBox_INSTALLED"; //NOI18N
215: break;
216: case UPDATE:
217: key0 = "UnitTab_TooltipCheckBox_UPDATE"; //NOI18N
218: break;
219: case AVAILABLE:
220: key0 = "UnitTab_TooltipCheckBox_AVAILABLE"; //NOI18N
221: break;
222: case LOCAL:
223: key0 = "UnitTab_TooltipCheckBox_LOCAL"; //NOI18N
224: break;
225: }
226: return (key0 != null) ? NbBundle.getMessage(
227: UnitCategoryTableModel.class, key0,
228: (String) getValueAt(row, 1)) : null;
229: }
230:
231: private final void setData(List<UnitCategory> data,
232: Comparator<Unit> unitCmp) {
233: this .unitCmp = unitCmp != null ? unitCmp
234: : getDefaultComparator();
235: featuretData = null;
236: if (data != null) {
237: this .unitData = Collections.emptyList();
238: this .unitData = new ArrayList<Unit>();
239: for (UnitCategory unitCategory : data) {
240: this .unitData.addAll(unitCategory.getUnits());
241: }
242: standAloneModules = new ArrayList<Unit>();
243: for (Unit u : unitData) {
244: if (UpdateManager.TYPE.STANDALONE_MODULE
245: .equals(u.updateUnit.getType())) {
246: standAloneModules.add(u);
247: }
248: }
249: computeExtensionState();
250: } else {
251: assert unitData != null;
252: }
253: if (unitCmp != null) {
254: Collections.sort(unitData, unitCmp);
255: }
256: this .fireTableDataChanged();
257: }
258:
259: private void computeExtensionState() {
260: boolean exp = isExpandableType(getType())
261: && !Utilities.modulesOnly()
262: && !getVisibleUnits(getFeatureList(), getFilter(),
263: false).isEmpty()
264: && !getVisibleUnits(standAloneModules, getFilter(),
265: false).isEmpty();
266: if (exp) {
267: isExpanded = NbPreferences.forModule(
268: UnitCategoryTableModel.class).getBoolean(
269: EXPAND_STATE, false);
270: } else {
271: isExpanded = null;
272: }
273: }
274:
275: public void setUnitComparator(Comparator<Unit> comparator) {
276: setData(null, comparator);
277: }
278:
279: public final void setData(List<UnitCategory> data) {
280: setData(data, unitCmp);
281: }
282:
283: public void setFilter(final String filter,
284: final Runnable runAfterwards) {
285: SwingUtilities.invokeLater(new Runnable() {
286: public void run() {
287: synchronized (UnitCategoryTableModel.class) {
288: UnitCategoryTableModel.this .filter = filter
289: .toLowerCase();
290: }
291: computeExtensionState();
292: fireFilterChange();
293: if (runAfterwards != null) {
294: runAfterwards.run();
295: }
296: }
297: });
298: }
299:
300: public String getFilter() {
301: synchronized (UnitCategoryTableModel.class) {
302: return this .filter == null ? "" : this .filter;
303: }
304: }
305:
306: public void addUpdateUnitListener(UpdateUnitListener l) {
307: listeners.add(l);
308: }
309:
310: public void removeUpdateUnitListener(UpdateUnitListener l) {
311: listeners.remove(l);
312: }
313:
314: void fireUpdataUnitChange() {
315: assert listeners != null : "UpdateUnitListener found.";
316: for (UpdateUnitListener l : listeners) {
317: l.updateUnitsChanged();
318: }
319: }
320:
321: void fireButtonsChange() {
322: assert listeners != null : "UpdateUnitListener found.";
323: for (UpdateUnitListener l : listeners) {
324: l.buttonsChanged();
325: }
326: }
327:
328: void fireFilterChange() {
329: assert listeners != null : "UpdateUnitListener found.";
330: for (UpdateUnitListener l : listeners) {
331: l.filterChanged();
332: }
333: }
334:
335: List<Unit> getVisibleUnits() {
336: return getVisibleUnits(getUnits(), getFilter(), true);
337: }
338:
339: private List<Unit> getVisibleUnits(List<Unit> units, String filter,
340: boolean filterAlsoStandardModules) {
341: List<Unit> retval = new ArrayList<Unit>();
342: for (Unit unit : units) {
343: if (filterAlsoStandardModules) {
344: if (unit.isVisible(filter)
345: && (!isExpandable() || isExpanded() || UpdateManager.TYPE.FEATURE
346: .equals(unit.updateUnit.getType()))) {
347: retval.add(unit);
348: }
349: } else {
350: if (unit.isVisible(filter)) {
351: retval.add(unit);
352: }
353: }
354: }
355: return retval;
356: }
357:
358: public int getRowCount() {
359: int retval = getVisibleUnits().size();
360: return (isExpansionControlPresent()) ? (retval + 1) : retval;
361: }
362:
363: public int getRawItemCount() {
364: return unitData.size();
365: }
366:
367: public int getItemCount() {
368: return getVisibleUnits().size();
369: }
370:
371: public Collection<Unit> getMarkedUnits() {
372: List<Unit> markedUnits = new ArrayList<Unit>();
373: List<Unit> units = getUnits();
374:
375: for (Unit u : units) {
376: if (u.isMarked()) {
377: markedUnits.add(u);
378: }
379: }
380: return markedUnits;
381: }
382:
383: public Unit getUnitAtRow(int row) {
384: return getVisibleUnits().size() <= row ? null
385: : getVisibleUnits().get(row);
386: }
387:
388: public boolean isExpansionControlAtRow(int row) {
389: return ((row + 1) == getRowCount())
390: && isExpansionControlPresent();
391: }
392:
393: public String getExpansionControlText() {
394: assert isExpansionControlPresent();
395: String bundleKey = isExpanded() ? "Less_Command_Text"
396: : "More_Command_Text";//NOI18N
397: return NbBundle.getMessage(UnitCategoryTableModel.class,
398: bundleKey, getVisibleUnits(getStandAloneModules(),
399: getFilter(), false).size());
400: }
401:
402: public boolean isExpandable() {
403: return isExpanded != null;
404: }
405:
406: public boolean isExpansionControlPresent() {
407: return isExpandable()
408: && !getFeatureList().isEmpty()
409: && !getVisibleUnits(getStandAloneModules(),
410: getFilter(), false).isEmpty();
411: }
412:
413: public void setExpanded(Boolean expanded) {
414: this .isExpanded = expanded;
415: featuretData = null;
416: if (expanded != null) {
417: NbPreferences.forModule(UnitCategoryTableModel.class)
418: .putBoolean(EXPAND_STATE, expanded);
419: }
420: }
421:
422: public boolean isExpanded() {
423: boolean retval = isExpandable();
424: if (retval) {
425: retval = isExpanded != null && isExpanded;
426: }
427: return retval;
428: }
429:
430: public boolean isCollapsed() {
431: boolean retval = isExpandable();
432: if (retval) {
433: retval = isExpanded != null && !isExpanded;
434: }
435: return retval;
436: }
437:
438: private List<Unit> getFeatureList() {
439: if (featuretData == null) {
440: featuretData = new ArrayList<Unit>(unitData);
441: featuretData.removeAll(getStandAloneModules());
442: if (unitCmp != null) {
443: Collections.sort(featuretData, unitCmp);
444: }
445: }
446: return featuretData;
447: }
448:
449: List<Unit> getStandAloneModules() {
450: List<Unit> retval = standAloneModules;
451: if (retval == null) {
452: retval = Collections.emptyList();
453: }
454: return retval;
455: }
456:
457: public static boolean isExpandableType(Type type) {
458: return type.equals(UnitCategoryTableModel.Type.AVAILABLE)
459: || type.equals(UnitCategoryTableModel.Type.UPDATE);
460: }
461:
462: @Override
463: public boolean isCellEditable(int row, int col) {
464: if (isExpansionControlAtRow(row)) {
465: return false;
466: }
467: RequestProcessor.Task t = PluginManagerUI.getRunningTask();
468: return (t == null || t.isFinished()) && col == 0
469: && Boolean.class.equals(getColumnClass(col));
470: }
471:
472: }
|