001: /*
002: * Copyright 2001-2006 C:1 Financial Services GmbH
003: *
004: * This software is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License Version 2.1, as published by the Free Software Foundation.
007: *
008: * This software is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
011: * Lesser General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public
014: * License along with this library; if not, write to the Free Software
015: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
016: */
017:
018: package de.finix.contelligent.client.gui.directory;
019:
020: import java.awt.BorderLayout;
021: import java.awt.Dimension;
022: import java.awt.Point;
023: import java.awt.Rectangle;
024: import java.awt.SystemColor;
025: import java.awt.datatransfer.DataFlavor;
026: import java.awt.datatransfer.Transferable;
027: import java.awt.datatransfer.UnsupportedFlavorException;
028: import java.awt.dnd.DnDConstants;
029: import java.awt.dnd.DropTarget;
030: import java.awt.dnd.DropTargetDragEvent;
031: import java.awt.dnd.DropTargetDropEvent;
032: import java.awt.dnd.DropTargetEvent;
033: import java.awt.dnd.DropTargetListener;
034: import java.awt.event.FocusAdapter;
035: import java.awt.event.FocusEvent;
036: import java.io.IOException;
037: import java.io.Serializable;
038: import java.util.ArrayList;
039: import java.util.List;
040: import java.util.logging.Level;
041: import java.util.logging.Logger;
042:
043: import javax.swing.JComponent;
044: import javax.swing.JScrollPane;
045: import javax.swing.ListSelectionModel;
046: import javax.swing.TransferHandler;
047:
048: import de.finix.contelligent.client.base.ComponentFactory;
049: import de.finix.contelligent.client.base.ComponentNotFoundException;
050: import de.finix.contelligent.client.base.ComponentProperty;
051: import de.finix.contelligent.client.base.ContelligentComponent;
052: import de.finix.contelligent.client.event.ContelligentEvent;
053: import de.finix.contelligent.client.gui.AbstractComponentEditor;
054: import de.finix.contelligent.client.util.dnd.MultipleComponentTransferable;
055: import de.finix.contelligent.client.util.dnd.SingleComponentTransferable;
056:
057: public class FolderEditor extends AbstractComponentEditor {
058:
059: private static Logger logger = Logger.getLogger(FolderEditor.class
060: .getName());
061:
062: private FolderTableModel folderTableModel;
063:
064: private FolderTable folderTable;
065:
066: // Master switch for enabling the new sorted folder rearrangement interface
067: private boolean dropEnabled = false;
068:
069: // This flavor is used internally to transfer a ContelligentComponent[] that
070: // we dont want to be recognized by the usual drop targets for it.
071: private static final DataFlavor concealedFlavor = new DataFlavor(
072: FolderEditor.class, "Concealed components");
073:
074: public void init() {
075: JScrollPane scrollPane = null;
076: update();
077: folderTable.getColumnModel().getColumn(0).setPreferredWidth(20);
078: folderTable.getColumnModel().getColumn(1).setPreferredWidth(80);
079: folderTable.getColumnModel().getColumn(2).setPreferredWidth(10);
080: folderTable
081: .setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
082: folderTable.setShowHorizontalLines(false);
083: folderTable.setShowVerticalLines(false);
084: folderTable.setIntercellSpacing(new Dimension(0, 0));
085: folderTable.setRowHeight(18);
086: folderTable.setDragEnabled(true);
087: folderTable.setTransferHandler(new FolderTransferHandler());
088: folderTable.setDropTarget(new DropTarget(this , new DropHandler(
089: DnDConstants.ACTION_MOVE)));
090: scrollPane = new JScrollPane(folderTable);
091: // Not opaque, since it doesnt have any row separators
092: // (Example: Windows Explorer)
093: scrollPane.getViewport().setBackground(SystemColor.text);
094: add(scrollPane, BorderLayout.CENTER);
095: folderTable.addFocusListener(new FocusAdapter() {
096: public void focusGained(FocusEvent e) {
097: getView().setActions(FolderEditor.this .getActions());
098: }
099: });
100:
101: }
102:
103: public void update() {
104: if (folderTableModel == null) {
105: folderTableModel = new FolderTableModel(getComponent(),
106: getView());
107: folderTableModel.setEditable(isEditable());
108: folderTable = new FolderTable(folderTableModel);
109: } else {
110: folderTableModel.setComponent(getComponent());
111: folderTableModel.updateModel();
112: folderTableModel.fireTableDataChanged();
113: }
114: }
115:
116: public void commit() {
117: setEditable(false);
118: ComponentFactory.getInstance().save(getComponent());
119: }
120:
121: public void setEditable(boolean editable) {
122: super .setEditable(editable);
123: if (editable) {
124: // Only switch to new sortable mode if this is a SortedFolder or
125: // subtype of it
126: if (getComponent().getType().instanceOf(
127: "contelligent.core.SortedFolder")) {
128: dropEnabled = true;
129: } else {
130: dropEnabled = false;
131: }
132: } else {
133: dropEnabled = false;
134: }
135: if (folderTable != null) {
136: if (dropEnabled) {
137: // Restrict this while we are in edit mode, since discontinuous
138: // intervals
139: // cant be sensibly reordered by means of drag and drop.
140: folderTable
141: .setSelectionMode(ListSelectionModel.SINGLE_INTERVAL_SELECTION);
142: } else {
143: folderTable
144: .setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
145: }
146: folderTable.setEditable(editable);
147: }
148: if (folderTableModel != null) {
149: if (editable) {
150: folderTableModel.updateModel();
151: }
152: folderTableModel.setEditable(isEditable());
153: }
154: }
155:
156: protected void updateComponent() {
157: }
158:
159: protected void componentChanged(ContelligentEvent event) {
160: }
161:
162: protected void childComponentAdded(ContelligentEvent event) {
163: update();
164: }
165:
166: protected void childComponentRemoved(ContelligentEvent event) {
167: update();
168: }
169:
170: protected void childComponentChanged(ContelligentEvent event) {
171: }
172:
173: protected void descendentComponentChanged(ContelligentEvent event) {
174: }
175:
176: private class FolderTransferHandler extends TransferHandler {
177: protected Transferable createTransferable(JComponent c) {
178: FolderTable table = (FolderTable) c;
179: if (!dropEnabled) {
180: if (table.getSelectedRowCount() == 1) {
181: return new SingleComponentTransferable(
182: (ContelligentComponent) table.getModel()
183: .getValueAt(table.getSelectedRow(),
184: 1), getView()
185: .getEnvironment());
186: } else if (table.getSelectedRowCount() > 1) {
187: List selectedComponents = new ArrayList(10);
188: for (int i = 0; i < table.getSelectedRows().length; i++) {
189: int selectedRow = table.getSelectedRows()[i];
190: selectedComponents
191: .add((ContelligentComponent) table
192: .getModel().getValueAt(
193: selectedRow, 1));
194: }
195: return new MultipleComponentTransferable(
196: (ContelligentComponent[]) selectedComponents
197: .toArray(new ContelligentComponent[0]),
198: getView().getEnvironment());
199: }
200: } else {
201: List selectedComponents = new ArrayList(10);
202: for (int i = 0; i < table.getSelectedRows().length; i++) {
203: int selectedRow = table.getSelectedRows()[i];
204: selectedComponents
205: .add((ContelligentComponent) table
206: .getModel().getValueAt(selectedRow,
207: 1));
208: }
209: MultipleComponentTransferable mct = new MultipleComponentTransferable(
210: (ContelligentComponent[]) selectedComponents
211: .toArray(new ContelligentComponent[0]),
212: getView().getEnvironment());
213: // Intentionally conceal the actual flavor here... In this
214: // special case we dont want usual drop targets to apply
215: mct.setDataFlavor(FolderEditor.concealedFlavor);
216: return mct;
217: }
218: // no row selected
219: return null;
220: }
221:
222: public int getSourceActions(JComponent c) {
223: if (dropEnabled) {
224: return MOVE;
225: } else {
226: return COPY_OR_MOVE;
227: }
228: }
229: }
230:
231: private class DropHandler implements DropTargetListener,
232: Serializable {
233: private int sourceActions;
234:
235: public DropHandler(int sourceActions) {
236: this .sourceActions = sourceActions;
237: }
238:
239: private boolean canImport = true;
240:
241: private boolean actionSupported(int action) {
242: return ((sourceActions & action) != 0);
243: }
244:
245: // --- DropTargetListener methods -----------------------------------
246:
247: public void dragEnter(DropTargetDragEvent e) {
248: if (isDragOK(e)) {
249: e.acceptDrag(sourceActions);
250: } else {
251: e.rejectDrag();
252: }
253: }
254:
255: public void dragOver(DropTargetDragEvent e) {
256: if (isDragOK(e)) {
257: Point p = e.getLocation();
258: int scrollSpeed = 20;
259: Rectangle r = new Rectangle(p.x, p.y - scrollSpeed, 1,
260: scrollSpeed * 2);
261: folderTable.scrollRectToVisible(r);
262: int dropRow = folderTable.rowAtPoint(p);
263: int firstDragged = 0;
264: if (folderTable.getSelectedRowCount() == 1) {
265: firstDragged = folderTable.getSelectedRow();
266: } else if (folderTable.getSelectedRowCount() > 1) {
267: int[] selectedRows = folderTable.getSelectedRows();
268: firstDragged = selectedRows[0];
269: }
270: folderTable.setHighlightedRow(dropRow,
271: (dropRow > firstDragged));
272: e.acceptDrag(sourceActions);
273: } else {
274: folderTable.setHighlightedRow(-1, false);
275: e.rejectDrag();
276: }
277: }
278:
279: private boolean isDragOK(DropTargetDragEvent e) {
280: int dropAction = e.getDropAction();
281: if (!dropEnabled || !actionSupported(dropAction)) {
282: return false;
283: } else {
284: if (e
285: .isDataFlavorSupported(FolderEditor.concealedFlavor)) {
286: int firstDragged = 0;
287: int lastDragged = 0;
288: if (folderTable.getSelectedRowCount() == 1) {
289: firstDragged = folderTable.getSelectedRow();
290: lastDragged = firstDragged;
291: } else if (folderTable.getSelectedRowCount() > 1) {
292: int[] selectedRows = folderTable
293: .getSelectedRows();
294: firstDragged = selectedRows[0];
295: lastDragged = selectedRows[selectedRows.length - 1];
296: }
297: Point p = e.getLocation();
298: int dropRow = folderTable.rowAtPoint(p);
299: if ((dropRow < firstDragged)
300: || (dropRow > lastDragged)) {
301: return true;
302: } else {
303: return false;
304: }
305: } else {
306: return false;
307: }
308: }
309: }
310:
311: public void dragExit(DropTargetEvent e) {
312: folderTable.setHighlightedRow(-1, false);
313: }
314:
315: public void drop(DropTargetDropEvent e) {
316: final int dropAction = e.getDropAction();
317: folderTable.setHighlightedRow(-1, false);
318: if (!dropEnabled) {
319: e.rejectDrop();
320: } else {
321: if (e
322: .isDataFlavorSupported(FolderEditor.concealedFlavor)) {
323: Transferable transferable = e.getTransferable();
324: try {
325: e.acceptDrop(DnDConstants.ACTION_MOVE);
326: ContelligentComponent[] dropped = (ContelligentComponent[]) transferable
327: .getTransferData(FolderEditor.concealedFlavor);
328: Point p = e.getLocation();
329: int dropRow = folderTable.rowAtPoint(p);
330: // Since we switched the table selection mode to
331: // SINGLE_INTERVAL_SELECTION
332: // we will always get a consistent span of dragged
333: // components
334: int firstDragged = folderTableModel
335: .getComponentRow(dropped[0]);
336: int lastDragged = folderTableModel
337: .getComponentRow(dropped[dropped.length - 1]);
338: if ((firstDragged == -1) || (lastDragged == -1)
339: || (dropRow == -1)) {
340: logger
341: .log(
342: Level.INFO,
343: "Unable to locate some dragged components in the table for movement. Drop aborted.");
344: } else {
345: if (dropRow > firstDragged) {
346: // Dragged down; we need to adjust the dropRow
347: // to account
348: // for rows being first removed from the model,
349: // then
350: // inserted at the given position in the new
351: // model
352: dropRow -= (lastDragged - firstDragged);
353: }
354: folderTableModel.moveRow(firstDragged,
355: lastDragged, dropRow);
356: ContelligentComponent folder = FolderEditor.this
357: .getComponent();
358: ComponentProperty nameList = folder
359: .getProperty("nameList");
360: StringBuffer newListValue = new StringBuffer();
361: for (int i = 0; i < folderTableModel
362: .getRowCount(); i++) {
363: String name = ((ContelligentComponent) folderTableModel
364: .getValueAt(i, 0)).getName();
365: if (newListValue.length() > 0) {
366: newListValue.append(",");
367: }
368: newListValue.append(name);
369: }
370: nameList.setValue(newListValue.toString());
371: FolderEditor.this .getComponent()
372: .setModified(true);
373: }
374: } catch (UnsupportedFlavorException ufe) {
375: logger.log(Level.INFO, "Drop failed: ", ufe);
376: } catch (IOException ioe) {
377: logger.log(Level.INFO, "Drop failed: ", ioe);
378: } catch (ClassCastException cce) {
379: logger.log(Level.INFO, "Drop failed: ", cce);
380: }
381: }
382: }
383: }
384:
385: public void dropActionChanged(DropTargetDragEvent e) {
386: int dropAction = e.getDropAction();
387:
388: if (canImport && actionSupported(dropAction) && dropEnabled) {
389: e.acceptDrag(dropAction);
390: } else {
391: e.rejectDrag();
392: }
393: }
394: }
395:
396: }
|