blob: 7128e338a9e2c35f21c69384846589cfef786501 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2016 IBM Corporation 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:
* Tasktop Technologies - initial API and implementation
* Daniel Kruegler <daniel.kruegler@gmail.com> - Bug 471310
******************************************************************************/
package org.eclipse.ui.internal.progress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.TaskItem;
import org.eclipse.ui.progress.IProgressConstants;
import org.eclipse.ui.progress.IProgressConstants2;
import org.eclipse.ui.progress.WorkbenchJob;
/**
* The TaskBarProgressManager is the class that displays progress in the
* application TaskBar if the job specifies that it should show progress (@see
* {@link IProgressConstants2#SHOW_IN_TASKBAR_ICON_PROPERTY}
*
* @since 3.6
*/
public class TaskBarProgressManager {
private IJobProgressManagerListener listener;
private WorkbenchJob animationUpdateJob;
private boolean isAnimated = false;
private List<Job> jobs = Collections.synchronizedList(new ArrayList<Job>());
private Map<Job, JobInfo> jobInfoMap = Collections.synchronizedMap(new HashMap<Job, JobInfo>());
private final TaskItem taskItem;
private ImageDescriptor overlayDescriptor;
private Image overlayImage;
public TaskBarProgressManager(TaskItem taskItem) {
Assert.isNotNull(taskItem);
this.taskItem = taskItem;
animationUpdateJob = getAnimationUpdateJob();
animationUpdateJob.setSystem(true);
listener = getProgressListener();
// Register the IJobProgressManagerListener so we can display progress
// on the application TaskBar
ProgressManager.getInstance().addListener(listener);
taskItem.addDisposeListener(e -> dispose());
}
/**
* Remove the listener and stop the animation
*/
public void dispose() {
ProgressManager.getInstance().removeListener(listener);
setAnimated(false);
disposeOverlay();
}
private WorkbenchJob getAnimationUpdateJob() {
return new WorkbenchJob(ProgressMessages.AnimationManager_AnimationStart) {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
if (isAnimated) {
if (!taskItem.isDisposed() && !jobs.isEmpty()) {
Job job = jobs.get(0);
JobInfo jobInfo = jobInfoMap.get(job);
if (job != null && jobInfo != null) {
int percentDone = getPercentDone(jobInfo);
if (percentDone == IProgressMonitor.UNKNOWN || (jobInfo.hasTaskInfo()
&& jobInfo.getTaskInfo().totalWork == IProgressMonitor.UNKNOWN)) {
setProgressState(SWT.INDETERMINATE);
} else {
setProgressState(SWT.NORMAL);
if (!taskItem.isDisposed()) {
taskItem.setProgress(percentDone);
}
}
} else {
setProgressState(SWT.DEFAULT);
}
updateImage(job);
} else {
updateImage(null);
}
} else {
setProgressState(SWT.DEFAULT);
updateImage(null);
}
if (isAnimated && taskItem != null && !taskItem.isDisposed()) {
schedule(400);
}
return Status.OK_STATUS;
}
private void setProgressState(int state) {
if (!taskItem.isDisposed() && taskItem.getProgressState() != state) {
taskItem.setProgressState(SWT.DEFAULT);
taskItem.setProgressState(state);
}
}
private int getPercentDone(JobTreeElement info) {
if (info.isJobInfo()) {
return ((JobInfo) info).getPercentDone();
}
if (info.hasChildren()) {
Object[] roots = ((GroupInfo) info).getChildren();
if (roots.length == 1 && roots[0] instanceof JobTreeElement) {
TaskInfo ti = ((JobInfo) roots[0]).getTaskInfo();
if (ti != null) {
return ti.getPercentDone();
}
}
return ((GroupInfo) info).getPercentDone();
}
return 0;
}
};
}
private void updateImage(Job job) {
if (taskItem == null || taskItem.isDisposed())
return;
if (job == null) {
disposeOverlay();
taskItem.setOverlayImage(null);
return;
}
// first check whether the job specifies image property
// if not check with progress manager for its family
ImageDescriptor descriptor = (ImageDescriptor) job.getProperty(IProgressConstants.ICON_PROPERTY);
if (descriptor != null) {
// if the description is same, do nothing.
// Else dispose old one and store this
if (!descriptor.equals(overlayDescriptor)) {
disposeOverlay();
setOverlay(descriptor);
}
} else if (ProgressManager.getInstance().getIconFor(job) != null) {
disposeOverlay();
Image newImage = ProgressManager.getInstance().getIconFor(job);
taskItem.setOverlayImage(newImage);
} else {
disposeOverlay();
taskItem.setOverlayImage(null);
}
}
private void setOverlay(ImageDescriptor descriptor) {
overlayDescriptor = descriptor;
overlayImage = descriptor.createImage();
taskItem.setOverlayImage(overlayImage);
}
private void disposeOverlay() {
overlayDescriptor = null;
if (overlayImage != null) {
overlayImage.dispose();
overlayImage = null;
}
}
private IJobProgressManagerListener getProgressListener() {
return new IJobProgressManagerListener() {
@Override
public void addJob(JobInfo info) {
// Don't count the animate job itself
if (isNotTracked(info)) {
return;
}
if (jobs.isEmpty()) {
setAnimated(true);
}
if (!jobs.contains(info.getJob())) {
jobs.add(info.getJob());
}
jobInfoMap.put(info.getJob(), info);
}
@Override
public void refreshJobInfo(JobInfo info) {
int state = info.getJob().getState();
if (state == Job.RUNNING) {
addJob(info);
} else {
removeJob(info);
}
}
@Override
public void refreshAll() {
ProgressManager manager = ProgressManager.getInstance();
jobs.clear();
jobInfoMap.clear();
setAnimated(false);
for (JobInfo currentInfo : manager.getJobInfos(showsDebug())) {
addJob(currentInfo);
}
}
@Override
public void removeJob(JobInfo info) {
jobs.remove(info.getJob());
jobInfoMap.remove(info.getJob());
if (jobs.isEmpty()) {
setAnimated(false);
}
}
@Override
public boolean showsDebug() {
return false;
}
/**
* If the job isn't running or doesn't specify the
* IProgressConstants#SHOW_IN_TASKBAR_ICON_PROPERTY property, don't
* bother tracking it.
*/
private boolean isNotTracked(JobInfo info) {
Job job = info.getJob();
return job.getState() != Job.RUNNING || !shouldShowSystemProgress(info);
}
private boolean shouldShowSystemProgress(JobInfo info) {
Boolean showInTaskBarIcon = Boolean.FALSE;
Object property = info.getJob().getProperty(IProgressConstants2.SHOW_IN_TASKBAR_ICON_PROPERTY);
if (property instanceof Boolean) {
showInTaskBarIcon = (Boolean) property;
}
return showInTaskBarIcon.booleanValue();
}
@Override
public void addGroup(GroupInfo info) {
// Don't care about groups
}
@Override
public void removeGroup(GroupInfo group) {
// Don't care about groups
}
@Override
public void refreshGroup(GroupInfo info) {
// Don't care about groups
}
};
}
private synchronized void setAnimated(boolean animated) {
isAnimated = animated;
animationUpdateJob.schedule();
}
}