/******************************************************************************* * 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; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.TimerTask; import org.eclipse.core.runtime.Assert; import org.eclipse.swt.widgets.Display; import org.eclipse.riena.core.wire.InjectExtension; import org.eclipse.riena.core.wire.Wire; import org.eclipse.riena.internal.ui.ridgets.swt.uiprocess.DefaultProcessDetailComparator; import org.eclipse.riena.internal.ui.ridgets.swt.uiprocess.IProcessDetailComparatorExtension; import org.eclipse.riena.internal.ui.ridgets.swt.uiprocess.ProcessDetail; import org.eclipse.riena.internal.ui.ridgets.swt.uiprocess.TimerUtil; import org.eclipse.riena.ui.core.uiprocess.IProgressVisualizer; import org.eclipse.riena.ui.core.uiprocess.ProcessInfo; import org.eclipse.riena.ui.core.uiprocess.UIProcess; import org.eclipse.riena.ui.ridgets.AbstractRidget; import org.eclipse.riena.ui.ridgets.IStatuslineUIProcessRidget; import org.eclipse.riena.ui.ridgets.IVisualContextManager; import org.eclipse.riena.ui.swt.Statusline; import org.eclipse.riena.ui.swt.StatuslineUIProcess; import org.eclipse.riena.ui.swt.uiprocess.ProcessState; import org.eclipse.riena.ui.swt.uiprocess.ProgressInfoDataObject; /** * Controls the {@link StatuslineUIProcess} (part of {@link Statusline}). */ public class StatuslineUIProcessRidget extends AbstractRidget implements IStatuslineUIProcessRidget { private StatuslineUIProcess uiControl; // the manager organizing all data private final ProcessDetailManager processManager = new ProcessDetailManager(); // the task triggering updates for pending processes private TimerTask timedTrigger; private Display display; public StatuslineUIProcessRidget() { Wire.instance(processManager).andStart(Activator.getDefault().getContext()); timedTrigger = null; } /** * creates the update trigger */ private void buildTrigger() { timedTrigger = new TimedTrigger(); } /** * @return the data manager */ protected ProcessDetailManager getProcessManager() { return processManager; } /** * a filter for {@link ProcessDetail} instances */ interface IProcessDetailFilter { /** * @param procDetail * a {@link ProcessDetail} instance * @return true if we accept the detail */ boolean accept(ProcessDetail procDetail); } /** * manages data of all known {@link UIProcess} instances */ public static class ProcessDetailManager { private final DefaultProcessDetailComparator defaultProcessDetailComparator = new DefaultProcessDetailComparator(); private final List<ProcessDetail> processDetails = Collections.synchronizedList(new ArrayList<ProcessDetail>()); private Comparator<ProcessDetail> processDetailComparator; @InjectExtension(min = 0, max = 1) public void update(final IProcessDetailComparatorExtension extension) { if (extension != null) { processDetailComparator = extension.createComparator(); } } private Comparator<ProcessDetail> getProcessDetailComparator() { return processDetailComparator != null ? processDetailComparator : defaultProcessDetailComparator; } void register(final ProcessDetail detail) { processDetails.add(detail); sort(); } void unregister(final ProcessDetail detail) { processDetails.remove(detail); sort(); } private void sort() { Collections.sort(processDetails, getProcessDetailComparator()); } /** * * @return all detail information about running visualizers */ List<ProcessDetail> getProcessDetails() { return Collections.unmodifiableList(processDetails); } List<ProgressInfoDataObject> getPidos(final IProcessDetailFilter pidoFilter) { final List<ProgressInfoDataObject> pidos = new ArrayList<ProgressInfoDataObject>(); for (final ProcessDetail procDetail : processDetails) { if (pidoFilter.accept(procDetail)) { pidos.add(procDetail.toPido()); } } return pidos; } /** * * @return the {@link ProcessDetail} of the {@link IProgressVisualizer} * currently visualized in the {@link Statusline} */ ProcessDetail getStatuslineRelevant() { if (processDetails.size() > 0) { return getProcessDetails().get(0); } return null; } /** * * @see #getStatuslineRelevant() should the visualizer be shown in the * {@link Statusline}? * * @param visualizer * @return true if it should be visualized in the {@link Statusline} */ boolean isRelevantForStatusline(final IProgressVisualizer visualizer) { return visualizer != null && visualizer.equals(getStatuslineRelevant().getVisualizer()); } /** * convenience method * * @return the {@link IProgressVisualizer}s of all registerd * {@link ProcessDetail}s */ List<IProgressVisualizer> getAlVisualizers() { final List<IProgressVisualizer> visualizers = new ArrayList<IProgressVisualizer>(processDetails.size()); for (final ProcessDetail pDetail : processDetails) { visualizers.add(pDetail.getVisualizer()); } return visualizers; } /** * @param visualizer * @return the {@link ProcessDetail} for the given * {@link IProgressVisualizer} */ ProcessDetail detailForVisualizer(final IProgressVisualizer visualizer) { if (visualizer == null) { return null; } for (final ProcessDetail pDetail : processDetails) { if (visualizer.equals(pDetail.getVisualizer())) { return pDetail; } } // nothing found :-/ return null; } /** * * @return a list containing all <strong>pending</strong> * {@link ProcessDetail} instances. */ List<ProcessDetail> getPending() { final List<ProcessDetail> pending = new ArrayList<ProcessDetail>(); final List<ProcessDetail> processDetailsTmp = new ArrayList<ProcessDetail>(processDetails); for (final ProcessDetail pDetail : processDetailsTmp) { if (pDetail.isPending()) { pending.add(pDetail); } } return pending; } boolean hasPending() { synchronized (processDetails) { for (final ProcessDetail pDetail : processDetails) { if (pDetail.isPending()) { return true; } } } return false; } void updatePending() { // be carful: we are not on the ui thread! sync! synchronized (processDetails) { for (final ProcessDetail pendingDetail : processDetails) { // force a step if (pendingDetail.isPending()) { pendingDetail.triggerPending(); } } } } /** * updates the {@link ProcessDetail} holding the visualizer. * * @param visualizer * the visualizer which is progressed * @param progress * the number of work units done by the {@link UIProcess} */ public void saveProgress(final IProgressVisualizer visualizer, final int progress) { // anybody there? final ProcessDetail pDetail = detailForVisualizer(visualizer); Assert.isNotNull(pDetail, "no ProcessDetail for visualizer " + String.valueOf(visualizer)); //$NON-NLS-1$ // pending? if (pDetail.isPending()) { // no more! pDetail.setState(ProcessState.RUNNING); } int newProgress = 0; if (ProcessInfo.ProgresStrategy.UNIT.equals(visualizer.getProcessInfo().getProgresStartegy())) { newProgress = pDetail.getProgress() + progress; } else { newProgress = progress; } pDetail.setProgress(newProgress); } } /** * standard ridget code */ public StatuslineUIProcess getUIControl() { return uiControl; } public void setUIControl(final Object uiControl) { // type check checkUIControl(uiControl); // unbind predecessor unbindUIControl(); // real set .. this.uiControl = StatuslineUIProcess.class.cast(uiControl); //bind the control bindUIControl(); } protected void checkUIControl(final Object uiControl) { checkType(uiControl, StatuslineUIProcess.class); } protected void bindUIControl() { final StatuslineUIProcess control = getUIControl(); if (control != null) { display = control.getDisplay(); } } protected void unbindUIControl() { display = null; } public String getToolTipText() { return ""; //$NON-NLS-1$ } public boolean hasFocus() { return false; } public boolean isFocusable() { return false; } public boolean isVisible() { return true; } public boolean isEnabled() { return true; } public void requestFocus() { // not focusable } public void setFocusable(final boolean focusable) { // not focusable } public void setToolTipText(final String toolTipText) { // TODO render a tooltip } public void setVisible(final boolean visible) { // always visible } public void setEnabled(final boolean enabled) { // always enabled } public String getID() { return null; } /** * {@link UIProcess} specific code */ public synchronized void updateProgress(final IProgressVisualizer visualizer, final int progress) { // save progress getProcessManager().saveProgress(visualizer, progress); checkTrigger(); //update user interface updateUserInterface(); } private void checkTrigger() { if (!getProcessManager().hasPending() && timedTrigger != null) { TimerUtil.stop(timedTrigger); timedTrigger = null; } } /** * updates the user interface taking care of thread serialization */ private void updateUserInterface() { /* * Consider the client closing while executing this method. The display * may be disposed! */ if (display == null || display.isDisposed()) { return; } if (display.getThread().equals(Thread.currentThread())) { // update on the current thread = user interface thread updateBaseAndList(); return; } // we are not on the user interface thread display.asyncExec(new Runnable() { public void run() { updateBaseAndList(); } }); } /** * updates the controls for the statusline base and the list of * {@link UIProcess}es */ private void updateBaseAndList() { triggerUIBaseUpdate(); triggerUIListUpdate(); } /** * update the base progress in the {@link Statusline} */ private void triggerUIBaseUpdate() { final ProcessDetail pDetailBase = getProcessManager().getStatuslineRelevant(); if (pDetailBase == null) { return; } getUIControl().triggerBaseUpdate(pDetailBase.toPido()); } /** * update the list (user interface) containing all {@link UIProcess}es * excluding base */ private void triggerUIListUpdate() { // determine all infos final List<ProgressInfoDataObject> pidos = getProcessManager().getPidos(new IProcessDetailFilter() { public boolean accept(final ProcessDetail procDetail) { // we do not want the list containing the base info return /* * !getProcessManager().isRelevantForStatusline(procDetail * .getVisualizer()); */true; } }); // tell the ui getUIControl().triggerListUpdate(pidos, true); } public synchronized void finalUpdateUI(final IProgressVisualizer visualizer) { final ProcessDetail detailForVisualizer = getProcessManager().detailForVisualizer(visualizer); if (detailForVisualizer == null) { return; } detailForVisualizer.setState(visualizer.getProcessInfo().isCanceled() ? ProcessState.CANCELED : ProcessState.FINISHED); updateUserInterface(); checkTrigger(); getProcessManager().unregister(getProcessManager().detailForVisualizer(visualizer)); } public void addProgressVisualizer(final IProgressVisualizer visualizer) { } public synchronized void initialUpdateUI(final IProgressVisualizer visualizer, final int totalWork) { createAndRegisterProcessDetail(visualizer); // register timed updater if first time called if (getProcessManager().hasPending() && timedTrigger == null) { buildTrigger(); TimerUtil.schedule(timedTrigger, 0, 200); } } class TimedTrigger extends TimerTask { @Override public void run() { triggerTimedUpdate(); } } private void createAndRegisterProcessDetail(final IProgressVisualizer visualizer) { if (getProcessManager().detailForVisualizer(visualizer) != null) { return; } getProcessManager().register(new ProcessDetail(timeStamp(), visualizer)); } /** * This method should be called by a timer thread */ public void triggerTimedUpdate() { if (getProcessManager().hasPending()) { getProcessManager().updatePending(); updateUserInterface(); } } private static long timeStamp() { return System.currentTimeMillis(); } public void removeProgressVisualizer(final IProgressVisualizer visualizer) { } /* * (non-Javadoc) * * @see * org.eclipse.riena.ui.ridgets.IStatuslineUIProcessRidget#setContextLocator * (org.eclipse.riena.ui.ridgets.IVisualContextManager) */ public void setContextLocator(final IVisualContextManager contextLocator) { // TODO Auto-generated method stub } }