1 /*******************************************************************************
\r
2 * in Industry THTH ry.
\r
3 * All rights reserved. This program and the accompanying materials
\r
4 * are made available under the terms of the Eclipse Public License v1.0
\r
5 * which accompanies this distribution, and is available at
\r
6 * http://www.eclipse.org/legal/epl-v10.html
\r
9 * VTT Technical Research Centre of Finland - initial API and implementation
\r
10 *******************************************************************************/
\r
11 package org.simantics.spreadsheet.ui;
\r
13 import gnu.trove.map.hash.THashMap;
\r
15 import java.awt.Rectangle;
\r
16 import java.util.ArrayList;
\r
17 import java.util.Collection;
\r
18 import java.util.HashMap;
\r
19 import java.util.HashSet;
\r
20 import java.util.List;
\r
21 import java.util.Map;
\r
22 import java.util.Set;
\r
23 import java.util.concurrent.CopyOnWriteArrayList;
\r
25 import org.simantics.spreadsheet.ClientModel;
\r
26 import org.simantics.spreadsheet.util.SpreadsheetUtils;
\r
27 import org.simantics.utils.datastructures.Pair;
\r
28 import org.simantics.utils.datastructures.collections.CollectionUtils;
\r
30 public class ClientModelImpl implements ClientModel {
\r
32 final private Map<String, Map<String, Object>> properties = new THashMap<String, Map<String, Object>>();
\r
33 final private Map<String, Map<String, Object>> cells = new THashMap<String, Map<String, Object>>();
\r
35 private int maxRow = 0;
\r
36 private int maxColumn = 0;
\r
38 private Set<String> clears = new HashSet<String>();
\r
39 // private ArrayList<Triple<String, String, Object>> modifications = new ArrayList<Triple<String, String, Object>>();
\r
41 private ArrayList<String> modLocation = new ArrayList<String>();
\r
42 private ArrayList<String> modProperty = new ArrayList<String>();
\r
43 private ArrayList<Object> modValue = new ArrayList<Object>();
\r
44 private Map<Long, Rectangle> spanMap = new HashMap<Long, Rectangle>();
\r
46 public ClientModelImpl() {
\r
48 if(Spreadsheet.DEBUG) System.out.println("SimpleContainerTableModel.init");
\r
50 Map<String, Object> sheetDimensions = new THashMap<String, Object>();
\r
51 Map<String, Object> headers = new THashMap<String, Object>();
\r
52 Map<String, Object> excel = new THashMap<String, Object>();
\r
53 Map<String, Object> sources = new THashMap<String, Object>();
\r
54 Map<String, Object> sheets = new THashMap<String, Object>();
\r
55 Map<String, Object> context = new THashMap<String, Object>();
\r
56 Map<String, Object> mode = new THashMap<String, Object>();
\r
57 Map<String, Object> states = new THashMap<String, Object>();
\r
59 sheetDimensions.put(DIMENSIONS_FIT_ROWS, false);
\r
60 sheetDimensions.put(DIMENSIONS_FIT_COLS, false);
\r
61 sheetDimensions.put(DIMENSIONS_COL_COUNT, 0);
\r
62 sheetDimensions.put(DIMENSIONS_ROW_COUNT, 0);
\r
64 excel.put(EXCEL_VISIBLE, false);
\r
66 headers.put(HEADERS_COL_WIDTHS, new int[] { });
\r
67 headers.put(HEADERS_ROW_HEIGHTS, new int[] { });
\r
68 headers.put(HEADERS_COL_LABELS, new String[] { });
\r
69 headers.put(HEADERS_ROW_LABELS, new String[] { });
\r
71 sources.put(SOURCES_AVAILABLE, new String[] { });
\r
72 sources.put(SOURCES_CURRENT, "");
\r
74 properties.put(DIMENSIONS, sheetDimensions);
\r
75 properties.put(HEADERS, headers);
\r
76 properties.put(EXCEL, excel);
\r
77 properties.put(SOURCES, sources);
\r
78 properties.put(SHEETS, sheets);
\r
79 properties.put(CONTEXT, context);
\r
80 properties.put(MODE, mode);
\r
81 properties.put(STATES, states);
\r
83 setProperty(EXCEL, EXCEL_VISIBLE, false);
\r
87 CopyOnWriteArrayList<ClientModelListener> listeners = new CopyOnWriteArrayList<ClientModelListener>();
\r
90 public void addListener(ClientModelListener listener) {
\r
91 listeners.add(listener);
\r
92 listener.rows(getRows());
\r
93 listener.columns(getColumns());
\r
94 listener.rowLabels((String[])getPropertyAt(HEADERS, HEADERS_ROW_LABELS));
\r
95 listener.columnWidths((int[])getPropertyAt(HEADERS, HEADERS_COL_WIDTHS));
\r
96 listener.sources((String[])getPropertyAt(SOURCES, SOURCES_AVAILABLE), (String)getPropertyAt(SOURCES, SOURCES_CURRENT));
\r
100 public void removeListener(ClientModelListener listener) {
\r
101 listeners.remove(listener);
\r
104 private void fireFlush() {
\r
105 for(ClientModelListener listener : listeners)
\r
109 private void fireRows() {
\r
110 for(ClientModelListener listener : listeners)
\r
111 listener.rows(getRows());
\r
114 private void fireColumns() {
\r
115 for(ClientModelListener listener : listeners)
\r
116 listener.columns(getColumns());
\r
119 private void fireColumnWidths() {
\r
120 for(ClientModelListener listener : listeners)
\r
121 listener.columnWidths((int[])getPropertyAt(HEADERS, HEADERS_COL_WIDTHS));
\r
124 private void fireSources() {
\r
125 for(ClientModelListener listener : listeners)
\r
126 listener.sources((String[])getPropertyAt(SOURCES, SOURCES_AVAILABLE), (String)getPropertyAt(SOURCES, SOURCES_CURRENT));
\r
129 private void fireProperty(String location, String property, Object value) {
\r
130 for(ClientModelListener listener : listeners)
\r
131 listener.propertyChange(location, property, value);
\r
134 private void fireCleared(String location) {
\r
135 for(ClientModelListener listener : listeners)
\r
136 listener.cleared(location);
\r
140 public <T> T getPossiblePropertyAt(String location, String property) {
\r
142 T t = getPropertyAt(location, property);
\r
144 } catch (Throwable e) {
\r
149 @SuppressWarnings("unchecked")
\r
150 public synchronized <T> T getPropertyAt(String location, String property) {
\r
152 if(Spreadsheet.DEBUG) System.out.println("SimpleContainerTableModel.getPropertyAt " + location + " " + property);
\r
154 Map<String, Object> props = properties.get(location);
\r
155 if(props != null) {
\r
156 return (T)props.get(property);
\r
158 Map<String, Object> cls = cells.get(location);
\r
160 return (T)cls.get(property);
\r
168 private final Set<String> sizing = CollectionUtils.toSet(
\r
169 DIMENSIONS_COL_COUNT,
\r
170 DIMENSIONS_ROW_COUNT,
\r
171 DIMENSIONS_FIT_COLS,
\r
172 DIMENSIONS_FIT_ROWS);
\r
174 private boolean sizingProperty(String property) {
\r
175 return sizing.contains(property);
\r
178 private boolean checkMaxRow(int row) {
\r
179 if((row+1) > maxRow) {
\r
187 private boolean checkMaxColumn(int column) {
\r
188 if((column+1) > maxColumn) {
\r
189 maxColumn = column+1;
\r
197 public synchronized void setProperty(final String location, final String property, final Object value) {
\r
199 assert(location != null);
\r
200 assert(property != null);
\r
202 modLocation.add(location);
\r
203 modProperty.add(property);
\r
204 modValue.add(value);
\r
209 public void clearAll() {
\r
210 clears.addAll(cells.keySet());
\r
214 public synchronized void clear(String location) {
\r
215 if (location.startsWith("Style"))
\r
216 // Nothing to do for now..
\r
218 clears.add(location);
\r
222 public synchronized void flush() {
\r
224 for(String location : clears) {
\r
226 Map<String, Object> cls = cells.remove(location);
\r
227 if(cls == null) return;
\r
229 long l = SpreadsheetUtils.decodeCellCoded(location);
\r
230 int row = (int)(l & 0xffffffff) - 1;
\r
231 int column = (int)((l>>32) & 0xffffffff);
\r
233 if(checkMaxRow(row)) fireRows();
\r
234 if(checkMaxColumn(column)) fireColumns();
\r
236 removeSpan(row, column);
\r
238 fireCleared(location);
\r
242 for(int i=0;i<modLocation.size();i++) {
\r
244 String location = modLocation.get(i);
\r
245 String property = modProperty.get(i);
\r
246 Object value = modValue.get(i);
\r
248 if(Spreadsheet.DEBUG) System.out.println("ClientModelImpl.setProperty " + location + " " + property + " " + value);
\r
250 Map<String, Object> props = properties.get(location);
\r
251 if(props != null || location.startsWith("Style")) {
\r
252 if (location.startsWith("Style") && props == null) {
\r
253 props = new HashMap<>();
\r
254 properties.put(location, props);
\r
257 if(sizingProperty(property)) {
\r
259 int currentRows = getRows();
\r
260 int currentCols = getColumns();
\r
261 props.put(property, value);
\r
262 if(getRows() != currentRows) fireRows();
\r
263 if(getColumns() != currentCols) fireColumns();
\r
267 props.put(property, value);
\r
268 if(property.equals(HEADERS_COL_WIDTHS)) fireColumnWidths();
\r
269 if(property.equals(SOURCES_AVAILABLE) || property.equals(SOURCES_CURRENT)) fireSources();
\r
275 Map<String, Object> cls = cells.get(location);
\r
277 cls = new HashMap<String, Object>();
\r
278 cells.put(location, cls);
\r
281 cls.put(property, value);
\r
283 long l = SpreadsheetUtils.decodeCellCoded(location);
\r
284 int row = (int)(l & 0xffffffff) - 1;
\r
285 int column = (int)((l>>32) & 0xffffffff);
\r
287 if(checkMaxRow(row)) fireRows();
\r
288 if(checkMaxColumn(column)) fireColumns();
\r
290 boolean rowSpanProperty = property.equals(ROW_SPAN);
\r
291 boolean columnSpanProperty = property.equals(COLUMN_SPAN);
\r
292 if (rowSpanProperty || columnSpanProperty) {
\r
293 Rectangle span = getRootSpan(row, column);
\r
294 int size = (Integer)value;
\r
296 if (span == null) {
\r
298 if (rowSpanProperty) {
\r
299 span = createSpan(row, column, size, 1);
\r
301 span = createSpan(row, column, 1, size);
\r
305 if (rowSpanProperty) {
\r
306 span.height = size;
\r
310 if ((span.width == 1) && (span.height == 1)) {
\r
311 removeSpan(row, column);
\r
317 fireProperty(location, property, value);
\r
322 modLocation.clear();
\r
323 modProperty.clear();
\r
331 public int getRows() {
\r
332 boolean fitRows = getPropertyAt(ClientModel.DIMENSIONS, ClientModel.DIMENSIONS_FIT_ROWS);
\r
333 if(fitRows) return maxRow;
\r
334 else return getPropertyAt(ClientModel.DIMENSIONS, ClientModel.DIMENSIONS_ROW_COUNT);
\r
338 public int getColumns() {
\r
339 boolean fitCols = getPropertyAt(ClientModel.DIMENSIONS, ClientModel.DIMENSIONS_FIT_COLS);
\r
343 else return getPropertyAt(ClientModel.DIMENSIONS, ClientModel.DIMENSIONS_COL_COUNT);
\r
347 public int[] getColumnWidths() {
\r
348 int[] data = getPropertyAt(HEADERS, HEADERS_COL_WIDTHS);
\r
349 return data.clone();
\r
353 public int[] getRowHeights() {
\r
354 int[] data = getPropertyAt(HEADERS, HEADERS_ROW_HEIGHTS);
\r
355 return data.clone();
\r
359 public synchronized Collection<Pair<String, Object>> listAll(String property) {
\r
361 ArrayList<Pair<String, Object>> result = new ArrayList<Pair<String, Object>>();
\r
363 for(Map.Entry<String, Map<String, Object>> entry : properties.entrySet()) {
\r
364 Object value = entry.getValue().get(property);
\r
365 if(value != null) result.add(Pair.make(entry.getKey(), value));
\r
367 for(Map.Entry<String, Map<String, Object>> entry : cells.entrySet()) {
\r
368 Object value = entry.getValue().get(property);
\r
369 if(value != null) result.add(Pair.make(entry.getKey(), value));
\r
376 private long spanKey(int row, int column) {
\r
377 return (row & 0xffffffff) | (((long)(column & 0xffffffff)) << 32);
\r
381 public synchronized Rectangle getSpan(int row, int column) {
\r
382 for (Rectangle span : spanMap.values()) {
\r
383 if (span.contains(column, row)) {
\r
384 return new Rectangle(span);
\r
391 public synchronized List<Rectangle> getSpans() {
\r
392 List<Rectangle> spans = new ArrayList<Rectangle>(spanMap.size());
\r
393 for (Rectangle span : spanMap.values()) {
\r
394 spans.add(new Rectangle(span));
\r
399 private Rectangle getRootSpan(int row, int column) {
\r
400 return spanMap.get(spanKey(row, column));
\r
403 private Rectangle createSpan(int row, int column, int rowSpan, int columnSpan) {
\r
404 Rectangle span = new Rectangle(column, row, columnSpan, rowSpan);
\r
405 spanMap.put(spanKey(row, column), span);
\r
409 private void removeSpan(int row, int column) {
\r
410 spanMap.remove(spanKey(row, column));
\r