View Javadoc

1   /*
2    *  File: DefaultCCPStrategy.java 
3    *  Copyright (c) 2004-2007  Peter Kliem (Peter.Kliem@jaret.de)
4    *  A commercial license is available, see http://www.jaret.de.
5    *
6    * All rights reserved. This program and the accompanying materials
7    * are made available under the terms of the Common Public License v1.0
8    * which accompanies this distribution, and is available at
9    * http://www.eclipse.org/legal/cpl-v10.html
10   */
11  package de.jaret.util.ui.table.strategies;
12  
13  import java.util.ArrayList;
14  import java.util.HashMap;
15  import java.util.List;
16  import java.util.Map;
17  import java.util.Set;
18  import java.util.StringTokenizer;
19  
20  import org.eclipse.swt.dnd.Clipboard;
21  import org.eclipse.swt.dnd.TextTransfer;
22  import org.eclipse.swt.dnd.Transfer;
23  import org.eclipse.swt.graphics.Point;
24  import org.eclipse.swt.widgets.Display;
25  
26  import de.jaret.util.ui.table.JaretTable;
27  import de.jaret.util.ui.table.model.IJaretTableCell;
28  import de.jaret.util.ui.table.model.IJaretTableSelection;
29  
30  /***
31   * Default implementation for cut, copy, paste. See the the description of the methods for details. The implementation
32   * is not yet perfect. It uses "brutal" String conversions. May be it would best to introduce converter services in the
33   * table model (optional) or use methods in renderers or editors for conversion.
34   * 
35   * @author Peter Kliem
36   * @version $Id: DefaultCCPStrategy.java 385 2007-04-29 20:31:49Z olk $
37   */
38  public class DefaultCCPStrategy implements ICCPStrategy {
39      /*** Delimiter used when copying. */
40      private static final String COPY_DELIMITER = "\t";
41      /*** Delimiters for separating fields in paste operations. */
42      private static final String PASTE_DELIMITERS = "\t;";
43  
44      /*** Clipboard instance. */
45      private Clipboard _clipboard;
46      /*** If set to true header labels will always included in copies. */
47      private boolean _includeHeadersInCopy = false;
48  
49      /***
50       * Aquire clipboard.
51       * 
52       * @return Clipboard instance
53       */
54      private synchronized Clipboard getClipboard() {
55          if (_clipboard == null) {
56              _clipboard = new Clipboard(Display.getCurrent());
57          }
58          return _clipboard;
59      }
60  
61      /***
62       * {@inheritDoc}
63       */
64      public void dispose() {
65          if (_clipboard != null) {
66              _clipboard.dispose();
67          }
68      }
69  
70      /***
71       * Do the copy operation using the constant COPY_DELIMITER. Empty lines will be omitted, missing cels will be empty.
72       * 
73       * @param table jaret table the operation is invoked on
74       */
75      public void copy(JaretTable table) {
76          cutOrCopy(table, false);
77      }
78  
79      /***
80       * Do the cut operation. Basicly a a copy and a empty operation.
81       * 
82       * @param table jaret table the operation is invoked on
83       */
84      public void cut(JaretTable table) {
85          cutOrCopy(table, true);
86      }
87  
88      /***
89       * Do the actual copy or cut operation.
90       * 
91       * @param table table
92       * @param cut if set to true cells we be emptied
93       */
94      protected void cutOrCopy(JaretTable table, boolean cut) {
95          IJaretTableSelection selection = table.getSelectionModel().getSelection();
96          Clipboard cb = getClipboard();
97          if (!selection.isEmpty()) {
98              Set<IJaretTableCell> cells = selection.getAllSelectedCells(table.getTableModel());
99              int minx = -1;
100             int maxx = -1;
101             int miny = -1;
102             int maxy = -1;
103             // line is the outer map
104             Map<Integer, Map<Integer, IJaretTableCell>> cellMap = new HashMap<Integer, Map<Integer, IJaretTableCell>>();
105             for (IJaretTableCell cell : cells) {
106                 Point p = table.getCellDisplayIdx(cell);
107                 Map<Integer, IJaretTableCell> lineMap = cellMap.get(p.y);
108                 if (lineMap == null) {
109                     lineMap = new HashMap<Integer, IJaretTableCell>();
110                     cellMap.put(p.y, lineMap);
111                 }
112                 if (miny == -1 || p.y < miny) {
113                     miny = p.y;
114                 }
115                 if (maxy == -1 || p.y > maxy) {
116                     maxy = p.y;
117                 }
118                 lineMap.put(p.x, cell);
119                 if (minx == -1 || p.x < minx) {
120                     minx = p.x;
121                 }
122                 if (maxx == -1 || p.x > maxx) {
123                     maxx = p.x;
124                 }
125             }
126             StringBuilder buf = new StringBuilder();
127             if (_includeHeadersInCopy) {
128                 for (int x = minx; x <= maxx; x++) {
129                     String headerLabel = table.getColumn(x).getHeaderLabel();
130                     buf.append(headerLabel);
131                     buf.append(COPY_DELIMITER);
132                 }
133                 buf.append("\n");
134             }
135             for (int y = miny; y <= maxy; y++) {
136                 Map<Integer, IJaretTableCell> lineMap = cellMap.get(y);
137                 // empty lines are ommitted
138                 if (lineMap != null) {
139                     for (int x = minx; x <= maxx; x++) {
140                         IJaretTableCell cell = lineMap.get(x);
141                         String value = null;
142                         if (cell != null) {
143                             Object val = cell.getColumn().getValue(cell.getRow());
144                             value = val != null ? val.toString() : null;
145                             if (cut) {
146                                 emptyCell(cell);
147                             }
148                         }
149                         if (value != null) {
150                             buf.append(value);
151                         }
152                         buf.append(COPY_DELIMITER);
153                     }
154                     buf.append("\n");
155                 }
156             }
157             TextTransfer textTransfer = TextTransfer.getInstance();
158             cb.setContents(new Object[] {buf.toString()}, new Transfer[] {textTransfer});
159         }
160     }
161 
162     /***
163      * Empty the given cell. First try null, if an exception is thrown by the modell try the empty string.
164      * 
165      * @param cell cell to be emptied
166      */
167     protected void emptyCell(IJaretTableCell cell) {
168         try {
169             cell.getColumn().setValue(cell.getRow(), null);
170         } catch (Exception e) {
171             try {
172                 cell.getColumn().setValue(cell.getRow(), "");
173             } catch (Exception ex) {
174                 // ignore
175             }
176         }
177     }
178 
179     /***
180      * Paste pastes textual context starting at the focussed cell (does not use the selection by now). Uses TAB and
181      * semicolon as delimiters (Excel uses TAB, semicolon for pasting csv).
182      * 
183      * @param table the jaret table
184      */
185     public void paste(JaretTable table) {
186         Clipboard cb = getClipboard();
187 
188         TextTransfer textTransfer = TextTransfer.getInstance();
189         Object content = cb.getContents(textTransfer);
190         if (content != null) {
191             if (content instanceof String) {
192                 String string = (String) content;
193                 List<String> lines = new ArrayList<String>();
194                 StringTokenizer tokenizer = new StringTokenizer(string, "\n");
195                 while (tokenizer.hasMoreTokens()) {
196                     lines.add(tokenizer.nextToken());
197                 }
198                 Point focus = table.getFocussedCellIdx();
199                 if (focus == null) {
200                     table.setFocus();
201                     focus = table.getFocussedCellIdx();
202                 }
203                 int lineOff = 0;
204                 for (String line : lines) {
205                     tokenizer = new StringTokenizer(line, PASTE_DELIMITERS, true);
206                     int colOff = 0;
207                     String last = null;
208                     while (tokenizer.hasMoreTokens()) {
209                         String value = tokenizer.nextToken();
210                         boolean ignore = false;
211                         if (PASTE_DELIMITERS.indexOf(value) != -1) {
212                             // delimiter
213                             if (last != null && last.equals(value)) {
214                                 value = "";
215                             } else {
216                                 ignore = true;
217                             }
218                         }
219                         if (!ignore) {
220                             try {
221                                 table.setValue(focus.x + colOff, focus.y + lineOff, value);
222                             } catch (Exception e) {
223                                 // silently ignore -- this can happen
224                             }
225 
226                             colOff++;
227                         }
228                         last = value;
229                     }
230                     lineOff++;
231                 }
232             }
233         }
234     }
235 
236     /***
237      * Retrieve the state of header include in the copied content.
238      * 
239      * @return the includeHeadersInCopy
240      */
241     public boolean getIncludeHeadersInCopy() {
242         return _includeHeadersInCopy;
243     }
244 
245     /***
246      * Set includeHeaders: if set to true in copy and cut context the headline (=col headers) labels will be included.
247      * 
248      * @param includeHeadersInCopy the includeHeadersInCopy to set
249      */
250     public void setIncludeHeadersInCopy(boolean includeHeadersInCopy) {
251         _includeHeadersInCopy = includeHeadersInCopy;
252     }
253 
254 }