|
Added
Link Here
|
| 1 |
/******************************************************************************* |
| 2 |
* Copyright (c) 2009 Tasktop Technologies and others. |
| 3 |
* All rights reserved. This program and the accompanying materials |
| 4 |
* are made available under the terms of the Eclipse Public License v1.0 |
| 5 |
* which accompanies this distribution, and is available at |
| 6 |
* http://www.eclipse.org/legal/epl-v10.html |
| 7 |
* |
| 8 |
* Contributors: |
| 9 |
* Tasktop Technologies - initial API and implementation |
| 10 |
*******************************************************************************/ |
| 11 |
|
| 12 |
package org.eclipse.mylyn.internal.context.ui.wizards; |
| 13 |
|
| 14 |
import java.lang.reflect.InvocationTargetException; |
| 15 |
import java.util.Collections; |
| 16 |
import java.util.Date; |
| 17 |
import java.util.HashMap; |
| 18 |
import java.util.List; |
| 19 |
import java.util.Map; |
| 20 |
|
| 21 |
import org.eclipse.core.runtime.IProgressMonitor; |
| 22 |
import org.eclipse.jface.dialogs.IDialogConstants; |
| 23 |
import org.eclipse.jface.dialogs.MessageDialog; |
| 24 |
import org.eclipse.jface.layout.GridDataFactory; |
| 25 |
import org.eclipse.jface.layout.GridLayoutFactory; |
| 26 |
import org.eclipse.jface.operation.IRunnableContext; |
| 27 |
import org.eclipse.jface.operation.IRunnableWithProgress; |
| 28 |
import org.eclipse.jface.operation.ModalContext; |
| 29 |
import org.eclipse.jface.wizard.ProgressMonitorPart; |
| 30 |
import org.eclipse.jface.wizard.WizardDialog; |
| 31 |
import org.eclipse.mylyn.internal.provisional.commons.ui.WorkbenchUtil; |
| 32 |
import org.eclipse.mylyn.internal.tasks.ui.util.AttachmentUtil; |
| 33 |
import org.eclipse.mylyn.tasks.core.ITask; |
| 34 |
import org.eclipse.mylyn.tasks.core.ITaskAttachment; |
| 35 |
import org.eclipse.mylyn.tasks.core.TaskRepository; |
| 36 |
import org.eclipse.mylyn.tasks.ui.TasksUi; |
| 37 |
import org.eclipse.osgi.util.NLS; |
| 38 |
import org.eclipse.swt.SWT; |
| 39 |
import org.eclipse.swt.events.SelectionAdapter; |
| 40 |
import org.eclipse.swt.events.SelectionEvent; |
| 41 |
import org.eclipse.swt.graphics.Cursor; |
| 42 |
import org.eclipse.swt.graphics.Image; |
| 43 |
import org.eclipse.swt.widgets.Button; |
| 44 |
import org.eclipse.swt.widgets.Composite; |
| 45 |
import org.eclipse.swt.widgets.Control; |
| 46 |
import org.eclipse.swt.widgets.Display; |
| 47 |
import org.eclipse.swt.widgets.Link; |
| 48 |
import org.eclipse.swt.widgets.Shell; |
| 49 |
|
| 50 |
import com.ibm.icu.text.DateFormat; |
| 51 |
|
| 52 |
/** |
| 53 |
* @author Steffen Pingel |
| 54 |
*/ |
| 55 |
public class RetrieveLatestContextDialog extends MessageDialog { |
| 56 |
|
| 57 |
public static class ProgressContainer implements IRunnableContext { |
| 58 |
|
| 59 |
private static final String FOCUS_CONTROL = "focusControl"; //$NON-NLS-1$ |
| 60 |
|
| 61 |
// The number of long running operation executed from the dialog. |
| 62 |
private long activeRunningOperations = 0; |
| 63 |
|
| 64 |
private Cursor arrowCursor; |
| 65 |
|
| 66 |
private Button cancelButton; |
| 67 |
|
| 68 |
private boolean lockedUI = false; |
| 69 |
|
| 70 |
private boolean needsProgressMonitor; |
| 71 |
|
| 72 |
// The progress monitor |
| 73 |
private final ProgressMonitorPart progressMonitorPart; |
| 74 |
|
| 75 |
private final Shell shell; |
| 76 |
|
| 77 |
private Cursor waitCursor; |
| 78 |
|
| 79 |
public ProgressContainer(Shell shell, ProgressMonitorPart progressMonitorPart) { |
| 80 |
this.shell = shell; |
| 81 |
this.progressMonitorPart = progressMonitorPart; |
| 82 |
} |
| 83 |
|
| 84 |
/** |
| 85 |
* About to start a long running operation triggered through the wizard. Shows the progress monitor and disables |
| 86 |
* the wizard's buttons and controls. |
| 87 |
* |
| 88 |
* @param enableCancelButton |
| 89 |
* <code>true</code> if the Cancel button should be enabled, and <code>false</code> if it should be |
| 90 |
* disabled |
| 91 |
* @return the saved UI state |
| 92 |
*/ |
| 93 |
private Object aboutToStart(boolean enableCancelButton) { |
| 94 |
Map<Object, Object> savedState = null; |
| 95 |
if (getShell() != null) { |
| 96 |
// Save focus control |
| 97 |
Control focusControl = getShell().getDisplay().getFocusControl(); |
| 98 |
if (focusControl != null && focusControl.getShell() != getShell()) { |
| 99 |
focusControl = null; |
| 100 |
} |
| 101 |
//cancelButton.removeSelectionListener(cancelListener); |
| 102 |
// Set the busy cursor to all shells. |
| 103 |
Display d = getShell().getDisplay(); |
| 104 |
waitCursor = new Cursor(d, SWT.CURSOR_WAIT); |
| 105 |
setDisplayCursor(waitCursor); |
| 106 |
// Set the arrow cursor to the cancel component. |
| 107 |
arrowCursor = new Cursor(d, SWT.CURSOR_ARROW); |
| 108 |
cancelButton.setCursor(arrowCursor); |
| 109 |
// Deactivate shell |
| 110 |
savedState = new HashMap<Object, Object>(10); |
| 111 |
saveUiState(savedState); |
| 112 |
if (focusControl != null) { |
| 113 |
savedState.put(FOCUS_CONTROL, focusControl); |
| 114 |
} |
| 115 |
// Attach the progress monitor part to the cancel button |
| 116 |
progressMonitorPart.attachToCancelComponent(cancelButton); |
| 117 |
progressMonitorPart.setVisible(true); |
| 118 |
} |
| 119 |
return savedState; |
| 120 |
} |
| 121 |
|
| 122 |
public Button getCancelButton() { |
| 123 |
return cancelButton; |
| 124 |
} |
| 125 |
|
| 126 |
private IProgressMonitor getProgressMonitor() { |
| 127 |
return progressMonitorPart; |
| 128 |
} |
| 129 |
|
| 130 |
public Shell getShell() { |
| 131 |
return shell; |
| 132 |
} |
| 133 |
|
| 134 |
public boolean isActive() { |
| 135 |
return activeRunningOperations > 0; |
| 136 |
} |
| 137 |
|
| 138 |
protected void restoreUiState(Map<Object, Object> state) { |
| 139 |
// ignore |
| 140 |
|
| 141 |
} |
| 142 |
|
| 143 |
/** |
| 144 |
* This implementation of IRunnableContext#run(boolean, boolean, IRunnableWithProgress) blocks until the |
| 145 |
* runnable has been run, regardless of the value of <code>fork</code>. It is recommended that <code>fork</code> |
| 146 |
* is set to true in most cases. If <code>fork</code> is set to <code>false</code>, the runnable will run in the |
| 147 |
* UI thread and it is the runnable's responsibility to call <code>Display.readAndDispatch()</code> to ensure UI |
| 148 |
* responsiveness. UI state is saved prior to executing the long-running operation and is restored after the |
| 149 |
* long-running operation completes executing. Any attempt to change the UI state of the wizard in the |
| 150 |
* long-running operation will be nullified when original UI state is restored. |
| 151 |
*/ |
| 152 |
public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable) |
| 153 |
throws InvocationTargetException, InterruptedException { |
| 154 |
// The operation can only be canceled if it is executed in a separate |
| 155 |
// thread. |
| 156 |
// Otherwise the UI is blocked anyway. |
| 157 |
Object state = null; |
| 158 |
if (activeRunningOperations == 0) { |
| 159 |
state = aboutToStart(fork && cancelable); |
| 160 |
} |
| 161 |
activeRunningOperations++; |
| 162 |
try { |
| 163 |
if (!fork) { |
| 164 |
lockedUI = true; |
| 165 |
} |
| 166 |
ModalContext.run(runnable, fork, getProgressMonitor(), getShell().getDisplay()); |
| 167 |
lockedUI = false; |
| 168 |
} finally { |
| 169 |
activeRunningOperations--; |
| 170 |
// Stop if this is the last one |
| 171 |
if (state != null) { |
| 172 |
stopped(state); |
| 173 |
} |
| 174 |
} |
| 175 |
} |
| 176 |
|
| 177 |
protected void saveUiState(Map<Object, Object> savedState) { |
| 178 |
// ignore |
| 179 |
|
| 180 |
} |
| 181 |
|
| 182 |
public void setCancelButton(Button cancelButton) { |
| 183 |
this.cancelButton = cancelButton; |
| 184 |
} |
| 185 |
|
| 186 |
/** |
| 187 |
* Sets the given cursor for all shells currently active for this window's display. |
| 188 |
* |
| 189 |
* @param c |
| 190 |
* the cursor |
| 191 |
*/ |
| 192 |
private void setDisplayCursor(Cursor c) { |
| 193 |
Shell[] shells = getShell().getDisplay().getShells(); |
| 194 |
for (Shell shell2 : shells) { |
| 195 |
shell2.setCursor(c); |
| 196 |
} |
| 197 |
} |
| 198 |
|
| 199 |
/** |
| 200 |
* A long running operation triggered through the wizard was stopped either by user input or by normal end. |
| 201 |
* Hides the progress monitor and restores the enable state wizard's buttons and controls. |
| 202 |
* |
| 203 |
* @param savedState |
| 204 |
* the saved UI state as returned by <code>aboutToStart</code> |
| 205 |
* @see #aboutToStart |
| 206 |
*/ |
| 207 |
@SuppressWarnings("unchecked") |
| 208 |
private void stopped(Object savedState) { |
| 209 |
if (getShell() != null && !getShell().isDisposed()) { |
| 210 |
if (needsProgressMonitor) { |
| 211 |
progressMonitorPart.setVisible(false); |
| 212 |
progressMonitorPart.removeFromCancelComponent(cancelButton); |
| 213 |
} |
| 214 |
Map<Object, Object> state = (Map<Object, Object>) savedState; |
| 215 |
restoreUiState(state); |
| 216 |
// cancelButton.addSelectionListener(cancelListener); |
| 217 |
setDisplayCursor(null); |
| 218 |
cancelButton.setCursor(null); |
| 219 |
waitCursor.dispose(); |
| 220 |
waitCursor = null; |
| 221 |
arrowCursor.dispose(); |
| 222 |
arrowCursor = null; |
| 223 |
Control focusControl = (Control) state.get(FOCUS_CONTROL); |
| 224 |
if (focusControl != null && !focusControl.isDisposed()) { |
| 225 |
focusControl.setFocus(); |
| 226 |
} |
| 227 |
} |
| 228 |
} |
| 229 |
|
| 230 |
} |
| 231 |
|
| 232 |
public static boolean openQuestion(Shell shell, ITask task) { |
| 233 |
String title = "Task Activation"; |
| 234 |
TaskRepository repository = TasksUi.getRepositoryManager().getRepository(task.getConnectorKind(), |
| 235 |
task.getRepositoryUrl()); |
| 236 |
List<ITaskAttachment> contextAttachments = AttachmentUtil.getContextAttachments(repository, task); |
| 237 |
Collections.sort(contextAttachments, new TaskAttachmentComparator()); |
| 238 |
if (contextAttachments.size() > 0) { |
| 239 |
ITaskAttachment attachment = contextAttachments.get(0); |
| 240 |
String author = null; |
| 241 |
if (attachment.getAuthor() != null) { |
| 242 |
author = (attachment.getAuthor().getName()) != null ? attachment.getAuthor().getName() |
| 243 |
: attachment.getAuthor().getPersonId(); |
| 244 |
} |
| 245 |
if (author == null) { |
| 246 |
author = "<Unknown>"; |
| 247 |
} |
| 248 |
Date date = attachment.getCreationDate(); |
| 249 |
String dateString = null; |
| 250 |
if (date != null) { |
| 251 |
dateString = DateFormat.getDateInstance(DateFormat.LONG).format(date); |
| 252 |
} |
| 253 |
if (dateString == null) { |
| 254 |
dateString = "<Unkown>"; |
| 255 |
} |
| 256 |
String message = NLS.bind( |
| 257 |
"No local context exists. Retrieve latest context created by {0} on {1} from repository?", author, |
| 258 |
dateString); |
| 259 |
int kind = QUESTION; |
| 260 |
int style = SWT.NONE; |
| 261 |
RetrieveLatestContextDialog dialog = new RetrieveLatestContextDialog(shell, title, null, message, kind, |
| 262 |
new String[] { IDialogConstants.OK_LABEL, IDialogConstants.CANCEL_LABEL }, 0, task, attachment); |
| 263 |
style &= SWT.SHEET; |
| 264 |
dialog.setShellStyle(dialog.getShellStyle() | style); |
| 265 |
return dialog.open() == 0; |
| 266 |
} |
| 267 |
return false; |
| 268 |
} |
| 269 |
|
| 270 |
private final ITaskAttachment attachment; |
| 271 |
|
| 272 |
private Link link; |
| 273 |
|
| 274 |
private ProgressContainer progressContainer; |
| 275 |
|
| 276 |
private ProgressMonitorPart progressMonitorPart; |
| 277 |
|
| 278 |
private final ITask task; |
| 279 |
|
| 280 |
public RetrieveLatestContextDialog(Shell parentShell, String dialogTitle, Image dialogTitleImage, |
| 281 |
String dialogMessage, int dialogImageType, String[] dialogButtonLabels, int defaultIndex, ITask task, |
| 282 |
ITaskAttachment attachment) { |
| 283 |
super(parentShell, dialogTitle, dialogTitleImage, dialogMessage, dialogImageType, dialogButtonLabels, |
| 284 |
defaultIndex); |
| 285 |
this.task = task; |
| 286 |
this.attachment = attachment; |
| 287 |
} |
| 288 |
|
| 289 |
@Override |
| 290 |
protected void buttonPressed(int buttonId) { |
| 291 |
if (progressContainer.isActive()) { |
| 292 |
return; |
| 293 |
} |
| 294 |
if (buttonId == 0) { |
| 295 |
if (!AttachmentUtil.downloadContext(task, attachment, progressContainer)) { |
| 296 |
// failed |
| 297 |
return; |
| 298 |
} |
| 299 |
} |
| 300 |
super.buttonPressed(buttonId); |
| 301 |
} |
| 302 |
|
| 303 |
@Override |
| 304 |
protected Control createButtonBar(Composite parent) { |
| 305 |
Composite composite = new Composite(parent, SWT.NONE); |
| 306 |
composite.setLayout(GridLayoutFactory.fillDefaults().numColumns(3).create()); |
| 307 |
GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(composite); |
| 308 |
Control control = createLink(composite); |
| 309 |
GridDataFactory.fillDefaults().grab(true, false).applyTo(control); |
| 310 |
super.createButtonBar(composite); |
| 311 |
return composite; |
| 312 |
} |
| 313 |
|
| 314 |
@Override |
| 315 |
protected Control createContents(Composite parent) { |
| 316 |
Control control = super.createContents(parent); |
| 317 |
progressContainer.setCancelButton(getButton(1)); |
| 318 |
getButton(0).setFocus(); |
| 319 |
return control; |
| 320 |
} |
| 321 |
|
| 322 |
@Override |
| 323 |
protected Control createCustomArea(Composite parent) { |
| 324 |
progressMonitorPart = new ProgressMonitorPart(parent, null); |
| 325 |
GridDataFactory.fillDefaults().grab(true, false).applyTo(progressMonitorPart); |
| 326 |
progressContainer = new ProgressContainer(getShell(), progressMonitorPart) { |
| 327 |
@Override |
| 328 |
protected void restoreUiState(Map<Object, Object> state) { |
| 329 |
link.setEnabled(true); |
| 330 |
getButton(0).setEnabled(true); |
| 331 |
getButton(1).setEnabled(true); |
| 332 |
}; |
| 333 |
|
| 334 |
@Override |
| 335 |
protected void saveUiState(Map<Object, Object> savedState) { |
| 336 |
link.setEnabled(false); |
| 337 |
getButton(0).setEnabled(false); |
| 338 |
}; |
| 339 |
}; |
| 340 |
return progressMonitorPart; |
| 341 |
} |
| 342 |
|
| 343 |
protected Control createLink(Composite parent) { |
| 344 |
link = new Link(parent, SWT.NONE); |
| 345 |
link.setText("<a>Select Context from History</a>"); |
| 346 |
link.addSelectionListener(new SelectionAdapter() { |
| 347 |
@Override |
| 348 |
public void widgetSelected(SelectionEvent e) { |
| 349 |
close(); |
| 350 |
|
| 351 |
ContextRetrieveWizard wizard = new ContextRetrieveWizard(task); |
| 352 |
WizardDialog dialog = new WizardDialog(WorkbenchUtil.getShell(), wizard); |
| 353 |
dialog.create(); |
| 354 |
dialog.setBlockOnOpen(true); |
| 355 |
setReturnCode(dialog.open()); |
| 356 |
} |
| 357 |
}); |
| 358 |
return link; |
| 359 |
} |
| 360 |
|
| 361 |
} |