crashanalysis/crashanalyser/com.nokia.s60tools.crashanalyser/src/com/nokia/s60tools/crashanalyser/ui/views/MainView.java
changeset 0 5ad7ad99af01
child 4 615035072f7e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/crashanalysis/crashanalyser/com.nokia.s60tools.crashanalyser/src/com/nokia/s60tools/crashanalyser/ui/views/MainView.java	Thu Feb 11 15:06:45 2010 +0200
@@ -0,0 +1,974 @@
+/*
+* Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). 
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:
+*
+*/
+
+package com.nokia.s60tools.crashanalyser.ui.views;
+
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.browser.Browser;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.MessageBox;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.custom.SashForm;
+import org.eclipse.swt.layout.*;
+import org.eclipse.jface.viewers.*;
+import org.eclipse.jface.action.*;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.wizard.WizardDialog;
+import org.eclipse.ui.*;
+import org.eclipse.ui.part.*;
+import org.eclipse.swt.dnd.*;
+
+import com.nokia.s60tools.ui.*;
+import com.nokia.s60tools.util.resource.*;
+import com.nokia.s60tools.crashanalyser.model.*;
+import com.nokia.s60tools.crashanalyser.plugin.*;
+import com.nokia.s60tools.crashanalyser.resources.*;
+import com.nokia.s60tools.crashanalyser.files.*;
+import com.nokia.s60tools.crashanalyser.ui.wizards.*;
+import com.nokia.s60tools.crashanalyser.ui.dialogs.*;
+import com.nokia.s60tools.crashanalyser.data.*;
+import com.nokia.s60tools.crashanalyser.interfaces.*;
+import java.io.*;
+import java.util.*;
+
+/**
+ * Crash Analyser's main view class. Shows Crash files in a table. 
+ * 
+ * 
+ */
+public class MainView extends ViewPart implements IDecodingObserver, 
+												  IErrorLibraryObserver,
+												  INewCrashFilesObserver,
+												  ISelectionChangedListener,
+												  DropTargetListener {
+	
+	/**
+	 * We can get view ID at runtime once the view is instantiated, but we
+	 * also need static access to ID in order to be able to invoke the view.
+	 */
+	public static final String ID = "com.nokia.s60tools.crashanalyser.ui.views.MainView"; //$NON-NLS-1$
+	private TableViewer tableViewerCrashFiles;
+	private Action actionDoubleClick;
+	private Action actionOpenWizard;
+	private Action actionDecode;
+	private Action actionDeleteFiles;
+	private Action actionPanicLibrary;
+	private Action actionExportToHtml;
+	private Action actionExportToXml;
+	private Action actionExportAsHtml;
+	private Action actionExportAsXml;
+	private Action actionExportAll;
+	private String errorMessage = "";
+	private Browser browserPanicDescription;
+	private MainViewContentProvider contentProvider = null;
+	private ErrorLibrary errorLibrary = null;
+	private boolean mainViewLoaded = false;
+	private boolean showWizard = false;
+	private boolean wizardRunning = false;
+	private static SummaryFile summaryFileFromTrace = null;
+	private CrashAnalyserFile fileToBeShown = null;
+	private CrashAnalyserWizard wizard = null;
+
+	
+	public void selectionChanged(SelectionChangedEvent arg0) {
+		ISelection selection = tableViewerCrashFiles.getSelection();
+		
+		// no files selected, don't show description
+		if (selection == null || selection.isEmpty()) {
+			browserPanicDescription.setText("");
+			return;
+		}
+		
+		@SuppressWarnings("unchecked")
+		Iterator<CrashFileBundle> i = ((IStructuredSelection)selection).iterator();
+		while (i.hasNext()) {
+			CrashFileBundle cFileBundle = i.next();
+			// multiple files selected, don't show description
+			if (i.hasNext()) {
+				browserPanicDescription.setText("");
+				break;
+			// only one selected file, show description
+			} else {
+				browserPanicDescription.setText(HtmlFormatter.formatHtmlStyle(browserPanicDescription.getFont(), 
+												cFileBundle.getDescription(true)));
+			}
+		}
+	}
+
+	/**
+	 * Error library calls this method when it's ready to be used (i.e.
+	 * it has finished reading in all errors & panics from xml files)
+	 */
+	public void errorLibraryReady() {
+		contentProvider.setErrorLibrary(errorLibrary);
+		actionPanicLibrary.setEnabled(true);
+		refreshView();
+	}
+	
+	/**
+	 * MainView gets notified by this method to refresh the crash files table
+	 */
+	public void crashFilesUpdated() {
+		mainViewLoaded = true;
+		actionOpenWizard.setEnabled(true);		
+		actionDecode.setEnabled(true);
+		refreshView();
+		if (showWizard) {
+			showWizard = false;
+			showWizardIfNoFiles();
+		}
+	}
+
+	/**
+	 * The constructor.
+	 */
+	public MainView() {
+		// no implementation needed
+	}
+
+	/**
+	 * This is a callback that will allow us
+	 * to create the viewer and initialize it.
+	 */
+	public void createPartControl(Composite parent) {
+		SashForm sashFormMain = new SashForm(parent, SWT.HORIZONTAL);
+		createCrashFilesListViewTableViewer(sashFormMain);
+		createPanicsViewer(sashFormMain);
+		sashFormMain.setWeights(new int[] {2,1});
+		
+		makeActions();
+		hookContextMenu();
+		hookDoubleClickAction();
+		contributeToActionBars();
+		
+		actionOpenWizard.setEnabled(false);
+		actionDecode.setEnabled(false);
+		actionPanicLibrary.setEnabled(false);
+		errorLibrary = ErrorLibrary.getInstance(this);
+		
+		try {
+			PlatformUI.getWorkbench().getHelpSystem().setHelp(tableViewerCrashFiles.getControl(),
+				HelpContextIDs.CRASH_ANALYSER_HELP_MAIN_VIEW);
+		} catch (Exception e) {
+			e.printStackTrace();
+		}
+	}
+	
+	/**
+	 * Starts Crash Analyser wizard if there are no decoded Crash 
+	 * files in the MainView table.
+	 *
+	 */
+	public void showWizardIfNoFiles() {
+		Runnable showWizardRunnable = new Runnable(){
+			public void run(){
+				try {
+					if (mainViewLoaded) {
+						CrashFileBundle cFile = (CrashFileBundle)tableViewerCrashFiles.getElementAt(0);
+						if (cFile.isEmpty()) {
+							showWizard();
+						}
+					} else {
+						showWizard = true;
+					}
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+			}
+		};
+		
+		Display.getDefault().asyncExec(showWizardRunnable);   		
+		
+	}
+	
+	/**
+	 * Starts Crash Analyser wizard asynchronously.
+	 *
+	 */
+	public void showWizard() {
+		showWizard(null);
+	}
+
+	/**
+	 * Starts Crash Analyser wizard asynchronously.
+	 * @param filesToBeDecoded list of files which needs to be decoded with wizard, null if wizard should
+	 * be started from the beginning.
+	 *
+	 */
+	public void showWizard(List<CrashFileBundle> filesToBeDecoded) {
+		wizard = new CrashAnalyserWizard(filesToBeDecoded, errorLibrary);
+		openWizard();
+	}
+	
+	public void showWizard(String prefilledFileOrFolder, String[] filesToBeShown) {
+		wizard = new CrashAnalyserWizard(prefilledFileOrFolder, filesToBeShown, errorLibrary);
+		openWizard();
+	}
+	
+	void openWizard() {
+		Runnable showWizardRunnable = new Runnable(){
+			public void run(){
+				WizardDialog wizDialog;
+				wizDialog = new WizardDialog(getViewSite().getShell(), wizard);
+				wizDialog.create();		
+				wizDialog.getShell().setSize(500, 650);		
+				wizDialog.addPageChangedListener(wizard);				
+				wizDialog.open();		
+			}
+		};
+		
+		Display.getDefault().asyncExec(showWizardRunnable);   		
+	}
+	
+	/**
+	 * Creates the MainView table which is used for showing crash files 
+	 */
+	private void createCrashFilesListViewTableViewer(Composite parent) {
+		SashForm sashFormCrashFiles = new SashForm(parent, SWT.VERTICAL);
+		
+		List<S60ToolsTableColumnData> columnDataArr = new ArrayList<S60ToolsTableColumnData>();
+		
+		columnDataArr.add(new S60ToolsTableColumnData("Crash File", 690, 0));
+		columnDataArr.add(new S60ToolsTableColumnData("Panic Code", 70, 0)); 
+		columnDataArr.add(new S60ToolsTableColumnData("Panic Category", 90, 0)); 
+		columnDataArr.add(new S60ToolsTableColumnData("Thread", 250, 0));
+		columnDataArr.add(new S60ToolsTableColumnData("Time", 130, 0));
+		
+		S60ToolsTableColumnData[] arr 
+				= columnDataArr.toArray(new S60ToolsTableColumnData[columnDataArr.size()]);
+		
+		S60ToolsTable tbl = S60ToolsTableFactory.create(sashFormCrashFiles, arr);
+		
+		TableViewer tblViewer = new TableViewer(tbl.getTableInstance());
+		tbl.setHostingViewer(tblViewer);
+		tblViewer.addDropSupport(DND.DROP_COPY, new Transfer[] {FileTransfer.getInstance()}, this);
+
+		contentProvider = new MainViewContentProvider(this);
+		tblViewer.setContentProvider(contentProvider);
+		tblViewer.setLabelProvider(new MainViewLabelProvider());
+		tblViewer.setSorter(new ViewerSorter());
+		tblViewer.setInput(getViewSite());
+		tblViewer.addSelectionChangedListener(this);
+		
+		tableViewerCrashFiles = tblViewer;
+	}
+	
+	/**
+	 * Creates the right side view of the MainView. Contains a browser which
+	 * is used for showing information about a selected file in MainView table. 
+	 */
+	private void createPanicsViewer(Composite parent) {
+		SashForm sashFormPanics = new SashForm(parent, SWT.HORIZONTAL);
+		
+		SashForm sashFormPanicLookup = new SashForm(sashFormPanics, SWT.VERTICAL);
+
+		Composite composite = new Composite(sashFormPanicLookup, SWT.NONE);
+		
+		GridLayout gridLayout = new GridLayout();
+		gridLayout.numColumns = 1;
+		composite.setLayout(gridLayout);		
+
+		browserPanicDescription = new Browser(composite, SWT.BORDER);
+		browserPanicDescription.setLayoutData(new GridData(GridData.FILL_BOTH));
+	}
+	
+	/**
+	 * Initialize double-click action
+	 */
+	private void hookDoubleClickAction() {
+		tableViewerCrashFiles.addDoubleClickListener(new IDoubleClickListener() {
+			public void doubleClick(DoubleClickEvent event) {
+				actionDoubleClick.run();
+			}
+		});
+	}	
+
+	/**
+	 * Initialize context menu
+	 */
+	private void hookContextMenu() {
+		MenuManager menuMgr = new MenuManager("#PopupMenu"); //$NON-NLS-1$
+		menuMgr.setRemoveAllWhenShown(true);
+		menuMgr.addMenuListener(new IMenuListener() {
+			public void menuAboutToShow(IMenuManager manager) {
+				MainView.this.fillContextMenu(manager);
+				
+			}
+		});
+		Menu menu = menuMgr.createContextMenu(tableViewerCrashFiles.getControl());
+		tableViewerCrashFiles.getControl().setMenu(menu);
+		getSite().registerContextMenu(menuMgr, tableViewerCrashFiles);
+	}
+
+	/**
+	 * Action bars initializer
+	 */
+	private void contributeToActionBars() {
+		IActionBars bars = getViewSite().getActionBars();
+		fillLocalPullDown(bars.getMenuManager());
+		fillLocalToolBar(bars.getToolBarManager());
+	}
+
+	/**
+	 * Fill pull down menu
+	 */
+	private void fillLocalPullDown(IMenuManager manager) {
+		manager.add(actionOpenWizard);
+		manager.add(actionDecode);
+		manager.add(actionDeleteFiles);
+		manager.add(actionPanicLibrary);
+		MenuManager exportMenu = getExportMenu(true);
+		if (exportMenu != null)
+			manager.add(exportMenu);
+	}
+
+	/**
+	 * Fills context menu
+	 */
+	private void fillContextMenu(IMenuManager manager) {
+		manager.add(actionDecode);
+		manager.add(actionDeleteFiles);
+		MenuManager exportMenu = getExportMenu(false);
+		if (exportMenu != null)
+			manager.add(exportMenu);
+	}
+	
+	/**
+	 * Fills tool bar
+	 */
+	private void fillLocalToolBar(IToolBarManager manager) {
+		manager.add(actionOpenWizard);
+		manager.add(actionDecode);
+		manager.add(actionDeleteFiles);
+		manager.add(actionPanicLibrary);
+	}
+	
+	public void dispose() {
+		EditorHandler.closeAllEditors();
+	}
+	
+	/**
+	 * Checks based on which files are selected in MainView table, that
+	 * can Export sub-menu be shown.
+	 * @return Export menu if it can be show, null if it can't
+	 */
+	private MenuManager getExportMenu(boolean showAlways) {
+		MenuManager subMenuExport = new MenuManager("Export");
+		
+		ISelection selection = tableViewerCrashFiles.getSelection();
+		// if no files are selected, don't show export menu
+		if (!showAlways && (selection == null || selection.isEmpty()))
+			return null;
+		
+		boolean allFilesContainsXml = true;
+		boolean itemsAdded = false;
+		
+        // go through all selected files
+    	@SuppressWarnings("unchecked")
+		Iterator<CrashFileBundle> i = ((IStructuredSelection)selection).iterator();
+		while (i.hasNext()) {
+			CrashFileBundle cFileBundle = i.next();
+
+			// if only one item selected, allow export to html and xml if it contains xml file
+			if (((IStructuredSelection)selection).size() == 1 && cFileBundle.hasXml()) {
+				subMenuExport.add(actionExportToHtml);
+				subMenuExport.add(actionExportToXml);
+				itemsAdded = true;
+			}
+			
+			if (!cFileBundle.hasFiles())
+				allFilesContainsXml = false;
+			
+			// do not show Export menu if "empty file" or emulator panic is selected
+			if (cFileBundle.isEmpty() || cFileBundle.isEmulatorPanic())
+				return null;
+		}
+		
+		if (showAlways && !itemsAdded) {
+			subMenuExport.add(actionExportToHtml);
+			subMenuExport.add(actionExportToXml);
+		}
+		
+		// if all selected files contains xml file, these actions can be added
+		if (allFilesContainsXml || showAlways) {
+			subMenuExport.add(actionExportAsHtml);
+			subMenuExport.add(actionExportAsXml);
+		}
+		
+		// export all can always be added, since they will export
+		// all files which are available.
+		subMenuExport.add(actionExportAll);
+		
+		return subMenuExport;
+	}
+
+	/**
+	 * Make all actions (buttons, double-click)
+	 */
+	private void makeActions() {
+		makeDeleteFilesAction();
+		makeOpenWizardAction();
+		makeOpenPanicLibraryAction();	
+		makeExportToHtml();
+		makeExportToXml();
+		makeExportAllAction();
+		makeExportAsHtmlAction();
+		makeExportAsXmlAction();
+		makeDoubleClickAction();
+		makeDecodeFilesAction();
+	}
+	
+	/**
+	 * Double-click action
+	 */
+	private void makeDoubleClickAction() {
+		// when table item is double-clicked
+		actionDoubleClick = new Action() {
+			public void run() {
+				try {
+					if (wizardRunning)
+						return;
+					ISelection selection = tableViewerCrashFiles.getSelection();
+					Object obj = ((IStructuredSelection)selection).getFirstElement();
+					CrashFileBundle cFile = (CrashFileBundle)obj;
+					// if empty file is double-clicked, open wizard
+					if (cFile.isEmpty()) {
+						showWizard();
+					// Crash file (not empty file) is double-clicked
+					} else {
+						// fully decoded
+						if (cFile.isFullyDecoded())
+							EditorHandler.openCrashAnalyserEditor(cFile.getCrashFile());
+						// partially decoded
+						else if (cFile.isPartiallyDecoded())
+							EditorHandler.openCrashAnalyserEditor(cFile.getSummaryFile());
+					}
+				} catch (Exception e) {
+					e.printStackTrace();
+				}
+			}
+		};
+	}
+
+	/**
+	 * Export selected file to html file
+	 */
+	private void makeExportToHtml() {
+		actionExportToHtml = new Action() {
+			public void run() {
+				ISelection selection = tableViewerCrashFiles.getSelection();
+				FileExportManager.ExportSelectedFileToHtml(selection, getShell());
+			}
+		};
+		actionExportToHtml.setText("to HTML File");
+	}
+
+	/**
+	 * Export selected file to .xml or .crashxml
+	 */
+	private void makeExportToXml() {
+		actionExportToXml = new Action() {
+			public void run() {
+				ISelection selection = tableViewerCrashFiles.getSelection();
+				FileExportManager.ExportSelectedFileToXml(selection, getShell());
+			}
+		};
+		actionExportToXml.setText("to XML File");
+	}
+
+	/**
+	 * Export to Zip as Xml
+	 */
+	private void makeExportAsXmlAction() {
+		actionExportAsXml = new Action() {
+			public void run() {
+				ISelection selection = tableViewerCrashFiles.getSelection();
+				FileExportManager.ExportSelectedFilesAsXmlToZip(selection, getShell());
+			}
+		};
+		actionExportAsXml.setText("to Zip as XML");
+	}
+
+	/**
+	 * Export to Zip as Html
+	 */
+	private void makeExportAsHtmlAction() {
+		actionExportAsHtml = new Action() {
+			public void run() {
+				ISelection selection = tableViewerCrashFiles.getSelection();
+				FileExportManager.ExportSelectedFilesAsHtmlToZip(selection, getShell());
+			}
+		};
+		actionExportAsHtml.setText("to Zip as HTML");
+	}
+
+	/**
+	 * Export all formats to zip
+	 */
+	private void makeExportAllAction() {
+		actionExportAll = new Action() {
+			public void run() {
+				ISelection selection = tableViewerCrashFiles.getSelection();
+				FileExportManager.ExportSelectedFilesToZipInAllFormats(selection, getShell());
+			}
+		};
+		actionExportAll.setText("All Formats to Zip");
+	}
+
+	/**
+	 * Open Error Library button
+	 */
+	private void makeOpenPanicLibraryAction() {
+		actionPanicLibrary = new Action() {
+			public void run() {
+				ErrorLibraryDialog dlg = new ErrorLibraryDialog(tableViewerCrashFiles.getControl().getShell(), errorLibrary);
+				dlg.open();
+			}
+		};
+		actionPanicLibrary.setText("Open Error Library...");
+		actionPanicLibrary.setToolTipText("Open Error Library Window");
+		actionPanicLibrary.setImageDescriptor(ImageResourceManager.getImageDescriptor(ImageKeys.ERROR_LIBRARY));
+	}
+
+	/**
+	 * Import Files button
+	 */
+	private void makeOpenWizardAction() {
+		// Open wizard button
+		actionOpenWizard = new Action() {
+			public void run() {
+				showWizard();
+			}
+		};
+		actionOpenWizard.setText("Import Files...");
+		actionOpenWizard.setToolTipText("Open File Import Wizard");
+		actionOpenWizard.setImageDescriptor(ImageResourceManager.getImageDescriptor(ImageKeys.IMG_APP_ICON));		
+	}
+	
+	/**
+	 * Starts decoding process for the given file by opening the wizard for given file.
+	 * @param f file to be decoded
+	 */
+	public void decodeFile(SummaryFile f) {
+		int sep = f.getFilePath().lastIndexOf(File.separator);
+		CrashFileBundle cfb = new CrashFileBundle(f.getFilePath().substring(0, sep), errorLibrary);
+		List<CrashFileBundle> files = new ArrayList<CrashFileBundle>();
+		files.add(cfb);
+		EditorHandler.closeEditors(files);
+		showWizard(files);
+	}
+	
+	/**
+	 * Decode files button
+	 */
+	private void makeDecodeFilesAction() {
+		actionDecode = new Action() {
+			public void run() {
+				ISelection selection = tableViewerCrashFiles.getSelection();
+				// if nothing is selected, just ignore button press
+				if (selection == null || selection.isEmpty())
+					return;
+
+				String romId = "";
+				// go through all files and check that they can be re-decoded
+				List<CrashFileBundle> files = new ArrayList<CrashFileBundle>();
+				@SuppressWarnings("unchecked")
+				Iterator i = ((IStructuredSelection)selection).iterator();
+				while (i.hasNext()) {
+					CrashFileBundle cFileBundle = (CrashFileBundle)i.next();
+					UndecodedFile udf = cFileBundle.getUndecodedFile();
+					// must have original binaries to be able to re-decode
+					if (udf == null) {
+						errorMessage = "All selected files do not have original binaries available. Cannot Decode.";
+						showErrorMessage();
+						return;
+					}
+					
+					// only mobilecrash files can be re-decoded (e.g. D_EXC files can't)
+					if (!udf.getFileName().toLowerCase().endsWith("."+CrashAnalyserFile.MOBILECRASH_FILE_EXTENSION) &&
+						!udf.getFileName().toLowerCase().endsWith("."+CrashAnalyserFile.TRACE_EXTENSION)) {
+						errorMessage = "Only MobileCrash files can be Decoded.";
+						showErrorMessage();
+						return;
+					}
+					
+					// rom id hasn't yet been read
+					if ("".equals(romId)) {
+						romId = cFileBundle.getRomId();
+					// rom ids of selected files must match
+					} else if (!romId.equalsIgnoreCase(cFileBundle.getRomId())) {
+						errorMessage = "Select only files which have same ROM IDs";
+						showErrorMessage();
+						return;
+					}
+					
+					files.add(cFileBundle);
+				}
+				
+				if (!files.isEmpty()) {
+					EditorHandler.closeEditors(files);
+					showWizard(files);
+				}
+			}
+		};
+		actionDecode.setText("Decode Files...");
+		actionDecode.setToolTipText("Decode Selected Files");
+		actionDecode.setImageDescriptor(ImageResourceManager.getImageDescriptor(ImageKeys.DECODE_FILES));
+	}
+
+	/**
+	 * Delete Files button
+	 */
+	private void makeDeleteFilesAction() {
+		// Delete file button
+		actionDeleteFiles = new Action() {
+			public void run() {
+				ISelection selection = tableViewerCrashFiles.getSelection();
+				if (selection == null || selection.isEmpty())
+					return;
+				
+				// Confirm file delete
+				MessageBox messageBox = new MessageBox(tableViewerCrashFiles.getControl().getShell(), SWT.ICON_QUESTION | SWT.YES | SWT.NO);
+		        messageBox.setText("Crash Analyser - Delete Files");
+		        messageBox.setMessage("Are you sure you want to delete selected files?");
+		        int buttonID = messageBox.open();
+		        if (buttonID == SWT.YES) {
+			        // go through all selected files and remove them
+		        	List<CrashFileBundle> closeFiles = new ArrayList<CrashFileBundle>();
+		        	@SuppressWarnings("unchecked")
+					Iterator<CrashFileBundle> i = ((IStructuredSelection)selection).iterator();
+					while (i.hasNext()) {
+						CrashFileBundle cFileBundle = i.next();
+						if (!cFileBundle.isEmpty()) {
+							cFileBundle.delete();
+							closeFiles.add(cFileBundle);
+						}
+					}
+					EditorHandler.closeEditors(closeFiles);
+					contentProvider.refresh();
+				}
+
+		        // All items were removed, add empty item
+				if (tableViewerCrashFiles.getTable().getItemCount() == 0) {
+					CrashFileBundle empty = new CrashFileBundle(true);
+					tableViewerCrashFiles.add(empty);
+				}
+			}
+		};
+		actionDeleteFiles.setText("Delete Files");
+		actionDeleteFiles.setToolTipText("Delete Selected Files");
+		actionDeleteFiles.setImageDescriptor(PlatformUI.getWorkbench().getSharedImages().getImageDescriptor(ISharedImages.IMG_TOOL_DELETE));
+	}
+
+	/**
+	 * Shows a message box with given message
+	 * @param message
+	 */
+	private void showMessage(String message) {
+		MessageDialog.openInformation(
+			tableViewerCrashFiles.getControl().getShell(),
+			"Crash Analyser",
+			message);
+	}
+	
+	/**
+	 * Shows an error message asynchronously.
+	 *
+	 */
+	private void showErrorMessage() {
+		Runnable showErrorMessageRunnable = new Runnable(){
+			public void run(){
+				MessageDialog.openError(
+						tableViewerCrashFiles.getControl().getShell(),
+						"Crash Analyser",
+						errorMessage);
+				errorMessage = ""; //$NON-NLS-1$
+			}
+		};
+		
+		// Has to be done in its own thread
+		// in order not to cause invalid thread access
+		Display.getDefault().asyncExec(showErrorMessageRunnable);        		
+	}
+	
+	/**
+	 * Returns a shell
+	 * @return a shell
+	 */
+	private Shell getShell() {
+		return tableViewerCrashFiles.getControl().getShell();
+	}
+
+	/**
+	 * Passing the focus request to the viewer's control.
+	 */
+	public void setFocus() {
+		tableViewerCrashFiles.getControl().setFocus();
+	}
+	
+	/**
+	 * When wizard is finished, wizard passes decoder engine to MainView so that 
+	 * MainView can start decoding process and register itself as the progress listener.
+	 * @param decoder decoder engine
+	 */
+	public void startDecoding(DecoderEngine decoder) {
+		disableUIForImport(true);
+		try {
+			decoder.decode(this);
+		} catch (Exception e) {
+			disableUIForImport(false);
+			showMessage("Processing Failed: "+e.getMessage());
+			setMainViewVisible();
+		}
+	}
+	
+	/**
+	 * While import is in progess, some actions should be disabled. This method
+	 * enables/disables necessary actions after/before import.
+	 * @param disable
+	 */
+	void disableUIForImport(boolean disable) {
+		actionDecode.setEnabled(!disable);
+		actionOpenWizard.setEnabled(!disable);
+		actionDeleteFiles.setEnabled(!disable);
+		wizardRunning = disable;
+	}
+	
+	/**
+	 * Makes Crash Analyser view visible asynchronously.
+	 *
+	 */
+	private void setMainViewVisible() {
+		Runnable decodingFinishedRunnable = new Runnable(){
+			public void run(){
+				updateView();
+				if (fileToBeShown != null) {
+					if (fileToBeShown instanceof CrashFile) {
+						EditorHandler.openCrashAnalyserEditor((CrashFile)fileToBeShown);
+					} else if (fileToBeShown instanceof SummaryFile) {
+						EditorHandler.openCrashAnalyserEditor((SummaryFile)fileToBeShown);
+					}
+					fileToBeShown = null;
+				}
+			}
+		};
+		
+		// Has to be done in its own thread
+		// in order not to cause invalid thread access
+		Display.getDefault().asyncExec(decodingFinishedRunnable);        		
+	}
+	
+	/**
+	 * Refreshes view asynchronously.
+	 */
+	private void refreshView() {
+		Runnable refreshRunnable = new Runnable(){
+			public void run(){
+				tableViewerCrashFiles.refresh();
+			}
+		};
+		
+		// Has to be done in its own thread
+		// in order not to cause invalid thread access
+		Display.getDefault().asyncExec(refreshRunnable);        		
+	}
+	
+	/**
+	 * Reloads Crash files to table
+	 *
+	 */
+	private void updateView() {
+		try {
+			getViewSite().getPage().showView(MainView.ID);
+			tableViewerCrashFiles.refresh();
+		} catch (Exception e) {			
+			e.printStackTrace();
+		}
+	}
+
+	/**
+	 * Used by Decoder Engine to inform when decoding is finished.
+	 * @param error error message if something went wrong with decoding. Empty if no error occurred
+	 */
+	public void decodingFinished(String error, CrashAnalyserFile caFile) {
+		disableUIForImport(false);
+		// no errors while decoding
+		if ("".equals(error)) { //$NON-NLS-1$
+			contentProvider.refresh();
+			setMainViewVisible();
+			fileToBeShown = caFile;
+		// there were errors in decoding process
+		} else {
+			errorMessage = error;
+			showErrorMessage();
+			contentProvider.refresh();
+		}
+	}
+	
+	/**
+	 * Refreshes table
+	 */
+	public void refresh() {
+		contentProvider.refresh();
+		tableViewerCrashFiles.refresh();
+	}
+	
+	public void dragEnter(DropTargetEvent event) {
+		event.detail = DND.DROP_COPY;
+	}
+
+	public void dragLeave(DropTargetEvent event) {
+		// Nothing to be done
+	}
+
+	public void dragOperationChanged(DropTargetEvent event) {
+		// Nothing to be done
+	}
+
+	public void dragOver(DropTargetEvent event) {
+		event.feedback = DND.FEEDBACK_NONE;
+	}
+
+	public void drop(DropTargetEvent event) {
+		// we accept only file drops
+		if (FileTransfer.getInstance().isSupportedType(event.currentDataType)) {
+			if (event.data != null) {
+				String[] files = (String[])event.data;
+				executeDrop(files);
+			}
+		}
+	}
+
+	public void dropAccept(DropTargetEvent event) {
+		// Nothing to be done
+	}
+	
+	/**
+	 * Executes drop functionality when files are drag&dropped to main view.
+	 * Checks if supported files where dropped and starts wizard accordingly.
+	 * @param files drag&dropped files (paths)
+	 */
+	void executeDrop(String[] files) {
+		// just one (supported) file dropped
+		if (files.length == 1 && DecoderEngine.isFileValidCrashFile(files[0])) {
+			showWizard(files[0], files);
+		// multiple files dropped
+		} else if (files.length > 1) {
+			String path = "";
+			// go through all dropped files, and check that they are from same folder
+			// (wizard doesn't know how to handle multiple files from multiple locations)
+			for (int i = 0; i < files.length; i++) {
+				if (DecoderEngine.isFileValidCrashFile(files[i])) {
+					if ("".equals(path)) {
+						path = FileOperations.getFolder(files[i]);
+					} else if (!FileOperations.getFolder(files[i]).equalsIgnoreCase(path)){
+						showMessage("Multiple files from different folders are not supported");
+						break;
+					}
+				} else {
+					showMessage("Unsupported file type");
+					break;
+				}
+			}
+			showWizard(path, files);
+		} else {
+			showMessage("Unsupported file type");
+		}
+	}
+
+    /**
+     * Opens MainView if it is not open. Can be called from a non-UI thread.
+     */
+    public static void showOrRefresh() {
+    	
+		PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+			public void run() {
+				MainView mv = showAndReturnYourself(true);
+				mv.refresh();
+			}
+		}
+		);
+    }
+    
+    /**
+     * Opens MainView if it is not open. Can be called from a non-UI thread.
+     */
+    public static void showOrRefreshAndOpenFile(SummaryFile summaryFile) {
+    	MainView.summaryFileFromTrace = summaryFile;
+		PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
+			public void run() {
+				MainView mv = showAndReturnYourself(true);
+				mv.refresh();
+				EditorHandler.openCrashAnalyserEditor(summaryFileFromTrace);
+			}
+		}
+		);
+    }
+
+    /**
+     * Makes main view visible and returns an instance of itself. Can be called from
+     * an UI thread only.
+     * @return instance of main view
+     */
+    public static MainView showAndReturnYourself() {
+    	return showAndReturnYourself(false);
+    }
+    
+    /**
+     * Makes main view visible and returns an instance of itself. Can be called from
+     * an UI thread only.
+     * @return instance of main view
+     */
+	public static MainView showAndReturnYourself(boolean openOnly) {
+    	try {
+    		
+    		IWorkbenchWindow ww = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
+    		if (ww == null)
+    			return null;
+    		
+    		IWorkbenchPage page = ww.getActivePage();
+    		IWorkbenchPart currentPart = page.getActivePart();
+    		
+    		// Checking if view is already open
+    		IViewReference[] viewRefs = page.getViewReferences();
+    		for (int i = 0; i < viewRefs.length; i++) {
+				IViewReference reference = viewRefs[i];
+				String id = reference.getId();
+				if(id.equalsIgnoreCase(MainView.ID)){
+					// Found, restoring the view
+					IViewPart viewPart = reference.getView(true);
+					if (!openOnly)
+						page.activate(viewPart);
+					return (MainView)viewPart;
+				}
+			}
+    		
+    		// View was not found, opening it up as a new view.
+    		MainView mView = (MainView)page.showView(MainView.ID);
+    		if (openOnly)
+    			page.bringToTop(currentPart);
+    		return mView;
+    		
+    	} catch (Exception e) {
+			e.printStackTrace();
+			
+			return null;
+		}
+	}
+}
\ No newline at end of file