1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.vectomatic.svg.edit.client.inspector;
19
20 import java.util.ArrayList;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.List;
24 import java.util.Map;
25
26 import org.vectomatic.dom.svg.OMSVGPathSeg;
27 import org.vectomatic.dom.svg.utils.SVGConstants;
28 import org.vectomatic.svg.edit.client.AppBundle;
29 import org.vectomatic.svg.edit.client.event.SelectionChangedProcessor;
30 import org.vectomatic.svg.edit.client.event.SelectionChangedProxy;
31 import org.vectomatic.svg.edit.client.model.MetaModel;
32 import org.vectomatic.svg.edit.client.model.ModelCategory;
33 import org.vectomatic.svg.edit.client.model.ModelConstants;
34 import org.vectomatic.svg.edit.client.model.svg.SVGPathElementModel;
35 import org.vectomatic.svg.edit.client.model.svg.SVGPathSegType;
36 import org.vectomatic.svg.edit.client.model.svg.path.SVGCloseSegModel;
37 import org.vectomatic.svg.edit.client.model.svg.path.SVGSegModel;
38 import org.vectomatic.svg.edit.client.model.svg.path.SVGSegStore;
39
40 import com.extjs.gxt.ui.client.Style.LayoutRegion;
41 import com.extjs.gxt.ui.client.Style.Scroll;
42 import com.extjs.gxt.ui.client.data.ModelData;
43 import com.extjs.gxt.ui.client.event.ButtonEvent;
44 import com.extjs.gxt.ui.client.event.Events;
45 import com.extjs.gxt.ui.client.event.GridEvent;
46 import com.extjs.gxt.ui.client.event.Listener;
47 import com.extjs.gxt.ui.client.event.SelectionChangedEvent;
48 import com.extjs.gxt.ui.client.event.SelectionListener;
49 import com.extjs.gxt.ui.client.store.ListStore;
50 import com.extjs.gxt.ui.client.util.Format;
51 import com.extjs.gxt.ui.client.util.Margins;
52 import com.extjs.gxt.ui.client.widget.Component;
53 import com.extjs.gxt.ui.client.widget.ContentPanel;
54 import com.extjs.gxt.ui.client.widget.LayoutContainer;
55 import com.extjs.gxt.ui.client.widget.button.Button;
56 import com.extjs.gxt.ui.client.widget.form.FormPanel;
57 import com.extjs.gxt.ui.client.widget.form.NumberField;
58 import com.extjs.gxt.ui.client.widget.form.SimpleComboBox;
59 import com.extjs.gxt.ui.client.widget.grid.CellEditor;
60 import com.extjs.gxt.ui.client.widget.grid.ColumnConfig;
61 import com.extjs.gxt.ui.client.widget.grid.ColumnData;
62 import com.extjs.gxt.ui.client.widget.grid.ColumnModel;
63 import com.extjs.gxt.ui.client.widget.grid.EditorGrid;
64 import com.extjs.gxt.ui.client.widget.grid.EditorGrid.ClicksToEdit;
65 import com.extjs.gxt.ui.client.widget.grid.Grid;
66 import com.extjs.gxt.ui.client.widget.grid.GridCellRenderer;
67 import com.extjs.gxt.ui.client.widget.grid.GridSelectionModel;
68 import com.extjs.gxt.ui.client.widget.layout.BorderLayout;
69 import com.extjs.gxt.ui.client.widget.layout.BorderLayoutData;
70 import com.extjs.gxt.ui.client.widget.layout.CardLayout;
71 import com.extjs.gxt.ui.client.widget.layout.ColumnLayout;
72 import com.extjs.gxt.ui.client.widget.layout.FitLayout;
73 import com.extjs.gxt.ui.client.widget.layout.VBoxLayout;
74 import com.extjs.gxt.ui.client.widget.layout.VBoxLayout.VBoxLayoutAlign;
75 import com.extjs.gxt.ui.client.widget.layout.VBoxLayoutData;
76 import com.google.gwt.core.client.GWT;
77 import com.google.gwt.user.client.ui.AbstractImagePrototype;
78
79
80
81
82
83 public class PathInspectorSection implements IInspectorSection<SVGPathElementModel> {
84 private class PathPanel extends ContentPanel implements SelectionChangedProcessor<SVGSegModel> {
85 protected ColumnModel cm;
86 protected EditorGrid<SVGSegModel> grid;
87
88 protected Button addSegmentButton;
89 protected Button insertSegmentButton;
90 protected Button removeSegmentsButton;
91
92 protected SelectionChangedProxy<SVGSegModel> selChangeProxy = new SelectionChangedProxy<SVGSegModel>(this);
93
94 private CardLayout cardLayout;
95 private LayoutContainer container;
96 private LayoutContainer noSelection;
97 private LayoutContainer multipleSelection;
98 private IInspectorSection<SVGSegModel> currentSection;
99
100 public PathPanel() {
101 setHeading(Format.capitalize(category.getDescription()));
102 ModelConstants constants = ModelConstants.INSTANCE;
103 List<ColumnConfig> configs = new ArrayList<ColumnConfig>();
104
105 final SimpleComboBox<String> combo = (SimpleComboBox<String>) SVGPathSegType.INSTANCE.createField(SVGSegModel.TYPE);
106 CellEditor editor = new CellEditor(combo) {
107 @Override
108 public Object preProcessValue(Object value) {
109 if (value == null) {
110 return value;
111 }
112 return combo.findModel(value.toString());
113 }
114
115 @Override
116 public Object postProcessValue(Object value) {
117 if (value == null) {
118 return value;
119 }
120 return ((ModelData) value).get("value");
121 }
122 };
123
124 ColumnConfig typeColumn = new ColumnConfig();
125 typeColumn.setSortable(false);
126 typeColumn.setId(SVGSegModel.TYPE_ID);
127 typeColumn.setHeader(constants.segTypeDesc());
128 typeColumn.setWidth(220);
129 typeColumn.setEditor(editor);
130 configs.add(typeColumn);
131
132 GridCellRenderer<ModelData> coordinateRenderer = new GridCellRenderer<ModelData>() {
133 @Override
134 public Object render(ModelData model, String property,
135 ColumnData config, int rowIndex, int colIndex,
136 ListStore<ModelData> store, Grid<ModelData> grid) {
137 if (model instanceof SVGCloseSegModel) {
138 return "";
139 }
140 return null;
141 }
142 };
143 ColumnConfig xColumn = new ColumnConfig();
144 xColumn.setSortable(false);
145 xColumn.setId(SVGConstants.SVG_X_ATTRIBUTE);
146 xColumn.setHeader(constants.x());
147 xColumn.setWidth(100);
148 NumberField xField = new NumberField();
149 xField.setPropertyEditorType(Float.class);
150 xField.setAllowBlank(false);
151 xColumn.setEditor(new CellEditor(xField));
152 xColumn.setRenderer(coordinateRenderer);
153 configs.add(xColumn);
154
155 ColumnConfig yColumn = new ColumnConfig();
156 yColumn.setSortable(false);
157 yColumn.setId(SVGConstants.SVG_Y_ATTRIBUTE);
158 yColumn.setHeader(constants.y());
159 yColumn.setWidth(100);
160 NumberField yField = new NumberField();
161 yField.setPropertyEditorType(Float.class);
162 yField.setAllowBlank(false);
163 yColumn.setEditor(new CellEditor(yField));
164 yColumn.setRenderer(coordinateRenderer);
165 configs.add(yColumn);
166
167 cm = new ColumnModel(configs);
168 grid = new EditorGrid<SVGSegModel>(new SVGSegStore(), cm);
169 grid.setAutoExpandColumn(SVGSegModel.TYPE_ID);
170 grid.setBorders(true);
171 grid.setClicksToEdit(ClicksToEdit.TWO);
172 grid.addListener(Events.BeforeEdit, new Listener<GridEvent<SVGSegModel>>() {
173 @Override
174 public void handleEvent(GridEvent<SVGSegModel> ge) {
175 String property = ge.getProperty();
176
177 if (SVGSegModel.TYPE_ID.equals(property) && ge.getRowIndex() == 0) {
178 ge.setCancelled(true);
179 }
180
181 if (ge.getModel() instanceof SVGCloseSegModel
182 && (SVGConstants.SVG_X_ATTRIBUTE.equals(property)
183 || SVGConstants.SVG_Y_ATTRIBUTE.equals(property))) {
184 ge.setCancelled(true);
185 }
186 }
187 });
188
189
190 addSegmentButton = new Button();
191 addSegmentButton.addSelectionListener(new SelectionListener<ButtonEvent>() {
192 @Override
193 public void componentSelected(ButtonEvent ce) {
194 GWT.log("Add segment");
195 getStore().appendSegment();
196 }
197 });
198 addSegmentButton.setIcon(AbstractImagePrototype.create(AppBundle.INSTANCE.addPoint()));
199 addSegmentButton.setToolTip(Format.capitalize(constants.addSegmentButton()));
200
201 insertSegmentButton = new Button();
202 insertSegmentButton.addSelectionListener(new SelectionListener<ButtonEvent>() {
203 @Override
204 public void componentSelected(ButtonEvent ce) {
205 GWT.log("Insert segment");
206 getStore().insertSegment();
207 }
208 });
209 insertSegmentButton.setIcon(AbstractImagePrototype.create(AppBundle.INSTANCE.insertPoint()));
210 insertSegmentButton.setToolTip(Format.capitalize(constants.insertSegmentButton()));
211
212 removeSegmentsButton = new Button();
213 removeSegmentsButton.addSelectionListener(new SelectionListener<ButtonEvent>() {
214 @Override
215 public void componentSelected(ButtonEvent ce) {
216 GWT.log("Remove segment");
217 getStore().removeSelectedSegments();
218 }
219 });
220 removeSegmentsButton.setIcon(AbstractImagePrototype.create(AppBundle.INSTANCE.removePoints()));
221 removeSegmentsButton.setToolTip(Format.capitalize(constants.removeSegmentsButton()));
222
223 LayoutContainer c = new LayoutContainer(new ColumnLayout());
224 c.add(addSegmentButton);
225 c.add(insertSegmentButton);
226 c.add(removeSegmentsButton);
227
228 container = new LayoutContainer();
229 cardLayout = new CardLayout();
230 container.setLayout(cardLayout);
231
232 noSelection = new LayoutContainer();
233 noSelection.addText("No selection");
234 noSelection.setLayout(new FitLayout());
235 container.add(noSelection);
236
237 multipleSelection = new LayoutContainer();
238 multipleSelection.addText("Multiple selection");
239 multipleSelection.setLayout(new FitLayout());
240 container.add(multipleSelection);
241
242 LayoutContainer detailPanel = new LayoutContainer();
243 detailPanel.setLayout(new FitLayout());
244 detailPanel.add(container);
245
246 LayoutContainer top = new LayoutContainer();
247 top.setLayout(new VBoxLayout(VBoxLayoutAlign.STRETCH));
248 VBoxLayoutData fl1 = new VBoxLayoutData(new Margins(10, 10, 0, 10));
249 fl1.setFlex(0);
250 top.add(c, fl1);
251 VBoxLayoutData fl2 = new VBoxLayoutData(new Margins(10));
252 fl2.setFlex(1);
253 top.add(grid, fl2);
254
255 BorderLayout layout = new BorderLayout();
256 layout.setEnableState(true);
257 setLayout(layout);
258
259 BorderLayoutData centerData = new BorderLayoutData(LayoutRegion.CENTER);
260 centerData.setMargins(new Margins(0));
261 add(top, centerData);
262
263 BorderLayoutData southData = new BorderLayoutData(LayoutRegion.SOUTH, 100);
264 southData.setMargins(new Margins(0,10,10,10));
265 southData.setSplit(true);
266 southData.setCollapsible(true);
267 add(detailPanel, southData);
268 setScrollMode(Scroll.AUTOY);
269
270 processSelectionChanged(null);
271 }
272
273 public void bind(SVGSegStore store) {
274 grid.reconfigure(store, cm);
275 final GridSelectionModel<SVGSegModel> selectionModel = store.getSelectionModel();
276 selectionModel.addSelectionChangedListener(selChangeProxy);
277 grid.setSelectionModel(selectionModel);
278 selectionModel.refresh();
279 }
280
281 public void unbind() {
282 GridSelectionModel<SVGSegModel> selectionModel = grid.getSelectionModel();
283 if (selectionModel != null) {
284 selectionModel.removeSelectionListener(selChangeProxy);
285 }
286 grid.setSelectionModel(null);
287 }
288
289 @Override
290 public boolean processSelectionChanged(SelectionChangedEvent<SVGSegModel> se) {
291 List<SVGSegModel> models = se != null ? se.getSelection() : Collections.<SVGSegModel>emptyList();
292 GWT.log("PathPanel.selectionChanged: " + models);
293 SVGSegStore store = getStore();
294 insertSegmentButton.setEnabled(store.canInsertSegment());
295 removeSegmentsButton.setEnabled(store.canRemoveSelectedSegments());
296
297 Component panel = noSelection;
298 SVGSegModel model = null;
299 if (currentSection != null) {
300 currentSection.unbind();
301 currentSection = null;
302 }
303 if (models != null) {
304 if (models.size() > 1) {
305 panel = multipleSelection;
306 } else if (models.size() == 1) {
307 model = models.get(0);
308 currentSection = getSection(model);
309 currentSection.bind(model);
310 panel = currentSection.getPanel();
311 }
312 }
313 cardLayout.setActiveItem(panel);
314 return false;
315 }
316
317 private SVGSegStore getStore() {
318 return (SVGSegStore) grid.getStore();
319 }
320
321 private IInspectorSection<SVGSegModel> getSection(SVGSegModel model) {
322 if (metaModelToSection == null) {
323 metaModelToSection = new HashMap<MetaModel<OMSVGPathSeg>, IInspectorSection<SVGSegModel>>();
324 }
325 MetaModel<OMSVGPathSeg> metaModel = model.getMetaModel();
326 IInspectorSection<SVGSegModel> section = metaModelToSection.get(metaModel);
327 if (section == null) {
328 ModelCategory<?> geometry = model.getMetaModel().getCategory(ModelCategory.GEOMETRY);
329 section = geometry.getInspectorSection();
330 metaModelToSection.put(metaModel, section);
331 FormPanel form = (FormPanel)section.getPanel();
332 form.setHeaderVisible(false);
333 container.add(form);
334 }
335 return section;
336 }
337
338 }
339
340 protected static Map<MetaModel<OMSVGPathSeg>, IInspectorSection<SVGSegModel>> metaModelToSection;
341 private PathPanel panel;
342 private ModelCategory<?> category;
343
344 public PathInspectorSection(ModelCategory<?> category) {
345 this.category = category;
346 panel = new PathPanel();
347 }
348 @Override
349 public Component getPanel() {
350 return panel ;
351 }
352 @Override
353 public String toString() {
354 StringBuilder builder = new StringBuilder("PathInspectorSection(");
355 builder.append(category);
356 builder.append(")");
357 return builder.toString();
358 }
359
360 @Override
361 public void bind(SVGPathElementModel model) {
362 GWT.log("PathInspectorSection.bind(" + model + ")");
363 panel.bind(model.getSegStore());
364 }
365
366 @Override
367 public void unbind() {
368 panel.unbind();
369 }
370
371 }
372