1 /*
2  * Copyright (c) 2016 Martin Davis.
3  *
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * and Eclipse Distribution License v. 1.0 which accompanies this distribution.
7  * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v20.html
8  * and the Eclipse Distribution License is available at
9  *
10  * http://www.eclipse.org/org/documents/edl-v10.php.
11  */
12 package org.locationtech.jtstest.testbuilder;
13 
14 import java.awt.BorderLayout;
15 import java.awt.Color;
16 import java.awt.Component;
17 import java.awt.Dimension;
18 import java.awt.Font;
19 import java.awt.Graphics2D;
20 import java.awt.event.ActionEvent;
21 import java.awt.event.ActionListener;
22 import java.awt.event.MouseAdapter;
23 import java.awt.event.MouseEvent;
24 import java.util.ArrayList;
25 import java.util.List;
26 
27 import javax.swing.BorderFactory;
28 import javax.swing.Box;
29 import javax.swing.BoxLayout;
30 import javax.swing.JButton;
31 import javax.swing.JCheckBox;
32 import javax.swing.JLabel;
33 import javax.swing.JPanel;
34 import javax.swing.JScrollPane;
35 import javax.swing.JTabbedPane;
36 import javax.swing.border.Border;
37 
38 import org.locationtech.jts.geom.Geometry;
39 import org.locationtech.jtstest.testbuilder.model.Layer;
40 import org.locationtech.jtstest.testbuilder.model.LayerList;
41 import org.locationtech.jtstest.testbuilder.model.StaticGeometryContainer;
42 import org.locationtech.jtstest.testbuilder.ui.ColorUtil;
43 import org.locationtech.jtstest.testbuilder.ui.SwingUtil;
44 
45 /**
46  * @version 1.7
47  */
48 public class LayerListPanel extends JPanel {
49 
50   private static final int TAB_INDEX_LAYER = 0;
51 
52   private static final String LBL_LAYER = "Style";
53   private static final String LBL_VIEW = "View";
54 
55   JPanel listPanel = new JPanel();
56   Box buttonPanel = Box.createVerticalBox();
57   JTabbedPane tabPane = new JTabbedPane();
58   private LayerStylePanel lyrStylePanel;
59   List<LayerItemPanel> layerItems = new ArrayList<LayerItemPanel>();
60 
61   private JButton btnCopy;
62 
63   private JButton btnUp;
64 
65   private JButton btnDown;
66 
67   private JButton btnDelete;
68 
69   private JButton btnPaste;
70 
71   private Layer focusLayer;
72 
LayerListPanel()73   public LayerListPanel() {
74     try {
75       uiInit();
76     } catch (Exception ex) {
77       ex.printStackTrace();
78     }
79   }
80 
uiInit()81   private void uiInit() throws Exception {
82     //setSize(300, 250);
83     setBackground(AppColors.BACKGROUND);
84     setLayout(new BorderLayout());
85 
86     JPanel panelLeft = new JPanel();
87     panelLeft.setLayout(new BorderLayout());
88 
89     listPanel.setLayout(new BoxLayout(listPanel, BoxLayout.Y_AXIS));
90     listPanel.setBackground(AppColors.BACKGROUND);
91     listPanel.setBorder(BorderFactory.createEmptyBorder(2,2,2,0));
92 
93     JScrollPane jScrollPane1 = new JScrollPane();
94     jScrollPane1.setBackground(AppColors.BACKGROUND);
95     jScrollPane1.setOpaque(true);
96     jScrollPane1.getViewport().add(listPanel, null);
97     jScrollPane1.setPreferredSize(new Dimension(150, 250));
98 
99     panelLeft.add(jScrollPane1, BorderLayout.CENTER);
100     panelLeft.add(buttonPanel, BorderLayout.EAST);
101 
102     btnCopy = SwingUtil.createButton(AppIcons.ADD,
103         "Copy layer to a new layer",
104             new ActionListener() {
105           public void actionPerformed(ActionEvent e) {
106             layerCopy();
107           }
108         });
109     buttonPanel.add(btnCopy);
110 
111     btnPaste = SwingUtil.createButton(AppIcons.PASTE,
112         "Paste geometry into layer",
113             new ActionListener() {
114           public void actionPerformed(ActionEvent e) {
115             layerPaste(focusLayer);
116           }
117         });
118     buttonPanel.add(btnPaste);
119     btnUp = SwingUtil.createButton(AppIcons.UP,
120         "Move layer up",
121             new ActionListener() {
122           public void actionPerformed(ActionEvent e) {
123             layerUp(focusLayer);
124           }
125         });
126     buttonPanel.add(btnUp);
127     btnDown = SwingUtil.createButton(AppIcons.DOWN,
128         "Move layer down",
129             new ActionListener() {
130           public void actionPerformed(ActionEvent e) {
131             layerDown(focusLayer);
132           }
133         });
134     buttonPanel.add(btnDown);
135 
136     btnDelete = SwingUtil.createButton(AppIcons.DELETE,
137         AppStrings.TIP_LAYER_CLEAR,
138             new ActionListener() {
139           public void actionPerformed(ActionEvent e) {
140             if (SwingUtil.isCtlKeyPressed(e)) {
141               layerDelete(focusLayer);
142             }
143             else {
144               layerClear(focusLayer);
145             }
146          }
147         });
148     buttonPanel.add(btnDelete);
149 
150     add(panelLeft, BorderLayout.WEST);
151 
152     lyrStylePanel = new LayerStylePanel();
153     GeometryViewStylePanel viewStylePanel = new GeometryViewStylePanel();
154     //add(lyrStylePanel, BorderLayout.CENTER);
155 
156     //tabFunctions.setBackground(jTabbedPane1.getBackground());
157     JScrollPane scrollPane = new JScrollPane();
158     scrollPane.getViewport().add(lyrStylePanel, null);
159     tabPane.add(scrollPane,  LBL_LAYER);
160     tabPane.add(viewStylePanel,   LBL_VIEW);
161     add(tabPane, BorderLayout.CENTER);
162   }
163 
findLayerItem(Layer lyr)164   protected LayerItemPanel findLayerItem(Layer lyr) {
165     for (LayerItemPanel lip : layerItems) {
166       if (lip.getLayer() == lyr) return lip;
167     }
168     return null;
169   }
170 
showTabLayerStyle(String title)171   public void showTabLayerStyle(String title) {
172     tabPane.setSelectedIndex(TAB_INDEX_LAYER);
173     //tabPane.setTitleAt(0, LBL_LAYER + " - " + title);
174     //SwingUtil.showTab(tabPane, LBL_LAYER_STYLE);
175   }
176 
populateList()177   public void populateList() {
178     listPanel.removeAll();
179     layerItems.clear();
180 
181     addLayers(JTSTestBuilder.model().getLayersTop());
182     addLayers(JTSTestBuilder.model().getLayers());
183     addLayers(JTSTestBuilder.model().getLayersBase());
184     setLayerFocus(layerItems.get(0));
185   }
186 
updateList()187   public void updateList() {
188     for (LayerItemPanel layerItemPanel : layerItems) {
189       layerItemPanel.update();
190     }
191   }
192 
addLayers(LayerList lyrList)193   private void addLayers(LayerList lyrList) {
194     for (int i = 0; i < lyrList.size(); i++) {
195       Layer lyr = lyrList.getLayer(i);
196       LayerItemPanel item = new LayerItemPanel(lyr, this);
197       listPanel.add(item);
198       layerItems.add(item);
199     }
200   }
201 
setLayerFocus(LayerItemPanel layerItem)202   public void setLayerFocus(LayerItemPanel layerItem) {
203     for (LayerItemPanel item : layerItems) {
204       item.setFocusLayer(false);
205     }
206     layerItem.setFocusLayer(true);
207     Layer layer = layerItem.getLayer();
208     boolean isModifiable = ! JTSTestBuilder.model().isLayerFixed(layer);
209     showTabLayerStyle(layer.getName());
210     lyrStylePanel.setLayer(layer, isModifiable);
211     focusLayer = layer;
212     updateButtons(focusLayer);
213   }
214 
updateButtons(Layer lyr)215   private void updateButtons(Layer lyr) {
216     boolean isModifiable = ! JTSTestBuilder.model().isLayerFixed(lyr);
217 
218     // every layer is copyable
219     btnCopy.setEnabled(true);
220     btnPaste.setEnabled(isModifiable && ! lyr.hasGeometry());
221     btnUp.setEnabled(isModifiable);
222     btnDown.setEnabled(isModifiable);
223     btnDelete.setEnabled(isModifiable);
224   }
225 
layerCopy()226   private void layerCopy() {
227     Layer copy = JTSTestBuilder.model().layerCopy(focusLayer);
228     populateList();
229     setLayerFocus(findLayerItem(copy));
230     JTSTestBuilder.controller().geometryViewChanged();
231   }
232 
layerDelete(Layer lyr)233   private void layerDelete(Layer lyr) {
234     // don't remove if non-empty
235     if (lyr.hasGeometry()) return;
236 
237     JTSTestBuilder.model().layerDelete(lyr);
238     populateList();
239     JTSTestBuilder.controller().geometryViewChanged();
240   }
241 
layerUp(Layer lyr)242   private void layerUp(Layer lyr) {
243     JTSTestBuilder.model().layerUp(lyr);
244     populateList();
245     setLayerFocus(findLayerItem(lyr));
246     JTSTestBuilder.controller().geometryViewChanged();
247   }
248 
layerDown(Layer lyr)249   private void layerDown(Layer lyr) {
250     JTSTestBuilder.model().layerDown(lyr);
251     populateList();
252     setLayerFocus(findLayerItem(lyr));
253     JTSTestBuilder.controller().geometryViewChanged();
254   }
255 
layerClear(Layer lyr)256   private void layerClear(Layer lyr) {
257     StaticGeometryContainer src = (StaticGeometryContainer) lyr.getSource();
258     src.setGeometry(null);
259     updateButtons(focusLayer);
260     JTSTestBuilder.controller().geometryViewChanged();
261   }
262 
layerPaste(Layer lyr)263   protected void layerPaste(Layer lyr) {
264     try {
265       // don't paste into non-empty layers to avoid losing data
266       if (lyr.hasGeometry()) return;
267 
268       Geometry geom = JTSTestBuilder.model().readGeometryFromClipboard();
269       // this will error if layer is not modifiable
270       StaticGeometryContainer src = (StaticGeometryContainer) lyr.getSource();
271       src.setGeometry(geom);
272       updateButtons(focusLayer);
273       JTSTestBuilder.controller().geometryViewChanged();
274     } catch (Exception e) {
275       SwingUtil.reportException(this, e);
276     }
277   }
278 }
279 
280 class LayerItemPanel extends JPanel {
281   private static Font FONT_FOCUS = new java.awt.Font("Dialog", Font.BOLD, 12);
282   private static Font FONT_NORMAL = new java.awt.Font("Dialog", Font.PLAIN, 12);
283 
284   private Border BORDER_CONTROL = BorderFactory.createLineBorder(CLR_CONTROL);
285   private Border BORDER_HIGHLIGHT = BorderFactory.createLineBorder(Color.LIGHT_GRAY);
286 
287   private static final Color CLR_CONTROL = AppColors.BACKGROUND;
288   private static final Color CLR_HIGHLIGHT = ColorUtil.darker(CLR_CONTROL, .95);
289 
290   private Layer layer;
291   private JCheckBox checkbox;
292   private LayerListPanel lyrListPanel;
293   private LayerItemPanel self;
294   private JPanel namePanel;
295   private boolean hasFocus;
296   private JLabel lblName;
297   private JPanel swatch;
298 
LayerItemPanel(Layer lyr, LayerListPanel lyrListPanel)299   LayerItemPanel(Layer lyr, LayerListPanel lyrListPanel) {
300     this.layer = lyr;
301     this.lyrListPanel = lyrListPanel;
302     try {
303       uiInit();
304     } catch (Exception ex) {
305       ex.printStackTrace();
306     }
307     self = this;
308   }
309 
getLayer()310   public Layer getLayer() {
311     return layer;
312   }
313 
setFocusLayer(boolean hasFocus)314   public void setFocusLayer(boolean hasFocus) {
315     setBackground(hasFocus ? AppColors.TAB_FOCUS : AppColors.BACKGROUND);
316     setBorder(hasFocus ? BORDER_HIGHLIGHT : BORDER_CONTROL );
317     lblName.setFont(hasFocus ? FONT_FOCUS : FONT_NORMAL);
318     revalidate();
319     this.hasFocus = hasFocus;
320   }
321 
isFocusLayer()322   public boolean isFocusLayer() {
323     return hasFocus;
324   }
325 
update()326   public void update() {
327     LayerStyleSwatchControl.update(swatch, layer);
328     lblName.setText( layer.getName());
329   }
330 
uiInit()331   private void uiInit() throws Exception {
332     setSize(250, 250);
333     setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
334     setBackground(AppColors.BACKGROUND);
335     //setOpaque(true);
336     setAlignmentX(Component.LEFT_ALIGNMENT);
337     setBorder(BORDER_CONTROL);
338 
339     checkbox = new JCheckBox();
340     add(checkbox);
341     checkbox.setAlignmentX(Component.LEFT_ALIGNMENT);
342     checkbox.addActionListener(new java.awt.event.ActionListener() {
343       public void actionPerformed(ActionEvent e) {
344         layerVisAction();
345       }
346     });
347     checkbox.setSelected(layer.isEnabled());
348     checkbox.setOpaque(false);
349 
350 
351     lblName = new JLabel(layer.getName());
352     lblName.setAlignmentX(Component.LEFT_ALIGNMENT);
353     lblName.setMinimumSize(new Dimension(100,12));
354     lblName.setPreferredSize(new Dimension(100,12));
355     lblName.setMaximumSize(new Dimension(100,12));
356     lblName.setFont(FONT_NORMAL);
357     lblName.addMouseListener(new HighlightMouseListener(this));
358     lblName.addMouseListener(new MouseAdapter()
359     {
360       public void mouseClicked(MouseEvent e)
361       {
362         lyrListPanel.setLayerFocus(self);
363       }
364     });
365 
366     swatch = LayerStyleSwatchControl.create(layer);
367     add(swatch);
368     add(Box.createRigidArea(new Dimension(4,0)));
369 
370     namePanel = new JPanel();
371     add(namePanel);
372     namePanel.setLayout(new BoxLayout(namePanel, BoxLayout.X_AXIS));
373     //namePanel.setBackground(CLR_CONTROL);
374     namePanel.setOpaque(false);
375     namePanel.setAlignmentX(Component.LEFT_ALIGNMENT);
376     namePanel.setMinimumSize(new Dimension(100,14));
377     namePanel.setPreferredSize(new Dimension(100,14));
378     namePanel.setMaximumSize(new Dimension(100,14));
379     //namePanel.setBorder(BORDER_HIGHLIGHT);;
380     namePanel.addMouseListener(new HighlightMouseListener(this));
381 
382     namePanel.add(lblName);
383   }
384 
layerVisAction()385   private void layerVisAction() {
386     boolean isVisible = checkbox.isSelected();
387     layer.setEnabled(isVisible);
388     repaint();
389     JTSTestBuilder.controller().geometryViewChanged();
390   }
391 
392   class HighlightMouseListener extends MouseAdapter {
393     private LayerItemPanel comp;
394 
HighlightMouseListener(LayerItemPanel comp)395     HighlightMouseListener(LayerItemPanel comp) {
396       this.comp = comp;
397     }
398 
mouseEntered(MouseEvent e)399     public void mouseEntered(MouseEvent e) {
400       if (comp.isFocusLayer()) return;
401       comp.setBackground(CLR_HIGHLIGHT);
402       //comp.setBorder(BORDER_HIGHLIGHT);
403       comp.revalidate();
404     }
405 
mouseExited(MouseEvent e)406     public void mouseExited(MouseEvent e) {
407       if (comp.isFocusLayer()) return;
408       comp.setBackground(AppColors.BACKGROUND);
409       //comp.setBorder(BORDER_CONTROL);
410       comp.revalidate();
411    }
412   }
413 
414 }
415 
416 
417 class LayerStyleSwatchControl {
418 
create(Layer layer)419   public static JPanel create(Layer layer) {
420     JPanel ctl = new JPanel();
421     Dimension dim = new Dimension(16,16);
422     ctl.setMinimumSize(dim);
423     ctl.setPreferredSize(dim);
424     ctl.setMaximumSize(dim);
425     ctl.setOpaque(true);
426     //update(ctl, layer);
427     return ctl;
428   }
429 
update(JPanel ctl, Layer layer)430   public static void update(JPanel ctl, Layer layer) {
431 
432     Color fillClr = Color.WHITE;
433     if (layer.getGeometryStyle().isFilled())
434       fillClr = layer.getGeometryStyle().getFillColor();
435 
436     int lineWidth = 1;
437     if (layer.getGeometryStyle().getStrokeWidth() > 1)
438       lineWidth = 2;
439 
440     /*
441     Graphics2D gr = (Graphics2D) ctl.getGraphics();
442     gr.setColor(fillClr);
443     gr.setPaint(fillClr);
444     gr.fillRect(0, 0, 10, 10);
445 */
446     ctl.setBackground( fillClr );
447     ctl.setBorder(BorderFactory.createLineBorder(layer.getGeometryStyle().getLineColor(), lineWidth));
448   }
449 }
450