/*******************************************************************************
* CogTool Copyright Notice and Distribution Terms
* CogTool 1.3, Copyright (c) 2005-2013 Carnegie Mellon University
* This software is distributed under the terms of the FSF Lesser
* Gnu Public License (see LGPL.txt).
*
* CogTool is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* CogTool 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CogTool; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* CogTool makes use of several third-party components, with the
* following notices:
*
* Eclipse SWT version 3.448
* Eclipse GEF Draw2D version 3.2.1
*
* Unless otherwise indicated, all Content made available by the Eclipse
* Foundation is provided to you under the terms and conditions of the Eclipse
* Public License Version 1.0 ("EPL"). A copy of the EPL is provided with this
* Content and is also available at http://www.eclipse.org/legal/epl-v10.html.
*
* CLISP version 2.38
*
* Copyright (c) Sam Steingold, Bruno Haible 2001-2006
* This software is distributed under the terms of the FSF Gnu Public License.
* See COPYRIGHT file in clisp installation folder for more information.
*
* ACT-R 6.0
*
* Copyright (c) 1998-2007 Dan Bothell, Mike Byrne, Christian Lebiere &
* John R Anderson.
* This software is distributed under the terms of the FSF Lesser
* Gnu Public License (see LGPL.txt).
*
* Apache Jakarta Commons-Lang 2.1
*
* This product contains software developed by the Apache Software Foundation
* (http://www.apache.org/)
*
* jopt-simple version 1.0
*
* Copyright (c) 2004-2013 Paul R. Holser, Jr.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Mozilla XULRunner 1.9.0.5
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/.
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The J2SE(TM) Java Runtime Environment version 5.0
*
* Copyright 2009 Sun Microsystems, Inc., 4150
* Network Circle, Santa Clara, California 95054, U.S.A. All
* rights reserved. U.S.
* See the LICENSE file in the jre folder for more information.
******************************************************************************/
package edu.cmu.cs.hcii.cogtool.view;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Label;
import edu.cmu.cs.hcii.cogtool.model.APredictionResult;
import edu.cmu.cs.hcii.cogtool.model.AUndertaking;
import edu.cmu.cs.hcii.cogtool.model.CognitiveModelGenerator;
import edu.cmu.cs.hcii.cogtool.model.Design;
import edu.cmu.cs.hcii.cogtool.model.IPredictionAlgo;
import edu.cmu.cs.hcii.cogtool.model.Project;
import edu.cmu.cs.hcii.cogtool.model.Script;
import edu.cmu.cs.hcii.cogtool.model.TaskApplication;
import edu.cmu.cs.hcii.cogtool.model.TaskGroup;
import edu.cmu.cs.hcii.cogtool.model.TimeDistributionPredictionResult;
import edu.cmu.cs.hcii.cogtool.uimodel.PERTChartUIModel;
import edu.cmu.cs.hcii.cogtool.util.AlertHandler;
import edu.cmu.cs.hcii.cogtool.util.L10N;
import edu.cmu.cs.hcii.cogtool.util.ListenerIdentifierMap;
import edu.cmu.cs.hcii.cogtool.util.RcvrUIException;
import edu.cmu.cs.hcii.cogtool.util.SWTStringUtil;
import edu.cmu.cs.hcii.cogtool.util.StringUtil;
import edu.cmu.cs.hcii.cogtool.util.WindowUtil;
import edu.cmu.cs.hcii.cogtool.util.ListenerIdentifier.ILIDTransmuter;
/**
* Window that displays a PERT Chart visualization of model traces.
*
* @author jason
*
*/
public class PERTChartView extends View
{
public static final int LINK_BARS_ABSOLUTE = 0;
public static final int LINK_BARS_OFFSET = 1;
public static final int LINK_BARS_SELECTED_START = 2;
public static final int LINK_BARS_SELECTED_END = 3;
protected PERTChartPanel topPanel;
protected PERTChartPanel bottomPanel;
protected Combo visCombo;
protected Combo compCombo;
protected Button compareButton;
protected Button linkButton;
protected Combo linkCombo;
protected Map<String, APredictionResult> resultMap =
new HashMap<String, APredictionResult>();
protected double syncOffset = 0;
protected class NamedPredictionResult{
public APredictionResult result;
public String name;
public NamedPredictionResult(APredictionResult result, String name)
{
this.result = result;
this.name = name;
}
}
protected void addAvailableResult(NamedPredictionResult result,
List<NamedPredictionResult> availableResults)
{
if (result.result instanceof TimeDistributionPredictionResult) {
Iterator listIt = ((TimeDistributionPredictionResult) result.result).getResultList().iterator();
while( listIt.hasNext() ){
NamedPredictionResult npr = new NamedPredictionResult( (APredictionResult) listIt.next(), result.name);
availableResults.add(npr);
}
}
else {
availableResults.add(result);
}
}
protected void addDesignResults(APredictionResult knownResult,
Project project,
Design design,
Iterator<AUndertaking> tasks,
List<NamedPredictionResult> availableResults)
{
while (tasks.hasNext()) {
AUndertaking task = tasks.next();
if ((design != null) && (task != null)) {
if (task.isTaskGroup()) {
addDesignResults(knownResult,
project,
design,
((TaskGroup) task).getUndertakings().iterator(),
availableResults);
}
else {
TaskApplication otherTa =
project.getTaskApplication(task, design);
if (otherTa != null) {
Iterator<CognitiveModelGenerator> modGenIt =
otherTa.getModelGenerators();
while (modGenIt.hasNext()) {
CognitiveModelGenerator otherModGen =
modGenIt.next();
if (otherModGen != null) {
Iterator<IPredictionAlgo> predAlgIt =
otherTa.getPredictionAlgs(otherModGen);
while (predAlgIt.hasNext()) {
APredictionResult otherResult =
otherTa.getResult(otherModGen,
predAlgIt.next());
if ((otherResult != knownResult) &&
(otherResult != null))
{
String name = otherTa.getDesign().getName() + " : " + otherTa.getTask().getName();
NamedPredictionResult npr = new NamedPredictionResult( otherResult, name);
addAvailableResult(npr,
availableResults);
}
}
}
}
}
}
}
}
} // addDesignResults
public PERTChartView(APredictionResult result,
Project project,
ListenerIdentifierMap listenerIDMap,
ILIDTransmuter transformer,
MenuFactory.IWindowMenuData<Project> menuData,
PERTChartUIModel uiModel,
Rectangle loc,
int initialStrategy)
{
// AView constructor creates shell, etc.
super(createShell(loc, 800, 600, new FormLayout()),
listenerIDMap,
transformer,
menuData);
// TODO is the following really necessary? Isn't this taken care of
// above?
shell.setLayout(new FormLayout());
List<NamedPredictionResult> availableResults =
new ArrayList<NamedPredictionResult>();
// TODO Setting name to unknown for now for the first CT-E demonstration.
// Longer term we should probably ensure that there is a Script
// in this case, too--this is complicated, though, as that is
// interrogated in other places to distinguish the CT-E case
// from normal CogTool (yuk).
Script s = result.getScript();
String name = "First CT-E Demonstration";
if (s != null) {
TaskApplication ta = s.getDemonstration().getTaskApplication();
name = new String(ta.getDesign().getName()
+ " : "
+ ta.getTask().getName());
}
NamedPredictionResult npr = new NamedPredictionResult( result, name );
addAvailableResult(npr, availableResults);
// Iterate through projects and add other results from the project
Iterator<Design> desIt = project.getDesigns().iterator();
while (desIt.hasNext()) {
Design otherDesign = desIt.next();
addDesignResults(result,
project,
otherDesign,
project.getUndertakings().iterator(),
availableResults);
}
// Create combo boxes for selecting comparisons
visCombo = new Combo(shell, SWT.READ_ONLY);
compCombo = new Combo(shell, SWT.READ_ONLY);
Iterator<NamedPredictionResult> resultIterator = availableResults.iterator();
while (resultIterator.hasNext()) {
NamedPredictionResult nr = resultIterator.next();
String uniqueName = new String();
int uniqueCounter = 0;
String displayName = SWTStringUtil.insertEllipsis(nr.name,
300,
StringUtil.EQUAL,
visCombo.getFont());
uniqueName = displayName;
while (resultMap.containsKey(uniqueName)) {
uniqueName = new String(displayName);
uniqueName += " " + ++uniqueCounter;
}
visCombo.add(uniqueName);
compCombo.add(uniqueName);
resultMap.put(uniqueName, nr.result);
}
int initialSelection = 0;
if (initialStrategy > 0) {
initialSelection = (initialStrategy - 1);
}
visCombo.select(initialSelection);
compCombo.select(initialSelection);
APredictionResult activeResult = (availableResults.get(initialSelection)).result;
// Create comparison UI
compareButton = new Button(shell, SWT.CHECK);
compareButton.setText(L10N.get("Vis.CompareTo", "Compare to "));
topPanel =
new PERTChartPanel(shell, activeResult, uiModel, false);
bottomPanel =
new PERTChartPanel(shell, activeResult, uiModel);
bottomPanel.setVisible(false);
Label visLabel = new Label(shell, SWT.HORIZONTAL);
visLabel.setText(L10N.get("Vis.ShowVisOf",
"Show Visualization of"));
linkButton = new Button(shell, SWT.CHECK);
linkButton.setText(L10N.get("Vis.LinkScrollbarsBy",
"Link Scrollbars by"));
linkButton.setVisible(false);
linkButton.setSelection(true);
linkCombo = new Combo(shell, SWT.READ_ONLY);
linkCombo.add(L10N.get("Vis.LinkAbsolute",
"matching start time and duration"),
LINK_BARS_ABSOLUTE);
linkCombo.add(L10N.get("vis.LinkOffset",
"maintaining current offset"),
LINK_BARS_OFFSET);
//this.linkCombo.add("matching starts of selections", LINK_BARS_SELECTED_START);
//this.linkCombo.add("matching ends of selections", LINK_BARS_SELECTED_END);
linkCombo.select(0);
linkCombo.setVisible(false);
// Lay out view
FormData fData = new FormData();
fData.top = new FormAttachment(0, 5);
fData.left = new FormAttachment(0, 5);
visLabel.setLayoutData(fData);
fData = new FormData();
fData.top = new FormAttachment(0, 0);
fData.left = new FormAttachment(visLabel, 5);
visCombo.setLayoutData(fData);
fData = new FormData();
fData.top = new FormAttachment(0, 5);
fData.left = new FormAttachment(visCombo, 50);
compareButton.setLayoutData(fData);
fData = new FormData();
fData.top = new FormAttachment(0, 0);
fData.left = new FormAttachment(compareButton, 5);
compCombo.setLayoutData(fData);
fData = new FormData();
fData.top = new FormAttachment(0, 5);
fData.left = new FormAttachment(compCombo, 50);
linkButton.setLayoutData(fData);
fData = new FormData();
fData.top = new FormAttachment(0, 0);
fData.left = new FormAttachment(linkButton, 5);
linkCombo.setLayoutData(fData);
fData = new FormData();
fData.top = new FormAttachment(visCombo, 5);
fData.left = new FormAttachment(0, 0);
fData.right = new FormAttachment(100, 0);
fData.bottom = new FormAttachment(100, 0);
topPanel.setLayoutData(fData);
fData = new FormData();
fData.top = new FormAttachment(topPanel, 0);
fData.left = new FormAttachment(0, 0);
fData.right = new FormAttachment(100, 0);
fData.bottom = new FormAttachment(100, 0);
bottomPanel.setLayoutData(fData);
shell.layout();
topPanel.setVisible(true);
bottomPanel.setVisible(true);
shell.setVisible(true);
shell.setMaximized(true);
class ComboSelectionListener implements SelectionListener
{
protected final Combo combo;
protected final boolean isComparison;
ComboSelectionListener(Combo c, boolean ic)
{
super();
combo = c;
isComparison = ic;
}
public void widgetDefaultSelected(SelectionEvent evt)
{
// TODO I've no idea what this does, or why it was added and
// then commented out. It should either be uncommented
// out, or excised completely, I reckon.
//this.widgetSelected(arg0);
}
public void widgetSelected(SelectionEvent evt)
{
String key = combo.getText();
APredictionResult viewResult = resultMap.get(key);
if (viewResult == null) {
try {
WindowUtil.presentWarningDialog(shell,
L10N.get("Vis.NeedsRecomputeTitle",
"Needs recomputation"),
L10N.get("Vis.NeedsRecomputeMsg",
"There is no such result available. "+
"Likely it needs to be recomputed."));
}
catch (SWTException ex) {
throw new RcvrUIException("Creation of dialog box failed.", ex);
}
}
if (isComparison) {
bottomPanel.setResult(viewResult);
}
else {
topPanel.setResult(viewResult);
}
if (isComparison || compareButton.getSelection()) {
double newTT = Math.max(topPanel.getNativeTotalTime(),
bottomPanel.getNativeTotalTime());
topPanel.setTotalTime(newTT);
bottomPanel.setTotalTime(newTT);
}
}
}
visCombo.addSelectionListener(new ComboSelectionListener(visCombo,
false));
compCombo.addSelectionListener(new ComboSelectionListener(compCombo,
true));
compareButton.addSelectionListener(new SelectionListener() {
public void widgetDefaultSelected(SelectionEvent se)
{
widgetSelected(se);
}
public void widgetSelected(SelectionEvent se)
{
boolean compare = compareButton.getSelection();
if (compare) {
FormData data = new FormData();
data.top = new FormAttachment(visCombo, 5);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
data.bottom = new FormAttachment(50, 0);
topPanel.setLayoutData(data);
double newTT = Math.max(topPanel.getNativeTotalTime(),
bottomPanel.getNativeTotalTime());
topPanel.setTotalTime(newTT);
bottomPanel.setTotalTime(newTT);
}
else {
FormData data = new FormData();
data.top = new FormAttachment(visCombo, 5);
data.left = new FormAttachment(0, 0);
data.right = new FormAttachment(100, 0);
data.bottom = new FormAttachment(100, 0);
topPanel.setLayoutData(data);
topPanel.setTotalTime(topPanel.getNativeTotalTime());
}
bottomPanel.setVisible(compare);
linkButton.setVisible(compare);
linkCombo.setVisible(compare);
shell.layout();
}
});
// set initial link
topPanel.getVisScrollBar().addHandler(this,
PERTScrollBar.ScrollEvent.class,
new AlertHandler()
{
public void handleAlert(EventObject alert)
{
if ((bottomPanel.getVisible()) && (linkButton.getSelection()))
{
PERTScrollBar.ScrollEvent evt =
(PERTScrollBar.ScrollEvent) alert;
bottomPanel.getVisScrollBar().changeRegion(evt.start - syncOffset,
evt.end - syncOffset,
false);
bottomPanel.getVisPanel().displayTimeSlice(evt.start - syncOffset,
evt.end - syncOffset);
}
}
});
bottomPanel.getVisScrollBar().addHandler(this,
PERTScrollBar.ScrollEvent.class,
new AlertHandler()
{
public void handleAlert(EventObject alert)
{
if (linkButton.getSelection())
{
PERTScrollBar.ScrollEvent evt =
(PERTScrollBar.ScrollEvent) alert;
topPanel.getVisScrollBar().changeRegion(evt.start + syncOffset,
evt.end + syncOffset,
false);
topPanel.getVisPanel().displayTimeSlice(evt.start + syncOffset,
evt.end + syncOffset);
}
}
});
// add handler for sync on selecting the sync checkbox
linkButton.addSelectionListener(new SelectionListener() {
public void widgetDefaultSelected(SelectionEvent evt)
{
widgetSelected(evt);
}
public void widgetSelected(SelectionEvent evt)
{
double bottomStart = bottomPanel.getVisPanel().displayStartTime;
double duration =
topPanel.getVisPanel().displayEndTime - topPanel.getVisPanel().displayStartTime;
switch (linkCombo.getSelectionIndex()) {
case LINK_BARS_ABSOLUTE:
syncOffset = 0;
bottomStart = topPanel.getVisPanel().displayStartTime;
break;
case LINK_BARS_OFFSET:
syncOffset =
topPanel.getVisPanel().displayStartTime - bottomPanel.getVisPanel().displayStartTime;
break;
case LINK_BARS_SELECTED_START:
break;
case LINK_BARS_SELECTED_END:
break;
}
bottomPanel.getVisScrollBar().changeRegion(bottomStart,
bottomStart + duration);
bottomPanel.getVisPanel().displayTimeSlice(bottomStart,
bottomStart + duration);
}});
// add handler for sync on selecting the sync checkbox
linkCombo.addSelectionListener(new SelectionListener() {
public void widgetDefaultSelected(SelectionEvent evt)
{
widgetSelected(evt);
}
public void widgetSelected(SelectionEvent evt)
{
if (linkButton.getSelection()) {
if (linkCombo.getSelectionIndex() == LINK_BARS_ABSOLUTE) {
syncOffset = 0;
double bottomStart =
topPanel.getVisPanel().displayStartTime;
double bottomEnd =
topPanel.getVisPanel().displayEndTime;
bottomPanel.getVisScrollBar().changeRegion(bottomStart,
bottomEnd);
bottomPanel.getVisPanel().displayTimeSlice(bottomStart,
bottomEnd);
}
}
}});
}
@Override
protected MenuFactory.MenuType[] neededMenus()
{
return new MenuFactory.MenuType[] { MenuFactory.MenuType.FileMenu,
MenuFactory.MenuType.EditMenu,
MenuFactory.MenuType.WindowMenu,
MenuFactory.MenuType.HelpMenu };
}
public List<PERTChartPanel> getPERTChartPanels()
{
List<PERTChartPanel> panels = new ArrayList<PERTChartPanel>();
panels.add(topPanel);
panels.add(bottomPanel);
return panels;
}
}