/******************************************************************************* * Copyright (c) 2008 Dennis Schenk, Peter Siska. * 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: * Dennis Schenk - initial implementation * Peter Siska - initial implementation * Gerry Weirich . modifications for API Change in 2.1 (ElexisEventDispatcher) *******************************************************************************/ package ch.unibe.iam.scg.archie.ui.views; import java.util.ArrayList; import org.eclipse.core.runtime.jobs.IJobChangeEvent; import org.eclipse.core.runtime.jobs.IJobChangeListener; import org.eclipse.jface.action.IToolBarManager; import org.eclipse.swt.SWT; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.ui.part.ViewPart; import ch.elexis.core.data.events.ElexisEvent; import ch.elexis.core.data.events.ElexisEventDispatcher; import ch.elexis.core.data.events.ElexisEventListener; import ch.elexis.core.ui.UiDesk; import ch.elexis.data.Anwender; import ch.unibe.iam.scg.archie.ArchieActivator; import ch.unibe.iam.scg.archie.acl.ArchieACL; import ch.unibe.iam.scg.archie.actions.CreateChartsAction; import ch.unibe.iam.scg.archie.actions.RefreshChartsAction; import ch.unibe.iam.scg.archie.i18n.Messages; import ch.unibe.iam.scg.archie.model.MutexRule; import ch.unibe.iam.scg.archie.ui.DashboardOverview; import ch.unibe.iam.scg.archie.ui.GraphicalMessage; import ch.unibe.iam.scg.archie.ui.charts.AbstractChartComposite; import ch.unibe.iam.scg.archie.ui.charts.AbstractDatasetCreator; import ch.unibe.iam.scg.archie.ui.charts.AgeHistogrammChart; import ch.unibe.iam.scg.archie.ui.charts.ConsultationMoneyChart; import ch.unibe.iam.scg.archie.ui.charts.ConsultationNumberChart; import ch.unibe.iam.scg.archie.ui.charts.PatientsConsHistChart; /** * <p> * The Dashboard View gives a general Overview of the Elexis System. E.g. How * many patients and consultations are in the system, what is the age * distribution of patients etc. * </p> * * $Id: Dashboard.java 774 2010-01-29 05:47:10Z gerry.weirich $ * * @author Peter Siska * @author Dennis Schenk * @version $Rev: 774 $ */ public class Dashboard extends ViewPart implements IJobChangeListener, ElexisEventListener { /** * ID of this view. */ public static final String ID = ArchieActivator.PLUGIN_ID + ".ui.views.Dashboard"; /** * List of chart composites in this dashboard. */ private ArrayList<AbstractChartComposite> charts; /** * Composite container of all parts in this view. */ private Composite container; /** * Upper part of the dashboard containing the system overview. */ private DashboardOverview overview; /** * The bottom part of the view. */ private Composite bottomPart; /** * Initial message about the not created charts. */ private Composite chartsNotCreatedMessage; /** * Internal variable to count the number of finished chart creator jobs. */ private int jobCounter; /** * Action for refreshing the already created charts (and overview). */ private RefreshChartsAction refreshChartsAction; /** * This action creates the charts initially. */ private CreateChartsAction createChartsAction; /** * Creates a Dashboard */ public Dashboard() { this.charts = new ArrayList<AbstractChartComposite>(4); this.jobCounter = 0; ElexisEventDispatcher.getInstance().addListeners(this); } // //////////////////////////////////////////////////////////////////////////// // PRIVATE HELPER METHODS // //////////////////////////////////////////////////////////////////////////// /** * Initializes the dashboard. This method is also called when a UserChanged * event is propagated to redraw the contents of the dashboard according to * the current user's access permissions. */ private void initialize() { // Create according to ACL if (ArchieACL.userHasAccess()) { this.initializeParts(); this.initializeChartsNotCreatedMessage(); } else { this.cancelAllCreators(); this.initializeAccessDisabled(); } this.container.layout(); } /** * Add actions to this view. */ private void addActions() { this.createChartsAction = new CreateChartsAction(this); this.refreshChartsAction = new RefreshChartsAction(this); IToolBarManager manager = this.getViewSite().getActionBars().getToolBarManager(); manager.add(this.createChartsAction); manager.add(this.refreshChartsAction); } /** * Cancels all running jobs that have been started by the chart's creators. */ private void cancelAllCreators() { for (AbstractChartComposite chart : this.charts) { chart.cancelCreator(); } } /** * Initialize charts in the given parent. * * @param container */ private void initializeCharts() { GridLayout layout = new GridLayout(); layout.numColumns = 2; layout.marginHeight = 0; layout.marginWidth = 0; layout.makeColumnsEqualWidth = true; this.bottomPart.setLayout(layout); this.bottomPart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); this.charts.add(new ConsultationNumberChart(this.bottomPart, SWT.NONE)); this.charts.add(new ConsultationMoneyChart(this.bottomPart, SWT.NONE)); this.charts.add(new PatientsConsHistChart(this.bottomPart, SWT.NONE)); this.charts.add(new AgeHistogrammChart(this.bottomPart, SWT.NONE)); // register job change listener for (AbstractChartComposite chart : this.charts) { chart.addJobChangeListener(this); } // layout container this.bottomPart.layout(); } /** * Initializes the access disabled message. */ private void initializeAccessDisabled() { this.container.setLayout(new GridLayout()); this.container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); new GraphicalMessage(this.container, ArchieActivator.getImage(ArchieActivator.IMG_ERROR), Messages.ACL_ACCESS_DENIED); } /** * Initializes the parts of this dashboard. The dashboard consists of two * parts, one upper part that has the a dashboard overview, a lower part * containing either a message about the status of the charts or the charts * themselves. */ private void initializeParts() { this.overview = new DashboardOverview(this.container, SWT.NONE); this.bottomPart = new Composite(this.container, SWT.NONE); GridLayout layout = new GridLayout(); this.container.setLayout(layout); this.container.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); this.bottomPart.setLayout(layout); this.bottomPart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); this.overview.setLayout(layout); this.overview.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); assert (this.overview != null); assert (this.bottomPart != null); } /** * Initializes the charts not created yet message. */ private void initializeChartsNotCreatedMessage() { assert (this.bottomPart != null); this.chartsNotCreatedMessage = new GraphicalMessage(this.bottomPart, ArchieActivator.getImage(ArchieActivator.IMG_INFO), Messages.DASHBOARD_CHARTS_NOT_CREATED); } /** * Removes the charts not created message container if available. */ private void clearChartsNotCreatedMessage() { if (this.chartsNotCreatedMessage != null && !this.chartsNotCreatedMessage.isDisposed()) { this.chartsNotCreatedMessage.dispose(); } } // //////////////////////////////////////////////////////////////////////////// // PUBLIC METHODS // //////////////////////////////////////////////////////////////////////////// /** * Redraws the charts in the dashboard. */ public void redrawCharts() { this.jobCounter = 0; for (AbstractChartComposite chart : this.charts) { chart.refresh(); } } /** * Triggers the update mechanism of the upper part of the dashboard * containing the system overview. This function should be called when the * overview should refresh (e.g. after DB changes) without having to restart * the program. */ public void updateOverview() { this.overview.refresh(); } /** * This method starts the chart creation. It starts running the jobs of each * chart container creator on demand. */ public void createCharts() { // dispose message if set this.clearChartsNotCreatedMessage(); this.initializeCharts(); MutexRule rule = new MutexRule(); for (AbstractChartComposite chart : this.charts) { AbstractDatasetCreator creator = chart.getCreator(); creator.setRule(rule); creator.schedule(); } } // //////////////////////////////////////////////////////////////////////////// // INTERFACE AND OVERRIDING METHODS // //////////////////////////////////////////////////////////////////////////// /** * @see org.eclipse.ui.part.WorkbenchPart#createPartControl(org.eclipse.swt.widgets.Composite) */ @Override public void createPartControl(Composite composite) { this.container = composite; this.addActions(); this.initialize(); } private void userChanged() { // reset job counter this.jobCounter = 0; // remove job listeners and clear charts for (AbstractChartComposite chart : this.charts) { chart.removeJobChangeListener(this); } this.charts.clear(); // Dispose any children if available if (this.container != null) { for (Control child : this.container.getChildren()) { child.dispose(); } } this.initialize(); // re-initialize // reset action states this.refreshChartsAction.setEnabled(false); this.createChartsAction.setEnabled(true); } /** * * @see org.eclipse.core.runtime.jobs.IJobChangeListener#done(org.eclipse.core * .runtime.jobs.IJobChangeEvent) */ public void done(IJobChangeEvent event) { // allow other threads to update this UI thread // http://www.eclipse.org/swt/faq.php#uithread UiDesk.getDisplay().syncExec(new Runnable() { public void run() { Dashboard.this.refreshChartsAction.setEnabled(++Dashboard.this.jobCounter == Dashboard.this.charts .size()); } }); } /** * {@inheritDoc} */ public void scheduled(IJobChangeEvent event) { // allow other threads to update this UI thread // http://www.eclipse.org/swt/faq.php#uithread UiDesk.getDisplay().syncExec(new Runnable() { public void run() { Dashboard.this.createChartsAction.setEnabled(false); } }); } // //////////////////////////////////////////////////////////////////////////// // UNUSED INTERFACE METHODS // //////////////////////////////////////////////////////////////////////////// /** * @see org.eclipse.ui.part.WorkbenchPart#setFocus() */ @Override public void setFocus() { // Nothing here... } /** * {@inheritDoc} */ public void aboutToRun(IJobChangeEvent event) { // Nothing here... } /** * {@inheritDoc} */ public void awake(IJobChangeEvent event) { // Nothing here... } /** * {@inheritDoc} */ public void running(IJobChangeEvent event) { // Nothing here... } /** * {@inheritDoc} */ public void sleeping(IJobChangeEvent event) { // Nothing here... } /** * @{inheritDoc */ public void catchElexisEvent(ElexisEvent ev) { userChanged(); } /** * @{inheritDoc */ private final ElexisEvent eetmpl = new ElexisEvent(null, Anwender.class, ElexisEvent.EVENT_USER_CHANGED); public ElexisEvent getElexisEventFilter() { return eetmpl; } }