/* Copyright (c) 2010, skobbler GmbH * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its * contributors may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package org.openstreetmap.josm.plugins.mapdust.gui; import static org.openstreetmap.josm.tools.I18n.tr; import java.awt.BorderLayout; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.JPanel; import javax.swing.JTabbedPane; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.gui.dialogs.ToggleDialog; import org.openstreetmap.josm.plugins.mapdust.MapdustPlugin; import org.openstreetmap.josm.plugins.mapdust.gui.component.panel.MapdustActionPanel; import org.openstreetmap.josm.plugins.mapdust.gui.component.panel.MapdustBugListPanel; import org.openstreetmap.josm.plugins.mapdust.gui.component.panel.MapdustBugPropertiesPanel; import org.openstreetmap.josm.plugins.mapdust.gui.observer.MapdustActionObserver; import org.openstreetmap.josm.plugins.mapdust.gui.observer.MapdustBugDetailsObservable; import org.openstreetmap.josm.plugins.mapdust.gui.observer.MapdustBugDetailsObserver; import org.openstreetmap.josm.plugins.mapdust.gui.observer.MapdustUpdateObservable; import org.openstreetmap.josm.plugins.mapdust.gui.observer.MapdustUpdateObserver; import org.openstreetmap.josm.plugins.mapdust.gui.value.MapdustAction; import org.openstreetmap.josm.plugins.mapdust.gui.value.MapdustPluginState; import org.openstreetmap.josm.plugins.mapdust.gui.value.MapdustServiceCommand; import org.openstreetmap.josm.plugins.mapdust.service.value.MapdustBug; import org.openstreetmap.josm.plugins.mapdust.service.value.MapdustBugFilter; import org.openstreetmap.josm.plugins.mapdust.service.value.Status; import org.openstreetmap.josm.tools.Shortcut; /** * This class is the main graphical user interface class. * * @author Bea */ public class MapdustGUI extends ToggleDialog implements MapdustActionObserver, MapdustBugDetailsObservable, MapdustUpdateObservable { /** The serial version UID */ private static final long serialVersionUID = -1194197412364335190L; /** The list of MapDust bug details observers */ private final ArrayList<MapdustBugDetailsObserver> bugDetailsObservers = new ArrayList<>(); /** The list of MapDust initial update observers */ private final ArrayList<MapdustUpdateObserver> initialUpdateObservers = new ArrayList<>(); /** The <code>MapdustPlugin</code> plug-in */ private MapdustPlugin mapdustPlugin; /** The <code>MapdustBugPropertiesPanel</code> */ private MapdustBugPropertiesPanel detailsPanel; /** The <code>MapdustBugListPanel</code> object */ private MapdustBugListPanel panel; /** The <code>MapdustActionPanel</code> object */ private MapdustActionPanel actionPanel; /** The <code>JTabbedPanel</code> object */ private JTabbedPane tabbedPane; /** The <code>JPanel</code> */ private JPanel mainPanel; /** Specifies if the MapDust data was or not downloaded */ private boolean downloaded; /** Remembers if the dialog has been destroyed */ private boolean destroyed; /** * Builds a <code>MapdustGUi</code> based on the given parameters. * * @param name The name of the GUI * @param iconName The name of the icon * @param tooltip The tool tip * @param shortcut The shortcut * @param preferredHeight The height * @param mapdustPlugin The <code>MapdustPlugin</code> object */ public MapdustGUI(String name, String iconName, String tooltip, Shortcut shortcut, int preferredHeight, MapdustPlugin mapdustPlugin) { super(tr(name), iconName, tr(tooltip), shortcut, preferredHeight); this.mapdustPlugin = mapdustPlugin; } /** * Displays the <code>MapdustGUI</code> dialog window in the JOSM editor. If * the MapDust data was not down-loaded yet or the MapDust layer was added * after a previous deletion, then the bug reports data will be deleted. */ @Override public void showDialog() { if (!downloaded) { notifyObservers(null, true); downloaded = true; } super.showDialog(); } /** * Destroys the <code>MapdustGUI</code> dialog window. */ @Override public void destroy() { setVisible(false); /* remove panels */ if (tabbedPane != null) { /* from off-line to online */ remove(mainPanel); tabbedPane = null; actionPanel = null; mainPanel = null; panel = null; actionPanel = null; } else { /* from online to off-line */ if (mainPanel != null) { remove(mainPanel); mainPanel = null; panel = null; detailsPanel = null; } } downloaded = false; button.setSelected(false); if (!destroyed) { super.destroy(); destroyed = true; } } @Override protected void toggleButtonHook() { if (isVisible()) { setVisible(false); button.setSelected(false); } else { setVisible(true); } } /** * Adds the given <code>MapdustAction</code> object to the list of actions. * * @param action The <code>MapdustAction</code> object */ @Override public synchronized void addAction(MapdustAction action) { /* add the action */ List<MapdustAction> actionList = actionPanel.getActionList(); actionList.add(action); List<MapdustBug> mapdustBugs = panel.getMapdustBugsList(); boolean showBug = shouldDisplay(action.getMapdustBug(), mapdustPlugin.getFilter()); mapdustBugs = modifyBug(mapdustBugs, action.getMapdustBug(), showBug); /* update panels */ updateMapdustPanel(mapdustBugs); updateMapdustActionPanel(actionList); if (showBug && !action.getCommand().equals(MapdustServiceCommand.ADD_BUG)) { panel.resetSelectedBug(0); } else { mapdustPlugin.getMapdustLayer().setBugSelected(null); } revalidate(); Main.map.mapView.revalidate(); Main.map.repaint(); } /** * Verifies if the given <code>MapdustBug</code> should be shown on the * MapDust bug list/ map. Any <code>MapdustBug</code> should be shown on the * MapDust bug list / map only if it is permitted by the selected filters. * * @param modifiedBug The <code>MapdustBug</code> object * @param filter The <code>MapdustBugFilter</code> object * @return true if the given MapDust bug should be shown in the list/map * false otherwise */ private boolean shouldDisplay(MapdustBug modifiedBug, MapdustBugFilter filter) { boolean result = false; if (filter != null && filter.getStatuses() != null && !filter.getStatuses().isEmpty()) { if (filter.getStatuses().contains(modifiedBug.getStatus().getKey())) { result = true; } } else { result = true; } return result; } /** * Updates the MapDust GUI with the given list of <code>MapdustBug</code> * objects. * * @param mapdustBugs The list of <code>MapdustBug</code> objects * @param mapdustPlugin The <code>MapdustPlugin</code> object */ public synchronized void update(List<MapdustBug> mapdustBugs, MapdustPlugin mapdustPlugin) { setMapdustPlugin(mapdustPlugin); String pluginState = Main.pref.get("mapdust.pluginState"); if (pluginState.equals(MapdustPluginState.ONLINE.getValue())) { if (tabbedPane != null) { /* from off-line to online */ remove(mainPanel); tabbedPane = null; actionPanel = null; mainPanel = null; panel = null; } updateMapdustPanel(mapdustBugs); if (mainPanel == null) { createMainPanel(); } } else { if (tabbedPane == null) { /* from online to off-line */ remove(mainPanel); mainPanel = null; panel = null; } List<MapdustAction> actionList = actionPanel != null ? actionPanel.getActionList() : new ArrayList<>(); /* update panels */ List<MapdustBug> bugs = filterMapdustBugList(mapdustBugs, actionList, mapdustPlugin.getFilter()); updateMapdustPanel(bugs); updateMapdustActionPanel(actionList); if (mainPanel == null) { createMainPanel(); } } } /** * Updates the MapDust bugs panel with the new list of data. * * @param mapdustBugs The list of <code>MapdustBug</code> objects */ private void updateMapdustPanel(List<MapdustBug> mapdustBugs) { MapdustBug selectedBug = (mapdustBugs != null && mapdustBugs.size() > 0) ? mapdustBugs.get(0) : null; if (detailsPanel == null) { detailsPanel = new MapdustBugPropertiesPanel(selectedBug); addObserver(detailsPanel); } if (panel == null) { panel = new MapdustBugListPanel(mapdustBugs, "Bug reports", mapdustPlugin); panel.addObserver(detailsPanel); } else { panel.updateComponents(mapdustBugs); notifyObservers(selectedBug); } } /** * Updates the MapDust action panel with the new list of data. * * @param actionList The list of <code>MapdustAction</code> objects */ private void updateMapdustActionPanel(List<MapdustAction> actionList) { if (actionPanel == null) { actionPanel = new MapdustActionPanel(actionList, "Offline Contribution", mapdustPlugin); } else { actionPanel.updateComponents(actionList); } } /** * Creates the main panel of the plug-in and adds to the content pane. */ private void createMainPanel() { mainPanel = new JPanel(); mainPanel.setAutoscrolls(true); mainPanel.setLayout(new BorderLayout()); mainPanel.add(detailsPanel, BorderLayout.NORTH); if (actionPanel == null) { mainPanel.add(panel, BorderLayout.CENTER); } else { tabbedPane = new JTabbedPane(); tabbedPane.add(panel, 0); tabbedPane.add(actionPanel); mainPanel.add(tabbedPane, BorderLayout.CENTER); } createLayout(mainPanel, false, null); } /** * Filters the given list of <code>MapdustBug</code>s based on the given * list of <code>MapdustAction</code>s. The filtering is done in order to * show the modified bugs ( but not already committed operations) if they * also appears in the new list of bugs, according to the latest * modifications. * * @param bugList The list of <code>MapdustBug</code> objects * @param actionList The list of <code>MapdustAction</code> objects * @param filter The <code>MapdustBugFilter</code> object * @return A filtered list of <code>MapdustBug</code>s */ private List<MapdustBug> filterMapdustBugList(List<MapdustBug> bugList, List<MapdustAction> actionList, MapdustBugFilter filter) { if (bugList != null && actionList != null) { for (MapdustAction action : actionList) { int index = bugList.indexOf(action.getMapdustBug()); if (index >= 0) { if (action.getNewStatus() != null) { Status newStatus = Status.getStatus(action.getNewStatus()); if (filter != null && filter.getStatuses() != null && !filter.getStatuses().isEmpty()) { if (filter.getStatuses().contains( newStatus.getKey())) { bugList.get(index).setStatus(newStatus); } else { bugList.remove(index); } } else { bugList.get(index).setStatus(newStatus); } } } } } return bugList; } /** * Modifies the given <code>MapdustBug</code> in the given list of * <code>MapdustBug</code> objects. Returns the list of bugs containing the * modified bug. * * @param mapdustBugs The list of <code>MapdustBug</code> objects * @param modifiedBug The <code>MapdustBug</code> object * @param showBug A flag indicating if the given modified bug should be * displayed on the map and on the list of bugs * @return the modified list */ private List<MapdustBug> modifyBug(List<MapdustBug> mapdustBugs, MapdustBug modifiedBug, boolean showBug) { int index = -1; for (int i = 0; i < mapdustBugs.size(); i++) { if (modifiedBug.getId() != null) { if (mapdustBugs.get(i).getId().equals(modifiedBug.getId())) { index = i; } } } if (index != -1) { /* remove, and add to the top of the list */ mapdustBugs.remove(index); if (showBug) { mapdustBugs.add(0, modifiedBug); } } return mapdustBugs; } /** * Returns the selected bug from the list of bugs. If there is no bug * selected then the returned result is null. * * @return The selected bug */ public MapdustBug getSelectedBug() { MapdustBug selectedBug = null; if (panel != null) { selectedBug = panel.getSelectedBug(); } return selectedBug; } /** * Sets the given <code>MapdustBug</code> to be selected from the list of * bugs. * * @param mapdustBug The <code>MapdustBug</code> object */ public void setSelectedBug(MapdustBug mapdustBug) { if (panel != null) { panel.setSelectedBug(mapdustBug); } } /** * Disables the buttons from the <code>MapdustPanel</code>. * */ public void disableBtnPanel() { if (panel != null) { panel.disableBtnPanel(); } } /** * Enables the basic components from the <code>MapdustButtonPanel</code>. * Basic components are considered the following buttons: work off-line, * filter bug report, and refresh.If the onlyBasic flag is true then the * other buttons will be disabled. * * @param onlyBasic If true then the not basic buttons will be disabled */ public void enableBtnPanel(boolean onlyBasic) { if (panel != null) { panel.enableBtnPanel(onlyBasic); } } /** * Returns the list of <code>MapdustAction</code> objects. * * @return list of <code>MapdustAction</code> */ public List<MapdustAction> getMapdustActionList() { return getActionPanel().getActionList(); } /** * Adds a new MapDust bug details observer to the list of observers. * * @param observer The <code>MapdustBugDetailsObserver</code> object */ @Override public void addObserver(MapdustBugDetailsObserver observer) { if (!this.bugDetailsObservers.contains(observer)) { this.bugDetailsObservers.add(observer); } } /** * Adds a new MapDust update observer to the list of observers. * * @param observer The <code>MapdustUpdateObserver</code> object */ @Override public void addObserver(MapdustUpdateObserver observer) { if (!this.initialUpdateObservers.contains(observer)) { this.initialUpdateObservers.add(observer); } } /** * Removes the given MapDust bug details observer from the list of * observers. * * @param observer The <code>MapdustBugDetailsObserver</code> object */ @Override public void removeObserver(MapdustBugDetailsObserver observer) { this.bugDetailsObservers.remove(observer); } /** * Removes the given MapDust initial update observer from the list of * observers. * * @param observer The <code>MapdustInitialUpdateObserver</code> object */ @Override public void removeObserver(MapdustUpdateObserver observer) { this.initialUpdateObservers.remove(observer); } /** * Notifies the <code>MapdustBugDetailsObserver</code> objects observing the * given <code>MapdustBug</code> object. */ @Override public void notifyObservers(MapdustBug mapdustBug) { Iterator<MapdustBugDetailsObserver> elements = this.bugDetailsObservers.iterator(); while (elements.hasNext()) { (elements.next()).showDetails(mapdustBug); } } /** * Notifies the <code>MapdustInitialUpdateObserver</code> objects waiting * for the initial down-load, and update of plug-in. */ @Override public void notifyObservers(MapdustBugFilter filter, boolean first) { Iterator<MapdustUpdateObserver> elements = this.initialUpdateObservers.iterator(); while (elements.hasNext()) { (elements.next()).update(filter, first); } } /** * Returns the <code>MapdustPanel</code> object * * @return the panel */ public MapdustBugListPanel getPanel() { return panel; } /** * Returns the <code>MapdustActionPanel</code> object * * @return the queuePanel */ public MapdustActionPanel getActionPanel() { return actionPanel; } /** * Returns the <code>MapdustPlugin</code> object * * @return the mapdustPlugin */ public MapdustPlugin getMapdustPlugin() { return mapdustPlugin; } /** * Sets the <code>MapdustPlugin</code> object * * @param mapdustPlugin the mapdustPlugin to set */ public void setMapdustPlugin(MapdustPlugin mapdustPlugin) { this.mapdustPlugin = mapdustPlugin; } /** * Returns the down-loaded flag * * @return the down-loaded */ public boolean isDownloaded() { return downloaded; } }