tmf.ui: Introduce TmfFileDialogFactory
[deliverable/tracecompass.git] / tmf / org.eclipse.tracecompass.tmf.ui / src / org / eclipse / tracecompass / internal / tmf / ui / project / wizards / tracepkg / AbstractTracePackageWizardPage.java
1 /*******************************************************************************
2 * Copyright (c) 2013, 2014 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Marc-Andre Laperle - Initial API and implementation
11 *******************************************************************************/
12
13 package org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg;
14
15 import java.io.ByteArrayOutputStream;
16 import java.io.File;
17 import java.io.IOException;
18 import java.io.PrintStream;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.List;
22
23 import org.eclipse.core.runtime.CoreException;
24 import org.eclipse.core.runtime.IStatus;
25 import org.eclipse.core.runtime.Status;
26 import org.eclipse.jface.dialogs.ErrorDialog;
27 import org.eclipse.jface.dialogs.IDialogSettings;
28 import org.eclipse.jface.resource.ImageDescriptor;
29 import org.eclipse.jface.viewers.CheckStateChangedEvent;
30 import org.eclipse.jface.viewers.CheckboxTreeViewer;
31 import org.eclipse.jface.viewers.ICheckStateListener;
32 import org.eclipse.jface.viewers.IStructuredSelection;
33 import org.eclipse.jface.wizard.WizardPage;
34 import org.eclipse.swt.SWT;
35 import org.eclipse.swt.events.SelectionAdapter;
36 import org.eclipse.swt.events.SelectionEvent;
37 import org.eclipse.swt.events.SelectionListener;
38 import org.eclipse.swt.layout.GridData;
39 import org.eclipse.swt.layout.GridLayout;
40 import org.eclipse.swt.widgets.Button;
41 import org.eclipse.swt.widgets.Combo;
42 import org.eclipse.swt.widgets.Composite;
43 import org.eclipse.swt.widgets.Event;
44 import org.eclipse.swt.widgets.FileDialog;
45 import org.eclipse.swt.widgets.Label;
46 import org.eclipse.swt.widgets.Listener;
47 import org.eclipse.swt.widgets.TreeItem;
48 import org.eclipse.tracecompass.internal.tmf.ui.Activator;
49 import org.eclipse.tracecompass.tmf.ui.dialog.TmfFileDialogFactory;
50
51 /**
52 * An abstract wizard page containing common code useful for both import and
53 * export trace package wizard pages
54 *
55 * @author Marc-Andre Laperle
56 */
57 public abstract class AbstractTracePackageWizardPage extends WizardPage {
58
59 private static final int COMBO_HISTORY_LENGTH = 5;
60 private static final String STORE_FILE_PATHS_ID = ".STORE_FILEPATHS_ID"; //$NON-NLS-1$
61
62 private final String fStoreFilePathId;
63 private final IStructuredSelection fSelection;
64
65 private CheckboxTreeViewer fElementViewer;
66 private Button fSelectAllButton;
67 private Button fDeselectAllButton;
68 private Combo fFilePathCombo;
69 private Button fBrowseButton;
70
71 /**
72 * Create the trace package wizard page
73 *
74 * @param pageName
75 * the name of the page
76 * @param title
77 * the title for this wizard page, or null if none
78 * @param titleImage
79 * the image descriptor for the title of this wizard page, or
80 * null if none
81 * @param selection
82 * the current object selection
83 */
84 protected AbstractTracePackageWizardPage(String pageName, String title, ImageDescriptor titleImage, IStructuredSelection selection) {
85 super(pageName, title, titleImage);
86 fStoreFilePathId = getName() + STORE_FILE_PATHS_ID;
87 fSelection = selection;
88 }
89
90 /**
91 * Create the element viewer
92 *
93 * @param compositeParent
94 * the parent composite
95 */
96 protected void createElementViewer(Composite compositeParent) {
97 fElementViewer = new CheckboxTreeViewer(compositeParent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.CHECK);
98
99 fElementViewer.addCheckStateListener(new ICheckStateListener() {
100 @Override
101 public void checkStateChanged(CheckStateChangedEvent event) {
102 TracePackageElement element = (TracePackageElement) event.getElement();
103 if (!element.isEnabled()) {
104 fElementViewer.setChecked(element, element.isChecked());
105 } else {
106 setSubtreeChecked(fElementViewer, element, true, event.getChecked());
107 }
108 maintainCheckIntegrity(element);
109
110 if (element.getParent() != null) {
111 // Uncheck everything in this trace if Trace files are unchecked
112 if (element instanceof TracePackageFilesElement) {
113 if (!element.isChecked()) {
114 setSubtreeChecked(fElementViewer, element.getParent(), false, false);
115 }
116 // Check Trace files if anything else is selected
117 } else if (element.isChecked()) {
118 TracePackageElement parent = element.getParent();
119 while (parent != null) {
120 for (TracePackageElement e : parent.getChildren()) {
121 if (e instanceof TracePackageFilesElement) {
122 setSubtreeChecked(fElementViewer, e, false, true);
123 break;
124 }
125 }
126 parent = parent.getParent();
127 }
128 }
129 }
130
131
132 updateApproximateSelectedSize();
133 updatePageCompletion();
134 }
135
136 private void maintainCheckIntegrity(final TracePackageElement element) {
137 TracePackageElement parentElement = element.getParent();
138 boolean allChecked = true;
139 boolean oneChecked = false;
140 if (parentElement != null) {
141 if (parentElement.getChildren() != null) {
142 for (TracePackageElement child : parentElement.getChildren()) {
143 if (fElementViewer.getGrayed(child)) {
144 oneChecked = true;
145 allChecked = false;
146 break;
147 }
148 boolean checked = fElementViewer.getChecked(child);
149 oneChecked |= checked;
150 allChecked &= checked;
151 }
152 }
153 if (oneChecked && !allChecked) {
154 fElementViewer.setGrayChecked(parentElement, true);
155 } else {
156 fElementViewer.setGrayed(parentElement, false);
157 fElementViewer.setChecked(parentElement, allChecked);
158 }
159 maintainCheckIntegrity(parentElement);
160 }
161 }
162 });
163 GridData layoutData = new GridData(GridData.FILL_BOTH);
164 fElementViewer.getTree().setLayoutData(layoutData);
165 fElementViewer.setContentProvider(new TracePackageContentProvider());
166 fElementViewer.setLabelProvider(new TracePackageLabelProvider());
167 }
168
169 /**
170 * Create the input for the element viewer
171 *
172 * @return the input for the element viewer
173 */
174 protected abstract Object createElementViewerInput();
175
176 /**
177 * Create the file path group that allows the user to type or browse for a
178 * file path
179 *
180 * @param parent
181 * the parent composite
182 * @param label
183 * the label to describe the file path (i.e. import/export)
184 * @param fileDialogStyle
185 * SWT.OPEN or SWT.SAVE
186 */
187 protected void createFilePathGroup(Composite parent, String label, final int fileDialogStyle) {
188
189 Composite filePathSelectionGroup = new Composite(parent, SWT.NONE);
190 GridLayout layout = new GridLayout();
191 layout.numColumns = 3;
192 filePathSelectionGroup.setLayout(layout);
193 filePathSelectionGroup.setLayoutData(new GridData(
194 GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL));
195
196 Label destinationLabel = new Label(filePathSelectionGroup, SWT.NONE);
197 destinationLabel.setText(label);
198
199 fFilePathCombo = new Combo(filePathSelectionGroup, SWT.SINGLE
200 | SWT.BORDER);
201 GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL
202 | GridData.GRAB_HORIZONTAL);
203 data.grabExcessHorizontalSpace = true;
204 fFilePathCombo.setLayoutData(data);
205
206 fBrowseButton = new Button(filePathSelectionGroup,
207 SWT.PUSH);
208 fBrowseButton.setText(org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.Messages.TracePackage_Browse);
209 fBrowseButton.addListener(SWT.Selection, new Listener() {
210 @Override
211 public void handleEvent(Event event) {
212 handleFilePathBrowseButtonPressed(fileDialogStyle);
213 }
214 });
215 setButtonLayoutData(fBrowseButton);
216 }
217
218 /**
219 * Update the page with the file path the current file path selection
220 */
221 protected abstract void updateWithFilePathSelection();
222
223 /**
224 * Creates the buttons for selecting all or none of the elements.
225 *
226 * @param parent
227 * the parent control
228 * @return the button group
229 */
230 protected Composite createButtonsGroup(Composite parent) {
231
232 // top level group
233 Composite buttonComposite = new Composite(parent, SWT.NONE);
234
235 GridLayout layout = new GridLayout();
236 layout.numColumns = 3;
237 buttonComposite.setLayout(layout);
238 buttonComposite.setLayoutData(new GridData(GridData.VERTICAL_ALIGN_FILL
239 | GridData.HORIZONTAL_ALIGN_FILL));
240
241 fSelectAllButton = new Button(buttonComposite, SWT.PUSH);
242 fSelectAllButton.setText(org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.Messages.TracePackage_SelectAll);
243
244 SelectionListener listener = new SelectionAdapter() {
245 @Override
246 public void widgetSelected(SelectionEvent e) {
247 setAllChecked(fElementViewer, true, true);
248 updateApproximateSelectedSize();
249 updatePageCompletion();
250 }
251 };
252 fSelectAllButton.addSelectionListener(listener);
253
254 fDeselectAllButton = new Button(buttonComposite, SWT.PUSH);
255 fDeselectAllButton.setText(org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.Messages.TracePackage_DeselectAll);
256
257 listener = new SelectionAdapter() {
258 @Override
259 public void widgetSelected(SelectionEvent e) {
260 setAllChecked(fElementViewer, true, false);
261 updateApproximateSelectedSize();
262 updatePageCompletion();
263 }
264 };
265 fDeselectAllButton.addSelectionListener(listener);
266
267 return buttonComposite;
268 }
269
270 /**
271 * Restore widget values to the values that they held last time this wizard
272 * was used to completion.
273 */
274 protected void restoreWidgetValues() {
275 IDialogSettings settings = getDialogSettings();
276 if (settings != null) {
277 String[] directoryNames = settings.getArray(fStoreFilePathId);
278 if (directoryNames == null || directoryNames.length == 0) {
279 return;
280 }
281
282 for (int i = 0; i < directoryNames.length; i++) {
283 fFilePathCombo.add(directoryNames[i]);
284 }
285 }
286 }
287
288 /**
289 * Save widget values to Dialog settings
290 */
291 protected void saveWidgetValues() {
292 IDialogSettings settings = getDialogSettings();
293 if (settings != null) {
294 // update directory names history
295 String[] directoryNames = settings.getArray(fStoreFilePathId);
296 if (directoryNames == null) {
297 directoryNames = new String[0];
298 }
299
300 directoryNames = addToHistory(directoryNames, getFilePathValue());
301 settings.put(fStoreFilePathId, directoryNames);
302 }
303 }
304
305 /**
306 * Determine if the page is complete and update the page appropriately.
307 */
308 protected void updatePageCompletion() {
309 boolean pageComplete = determinePageCompletion();
310 setPageComplete(pageComplete);
311 if (pageComplete) {
312 setErrorMessage(null);
313 }
314 }
315
316 /**
317 * Determine if the page is completed or not
318 *
319 * @return true if the page is completed, false otherwise
320 */
321 protected boolean determinePageCompletion() {
322 return fElementViewer.getCheckedElements().length > 0 && !getFilePathValue().isEmpty();
323 }
324
325 /**
326 * Handle error status
327 *
328 * @param status
329 * the error status
330 */
331 protected void handleErrorStatus(IStatus status) {
332
333 Throwable exception = status.getException();
334 String message = status.getMessage().length() > 0 ? status.getMessage() : org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.Messages.TracePackage_ErrorOperation;
335
336 if (!status.isMultiStatus()) {
337 handleError(message, exception);
338 return;
339 }
340
341 // Build a string with all the children status messages, exception
342 // messages and stack traces
343 StringBuilder sb = new StringBuilder();
344 for (IStatus childStatus : status.getChildren()) {
345 StringBuilder childSb = new StringBuilder();
346 if (!childStatus.getMessage().isEmpty()) {
347 childSb.append(childStatus.getMessage() + '\n');
348 }
349
350 Throwable childException = childStatus.getException();
351 if (childException != null) {
352 String reason = childException.getMessage();
353 // Some system exceptions have no message
354 if (reason == null) {
355 reason = childException.toString();
356 }
357
358 String stackMessage = getExceptionStackMessage(childException);
359 if (stackMessage == null) {
360 stackMessage = reason;
361 }
362
363 childSb.append(stackMessage);
364 }
365
366 if (childSb.length() > 0) {
367 childSb.insert(0, '\n');
368 sb.append(childSb.toString());
369 }
370 }
371
372 // ErrorDialog only prints the call stack for a CoreException
373 exception = new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.ERROR, sb.toString(), null));
374 final Status statusWithException = new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.ERROR, org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.Messages.TracePackage_ErrorMultipleProblems, exception);
375
376 Activator.getDefault().logError(message, exception);
377 ErrorDialog.openError(getContainer().getShell(), org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.Messages.TracePackage_InternalErrorTitle, message, statusWithException);
378 }
379
380 /**
381 * Handle errors occurring in the wizard operations
382 *
383 * @param message
384 * the error message
385 * @param exception
386 * the exception attached to the message
387 */
388 protected void handleError(String message, Throwable exception) {
389 Activator.getDefault().logError(message, exception);
390 displayErrorDialog(message, exception);
391 }
392
393 private static String getExceptionStackMessage(Throwable exception) {
394 String stackMessage = null;
395 ByteArrayOutputStream baos = new ByteArrayOutputStream();
396 PrintStream ps = new PrintStream(baos);
397 exception.printStackTrace(ps);
398 ps.flush();
399 try {
400 baos.flush();
401 stackMessage = baos.toString();
402 } catch (IOException e) {
403 }
404
405 return stackMessage;
406 }
407
408 private void displayErrorDialog(String message, Throwable exception) {
409 if (exception == null) {
410 final Status s = new Status(IStatus.ERROR, Activator.PLUGIN_ID, message);
411 ErrorDialog.openError(getContainer().getShell(), org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.Messages.TracePackage_InternalErrorTitle, null, s);
412 return;
413 }
414
415 String reason = exception.getMessage();
416 // Some system exceptions have no message
417 if (reason == null) {
418 reason = exception.toString();
419 }
420
421 String stackMessage = getExceptionStackMessage(exception);
422 if (stackMessage == null || stackMessage.isEmpty()) {
423 stackMessage = reason;
424 }
425
426 // ErrorDialog only prints the call stack for a CoreException
427 CoreException coreException = new CoreException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.ERROR, stackMessage, exception));
428 final Status s = new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.ERROR, reason, coreException);
429 ErrorDialog.openError(getContainer().getShell(), org.eclipse.tracecompass.internal.tmf.ui.project.wizards.tracepkg.Messages.TracePackage_InternalErrorTitle, message, s);
430 }
431
432 /**
433 * A version of setSubtreeChecked that is aware of isEnabled
434 *
435 * @param viewer
436 * the viewer
437 * @param element
438 * the element
439 * @param enabledOnly
440 * if only enabled elements should be considered
441 * @param checked
442 * true if the item should be checked, and false if it should be
443 * unchecked
444 */
445 protected static void setSubtreeChecked(CheckboxTreeViewer viewer, TracePackageElement element, boolean enabledOnly, boolean checked) {
446 if (!enabledOnly || element.isEnabled()) {
447 viewer.setChecked(element, checked);
448 if (checked) {
449 viewer.setGrayed(element, false);
450 }
451 element.setChecked(checked);
452 if (element.getChildren() != null) {
453 for (TracePackageElement child : element.getChildren()) {
454 setSubtreeChecked(viewer, child, enabledOnly, checked);
455 }
456 }
457 }
458 }
459
460 /**
461 * Sets all items in the element viewer to be checked or unchecked
462 *
463 * @param viewer
464 * the viewer
465 * @param enabledOnly
466 * if only enabled elements should be considered
467 * @param checked
468 * whether or not items should be checked
469 */
470 protected static void setAllChecked(CheckboxTreeViewer viewer, boolean enabledOnly, boolean checked) {
471 TreeItem[] items = viewer.getTree().getItems();
472 for (int i = 0; i < items.length; i++) {
473 Object element = items[i].getData();
474 setSubtreeChecked(viewer, (TracePackageElement) element, enabledOnly, checked);
475 }
476 }
477
478 private static void addToHistory(List<String> history, String newEntry) {
479 history.remove(newEntry);
480 history.add(0, newEntry);
481
482 // since only one new item was added, we can be over the limit
483 // by at most one item
484 if (history.size() > COMBO_HISTORY_LENGTH) {
485 history.remove(COMBO_HISTORY_LENGTH);
486 }
487 }
488
489 private static String[] addToHistory(String[] history, String newEntry) {
490 ArrayList<String> l = new ArrayList<>(Arrays.asList(history));
491 addToHistory(l, newEntry);
492 String[] r = new String[l.size()];
493 l.toArray(r);
494 return r;
495 }
496
497 /**
498 * Open an appropriate file dialog so that the user can specify a file to
499 * import/export
500 * @param fileDialogStyle
501 */
502 private void handleFilePathBrowseButtonPressed(int fileDialogStyle) {
503 FileDialog dialog = TmfFileDialogFactory.create(getContainer().getShell(), fileDialogStyle | SWT.SHEET);
504 dialog.setFilterExtensions(new String[] { "*.zip;*.tar.gz;*.tar;*.tgz", "*.*" }); //$NON-NLS-1$ //$NON-NLS-2$
505 dialog.setText(Messages.TracePackage_FileDialogTitle);
506 String currentSourceString = getFilePathValue();
507 int lastSeparatorIndex = currentSourceString.lastIndexOf(File.separator);
508 if (lastSeparatorIndex != -1) {
509 dialog.setFilterPath(currentSourceString.substring(0, lastSeparatorIndex));
510 }
511 String selectedFileName = dialog.open();
512
513 if (selectedFileName != null) {
514 setFilePathValue(selectedFileName);
515 updateWithFilePathSelection();
516 }
517 }
518
519 /**
520 * Get the current file path value
521 *
522 * @return the current file path value
523 */
524 protected String getFilePathValue() {
525 return fFilePathCombo.getText().trim();
526 }
527
528 /**
529 * Set the file path value
530 *
531 * @param value
532 * file path value
533 */
534 protected void setFilePathValue(String value) {
535 fFilePathCombo.setText(value);
536 updatePageCompletion();
537 }
538
539 /**
540 * Update the approximate size of the selected elements
541 */
542 protected void updateApproximateSelectedSize() {
543 }
544
545 /**
546 * Get the element tree viewer
547 *
548 * @return the element tree viewer
549 */
550 protected CheckboxTreeViewer getElementViewer() {
551 return fElementViewer;
552 }
553
554 /**
555 * Get the file path combo box
556 *
557 * @return the file path combo box
558 */
559 protected Combo getFilePathCombo() {
560 return fFilePathCombo;
561 }
562
563 /**
564 * Get the object selection when the wizard was created
565 *
566 * @return the object selection
567 */
568 protected IStructuredSelection getSelection() {
569 return fSelection;
570 }
571 }
This page took 0.045442 seconds and 5 git commands to generate.