org.docx4all.ui.main.WordMLEditor.java Source code

Java tutorial

Introduction

Here is the source code for org.docx4all.ui.main.WordMLEditor.java

Source

/*
 *  Copyright 2007, Plutext Pty Ltd.
 *   
 *  This file is part of Docx4all.
    
Docx4all is free software: you can redistribute it and/or modify
it under the terms of version 3 of the GNU General Public License 
as published by the Free Software Foundation.
    
Docx4all 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 Docx4all.  If not, see <http://www.gnu.org/licenses/>.
    
 */

package org.docx4all.ui.main;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Rectangle;
import java.awt.datatransfer.Clipboard;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.MouseMotionListener;
import java.beans.PropertyVetoException;
import java.io.File;
import java.util.ArrayList;
import java.util.EventObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.Box;
import javax.swing.JApplet;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JEditorPane;
import javax.swing.JInternalFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.InternalFrameAdapter;
import javax.swing.event.InternalFrameEvent;
import javax.swing.plaf.basic.BasicInternalFrameUI;
import javax.swing.text.AbstractDocument;
import javax.swing.text.Document;
import javax.swing.text.EditorKit;
import javax.swing.text.MutableAttributeSet;
import javax.swing.text.PlainDocument;
import javax.swing.text.SimpleAttributeSet;
import javax.swing.text.StyleConstants;

import net.sf.vfsjfilechooser.utils.VFSURIParser;
import net.sf.vfsjfilechooser.utils.VFSUtils;

import org.apache.commons.vfs.FileObject;
import org.apache.commons.vfs.FileSystemException;
import org.apache.commons.vfs.provider.webdav.WebdavFileObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.bounce.text.xml.XMLDocument;
import org.bounce.text.xml.XMLEditorKit;
import org.bounce.text.xml.XMLStyleConstants;
import org.docx4all.datatransfer.TransferHandler;
import org.docx4all.datatransfer.WordMLTransferable;
import org.docx4all.script.FxScriptUIHelper;
import org.docx4all.swing.WordMLTextPane;
import org.docx4all.swing.text.DocumentElement;
import org.docx4all.swing.text.FontManager;
import org.docx4all.swing.text.WordMLDocument;
import org.docx4all.swing.text.WordMLDocumentFilter;
import org.docx4all.swing.text.WordMLEditorKit;
import org.docx4all.ui.menu.ContentControlMenu;
import org.docx4all.ui.menu.EditMenu;
import org.docx4all.ui.menu.FileMenu;
import org.docx4all.ui.menu.FormatMenu;
import org.docx4all.ui.menu.HelpMenu;
import org.docx4all.ui.menu.HyperlinkMenu;
import org.docx4all.ui.menu.PlutextMenu;
import org.docx4all.ui.menu.ReviewMenu;
import org.docx4all.ui.menu.ViewMenu;
import org.docx4all.ui.menu.WindowMenu;
import org.docx4all.util.DocUtil;
import org.docx4all.util.SwingUtil;
import org.docx4all.xml.BodyML;
import org.docx4all.xml.DocumentML;
import org.docx4all.xml.ElementML;
import org.docx4all.xml.SdtBlockML;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.jdesktop.application.ResourceMap;
import org.jdesktop.application.SingleFrameApplication;
import org.plutext.client.Mediator;

/**
 *   @author Jojada Tirtowidjojo - 13/11/2007
 */
public class WordMLEditor extends SingleFrameApplication {
    private static Logger log = LoggerFactory.getLogger(WordMLEditor.class);

    private ViewManager _viewManager;
    private JDesktopPane _desktop;
    private Map<String, JInternalFrame> _iframeMap;
    private InternalFrameListener _internalFrameListener;
    private MouseMotionListener _titleBarMouseListener;
    private ToolBarStates _toolbarStates;
    private JApplet _applet;

    public static String TRANSFORMER_FACTORY_DEFAULT;

    public static void main(String[] args) {

        //      // Whenever we flush Preferences on Linux, we need *not* to use
        //      // our Xalan jar (which we need 
        //      javax.xml.transform.TransformerFactory tfactory = javax.xml.transform.TransformerFactory.newInstance();         
        //      TRANSFORMER_FACTORY_DEFAULT = tfactory.getClass().getName();
        //      log.debug("Set TRANSFORMER_FACTORY_DEFAULT to " + TRANSFORMER_FACTORY_DEFAULT);

        // If we launch via JNLP with Java 6 JAXB (as opposed to the JAXB RI),
        // we trip up with access denied on RuntimePermission accessDeclaredMembers
        // (is this because NamespacePrefixMapperSunInternal extends com.sun.xml.internal.bind.marshaller.NamespacePrefixMapper?).
        // This is despite the JNLP file seeking all-permissions.
        // Workaround:
        if (System.getSecurityManager() == null) {
            System.out.println("Initial SecurityManager: null");
        } else {
            System.out.println("Initial SecurityManager: " + System.getSecurityManager().getClass().getName());
            System.setSecurityManager(null);
        }

        launch(WordMLEditor.class, args);
    }

    @Override
    protected void startup() {
        log.info("preStartup()...");
        preStartup(null);

        log.info("setting up createMenuBar");
        getMainFrame().setJMenuBar(createMenuBar());

        log.info("setting up createMainPanel");
        show(createMainPanel());
        log.info("startup() complete.");

    }

    void preStartup(JApplet applet) {
        _applet = applet;

        _viewManager = new ViewManager();

        _iframeMap = new HashMap<String, JInternalFrame>();

        log.info("setting up InternalFrameListener");
        _internalFrameListener = new InternalFrameListener();

        log.info("setting up TitleBarMouseListener");
        _titleBarMouseListener = new TitleBarMouseListener();

        log.info("setting up ToolBarStates");
        _toolbarStates = new ToolBarStates();
        Clipboard clipboard = getContext().getClipboard();
        clipboard.addFlavorListener(_toolbarStates);
        //As a FlavorListener, _toolbarStates will ONLY be notified
        //when there is a DataFlavor change in Clipboard. 
        //Therefore, make sure that toolbarStates' _isPasteEnable property 
        //is initialised correctly.
        boolean available = clipboard.isDataFlavorAvailable(WordMLTransferable.STRING_FLAVOR)
                || clipboard.isDataFlavorAvailable(WordMLTransferable.WORDML_FRAGMENT_FLAVOR);
        _toolbarStates.setPasteEnabled(available);

        log.info("setting up VFSJFileChooser");
        initVFSJFileChooser();

        log.info("setting up WmlExitListener");
        addExitListener(new WmlExitListener());
    }

    /**
     * Initialises VFSJFileChooser default bookmark entry which has to be 
     * a webdav folder pointed by Constants.VFSJFILECHOOSER_DEFAULT_FOLDER_URI 
     * property.
     */
    private void initVFSJFileChooser() {
        ResourceMap rm = getContext().getResourceMap(WordMLEditor.class);
        String uri = rm.getString(Constants.VFSJFILECHOOSER_DEFAULT_FOLDER_URI);
        System.out.println(Constants.VFSJFILECHOOSER_DEFAULT_FOLDER_URI + ":" + uri);
        if (uri != null && uri.length() > 0) {
            uri = uri.trim();
            try {
                FileObject fo = VFSUtils.getFileSystemManager().resolveFile(uri);

                if (fo instanceof WebdavFileObject) {
                    String name = rm.getString(Constants.VFSJFILECHOOSER_DEFAULT_WEBDAV_FOLDER_BOOKMARK_NAME);
                    if (name == null || name.length() == 0) {
                        name = "Default Webdav Folder";
                    } else {
                        name = name.trim();
                    }

                    VFSURIParser vup = new VFSURIParser(uri, false);
                    org.docx4all.vfs.VFSUtil.addBookmarkEntry(name, vup);
                }

            } catch (FileSystemException exc) {
                exc.printStackTrace();
                StringBuilder sb = new StringBuilder();
                sb.append("Bad ");
                sb.append(Constants.VFSJFILECHOOSER_DEFAULT_FOLDER_URI);
                sb.append(" property.");
                throw new RuntimeException(sb.toString());
            }
        }
    } //initVFSJFileChooser()

    public void closeAllInternalFrames() {

        List<JInternalFrame> list = getAllInternalFrames();

        //Start from current editor's frame
        JInternalFrame currentFrame = getCurrentInternalFrame();
        list.remove(currentFrame);
        list.add(0, currentFrame);

        for (final JInternalFrame iframe : list) {
            final Runnable disposeRunnable = new Runnable() {
                public void run() {
                    iframe.dispose();
                }
            };

            if (getToolbarStates().isDocumentDirty(iframe)) {
                try {
                    iframe.setSelected(true);
                    iframe.setIcon(false);
                } catch (PropertyVetoException exc) {
                    ;//ignore
                }

                int answer = showConfirmClosingInternalFrame(iframe, "internalframe.close");
                if (answer == JOptionPane.CANCEL_OPTION) {
                    break;
                }
            }

            SwingUtilities.invokeLater(disposeRunnable);
        }
    }

    public void closeInternalFrame(JInternalFrame iframe) {
        boolean canClose = true;

        if (getToolbarStates().isDocumentDirty(iframe)) {
            try {
                iframe.setSelected(true);
                iframe.setIcon(false);
            } catch (PropertyVetoException exc) {
                ;//ignore
            }

            int answer = showConfirmClosingInternalFrame(iframe, "internalframe.close");
            canClose = (answer != JOptionPane.CANCEL_OPTION);
        }

        if (canClose) {
            WordMLTextPane editor = SwingUtil.getWordMLTextPane(iframe);
            if (editor != null) {
                editor.removeCaretListener(getToolbarStates());
                editor.removeFocusListener(getToolbarStates());
                editor.setTransferHandler(null);

                editor.getDocument().removeDocumentListener(getToolbarStates());

                WordMLEditorKit editorKit = (WordMLEditorKit) editor.getEditorKit();
                editorKit.removeInputAttributeListener(getToolbarStates());
                editor.getWordMLEditorKit().deinstall(editor);
            }
            iframe.dispose();
        }
    }

    public void createInternalFrame(FileObject f) {
        if (f == null) {
            return;
        }

        log.info(VFSUtils.getFriendlyName(f.getName().getURI()));

        JInternalFrame iframe = _iframeMap.get(f.getName().getURI());
        if (iframe != null) {
            iframe.setVisible(true);

        } else {
            iframe = new JInternalFrame(f.getName().getBaseName(), true, true, true, true);
            iframe.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
            iframe.addInternalFrameListener(_internalFrameListener);
            iframe.addInternalFrameListener(_toolbarStates);
            iframe.addPropertyChangeListener(WindowMenu.getInstance());

            if (iframe.getUI() instanceof BasicInternalFrameUI) {
                BasicInternalFrameUI ui = (BasicInternalFrameUI) iframe.getUI();
                javax.swing.JComponent northPane = ui.getNorthPane();
                if (northPane == null) {
                    // Happens on Mac OSX: Google for "osx java getNorthPane"
                    // Fix is from it.businesslogic.ireport.gui.JMDIFrame
                    javax.swing.plaf.basic.BasicInternalFrameUI aUI = new javax.swing.plaf.basic.BasicInternalFrameUI(
                            iframe);
                    iframe.setUI(aUI);

                    // Try again
                    ui = (BasicInternalFrameUI) iframe.getUI();
                    northPane = ((javax.swing.plaf.basic.BasicInternalFrameUI) ui).getNorthPane();
                }
                northPane.addMouseMotionListener(_titleBarMouseListener);
            }

            JEditorPane editorView = createEditorView(f);
            JPanel panel = FxScriptUIHelper.getInstance().createEditorPanel(editorView);

            iframe.getContentPane().add(panel);
            iframe.pack();
            _desktop.add(iframe);

            editorView.requestFocusInWindow();
            editorView.select(0, 0);

            String filePath = f.getName().getURI();
            iframe.putClientProperty(WordMLDocument.FILE_PATH_PROPERTY, filePath);
            _iframeMap.put(filePath, iframe);

            iframe.show();
        }

        try {
            iframe.setSelected(true);
            iframe.setIcon(false);
            iframe.setMaximum(true);
        } catch (PropertyVetoException exc) {
            // do nothing
        }
    }

    public void tileLayout(String filePath1, String filePath2) {
        if (_iframeMap.containsKey(filePath1) && _iframeMap.containsKey(filePath2)) {
            JInternalFrame[] frames = { _iframeMap.get(filePath1), _iframeMap.get(filePath2) };
            WindowMenu.tileLayout(frames);
        }
    }

    public void updateInternalFrame(FileObject oldFile, FileObject newFile) {
        if (oldFile.equals(newFile)) {
            return;
        }

        String fileUri = oldFile.getName().getURI();
        JInternalFrame iframe = _iframeMap.remove(fileUri);
        if (iframe != null) {
            fileUri = newFile.getName().getURI();
            iframe.putClientProperty(WordMLDocument.FILE_PATH_PROPERTY, fileUri);
            iframe.setTitle(newFile.getName().getBaseName());
        }
    }

    public String getPlutextWebdavUrlKeyword() {
        ResourceMap rm = getContext().getResourceMap(WordMLEditor.class);
        return rm.getString(Constants.PLUTEXT_WEBDAV_URL_KEYWORD);
    }

    public JDesktopPane getDesktopPane() {
        return _desktop;
    }

    public ToolBarStates getToolbarStates() {
        return _toolbarStates;
    }

    public JInternalFrame getCurrentInternalFrame() {
        return _toolbarStates.getCurrentInternalFrame();
    }

    public JEditorPane getCurrentEditor() {
        return _toolbarStates.getCurrentEditor();
    }

    public JEditorPane getView(String viewTabTitle) {
        if (getCurrentInternalFrame() != null) {
            return getCurrentViewManager().getView(viewTabTitle);
        }
        return null;
    }

    public List<JInternalFrame> getAllInternalFrames() {
        return new ArrayList<JInternalFrame>(_iframeMap.values());
    }

    public String getEditorViewTabTitle() {
        ResourceMap rm = getContext().getResourceMap(WordMLEditor.class);
        return rm.getString(Constants.EDITOR_VIEW_TAB_TITLE);
    }

    public String getSourceViewTabTitle() {
        ResourceMap rm = getContext().getResourceMap(WordMLEditor.class);
        return rm.getString(Constants.SOURCE_VIEW_TAB_TITLE);
    }

    public String getContentControlHistoryViewTabTitle() {
        ResourceMap rm = getContext().getResourceMap(WordMLEditor.class);
        return rm.getString(Constants.CONTENT_CONTROL_HISTORY_VIEW_TAB_TITLE);
    }

    public String getRecentChangesViewTabTitle() {
        ResourceMap rm = getContext().getResourceMap(WordMLEditor.class);
        return rm.getString(Constants.RECENT_CHANGES_VIEW_TAB_TITLE);
    }

    public String getUntitledFileName() {
        ResourceMap rm = getContext().getResourceMap(WordMLEditor.class);
        String filename = rm.getString(Constants.UNTITLED_FILE_NAME);
        if (filename == null || filename.length() == 0) {
            filename = "Untitled";
        }
        return filename;
    }

    public Frame getWindowFrame() {
        if (_applet == null) {
            return getMainFrame();
        }

        Component c = _applet;
        while (c != null && !(c instanceof Frame)) {
            c = c.getParent();
        }

        return (Frame) c;
    }

    public JMenuBar getJMenuBar() {
        if (_applet == null) {
            return getMainFrame().getJMenuBar();
        }
        return _applet.getJMenuBar();
    }

    public int showConfirmDialog(String title, String message, int optionType, int messageType) {
        return JOptionPane.showConfirmDialog(getWindowFrame(), message, title, optionType, messageType);
    }

    public int showConfirmDialog(String title, String message, int optionType, int messageType, Object[] options,
            Object initialValue) {

        return JOptionPane.showOptionDialog(getWindowFrame(), message, title, optionType, messageType, null,
                options, initialValue);
    }

    public void showMessageDialog(String title, String message, int optionType) {
        JOptionPane.showMessageDialog(getWindowFrame(), message, title, optionType);
    }

    public void showViewInTab(final String tabTitle) {
        if (getCurrentInternalFrame() != null) {
            Cursor origCursor = getMainFrame().getCursor();
            getMainFrame().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));

            getCurrentViewManager().showViewTab(tabTitle);

            getMainFrame().setCursor(origCursor);
        }
    }

    public void closeViewTab(String tabTitle) {
        if (getCurrentInternalFrame() != null) {
            getCurrentViewManager().closeViewTab(tabTitle);
        }
    }

    private JEditorPane createSourceView(WordMLTextPane editorView) {
        //Create the Source View
        JEditorPane sourceView = new JEditorPane();

        MutableAttributeSet attrs = new SimpleAttributeSet();
        StyleConstants.setFontFamily(attrs, FontManager.getInstance().getSourceViewFontFamilyName());
        StyleConstants.setFontSize(attrs, FontManager.getInstance().getSourceViewFontSize());

        // TODO - only do this if the font is available.
        Font font = new Font("Arial Unicode MS", Font.PLAIN, 12);

        System.out.println(font.getFamily());
        System.out.println(font.getFontName());
        System.out.println(font.getPSName());

        sourceView.setFont(font);
        //sourceView.setFont(FontManager.getInstance().getFontInAction(attrs));

        sourceView.setContentType("text/xml; charset=UTF-16");

        // Instantiate a XMLEditorKit with wrapping enabled.
        XMLEditorKit kit = new XMLEditorKit(true);
        // Set the wrapping style.
        kit.setWrapStyleWord(true);

        sourceView.setEditorKit(kit);

        WordMLDocument editorViewDoc = (WordMLDocument) editorView.getDocument();

        try {
            editorViewDoc.readLock();

            editorView.getWordMLEditorKit().saveCaretText();

            DocumentElement elem = (DocumentElement) editorViewDoc.getDefaultRootElement();
            WordprocessingMLPackage wmlPackage = ((DocumentML) elem.getElementML()).getWordprocessingMLPackage();
            String filePath = (String) editorView.getDocument().getProperty(WordMLDocument.FILE_PATH_PROPERTY);

            //Do not include the last paragraph which is an extra paragraph.
            elem = (DocumentElement) elem.getElement(elem.getElementCount() - 1);
            ElementML paraML = elem.getElementML();
            ElementML bodyML = paraML.getParent();
            paraML.delete();

            Document doc = DocUtil.read(sourceView, wmlPackage);
            doc.putProperty(WordMLDocument.FILE_PATH_PROPERTY, filePath);
            doc.putProperty(WordMLDocument.WML_PACKAGE_PROPERTY, wmlPackage);
            doc.addDocumentListener(getToolbarStates());

            //Below are the properties used by bounce.jar library
            //See http://www.edankert.com/bounce/xmleditorkit.html
            doc.putProperty(PlainDocument.tabSizeAttribute, new Integer(4));
            doc.putProperty(XMLDocument.AUTO_INDENTATION_ATTRIBUTE, Boolean.TRUE);
            doc.putProperty(XMLDocument.TAG_COMPLETION_ATTRIBUTE, Boolean.TRUE);

            //Remember to put 'paraML' as last paragraph
            bodyML.addChild(paraML);

        } finally {
            editorViewDoc.readUnlock();
        }

        kit.setStyle(XMLStyleConstants.ATTRIBUTE_NAME, new Color(255, 0, 0), Font.PLAIN);

        sourceView.addFocusListener(getToolbarStates());
        //sourceView.setDocument(doc);
        sourceView.putClientProperty(Constants.LOCAL_VIEWS_SYNCHRONIZED_FLAG, Boolean.TRUE);

        return sourceView;
    }

    private JEditorPane createContentControlHistoryView(WordMLTextPane editorView) {
        WordMLTextPane theView = new WordMLTextPane();

        editorView.saveCaretText();

        int pos = editorView.getCaretPosition();
        WordMLDocument editorViewDoc = (WordMLDocument) editorView.getDocument();

        Mediator plutextClient = editorView.getWordMLEditorKit().getPlutextClient();

        try {
            editorViewDoc.readLock();

            DocumentElement elem = (DocumentElement) editorViewDoc.getSdtBlockMLElement(pos);
            SdtBlockML sdt = (SdtBlockML) elem.getElementML();
            String sdtId = sdt.getSdtProperties().getPlutextId();

            plutextClient.startSession();
            WordprocessingMLPackage wp = plutextClient.getVersionHistory(sdtId);
            org.docx4j.wml.Document wmlDoc = (org.docx4j.wml.Document) wp.getMainDocumentPart().getJaxbElement();

            WordMLDocument historyDoc = (WordMLDocument) theView.getDocument();
            historyDoc.setDocumentFilter(new WordMLDocumentFilter());
            historyDoc.replaceBodyML(new BodyML(wmlDoc.getBody()));

        } catch (Exception exc) {
            exc.printStackTrace();

        } finally {
            plutextClient.endSession();
            editorViewDoc.readUnlock();
        }

        theView.setEditable(false);
        theView.addFocusListener(getToolbarStates());

        return theView;
    }

    private JEditorPane createRecentChangesView(WordMLTextPane editorView) {
        WordMLTextPane theView = new WordMLTextPane();

        editorView.saveCaretText();

        Mediator plutextClient = editorView.getWordMLEditorKit().getPlutextClient();

        try {
            plutextClient.startSession();
            WordprocessingMLPackage wp = plutextClient.getRecentChangesReport();
            org.docx4j.wml.Document wmlDoc = (org.docx4j.wml.Document) wp.getMainDocumentPart().getJaxbElement();

            WordMLDocument doc = (WordMLDocument) theView.getDocument();
            doc.setDocumentFilter(new WordMLDocumentFilter());
            doc.replaceBodyML(new BodyML(wmlDoc.getBody()));

        } catch (Exception exc) {
            exc.printStackTrace();

        } finally {
            plutextClient.endSession();
        }

        theView.setEditable(false);
        theView.addFocusListener(getToolbarStates());

        return theView;
    }

    private JEditorPane createEditorView(FileObject f) {
        String fileUri = f.getName().getURI();

        WordMLTextPane editorView = new WordMLTextPane();
        editorView.addFocusListener(_toolbarStates);
        editorView.addCaretListener(_toolbarStates);
        editorView.setTransferHandler(new TransferHandler());

        WordMLEditorKit editorKit = (WordMLEditorKit) editorView.getEditorKit();
        editorKit.addInputAttributeListener(_toolbarStates);

        WordMLDocument doc = null;

        try {
            if (f.exists()) {
                doc = editorKit.read(f);
            }
        } catch (Exception exc) {
            exc.printStackTrace();

            ResourceMap rm = getContext().getResourceMap();
            String title = rm.getString(Constants.INIT_EDITOR_VIEW_IO_ERROR_DIALOG_TITLE);
            StringBuffer msg = new StringBuffer();
            msg.append(rm.getString(Constants.INIT_EDITOR_VIEW_IO_ERROR_MESSAGE));
            msg.append(Constants.NEWLINE);
            msg.append(VFSUtils.getFriendlyName(fileUri));
            showMessageDialog(title, msg.toString(), JOptionPane.ERROR_MESSAGE);
            doc = null;
        }

        if (doc == null) {
            doc = (WordMLDocument) editorKit.createDefaultDocument();
        }

        doc.putProperty(WordMLDocument.FILE_PATH_PROPERTY, fileUri);
        doc.addDocumentListener(_toolbarStates);
        doc.setDocumentFilter(new WordMLDocumentFilter());
        editorView.setDocument(doc);
        editorView.putClientProperty(Constants.LOCAL_VIEWS_SYNCHRONIZED_FLAG, Boolean.TRUE);

        if (DocUtil.isSharedDocument(doc)) {
            editorKit.initPlutextClient(editorView);
        }

        return editorView;
    }

    JComponent createMainPanel() {
        _desktop = new JDesktopPane();
        _desktop.setDragMode(JDesktopPane.OUTLINE_DRAG_MODE);
        _desktop.setBackground(Color.LIGHT_GRAY);

        JPanel toolbar = FxScriptUIHelper.getInstance().createToolBar();

        JPanel panel = new JPanel(new BorderLayout());
        panel.add(toolbar, BorderLayout.NORTH);
        panel.add(_desktop, BorderLayout.CENTER);

        panel.setBorder(new EmptyBorder(0, 2, 2, 2)); // top, left, bottom, right
        panel.setPreferredSize(new Dimension(640, 480));

        return panel;
    }

    private int showConfirmClosingInternalFrame(JInternalFrame iframe, String resourceKeyPrefix) {
        int answer = JOptionPane.CANCEL_OPTION;

        String filePath = (String) iframe.getClientProperty(WordMLDocument.FILE_PATH_PROPERTY);

        ResourceMap rm = getContext().getResourceMap();
        String title = rm.getString(resourceKeyPrefix + ".dialog.title") + " "
                + filePath.substring(filePath.lastIndexOf(File.separator) + 1);
        String message = filePath + "\n" + rm.getString(resourceKeyPrefix + ".confirmMessage");
        Object[] options = { rm.getString(resourceKeyPrefix + ".confirm.saveNow"),
                rm.getString(resourceKeyPrefix + ".confirm.dontSave"),
                rm.getString(resourceKeyPrefix + ".confirm.cancel") };
        answer = showConfirmDialog(title, message, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE,
                options, options[0]);
        if (answer == JOptionPane.CANCEL_OPTION) {
            ;
        } else if (answer == JOptionPane.YES_OPTION) {
            boolean success = FileMenu.getInstance().save(iframe, null, FileMenu.SAVE_FILE_ACTION_NAME);
            if (success) {
                getToolbarStates().setDocumentDirty(iframe, false);
                getToolbarStates().setLocalEditsEnabled(iframe, false);
            } else {
                answer = JOptionPane.CANCEL_OPTION;
            }
        } else {
            //getToolbarStates().setDocumentDirty(iframe, false);
        }

        return answer;
    }

    private ViewManager getCurrentViewManager() {
        _viewManager.setOwner(getCurrentInternalFrame());
        return _viewManager;
    }

    JMenuBar createMenuBar() {
        JMenuBar menubar = new JMenuBar();

        JMenu fileMenu = FileMenu.getInstance().createJMenu();
        JMenu editMenu = EditMenu.getInstance().createJMenu();
        JMenu formatMenu = FormatMenu.getInstance().createJMenu();
        JMenu hyperlinkMenu = HyperlinkMenu.getInstance().createJMenu();
        JMenu contentControlMenu = ContentControlMenu.getInstance().createJMenu();
        JMenu reviewMenu = ReviewMenu.getInstance().createJMenu();
        JMenu viewMenu = ViewMenu.getInstance().createJMenu();
        JMenu plutextMenu = PlutextMenu.getInstance().createJMenu();
        JMenu windowMenu = WindowMenu.getInstance().createJMenu();
        JMenu helpMenu = HelpMenu.getInstance().createJMenu();

        menubar.add(fileMenu);
        menubar.add(Box.createRigidArea(new Dimension(12, 0)));
        menubar.add(editMenu);
        menubar.add(Box.createRigidArea(new Dimension(12, 0)));
        menubar.add(formatMenu);
        menubar.add(Box.createRigidArea(new Dimension(12, 0)));
        menubar.add(hyperlinkMenu);
        menubar.add(Box.createRigidArea(new Dimension(12, 0)));
        menubar.add(contentControlMenu);
        menubar.add(Box.createRigidArea(new Dimension(12, 0)));
        menubar.add(reviewMenu);
        menubar.add(Box.createRigidArea(new Dimension(12, 0)));
        menubar.add(viewMenu);
        menubar.add(Box.createRigidArea(new Dimension(12, 0)));
        menubar.add(plutextMenu);
        menubar.add(Box.createRigidArea(new Dimension(12, 0)));
        menubar.add(windowMenu);
        menubar.add(Box.createRigidArea(new Dimension(12, 0)));
        menubar.add(helpMenu);

        return menubar;
    }

    private class TitleBarMouseListener extends MouseMotionAdapter {
        public void mouseMoved(MouseEvent e) {
            JComponent titlePane = (JComponent) e.getSource();

            JInternalFrame frame = (JInternalFrame) titlePane.getParent();
            String tmp = frame.getTitle();
            FontMetrics fm = frame.getFontMetrics(frame.getFont());
            int width = fm.charsWidth(tmp.toCharArray(), 0, tmp.length());

            Rectangle tbounds = titlePane.getBounds();
            tbounds.width = width;

            if (tbounds.contains(e.getX(), e.getY())) {
                if (!tmp.startsWith(getUntitledFileName())) {
                    tmp = (String) frame.getClientProperty(WordMLDocument.FILE_PATH_PROPERTY);
                    //do not display password
                    tmp = VFSUtils.getFriendlyName(tmp);
                }
                titlePane.setToolTipText(tmp);
            } else {
                titlePane.setToolTipText(null);
            }
        }
    }

    private class InternalFrameListener extends InternalFrameAdapter {

        public void internalFrameIconified(InternalFrameEvent e) {
            // Sets JInternalFrame's maximum property to false.
            // 
            // When a user clicks the minimize/maximize button of
            // JInternalFrame, its maximum property value remains 
            // unchanged. This subsequently causes 
            // JInternalFrame.setMaximum() not working.
            JInternalFrame frame = (JInternalFrame) e.getSource();
            try {
                frame.setMaximum(false);
            } catch (PropertyVetoException exc) {
                ;// do nothing
            }
        }

        public void internalFrameDeiconified(InternalFrameEvent e) {
            // Sets JInternalFrame's maximum property to false.
            // 
            // When a user clicks the minimize/maximize button of
            // JInternalFrame, its maximum property value remains 
            // unchanged. This subsequently causes 
            // JInternalFrame.setMaximum() not working.
            JInternalFrame frame = (JInternalFrame) e.getSource();
            try {
                frame.setMaximum(true);
            } catch (PropertyVetoException exc) {
                ;//do nothing
            }
        }

        public void internalFrameOpened(InternalFrameEvent e) {
            JInternalFrame iframe = (JInternalFrame) e.getSource();
            WindowMenu.getInstance().addWindowMenuItem(iframe);
        }

        public void internalFrameClosing(InternalFrameEvent e) {
            JInternalFrame iframe = (JInternalFrame) e.getSource();

            boolean canClose = true;

            if (getToolbarStates().isDocumentDirty(iframe)) {
                int answer = showConfirmClosingInternalFrame(iframe, "internalframe.close");
                canClose = (answer != JOptionPane.CANCEL_OPTION);
            }

            if (canClose) {
                WordMLTextPane editor = SwingUtil.getWordMLTextPane(iframe);
                if (editor != null) {
                    editor.removeCaretListener(getToolbarStates());
                    editor.removeFocusListener(getToolbarStates());
                    editor.setTransferHandler(null);

                    editor.getDocument().removeDocumentListener(getToolbarStates());

                    WordMLEditorKit editorKit = (WordMLEditorKit) editor.getEditorKit();
                    editorKit.removeInputAttributeListener(getToolbarStates());
                    editor.getWordMLEditorKit().deinstall(editor);
                }
                iframe.dispose();
            }
        }

        public void internalFrameClosed(InternalFrameEvent e) {
            JInternalFrame iframe = (JInternalFrame) e.getSource();
            String filePath = (String) iframe.getClientProperty(WordMLDocument.FILE_PATH_PROPERTY);
            _iframeMap.remove(filePath);

            WindowMenu.getInstance().removeWindowMenuItem(iframe);
            _desktop.remove(iframe);
        }

        public void internalFrameActivated(InternalFrameEvent e) {
            JInternalFrame iframe = (JInternalFrame) e.getSource();
            WindowMenu.getInstance().selectWindowMenuItem(iframe);
        }

        public void internalFrameDeactivated(InternalFrameEvent e) {
            JInternalFrame iframe = (JInternalFrame) e.getSource();
            WindowMenu.getInstance().unSelectWindowMenuItem(iframe);
        }

    } //InternalFrameListener inner class

    private class ViewChangeListener implements ChangeListener {
        /*
         * On View tab changes this method checks whether
         * Editor View's content needs to be synchronized with
         * that of Source View's
         */
        public void stateChanged(ChangeEvent event) {
            JTabbedPane pane = (JTabbedPane) event.getSource();
            int editorViewIdx = pane.indexOfTab(getEditorViewTabTitle());
            int sourceViewIdx = pane.indexOfTab(getSourceViewTabTitle());
            int historyViewIdx = pane.indexOfTab(getContentControlHistoryViewTabTitle());
            int recentViewIdx = pane.indexOfTab(getRecentChangesViewTabTitle());

            if (pane.getSelectedIndex() == editorViewIdx && editorViewIdx != -1) {
                changeToEditorView(pane, editorViewIdx, sourceViewIdx);

            } else if (pane.getSelectedIndex() == sourceViewIdx && sourceViewIdx != -1) {
                changeToSourceView(pane, editorViewIdx, sourceViewIdx);

            } else if (pane.getSelectedIndex() == historyViewIdx && historyViewIdx != -1) {
                changeToContentControlHistoryView(pane, editorViewIdx, historyViewIdx);

            } else if (pane.getSelectedIndex() == recentViewIdx && recentViewIdx != -1) {
                changeToRecentChangesView(pane, editorViewIdx, recentViewIdx);
            }
        }

        private void changeToEditorView(JTabbedPane pane, int editorViewIdx, int sourceViewIdx) {

            if (sourceViewIdx == -1) {
                //No Source View
                return;
            }

            final WordMLTextPane editorView = (WordMLTextPane) SwingUtil.getDescendantOfClass(WordMLTextPane.class,
                    (Container) pane.getComponentAt(editorViewIdx), true);
            final JEditorPane sourceView = (JEditorPane) SwingUtil.getDescendantOfClass(JEditorPane.class,
                    (Container) pane.getComponentAt(sourceViewIdx), false);
            Boolean isSynched = (Boolean) sourceView.getClientProperty(Constants.LOCAL_VIEWS_SYNCHRONIZED_FLAG);
            if (!isSynched.booleanValue()) {
                //means that source view has been edited and
                //editorView has to be synchronised with source view.
                synchEditorView(editorView, sourceView);
            }

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    editorView.requestFocusInWindow();
                }
            });
        }

        private void changeToSourceView(JTabbedPane pane, int editorViewIdx, int sourceViewIdx) {
            if (editorViewIdx == -1) {
                //No Editor View.
                //This should not happen.
                throw new IllegalStateException("No Editor View");
            }

            final WordMLTextPane editorView = (WordMLTextPane) SwingUtil.getDescendantOfClass(WordMLTextPane.class,
                    (Container) pane.getComponentAt(editorViewIdx), true);
            final JEditorPane sourceView = (JEditorPane) SwingUtil.getDescendantOfClass(JEditorPane.class,
                    (Container) pane.getComponentAt(sourceViewIdx), false);
            Boolean isSynched = (Boolean) editorView.getClientProperty(Constants.LOCAL_VIEWS_SYNCHRONIZED_FLAG);
            if (!isSynched.booleanValue()) {
                synchSourceView(sourceView, editorView);
            }

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    sourceView.requestFocusInWindow();
                }
            });
        }

        private void changeToContentControlHistoryView(JTabbedPane pane, int editorViewIdx,
                int versionHistoryViewIdx) {
            if (editorViewIdx == -1) {
                //No Editor View.
                //This should not happen.
                throw new IllegalStateException("No Editor View");
            }
            final WordMLTextPane editorView = (WordMLTextPane) SwingUtil.getDescendantOfClass(WordMLTextPane.class,
                    (Container) pane.getComponentAt(editorViewIdx), true);
            final JEditorPane historyView = (JEditorPane) SwingUtil.getDescendantOfClass(JEditorPane.class,
                    (Container) pane.getComponentAt(versionHistoryViewIdx), false);
            synchContentControlHistoryView(historyView, editorView);

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    historyView.requestFocusInWindow();
                }
            });
        }

        private void changeToRecentChangesView(JTabbedPane pane, int editorViewIdx, int recentChangesViewIdx) {
            if (editorViewIdx == -1) {
                //No Editor View.
                //This should not happen.
                throw new IllegalStateException("No Editor View");
            }
            final WordMLTextPane editorView = (WordMLTextPane) SwingUtil.getDescendantOfClass(WordMLTextPane.class,
                    (Container) pane.getComponentAt(editorViewIdx), true);
            final JEditorPane recentView = (JEditorPane) SwingUtil.getDescendantOfClass(JEditorPane.class,
                    (Container) pane.getComponentAt(recentChangesViewIdx), false);
            synchRecentChangesView(recentView, editorView);

            SwingUtilities.invokeLater(new Runnable() {
                public void run() {
                    recentView.requestFocusInWindow();
                }
            });
        }

        private void synchEditorView(WordMLTextPane editorView, JEditorPane sourceView) {
            int caretPos = editorView.getCaretPosition();

            WordMLDocument editorViewDoc = (WordMLDocument) editorView.getDocument();
            EditorKit kit = sourceView.getEditorKit();
            AbstractDocument sourceViewDoc = (AbstractDocument) sourceView.getDocument();
            try {
                sourceViewDoc.readLock();
                editorViewDoc.lockWrite();

                //Firstly, save source view content into WordprocessingMLPackage
                WordprocessingMLPackage wmlPackage = (WordprocessingMLPackage) sourceViewDoc
                        .getProperty(WordMLDocument.WML_PACKAGE_PROPERTY);
                DocUtil.write(kit, sourceViewDoc, wmlPackage);

                //Now editorView's content has become invalid because
                //its WordprocessingMLPackage's main document part was
                //updated by DocUtil.write() above.
                //Need to refresh editor view.
                org.docx4j.wml.Document wmlDoc = (org.docx4j.wml.Document) wmlPackage.getMainDocumentPart()
                        .getJaxbElement();
                editorViewDoc.replaceBodyML(new BodyML(wmlDoc.getBody()));

                log.debug("stateChanged(): NEW Document Structure...");
                DocUtil.displayStructure(editorViewDoc);

                //reset LOCAL_VIEWS_SYNCHRONIZED_FLAG of source view
                sourceView.putClientProperty(Constants.LOCAL_VIEWS_SYNCHRONIZED_FLAG, Boolean.TRUE);

            } finally {
                editorViewDoc.unlockWrite();
                sourceViewDoc.readUnlock();

                editorView.validate();
                editorView.repaint();
                editorView.setCaretPosition(caretPos);
            }
        }

        private void synchSourceView(JEditorPane sourceView, WordMLTextPane editorView) {
            int caretPos = sourceView.getCaretPosition();

            JEditorPane newView = createSourceView(editorView);
            Document newDoc = newView.getDocument();

            sourceView.setDocument(newDoc);
            sourceView.validate();
            sourceView.repaint();
            sourceView.setCaretPosition(caretPos);

            //reset LOCAL_VIEWS_SYNCHRONIZED_FLAG of editor view
            editorView.putClientProperty(Constants.LOCAL_VIEWS_SYNCHRONIZED_FLAG, Boolean.TRUE);
        }

        private void synchContentControlHistoryView(JEditorPane historyView, WordMLTextPane editorView) {
            int caretPos = historyView.getCaretPosition();

            JEditorPane newView = createContentControlHistoryView(editorView);
            Document newDoc = newView.getDocument();

            historyView.setDocument(newDoc);
            historyView.validate();
            historyView.repaint();
            historyView.setCaretPosition(caretPos);
        }

        private void synchRecentChangesView(JEditorPane recentView, WordMLTextPane editorView) {
            int caretPos = recentView.getCaretPosition();

            JEditorPane newView = createRecentChangesView(editorView);
            Document newDoc = newView.getDocument();

            recentView.setDocument(newDoc);
            recentView.validate();
            recentView.repaint();
            recentView.setCaretPosition(caretPos);
        }
    } //ViewChangeListener inner class

    private class WmlExitListener implements ExitListener {
        public boolean canExit(EventObject event) {
            boolean cancelExit = false;

            if (getToolbarStates().isAnyDocumentDirty()) {
                List<JInternalFrame> list = getAllInternalFrames();

                //Start from current editor's frame
                JInternalFrame currentFrame = getCurrentInternalFrame();
                list.remove(currentFrame);
                list.add(0, currentFrame);

                for (JInternalFrame iframe : list) {
                    if (getToolbarStates().isDocumentDirty(iframe)) {
                        try {
                            iframe.setSelected(true);
                            iframe.setIcon(false);
                        } catch (PropertyVetoException exc) {
                            ;//ignore
                        }

                        int answer = showConfirmClosingInternalFrame(iframe, "Application.exit.saveFirst");
                        if (answer == JOptionPane.CANCEL_OPTION) {
                            cancelExit = true;
                            break;
                        }
                    }
                } //for (iframe) loop
            } //if (getToolbarStates().isAnyDocumentDirty()

            boolean canExit = false;
            if (!cancelExit) {
                ResourceMap rm = getContext().getResourceMap();
                String title = rm.getString("Application.exit.dialog.title");
                String message = rm.getString("Application.exit.confirmMessage");
                int answer = showConfirmDialog(title, message, JOptionPane.YES_NO_OPTION,
                        JOptionPane.QUESTION_MESSAGE);
                canExit = (answer == JOptionPane.YES_OPTION);
            } //if (!canExit)

            return canExit;
        } //canExit()

        public void willExit(EventObject event) {
            ;//not implemented
        }
    }//WMLExitListener inner class

    private class ViewManager {
        private JInternalFrame owner;
        private ViewChangeListener viewChangeListener;

        ViewManager() {
            this.viewChangeListener = new ViewChangeListener();
            this.owner = null;
        }

        void setOwner(JInternalFrame iframe) {
            owner = iframe;
        }

        JTabbedPane getJTabbedPane() {
            checkOwner();

            if (owner.getContentPane().getComponent(0) instanceof JTabbedPane) {
                return (JTabbedPane) owner.getContentPane().getComponent(0);
            }
            return null;
        }

        JEditorPane getEditorView() {
            checkOwner();
            return getView(getEditorViewTabTitle());
        }

        JEditorPane getSourceView() {
            checkOwner();
            return getView(getSourceViewTabTitle());
        }

        JEditorPane getVersionHistoryView() {
            checkOwner();
            return getView(getContentControlHistoryViewTabTitle());
        }

        JEditorPane getRecentChangesView() {
            checkOwner();
            return getView(getRecentChangesViewTabTitle());
        }

        private JEditorPane getView(String viewTabTitle) {
            JEditorPane theView = null;

            JTabbedPane tabbedPane = getJTabbedPane();
            if (tabbedPane == null) {
                if (getEditorViewTabTitle().equals(viewTabTitle)) {
                    theView = SwingUtil.getWordMLTextPane(owner);
                } else {
                    //There should not be any other view.
                    //Other views are always created in a Tabbed pane.
                    theView = null;
                }
            } else {
                int idx = tabbedPane.indexOfTab(viewTabTitle);
                if (idx != -1) {
                    theView = (JEditorPane) SwingUtil.getDescendantOfClass(JEditorPane.class,
                            (Container) tabbedPane.getComponentAt(idx), false);
                }
            }

            return theView;
        }

        void showViewTab(String tabTitle) {
            checkOwner();

            WordMLTextPane editorView = (WordMLTextPane) getEditorView();
            if (editorView == null) {
                throw new IllegalStateException("No Editor View");
            }

            JTabbedPane tabbedPane = getJTabbedPane();
            int tabIdx = -1;
            if (tabbedPane != null) {
                tabIdx = tabbedPane.indexOfTab(tabTitle);
            }

            JEditorPane view = null;
            if (tabIdx == -1) {
                //Add view tab
                if (getSourceViewTabTitle().equals(tabTitle)) {
                    view = createSourceView(editorView);
                    addViewTab(view, tabTitle);
                    view.setCaretPosition(0);
                } else if (getContentControlHistoryViewTabTitle().equals(tabTitle)) {
                    view = createContentControlHistoryView(editorView);
                    addViewTab(view, tabTitle);
                    view.setCaretPosition(0);
                } else if (getRecentChangesViewTabTitle().equals(tabTitle)) {
                    view = createRecentChangesView(editorView);
                    addViewTab(view, tabTitle);
                    view.setCaretPosition(0);
                }
            } else {
                tabbedPane.setSelectedIndex(tabIdx);
                view = (JEditorPane) SwingUtil.getDescendantOfClass(JEditorPane.class,
                        (Container) tabbedPane.getComponentAt(tabIdx), false);

            }

            if (view != null) {
                final JEditorPane ep = view;
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        ep.requestFocusInWindow();
                    }
                });
            }
        }

        void closeViewTab(String tabTitle) {
            removeViewTab(tabTitle);
        }

        void removeViewTab(String tabTitle) {
            checkOwner();

            final JTabbedPane tabbedPane = getJTabbedPane();
            if (tabbedPane == null) {
                return;
            }

            if (getEditorViewTabTitle().equals(tabTitle)) {
                //Editor View should not be closed
                throw new IllegalArgumentException("Tab title=" + tabTitle);
            }

            int idx = tabbedPane.indexOfTab(getEditorViewTabTitle());
            if (idx == -1) {
                throw new IllegalStateException("No Editor View Tab");
            }

            //Disable tabbedPane's change listener. 
            //This is needed to stop the view synchronisation process in it.
            tabbedPane.removeChangeListener(this.viewChangeListener);

            if (tabbedPane.getTabCount() > 2) {
                idx = tabbedPane.indexOfTab(tabTitle);
                tabbedPane.removeTabAt(idx);

                int lastTab = tabbedPane.getTabCount() - 1;
                tabbedPane.setSelectedIndex(lastTab);

                //Put change listener back on.
                tabbedPane.addChangeListener(ViewManager.this.viewChangeListener);

                final JEditorPane viewToFocus = (JEditorPane) SwingUtil.getDescendantOfClass(JEditorPane.class,
                        (Container) tabbedPane.getComponentAt(lastTab), false);
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        viewToFocus.requestFocusInWindow();
                    }
                });

            } else if (tabbedPane.getTabCount() == 2) {
                //If Tabbed pane consists of Editor View and the view being closed
                JPanel viewPanel = (JPanel) tabbedPane.getComponentAt(idx);

                Rectangle bounds = owner.getBounds();
                owner.getContentPane().removeAll();

                //Move editorViewPanel from tabbedPane to owner's content
                tabbedPane.remove(idx);
                owner.getContentPane().add(viewPanel);

                owner.invalidate();
                owner.validate();
                owner.setBounds(bounds);

                final JEditorPane viewToFocus = (JEditorPane) SwingUtil.getDescendantOfClass(JEditorPane.class,
                        (Container) viewPanel, false);
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        viewToFocus.requestFocusInWindow();
                    }
                });

            } else {
                //should never happen.
            }

        } //removeViewTab()

        void selectViewTab(String tabTitle) {
            JTabbedPane tabbedPane = getJTabbedPane();
            if (tabbedPane == null) {
                throw new IllegalStateException("No Tabbed Pane.");
            }

            int idx = tabbedPane.indexOfTab(tabTitle);
            if (idx != -1) {
                tabbedPane.setSelectedIndex(idx);
            }
        }

        private void addViewTab(final JEditorPane view, String tabTitle) {
            JPanel viewPanel = FxScriptUIHelper.getInstance().createEditorPanel(view);

            Rectangle bounds = owner.getBounds();
            JTabbedPane tabbedPane = getJTabbedPane();
            if (tabbedPane == null) {
                //Create Tabbed Pane.
                //ADD Editor View Tab and new 'viewPanel' Tab
                JPanel editorViewPanel = (JPanel) owner.getContentPane().getComponent(0);

                owner.getContentPane().removeAll();

                tabbedPane = new JTabbedPane();
                tabbedPane.addTab(getEditorViewTabTitle(), editorViewPanel);
                tabbedPane.addTab(tabTitle, viewPanel);

                owner.getContentPane().add(tabbedPane);
                owner.invalidate();
                owner.validate();

            } else {
                //Disable tabbedPane's change listener. 
                //This is needed to avoid unnecessary view synchronisation 
                //process due to tab addition.
                tabbedPane.removeChangeListener(this.viewChangeListener);

                //Add Tab
                tabbedPane.addTab(tabTitle, viewPanel);
                tabbedPane.invalidate();
                tabbedPane.validate();
            }

            int i = tabbedPane.indexOfTab(tabTitle);
            tabbedPane.setSelectedIndex(i);
            //Put change listener back on
            tabbedPane.addChangeListener(this.viewChangeListener);
            owner.setBounds(bounds);
        }

        private void checkOwner() {
            if (owner == null) {
                throw new IllegalStateException("No Owner");
            }
        }
    } //TabbedPaneManager inner class

}// WordMLEditor class