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-2007 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.tasklist.ui;
043:
044: import java.util.ArrayList;
045: import java.util.Collections;
046: import java.util.Comparator;
047: import java.util.HashMap;
048: import java.util.LinkedList;
049: import java.util.List;
050: import java.util.Map;
051: import org.netbeans.modules.tasklist.impl.Accessor;
052: import org.netbeans.modules.tasklist.impl.TaskComparator;
053: import org.netbeans.modules.tasklist.impl.TaskList;
054: import org.netbeans.spi.tasklist.Task;
055: import org.netbeans.modules.tasklist.trampoline.TaskGroup;
056:
057: /**
058: *
059: * @author S. Aubrecht
060: */
061: class FoldingTaskListModel extends TaskListModel {
062:
063: private LinkedList<FoldingGroup> groups = new LinkedList<FoldingGroup>();
064: private HashMap<String, FoldingGroup> groupMap = new HashMap<String, FoldingGroup>(
065: 10);
066:
067: /** Creates a new instance of FoldingTaskListModel */
068: public FoldingTaskListModel(TaskList taskList) {
069: super (taskList);
070:
071: sortTaskList();
072:
073: tasksAdded(taskList.getTasks());
074: }
075:
076: @Override
077: public int getRowCount() {
078: if (null == list)
079: return 0;
080: int count = 0;
081: for (FoldingGroup g : groups) {
082: count += g.getRowCount();
083: }
084: return count;
085: }
086:
087: @Override
088: public Class<?> getColumnClass(int column) {
089: if (COL_GROUP == column)
090: return FoldingGroup.class;
091: return super .getColumnClass(column);
092: }
093:
094: @Override
095: protected Task getTaskAtRow(int row) {
096: if (isGroupRow(row))
097: return null;
098: int groupRow = 0;
099: for (FoldingGroup g : groups) {
100: if (row < groupRow + g.getRowCount())
101: return g.getTaskAt(row - groupRow - 1);
102: groupRow += g.getRowCount();
103: }
104: return null;
105: }
106:
107: @Override
108: public Object getValueAt(int row, int col) {
109: FoldingGroup group = getGroupAtRow(row);
110: if (null != group) {
111: switch (col) {
112: case COL_GROUP: {
113: return group;
114: }
115: default:
116: return null;
117: }
118: }
119: return super .getValueAt(row, col);
120: }
121:
122: FoldingGroup getGroupAtRow(int row) {
123: int groupRow = 0;
124: for (FoldingGroup g : groups) {
125: if (g.isEmpty())
126: continue;
127: if (row == groupRow)
128: return g;
129: groupRow += g.getRowCount();
130: }
131: return null;
132: }
133:
134: private Map<FoldingGroup, List<Task>> divideByGroup(
135: List<? extends Task> tasks) {
136: Map<FoldingGroup, List<Task>> grouppedTasksMap = new HashMap<FoldingGroup, List<Task>>(
137: groupMap.size());
138: for (Task t : tasks) {
139: TaskGroup tg = Accessor.getGroup(t);
140: FoldingGroup group = groupMap.get(tg.getName());
141: if (null == group) {
142: group = new FoldingGroup(tg);
143: groupMap.put(tg.getName(), group);
144: groups.add(group);
145: Collections.sort(groups);
146: }
147: List<Task> tasksInGroup = grouppedTasksMap.get(group);
148: if (null == tasksInGroup) {
149: tasksInGroup = new LinkedList<Task>();
150: grouppedTasksMap.put(group, tasksInGroup);
151: }
152: tasksInGroup.add(t);
153: }
154: return grouppedTasksMap;
155: }
156:
157: @Override
158: public void tasksAdded(List<? extends Task> tasks) {
159: if (tasks.isEmpty())
160: return;
161: Map<FoldingGroup, List<Task>> grouppedTasksMap = divideByGroup(tasks);
162: for (FoldingGroup fg : grouppedTasksMap.keySet()) {
163: List<Task> tasksInGroup = grouppedTasksMap.get(fg);
164: fg.add(tasksInGroup);
165: }
166: }
167:
168: @Override
169: public void tasksRemoved(List<? extends Task> tasks) {
170: if (tasks.isEmpty())
171: return;
172: Map<FoldingGroup, List<Task>> grouppedTasksMap = divideByGroup(tasks);
173: for (FoldingGroup fg : grouppedTasksMap.keySet()) {
174: List<Task> tasksInGroup = grouppedTasksMap.get(fg);
175: fg.remove(tasksInGroup);
176: }
177: }
178:
179: @Override
180: public void cleared() {
181: for (FoldingGroup fg : groups) {
182: fg.clear();
183: }
184: }
185:
186: public boolean isGroupRow(int row) {
187: return null != getGroupAtRow(row);
188: }
189:
190: public void toggleGroupExpanded(int row) {
191: FoldingGroup fg = getGroupAtRow(row);
192: if (null != fg)
193: fg.toggleExpanded();
194: }
195:
196: private int getFoldingGroupStartingRow(FoldingGroup fg) {
197: if (fg.isEmpty())
198: return -1;
199: int startingRow = 0;
200: int groupIndex = groups.indexOf(fg);
201: for (int i = 0; i < groupIndex; i++) {
202: startingRow += groups.get(i).getRowCount();
203: }
204: return startingRow;
205: }
206:
207: @Override
208: protected void sortTaskList() {
209: Comparator<Task> comparator = null;
210: switch (sortingCol) {
211: case COL_DESCRIPTION:
212: comparator = TaskComparator
213: .getDescriptionComparator(ascending);
214: break;
215: case COL_LINE:
216: comparator = TaskComparator.getLineComparator(ascending);
217: break;
218: case COL_LOCATION:
219: comparator = TaskComparator
220: .getLocationComparator(ascending);
221: break;
222: case COL_FILE:
223: comparator = TaskComparator.getFileComparator(ascending);
224: break;
225: default:
226: comparator = TaskComparator.getDefault();
227: break;
228: }
229: if (null != groups) {
230: for (FoldingGroup fg : groups) {
231: fg.setComparator(comparator);
232: }
233:
234: Settings.getDefault().setSortingColumn(sortingCol);
235: Settings.getDefault().setAscendingSort(ascending);
236: }
237:
238: fireTableDataChanged();
239: }
240:
241: class FoldingGroup implements
242: Comparable<FoldingTaskListModel.FoldingGroup> {
243: private TaskGroup tg;
244: private ArrayList<Task> tasks = new ArrayList<Task>(100);
245: private boolean isExpanded;
246: private Comparator<Task> comparator;
247:
248: public FoldingGroup(TaskGroup tg) {
249: this .tg = tg;
250: isExpanded = Settings.getDefault().isGroupExpanded(
251: tg.getName());
252: }
253:
254: public void add(List<Task> newTasks) {
255: boolean wasEmpty = isEmpty();
256: synchronized (tasks) {
257: tasks.addAll(newTasks);
258: Collections.sort(tasks, getComparator());
259: }
260:
261: int startingRow = getFoldingGroupStartingRow(this );
262:
263: if (wasEmpty) {
264: fireTableRowsInserted(startingRow, startingRow
265: + getRowCount());
266: } else {
267: if (isExpanded) {
268: int firstRow = Integer.MAX_VALUE;
269: int lastRow = Integer.MIN_VALUE;
270: for (Task t : newTasks) {
271: int index = tasks.indexOf(t);
272: if (index < firstRow)
273: firstRow = index;
274: if (index > lastRow)
275: lastRow = index;
276: }
277: fireTableRowsInserted(firstRow + startingRow + 1,
278: lastRow + startingRow + 1);
279: }
280: fireTableCellUpdated(startingRow, COL_DESCRIPTION);
281: }
282: }
283:
284: public void remove(List<Task> removedTasks) {
285: int firstRow = Integer.MAX_VALUE;
286: int lastRow = Integer.MIN_VALUE;
287: int rowCount = getRowCount();
288: if (isExpanded) {
289: for (Task t : removedTasks) {
290: int index = tasks.indexOf(t);
291: if (index < firstRow)
292: firstRow = index;
293: if (index > lastRow)
294: lastRow = index;
295: }
296: }
297: synchronized (tasks) {
298: tasks.removeAll(removedTasks);
299: }
300: int startingRow = getFoldingGroupStartingRow(this );
301: if (isEmpty()) {
302: fireTableRowsDeleted(startingRow, startingRow
303: + rowCount);
304: } else {
305: if (isExpanded) {
306: fireTableRowsDeleted(firstRow + startingRow + 1,
307: lastRow + startingRow + 1);
308: }
309: fireTableCellUpdated(startingRow, COL_DESCRIPTION);
310: }
311: }
312:
313: public void clear() {
314: if (isEmpty())
315: return;
316:
317: int rowCount = getRowCount();
318: int startingRow = getFoldingGroupStartingRow(this );
319: synchronized (tasks) {
320: tasks.clear();
321: }
322:
323: fireTableRowsDeleted(startingRow, startingRow + rowCount);
324: }
325:
326: public boolean isEmpty() {
327: synchronized (tasks) {
328: return tasks.isEmpty();
329: }
330: }
331:
332: public void setExpanded(boolean expand) {
333: if (isExpanded == expand)
334: return;
335: toggleExpanded();
336: }
337:
338: public void toggleExpanded() {
339: this .isExpanded = !isExpanded;
340:
341: Settings.getDefault().setGroupExpanded(tg.getName(),
342: isExpanded);
343:
344: int firstRow = 0;
345: int groupIndex = groups.indexOf(this );
346: for (int i = 0; i < groupIndex; i++) {
347: firstRow += groups.get(i).getRowCount();
348: }
349: int lastRow = firstRow + getTaskCount();
350: firstRow += 1;
351:
352: if (isExpanded)
353: fireTableRowsInserted(firstRow, lastRow);
354: else
355: fireTableRowsDeleted(firstRow, lastRow);
356: fireTableCellUpdated(firstRow - 1, COL_GROUP);
357: }
358:
359: public int getRowCount() {
360: return isEmpty() ? 0 : (isExpanded ? 1 + tasks.size() : 1);
361: }
362:
363: public int getTaskCount() {
364: synchronized (tasks) {
365: return tasks.size();
366: }
367: }
368:
369: public Task getTaskAt(int index) {
370: synchronized (tasks) {
371: return tasks.get(index);
372: }
373: }
374:
375: public int compareTo(
376: org.netbeans.modules.tasklist.ui.FoldingTaskListModel.FoldingGroup other) {
377: List<? extends TaskGroup> groupList = TaskGroup.getGroups();
378: int myIndex = groupList.indexOf(tg);
379: int otherIndex = groupList.indexOf(other.tg);
380: return myIndex - otherIndex;
381: }
382:
383: public boolean isExpanded() {
384: return isExpanded;
385: }
386:
387: public TaskGroup getGroup() {
388: return tg;
389: }
390:
391: private Comparator<Task> getComparator() {
392: if (null == comparator)
393: comparator = TaskComparator.getDefault();
394: return comparator;
395: }
396:
397: private void setComparator(Comparator<Task> newComparator) {
398: if (getComparator().equals(newComparator))
399: return;
400: comparator = newComparator;
401: synchronized (tasks) {
402: if (!tasks.isEmpty()) {
403: Collections.sort(tasks, getComparator());
404: if (isExpanded()) {
405: int firstRow = 0;
406: int groupIndex = groups.indexOf(this );
407: for (int i = 0; i < groupIndex; i++) {
408: firstRow += groups.get(i).getRowCount();
409: }
410: int lastRow = firstRow + getTaskCount();
411: firstRow += 1;
412:
413: fireTableRowsUpdated(firstRow, lastRow);
414: }
415: }
416: }
417: }
418: }
419: }
|