/**
* Copyright (C) 2001-2017 by RapidMiner and the contributors
*
* Complete list of developers available at our web site:
*
* http://rapidminer.com
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU Affero General Public License as published by the Free Software Foundation, either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License along with this program.
* If not, see http://www.gnu.org/licenses/.
*/
package com.rapidminer.gui;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import com.rapidminer.gui.flow.ProcessPanel;
import com.rapidminer.gui.processeditor.NewOperatorEditor;
import com.rapidminer.gui.processeditor.results.ResultDisplay;
import com.rapidminer.gui.properties.OperatorPropertyPanel;
import com.rapidminer.repository.gui.RepositoryBrowser;
import com.rapidminer.tools.AbstractObservable;
import com.vlsolutions.swing.docking.DockingConstants;
import com.vlsolutions.swing.docking.ws.WSDesktop;
import com.vlsolutions.swing.docking.ws.WSDockKey;
/**
* The {@link PerspectiveModel} is managed by a {@link PerspectiveController} and stores all
* necessary information about the registered perspectives. This model can notify listeners/
* observers about perspective changes and new registered perspectives.
*
* @author Marcel Michel
* @since 7.0.0
*/
public class PerspectiveModel extends AbstractObservable<List<Perspective>> {
public static final String RESULT = "result";
public static final String DESIGN = "design";
private final Map<String, Perspective> perspectives = new LinkedHashMap<>();
private Perspective selectedPerspective;
private LinkedList<PerspectiveChangeListener> perspectiveChangeListenerList;
/**
* Creates a new perspective, and possibly switches to this new perspective immediately. The new
* perspective will be a copy of the current one.
*
* @throws IllegalArgumentException
* if name is already used
*/
public Perspective addPerspective(final String name, final boolean userDefined) {
final Perspective p = new Perspective(this, name);
if (!isValidName(name)) {
throw new IllegalArgumentException("Invalid or duplicate view name: " + name);
}
p.setUserDefined(userDefined);
perspectives.put(name, p);
fireUpdate(new ArrayList<Perspective>(perspectives.values()));
return p;
}
/**
* Removes the given perspective by name from the model.
*
* @param name
* the name of the perspective which should be removed
*/
public void deletePerspective(final String name) {
if (perspectives.containsKey(name)) {
deletePerspective(perspectives.get(name));
}
}
/**
* Removes the given perspective from the model.
*
* @param name
* the perspective which should be removed
*/
public void deletePerspective(final Perspective p) {
if (!p.isUserDefined()) {
return;
}
perspectives.remove(p.getName());
p.delete();
fireUpdate(new ArrayList<>(perspectives.values()));
}
/**
* Adds the default perspectives to the model.
*/
public void makePredefined() {
addPerspective(DESIGN, false);
restoreDefault(DESIGN);
addPerspective(RESULT, false);
restoreDefault(RESULT);
}
/**
* Restores the default layout of the perspectives. This method only works for predefined
* perspectives (like {@link #DESIGN} and {@link #RESULT}).
*
* @param perspectiveName
* the name of the perspective which should be restored
*
* @throws IllegalAccessException
* if the perspective is not known
*/
public void restoreDefault(String perspectiveName) {
WSDockKey processPanelKey = new WSDockKey(ProcessPanel.PROCESS_PANEL_DOCK_KEY);
WSDockKey propertyTableKey = new WSDockKey(OperatorPropertyPanel.PROPERTY_EDITOR_DOCK_KEY);
WSDockKey resultsKey = new WSDockKey(ResultDisplay.RESULT_DOCK_KEY);
WSDockKey repositoryKey = new WSDockKey(RepositoryBrowser.REPOSITORY_BROWSER_DOCK_KEY);
WSDockKey newOperatorEditorKey = new WSDockKey(NewOperatorEditor.NEW_OPERATOR_DOCK_KEY);
WSDockKey operatorHelpKey = new WSDockKey(OperatorDocumentationBrowser.OPERATOR_HELP_DOCK_KEY);
if (DESIGN.equals(perspectiveName)) {
Perspective designPerspective = getPerspective(DESIGN);
WSDesktop designDesktop = designPerspective.getWorkspace().getDesktop(0);
designDesktop.clear();
designDesktop.addDockable(processPanelKey);
designDesktop.split(processPanelKey, propertyTableKey, DockingConstants.SPLIT_RIGHT, 0.8);
designDesktop.split(propertyTableKey, operatorHelpKey, DockingConstants.SPLIT_BOTTOM, .66);
designDesktop.split(processPanelKey, repositoryKey, DockingConstants.SPLIT_LEFT, 0.25);
designDesktop.split(repositoryKey, newOperatorEditorKey, DockingConstants.SPLIT_BOTTOM, 0.5);
} else if (RESULT.equals(perspectiveName)) {
Perspective resultPerspective = getPerspective(RESULT);
WSDesktop resultsDesktop = resultPerspective.getWorkspace().getDesktop(0);
resultsDesktop.clear();
resultsDesktop.addDockable(resultsKey);
resultsDesktop.split(resultsKey, repositoryKey, DockingConstants.SPLIT_RIGHT, 0.8);
} else {
throw new IllegalArgumentException("Not a predefined view: " + perspectiveName);
}
}
/**
* Gets a perspective by name.
*
* @param name
* the name of the perspective
* @return the resolved {@link Perspective}
* @throws NoSuchElementException
* if the perspective is not known
*/
public Perspective getPerspective(final String name) {
Perspective result = perspectives.get(name);
if (result != null) {
return result;
} else {
throw new NoSuchElementException("No such view: " + name);
}
}
/**
* Getter for all registered perspectives
*
* @return all perspectives as {@link List}
*/
public List<Perspective> getAllPerspectives() {
return new ArrayList<>(perspectives.values());
}
/**
* Registers a new {@link PerspectiveChangeListener}.
*
* @param listener
* the listener which should be notified about perspective changes.
*/
public void addPerspectiveChangeListener(final PerspectiveChangeListener listener) {
if (listener == null) {
return;
}
if (perspectiveChangeListenerList == null) {
perspectiveChangeListenerList = new LinkedList<>();
}
perspectiveChangeListenerList.add(listener);
}
/**
* Removes the given {@link PerspectiveChangeListener} from the listener list.
*
* @param listener
* the listener which should be removed
* @return {@code true} if the listener could be removed, otherwise {@code false}
*/
public boolean removePerspectiveChangeListener(final PerspectiveChangeListener listener) {
if (perspectiveChangeListenerList == null) {
return false;
}
return perspectiveChangeListenerList.remove(listener);
}
/**
* Getter for the current selected perspective.
*
* @return the selected perspective
*/
public Perspective getSelectedPerspective() {
return selectedPerspective;
}
/**
* Updates the selected perspective and notifies the {@link PerspectiveChangeListener}.
*
* @param name
* the name of the new selected perspective
*/
public void setSelectedPerspective(String name) {
if (perspectives.containsKey(name)) {
setSelectedPerspective(perspectives.get(name));
}
}
/**
* Updates the selected perspective and notifies the {@link PerspectiveChangeListener}.
*
* @param perspective
* the new selected perspective
*/
public void setSelectedPerspective(Perspective perspective) {
if (selectedPerspective == perspective) {
return;
}
selectedPerspective = perspective;
this.notifyChangeListener();
}
/**
* Checks if the given string is valid as name of a new perspective.
*
* @param name
* @return validity
*/
public boolean isValidName(final String name) {
if (name == null) {
return false;
}
if (name.trim().isEmpty()) {
return false;
}
for (Perspective perspective : perspectives.values()) {
if (perspective.getName().toLowerCase().equals(name.toLowerCase())) {
return false;
}
}
return true;
}
/**
* Notifies the registered {@link PerspectiveChangeListener}s about the
* {@link #selectedPerspective}.
*/
public void notifyChangeListener() {
// do not fire these in the EDT
new Thread(new Runnable() {
@Override
public void run() {
if (perspectiveChangeListenerList != null) {
LinkedList<PerspectiveChangeListener> list = new LinkedList<>(perspectiveChangeListenerList);
for (PerspectiveChangeListener listener : list) {
listener.perspectiveChangedTo(selectedPerspective);
}
}
}
}).start();
}
}