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