All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.sf.okapi.lib.ui.verification.QualityCheckEditor Maven / Gradle / Ivy

/*===========================================================================
  Copyright (C) 2010-2017 by the Okapi Framework contributors
-----------------------------------------------------------------------------
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

  http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
===========================================================================*/

package net.sf.okapi.lib.ui.verification;

import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Timer;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.StyleRange;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.ControlAdapter;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.MenuEvent;
import org.eclipse.swt.events.MenuListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.ShellListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Text;

import net.sf.okapi.common.BaseContext;
import net.sf.okapi.common.IHelp;
import net.sf.okapi.common.LocaleId;
import net.sf.okapi.common.UserConfiguration;
import net.sf.okapi.common.Util;
import net.sf.okapi.common.annotation.IssueType;
import net.sf.okapi.common.filters.IFilterConfigurationMapper;
import net.sf.okapi.common.resource.Code;
import net.sf.okapi.common.resource.RawDocument;
import net.sf.okapi.common.ui.AboutDialog;
import net.sf.okapi.common.ui.Dialogs;
import net.sf.okapi.common.ui.InputDocumentDialog;
import net.sf.okapi.common.ui.MRUList;
import net.sf.okapi.common.ui.ResourceManager;
import net.sf.okapi.common.ui.TextOptions;
import net.sf.okapi.common.ui.UIUtil;
import net.sf.okapi.lib.verification.IQualityCheckEditor;
import net.sf.okapi.lib.verification.Issue;
import net.sf.okapi.lib.verification.IssueComparator;
import net.sf.okapi.lib.verification.Parameters;
import net.sf.okapi.lib.verification.QualityCheckSession;

public class QualityCheckEditor implements IQualityCheckEditor {

	static final int ISSUETYPE_ALL = 0;
	static final int ISSUETYPE_ENABLED = 1;
	static final int ISSUETYPE_DISABLED = 2;
	
	static final int CONTEXTMENU_COUNT = 8; // All the "fix" entries 
	
	private static final String OPT_BOUNDS = "bounds"; //$NON-NLS-1$
	private static final String OPT_MAXIMIZED = "maximized"; //$NON-NLS-1$
	private static final String APPNAME = "CheckMate"; //$NON-NLS-1$
	private static final String CFG_SOURCELOCALE = "sourceLocale"; //$NON-NLS-1$
	private static final String CFG_TARGETLOCALE = "targetLocale"; //$NON-NLS-1$
	private static final String CFG_SOURCETEXTOPTIONS = "sourceTextOptions"; //$NON-NLS-1$
	private static final String CFG_TARGETTEXTOPTIONS = "targetTextOptions"; //$NON-NLS-1$
	
	private String qcsPath;
	private UserConfiguration config;
	private MRUList mruList;
	private MenuItem miMRU;
	private IHelp help;
	private Shell shell;
	private ResourceManager rm;
	private Table tblIssues;
	private Text edMessage;
	private Combo cbDocument;
	private SashForm sashDisplay;
	private StyledText edSource;
	private StyledText edTarget;
	private TextOptions srcTextOpt;
	private TextOptions trgTextOpt;
	private IssuesTableModel issuesModel;
	private StatusBar statusBar;
	private QualityCheckSession session;
	private Combo cbDisplay;
	private Combo cbTypes;
	private int waitCount;
	private Menu contextMenu;
	private SelectionAdapter allowExtraCodesAdapter;
	private SelectionAdapter allowMissingCodesAdapter;
	private SelectionAdapter copyItemDataAdapter;
	private Timer watcherTimer;
	private Watcher watcher;
	
	private int displayType = 1;
	private int issueType = 0;

	/**
	 * Creates a default editor, to allow dynamic instantiation.
	 * Either {@link #edit(boolean)} or
	 * {@link #initialize(Object, boolean, IHelp, IFilterConfigurationMapper, QualityCheckSession)} must be called
	 * afterward. 
	 */
	public QualityCheckEditor () {
	}
	
	@Override
	public void edit (boolean processOnStart) {
		showDialog(null, processOnStart);
	}
	
	/**
	 * Initializes this IQualityCheckEditor object.
	 * @param parent the object representing the parent window/shell for this editor.
	 * In this implementation  this parameter must be the Shell of the caller.
	 * @param asDialog true if used from another program.
	 * @param helpParam the help engine to use.
	 * @param fcMapper the IFilterConfigurationMapper object to use with the editor.
	 * @param paramSession an optional session to use (null to use one created internally)
	 */
	@Override
	public void initialize (Object parent,
		boolean asDialog,
		IHelp helpParam,
		IFilterConfigurationMapper fcMapper,
		QualityCheckSession paramSession)
	{
		help = helpParam;
		config = new UserConfiguration();
		config.load(APPNAME);
		mruList = new MRUList(9);
		mruList.getFromProperties(config);
		
		// If no parent is defined, create a new display and shell
		if ( parent == null ) {
			// Start the application
			Display dispMain = new Display();
			parent = new Shell(dispMain);
		}

		// Set or create the session
		if ( paramSession == null ) {
			session = new QualityCheckSession();
		}
		else {
			session = paramSession;
		}
		session.setFilterConfigurationMapper(fcMapper);
		try {
			LocaleId tmpLoc = LocaleId.fromString(config.getProperty(CFG_SOURCELOCALE, "en"));
			session.setSourceLocale(tmpLoc);
			tmpLoc = LocaleId.fromString(config.getProperty(CFG_TARGETLOCALE, "fr"));
			session.setTargetLocale(tmpLoc);
			session.setModified(false);
		}
		catch ( Throwable e ) {
			// Just use the defaults, no need to have an error
		}

		if ( asDialog ) {
			shell = new Shell((Shell)parent, SWT.CLOSE | SWT.TITLE | SWT.RESIZE | SWT.MAX | SWT.MIN | SWT.APPLICATION_MODAL);
		}
		else {
			shell = (Shell)parent;
		}
		shell.setLayout(new GridLayout());

		rm = new ResourceManager(QualityCheckEditor.class, shell.getDisplay());
		rm.loadCommands("net.sf.okapi.lib.ui.verification.Commands"); //$NON-NLS-1$

		rm.addImages("checkmate", "checkmate16", "checkmate32"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
		shell.setImages(rm.getImages("checkmate")); //$NON-NLS-1$
		
		createMenus();
		createContent();
		
		if ( asDialog ) {
			Dialogs.centerWindow(shell, (Shell)parent);
		}
		tblIssues.setFocus();
	}
	
	@Override
	public void addRawDocument (RawDocument rawDoc) {
		session.addRawDocument(rawDoc);
	}
	
	@Override
	protected void finalize () {
		dispose();
	}

	private void deleteWatcher () {
		if ( watcherTimer != null ) {
			watcherTimer.cancel();
			watcherTimer.purge();
			watcherTimer = null;
		}
	}
	private void dispose () {
		deleteWatcher();
		if ( srcTextOpt != null ) {
			srcTextOpt.dispose();
			srcTextOpt = null;
		}
		if ( trgTextOpt != null ) {
			trgTextOpt.dispose();
			trgTextOpt = null;
		}
		if ( rm != null ) {
			rm.dispose();
			rm = null;
		}
	}

	/**
	 * Opens the dialog box, loads an QC session if one is specified.
	 * @param path Optional QC session to load. Use null to load nothing.
	 * @param processOnStart true to trigger the verification process when the editor is opened.
	 */
	public void showDialog (String path,
		boolean processOnStart)
	{
		shell.open();
		if ( path != null ) {
			String ext = Util.getExtension(path);
			if ( ext.equalsIgnoreCase(QualityCheckSession.FILE_EXTENSION) ) {
				loadSession(path);
				// Loading a session always trigger a re-processing
				// So don't call it here again
			}
			else {
				addDocumentFromUI(path, false, false);
				if ( processOnStart ) checkAll();
			}
		}
		else {
			if ( processOnStart ) {
				fillDocumentCombo(null);
				checkAll();
			}
		}
		
		while ( !shell.isDisposed() ) {
			if ( !shell.getDisplay().readAndDispatch() )
				shell.getDisplay().sleep();
		}
		dispose();
	}
	
	private void createMenus () {
		// Menus
	    Menu menuBar = new Menu(shell, SWT.BAR);
		shell.setMenuBar(menuBar);

		//=== File menu
		
		MenuItem topItem = new MenuItem(menuBar, SWT.CASCADE);
		topItem.setText(rm.getCommandLabel("file")); //$NON-NLS-1$
		Menu dropMenu = new Menu(shell, SWT.DROP_DOWN);
		topItem.setMenu(dropMenu);
		
		MenuItem menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "file.new"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				newSession();
            }
		});

		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "file.open"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				loadSession(null);
            }
		});

		new MenuItem(dropMenu, SWT.SEPARATOR);

		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "file.save"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				saveSessionAs(qcsPath);
            }
		});

		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "file.saveas"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				saveSessionAs(null);
            }
		});

		new MenuItem(dropMenu, SWT.SEPARATOR);

		miMRU = new MenuItem(dropMenu, SWT.CASCADE);
		rm.setCommand(miMRU, "file.mru"); //$NON-NLS-1$
		
		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "file.clearmru"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
            	clearMRU();
            }
		});
		
		new MenuItem(dropMenu, SWT.SEPARATOR);

		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "file.adddocument"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				addDocumentFromUI(null, false, false);
            }
		});

		new MenuItem(dropMenu, SWT.SEPARATOR);

		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "file.sessionsettings"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				editSessionSettings();
            }
		});

		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "file.preferences"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				editPreferences();
            }
		});

		new MenuItem(dropMenu, SWT.SEPARATOR);

		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "file.exit"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				shell.close();
            }
		});

		//=== Issues menu

		topItem = new MenuItem(menuBar, SWT.CASCADE);
		topItem.setText(rm.getCommandLabel("issues")); //$NON-NLS-1$
		dropMenu = new Menu(shell, SWT.DROP_DOWN);
		topItem.setMenu(dropMenu);
		
		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "issues.resetdisabledissues"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				resetDisabledIssues();
            }
		});
		
		new MenuItem(dropMenu, SWT.SEPARATOR);

		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "issues.editConfiguration"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				editConfiguration();
            }
		});
		
		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "issues.saveConfiguration"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				saveConfiguration();
            }
		});

		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "issues.loadConfiguration"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				loadConfiguration();
            }
		});

		new MenuItem(dropMenu, SWT.SEPARATOR);
		
		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "issues.checkall"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				checkAll();
            }
		});
		
		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "issues.checkdocument"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				checkCurrentDocument();
            }
		});
		
		new MenuItem(dropMenu, SWT.SEPARATOR);

		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "issues.generatereport"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				generateReport();
            }
		});
		
		//=== Help menu

		topItem = new MenuItem(menuBar, SWT.CASCADE);
		topItem.setText(rm.getCommandLabel("help")); //$NON-NLS-1$
		dropMenu = new Menu(shell, SWT.DROP_DOWN);
		topItem.setMenu(dropMenu);
		
		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "help.topics"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				if ( help != null ) help.showWiki("CheckMate");
            }
		});
		
		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "help.howtouse"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				if ( help != null ) help.showWiki("CheckMate - Usage");
			}
		});

		new MenuItem(dropMenu, SWT.SEPARATOR);

		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "help.update"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				UIUtil.start("http://okapiframework.org/updates.html?" //$NON-NLS-1$
					+ getClass().getPackage().getImplementationTitle()
					+ "=" //$NON-NLS-1$
					+ getClass().getPackage().getImplementationVersion());
			}
		});

		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "help.feedback"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				UIUtil.start("mailto:[email protected]&subject=Feedback (CheckMate: Quality Checker)"); //$NON-NLS-1$
            }
		});
		
		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "help.bugreport"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				UIUtil.start("https://bitbucket.org/okapiframework/okapi/issues"); //$NON-NLS-1$
			}
		});
		
		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "help.featurerequest"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				UIUtil.start("https://bitbucket.org/okapiframework/okapi/issues"); //$NON-NLS-1$
			}
		});
		
		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "help.users"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				UIUtil.start("http://groups.yahoo.com/group/okapitools/"); //$NON-NLS-1$
            }
		});

		new MenuItem(dropMenu, SWT.SEPARATOR);

		menuItem = new MenuItem(dropMenu, SWT.PUSH);
		rm.setCommand(menuItem, "help.about"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				AboutDialog dlg = new AboutDialog(shell,
					"About CheckMate",
					"CheckMate - Okapi Quality Checker",
					getClass().getPackage().getImplementationVersion());
				dlg.showDialog();
            }
		});
	}

	private Menu createIssuesContextMenu () {
		// Context menu for the input list
		contextMenu = new Menu(shell, SWT.POP_UP);
		
		MenuItem menuItem = new MenuItem(contextMenu, SWT.PUSH);
		rm.setCommand(menuItem, "context.opendocument"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				openDocument();
            }
		});

		menuItem = new MenuItem(contextMenu, SWT.PUSH);
		rm.setCommand(menuItem, "context.openfolder"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				openContainingFolder();
            }
		});

		new MenuItem(contextMenu, SWT.SEPARATOR);

		menuItem = new MenuItem(contextMenu, SWT.PUSH);
		rm.setCommand(menuItem, "issues.editConfiguration"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				editConfiguration();
            }
		});

		menuItem = new MenuItem(contextMenu, SWT.PUSH);
		rm.setCommand(menuItem, "issues.checkall"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				checkAll();
            }
		});
		
		menuItem = new MenuItem(contextMenu, SWT.PUSH);
		rm.setCommand(menuItem, "issues.checkdocument"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				checkCurrentDocument();
            }
		});

		new MenuItem(contextMenu, SWT.SEPARATOR);

		menuItem = new MenuItem(contextMenu, SWT.PUSH);
		rm.setCommand(menuItem, "file.adddocument"); //$NON-NLS-1$
		menuItem.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				addDocumentFromUI(null, false, false);
            }
		});
		
		// Adapter that add extra codes to exception list
		allowExtraCodesAdapter = new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				// The menu item has a data object that contains the string to add
				List list = session.getParameters().getExtraCodesAllowed();
				String code = (String)((MenuItem)event.getSource()).getData();
				if ( list.contains(code) ) return;
				list.add(code);
				session.setModified(true); // We have modified the session data
            }
		};
		
		// Adapter that add missing codes to exception list
		allowMissingCodesAdapter = new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				// The menu item has a data object that contains the string to add
				List list = session.getParameters().getMissingCodesAllowed();
				String code = (String)((MenuItem)event.getSource()).getData();
				if ( list.contains(code) ) return;
				list.add(code);
				session.setModified(true); // We have modified the session data
            }
		};
		
		copyItemDataAdapter = new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				// The menu item has a data object that we need to put into the Clipboard
				String data = (String)((MenuItem)event.getSource()).getData();
				Clipboard cb = new Clipboard(shell.getDisplay());
				cb.setContents(new Object[] {data}, new Transfer[] {TextTransfer.getInstance()});
            }
		};
		
		// Add listener to manage the actions
		contextMenu.addMenuListener(new MenuListener() {
			@Override
			public void menuShown(MenuEvent arg0) {
				// Clear possible previous menu entries
				clearContextMenu();
				int n = tblIssues.getSelectionIndex();
				if ( n == -1 ) return;
				Issue issue = (Issue)tblIssues.getItem(n).getData();
				
				// Copy TU id
				MenuItem item = new MenuItem(contextMenu, SWT.PUSH);
				item.setText(String.format("Copy Text Unit Extraction ID (\"%s\")", issue.getTuId()));
				item.setData(issue.getTuId());
				item.addSelectionListener(copyItemDataAdapter);
				
				// Copy resource name (if available)
				if ( !Util.isEmpty(issue.getTuName()) ) {
					item = new MenuItem(contextMenu, SWT.PUSH);
					item.setText(String.format("Copy Text Unit Resource ID/Name (\"%s\")", issue.getTuName()));
					item.setData(issue.getTuName());
					item.addSelectionListener(copyItemDataAdapter);
				}

				// Extra data cases
				if ( Util.isEmpty(issue.getCodes()) ) return;
				// If we have extra data attached to the issue:
				// Add actions to the menu
				new MenuItem(contextMenu, SWT.SEPARATOR);
				for ( Code code : issue.getCodes() ) {
					item = new MenuItem(contextMenu, SWT.PUSH);
					item.setData(code.getData());
					if ( issue.getIssueType() == IssueType.EXTRA_CODE ) {
						item.setText(String.format("Allow \"%s\" as an Extra Code", code.getData()));
						item.addSelectionListener(allowExtraCodesAdapter);
					}
					else {
						item.setText(String.format("Allow \"%s\" as a Missing Code", code.getData()));
						item.addSelectionListener(allowMissingCodesAdapter);
					}
				}
			}
			@Override
			public void menuHidden(MenuEvent arg0) {
				// Done by clearContextMenu() at the next display of the menu
			}
		});

		return contextMenu;
	}

	/**
	 * Clears the extra items of the context menu.
	 * Extra items need to stay after the menu is closed so if one is selected it
	 * can be acted upon. So the clear is done before showing the NEXT time around.
	 */
	private void clearContextMenu () {
		MenuItem[] list = contextMenu.getItems();
		if ( list.length <= CONTEXTMENU_COUNT ) return;
		// Else: remove extra items
		int i = list.length-1;
		while ( i >= CONTEXTMENU_COUNT ) {
			contextMenu.getItem(i).dispose();
			i--;
		}
	}
	
	private void createContent () {
		// Handling of the closing event
		shell.addShellListener(new ShellListener() {
			public void shellActivated(ShellEvent event) {}
			public void shellClosed(ShellEvent event) {
				if ( !saveSessionIfNeeded() ) event.doit = false;
			}
			public void shellDeactivated(ShellEvent event) {}
			public void shellDeiconified(ShellEvent event) {}
			public void shellIconified(ShellEvent event) {}
		});

		// Create the two main parts of the UI
		SashForm sashMain = new SashForm(shell, SWT.VERTICAL);
		sashMain.setLayoutData(new GridData(GridData.FILL_BOTH));
		sashMain.setSashWidth(4);
		//Not needed: sashMain.setBackground(shell.getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW));
		
		// Drop target for the table
		DropTarget dropTarget = new DropTarget(sashMain, DND.DROP_DEFAULT | DND.DROP_COPY | DND.DROP_MOVE);
		dropTarget.setTransfer(new FileTransfer[]{FileTransfer.getInstance()}); 
		dropTarget.addDropListener(new DropTargetAdapter() {
			public void drop (DropTargetEvent e) {
				FileTransfer FT = FileTransfer.getInstance();
				if ( FT.isSupportedType(e.currentDataType) ) {
					String[] paths = (String[])e.data;
					if ( paths != null ) {
						boolean acceptAll = false;
						for ( String path : paths ) {
							Boolean res;
							if ((res = addDocumentFromUI(path, paths.length>1, acceptAll)) == null ) {
								return; // Stop now
							}
							// Else use the result to set the next value of the accept-all button
							acceptAll = res;
						}
					}
				}
			}
		});

		//--- Edit panel
		
		Composite cmpTmp = new Composite(sashMain, SWT.NONE);
		cmpTmp.setLayoutData(new GridData(GridData.FILL_BOTH));
		GridLayout layTmp = new GridLayout();
		layTmp.marginWidth = 0;
		layTmp.marginHeight = 0;
		cmpTmp.setLayout(layTmp);
		
		sashDisplay = new SashForm(cmpTmp, SWT.VERTICAL);
		sashDisplay.setLayoutData(new GridData(GridData.FILL_BOTH));
		sashDisplay.setSashWidth(2);
		
		edSource = new StyledText(sashDisplay, SWT.BORDER | SWT.WRAP | SWT.V_SCROLL);
		edSource.setLayoutData(new GridData(GridData.FILL_BOTH));
		edSource.setEditable(false);

		edTarget = new StyledText(sashDisplay, SWT.BORDER | SWT.WRAP | SWT.V_SCROLL);
		edTarget.setLayoutData(new GridData(GridData.FILL_BOTH));
		edTarget.setEditable(false);

		// Get the source text option from the user configuration if possible
		String data = config.getProperty(CFG_SOURCETEXTOPTIONS, "");
		if ( !Util.isEmpty(data) ) {
			try {
				srcTextOpt = new TextOptions(shell.getDisplay(), data);
			}
			catch ( Throwable e ) {
				// Error: Keep it silent, just use the default
				data = null;
			}
		}
		if ( Util.isEmpty(data) ) { // Default
			// Create a copy of the default text field options for the source
			srcTextOpt = new TextOptions(shell.getDisplay(), edSource, 2);
		}
		// And apply them to the source control to allow clean disposal later
		srcTextOpt.applyTo(edSource);
		
		// Get the target text option from the user configuration if possible
		data = config.getProperty(CFG_TARGETTEXTOPTIONS, "");
		if ( !Util.isEmpty(data) ) {
			try {
				trgTextOpt = new TextOptions(shell.getDisplay(), data);
			}
			catch ( Throwable e ) {
				// Error: Keep it silent, just use the default
				data = null;
			}
		}
		if ( Util.isEmpty(data) ) { // Default
			// Use the same as the source by default
			trgTextOpt = new TextOptions(shell.getDisplay(), srcTextOpt);
		}
		// And apply them to the target control to allow clean disposal later
		trgTextOpt.applyTo(edTarget);

		//--- Issues panel
		
		cmpTmp = new Composite(sashMain, SWT.BORDER);
		layTmp = new GridLayout(3, false);
		cmpTmp.setLayout(layTmp);
		cmpTmp.setLayoutData(new GridData(GridData.FILL_BOTH));
		
		edMessage = new Text(cmpTmp, SWT.BORDER);
		GridData gdTmp = new GridData(GridData.FILL_HORIZONTAL);
		gdTmp.horizontalSpan = 3;
		edMessage.setLayoutData(gdTmp);
		edMessage.setEditable(false);

		cbDocument = new Combo(cmpTmp, SWT.DROP_DOWN | SWT.READ_ONLY);
		cbDocument.setVisibleItemCount(20);
		gdTmp = new GridData(GridData.FILL_HORIZONTAL);
		gdTmp.horizontalSpan = 3;
		cbDocument.setLayoutData(gdTmp);
		cbDocument.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				updateCurrentDocument();
			}
		});

		Composite cmpButtons = new Composite(cmpTmp, SWT.NONE);
		layTmp = new GridLayout(4, true);
		layTmp.marginHeight = 0;
		layTmp.marginWidth = 0;
		cmpButtons.setLayout(layTmp);
		cmpButtons.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		
		Button btCheckAll = new Button(cmpButtons, SWT.PUSH);
		btCheckAll.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		btCheckAll.setText("Check All");
		btCheckAll.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				checkAll();
			}
		});
		
		Button btCheckDoc = new Button(cmpButtons, SWT.PUSH);
		btCheckDoc.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		btCheckDoc.setText("Check Document");
		btCheckDoc.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				checkCurrentDocument();
			}
		});
		
		Button btOptions = new Button(cmpButtons, SWT.PUSH);
		btOptions.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		btOptions.setText("Configuration...");
		btOptions.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				editConfiguration();
			}
		});
		
		Button btSession = new Button(cmpButtons, SWT.PUSH);
		btSession.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		btSession.setText("Session...");
		btSession.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				editSessionSettings();
			}
		});
		
		cbTypes = new Combo(cmpTmp, SWT.DROP_DOWN | SWT.READ_ONLY);
		cbTypes.add(""); // All types
		cbTypes.add("Missing target"); // MISSING_TARGETTU
		cbTypes.add("Missing and extra segments"); // All missing and extra segments issues
		cbTypes.add("Empty segments"); // All empty segment issues
		cbTypes.add("Target same as source"); // TARGET_SAME_AS_SOURCE
		cbTypes.add("White spaces differences"); // All whitespace-related issues
		cbTypes.add("Inline codes differences"); // CODE_DIFFERENCE
		cbTypes.add("Unexpected patterns"); // UNEXPECTED_PATTERN
		cbTypes.add("Suspect patterns"); // SUSPECT_PATTERN
		cbTypes.add("Text length"); // Length-related issues
		cbTypes.add("Allowed characters"); // ALLOWED_CHARACTERS
		cbTypes.add("Terminology"); // TERMINOLOGY
		cbTypes.add("LanguageTool checker warnings"); // LANGUAGETOOL_ERROR
		cbTypes.add("Other issues"); // Other (e.g. type is null)
		cbTypes.setVisibleItemCount(cbTypes.getItemCount());
		cbTypes.setLayoutData(new GridData());
		cbTypes.select(issueType);
		cbTypes.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				refreshTableDisplay();
			};
		});
		
		cbDisplay = new Combo(cmpTmp, SWT.DROP_DOWN | SWT.READ_ONLY);
		cbDisplay.add("Enabled and disabled issues");
		cbDisplay.add("Only enabled issues");
		cbDisplay.add("Only disabled issues");
		cbDisplay.setLayoutData(new GridData()); //GridData.FILL_HORIZONTAL));
		cbDisplay.select(displayType);
		cbDisplay.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				refreshTableDisplay();
			};
		});
		
		tblIssues = new Table(cmpTmp, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION | SWT.CHECK | SWT.V_SCROLL);
		tblIssues.setHeaderVisible(true);
		tblIssues.setLinesVisible(true);
		gdTmp = new GridData(GridData.FILL_BOTH);
		gdTmp.horizontalSpan = 3;
		//gdTmp.minimumHeight = 250;
		tblIssues.setLayoutData(gdTmp);
		tblIssues.setMenu(createIssuesContextMenu());
		
		tblIssues.addControlListener(new ControlAdapter() {
		    public void controlResized(ControlEvent e) {
		    	Rectangle rect = tblIssues.getClientArea();
		    	int checkColWidth = 32;
		    	int severityColWidth = 28;
				int part = (int)((rect.width-(checkColWidth+severityColWidth)) / 100);
				int remainder = (int)((rect.width-(checkColWidth+severityColWidth)) % 100);
				tblIssues.getColumn(0).setWidth(checkColWidth);
				tblIssues.getColumn(1).setWidth(severityColWidth);
				tblIssues.getColumn(2).setWidth(part*10);
				tblIssues.getColumn(3).setWidth(part*5);
				tblIssues.getColumn(4).setWidth(remainder+(part*85));
		    }
		});
		
		tblIssues.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				if ( event.detail == SWT.CHECK ) {
					// Do not force the selection: tblIssues.setSelection((TableItem)event.item);
					Issue issue = (Issue)event.item.getData();
					issue.setEnabled(!issue.getEnabled());
					session.setModified(true);
				}
				updateCurrentIssue();
            }
		});

		tblIssues.addKeyListener(new KeyAdapter() {
			public void keyPressed(KeyEvent e) {
				if ( e.character == ' ' ) {
					TableItem si = tblIssues.getItem(tblIssues.getSelectionIndex());
					for ( TableItem ti : tblIssues.getSelection() ) {
						if ( ti == si ) continue; // Skip focused item because it will get set by SelectionAdapter()
						Issue issue = (Issue)ti.getData();
						issue.setEnabled(!issue.getEnabled());
						ti.setChecked(issue.getEnabled());
						session.setModified(true);
					}
				}
			}
		});

		// Sort listener for the table
		Listener sortListener = event -> {
			// Determine new sort column and direction
			TableColumn sortCol = tblIssues.getSortColumn();
			TableColumn curCol = (TableColumn)event.widget;
			int dir = tblIssues.getSortDirection();
			if ( sortCol == curCol ) {
				// Same column as before? then reverse sort direction
				dir = (dir == SWT.UP ? SWT.DOWN : SWT.UP);
			}
			else { // Other column? the set the new column
				tblIssues.setSortColumn(curCol);
				dir = SWT.UP;
			}
			// Select the issue part to sort
			int type = IssueComparator.TYPE_ENABLED;
			if ( tblIssues.indexOf(curCol) == 1 ) type = IssueComparator.TYPE_SEVERITY;
			else if ( tblIssues.indexOf(curCol) == 2 ) type = IssueComparator.TYPE_TU;
			else if ( tblIssues.indexOf(curCol) == 3 ) type = IssueComparator.TYPE_SEG;
			else if ( tblIssues.indexOf(curCol) == 4 ) type = IssueComparator.TYPE_MESSAGE;
			// Perform the sort
			session.getIssues().sort(new IssueComparator(type, dir == SWT.UP ? IssueComparator.DIR_ASC : IssueComparator.DIR_DESC));
			// Set direction
			tblIssues.setSortDirection(dir);
			refreshTableDisplay();
		};
		
		issuesModel = new IssuesTableModel(shell.getDisplay());
		issuesModel.linkTable(tblIssues, sortListener);

		sashMain.setWeights(new int[]{30, 70});
		
		statusBar = new StatusBar(shell, SWT.NONE);
		updateMRU();
		
		if (!shell.getMaximized()) { // not RWT full-screen mode
			// Set minimum and start sizes
			Point defaultSize = shell.getSize();
			shell.pack();
			shell.setMinimumSize(shell.getSize());
			Point startSize = defaultSize;			 
			shell.setSize(startSize);
			
			// Workaround for RWT (stretches the shell horizontally when column widths are adjusted)
			shell.setMaximized(true);
			shell.setMaximized(false);

			UIUtil.centerShell(shell);
						
			// Maximize if requested
			if ( config.getBoolean(OPT_MAXIMIZED) ) {
				shell.setMaximized(true);
			}
			else { // Or try to re-use the bounds of the previous session
				Rectangle ar = UIUtil.StringToRectangle(config.getProperty(OPT_BOUNDS));
				if ( ar != null ) {
					Rectangle dr = shell.getDisplay().getBounds();
					if ( dr.contains(ar.x+ar.width, ar.y+ar.height)
						&& dr.contains(ar.x, ar.y) ) {
						shell.setBounds(ar);
					}
				}
			}
		}
		
		updateCaption();
		resetTextFieldOrientation();
		resetTableDisplay();
	}

	private void resetTextFieldOrientation () {
		// Update orientation, making sure the TextOptions are also updated
		// Source field
		srcTextOpt.isBidirectional = LocaleId.isBidirectional(session.getSourceLocale());
		edSource.setOrientation(srcTextOpt.isBidirectional ? SWT.RIGHT_TO_LEFT : SWT.LEFT_TO_RIGHT);
		// Target field
		trgTextOpt.isBidirectional = LocaleId.isBidirectional(session.getTargetLocale());
		edTarget.setOrientation(trgTextOpt.isBidirectional ? SWT.RIGHT_TO_LEFT : SWT.LEFT_TO_RIGHT);
	}

	private void openDocument () {
		try {
			String path = cbDocument.getText();
			if ( Util.isEmpty(path) ) return;
			path = (new File(path)).getPath();
			Program.launch(path);
		}
		catch ( Exception e ) {
			Dialogs.showError(shell, e.getMessage(), null);
		}
	}
	
	private void openContainingFolder () {
		try {
			String path = cbDocument.getText();
			if ( Util.isEmpty(path) ) return;
			path = (new File(path)).getPath();
			Program.launch(Util.getDirectoryName(path));
		}
		catch ( Exception e ) {
			Dialogs.showError(shell, e.getMessage(), null);
		}
	}
	
	
	private void resetTableDisplay () {
		try {
			issuesModel.setIssues(session.getIssues());
			tblIssues.setSortColumn(null); // Reset the sort column
			displayType = cbDisplay.getSelectionIndex();
			issueType = cbTypes.getSelectionIndex();
			
			issuesModel.updateTable(0, displayType, issueType);
			updateCurrentIssue();
		}
		catch ( Throwable e ) {
			Dialogs.showError(shell, "Error resetting the table.\n"+e.getMessage(), null);
		}
		
	}

	private void refreshTableDisplay () {
		displayType = cbDisplay.getSelectionIndex();
		issueType = cbTypes.getSelectionIndex();
		issuesModel.updateTable(tblIssues.getSelectionIndex(), displayType, issueType);
		updateCurrentIssue();
	}

	ArrayList getDocumentURIs () {
		ArrayList list = new ArrayList();
		for ( String path : cbDocument.getItems() ) {
			list.add((URI)cbDocument.getData(path));
		}
		return list;
	}

	/**
	 * Fills the documents' combo box.
	 * @param docURI optional URI of the document to select, use null to select first.
	 * If the given URI does not exists anymore, the first document is selected.
	 */
	private void fillDocumentCombo (URI docURI) {
		URI requested = docURI;
		docURI = null;
		// Update the list of documents
		cbDocument.removeAll();
		for ( RawDocument rd : session.getDocuments() ) {
			String path = rd.getInputURI().getPath();
			cbDocument.add(path);
			cbDocument.setData(path, rd.getInputURI());
			// Check if the requested document is in the list
			if ( requested != null ) {
				if ( requested.equals(rd.getInputURI()) ) {
					docURI = requested;
				}
			}
		}
		
		// If no previous requested document, set the first document, if we can
		if ( docURI == null ) {
			if ( cbDocument.getItemCount() > 0 ) {
				docURI = (URI)cbDocument.getData(cbDocument.getItem(0));
			}
		}
		if ( docURI != null ) {
			cbDocument.setText(docURI.getPath());
		}
		updateCurrentIssue();
	}
	
	private void editPreferences () {
		try {
			PreferencesDialog dlg = new PreferencesDialog(shell, help);
			dlg.setData(srcTextOpt, trgTextOpt);
			// Call the dialog. A null return means the user canceled
			Object[] res = dlg.showDialog();
			if ( res == null ) return;
			
			// Else: set the modified options for the source
			TextOptions tmp = srcTextOpt; // With StyledText we cannot free the old before we set the new
			srcTextOpt = (TextOptions)res[0];
			srcTextOpt.applyTo(edSource);
			tmp.dispose();
			// And the target
			tmp = trgTextOpt;
			trgTextOpt = (TextOptions)res[1];
			trgTextOpt.applyTo(edTarget);
			tmp.dispose();
		}
		catch ( Throwable e ) {
			Dialogs.showError(shell, "Error editing preferences.\n"+e.getMessage(), null);
		}
	}
	
	private void editSessionSettings () {
		try {
			// Remember data before edit
			ArrayList prevList = getDocumentURIs();

			// Edit the settings, stop there if the user cancel
			SessionSettingsDialog dlg = new SessionSettingsDialog(shell, help);
			dlg.setData(session);
			if ( !dlg.showDialog() ) return;

			// Update the orientation if needed
			resetTextFieldOrientation();
			
			// Update the content of the documents list
			fillDocumentCombo(null);
			
			// Clean up issues list
			// Remove all current documents from the previous list
			// What is left are the documents to remove
			prevList.removeAll(getDocumentURIs());
			for ( URI uri : prevList ) {
				session.clearIssues(uri, false);
			}
			
			// Update the table of issues
			refreshTableDisplay();
			// And the file watcher
			resetWatcher();
		}
		catch ( Throwable e ) {
			Dialogs.showError(shell, "Error editing session settings.\n"+e.getMessage(), null);
		}
	}
	
	private void resetWatcher () {
		// Cancel the current watcher
		deleteWatcher();
		if ( session.getAutoRefresh() ) {
			watcher = new Watcher(this, shell.getDisplay());
			if ( watcherTimer == null ) {
				watcherTimer = new Timer();
			}
			watcherTimer.schedule(watcher, new Date(), 2000);
		}
	}
	
	/**
	 * Adds a document using the UI dialog.
	 * @param path the path of the document to add.
	 * @param batchMode true if the check box to accept all next documents should be displayed.
	 * @param acceptAll value of the check box to accept all.
	 * @return Null if the user cancel the operation, otherwise: true if the accept-all button was checked,
	 * or false if we are not in batch mode or if the accept-all button was not checked.
	 */
	private Boolean addDocumentFromUI (String path,
		boolean batchMode,
		boolean acceptAll)
	{
		try {
			InputDocumentDialog dlg = new InputDocumentDialog(shell, "Input Document",
				session.getFilterConfigurationMapper(), batchMode);
			// Lock the locales if we have already documents in the session
			boolean canChangeLocales = session.getDocumentCount()==0;
			dlg.setLocalesEditable(canChangeLocales);
			// Set default data
			dlg.setData(path, null, "UTF-8", session.getSourceLocale(), session.getTargetLocale());
			
			if ( batchMode && ( path != null )) {
				dlg.setAcceptAll(acceptAll);
			}

			// Edit
			Object[] data = dlg.showDialog();
			if ( data == null ) return null;
			
			// Create the raw document to add to the session
			URI uri = (new File((String)data[0])).toURI();
			RawDocument rd = new RawDocument(uri, (String)data[2], (LocaleId)data[3], (LocaleId)data[4]);
			rd.setFilterConfigId((String)data[1]);
			session.addRawDocument(rd);
			
			if ( canChangeLocales ) { // In case the locales have changed
				resetTextFieldOrientation();
			}
			fillDocumentCombo(null);
			
			// If dialog return OK, we return value of accept all
			return (Boolean)data[5];
		}
		catch ( Throwable e ) {
			Dialogs.showError(shell, "Error adding document.\n"+e.getMessage(), null);
			return null;
		}
	}
	
	private void updateCaption () {
		String filename;
		if ( qcsPath != null ) {
			filename = Util.getFilename(qcsPath, true);
		}
		else {
			filename = "Untitled";
		}
		String text = "CheckMate";
		shell.setText(filename + " - " + text); //$NON-NLS-1$
	}

	private void updateCurrentDocument () {
		int n = cbDocument.getSelectionIndex();
		if ( n == -1 ) return;
		// Get the document id for the new selected document
		URI uri = (URI)cbDocument.getData(cbDocument.getItem(n));
		// Find the first issue for that document in from the top of the displayed issues
		for ( int i=0; i 0 ) {
			StyleRange sr = new StyleRange();
			sr.background = shell.getDisplay().getSystemColor(SWT.COLOR_YELLOW);
			sr.start = issue.getSourceStart();
			sr.length = issue.getSourceEnd()-issue.getSourceStart();
			edSource.setStyleRange(sr);
			edSource.setCaretOffset(issue.getSourceEnd());
			edSource.showSelection();
		}
		if ( issue.getTargetEnd() > 0 ) {
			StyleRange sr = new StyleRange();
			sr.background = shell.getDisplay().getSystemColor(SWT.COLOR_YELLOW);
			sr.start = issue.getTargetStart();
			sr.length = issue.getTargetEnd()-issue.getTargetStart();
			edTarget.setStyleRange(sr);
			edTarget.setCaretOffset(issue.getTargetEnd());
			edTarget.showSelection();
		}
	}

	private void startWaiting (String text) {
		if ( ++waitCount > 1 ) {
			shell.getDisplay().update();
			return;
		}
		if ( text != null ) statusBar.setInfo(text);
		//startLogWasRequested = p_bStartLog;
		//if ( startLogWasRequested ) log.beginProcess(null);
		shell.getDisplay().update();
	}

	private void stopWaiting () {
		waitCount--;
		if ( waitCount < 1 ) statusBar.clearInfo();
		shell.getDisplay().update();
		//if ( log.inProgress() ) log.endProcess(null); 
		//if ( startLogWasRequested && ( log.getErrorAndWarningCount() > 0 )) log.show();
		//startLogWasRequested = false;
	}

	private void generateReport () {
		try {
			startWaiting("Generating report...");
			String rootDir = (qcsPath==null ? null : Util.getDirectoryName(qcsPath));
			session.generateReport(rootDir);
			String finalPath = Util.fillRootDirectoryVariable(session.getParameters().getOutputPath(), rootDir);
			if ( session.getParameters().getAutoOpen() ) {
				Util.openURL((new File(finalPath)).getAbsolutePath());
			}
		}
		catch ( Throwable e ) {
			Dialogs.showError(shell, "Error while generating report.\n"+e.getMessage(), null);
		}
		finally {
			stopWaiting();
		}
	}

	private void resetDisabledIssues () {
		MessageBox dlg = new MessageBox(shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL);
		dlg.setText(shell.getText());
		dlg.setMessage("This command will re-enabled all issues that have been disabled.\nDo you want to proceed?");
		if  ( dlg.open() != SWT.YES ) return;
		// Proceed
		session.resetDisabledIssues();
		issuesModel.updateTable(0, displayType, issueType);
	}
	
	private void checkCurrentDocument () {
		try {
			int n = cbDocument.getSelectionIndex();
			if ( n == -1 ) return;
			// Get the document id for the new selected document
			URI uri = (URI)cbDocument.getData(cbDocument.getItem(n));
			
			startWaiting("Checking current document...");
			session.recheckDocument(uri);
			resetTableDisplay();
			// Select the re-checked document
			String curPath = uri.getPath();
			n = 0;
			for ( String path : cbDocument.getItems() ) {
				if ( path.equals(curPath) ) {
					cbDocument.select(n);
					updateCurrentDocument();
					break;
				}
				n++;
			}
		}
		catch ( Throwable e ) {
			Dialogs.showError(shell, "Error while running the verification.\n"+e.getMessage(), null);
		}
		finally {
			stopWaiting();
		}
	}
	
	void checkAll () {
		try {
			startWaiting("Checking all documents...");
			// Re-check all using the signatures of the current issue lists
			session.recheckAll(null);
		}
		catch ( Throwable e ) {
			Dialogs.showError(shell, "Error while running the verification.\n"+e.getMessage(), null);
		}
		finally {
			resetTableDisplay();
			stopWaiting();
		}
	}

	private void editConfiguration () {
		try {
			ParametersEditor editor = new ParametersEditor(false); // Not in step-mode
			BaseContext context = new BaseContext();
			if ( help != null ) context.setObject("help", help);
			context.setObject("shell", shell);
			context.setBoolean("stepMode", false); // Not in a step
			String old = session.getParameters().toString();
			if ( editor.edit(session.getParameters(), false, context) ) {
				// Compare before and after to set or not the modified flag
				if ( !old.equals(session.getParameters().toString()) ) {
					session.setModified(true);
				}
			}
		}
		catch ( Throwable e ) {
			Dialogs.showError(shell, "Error editing options.\n"+e.getMessage(), null);
		}
	}

	private void saveConfiguration () {
		try {
			String path = Dialogs.browseFilenamesForSave(shell, "Save Configuration", null, null,
				String.format("Quality Check Configurations (*%s)\tAll Files (*.*)", Parameters.FILE_EXTENSION),
				String.format("*%s\t*.*", Parameters.FILE_EXTENSION));
				if ( path == null ) return;
			startWaiting("Saving configuration...");
			Parameters params = session.getParameters();
			params.save(path);
		}
		catch ( Throwable e ) {
			Dialogs.showError(shell, "Error while saving configuration.\n"+e.getMessage(), null);
		}
		finally {
			stopWaiting();
		}
	}

	private void loadConfiguration () {
		try {
			String[] paths = Dialogs.browseFilenames(shell, "Load Configuration", false, null,
				String.format("Quality Check Configurations (*%s)\tAll Files (*.*)", Parameters.FILE_EXTENSION),
				String.format("*%s\t*.*", Parameters.FILE_EXTENSION));
			if ( paths == null ) return;
			startWaiting("Loading configuration...");
			Parameters params = session.getParameters();
			params.load(Util.toURL(paths[0]), false);
		}
		catch ( Throwable e ) {
			Dialogs.showError(shell, "Error while saving configuration.\n"+e.getMessage(), null);
		}
		finally {
			stopWaiting();
		}
	}

	private void saveSessionAs (String path) {
		try {
			if ( path == null ) {
				path = Dialogs.browseFilenamesForSave(shell, "Save Session", null, null,
					String.format("Quality Check Sessions (*%s)\tAll Files (*.*)", QualityCheckSession.FILE_EXTENSION),
					String.format("*%s\t*.*", QualityCheckSession.FILE_EXTENSION));
				if ( path == null ) return;
				qcsPath = path;
				mruList.add(path);
				updateMRU();
			}
			startWaiting("Saving session...");
			session.saveSession(qcsPath);
			updateCaption();
		}
		catch ( Throwable e ) {
			Dialogs.showError(shell, "Error while saving.\n"+e.getMessage(), null);
		}
		finally {
			stopWaiting();
		}
	}

	private void newSession () {
		try {
			if ( !saveSessionIfNeeded() ) return;
			session.reset();
			qcsPath = null;
			updateCaption();
			fillDocumentCombo(null);
			resetTableDisplay();
		}
		catch ( Throwable e ) {
			Dialogs.showError(shell, e.getMessage(), null);
		}
	}
	
	private void loadSession (String path) {
		try {
			if ( !saveSessionIfNeeded() ) return;
			if ( path == null ) {
				String[] paths = Dialogs.browseFilenames(shell, "Open Session", false, null,
					String.format("Quality Check Sessions (*%s)\tAll Files (*.*)", QualityCheckSession.FILE_EXTENSION),
					String.format("*%s\t*.*", QualityCheckSession.FILE_EXTENSION));
				if ( paths == null ) return;
				path = paths[0];
			}
			
			// Check if the file exists
			if ( !(new File(path)).exists() ) {
				Dialogs.showError(shell, "The settings file cannot be found:\n"+path, null);
				mruList.remove(path);
				updateMRU();
				return;
			}
			
			startWaiting("Loading session and processing documents...");
			session.loadSession(path);
			qcsPath = path;
			updateCaption();
			mruList.add(path);
			updateMRU();
		}
		catch ( Throwable e ) {
			Dialogs.showError(shell, "Error while loading.\n"+e.getMessage(), null);
		}
		finally {
			fillDocumentCombo(null);
			resetTableDisplay();
			stopWaiting();
		}
	}

	/**
	 * Checks if the session needs to be saved, if so save them after prompting
	 * the user if needed.
	 * @return False if the user cancel, true if a decision is made. 
	 */
	private boolean saveSessionIfNeeded () {
		// Save user preferences and configuration
		config.setProperty(CFG_SOURCELOCALE, session.getSourceLocale().toString());
		config.setProperty(CFG_TARGETLOCALE, session.getTargetLocale().toString());
		// Set the window placement
		config.setProperty(OPT_MAXIMIZED, shell.getMaximized());
		Rectangle r = shell.getBounds();
		config.setProperty(OPT_BOUNDS, String.format("%d,%d,%d,%d", r.x, r.y, r.width, r.height)); //$NON-NLS-1$
		config.setProperty(CFG_SOURCETEXTOPTIONS, srcTextOpt.toString());
		config.setProperty(CFG_TARGETTEXTOPTIONS, trgTextOpt.toString());
		// Set the MRU list
		mruList.copyToProperties(config);
		// Save to the user home directory as ".appname" file
		config.save(APPNAME, getClass().getPackage().getImplementationVersion());
		
		if ( session.isModified() ) {
			MessageBox dlg = new MessageBox(shell, SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL);
			dlg.setText(shell.getText());
			dlg.setMessage("The session has been modified. Do you want to save it?");
			switch ( dlg.open() ) {
			case SWT.CANCEL:
				return false;
			case SWT.YES:
				saveSessionAs(qcsPath);
			}
		}
		return true;
	}

	private void clearMRU () {
		try {
			mruList.clear();
			updateMRU();
		}
		catch ( Exception e ) {
			Dialogs.showError(shell, e.getMessage(), null);
		}
	}
	
	private void updateMRU () {
		try {
			// miMRU is the MenuItem where to attached the sub-menu
			// Remove and dispose of the previous sub-menu
			Menu oldMenu = miMRU.getMenu();
			miMRU.setMenu(null);
			if ( oldMenu != null ) oldMenu.dispose();

			// Set the new one
			if ( mruList.getfirst() == null ) {
				// No items to set: it's disabled
				miMRU.setEnabled(false);
			}
			else { // One or more items
				// Create the menu
				Menu submenu = new Menu(shell, SWT.DROP_DOWN);
				int i = 0;
				String path;
				MenuItem menuItem;
				Iterator iter = mruList.getIterator();
				while ( iter.hasNext() ) {
					menuItem = new MenuItem(submenu, SWT.PUSH);
					path = iter.next();
					menuItem.setText(String.format("&%d %s", ++i, path)); //$NON-NLS-1$
					menuItem.setData(path);
					menuItem.addSelectionListener(new SelectionAdapter() {
						public void widgetSelected(SelectionEvent event) {
							loadSession((String)((MenuItem)event.getSource()).getData());
						}
					});
				}
				miMRU.setMenu(submenu);
				miMRU.setEnabled(true);
			}
		}
		catch ( Exception e ) {
			Dialogs.showError(shell, e.getMessage(), null);
		}
	}

	@Override
	public QualityCheckSession getSession() {
		return session;
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy