From 8cd0a3254fe26fa0485f25d64c608dafedf3903c Mon Sep 17 00:00:00 2001 From: Nils Christian Ehmke <nie@informatik.uni-kiel.de> Date: Thu, 30 May 2013 13:04:52 +0200 Subject: [PATCH] Further development of the cockpit editor. --- .../beans/view/CurrentCockpitEditorBean.java | 288 ++++++++++++------ .../main/webapp/pages/CockpitEditorPage.xhtml | 10 +- 2 files changed, 208 insertions(+), 90 deletions(-) diff --git a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentCockpitEditorBean.java b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentCockpitEditorBean.java index b473990d..e5b1db14 100644 --- a/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentCockpitEditorBean.java +++ b/Kieker.WebGUI/src/main/java/kieker/webgui/web/beans/view/CurrentCockpitEditorBean.java @@ -16,18 +16,28 @@ package kieker.webgui.web.beans.view; +import java.awt.Point; import java.io.IOException; +import java.util.ArrayList; import java.util.Collection; import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; import java.util.List; import java.util.Map; +import javax.annotation.Nullable; import javax.faces.application.Application; import javax.faces.application.FacesMessage; import javax.faces.component.UIComponent; import javax.faces.component.UIInput; import javax.faces.component.html.HtmlOutputText; import javax.faces.context.FacesContext; +import javax.faces.event.AbortProcessingException; +import javax.faces.event.AjaxBehaviorEvent; + +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; import kieker.analysis.model.analysisMetaModel.MIAnalysisMetaModelFactory; import kieker.analysis.model.analysisMetaModel.MIDisplay; @@ -52,15 +62,20 @@ import kieker.webgui.web.beans.application.GlobalPropertiesBean; import kieker.webgui.web.beans.application.ProjectsBean; import kieker.webgui.web.beans.session.UserBean; +import org.primefaces.component.behavior.ajax.AjaxBehavior; +import org.primefaces.component.behavior.ajax.AjaxBehaviorListenerImpl; import org.primefaces.component.dashboard.Dashboard; import org.primefaces.component.panel.Panel; import org.primefaces.context.RequestContext; +import org.primefaces.event.DashboardReorderEvent; import org.primefaces.event.TabChangeEvent; import org.primefaces.model.DashboardColumn; import org.primefaces.model.DashboardModel; import org.primefaces.model.DefaultDashboardColumn; import org.primefaces.model.DefaultDashboardModel; +import org.eclipse.emf.common.util.EList; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @@ -79,7 +94,7 @@ public class CurrentCockpitEditorBean { private static final int NUMBER_COLUMNS = 2; private static final Log LOG = LogFactory.getLog(CurrentCockpitEditorBean.class); - private final MIAnalysisMetaModelFactory factory = new MAnalysisMetaModelFactory(); + private final MIAnalysisMetaModelFactory factory = MAnalysisMetaModelFactory.eINSTANCE; @Autowired private IProjectService projectService; @@ -157,38 +172,50 @@ public class CurrentCockpitEditorBean { final Application application = fc.getApplication(); // Add a panel for every display connector we have - final List<MIDisplayConnector> displayConnectors = this.activeView.getDisplayConnectors(); this.currId = 0; - for (final MIDisplayConnector connector : displayConnectors) { - final Panel panel = (Panel) application.createComponent(fc, "org.primefaces.component.Panel", "org.primefaces.component.PanelRenderer"); - panel.setId("displayConnector_" + Integer.toString(this.connectors.get(connector))); - panel.setHeader(connector.getName()); - panel.setClosable(true); - panel.setToggleable(false); - - this.getDashboard().getChildren().add(panel); - final DashboardColumn column = this.dashboard.getModel().getColumn(0); - column.addWidget(panel.getId()); - final HtmlOutputText text = new HtmlOutputText(); - text.setValue(connector.getDisplay().getName()); - - panel.getChildren().add(text); - this.currId++; + + for (int col = 0; col < 2; col++) { + final Collection<MIDisplayConnector> displayConnectors = this.getSortedDisplayConnectors(this.activeView.getDisplayConnectors(), col); + final DashboardColumn column = this.dashboard.getModel().getColumn(col); + + for (final MIDisplayConnector connector : displayConnectors) { + final Panel panel = (Panel) application.createComponent(fc, "org.primefaces.component.Panel", "org.primefaces.component.PanelRenderer"); + panel.setId("displayConnector_" + Integer.toString(this.connectors.get(connector))); + panel.setHeader(connector.getName()); + panel.setClosable(true); + panel.setToggleable(false); + + final AjaxBehavior behaviour = new AjaxBehavior(); + behaviour.setProcess("@this"); + behaviour.addAjaxBehaviorListener(new AjaxBehaviorListenerImpl() { + + @Override + public void processAjaxBehavior(final AjaxBehaviorEvent event) throws AbortProcessingException { + System.out.println(((Panel) event.getSource()).getId()); + } + + }); + panel.addClientBehavior("close", behaviour); + + this.getDashboard().getChildren().add(panel); + + column.addWidget(panel.getId()); + final HtmlOutputText text = new HtmlOutputText(); + text.setValue(connector.getDisplay().getName()); + + panel.getChildren().add(text); + this.currId++; + } } } } - /** - * Clears the dashboard and removes all children within it. - */ - private void clearDashboard() { - // Run through all columns of the dashboard and remove the items - final List<DashboardColumn> columns = this.dashboard.getModel().getColumns(); - for (final DashboardColumn column : columns) { - column.getWidgets().clear(); - } - // Now run through the dashboard itself and remove the items as well - this.dashboard.getChildren().clear(); + private Collection<MIDisplayConnector> getSortedDisplayConnectors(final EList<MIDisplayConnector> eList, final int col) { + final List<MIDisplayConnector> l = new ArrayList<MIDisplayConnector>(Collections2.filter(eList, new ColumnPredicate(col))); + + Collections.sort(l, new PosComparator()); + + return l; } /** @@ -206,6 +233,9 @@ public class CurrentCockpitEditorBean { // Remember the current time! This is important for the later comparison of the time stamps. this.resetTimeStamp(); this.reloadComponents(); + + this.stringToLayout(this.projectService.getCockpitLayout(this.projectName)); + // Update the class loader and the specific classes used within various methods in this bean this.fillDashboard(); } @@ -221,18 +251,10 @@ public class CurrentCockpitEditorBean { } } - /** - * Reloads the available components within this bean. - */ private void reloadComponents() { this.availableComponents = this.projectService.getAvailableComponents(this.projectName); } - /** - * This method delivers the project stored in this bean. - * - * @return The project for this user. - */ public MIProject getProject() { return this.project; } @@ -246,21 +268,10 @@ public class CurrentCockpitEditorBean { return Collections.nCopies(3, null); } - /** - * This method sets the project stored within this bean and returns the new page for the navigation. - * - * @param newName - * The name of the project. - */ public void setProjectName(final String newName) { this.projectName = newName; } - /** - * This method delivers the project name stored in this bean. - * - * @return The project name for this user. - */ public String getProjectName() { return this.projectName; } @@ -310,7 +321,7 @@ public class CurrentCockpitEditorBean { public void saveProject(final boolean overwriteNewerProject) { try { this.projectService.saveProject(this.projectName, this.project, this.timeStamp, overwriteNewerProject, this.userBean.getUsername(), null, - this.projectService.getCockpitLayout(this.projectName)); + this.layoutToString()); GlobalPropertiesBean.showMessage(FacesMessage.SEVERITY_INFO, this.globalPropertiesBean.getMsgProjectSaved()); // Update the time stamp! this.resetTimeStamp(); @@ -420,7 +431,7 @@ public class CurrentCockpitEditorBean { final Application application = fc.getApplication(); final Panel panel = (Panel) application.createComponent(fc, "org.primefaces.component.Panel", "org.primefaces.component.PanelRenderer"); - panel.setId("displayConnector_" + Integer.toString(this.connectors.get(connector))); + panel.setId(this.displayConnectorToID(connector)); panel.setHeader(connector.getName()); panel.setClosable(true); panel.setToggleable(false); @@ -435,6 +446,33 @@ public class CurrentCockpitEditorBean { this.currId++; this.setModificationsFlag(); + + this.saveLayoutOfCurrentView(); + } + } + + private static final String ID_PREFIX = "displayConnector_"; + + final Map<MIView, Map<MIDisplayConnector, Point>> connectorPositions = new HashMap<MIView, Map<MIDisplayConnector, Point>>(); + + public void handleReorder(final DashboardReorderEvent event) { + this.saveLayoutOfCurrentView(); + } + + /** + * This method is used as a validator for new display connector names. + * + * @param context + * The context of the validation. + * @param toValidate + * The components which has be validated. + * @param value + * The new value. + */ + public void validateDisplayConnectorName(final FacesContext context, final UIComponent toValidate, final Object value) { + if ((value instanceof String) && (toValidate instanceof UIInput)) { + final boolean nameExists = this.existsDisplayConnectorName((String) value); + ((UIInput) toValidate).setValid(!nameExists); } } @@ -465,6 +503,26 @@ public class CurrentCockpitEditorBean { } } + /** + * Getter for the property {@link CurrentCockpitEditorBean#dashboard}. + * + * @return The current value of the property. + */ + public Dashboard getDashboard() { + return this.dashboard; + } + + /** + * Setter for the property {@link CurrentCockpitEditorBean#dashboard}. + * + * @param dashboard + * The new value for the property. + */ + public void setDashboard(final Dashboard dashboard) { + this.dashboard = dashboard; + this.dashboard.setModel(this.dashboardModel); + } + /** * This method checks whether a display connector with the given name exists already. * @@ -491,50 +549,108 @@ public class CurrentCockpitEditorBean { return result; } - /** - * This method is used as a validator for new display connector names. - * - * @param context - * The context of the validation. - * @param toValidate - * The components which has be validated. - * @param value - * The new value. - */ - public void validateDisplayConnectorName(final FacesContext context, final UIComponent toValidate, final Object value) { - if ((value instanceof String) && (toValidate instanceof UIInput)) { - final boolean nameExists = this.existsDisplayConnectorName((String) value); - ((UIInput) toValidate).setValid(!nameExists); + private void clearDashboard() { + // Run through all columns of the dashboard model and remove the items + final List<DashboardColumn> columns = this.dashboard.getModel().getColumns(); + for (final DashboardColumn column : columns) { + column.getWidgets().clear(); } + // Now remove the items from the dashboard + this.dashboard.getChildren().clear(); } - /** - * Getter for the property {@link CurrentCockpitEditorBean#dashboard}. - * - * @return The current value of the property. - */ - public Dashboard getDashboard() { - return this.dashboard; + private void saveLayoutOfCurrentView() { + if (this.activeView != null) { + final Map<MIDisplayConnector, Point> layout = new HashMap<MIDisplayConnector, Point>(); + + // Run through all columns and put the position of every widget into the map + int col = 0; + for (final DashboardColumn column : this.dashboardModel.getColumns()) { + int row = 0; + for (final String widgetID : column.getWidgets()) { + layout.put(this.idToDisplayConnector(widgetID), new Point(col, row)); + row++; + } + col++; + } + + // Store the map for the whole view + this.connectorPositions.put(this.activeView, layout); + } } - /** - * Setter for the property {@link CurrentCockpitEditorBean#dashboard}. - * - * @param dashboard - * The new value for the property. - */ - public void setDashboard(final Dashboard dashboard) { - this.dashboard = dashboard; - this.dashboard.setModel(this.dashboardModel); + private void stringToLayout(final String layout) { + final String[] elements = layout.split(" "); + int i = 0; + + for (final MIView view : this.project.getViews()) { + final Map<MIDisplayConnector, Point> positions = new HashMap<MIDisplayConnector, Point>(); + this.connectorPositions.put(view, positions); + for (final MIDisplayConnector connector : view.getDisplayConnectors()) { + final Point pos = new Point(Integer.valueOf(elements[i]), Integer.valueOf(elements[i + 1])); + + positions.put(connector, pos); + + i += 2; + } + } + } - /** - * Delivers the available components. - * - * @return A list with the available components. - */ - public ComponentListContainer getAvailableComponents() { - return this.availableComponents; + private String layoutToString() { + final StringBuilder builder = new StringBuilder(); + + for (final MIView view : this.project.getViews()) { + final Map<MIDisplayConnector, Point> positions = this.connectorPositions.get(view); + for (final MIDisplayConnector connector : view.getDisplayConnectors()) { + final Point pos = positions.get(connector); + + builder.append(pos.x); + builder.append(' '); + builder.append(pos.y); + builder.append(' '); + } + } + + return builder.toString(); + } + + private String displayConnectorToID(final MIDisplayConnector displayConnector) { + return ID_PREFIX + this.connectors.get(displayConnector); + } + + private MIDisplayConnector idToDisplayConnector(final String id) { + final String shortID = id.substring(ID_PREFIX.length()); + final int intID = Integer.valueOf(shortID); + + return this.connectors.get(intID); + } + + private class PosComparator implements Comparator<MIDisplayConnector> { + + @Override + public int compare(final MIDisplayConnector o1, final MIDisplayConnector o2) { + final int pos1 = CurrentCockpitEditorBean.this.connectorPositions.get(CurrentCockpitEditorBean.this.activeView).get(o1).y; + final int pos2 = CurrentCockpitEditorBean.this.connectorPositions.get(CurrentCockpitEditorBean.this.activeView).get(o2).y; + + return pos1 - pos2; + } + + } + + private class ColumnPredicate implements Predicate<MIDisplayConnector> { + + private final int col; + + public ColumnPredicate(final int col) { + this.col = col; + } + + @Override + public boolean apply(@Nullable final MIDisplayConnector key) { + return CurrentCockpitEditorBean.this.connectorPositions.get(CurrentCockpitEditorBean.this.activeView).get(key).x == this.col; + } + } } diff --git a/Kieker.WebGUI/src/main/webapp/pages/CockpitEditorPage.xhtml b/Kieker.WebGUI/src/main/webapp/pages/CockpitEditorPage.xhtml index d8bb83b7..cddfd5c7 100644 --- a/Kieker.WebGUI/src/main/webapp/pages/CockpitEditorPage.xhtml +++ b/Kieker.WebGUI/src/main/webapp/pages/CockpitEditorPage.xhtml @@ -36,7 +36,7 @@ <p:remoteCommand autoRun="false" name="nodeSelected" action="#{currentCockpitEditorBean.nodeSelected()}" update=":propertiesForm" /> </h:form> </ui:define> - + <ui:define name="js"> <!-- This javascript code will be executed in the onload-part of the body and shows a localized message via the growl-component. --> <script> @@ -73,7 +73,9 @@ nodeSelected([{name : 'id', value : event.currentTarget.id}]); }); </script> - <p:dashboard id="dynamicDashboard" binding="#{currentCockpitEditorBean.dashboard}"/> + <p:dashboard id="dynamicDashboard" binding="#{currentCockpitEditorBean.dashboard}"> + <p:ajax event="reorder" listener="#{currentCockpitEditorBean.handleReorder}"/> + </p:dashboard> </ui:fragment> </h:form> </ui:define> @@ -143,13 +145,13 @@ </ui:define> <ui:define name="furtherDialogIncludes"> - <p:confirmDialog id="confirmDialog" message="You have unsaved changed on your page. Do you really want to continue?" header="Unsaved Changes" severity="alert" widgetVar="closeConfirmation" > + <p:confirmDialog id="confirmDialog" message="You have unsaved changed on your page. Do you really want to continue?" header="Unsaved Changes" severity="alert" widgetVar="closeConfirmation" > <h:form> <p:commandButton id="confirm" value="#{localizedMessages.yes}" ajax="false" oncomplete="closeConfirmation.hide()" action="ProjectOverviewPage.xhtml?faces-redirect=true" /> <p:commandButton id="decline" value="#{localizedMessages.cancel}" onclick="closeConfirmation.hide()" type="button" /> </h:form> </p:confirmDialog> - + <!-- Include the dialogs for the views. --> <ui:include src="../dialogs/CockpitEditorPageDialogs.xhtml" /> </ui:define> -- GitLab