/******************************************************************************* * Copyright (c) 2007, 2014 compeople AG and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * compeople AG - initial API and implementation *******************************************************************************/ package org.eclipse.riena.internal.ui.ridgets.swt.uiprocess; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Shell; import org.eclipse.riena.ui.core.uiprocess.IProgressVisualizer; import org.eclipse.riena.ui.core.uiprocess.ProcessInfo; import org.eclipse.riena.ui.ridgets.AbstractRidget; import org.eclipse.riena.ui.ridgets.IContextUpdateListener; import org.eclipse.riena.ui.ridgets.IUIProcessRidget; import org.eclipse.riena.ui.ridgets.IVisualContextManager; import org.eclipse.riena.ui.ridgets.swt.AbstractSWTRidget; import org.eclipse.riena.ui.ridgets.uibinding.IBindingPropertyLocator; import org.eclipse.riena.ui.swt.uiprocess.ICancelListener; import org.eclipse.riena.ui.swt.uiprocess.UIProcessControl; import org.eclipse.riena.ui.swt.utils.SWTBindingPropertyLocator; /** * The {@link UIProcessRidget} is not a standard {@link AbstractSWTRidget} as it * does not bind a {@link Control} but a {@link UIProcessControl}. Another * difference is that it does not hold any detail state of the uiProcessControl. */ public class UIProcessRidget extends AbstractRidget implements IUIProcessRidget { private UIProcessControl uiProcessControl; private final CancelListener cancelListener; private final Map<IProgressVisualizer, Progress> visualizerProgress; private final Map<Object, VisualizerContainer> contexts; private IVisualContextManager contextLocator; private boolean focusAble; public UIProcessRidget() { cancelListener = new CancelListener(); visualizerProgress = new HashMap<IProgressVisualizer, Progress>(); contexts = new HashMap<Object, VisualizerContainer>(); } static class ContextDataComparator implements Comparator<VisualizerContainer> { public int compare(final VisualizerContainer o1, final VisualizerContainer o2) { final int time1 = getVisualizerTime(o1); final int time2 = getVisualizerTime(o2); if (time1 > time2) { return -1; } if (time1 == time2) { return 0; } return 1; } private int getVisualizerTime(final VisualizerContainer data) { return data.get(data.getCurrentVisualizer()); } } //get the container of the active context if exists private VisualizerContainer getActiveContextContainer() { final List<VisualizerContainer> data = getActiveContextContainerList(); Collections.sort(data, new ContextDataComparator()); if (data.size() > 0) { return data.get(0); } return null; } // get the list of active contexts private List<VisualizerContainer> getActiveContextContainerList() { final List<Object> activeContexts = getActiveContexts(); final List<VisualizerContainer> data = new ArrayList<VisualizerContainer>(); for (final Object object : activeContexts) { data.add(contexts.get(object)); } return data; } private List<Object> getActiveContexts() { return getContextLocator().getActiveContexts(new LinkedList<Object>(contexts.keySet())); } /* * holds the progress of a visualized UiProcess */ private final static class Progress { private int totalWork = -1; private int completed = 0; private Progress() { super(); } } private void showProcessing() { getUIControl().showProcessing(); } /** * open the window controlled by the ridget */ public void open() { getUIControl().start(); updateUi(); } /** * close the window controlled by the ridget */ public void close() { getUIControl().stop(); } private class CancelListener implements ICancelListener { public void canceled(final boolean windowClosed) { if (getCurrentVisualizer() != null) { cancelCurrentVisualizer(windowClosed); } } private void cancelCurrentVisualizer(final boolean windowClosed) { if (windowClosed) { cancelAllVisualizersInContext(); } else { getCurrentProcessInfo().cancel(); } if (isLonelyVisualizer(getCurrentVisualizer()) && !windowClosed) { close(); } removeProgressVisualizer(getCurrentVisualizer()); } private void cancelAllVisualizersInContext() { // clean up final List<VisualizerContainer> activeContextDataList = getActiveContextContainerList(); for (final VisualizerContainer visualizerContextData : activeContextDataList) { for (final IProgressVisualizer visualizer : visualizerContextData.keySet()) { visualizer.getProcessInfo().cancel(); } } } } protected void bindUIControl() { if (getUIControl() != null) { uiProcessControl.addCancelListener(cancelListener); } } public void setUIControl(final Object uiControl) { checkUIControl(uiControl); unbindUIControl(); uiProcessControl = (UIProcessControl) uiControl; bindUIControl(); } protected void checkUIControl(final Object uiControl) { checkType(uiControl, UIProcessControl.class); } protected void unbindUIControl() { if (getUIControl() != null) { getUIControl().removeCancelListener(cancelListener); } } public void addProgressVisualizer(final IProgressVisualizer visualizer) { final Object context = visualizer.getProcessInfo().getContext(); // is there any contextData for this context? (any other visualizers for // the same context?) VisualizerContainer contextData = contexts.get(context); if (contextData == null) { // create Container for context to hold all visualizers for the // context contextData = new VisualizerContainer(); contexts.put(context, contextData); contextLocator.addContextUpdateListener(contextChangeHandler, context); } // save when the visualizers was started saveVisualizerStartupTime(visualizer, contextData); // create the Progress Object to save progress and total work for the // visualizer createVisualizerProgress(visualizer); // observe processInfo changes(description, title, ..) observeProcessInfo(visualizer.getProcessInfo()); } private final ContextChangeHandler contextChangeHandler = new ContextChangeHandler(); // saves the bounds of the window whenever the execution context changes private class ContextChangeHandler implements IContextUpdateListener { public boolean contextUpdated(final Object context) { checkContexts(); return false; } public void beforeContextUpdate(final Object context) { // save the bounds in all context parts final List<Object> activeContexts = getActiveContexts(); for (final Object subContext : activeContexts) { saveBounds(subContext); } } } private void checkContexts() { if (getActiveContexts().size() == 0) { close(); } else { final IProgressVisualizer currentVisualizer = getCurrentVisualizer(); // if this is a user-job, show the ProgressWindow if (currentVisualizer != null && currentVisualizer.getProcessInfo().isDialogVisible()) { open(); Object currentContext = null; for (final Entry<Object, VisualizerContainer> entry : contexts.entrySet()) { final VisualizerContainer container = entry.getValue(); if (container != null && container.getCurrentVisualizer() == currentVisualizer) { currentContext = entry.getKey(); break; } } if (currentContext != null) { getUIControl().getWindow().getShell().setBounds(contexts.get(currentContext).getBounds()); } final int progress = getProgress(currentVisualizer).completed; if (progress <= 0) { showProcessing(); } } else { showProcessing(); } } } /** * @see IProgressVisualizer#finalUpdateUI() */ public void finalUpdateUI(final IProgressVisualizer visualizer) { if (!visualizer.getProcessInfo().isDialogVisible()) { return; } // if it�s the only visualizer for the current context: close window if (isActive(visualizer) && isLonelyVisualizer(visualizer)) { visualizer.getProcessInfo().setIgnoreCancel(true); close(); } } // is the visualizer the only one? private boolean isLonelyVisualizer(final IProgressVisualizer visualizer) { final List<VisualizerContainer> activeContextContainerList = getActiveContextContainerList(); if (activeContextContainerList.size() == 0) { return true; } int count = 0; for (final VisualizerContainer visualizerContainer : activeContextContainerList) { count += visualizerContainer.size(); if (count > 1) { return false; } } return true; } /** * @see IProgressVisualizer#initialUpdateUI(int) */ public void initialUpdateUI(final IProgressVisualizer visualizer, final int totalWork) { if (!visualizer.getProcessInfo().isDialogVisible()) { return; } if (isActive(visualizer)) { // all this makes sense if the visualizers is part of one of the // active contexts open(); showProcessing(); saveTotalWork(visualizer, totalWork); getProgress(visualizer).completed = 0; updateUi(); } } private void createVisualizerProgress(final IProgressVisualizer visualizer) { // for progress data (total & completed work) visualizerProgress.put(visualizer, new Progress()); } // save the time when tihe visualizer has been first seen private void saveVisualizerStartupTime(final IProgressVisualizer visualizer, final VisualizerContainer contextData) { final long time = System.currentTimeMillis(); contextData.put(visualizer, Integer.valueOf((int) time)); } private void observeProcessInfo(final ProcessInfo processInfo) { processInfo.addPropertyChangeListener(getProcessInfoListener()); } private PropertyChangeListener processInfoListener; private PropertyChangeListener getProcessInfoListener() { if (processInfoListener == null) { processInfoListener = new ProcessInfoListener(); } return processInfoListener; } /* * observes the processInfo of a visualizer */ private class ProcessInfoListener implements PropertyChangeListener { public void propertyChange(final PropertyChangeEvent evt) { final ProcessInfo pInfo = ProcessInfo.class.cast(evt.getSource()); if (isActive(pInfo) && !ProcessInfo.PROPERTY_CANCELED.equals(evt.getPropertyName())) { updateUi(); } } private boolean isActive(final ProcessInfo info) { return getCurrentVisualizer() != null && getCurrentVisualizer().getProcessInfo().equals(info); } } /* * the current visualizer is part of the list of active contexts at the * specific point in time. the current visualizer is the last one added to * the merged list of visualizers in all contexts. */ protected IProgressVisualizer getCurrentVisualizer() { final VisualizerContainer activeContextContainer = getActiveContextContainer(); if (activeContextContainer != null) { return activeContextContainer.getCurrentVisualizer(); } return null; } /* * update ui but take care of disposed widgets! */ private void updateUi() { final Shell windowShell = getWindowShell(); if (windowShell != null) { windowShell.getDisplay().syncExec(new Runnable() { public void run() { final Shell shell = getWindowShell(); if (shell != null && !shell.isDisposed()) { getUIControl().setDescription(getCurrentProcessInfo().getNote()); getUIControl().setTitle(getCurrentProcessInfo().getTitle()); getUIControl().setCancelVisible(getCurrentProcessInfo().isCancelVisible()); getUIControl().setCancelEnabled(getCurrentProcessInfo().isCancelEnabled()); getUIControl().pack(); // show the progress reinitializeProgress(); } } private void reinitializeProgress() { final int progress = visualizerProgress.get(getCurrentVisualizer()).completed; if (progress <= -1) { showProcessing(); } } }); } } private Shell getWindowShell() { final Shell shell = getUIControl().getWindow().getShell(); return shell; } // get the info for the current visualizer private ProcessInfo getCurrentProcessInfo() { return getCurrentVisualizer().getProcessInfo(); } private void saveTotalWork(final IProgressVisualizer visualizer, final int totalWork) { this.visualizerProgress.get(visualizer).totalWork = totalWork; } /** * cleanly remove the visualizer from the it�s container and update user * interface */ public void removeProgressVisualizer(final IProgressVisualizer visualizer) { if (visualizer == null) { return; } removeVisualizerFromContextData(visualizer); removeVisualizerProgress(visualizer); cleanContext(); if (getCurrentVisualizer() != null) { updateUi(); } //remove context listener to avoid memory leaks final Object context = visualizer.getProcessInfo().getContext(); contextLocator.removeContextUpdateListener(contextChangeHandler, context); } private void cleanContext() { final Iterator<VisualizerContainer> contextIter = contexts.values().iterator(); while (contextIter.hasNext()) { final VisualizerContainer container = contextIter.next(); if (container != null && container.size() == 0) { contextIter.remove(); } } } private void removeVisualizerProgress(final IProgressVisualizer visualizer) { visualizerProgress.remove(visualizer); } private void removeVisualizerFromContextData(final IProgressVisualizer visualizer) { final Collection<VisualizerContainer> contextDataKeys = contexts.values(); for (final VisualizerContainer contextData : contextDataKeys) { contextData.remove(visualizer); } } protected boolean isActive(final IProgressVisualizer visualizer) { return visualizer != null && visualizer == getCurrentVisualizer(); } /** * @see IProgressVisualizer#updateProgress(int) */ public void updateProgress(final IProgressVisualizer visualizer, final int progress) { if (!visualizer.getProcessInfo().isDialogVisible()) { return; } saveProgress(visualizer, progress); if (isActive(visualizer)) { getUIControl().showProgress(getProgress(visualizer).completed, getTotalWork(visualizer)); } } // cache the progress of a visualizer private void saveProgress(final IProgressVisualizer visualizer, final int progressValue) { final Progress progress = getProgress(visualizer); if (progress != null) { if (ProcessInfo.ProgresStrategy.UNIT.equals(visualizer.getProcessInfo().getProgresStartegy())) { progress.completed += progressValue; } else { progress.completed = progressValue; } } } private Progress getProgress(final IProgressVisualizer visualizer) { return visualizerProgress.get(visualizer); } private Integer getTotalWork(final IProgressVisualizer visualizer) { return getProgress(visualizer).totalWork; } private void saveBounds(final Object visualContext) { if (visualContext != null) { final Shell shell = getUIControl().getWindow().getShell(); if (shell != null) { contexts.get(visualContext).setBounds(shell.getBounds()); } } } public void setContextLocator(final IVisualContextManager contextLocator) { this.contextLocator = contextLocator; } public IVisualContextManager getContextLocator() { return contextLocator; } /* * (non-Javadoc) * * @see org.eclipse.riena.ui.ridgets.IRidget#getToolTipText() */ public String getToolTipText() { if (getWindowShell() != null && !getWindowShell().isDisposed() && isFocusable()) { return getWindowShell().getToolTipText(); } return ""; //$NON-NLS-1$ } /* * (non-Javadoc) * * @see org.eclipse.riena.ui.ridgets.IRidget#getUIControl() */ public UIProcessControl getUIControl() { return uiProcessControl; } /** * answers true if the dialog window has the focus. otherwise false */ public boolean hasFocus() { if (getWindowShell() != null && !getWindowShell().isDisposed() && isFocusable()) { return getWindowShell().isFocusControl(); } return false; } public boolean isFocusable() { return focusAble; } public void setFocusable(final boolean focusable) { this.focusAble = focusable; } /** * answers true if the dialog window is visible. otherwise false */ public boolean isVisible() { if (getWindowShell() != null && !getWindowShell().isDisposed()) { return getWindowShell().isVisible(); } return false; } /** * answers true if the dialog window is enabled. otherwise false */ public boolean isEnabled() { if (getWindowShell() != null && !getWindowShell().isDisposed()) { return getWindowShell().isEnabled(); } return false; } /** * request focus of window */ public void requestFocus() { if (getWindowShell() != null && !getWindowShell().isDisposed() && isFocusable()) { getWindowShell().forceFocus(); } } public void setToolTipText(final String toolTipText) { if (getWindowShell() != null && !getWindowShell().isDisposed()) { getWindowShell().setToolTipText(toolTipText); } } /** * controls the visibility of the dialog */ public void setVisible(final boolean visible) { if (visible) { open(); } else { close(); } } /** * controls the enabled state of the managed dialog window */ public void setEnabled(final boolean enabled) { if (getWindowShell() != null && !getWindowShell().isDisposed()) { getWindowShell().setEnabled(enabled); } } public String getID() { final IBindingPropertyLocator locator = SWTBindingPropertyLocator.getInstance(); return locator.locateBindingProperty(getUIControl()); } }