blob: 44d75d450b7c223cba54f1776bae0df43703ee15 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2007 Mylyn project committers 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
*******************************************************************************/
/**
* Copied from newsgroup, forwarded from Make Technologies
*/
package org.eclipse.mylyn.internal.tasks.ui.views;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.mylyn.internal.tasks.core.ScheduledTaskContainer;
import org.eclipse.mylyn.internal.tasks.ui.ITaskListNotification;
import org.eclipse.mylyn.internal.tasks.ui.TasksUiImages;
import org.eclipse.mylyn.monitor.core.DateUtil;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryQuery;
import org.eclipse.mylyn.tasks.core.AbstractTask;
import org.eclipse.mylyn.tasks.core.AbstractTaskContainer;
import org.eclipse.mylyn.tasks.core.RepositoryStatus;
import org.eclipse.mylyn.tasks.core.TaskRepository;
import org.eclipse.mylyn.tasks.core.AbstractTask.RepositoryTaskSyncState;
import org.eclipse.mylyn.tasks.ui.TasksUiPlugin;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseTrackAdapter;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
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.swt.widgets.Label;
import org.eclipse.swt.widgets.Monitor;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.IWindowListener;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
/**
* @author Mik Kersten
* @author Eric Booth
* @author Leo Dos Santos - multi-monitor support
* @author Steffen Pingel
*/
public class TaskListToolTipHandler {
private Shell tipShell;
private Widget tipWidget;
private AbstractTaskContainer getTaskListElement(Object hoverObject) {
if (hoverObject instanceof Widget) {
Object data = ((Widget) hoverObject).getData();
if (data != null) {
if (data instanceof AbstractTaskContainer) {
return (AbstractTaskContainer) data;
} else if (data instanceof IAdaptable) {
return (AbstractTaskContainer) ((IAdaptable) data).getAdapter(AbstractTaskContainer.class);
}
}
}
return null;
}
private String getTitleText(AbstractTaskContainer element) {
if (element instanceof ScheduledTaskContainer) {
StringBuilder sb = new StringBuilder();
sb.append(element.getSummary());
Calendar start = ((ScheduledTaskContainer) element).getStart();
sb.append(" [");
sb.append(DateFormat.getDateInstance(DateFormat.LONG).format(start.getTime()));
sb.append("]");
return sb.toString();
} else if (element instanceof AbstractRepositoryQuery) {
AbstractRepositoryQuery query = (AbstractRepositoryQuery) element;
StringBuilder sb = new StringBuilder();
sb.append(element.getSummary());
sb.append(" [");
sb.append(getRepositoryLabel(query.getRepositoryKind(), query.getRepositoryUrl()));
sb.append("]");
return sb.toString();
} else {
return element.getSummary();
}
}
private String getDetailsText(AbstractTaskContainer element, TaskListView taskListView) {
if (element instanceof ScheduledTaskContainer) {
ScheduledTaskContainer container = (ScheduledTaskContainer) element;
int estimateTotal = 0;
long elapsedTotal = 0;
if (taskListView != null) {
Object[] children = ((TaskListContentProvider)taskListView.getViewer().getContentProvider()).getChildren(element);
for (Object object : children) {
if (object instanceof AbstractTask) {
estimateTotal += ((AbstractTask) object).getEstimateTimeHours();
elapsedTotal += TasksUiPlugin.getTaskActivityManager().getElapsedTime((AbstractTask) object,
container.getStart(), container.getEnd());
}
}
}
StringBuilder sb = new StringBuilder();
sb.append("Estimate: ");
sb.append(estimateTotal);
sb.append(" hours");
sb.append("\n");
sb.append("Elapsed: ");
sb.append(DateUtil.getFormattedDurationShort(elapsedTotal));
sb.append("\n");
return sb.toString();
} else if (element instanceof AbstractTask) {
AbstractTask task = (AbstractTask) element;
StringBuilder sb = new StringBuilder();
sb.append(TasksUiPlugin.getConnectorUi(task.getConnectorKind()).getTaskKindLabel(task));
String key = task.getTaskKey();
if (key != null) {
sb.append(" ");
sb.append(key);
}
sb.append(", ");
sb.append(task.getPriority());
sb.append(" [");
sb.append(getRepositoryLabel(task.getConnectorKind(), task.getRepositoryUrl()));
sb.append("]");
sb.append("\n");
return sb.toString();
} else {
return null;
}
}
private String getRepositoryLabel(String repositoryKind, String repositoryUrl) {
TaskRepository repository = TasksUiPlugin.getRepositoryManager().getRepository(repositoryKind, repositoryUrl);
if (repository != null) {
String label = repository.getRepositoryLabel();
if (label.indexOf("//") != -1) {
return label.substring((repository.getUrl().indexOf("//") + 2));
}
return label + "";
}
return "";
}
private String getActivityText(AbstractTaskContainer element) {
// if (element instanceof ScheduledTaskDelegate) {
// ScheduledTaskDelegate task = (ScheduledTaskDelegate) element;
//
// StringBuilder sb = new StringBuilder();
// Date date = task.getScheduledForDate();
// if (date != null) {
// sb.append("Scheduled for: ");
// sb.append(new SimpleDateFormat("E").format(date)).append(", ");
// sb.append(DateFormat.getDateInstance(DateFormat.LONG).format(date));
// sb.append(" (").append(DateFormat.getTimeInstance(DateFormat.SHORT).format(date)).append(")\n");
// }
//
// long elapsed = TasksUiPlugin.getTaskActivityManager().getElapsedTime(task.getCorrespondingTask(),
// task.getDateRangeContainer().getStart(), task.getDateRangeContainer().getEnd());
// String elapsedTimeString = DateUtil.getFormattedDurationShort(elapsed);
// sb.append("Elapsed: ");
// sb.append(elapsedTimeString);
// sb.append("\n");
//
// return sb.toString();
// } else
//
if (element instanceof AbstractTask) {
AbstractTask task = (AbstractTask) element;
StringBuilder sb = new StringBuilder();
Date date = task.getScheduledForDate();
if (date != null) {
sb.append("Scheduled for: ");
sb.append(new SimpleDateFormat("E").format(date)).append(", ");
sb.append(DateFormat.getDateInstance(DateFormat.LONG).format(date));
sb.append(" (").append(DateFormat.getTimeInstance(DateFormat.SHORT).format(date)).append(")\n");
}
long elapsed = TasksUiPlugin.getTaskActivityManager().getElapsedTime(task);
String elapsedTimeString = DateUtil.getFormattedDurationShort(elapsed);
sb.append("Elapsed: ");
sb.append(elapsedTimeString);
sb.append("\n");
return sb.toString();
}
return null;
}
private String getIncommingText(AbstractTaskContainer element) {
if (element instanceof AbstractTask) {
AbstractTask task = (AbstractTask) element;
if (task.getSynchronizationState() == RepositoryTaskSyncState.INCOMING) {
AbstractRepositoryConnector connector = TasksUiPlugin.getRepositoryManager().getRepositoryConnector(
task);
if (connector != null) {
ITaskListNotification notification = TasksUiPlugin.getDefault().getIncommingNotification(connector,
task);
if (notification != null) {
String res = null;
if (notification.getDescription() != null) {
String descriptionText = notification.getDescription();
if (descriptionText != null && descriptionText.length() > 0) {
res = descriptionText;
}
}
if (notification.getDetails() != null) {
String details = notification.getDetails();
if (details != null && details.length() > 0) {
res = res == null ? details : res + "\n" + details;
}
}
return res;
}
}
}
}
return null;
}
private String getStatusText(AbstractTaskContainer element) {
IStatus status = null;
if (element instanceof AbstractTask) {
AbstractTask task = (AbstractTask) element;
status = task.getSynchronizationStatus();
} else if (element instanceof AbstractRepositoryQuery) {
AbstractRepositoryQuery query = (AbstractRepositoryQuery) element;
status = query.getSynchronizationStatus();
}
if (status != null) {
StringBuilder sb = new StringBuilder();
sb.append(status.getMessage());
if (status instanceof RepositoryStatus && ((RepositoryStatus) status).isHtmlMessage()) {
sb.append(" Please synchronize manually for full error message.");
}
return sb.toString();
}
return null;
}
private ProgressData getProgressData(AbstractTaskContainer element, TaskListView taskListView) {
if (element instanceof AbstractTask) {
return null;
}
Object[] children = new Object[0];
if (element instanceof ScheduledTaskContainer && taskListView != null) {
children = ((TaskListContentProvider)taskListView.getViewer().getContentProvider()).getChildren(element);
} else {
children = element.getChildren().toArray();
}
int total = children.length;
int completed = 0;
for (AbstractTask task : element.getChildren()) {
if (task.isCompleted()) {
completed++;
}
}
String text = "Total: " + total + " (Complete: " + completed + ", Incomplete: " + (total - completed) + ")";
return new ProgressData(completed, total, text);
}
private Image getImage(AbstractTaskContainer element) {
if (element instanceof AbstractRepositoryQuery) {
AbstractRepositoryQuery query = (AbstractRepositoryQuery) element;
AbstractRepositoryConnector connector = TasksUiPlugin.getRepositoryManager().getRepositoryConnector(
query.getRepositoryKind());
if (connector != null) {
return TasksUiPlugin.getDefault().getBrandingIcon(connector.getConnectorKind());
}
} else if (element instanceof AbstractTask) {
AbstractTask repositoryTask = (AbstractTask) element;
AbstractRepositoryConnector connector = TasksUiPlugin.getRepositoryManager().getRepositoryConnector(
repositoryTask.getConnectorKind());
if (connector != null) {
return TasksUiPlugin.getDefault().getBrandingIcon(connector.getConnectorKind());
}
} else if (element instanceof ScheduledTaskContainer) {
return TasksUiImages.getImage(TasksUiImages.CALENDAR);
}
return null;
}
/**
* Enables customized hover help for a specified control
*
* @control the control on which to enable hoverhelp
*/
public void activateHoverHelp(final Control control) {
// hide tooltip if any window is deactivated
PlatformUI.getWorkbench().addWindowListener(new IWindowListener() {
public void windowActivated(IWorkbenchWindow window) {
}
public void windowClosed(IWorkbenchWindow window) {
}
public void windowDeactivated(IWorkbenchWindow window) {
hideTooltip();
}
public void windowOpened(IWorkbenchWindow window) {
}
});
// hide tooltip if control underneath is activated
control.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
hideTooltip();
}
});
// trap hover events to pop-up tooltip
control.addMouseTrackListener(new MouseTrackAdapter() {
@Override
public void mouseExit(MouseEvent e) {
// TODO can these conditions be simplified? see bug 131776
if (tipShell != null && !tipShell.isDisposed() && tipShell.getDisplay() != null
&& !tipShell.getDisplay().isDisposed() && tipShell.isVisible()) {
tipShell.setVisible(false);
}
tipWidget = null;
}
@Override
public void mouseHover(MouseEvent event) {
Point widgetPosition = new Point(event.x, event.y);
Widget widget = event.widget;
if (widget instanceof ToolBar) {
ToolBar w = (ToolBar) widget;
widget = w.getItem(widgetPosition);
}
if (widget instanceof Table) {
Table w = (Table) widget;
widget = w.getItem(widgetPosition);
}
if (widget instanceof Tree) {
Tree w = (Tree) widget;
widget = w.getItem(widgetPosition);
}
if (widget == null) {
hideTooltip();
tipWidget = null;
return;
}
if (widget == tipWidget) {
// already displaying tooltip
return;
}
tipWidget = widget;
TaskListView taskListView = TaskListView.getFromActivePerspective();
showTooltip(control.toDisplay(widgetPosition), taskListView);
}
});
}
/**
* Sets the location for a hovering shell
*
* @param shell
* the object that is to hover
* @param position
* the position of a widget to hover over
* @return the top-left location for a hovering box
*/
private void setHoverLocation(Shell shell, Point position) {
Rectangle displayBounds = shell.getMonitor().getClientArea();
Rectangle shellBounds = shell.getBounds();
// We need to find the exact monitor we're mousing over
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=166990
Monitor[] array = PlatformUI.getWorkbench().getDisplay().getMonitors();
for (Monitor m : array) {
Rectangle monitorBounds = m.getBounds();
if ((position.x >= monitorBounds.x) && (position.x < (monitorBounds.x + monitorBounds.width))
&& (position.y >= monitorBounds.y) && (position.y < (monitorBounds.y + monitorBounds.height))) {
displayBounds = m.getClientArea();
}
}
if ((position.x + shellBounds.width) > (displayBounds.x + displayBounds.width))
shellBounds.x = displayBounds.x + displayBounds.width - shellBounds.width;
else
shellBounds.x = position.x;
if ((position.y + 10 + shellBounds.height) > (displayBounds.y + displayBounds.height))
shellBounds.y = displayBounds.y + displayBounds.height - shellBounds.height;
else
shellBounds.y = position.y + 10;
shell.setBounds(shellBounds);
}
private void hideTooltip() {
if (tipShell != null && !tipShell.isDisposed() && tipShell.isVisible()) {
tipShell.setVisible(false);
}
}
private void showTooltip(Point location, TaskListView taskListView) {
hideTooltip();
AbstractTaskContainer element = getTaskListElement(tipWidget);
if (element == null) {
return;
}
Shell parent = PlatformUI.getWorkbench().getDisplay().getActiveShell();
if (parent == null) {
return;
}
// dispose old tooltip
if (tipShell != null && !tipShell.isDisposed() && tipShell.getShell() != null) {
tipShell.close();
}
tipShell = new Shell(parent.getDisplay(), SWT.TOOL | SWT.NO_FOCUS | SWT.MODELESS | SWT.ON_TOP);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 2;
gridLayout.marginWidth = 5;
gridLayout.marginHeight = 2;
tipShell.setLayout(gridLayout);
tipShell.setBackground(tipShell.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
addIconAndLabel(tipShell, getImage(element), getTitleText(element));
String detailsText = getDetailsText(element, taskListView);
if (detailsText != null) {
addIconAndLabel(tipShell, null, detailsText);
}
String synchText = getSynchText(element);
if (synchText != null) {
addIconAndLabel(tipShell, TasksUiImages.getImage(TasksUiImages.REPOSITORY_SYNCHRONIZE), synchText);
}
String activityText = getActivityText(element);
if (activityText != null) {
addIconAndLabel(tipShell, TasksUiImages.getImage(TasksUiImages.CALENDAR), activityText);
}
String incommingText = getIncommingText(element);
if (incommingText != null) {
addIconAndLabel(tipShell, TasksUiImages.getImage(TasksUiImages.OVERLAY_INCOMMING), incommingText);
}
ProgressData progress = getProgressData(element, taskListView);
if (progress != null) {
addIconAndLabel(tipShell, null, progress.text);
// label height need to be set to 0 to remove gap below the progress bar
Label label = new Label(tipShell, SWT.NONE);
GridData labelGridData = new GridData(SWT.FILL, SWT.TOP, true, false);
labelGridData.heightHint = 0;
label.setLayoutData(labelGridData);
Composite progressComposite = new Composite(tipShell, SWT.NONE);
GridLayout progressLayout = new GridLayout(1, false);
progressLayout.marginWidth = 0;
progressLayout.marginHeight = 0;
progressComposite.setLayout(progressLayout);
progressComposite.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
WorkweekProgressBar taskProgressBar = new WorkweekProgressBar(progressComposite);
taskProgressBar.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
taskProgressBar.reset(progress.completed, progress.total);
// do we really need custom canvas? code below renders the same
// IThemeManager themeManager = PlatformUI.getWorkbench().getThemeManager();
// Color color = themeManager.getCurrentTheme().getColorRegistry().get(
// TaskListColorsAndFonts.THEME_COLOR_TASK_TODAY_COMPLETED);
// ProgressBar bar = new ProgressBar(tipShell, SWT.SMOOTH);
// bar.setForeground(color);
// bar.setSelection((int) (100d * progress.completed / progress.total));
// GridData gridData = new GridData(SWT.FILL, SWT.TOP, true, false);
// gridData.heightHint = 5;
// bar.setLayoutData(gridData);
}
String statusText = getStatusText(element);
if (statusText != null) {
addIconAndLabel(tipShell, TasksUiImages.getImage(TasksUiImages.WARNING), statusText);
}
tipShell.pack();
setHoverLocation(tipShell, location);
tipShell.setVisible(true);
}
private String getSynchText(AbstractTaskContainer element) {
if (element instanceof AbstractRepositoryQuery) {
String syncStamp = ((AbstractRepositoryQuery) element).getLastSynchronizedTimeStamp();
if (syncStamp != null) {
return "Synchronized: " + syncStamp;
}
}
return null;
}
private String removeTrailingNewline(String text) {
if (text.endsWith("\n")) {
return text.substring(0, text.length() - 1);
}
return text;
}
private void addIconAndLabel(Composite parent, Image image, String text) {
Label imageLabel = new Label(parent, SWT.NONE);
imageLabel.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
imageLabel.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
imageLabel.setLayoutData(new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.VERTICAL_ALIGN_BEGINNING));
imageLabel.setImage(image);
Label textLabel = new Label(parent, SWT.NONE);
textLabel.setForeground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_FOREGROUND));
textLabel.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_INFO_BACKGROUND));
textLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_CENTER));
textLabel.setText(removeTrailingNewline(text));
}
private static class ProgressData {
int completed;
int total;
String text;
public ProgressData(int completed, int total, String text) {
this.completed = completed;
this.total = total;
this.text = text;
}
}
}