rechi.xml.MainWindow.java Source code

Java tutorial

Introduction

Here is the source code for rechi.xml.MainWindow.java

Source

/*
The MIT License (MIT)
    
Copyright (c) 2015 Bogdan Rechi
    
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
    
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
    
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package rechi.xml;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.io.FilenameUtils;
import org.apache.log4j.Logger;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CaretEvent;
import org.eclipse.swt.custom.CaretListener;
import org.eclipse.swt.custom.SashForm;
import org.eclipse.swt.custom.VerifyKeyListener;
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.Transfer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.events.ShellAdapter;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.VerifyEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.layout.RowData;
import org.eclipse.swt.layout.RowLayout;
import org.eclipse.swt.program.Program;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FontDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.TabFolder;
import org.eclipse.swt.widgets.TabItem;
import org.eclipse.swt.widgets.ToolBar;
import org.eclipse.swt.widgets.ToolItem;
import org.w3c.dom.NodeList;

import bogdan.rechi.commons.Configuration;
import bogdan.rechi.commons.Debug;
import bogdan.rechi.commons.Files;
import bogdan.rechi.commons.Platform;
import bogdan.rechi.commons.Property;
import bogdan.rechi.commons.time.TimeMeasure;
import bogdan.rechi.commons.xml.Xml;
import bogdan.rechi.swt.commons.SwtFiles;
import bogdan.rechi.swt.commons.SwtGeneral;
import bogdan.rechi.swt.commons.SwtResourceManager;
import bogdan.rechi.swt.commons.dialogs.AboutBox;
import bogdan.rechi.swt.commons.textbox.EnhancedStyledText;
import bogdan.rechi.swt.commons.textbox.IndentingImpl;
import bogdan.rechi.swt.commons.textbox.UndoRedoImpl;

/**
 * XML operations.
 *
 * @author Bogdan Rechi
 */
public class MainWindow {
    // Constants
    private static final String XML_OPERATIONS_TITLE = "XML Operations - %s%s";

    private static final int TEXT_TAB_SIZE = 2;

    private static final String DTD_TEMP_FILE = Files.appendToPath(Files.TEMPORARY_DIRECTORY,
            "xmloperationsdtd.dtd");

    private static final String XSLT_NO_BLANKS = "<xsl:strip-space elements=\"*\"/>";
    private static final int XSLT_NO_BLANKS_LENGTH = XSLT_NO_BLANKS.length();

    private static final String XQUERY_SOURCE_FILE_PATH = "declare variable $sourceFilePath as xs:string external;";
    private static final int XQUERY_SOURCE_FILE_PATH_LENGTH = XQUERY_SOURCE_FILE_PATH.length();

    private static Color COLOR_RED;
    private static Color COLOR_BLACK;
    private static Color COLOR_GREEN;

    private static int LABEL_GAP = 6;

    // Fields
    protected Shell _shlXmlOperations;

    // =============================================================================

    /**
     * Type of current file.
     */
    enum FileType {
        XML, XPATH, XSLT, XQUERY, XSD, DTD
    }

    /**
     * Last pressed toolbar button.
     */
    enum LastPressedButton {
        NEW, OPEN, SAVE, SAVE_AS
    }

    // =============================================================================

    FileType _lastFocusedFileType = FileType.XML;
    FileType _lastFocusedOperationType = FileType.XPATH;

    UndoRedoImpl _xmlFileUndoRedo;
    UndoRedoImpl _xpathFileUndoRedo;
    UndoRedoImpl _xsltFileUndoRedo;
    UndoRedoImpl _xqueryFileUndoRedo;
    UndoRedoImpl _xsdFileUndoRedo;
    UndoRedoImpl _dtdFileUndoRedo;

    IndentingImpl _xmlFileIndenting;
    IndentingImpl _xsltFileIndenting;
    IndentingImpl _xqueryFileIndenting;
    IndentingImpl _xsdFileIndenting;
    IndentingImpl _dtdFileIndenting;

    String _xmlFile = "";
    String _xpathFile = "";
    String _xsltFile = "";
    String _xqueryFile = "";
    String _xsdFile = "";
    String _dtdFile = "";

    Boolean _xmlFileChanged = false;
    Boolean _xpathFileChanged = false;
    Boolean _xsltFileChanged = false;
    Boolean _xqueryFileChanged = false;
    Boolean _xsdFileChanged = false;
    Boolean _dtdFileChanged = false;

    List<String> _extensions;

    // =============================================================================

    Boolean _modifyingFile = false;

    LastPressedButton _lastPressedButton = LastPressedButton.NEW;

    EnhancedStyledText _lastSelectedForClipboard = null;
    UndoRedoImpl _lastSelectedForClipboardUndoRedo = null;

    Boolean _firstTime = true;

    int _searchBoxFocusTime = 0;

    String _xpathTags = null;
    String _xsltTags = null;
    String _xqueryTags = null;

    private FileTransfer _fileTransfer;

    private Program _defaultBrowserProgram;

    private String _resultFile = Files.appendToPath(Files.TEMPORARY_DIRECTORY, "xmloperations.html");

    private Cursor _cursorWait;

    private Configuration _config;

    public static Logger _log;

    private Display _display;

    // =============================================================================

    private EnhancedStyledText textXml;
    private EnhancedStyledText textXpath;
    private EnhancedStyledText textXslt;
    private EnhancedStyledText textXsd;
    private EnhancedStyledText textDtd;
    private EnhancedStyledText textResult;
    private EnhancedStyledText textSearch;
    private Button btnSave;
    private Composite compositeXmlOthers;
    private TabItem tabXpath;
    private TabItem tabXslt;
    private TabItem tabXsd;
    private TabItem tabDtd;
    private TabFolder tabsOthers;
    private Button btnPaste;
    private Button btnCopy;
    private Button btnCut;
    private Button btnUndo;
    private Button btnRedo;
    private Button btnNew;
    private Button btnOpen;
    private Button btnSaveAs;
    private Button btnAutoSave;
    private TabItem tabXml;
    private TabFolder tabsXml;
    private Button btnCopyFolder;
    private Composite compositeStatusBar;
    private Label lblStatus;
    private Composite compositeMain;
    private Composite compositeToolBar;
    private Button btnFont;
    private Button btnClearResult;
    private Button btnNamespaces;
    private Button btnAbout;
    private SashForm splitSourceResult;
    private SashForm splitXmlOthers;
    private Composite compositeResult;
    private TabFolder tabsResultText;
    private TabItem tabResultText;
    private TabItem tabXquery;
    private EnhancedStyledText textXquery;
    private ToolBar toolBarApply;
    private ToolItem btnDropDownApply;
    private Menu menuApply;
    private MenuItem menuItemApplyAndShowInBrowser;
    private MenuItem menuItemShowInBrowser;
    private DropTarget dropTarget;

    // =============================================================================

    /**
     * Launch the application.
     *
     * @param args
     */
    public static void main(String[] args) {
        try {
            MainWindow window = new MainWindow();
            window.open();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Open the window.
     *
     * @throws IOException
     */
    public void open() throws IOException {
        _log = Logger.getLogger(MainWindow.class);

        _extensions = Arrays.asList("xml", "xml.nobom", "config", "config.nobom", "csproj", "csproj.nobom", "xpath",
                "xpath.nobom", "xsl", "xsl.nobom", "xslt", "xslt.nobom", "xsd", "xsd.nobom", "xquery",
                "xquery.nobom", "xq", "xq.nobom", "xqy", "xqy.nobom", "dtd", "dtd.nobom");

        Property<String> errorMessage = new Property<String>();

        try {
            _display = Display.getDefault();

            COLOR_RED = SwtResourceManager.getColor(SWT.COLOR_RED);
            COLOR_BLACK = SwtResourceManager.getColor(SWT.COLOR_BLACK);
            COLOR_GREEN = SwtResourceManager.getColor(SWT.COLOR_DARK_GREEN);

            _cursorWait = new Cursor(_display, SWT.CURSOR_WAIT);

            createContents();

            _xmlFileUndoRedo = new UndoRedoImpl(textXml);
            _xpathFileUndoRedo = new UndoRedoImpl(textXpath);
            _xsltFileUndoRedo = new UndoRedoImpl(textXslt);
            _xqueryFileUndoRedo = new UndoRedoImpl(textXquery);
            _xsdFileUndoRedo = new UndoRedoImpl(textXsd);
            _dtdFileUndoRedo = new UndoRedoImpl(textDtd);

            _xmlFileIndenting = new IndentingImpl(textXml);
            _xsltFileIndenting = new IndentingImpl(textXslt);
            _xqueryFileIndenting = new IndentingImpl(textXquery);
            _xsdFileIndenting = new IndentingImpl(textXsd);
            _dtdFileIndenting = new IndentingImpl(textDtd);

            menuApply = new Menu(_shlXmlOperations);
            _shlXmlOperations.setMenu(menuApply);

            menuItemApplyAndShowInBrowser = new MenuItem(menuApply, SWT.NONE);
            menuItemApplyAndShowInBrowser.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    onKeyEvent(null, SWT.F5, e.stateMask | SWT.CONTROL);
                    setFocus(_lastFocusedFileType);
                }
            });
            menuItemApplyAndShowInBrowser.setEnabled(false);
            menuItemApplyAndShowInBrowser.setText("Apply and Show Result in Browser (Ctrl+F5)");

            menuItemShowInBrowser = new MenuItem(menuApply, SWT.NONE);
            menuItemShowInBrowser.addSelectionListener(new SelectionAdapter() {
                @Override
                public void widgetSelected(SelectionEvent e) {
                    onKeyEvent(null, SWT.F6, e.stateMask);
                    setFocus(_lastFocusedFileType);
                }
            });
            menuItemShowInBrowser.setEnabled(false);
            menuItemShowInBrowser.setText("Show Result in Browser (F6)");

            _config = new Configuration(Files.CURRENT_DIRECTORY + "xmloperations.properties");

            setFont(_config.getStringProperty("fontName"), _config.getIntProperty("fontSize"),
                    _config.getIntProperty("fontStyle"));

            textXml.setWordWrap(_config.getBoolProperty("xmlWrap"));
            textXpath.setWordWrap(_config.getBoolProperty("xpathWrap"));
            textXslt.setWordWrap(_config.getBoolProperty("xsltWrap"));
            textXquery.setWordWrap(_config.getBoolProperty("xqueryWrap"));
            textXsd.setWordWrap(_config.getBoolProperty("xsdWrap"));
            textDtd.setWordWrap(_config.getBoolProperty("dtdWrap"));
            textResult.setWordWrap(_config.getBoolProperty("resultWrap"));

            dropTarget = new DropTarget(_shlXmlOperations, DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT);
            dropTarget.setTransfer(new Transfer[] { _fileTransfer = FileTransfer.getInstance() });
            dropTarget.addDropListener(new DropTargetAdapter() {
                @Override
                public void dragEnter(DropTargetEvent event) {
                    event.detail = DND.DROP_DEFAULT;

                    if (Platform.SYSTEM_IS_LINUX) // Linux is lazy by design
                        return;

                    if (event.dataTypes != null)
                        if (event.dataTypes.length > 0) {
                            String[] files = (String[]) _fileTransfer.nativeToJava(event.dataTypes[0]);
                            for (String file : files) {
                                String fileName = file.toLowerCase();

                                Boolean ok = false;
                                for (String extension : _extensions)
                                    if (fileName.endsWith(extension)) {
                                        ok = true;
                                        break;
                                    }

                                if (!ok) {
                                    event.detail = DND.DROP_NONE;
                                    return;
                                }
                            }

                            return;
                        }

                    event.detail = DND.DROP_NONE;
                }

                @Override
                public void drop(final DropTargetEvent event) {
                    _shlXmlOperations.forceActive();
                    _shlXmlOperations.forceFocus();

                    _display.asyncExec(new Runnable() {
                        @Override
                        public void run() {
                            if (event.data != null)
                                for (String file : (String[]) event.data)
                                    loadDroppedFile(file);
                        }
                    });
                }
            });

            _xpathTags = Files.readTextFileFromResources("resources/xmloperations/xpath-tags.txt", errorMessage);
            if (_xpathTags == null)
                throw new Exception(errorMessage.get());

            _xsltTags = Files.readTextFileFromResources("resources/xmloperations/xslt-tags.txt", errorMessage);
            if (_xsltTags == null)
                throw new Exception(errorMessage.get());

            _xqueryTags = Files.readTextFileFromResources("resources/xmloperations/xquery-tags.txt", errorMessage);
            if (_xqueryTags == null)
                throw new Exception(errorMessage.get());

            _xsltTags = String.format("<a>\n%s%s\n</a>", _xpathTags, _xsltTags);
            _xqueryTags = String.format("<a>\n%s%s\n</a>", _xpathTags, _xqueryTags);
            _xpathTags = String.format("<a>\n%s</a>", _xpathTags);

            _shlXmlOperations.open();
            _shlXmlOperations.layout();

            while (!_shlXmlOperations.isDisposed()) {
                if (!_display.readAndDispatch()) {
                    _display.sleep();
                }
            }
        } catch (Exception e) {
            _log.fatal(Debug.printStackTraceForLogging(e));
            SwtGeneral.messageBoxError(
                    "The application is closing due to a severe internal error.\nSee the log file for details.");
        } finally {
            _cursorWait.dispose();
        }
    }

    /**
     * Create contents of the window.
     */
    protected void createContents() {
        _shlXmlOperations = new Shell();
        _shlXmlOperations.addDisposeListener(new DisposeListener() {
            @Override
            public void widgetDisposed(DisposeEvent e) {
                SwtResourceManager.dispose();
            }
        });
        _shlXmlOperations.addShellListener(new ShellAdapter() {
            @Override
            public void shellClosed(ShellEvent e) {
                _shlXmlOperations.setActive();

                if (_xmlFileChanged)
                    switch (SwtGeneral.messageBox("Do you want to save the current XML changes?", "XML Operations",
                            SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL)) {
                    case SWT.YES:
                        if (!fileSave(FileType.XML)) {
                            e.doit = false;
                            return;
                        } else
                            break;

                    case SWT.NO:
                        break;

                    case SWT.CANCEL:
                        e.doit = false;
                        return;
                    }

                if (_xpathFileChanged) {
                    tabsOthers.setSelection(0);
                    textXpath.setFocus();

                    switch (SwtGeneral.messageBox("Do you want to save the current XPath changes?",
                            "XML Operations", SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL)) {
                    case SWT.YES:
                        if (!fileSave(FileType.XPATH)) {
                            e.doit = false;
                            return;
                        } else
                            break;

                    case SWT.NO:
                        break;

                    case SWT.CANCEL:
                        e.doit = false;
                        return;
                    }
                }

                if (_xsltFileChanged) {
                    tabsOthers.setSelection(1);
                    textXslt.setFocus();

                    switch (SwtGeneral.messageBox("Do you want to save the current XSLT changes?", "XML Operations",
                            SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL)) {
                    case SWT.YES:
                        if (!fileSave(FileType.XSLT)) {
                            e.doit = false;
                            return;
                        } else
                            break;

                    case SWT.NO:
                        break;

                    case SWT.CANCEL:
                        e.doit = false;
                        return;
                    }
                }

                if (_xqueryFileChanged) {
                    tabsOthers.setSelection(2);
                    textXquery.setFocus();

                    switch (SwtGeneral.messageBox("Do you want to save the current XQuery changes?",
                            "XML Operations", SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL)) {
                    case SWT.YES:
                        if (!fileSave(FileType.XQUERY)) {
                            e.doit = false;
                            return;
                        } else
                            break;

                    case SWT.NO:
                        break;

                    case SWT.CANCEL:
                        e.doit = false;
                        return;
                    }
                }

                if (_xsdFileChanged) {
                    tabsOthers.setSelection(3);
                    textXsd.setFocus();

                    switch (SwtGeneral.messageBox("Do you want to save the current XSD changes?", "XML Operations",
                            SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL)) {
                    case SWT.YES:
                        if (!fileSave(FileType.XSD)) {
                            e.doit = false;
                            return;
                        } else
                            break;

                    case SWT.NO:
                        break;

                    case SWT.CANCEL:
                        e.doit = false;
                        return;
                    }
                }

                if (_dtdFileChanged) {
                    tabsOthers.setSelection(4);
                    textDtd.setFocus();

                    switch (SwtGeneral.messageBox("Do you want to save the current DTD changes?", "XML Operations",
                            SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL)) {
                    case SWT.YES:
                        if (!fileSave(FileType.DTD)) {
                            e.doit = false;
                            return;
                        } else
                            break;

                    case SWT.NO:
                        break;

                    case SWT.CANCEL:
                        e.doit = false;
                        return;
                    }
                }

                try {
                    FontData fontData = textXml.getFont().getFontData()[0];
                    _config.setStringProperty("fontName", fontData.getName());
                    _config.setIntProperty("fontSize", (int) fontData.height);
                    _config.setIntProperty("fontStyle", (int) fontData.getStyle());

                    _config.setBoolProperty("xmlWrap", textXml.getWordWrap());
                    _config.setBoolProperty("xpathWrap", textXpath.getWordWrap());
                    _config.setBoolProperty("xsltWrap", textXslt.getWordWrap());
                    _config.setBoolProperty("xqueryWrap", textXquery.getWordWrap());
                    _config.setBoolProperty("xsdWrap", textXsd.getWordWrap());
                    _config.setBoolProperty("dtdWrap", textDtd.getWordWrap());
                    _config.setBoolProperty("resultWrap", textResult.getWordWrap());

                    _config.save();
                } catch (IOException ex) {
                    SwtGeneral.messageBoxError(
                            String.format("Unable to save the properties file (%s)", ex.getMessage()),
                            "XML Operations");
                    _log.error(Debug.printStackTraceForLogging(ex));
                }

                Files.delete(DTD_TEMP_FILE);

                e.doit = true;
            }

            @Override
            public void shellActivated(ShellEvent e) {
                if (_firstTime) {
                    _defaultBrowserProgram = Program.findProgram("html");
                    if (_defaultBrowserProgram == null)
                        _log.warn("Unable to detect the default system browser");

                    textXml.setFocus();
                    _firstTime = false;
                }
            }
        });
        _shlXmlOperations.setMinimumSize(new Point(970, 500));
        _shlXmlOperations.setSize(971, 600);
        _shlXmlOperations.setText("XML Operations");
        _shlXmlOperations.setLayout(new FillLayout(SWT.HORIZONTAL));
        _shlXmlOperations.setImages(new Image[] {
                SwtResourceManager.getImage(_display, MainWindow.class,
                        "/resources/xmloperations/icons/mainxml.png"),
                SwtResourceManager.getImage(_display, MainWindow.class,
                        "/resources/xmloperations/icons/mainbigxml.png"),
                SwtResourceManager.getImage(_display, MainWindow.class,
                        "/resources/xmloperations/icons/maininterxml.png") });

        SwtGeneral.CurrentApplicationShell = _shlXmlOperations;

        compositeMain = new Composite(_shlXmlOperations, SWT.NONE);
        compositeMain.setLayout(new GridLayout(1, false));

        compositeToolBar = new Composite(compositeMain, SWT.NONE);
        compositeToolBar.setLayout(new RowLayout(SWT.HORIZONTAL));
        compositeToolBar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false, 1, 1));

        btnNew = new Button(compositeToolBar, SWT.NONE);
        btnNew.setImage(
                SwtResourceManager.getImage(_display, MainWindow.class, "/resources/xmloperations/icons/new.png"));
        btnNew.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                _lastPressedButton = LastPressedButton.NEW;
                fileNew(_lastFocusedFileType);
            }
        });
        btnNew.setToolTipText("New XML (Ctrl+N)");

        btnOpen = new Button(compositeToolBar, SWT.NONE);
        btnOpen.setImage(
                SwtResourceManager.getImage(_display, MainWindow.class, "/resources/xmloperations/icons/open.png"));
        btnOpen.setToolTipText("Open XML (Ctrl+O)");
        btnOpen.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                _lastPressedButton = LastPressedButton.OPEN;
                fileOpen(_lastFocusedFileType);
            }
        });
        btnOpen.setText("Open");

        btnSave = new Button(compositeToolBar, SWT.NONE);
        btnSave.setEnabled(false);
        btnSave.setImage(
                SwtResourceManager.getImage(_display, MainWindow.class, "/resources/xmloperations/icons/save.png"));
        btnSave.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                _lastPressedButton = LastPressedButton.SAVE;
                fileSave(_lastFocusedFileType);
            }
        });
        btnSave.setToolTipText("Save XML (Ctrl+S)");
        btnSave.setText("Save");

        btnSaveAs = new Button(compositeToolBar, SWT.NONE);
        btnSaveAs.setText("As");
        btnSaveAs.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                _lastPressedButton = LastPressedButton.SAVE_AS;
                fileSaveAs(_lastFocusedFileType);
            }
        });
        btnSaveAs.setToolTipText("Save XML as");

        Label labelSeparator4 = new Label(compositeToolBar, SWT.NONE);
        labelSeparator4.setLayoutData(new RowData(LABEL_GAP, SWT.DEFAULT));

        textSearch = new EnhancedStyledText(compositeToolBar, SWT.BORDER | SWT.SINGLE, "en");
        textSearch.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                if (e.time == _searchBoxFocusTime)
                    textSearch.selectAll();
            }
        });
        textSearch.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                _searchBoxFocusTime = e.time;
                textSearch.selectAll();
            }
        });
        textSearch.setLayoutData(new RowData(160, SWT.DEFAULT));
        textSearch.setToolTipText("Search text (Ctrl+F, F3)\nHold Ctrl for case-sensitive search");
        textSearch.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                switch (e.keyCode) {
                case SWT.CR:
                case SWT.F3:
                    search((e.stateMask & SWT.CONTROL) != 0);
                    if ((e.stateMask & SWT.CONTROL) != 0)
                        e.doit = false;
                    break;

                case SWT.ARROW_LEFT:
                case SWT.ARROW_RIGHT:
                    break;

                case SWT.ESC:
                    if (_lastSelectedForClipboard != null)
                        _lastSelectedForClipboard.setFocus();
                    e.doit = false;
                    break;

                default:
                    onKeyEvent(null, e.keyCode, e.stateMask);

                    if ((e.stateMask & SWT.CONTROL) != 0)
                        e.doit = false;
                    break;
                }
            }
        });

        Label labelSeparator1 = new Label(compositeToolBar, SWT.NONE);
        labelSeparator1.setLayoutData(new RowData(LABEL_GAP, -1));

        btnCopyFolder = new Button(compositeToolBar, SWT.NONE);
        btnCopyFolder.setToolTipText("Copy XML file's folder path");
        btnCopyFolder.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                String copiedFolder = "";

                switch (_lastFocusedFileType) {
                case DTD:
                    SwtGeneral.copyTextToClipboard(copiedFolder = Files.getParent(_dtdFile));
                    textDtd.setFocus();
                    break;

                case XML:
                    SwtGeneral.copyTextToClipboard(copiedFolder = Files.getParent(_xmlFile));
                    textXml.setFocus();
                    break;

                case XPATH:
                    SwtGeneral.copyTextToClipboard(copiedFolder = Files.getParent(_xpathFile));
                    textXpath.setFocus();
                    break;

                case XSD:
                    SwtGeneral.copyTextToClipboard(copiedFolder = Files.getParent(_xsdFile));
                    textXsd.setFocus();
                    break;

                case XSLT:
                    SwtGeneral.copyTextToClipboard(copiedFolder = Files.getParent(_xsltFile));
                    textXslt.setFocus();
                    break;

                case XQUERY:
                    SwtGeneral.copyTextToClipboard(copiedFolder = Files.getParent(_xqueryFile));
                    textXquery.setFocus();
                    break;
                }

                SwtGeneral.messageBox("Copied to clipboard:\n\n" + copiedFolder, "XML Operations");
            }
        });
        btnCopyFolder.setEnabled(false);
        btnCopyFolder.setImage(SwtResourceManager.getImage(_display, MainWindow.class,
                "/resources/xmloperations/icons/copyfolder.png"));

        btnCut = new Button(compositeToolBar, SWT.NONE);
        btnCut.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                _lastSelectedForClipboard.cut();
                _lastSelectedForClipboard.setFocus();
            }
        });
        btnCut.setToolTipText("Cut (Ctrl+X)");
        btnCut.setEnabled(false);
        btnCut.setImage(
                SwtResourceManager.getImage(_display, MainWindow.class, "/resources/xmloperations/icons/cut.png"));

        btnCopy = new Button(compositeToolBar, SWT.NONE);
        btnCopy.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                _lastSelectedForClipboard.copy();
                _lastSelectedForClipboard.setFocus();
            }
        });
        btnCopy.setToolTipText("Copy (Ctrl+Ins)");
        btnCopy.setEnabled(false);
        btnCopy.setImage(
                SwtResourceManager.getImage(_display, MainWindow.class, "/resources/xmloperations/icons/copy.png"));

        btnPaste = new Button(compositeToolBar, SWT.NONE);
        btnPaste.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                if ((e.stateMask & SWT.CTRL) != 0 && _lastFocusedOperationType == FileType.XSLT) {
                    int selectionPos = textXslt.getSelection().x + XSLT_NO_BLANKS_LENGTH;
                    textXslt.insert(XSLT_NO_BLANKS);
                    textXslt.setSelection(selectionPos, selectionPos);
                    textXslt.setFocus();
                } else if ((e.stateMask & SWT.CTRL) != 0 && _lastFocusedOperationType == FileType.XQUERY) {
                    int selectionPos = textXquery.getSelection().x + XQUERY_SOURCE_FILE_PATH_LENGTH;
                    textXquery.insert(XQUERY_SOURCE_FILE_PATH);
                    textXquery.setSelection(selectionPos, selectionPos);
                    textXquery.setFocus();
                } else {
                    _lastSelectedForClipboard.paste();
                    _lastSelectedForClipboard.setFocus();
                }
            }
        });
        btnPaste.setToolTipText("Paste (Shift+Ins)");
        btnPaste.setEnabled(false);
        btnPaste.setImage(SwtResourceManager.getImage(_display, MainWindow.class,
                "/resources/xmloperations/icons/paste.png"));

        Label labelSeparator2 = new Label(compositeToolBar, SWT.NONE);
        labelSeparator2.setLayoutData(new RowData(LABEL_GAP, SWT.DEFAULT));

        btnUndo = new Button(compositeToolBar, SWT.NONE);
        btnUndo.setToolTipText("Undo (Ctrl+Z)");
        btnUndo.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                if (_lastSelectedForClipboardUndoRedo != null) {
                    _lastSelectedForClipboardUndoRedo.undo();
                    _lastSelectedForClipboard.setFocus();
                }
            }
        });
        btnUndo.setEnabled(false);
        btnUndo.setImage(
                SwtResourceManager.getImage(_display, MainWindow.class, "/resources/xmloperations/icons/undo.png"));

        btnRedo = new Button(compositeToolBar, SWT.NONE);
        btnRedo.setToolTipText("Redo (Ctrl+Y)");
        btnRedo.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                if (_lastSelectedForClipboardUndoRedo != null) {
                    _lastSelectedForClipboardUndoRedo.redo();
                    _lastSelectedForClipboard.setFocus();
                }
            }
        });
        btnRedo.setEnabled(false);
        btnRedo.setImage(
                SwtResourceManager.getImage(_display, MainWindow.class, "/resources/xmloperations/icons/redo.png"));

        Label labelSeparator7 = new Label(compositeToolBar, SWT.NONE);
        labelSeparator7.setLayoutData(new RowData(LABEL_GAP, SWT.DEFAULT));

        btnFont = new Button(compositeToolBar, SWT.NONE);
        btnFont.setImage(
                SwtResourceManager.getImage(_display, MainWindow.class, "/resources/xmloperations/icons/font.png"));
        btnFont.setToolTipText("Font");
        btnFont.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                FontDialog fontDialog = new FontDialog(_shlXmlOperations);
                fontDialog.setFontList(textXml.getFont().getFontData());
                FontData fontData = fontDialog.open();
                if (fontData != null)
                    setFont(fontData.getName(), (int) fontData.height, fontData.getStyle());

                setFocus(_lastFocusedFileType);
            }
        });

        btnClearResult = new Button(compositeToolBar, SWT.NONE);
        btnClearResult.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                textResult.setText("");

                lblStatus.setText("Ready");

                setFocus(_lastFocusedFileType);
            }
        });
        btnClearResult.setToolTipText("Clear result");
        btnClearResult.setImage(SwtResourceManager.getImage(_display, MainWindow.class,
                "/resources/xmloperations/icons/clear.png"));

        Label labelSeparator3 = new Label(compositeToolBar, SWT.NONE);
        labelSeparator3.setLayoutData(new RowData(LABEL_GAP, SWT.DEFAULT));

        btnNamespaces = new Button(compositeToolBar, SWT.NONE);
        btnNamespaces.setImage(SwtResourceManager.getImage(_display, MainWindow.class,
                "/resources/xmloperations/icons/namespaces.png"));
        btnNamespaces.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                printNamespaces();
            }
        });
        btnNamespaces.setToolTipText("Namespaces (F4)");
        btnNamespaces.setText("NS:");

        Label labelSeparator8 = new Label(compositeToolBar, SWT.NONE);
        labelSeparator8.setLayoutData(new RowData(LABEL_GAP, SWT.DEFAULT));

        btnAutoSave = new Button(compositeToolBar, SWT.TOGGLE);
        btnAutoSave.setToolTipText("Autosave before each operation");
        btnAutoSave.setImage(SwtResourceManager.getImage(_display, MainWindow.class,
                "/resources/xmloperations/icons/autosave.png"));

        toolBarApply = new ToolBar(compositeToolBar, SWT.FLAT | SWT.WRAP | SWT.RIGHT);

        btnDropDownApply = new ToolItem(toolBarApply, SWT.DROP_DOWN);
        btnDropDownApply.setWidth(-1);
        btnDropDownApply.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                if (e.detail == SWT.ARROW) {
                    Rectangle rect = btnDropDownApply.getBounds();
                    Point pt = btnDropDownApply.getParent().toDisplay(new Point(rect.x, rect.y));
                    menuApply.setLocation(pt.x, pt.y + rect.height);
                    menuApply.setVisible(true);
                } else {
                    onKeyEvent(null, SWT.F5, e.stateMask);
                    setFocus(_lastFocusedFileType);
                }
            }
        });
        btnDropDownApply.setText("Apply");
        btnDropDownApply.setToolTipText("Apply XPath (F5)");
        btnDropDownApply.setImage(SwtResourceManager.getImage(_display, MainWindow.class,
                "/resources/xmloperations/icons/apply.png"));

        Label labelSeparator5 = new Label(compositeToolBar, SWT.NONE);
        labelSeparator5.setLayoutData(new RowData(LABEL_GAP, SWT.DEFAULT));

        btnAbout = new Button(compositeToolBar, SWT.NONE);
        btnAbout.setImage(SwtResourceManager.getImage(_display, MainWindow.class,
                "/resources/xmloperations/icons/about.png"));
        btnAbout.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                AboutBox about = new AboutBox(_shlXmlOperations, "resources/xmloperations/metadata.properties",
                        "en");
                about.open();

                setFocus(_lastFocusedFileType);
            }
        });
        btnAbout.setToolTipText("About XML Operations\nPress F1 for help");

        splitSourceResult = new SashForm(compositeMain, SWT.NONE);
        splitSourceResult.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1));

        compositeXmlOthers = new Composite(splitSourceResult, SWT.NONE);
        compositeXmlOthers.setLayout(new FillLayout(SWT.VERTICAL));

        splitXmlOthers = new SashForm(compositeXmlOthers, SWT.VERTICAL);

        tabsXml = new TabFolder(splitXmlOthers, SWT.NONE);
        tabsXml.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDoubleClick(MouseEvent e) {
                if ((e.stateMask & SWT.CONTROL) == 0)
                    splitToRightOrDownOnDoubleClick(splitXmlOthers);
                else
                    splitToRightOrDownOnDoubleClick(splitSourceResult);
            }
        });
        tabsXml.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                textXml.setFocus();
            }
        });

        tabXml = new TabItem(tabsXml, SWT.NONE);
        tabXml.setToolTipText("Source document (Ctrl+0)");
        tabXml.setText("XML");

        textXml = new EnhancedStyledText(tabsXml, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL);
        textXml.setAlwaysShowScrollBars(false);
        textXml.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                setClipboardAndUndoEnable();
            }
        });
        textXml.addCaretListener(new CaretListener() {
            @Override
            public void caretMoved(CaretEvent arg0) {
                setClipboardAndUndoEnable();
            }
        });
        textXml.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                onKeyEvent(FileType.XML, e.keyCode, e.stateMask);
            }
        });
        textXml.setBackground(SwtResourceManager.getColor(255, 255, 255));
        textXml.setTabs(TEXT_TAB_SIZE);
        textXml.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent arg0) {
                if (!_modifyingFile)
                    _xmlFileChanged = true;
                else
                    _modifyingFile = false;

                setStatus();
            }
        });
        textXml.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                _lastFocusedFileType = FileType.XML;
                setStatus();

                _lastSelectedForClipboard = textXml;
                _lastSelectedForClipboardUndoRedo = _xmlFileUndoRedo;
                setClipboardAndUndoEnable();

                btnNew.setToolTipText("New XML (Ctrl+N)");
                btnOpen.setToolTipText("Open XML (Ctrl+O)");
                btnSave.setToolTipText("Save XML (Ctrl+S)");
                btnSaveAs.setToolTipText("Save XML as");
            }
        });
        tabXml.setControl(textXml);

        tabsOthers = new TabFolder(splitXmlOthers, SWT.NONE);
        tabsOthers.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDoubleClick(MouseEvent e) {
                if ((e.stateMask & SWT.CONTROL) == 0)
                    splitToLeftOrUpOnDoubleClick(splitXmlOthers);
                else
                    splitToRightOrDownOnDoubleClick(splitSourceResult);
            }
        });
        tabsOthers.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                if (textXpath == null)
                    return;

                switch (tabsOthers.getSelectionIndex()) {
                case 0:
                    textXpath.setFocus();
                    break;

                case 1:
                    textXslt.setFocus();
                    break;

                case 2:
                    textXquery.setFocus();
                    break;

                case 3:
                    textXsd.setFocus();
                    break;

                case 4:
                    textDtd.setFocus();
                    break;
                }
            }
        });
        tabsOthers.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                if (textXpath == null)
                    return;

                switch (tabsOthers.getSelectionIndex()) {
                case 0:
                    textXpath.setFocus();
                    break;

                case 1:
                    textXslt.setFocus();
                    break;

                case 2:
                    textXquery.setFocus();
                    break;

                case 3:
                    textXsd.setFocus();
                    break;

                case 4:
                    textDtd.setFocus();
                    break;
                }
            }
        });

        tabXpath = new TabItem(tabsOthers, SWT.NONE);
        tabXpath.setToolTipText(
                "XPath query (Ctrl+1)\r\nCheck the namespace prefixes (hit F4) if needed\r\nPress Enter to apply or hit F1 for instructions");
        tabXpath.setText("XPath");

        textXpath = new EnhancedStyledText(tabsOthers, SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.MULTI);
        textXpath.setAlwaysShowScrollBars(false);
        textXpath.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                setClipboardAndUndoEnable();
            }
        });
        textXpath.addCaretListener(new CaretListener() {
            @Override
            public void caretMoved(CaretEvent arg0) {
                setClipboardAndUndoEnable();
            }
        });
        textXpath.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                onKeyEvent(FileType.XPATH, e.keyCode, e.stateMask);
            }
        });
        textXpath.addVerifyKeyListener(new VerifyKeyListener() {
            @Override
            public void verifyKey(VerifyEvent arg0) {
                if (arg0.keyCode == SWT.CR && (arg0.stateMask & SWT.CONTROL) == 0) {
                    arg0.doit = false;
                    applyXPath();
                }
            }
        });
        textXpath.setTabs(TEXT_TAB_SIZE);
        textXpath.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent arg0) {
                if (!_modifyingFile)
                    _xpathFileChanged = true;
                else
                    _modifyingFile = false;

                setStatus();
            }
        });
        textXpath.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                _lastFocusedFileType = _lastFocusedOperationType = FileType.XPATH;
                setStatus();

                _lastSelectedForClipboard = textXpath;
                _lastSelectedForClipboardUndoRedo = _xpathFileUndoRedo;
                setClipboardAndUndoEnable();

                btnNew.setToolTipText("New XPath (Ctrl+N)");
                btnOpen.setToolTipText("Open XPath (Ctrl+O)");
                btnSave.setToolTipText("Save XPath (Ctrl+S)");
                btnSaveAs.setToolTipText("Save XPath as");

                btnDropDownApply.setToolTipText("Apply XPath (F5)");
                menuItemApplyAndShowInBrowser.setEnabled(false);
                menuItemShowInBrowser.setEnabled(false);
            }
        });
        tabXpath.setControl(textXpath);

        tabXslt = new TabItem(tabsOthers, SWT.NONE);
        tabXslt.setToolTipText("XSLT transformation (Ctrl+2)\r\nUse files from disk when dealing with xsl:import");
        tabXslt.setText("XSLT");

        textXslt = new EnhancedStyledText(tabsOthers,
                SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL | SWT.MULTI);
        textXslt.setAlwaysShowScrollBars(false);
        textXslt.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                setClipboardAndUndoEnable();
            }
        });
        textXslt.addCaretListener(new CaretListener() {
            @Override
            public void caretMoved(CaretEvent arg0) {
                setClipboardAndUndoEnable();
            }
        });
        textXslt.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                onKeyEvent(FileType.XSLT, e.keyCode, e.stateMask);
            }
        });
        textXslt.setTabs(TEXT_TAB_SIZE);
        textXslt.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent arg0) {
                if (!_modifyingFile)
                    _xsltFileChanged = true;
                else
                    _modifyingFile = false;

                setStatus();
            }
        });
        textXslt.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                _lastFocusedFileType = _lastFocusedOperationType = FileType.XSLT;
                setStatus();

                _lastSelectedForClipboard = textXslt;
                _lastSelectedForClipboardUndoRedo = _xsltFileUndoRedo;
                setClipboardAndUndoEnable();

                btnNew.setToolTipText("New XSLT (Ctrl+N)");
                btnOpen.setToolTipText("Open XSLT (Ctrl+O)");
                btnSave.setToolTipText("Save XSLT (Ctrl+S)");
                btnSaveAs.setToolTipText("Save XSLT as");

                btnDropDownApply.setToolTipText("Apply XSLT (F5)");
                menuItemApplyAndShowInBrowser.setEnabled(true);
                menuItemShowInBrowser.setEnabled(true);
            }
        });
        tabXslt.setControl(textXslt);

        tabXquery = new TabItem(tabsOthers, 0);
        tabXquery.setToolTipText("XQuery (Ctrl+3)");
        tabXquery.setText("XQuery");

        textXquery = new EnhancedStyledText(tabsOthers,
                SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL | SWT.MULTI);
        textXquery.setAlwaysShowScrollBars(false);
        textXquery.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent arg0) {
                setClipboardAndUndoEnable();
            }
        });
        textXquery.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent arg0) {
                if (!_modifyingFile)
                    _xqueryFileChanged = true;
                else
                    _modifyingFile = false;

                setStatus();
            }
        });
        textXquery.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                onKeyEvent(FileType.XQUERY, e.keyCode, e.stateMask);
            }
        });
        textXquery.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent arg0) {
                _lastFocusedFileType = _lastFocusedOperationType = FileType.XQUERY;
                setStatus();

                _lastSelectedForClipboard = textXquery;
                _lastSelectedForClipboardUndoRedo = _xqueryFileUndoRedo;
                setClipboardAndUndoEnable();

                btnNew.setToolTipText("New XQuery (Ctrl+N)");
                btnOpen.setToolTipText("Open XQuery (Ctrl+O)");
                btnSave.setToolTipText("Save XQuery (Ctrl+S)");
                btnSaveAs.setToolTipText("Save XQuery as");

                btnDropDownApply.setToolTipText("Apply XQuery (F5)");
                menuItemApplyAndShowInBrowser.setEnabled(true);
                menuItemShowInBrowser.setEnabled(true);
            }
        });
        textXquery.addCaretListener(new CaretListener() {
            @Override
            public void caretMoved(CaretEvent arg0) {
                setClipboardAndUndoEnable();
            }
        });
        textXquery.setTabs(TEXT_TAB_SIZE);
        tabXquery.setControl(textXquery);

        tabXsd = new TabItem(tabsOthers, SWT.NONE);
        tabXsd.setToolTipText("XSD verification (Ctrl+4)\r\nUse files from disk when dealing with xsd:import");
        tabXsd.setText("XSD");

        textXsd = new EnhancedStyledText(tabsOthers,
                SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL | SWT.MULTI);
        textXsd.setAlwaysShowScrollBars(false);
        textXsd.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                setClipboardAndUndoEnable();
            }
        });
        textXsd.addCaretListener(new CaretListener() {
            @Override
            public void caretMoved(CaretEvent arg0) {
                setClipboardAndUndoEnable();
            }
        });
        textXsd.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                onKeyEvent(FileType.XSD, e.keyCode, e.stateMask);
            }
        });
        textXsd.setTabs(TEXT_TAB_SIZE);
        textXsd.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent arg0) {
                if (!_modifyingFile)
                    _xsdFileChanged = true;
                else
                    _modifyingFile = false;

                setStatus();
            }
        });
        textXsd.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                _lastFocusedFileType = _lastFocusedOperationType = FileType.XSD;
                setStatus();

                _lastSelectedForClipboard = textXsd;
                _lastSelectedForClipboardUndoRedo = _xsdFileUndoRedo;
                setClipboardAndUndoEnable();

                btnNew.setToolTipText("New XSD (Ctrl+N)");
                btnOpen.setToolTipText("Open XSD (Ctrl+O)");
                btnSave.setToolTipText("Save XSD (Ctrl+S)");
                btnSaveAs.setToolTipText("Save XSD as");

                btnDropDownApply.setToolTipText("Apply XSD (F5)");
                menuItemApplyAndShowInBrowser.setEnabled(false);
                menuItemShowInBrowser.setEnabled(false);
            }
        });
        tabXsd.setControl(textXsd);
        splitXmlOthers.setWeights(new int[] { 1, 1 });

        tabDtd = new TabItem(tabsOthers, SWT.NONE);
        tabDtd.setToolTipText("DTD verification (Ctrl+5)");
        tabDtd.setText("DTD");

        textDtd = new EnhancedStyledText(tabsOthers,
                SWT.BORDER | SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL | SWT.MULTI);
        textDtd.setAlwaysShowScrollBars(false);
        textDtd.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                setClipboardAndUndoEnable();
            }
        });
        textDtd.addModifyListener(new ModifyListener() {
            @Override
            public void modifyText(ModifyEvent arg0) {
                if (!_modifyingFile)
                    _dtdFileChanged = true;
                else
                    _modifyingFile = false;

                setStatus();
            }
        });
        textDtd.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                onKeyEvent(FileType.DTD, e.keyCode, e.stateMask);
            }
        });
        textDtd.addCaretListener(new CaretListener() {
            @Override
            public void caretMoved(CaretEvent arg0) {
                setClipboardAndUndoEnable();
            }
        });
        textDtd.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                _lastFocusedFileType = _lastFocusedOperationType = FileType.DTD;
                setStatus();

                _lastSelectedForClipboard = textDtd;
                _lastSelectedForClipboardUndoRedo = _dtdFileUndoRedo;
                setClipboardAndUndoEnable();

                btnNew.setToolTipText("New DTD (Ctrl+N)");
                btnOpen.setToolTipText("Open DTD (Ctrl+O)");
                btnSave.setToolTipText("Save DTD (Ctrl+S)");
                btnSaveAs.setToolTipText("Save DTD as");

                btnDropDownApply.setToolTipText("Apply DTD (F5)");
                menuItemApplyAndShowInBrowser.setEnabled(false);
                menuItemShowInBrowser.setEnabled(false);
            }
        });
        textDtd.setTabs(2);
        tabDtd.setControl(textDtd);

        compositeResult = new Composite(splitSourceResult, SWT.NONE);
        compositeResult.setLayout(new FillLayout(SWT.HORIZONTAL));

        tabsResultText = new TabFolder(compositeResult, SWT.NONE);
        tabsResultText.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDoubleClick(MouseEvent e) {
                splitToLeftOrUpOnDoubleClick(splitSourceResult);
            }
        });
        tabsResultText.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                textResult.setFocus();
            }
        });

        tabResultText = new TabItem(tabsResultText, SWT.NONE);
        tabResultText.setToolTipText("Result in text format (Ctrl+R)");
        tabResultText.setText("Result");

        textResult = new EnhancedStyledText(tabsResultText,
                SWT.BORDER | SWT.READ_ONLY | SWT.H_SCROLL | SWT.V_SCROLL | SWT.CANCEL | SWT.MULTI);
        textResult.setAlwaysShowScrollBars(false);
        textResult.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseDown(MouseEvent e) {
                setClipboardAndUndoEnable();
            }
        });
        textResult.addCaretListener(new CaretListener() {
            @Override
            public void caretMoved(CaretEvent arg0) {
                setClipboardAndUndoEnable();
            }
        });
        textResult.addFocusListener(new FocusAdapter() {
            @Override
            public void focusGained(FocusEvent e) {
                _lastSelectedForClipboard = textResult;
                _lastSelectedForClipboardUndoRedo = null;
                setClipboardAndUndoEnable();
            }
        });
        textResult.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                onKeyEvent(null, e.keyCode, e.stateMask);
            }
        });
        textResult.setBackground(SwtResourceManager.getColor(255, 255, 255));
        textResult.setTabs(TEXT_TAB_SIZE);
        tabResultText.setControl(textResult);
        splitSourceResult.setWeights(new int[] { 1, 1 });

        compositeStatusBar = new Composite(compositeMain, SWT.NONE);
        RowLayout rl_compositeStatusBar = new RowLayout(SWT.HORIZONTAL);
        rl_compositeStatusBar.fill = true;
        rl_compositeStatusBar.marginTop = 0;
        rl_compositeStatusBar.marginBottom = 0;
        compositeStatusBar.setLayout(rl_compositeStatusBar);
        GridData gd_compositeStatusBar = new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1);
        gd_compositeStatusBar.heightHint = 15;
        compositeStatusBar.setLayoutData(gd_compositeStatusBar);

        lblStatus = new Label(compositeStatusBar, SWT.NONE);
        lblStatus.setLayoutData(new RowData(850, 15));
        lblStatus.setText("Ready");
    }

    /**
     * Load dropped file.
     *
     * @param file
     *          Dropped file to load.
     */
    private void loadDroppedFile(String file) {
        Boolean fileChanged = false;

        FileType fileType = getFileType(file);

        switch (fileType) {
        case XML:
            fileChanged = _xmlFileChanged;
            break;

        case XPATH:
            fileChanged = _xpathFileChanged;
            tabsOthers.setSelection(0);
            break;

        case XSLT:
            fileChanged = _xsltFileChanged;
            tabsOthers.setSelection(1);
            break;

        case XQUERY:
            fileChanged = _xqueryFileChanged;
            tabsOthers.setSelection(2);
            break;

        case XSD:
            fileChanged = _xsdFileChanged;
            tabsOthers.setSelection(3);
            break;

        case DTD:
            fileChanged = _dtdFileChanged;
            tabsOthers.setSelection(4);
            break;

        default:
            break;
        }

        if (fileChanged)
            switch (SwtGeneral
                    .messageBox(
                            String.format("The %s file has been modified. Do you want to save the current changes?",
                                    fileType),
                            "XML Operations", SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL)) {
            case SWT.YES:
                if (!fileSave(fileType))
                    return;
                else
                    break;

            case SWT.NO:
                break;

            case SWT.CANCEL:
                setFocus(fileType);
                return;
            }

        try {
            if (Files.hasBom(file))
                switch (SwtGeneral.messageBox(String.format(
                        "BOM warning: the %s file you have selected has a Byte Order Mark (BOM) as prolog. "
                                + "This will not be supported by the XML libraries in the system so do you want to work on a no-BOM copy of the file?",
                        fileType), "XML Operations", SWT.ICON_QUESTION | SWT.YES | SWT.NO)) {
                case SWT.YES:
                    file = Files.copyFileNoBom(file);
                    break;

                case SWT.NO:
                    break;
                }
        } catch (IOException e) {
            SwtGeneral.messageBoxError(String.format("File manipulation error (%s)", e.getMessage()),
                    "XML Operations");
            _log.error(Debug.printStackTraceForLogging(e));
        }

        try {
            _modifyingFile = true;

            switch (fileType) {
            case XML:
                textXml.setText(Files.readTextFile(file));
                textXml.setFocus();
                _xmlFile = file;
                _xmlFileChanged = false;
                break;

            case XPATH:
                textXpath.setText(Files.readTextFile(file));
                textXpath.setFocus();
                _xpathFile = file;
                _xpathFileChanged = false;
                break;

            case XSD:
                textXsd.setText(Files.readTextFile(file));
                textXsd.setFocus();
                _xsdFile = file;
                _xsdFileChanged = false;
                break;

            case XSLT:
                textXslt.setText(Files.readTextFile(file));
                textXslt.setFocus();
                _xsltFile = file;
                _xsltFileChanged = false;
                break;

            case XQUERY:
                textXquery.setText(Files.readTextFile(file));
                textXquery.setFocus();
                _xqueryFile = file;
                _xqueryFileChanged = false;
                break;

            case DTD:
                textDtd.setText(Files.readTextFile(file));
                textDtd.setFocus();
                _dtdFile = file;
                _dtdFileChanged = false;
                break;

            default:
                break;
            }
        } catch (Exception e) {
            SwtGeneral.messageBoxError(String.format("Error while opening file (%s)", e.getMessage()),
                    "XML Operations");
            _log.error(Debug.printStackTraceForLogging(e));
        }

        _modifyingFile = false;
        setStatus();
        lblStatus.setText("Ready");
        setFocus(fileType);

        if (_lastSelectedForClipboardUndoRedo != null) {
            _lastSelectedForClipboardUndoRedo.reset();
            setClipboardAndUndoEnable();
        }
    }

    /**
     * Get file type.
     * 
     * @param file
     *          File to get the type for.
     * 
     * @return Type of input file.
     */
    private FileType getFileType(String file) {
        String extension = FilenameUtils.getExtension(file);
        if (extension.equals("nobom"))
            extension = FilenameUtils.getExtension(file.substring(0, file.length() - ".nobom".length()));

        switch (extension) {
        case "xml":
        case "config":
        case "csproj":
            return FileType.XML;

        case "xpath":
            return FileType.XPATH;

        case "xsl":
        case "xslt":
            return FileType.XSLT;

        case "xsd":
            return FileType.XSD;

        case "dtd":
            return FileType.DTD;

        case "xquery":
        case "xq":
        case "xqy":
            return FileType.XQUERY;
        }

        return FileType.XML;
    }

    /**
     * Open file.
     *
     * @param fileType
     *          Type of the file.
     */
    private void fileOpen(FileType fileType) {
        Boolean fileChanged = false;

        switch (fileType) {
        case XML:
            fileChanged = _xmlFileChanged;
            break;

        case XPATH:
            fileChanged = _xpathFileChanged;
            break;

        case XSD:
            fileChanged = _xsdFileChanged;
            break;

        case XSLT:
            fileChanged = _xsltFileChanged;
            break;

        case XQUERY:
            fileChanged = _xqueryFileChanged;
            break;

        case DTD:
            fileChanged = _dtdFileChanged;
            break;

        default:
            break;
        }

        if (fileChanged)
            switch (SwtGeneral.messageBox("Do you want to save the current changes?", "XML Operations",
                    SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL)) {
            case SWT.YES:
                if (!fileSave(fileType))
                    return;
                else
                    break;

            case SWT.NO:
                break;

            case SWT.CANCEL:
                setFocus(fileType);
                return;
            }

        String[] filters = null;

        switch (fileType) {
        case XML:
            filters = new String[] { "*.xml", "*.xml.nobom", "*" };
            break;

        case XPATH:
            filters = new String[] { "*.xpath", "*.xpath.nobom", "*.txt", "*" };
            break;

        case XSD:
            filters = new String[] { "*.xsd", "*.xsd.nobom", "*" };
            break;

        case XSLT:
            filters = new String[] { "*.xsl", "*.xslt", "*.xsl.nobom", "*.xslt.nobom", "*" };
            break;

        case XQUERY:
            filters = new String[] { "*.xquery", "*.xq", "*.xqy", "*.xquery.nobom", "*.xq.nobom", "*.xqy.nobom",
                    "*" };
            break;

        case DTD:
            filters = new String[] { "*.dtd", "*.dtd.nobom", "*" };
            break;

        default:
            break;
        }

        String[] target = SwtFiles.browseForFiles(null, null, "Open", filters, SWT.OPEN | SWT.SINGLE);

        if (target == null) {
            setFocus(fileType);
            return;
        }

        try {
            if (Files.hasBom(target[0]))
                switch (SwtGeneral.messageBox(
                        "BOM warning: the file you have selected has a Byte Order Mark (BOM) as prolog. "
                                + "This will not be supported by the XML libraries in the system so do you want to work on a no-BOM copy of the file?",
                        "XML Operations", SWT.ICON_QUESTION | SWT.YES | SWT.NO)) {
                case SWT.YES:
                    target[0] = Files.copyFileNoBom(target[0]);
                    break;

                case SWT.NO:
                    break;
                }
        } catch (IOException e) {
            SwtGeneral.messageBoxError(String.format("File manipulation error (%s)", e.getMessage()),
                    "XML Operations");
            _log.error(Debug.printStackTraceForLogging(e));
        }

        try {
            _modifyingFile = true;

            switch (fileType) {
            case XML:
                textXml.setText(Files.readTextFile(target[0]));
                textXml.setFocus();
                _xmlFile = target[0];
                _xmlFileChanged = false;
                break;

            case XPATH:
                textXpath.setText(Files.readTextFile(target[0]));
                textXpath.setFocus();
                _xpathFile = target[0];
                _xpathFileChanged = false;
                break;

            case XSD:
                textXsd.setText(Files.readTextFile(target[0]));
                textXsd.setFocus();
                _xsdFile = target[0];
                _xsdFileChanged = false;
                break;

            case XSLT:
                textXslt.setText(Files.readTextFile(target[0]));
                textXslt.setFocus();
                _xsltFile = target[0];
                _xsltFileChanged = false;
                break;

            case XQUERY:
                textXquery.setText(Files.readTextFile(target[0]));
                textXquery.setFocus();
                _xqueryFile = target[0];
                _xqueryFileChanged = false;
                break;

            case DTD:
                textDtd.setText(Files.readTextFile(target[0]));
                textDtd.setFocus();
                _dtdFile = target[0];
                _dtdFileChanged = false;
                break;

            default:
                break;
            }
        } catch (Exception e) {
            SwtGeneral.messageBoxError(String.format("Error while opening file (%s)", e.getMessage()),
                    "XML Operations");
            _log.error(Debug.printStackTraceForLogging(e));
        }

        _modifyingFile = false;
        setStatus();
        lblStatus.setText("Ready");
        setFocus(fileType);

        if (_lastSelectedForClipboardUndoRedo != null) {
            _lastSelectedForClipboardUndoRedo.reset();
            setClipboardAndUndoEnable();
        }
    }

    /**
     * Save file.
     *
     * @param fileType
     *          Type of the file.
     *
     * @return true if successfully saving, otherwise false.
     */
    private Boolean fileSave(FileType fileType) {
        switch (fileType) {
        case XML:
            if (_xmlFileChanged)
                if (_xmlFile.length() == 0)
                    return fileSaveAs(fileType);
                else
                    return saveFileOnDisk(_xmlFile, fileType);
            break;

        case XPATH:
            if (_xpathFileChanged)
                if (_xpathFile.length() == 0)
                    return fileSaveAs(fileType);
                else
                    return saveFileOnDisk(_xpathFile, fileType);
            break;

        case XSD:
            if (_xsdFileChanged)
                if (_xsdFile.length() == 0)
                    return fileSaveAs(fileType);
                else
                    return saveFileOnDisk(_xsdFile, fileType);
            break;

        case DTD:
            if (_dtdFileChanged)
                if (_dtdFile.length() == 0)
                    return fileSaveAs(fileType);
                else
                    return saveFileOnDisk(_dtdFile, fileType);
            break;

        case XSLT:
            if (_xsltFileChanged)
                if (_xsltFile.length() == 0)
                    return fileSaveAs(fileType);
                else
                    return saveFileOnDisk(_xsltFile, fileType);
            break;

        case XQUERY:
            if (_xqueryFileChanged)
                if (_xqueryFile.length() == 0)
                    return fileSaveAs(fileType);
                else
                    return saveFileOnDisk(_xqueryFile, fileType);
            break;

        default:
            break;
        }

        setFocus(fileType);
        return false;
    }

    /**
     * Save file.
     *
     * @param fileType
     *          Type of the file.
     *
     * @return true if the operation was completed, otherwise false.
     */
    private Boolean fileSaveAs(FileType fileType) {
        String[] filters = null;

        switch (_lastFocusedFileType) {
        case XML:
            filters = new String[] { "*.xml", "*" };
            break;

        case XPATH:
            filters = new String[] { "*.xpath", "*.txt", "*" };
            break;

        case XSD:
            filters = new String[] { "*.xsd", "*" };
            break;

        case DTD:
            filters = new String[] { "*.dtd", "*" };
            break;

        case XSLT:
            filters = new String[] { "*.xsl", "*.xslt", "*" };
            break;

        case XQUERY:
            filters = new String[] { "*.xquery", "*.xq", "*.xqy", "*" };
            break;

        default:
            break;
        }

        String[] target = SwtFiles.browseForFiles(null, null, "Save As", filters, SWT.SAVE | SWT.SINGLE);

        if (target == null) {
            setFocus(fileType);
            return false;
        }

        if (Files.exists(target[0]))
            if (SwtGeneral.messageBox("The file already exists, overwrite?", "XML Operations",
                    SWT.ICON_QUESTION | SWT.OK | SWT.CANCEL) == SWT.CANCEL) {
                setFocus(fileType);
                return false;
            }

        return saveFileOnDisk(target[0], fileType);
    }

    /**
     * Save file on disk.
     *
     * @param destinationFile
     *          File to save into.
     * @param fileType
     *          of the file to be saved.
     *
     * @return true if saved successfully, otherwise false.
     */
    private Boolean saveFileOnDisk(String destinationFile, FileType fileType) {
        try {
            switch (fileType) {
            case XML:
                Files.writeTextFile(textXml.getText(), _xmlFile = destinationFile);
                _xmlFileChanged = false;
                break;

            case XPATH:
                Files.writeTextFile(textXpath.getText(), _xpathFile = destinationFile);
                _xpathFileChanged = false;
                break;

            case XSD:
                Files.writeTextFile(textXsd.getText(), _xsdFile = destinationFile);
                _xsdFileChanged = false;
                break;

            case DTD:
                Files.writeTextFile(textDtd.getText(), destinationFile);
                if (destinationFile != DTD_TEMP_FILE) {
                    _dtdFile = destinationFile;
                    _dtdFileChanged = false;
                }
                break;

            case XSLT:
                Files.writeTextFile(textXslt.getText(), _xsltFile = destinationFile);
                _xsltFileChanged = false;
                break;

            case XQUERY:
                Files.writeTextFile(textXquery.getText(), _xqueryFile = destinationFile);
                _xqueryFileChanged = false;
                break;

            default:
                break;
            }
        } catch (IOException e) {
            SwtGeneral.messageBoxError(String.format("Error while saving file (%s)", e.getMessage()),
                    "XML Operations");
            _log.error(Debug.printStackTraceForLogging(e));

            return false;
        } finally {
            setStatus();
            setFocus(fileType);
        }

        return true;
    }

    /**
     * New file.
     *
     * @param fileType
     *          Type of file.
     */
    private void fileNew(FileType fileType) {
        Boolean fileChanged = false;

        switch (fileType) {
        case XML:
            fileChanged = _xmlFileChanged;
            break;

        case XPATH:
            fileChanged = _xpathFileChanged;
            break;

        case XSD:
            fileChanged = _xsdFileChanged;
            break;

        case DTD:
            fileChanged = _dtdFileChanged;
            break;

        case XSLT:
            fileChanged = _xsltFileChanged;
            break;

        case XQUERY:
            fileChanged = _xqueryFileChanged;
            break;

        default:
            break;
        }

        if (fileChanged)
            switch (SwtGeneral.messageBox("Do you want to save the current changes?", "XML Operations",
                    SWT.ICON_QUESTION | SWT.YES | SWT.NO | SWT.CANCEL)) {
            case SWT.YES:
                if (!fileSave(fileType))
                    return;
                else
                    break;

            case SWT.NO:
                break;

            case SWT.CANCEL:
                return;
            }

        _modifyingFile = true;

        switch (fileType) {
        case XML:
            textXml.setText("");
            textXml.setFocus();
            _xmlFileChanged = false;
            _xmlFile = "";
            break;

        case XPATH:
            textXpath.setText("");
            textXpath.setFocus();
            _xpathFileChanged = false;
            _xpathFile = "";
            break;

        case XSD:
            textXsd.setText("");
            textXsd.setFocus();
            _xsdFileChanged = false;
            _xsdFile = "";
            break;

        case DTD:
            textDtd.setText("");
            textDtd.setFocus();
            _dtdFileChanged = false;
            _dtdFile = "";
            break;

        case XSLT:
            textXslt.setText("");
            textXslt.setFocus();
            _xsltFileChanged = false;
            _xsltFile = "";
            break;

        case XQUERY:
            textXquery.setText("");
            textXquery.setFocus();
            _xqueryFileChanged = false;
            _xqueryFile = "";
            break;

        default:
            break;
        }

        setStatus();
        lblStatus.setText("Ready");

        if (_lastSelectedForClipboardUndoRedo != null) {
            _lastSelectedForClipboardUndoRedo.reset();
            setClipboardAndUndoEnable();
        }
    }

    /**
     * Set status.
     */
    private void setStatus() {
        switch (_lastFocusedFileType) {
        case XML:
            _shlXmlOperations.setText(String.format(XML_OPERATIONS_TITLE, _xmlFileChanged ? "* " : "",
                    _xmlFile.length() == 0 ? "XML" : _xmlFile));
            btnSave.setEnabled(_xmlFileChanged);
            break;

        case XPATH:
            _shlXmlOperations.setText(String.format(XML_OPERATIONS_TITLE, _xpathFileChanged ? "* " : "",
                    _xpathFile.length() == 0 ? "XPath" : _xpathFile));
            btnSave.setEnabled(_xpathFileChanged);
            break;

        case XSD:
            _shlXmlOperations.setText(String.format(XML_OPERATIONS_TITLE, _xsdFileChanged ? "* " : "",
                    _xsdFile.length() == 0 ? "XSD" : _xsdFile));
            btnSave.setEnabled(_xsdFileChanged);
            break;

        case DTD:
            _shlXmlOperations.setText(String.format(XML_OPERATIONS_TITLE, _dtdFileChanged ? "* " : "",
                    _dtdFile.length() == 0 ? "DTD" : _dtdFile));
            btnSave.setEnabled(_dtdFileChanged);
            break;

        case XSLT:
            _shlXmlOperations.setText(String.format(XML_OPERATIONS_TITLE, _xsltFileChanged ? "* " : "",
                    _xsltFile.length() == 0 ? "XSLT" : _xsltFile));
            btnSave.setEnabled(_xsltFileChanged);
            break;

        case XQUERY:
            _shlXmlOperations.setText(String.format(XML_OPERATIONS_TITLE, _xqueryFileChanged ? "* " : "",
                    _xqueryFile.length() == 0 ? "XQuery" : _xqueryFile));
            btnSave.setEnabled(_xqueryFileChanged);
            break;

        default:
            break;
        }
    }

    /**
     * Print error in the result text area.
     *
     * @param fileType
     *          File type.
     * @param error
     *          Error message.
     */
    private void printError(FileType fileType, String error) {
        if (error == null)
            error = "Unknown error";

        textResult.setForeground(COLOR_RED);

        int errorLine = -1;

        switch (fileType) {
        case XPATH:
            textXpath.setFocus();
            break;

        case XQUERY:
            if (error.indexOf("Stopped at") == 0)
                error = error.substring(error.indexOf('\n') + 1);

            if (error.indexOf("Error: ") == 0)
                error = error.substring("Error: ".length());

            if (error.indexOf("[") == 0) {
                int closingSquare = error.indexOf(']');
                int firstSpace = error.indexOf(' ');

                if (closingSquare != -1 && closingSquare < firstSpace)
                    error = error.substring(closingSquare + 2);
            }

            String[] lines = error.split("\n");
            String[] tokens = lines[0].split("[ ,\\.]");
            for (int i = 0; i < tokens.length - 1; i++)
                if (tokens[i].equals("Line:") || tokens[i].equals("line"))
                    try {
                        errorLine = Integer.parseInt(tokens[i + 1]);
                        break;
                    } catch (NumberFormatException ex) {
                    }

            if (errorLine != -1)
                selectErrorLine(FileType.XQUERY, errorLine);
            break;

        default:
            int lineColPosEnd = error.indexOf(":");
            if (lineColPosEnd != -1) {
                String lineCol = error.substring(0, lineColPosEnd);

                String[] lineColPair;
                if (lineCol.startsWith("line ")) {
                    lineColPair = lineCol.split(" ");
                    if (lineColPair.length == 2)
                        lineColPair[0] = lineColPair[1];
                } else
                    lineColPair = lineCol.split(",");

                if (lineColPair.length == 2) {
                    errorLine = 1;

                    try {
                        errorLine = Integer.parseInt(lineColPair[0]);
                    } catch (NumberFormatException e) {
                    }

                    selectErrorLine(fileType, errorLine);
                }
            }
            break;
        }

        textResult.setText(error);
    }

    /**
     * Select error line.
     *
     * @param fileType
     *          Type of the file to select the error line into.
     * @param errorLine
     *          Error line.
     */
    private void selectErrorLine(FileType fileType, int errorLine) {
        EnhancedStyledText theText = textXml;
        switch (fileType) {
        case XML:
            theText = textXml;
            break;

        case XSD:
            theText = textXsd;
            break;

        case DTD:
            theText = textDtd;
            break;

        case XSLT:
            theText = textXslt;
            break;

        case XQUERY:
            theText = textXquery;
            break;

        default:
            break;
        }

        int offset = 0;
        String[] rows = theText.getText().split("\n");

        errorLine = Math.min(Math.max(errorLine, 1), rows.length);
        for (int i = 0; i < errorLine - 1; i++)
            offset += rows[i].length() + 1;
        int len = rows[errorLine - 1].length() - (rows[errorLine - 1].endsWith("\r") ? 1 : 0);

        theText.setSelection(offset, offset + len);
        theText.setFocus();
    }

    /**
     * Autosave before operating.
     *
     * @param fileType
     *          Type of the file to be saved.
     *
     * @return true if successfully saved, otherwise false.
     */
    private Boolean autoSave(FileType fileType) {
        if (btnAutoSave.getSelection()) {
            if (!saveFileOnDisk(_xmlFile, FileType.XML))
                return false;

            switch (fileType) {
            case DTD:
                if (_dtdFile.length() != 0 && _dtdFileChanged)
                    if (!saveFileOnDisk(_dtdFile, FileType.DTD))
                        return false;
                break;

            case XPATH:
                if (_xpathFile.length() != 0 && _xpathFileChanged)
                    if (!saveFileOnDisk(_xpathFile, FileType.XPATH))
                        return false;
                break;

            case XSD:
                if (_xsdFile.length() != 0 && _xsdFileChanged)
                    if (!saveFileOnDisk(_xsdFile, FileType.XSD))
                        return false;
                break;

            case XSLT:
                if (_xsltFile.length() != 0 && _xsltFileChanged)
                    if (!saveFileOnDisk(_xsltFile, FileType.XSLT))
                        return false;
                break;

            case XQUERY:
                if (_xqueryFile.length() != 0 && _xqueryFileChanged)
                    if (!saveFileOnDisk(_xqueryFile, FileType.XQUERY))
                        return false;
                break;

            default:
                return false;
            }
        }

        return true;
    }

    /**
     * Apply XPath.
     */
    private void applyXPath() {
        long startTime = System.currentTimeMillis();
        lblStatus.setText("Ready");

        Cursor defaultCursor = _shlXmlOperations.getCursor();
        _shlXmlOperations.setCursor(_cursorWait);

        try {
            if (!autoSave(FileType.XPATH))
                return;

            Xml xml = new Xml();
            if (!xml.load(textXml.getText())) {
                printError(FileType.XML, xml.getLastError());
                lblStatus.setText(printErrorTime());

                return;
            }

            Property<NodeList> nodes = new Property<NodeList>();
            if (xml.selectNodesWithDefaultNamespaces(SwtGeneral.getCurrentLine(textXpath), nodes)) {
                textResult.setForeground(COLOR_BLACK);

                StringBuilder sb = new StringBuilder();
                int numNodes = nodes.get().getLength();

                if (numNodes == 0)
                    sb.append("Empty selection");
                else
                    sb.append(String.format("%d node%s selected\n\n%s", numNodes, numNodes == 1 ? "" : "s",
                            numNodes == 1 ? "" : "-1-\n\n"));

                for (int i = 0; i < numNodes; i++) {
                    String xmlText = xml.nodeToString(nodes.get().item(i), false);
                    if (!xmlText.endsWith("\n"))
                        xmlText += "\n";
                    sb.append(xmlText);
                    sb.append(i == numNodes - 1 ? "" : String.format("\n-%d-\n\n", i + 2));
                }

                lblStatus.setText(printTimeDifference("XPath", startTime));

                textResult.setText(sb.toString());
            } else {
                printError(FileType.XPATH, xml.getLastError());
                lblStatus.setText(printErrorTime());
            }
        } finally {
            _shlXmlOperations.setCursor(defaultCursor);
        }
    }

    /**
     * Verify by XSD.
     */
    private void verifyByXsd() {
        long startTime = System.currentTimeMillis();
        lblStatus.setText("Ready");

        Cursor defaultCursor = _shlXmlOperations.getCursor();
        _shlXmlOperations.setCursor(_cursorWait);

        try {
            if (!autoSave(FileType.XSD))
                return;

            Xml xml = new Xml();
            if (!xml.load(textXml.getText())) {
                printError(FileType.XML, xml.getLastError());
                lblStatus.setText(printErrorTime());

                return;
            }

            Boolean result;
            if (_xsdFile.length() > 0 && !_xsdFileChanged)
                result = xml.validateAgainstXsdFile(_xsdFile);
            else
                result = xml.validateAgainstXsd(textXsd.getText());

            if (result) {
                lblStatus.setText(printTimeDifference("XSD", startTime));

                textResult.setForeground(COLOR_GREEN);
                textResult.setText("XML valid by schema");
            } else {
                printError(FileType.XSD, xml.getLastError());
                lblStatus.setText(printErrorTime());
            }
        } finally {
            _shlXmlOperations.setCursor(defaultCursor);
        }
    }

    /**
     * Verify by DTD.
     */
    private void verifyByDtd() {
        long startTime = System.currentTimeMillis();
        lblStatus.setText("Ready");

        Cursor defaultCursor = _shlXmlOperations.getCursor();
        _shlXmlOperations.setCursor(_cursorWait);

        try {
            if (!autoSave(FileType.DTD))
                return;

            Boolean inline = textDtd.getText().trim().length() == 0;

            Xml xml = new Xml(inline);
            if (!xml.load(textXml.getText())) {
                printError(FileType.XML, xml.getLastError());
                lblStatus.setText(printErrorTime());

                return;
            }

            if (!inline) {
                Boolean result = false;
                Property<Boolean> dtdError = new Property<Boolean>();

                if (_dtdFile.length() > 0 && !_dtdFileChanged)
                    result = xml.validateAgainstDtdFile(_dtdFile, dtdError);
                else if (saveFileOnDisk(DTD_TEMP_FILE, FileType.DTD))
                    result = xml.validateAgainstDtdFile(DTD_TEMP_FILE, dtdError);
                else
                    return;

                if (!result) {
                    printError(dtdError.get() ? FileType.DTD : FileType.XML, xml.getLastError());
                    lblStatus.setText(printErrorTime());

                    return;
                }
            }

            lblStatus.setText(printTimeDifference("DTD", startTime));

            textResult.setForeground(COLOR_GREEN);
            textResult.setText("XML valid by document type");
        } finally {
            _shlXmlOperations.setCursor(defaultCursor);
        }
    }

    /**
     * Apply XSLT.
     *
     * @param viewInBrowser
     *          true to view in browser, otherwise false.
     */
    private void applyXslt(Boolean viewInBrowser) {
        long startTime = System.currentTimeMillis();
        lblStatus.setText("Ready");

        Cursor defaultCursor = _shlXmlOperations.getCursor();
        _shlXmlOperations.setCursor(_cursorWait);

        try {
            if (!autoSave(FileType.XSLT))
                return;

            Xml xml = new Xml();
            if (!xml.load(textXml.getText())) {
                printError(FileType.XML, xml.getLastError());
                lblStatus.setText(printErrorTime());

                return;
            }

            String result;
            if (_xsltFile.length() > 0 && !_xsltFileChanged)
                result = xml.applyXsltFile(_xsltFile);
            else
                result = xml.applyXslt(textXslt.getText());

            if (result != null) {
                lblStatus.setText(printTimeDifference("XSLT", startTime));

                textResult.setForeground(COLOR_BLACK);

                textResult.setText(result);
            } else {
                printError(FileType.XSLT, xml.getLastError());
                lblStatus.setText(printErrorTime());

                return;
            }
        } finally {
            _shlXmlOperations.setCursor(defaultCursor);
        }

        if (viewInBrowser)
            viewResultInExternalBrowser();
    }

    /**
     * Apply XQuery.
     *
     * @param viewInBrowser
     *          true to see the result in external browser, otherwise false.
     */
    private void applyXQuery(Boolean viewInBrowser) {
        long startTime = System.currentTimeMillis();
        lblStatus.setText("Ready");

        Cursor defaultCursor = _shlXmlOperations.getCursor();
        _shlXmlOperations.setCursor(_cursorWait);

        try {
            if (!autoSave(FileType.XQUERY))
                return;

            Xml xml = null;
            String xmlText = textXml.getText().trim();

            xml = new Xml();

            if (xmlText.length() != 0)
                if (!xml.load(xmlText)) {
                    printError(FileType.XML, xml.getLastError());
                    lblStatus.setText(printErrorTime());

                    return;
                }

            Property<String> errorMessage = new Property<String>();

            if (_xmlFile.length() > 0 && !_xmlFileChanged) {
                xml.setFilePath(_xmlFile);
                if (!xml.applyXQuery(textXquery.getText(), true, errorMessage)) {
                    printError(FileType.XQUERY, errorMessage.get());
                    lblStatus.setText(printErrorTime());

                    return;
                }
            } else if (!xml.applyXQuery(textXquery.getText(), false, errorMessage)) {
                printError(FileType.XQUERY, errorMessage.get());
                lblStatus.setText(printErrorTime());

                return;
            }

            StringBuilder sbText = new StringBuilder();
            StringBuilder sbHtml = new StringBuilder();

            if (xml.hasXQueryResults()) {
                int i = 1;
                String firstItemText = "";

                Property<String> itemTextProp = new Property<String>();

                for (; xml.getNextXQueryResultItem(itemTextProp, errorMessage); i++) {
                    String itemText = itemTextProp.get();

                    if (!itemText.endsWith("\n"))
                        itemText += "\n";

                    switch (i) {
                    case 1:
                        firstItemText = itemText;
                        break;

                    case 2:
                        sbText.append("\n-1-\n\n");
                        sbText.append(firstItemText);
                        sbHtml.append(firstItemText);

                    default:
                        sbText.append(String.format("\n-%d-\n\n", i));
                        sbText.append(itemText);
                        sbHtml.append(itemText);
                        break;
                    }
                }

                if (!errorMessage.isNull()) {
                    printError(FileType.XQUERY, errorMessage.get());
                    lblStatus.setText(printErrorTime());

                    return;
                }

                if (i == 1)
                    sbText.append("Empty selection");
                else {
                    if (i == 2) {
                        sbText.append(firstItemText);
                        sbHtml.append(firstItemText);
                    }

                    sbText.insert(0,
                            String.format("%d node%s selected\n%s", i - 1, i == 2 ? "" : "s", i == 2 ? "\n" : ""));
                }
            } else
                sbText.append("Empty selection");

            lblStatus.setText(printTimeDifference("XQuery", startTime));

            textResult.setForeground(COLOR_BLACK);

            textResult.setText(sbText.toString());
        } finally {
            _shlXmlOperations.setCursor(defaultCursor);
        }

        if (viewInBrowser)
            viewResultInExternalBrowser();
    }

    /**
     * Handle key event.
     *
     * @param textboxType
     *          Type of the textbox the event happened into, or null.
     * @param keyCode
     *          Key code.
     * @param mask
     *          Key event mask.
     */
    private void onKeyEvent(FileType textboxType, int keyCode, int mask) {
        switch (keyCode) {
        case SWT.F1:
            Program.launch(Files.CURRENT_DIRECTORY + "help.html");
            break;

        case SWT.F5:
            switch (_lastFocusedOperationType) {
            case XPATH:
                applyXPath();
                break;

            case XSD:
                verifyByXsd();
                break;

            case DTD:
                verifyByDtd();
                break;

            case XSLT:
                applyXslt((mask & SWT.CONTROL) != 0);
                break;

            case XQUERY:
                applyXQuery((mask & SWT.CONTROL) != 0);
                break;

            default:
                break;
            }
            break;

        case SWT.F6:
            switch (_lastFocusedOperationType) {
            case XSLT:
            case XQUERY:
                viewResultInExternalBrowser();
                break;

            default:
                break;
            }
            break;

        case SWT.F4:
            printNamespaces();
            break;

        case SWT.F3:
            if (_lastSelectedForClipboard.getSelectionText().length() > 0)
                textSearch.setText(_lastSelectedForClipboard.getSelectionText());

            search((mask & SWT.CONTROL) != 0);
            break;

        case 'f':
            if ((mask & SWT.CONTROL) != 0) {
                if (_lastSelectedForClipboard.getSelectionText().length() > 0)
                    textSearch.setText(_lastSelectedForClipboard.getSelectionText());

                textSearch.setFocus();
            }
            break;

        case 'n':
            if ((mask & SWT.CONTROL) != 0)
                fileNew(_lastFocusedFileType);
            break;

        case 'o':
            if ((mask & SWT.CONTROL) != 0)
                fileOpen(_lastFocusedFileType);
            break;

        case 's':
            if ((mask & SWT.CONTROL) != 0)
                fileSave(_lastFocusedFileType);
            break;

        case 'a':
            if ((mask & SWT.CONTROL) != 0) {
                _lastSelectedForClipboard.selectAll();
                setClipboardAndUndoEnable();
            }
            break;

        case '0':
            if ((mask & SWT.CONTROL) != 0)
                textXml.setFocus();
            break;

        case '1':
            if ((mask & SWT.CONTROL) != 0) {
                tabsOthers.setSelection(0);
                textXpath.setFocus();
            }
            break;

        case '2':
            if ((mask & SWT.CONTROL) != 0) {
                tabsOthers.setSelection(1);
                textXslt.setFocus();
            }
            break;

        case '3':
            if ((mask & SWT.CONTROL) != 0) {
                tabsOthers.setSelection(2);
                textXquery.setFocus();
            }
            break;

        case '4':
            if ((mask & SWT.CONTROL) != 0) {
                tabsOthers.setSelection(3);
                textXsd.setFocus();
            }
            break;

        case '5':
            if ((mask & SWT.CONTROL) != 0) {
                tabsOthers.setSelection(4);
                textDtd.setFocus();
            }
            break;

        case 'r':
            if ((mask & SWT.CONTROL) != 0)
                textResult.setFocus();
            break;

        case SWT.SPACE:
            if (textboxType != null && (mask & SWT.CONTROL) != 0)
                switch (textboxType) {
                case XML:
                    doXmlSuggestions(textboxType, textXml);
                    break;

                case XPATH:
                    doXmlSuggestions(textboxType, textXpath);
                    break;

                case XSLT:
                    doXmlSuggestions(textboxType, textXslt);
                    break;

                case XQUERY:
                    doXmlSuggestions(textboxType, textXquery);
                    break;

                case XSD:
                    doXmlSuggestions(textboxType, textXsd);
                    break;

                case DTD:
                    // no action yet

                default:
                    break;
                }

            break;
        }
    }

    /**
     * XML suggestions while editing a textbox.
     *
     * @param textboxType
     *          Type of the textbox the suggestions have been asked from.
     * @param textbox
     *          Textbox the suggestions have been asked from.
     */
    private void doXmlSuggestions(FileType textboxType, final EnhancedStyledText textbox) {
        Cursor defaultCursor = _shlXmlOperations.getCursor();
        _shlXmlOperations.setCursor(_cursorWait);

        try {
            String text = textbox.getText();
            final Point selection = textbox.getSelection();

            if (selection.x > 0 && text.charAt(selection.x - 1) == '>') {
                int k = -1;
                for (int i = selection.x - 2; i >= 0; i--)
                    if (text.charAt(i) == '<') {
                        if (i + 1 < text.length())
                            if (text.charAt(i + 1) == '/') // no suggestion for end tags
                                break;

                        k = i;
                        break;
                    }

                if (k != -1)
                    SwtGeneral.insertIntoTextboxAndMoveCaretAtFront(textbox,
                            String.format("</%s>", text.substring(k + 1, selection.x - 1).split(" ")[0]));
            } else if (textboxType != FileType.XML) {
                if (textXml.getText().trim().length() == 0)
                    return;

                int k = -1;
                for (int i = selection.x - 1; i >= 0; i--) {
                    char c = text.charAt(i);
                    if (!Character.isLetter(c) && !Character.isDigit(c) && "-_.".indexOf(c) == -1) {
                        k = i;
                        break;
                    }
                }
                final int prefixBegin = ++k;

                Xml doc = new Xml();
                if (!doc.load(textXml.getText())) {
                    printError(FileType.XML, doc.getLastError());
                    lblStatus.setText(printErrorTime());

                    return;
                }

                ArrayList<String> allEntities = doc.getAllAttributes(text.substring(k, selection.x));

                Boolean getTags = true;

                if (prefixBegin > 0)
                    if (text.charAt(prefixBegin - 1) == '@')
                        getTags = false;

                if (getTags) {
                    ArrayList<String> allTags;

                    switch (textboxType) {
                    case XPATH:
                        allTags = doc.getAllTagsWithExtra(text.substring(k, selection.x), _xpathTags, "xpath:");
                        break;

                    case XSLT:
                        allTags = doc.getAllTagsWithExtra(text.substring(k, selection.x), _xsltTags, "xsl:");
                        break;

                    case XQUERY:
                        allTags = doc.getAllTagsWithExtra(text.substring(k, selection.x), _xqueryTags, "xq:");
                        break;

                    default:
                        allTags = doc.getAllTags(text.substring(k, selection.x), null, null);
                        break;
                    }

                    allTags.addAll(allEntities);
                    allEntities = allTags;
                }

                int allCount = allEntities.size();

                if (allCount == 0)
                    return;

                if (allCount == 1)
                    insertXmlEntityAsSuggested(allEntities.get(0), textbox, selection, prefixBegin);
                else {
                    Menu menu = new Menu(textbox);

                    for (int i = 0; i < allCount; i++) {
                        final MenuItem mi = new MenuItem(menu, SWT.NONE);
                        mi.setText(allEntities.get(i));

                        mi.addSelectionListener(new SelectionListener() {
                            @Override
                            public void widgetSelected(SelectionEvent arg0) {
                                insertXmlEntityAsSuggested(mi.getText(), textbox, selection, prefixBegin);
                            }

                            @Override
                            public void widgetDefaultSelected(SelectionEvent arg0) {
                            }
                        });
                    }

                    menu.setLocation(
                            SwtGeneral.translate(textbox.toDisplay(textbox.getCaret().getLocation()), 3, 2));
                    menu.setVisible(true);
                }
            }
        } finally {
            setFocus(_lastFocusedFileType);
            _shlXmlOperations.setCursor(defaultCursor);
        }
    }

    /**
     * Insert suggested entity at the current selection position in a textbox.
     *
     * @param entityName
     *          Entity name.
     * @param textbox
     *          Textbox to insert into.
     * @param selection
     *          Selection beginning position.
     * @param prefixBegin
     *          Beginning of the detected prefix.
     */
    private static void insertXmlEntityAsSuggested(String entityName, final EnhancedStyledText textbox,
            final Point selection, final int prefixBegin) {
        if (entityName.charAt(0) == '@')
            entityName = entityName.substring(1);
        else {
            String textParts[] = entityName.split(":", 2);
            entityName = textParts.length > 1 ? textParts[1] : textParts[0];
        }

        SwtGeneral.insertIntoTextboxAndMoveCaret(textbox, entityName.substring(selection.x - prefixBegin));
    }

    /**
     * Print namespaces.
     */
    private void printNamespaces() {
        Cursor defaultCursor = _shlXmlOperations.getCursor();
        _shlXmlOperations.setCursor(_cursorWait);

        try {
            textResult.setForeground(COLOR_BLACK);
            textResult.setText("");

            Xml xml = new Xml();
            if (!xml.load(textXml.getText())) {
                printError(FileType.XML, xml.getLastError());
                lblStatus.setText(printErrorTime());

                return;
            }

            ArrayList<String[]> namespaces = xml.getNamespaces();
            if (namespaces.size() == 0)
                textResult.setText("No namespace found");
            else {
                StringBuilder sb = new StringBuilder();
                for (String[] pair : namespaces)
                    sb.append(String.format("%s = %s\n\n", pair[0], pair[1]));

                textResult.setText(sb.toString().substring(0, sb.length() - 2));
            }
        } finally {
            setFocus(_lastFocusedFileType);
            _shlXmlOperations.setCursor(defaultCursor);
        }
    }

    /**
     * Search text in the current textbox.
     *
     * @param caseSensitive
     *          true if the search is case-sensitive, otherwise false.
     */
    private void search(Boolean caseSensitive) {
        String text = caseSensitive ? textSearch.getText() : textSearch.getText().toLowerCase();

        if (text.length() == 0) {
            textSearch.setFocus();
            return;
        }

        String controlText = caseSensitive ? _lastSelectedForClipboard.getText()
                : _lastSelectedForClipboard.getText().toLowerCase();

        int pos = controlText.indexOf(text, _lastSelectedForClipboard.getSelection().y);
        if (pos == -1) {
            _lastSelectedForClipboard.setSelection(0);
            textSearch.setSelection(0, text.length());
            textSearch.setFocus();
        } else {
            textSearch.setSelection(text.length());
            _lastSelectedForClipboard.setSelection(pos, pos + text.length());
        }
    }

    /**
     * View result content in external browser.
     */
    private void viewResultInExternalBrowser() {
        if (_defaultBrowserProgram == null)
            return;

        try {
            Files.writeTextFile(textResult.getText(), _resultFile);
            _defaultBrowserProgram.execute(_resultFile);
        } catch (IOException ex) {
            SwtGeneral.messageBoxError(
                    String.format("Unable to write the result temporary file (%s)", ex.getMessage()),
                    "XML Operations");
            _log.error(Debug.printStackTraceForLogging(ex));
        }
    }

    /**
     * Set focus.
     *
     * @param fileType
     *          Type of the textbox to set focus on.
     */
    public void setFocus(FileType fileType) {
        EnhancedStyledText text = null;

        switch (fileType) {
        case XML:
            text = textXml;
            break;

        case XPATH:
            text = textXpath;
            break;

        case XSD:
            text = textXsd;
            break;

        case DTD:
            text = textDtd;
            break;

        case XSLT:
            text = textXslt;
            break;

        case XQUERY:
            text = textXquery;
            break;

        default:
            text = textXml;
            break;
        }

        text.setEnabled(false);
        text.setEnabled(true);
        text.setFocus();
    }

    /**
     * Set enabled status of the clipboard and undo-redo buttons.
     */
    public void setClipboardAndUndoEnable() {
        _display.asyncExec(new Runnable() {
            @Override
            public void run() {
                Boolean isEditable = _lastSelectedForClipboard.getEditable();
                Boolean hasSelection = _lastSelectedForClipboard.getSelectionText().length() != 0;

                btnPaste.setEnabled(isEditable);
                btnCopy.setEnabled(hasSelection);
                btnCut.setEnabled(isEditable && hasSelection);

                if (_lastSelectedForClipboardUndoRedo != null) {
                    btnUndo.setEnabled(isEditable && _lastSelectedForClipboardUndoRedo.hasUndo());
                    btnRedo.setEnabled(isEditable && _lastSelectedForClipboardUndoRedo.hasRedo());

                    switch (_lastFocusedFileType) {
                    case DTD:
                        btnCopyFolder.setEnabled(_dtdFile.length() != 0);
                        btnCopyFolder.setToolTipText("Copy DTD folder path");
                        break;

                    case XML:
                        btnCopyFolder.setEnabled(_xmlFile.length() != 0);
                        btnCopyFolder.setToolTipText("Copy XML folder path");
                        break;

                    case XPATH:
                        btnCopyFolder.setEnabled(_xpathFile.length() != 0);
                        btnCopyFolder.setToolTipText("Copy XPath folder path");
                        break;

                    case XSD:
                        btnCopyFolder.setEnabled(_xsdFile.length() != 0);
                        btnCopyFolder.setToolTipText("Copy XSD folder path");
                        break;

                    case XSLT:
                        btnCopyFolder.setEnabled(_xsltFile.length() != 0);
                        btnCopyFolder.setToolTipText("Copy XSLT folder path");
                        break;

                    case XQUERY:
                        btnCopyFolder.setEnabled(_xqueryFile.length() != 0);
                        btnCopyFolder.setToolTipText("Copy XQuery folder path");
                        break;
                    }
                } else {
                    btnUndo.setEnabled(false);
                    btnRedo.setEnabled(false);
                }
            }
        });
    }

    /**
     * Set text font.
     *
     * @param fontName
     *          Font name.
     * @param fontSize
     *          Font size.
     * @param fontStyle
     *          Font style.
     */
    private void setFont(String fontName, int fontSize, int fontStyle) {
        if (fontName == null)
            return;

        Font font = new Font(_display, fontName, fontSize, fontStyle);

        textXml.setFont(font);
        textXpath.setFont(font);
        textXslt.setFont(font);
        textXquery.setFont(font);
        textXsd.setFont(font);
        textDtd.setFont(font);
        textResult.setFont(font);
    }

    /**
     * Print last operation duration.
     *
     * @param operation
     *          Operation to print information relative to.
     * @param startTime
     *          Operation start moment.
     *
     * @return Friendly text of the operation duration.
     */
    private static String printTimeDifference(String operation, long startTime) {
        return String.format("%s duration: %s", operation, TimeMeasure.printTimeDifference(startTime));
    }

    /**
     * Print error time.
     *
     * @return Printed error time.
     */
    private static String printErrorTime() {
        return String.format("Error time: %tT", System.currentTimeMillis());
    }

    /**
     * Split sash form to right or down on double-click.
     *
     * @param sashForm
     *          SashForm to be split.
     */
    private static void splitToRightOrDownOnDoubleClick(SashForm sashForm) {
        int[] weights = sashForm.getWeights();

        if (weights[0] < weights[1])
            sashForm.setWeights(new int[] { 1, 1 });
        else
            sashForm.setWeights(new int[] { 10, 1 });
    }

    /**
     * Split sash form to left or up on double-click.
     *
     * @param sashForm
     *          SashForm to be split.
     */
    private static void splitToLeftOrUpOnDoubleClick(SashForm sashForm) {
        int[] weights = sashForm.getWeights();

        if (weights[0] > weights[1])
            sashForm.setWeights(new int[] { 1, 1 });
        else
            sashForm.setWeights(new int[] { 1, 10 });
    }
}