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