Thursday, February 17, 2011

Is there an RTF display widget in SWT

I would like to display an RTF document in an SWT (actually Eclipse RCP) application.

I know there is a Swing widget for displaying and editing RTF text, but it is Swing and quite alien in look and feel when used in the otherwise platform (not to mention that to the last of my knowledge it did not display images and had only limited support for formatting)

Other options is to use COM interface on windows, but that works only on the windows platform and requires that an ActiveX RichEdit contol be installed on the customer machine... which can make the deployment of the application quite horrendous...

What are the other options for displaying rich documents inside Eclipse/SWT application?

From stackoverflow
  • You may use swt.custom.StyledText. That has many features to change the look of the text. But I don't think it can load or save RTF right now.

    I once wrote an HTML editor with it, but it is quite difficult, since the StyledText model to add styles to a part of the text is so alien compared to the way HTML/RTF works.

    AFAIK you can directly print from this control, which internally creates an RTF representation of the contents. But that's not exactly what you asked for.

    Heath Borders : StyledText will save RTF to the clipboard if copied. A total hack way to get the RTF values would be to select all, copy, and extract the RTF value from the clipboard.
    Roland Tepp : Yesm you are right, it is not what I asked. StyledText can only copy text to clipboard as RTF and does not handle RTF as an input at all... Using StyledText as a base for RTF viewer/editor would probably be one way to start, but as a widget itself, it is not what I wanted...
  • I'm not sure of a way to do it without using ActiveX. If you do go this direction you might want to look into the IBM Container for ActiveX Documents, which is supposed to allow better integration of documents.

    Roland Tepp : What we have now come up with is halfway what I wanted. Basically we used JNI to call upon native Windows RTF control (the one used by WodPad) - this way we don't have to worry about nasty ActiveX deployment issues, but still it's a hack
  • Why not first read the RTF text into a StyledDocument using the RTFEditorKit and then writing the StyledDocument to a StringWriter using the HTMLEditorKit?

    String rtf = "whatever";
    BufferedReader input = new BufferedReader(new StringReader(rtf));
    
    RTFEditorKit rtfKit = new RTFEditorKit();
    StyledDocument doc = (StyledDocument) rtfKit.createDefaultDocument();
    rtfEdtrKt.read( input, doc, 0 );
    input.close();
    
    HTMLEditorKit htmlKit = new HTMLEditorKit();       
    StringWriter output = new StringWriter();
    htmlKit.write( output, doc, 0, doc.getLength());
    
    String html = output.toString();
    

    And then display the HTML?

    Roland Tepp : Nice hack ... although I am bit afraid of how does this conversion play along with various tab stop options we presently use in our RTF documents.
  • You might want to use the Swing control with the AWT/SWT bridge. I used this to embed OpenOffice into an SWT app:

    package ooswtviewer;
    
    import java.awt.Panel;
    
    import com.sun.star.awt.XView;
    import com.sun.star.beans.Property;
    import com.sun.star.beans.UnknownPropertyException;
    import com.sun.star.beans.XPropertySet;
    import com.sun.star.comp.beans.Frame;
    import com.sun.star.comp.beans.NoConnectionException;
    import com.sun.star.comp.beans.OOoBean;
    import com.sun.star.comp.beans.OfficeDocument;
    import com.sun.star.drawing.XDrawView;
    import com.sun.star.frame.XController;
    import com.sun.star.frame.XDesktop;
    import com.sun.star.frame.XFrame;
    import com.sun.star.frame.XFramesSupplier;
    import com.sun.star.frame.XLayoutManager;
    import com.sun.star.frame.XModel;
    import com.sun.star.lang.WrappedTargetException;
    import com.sun.star.ui.XUIElement;
    import com.sun.star.uno.Any;
    import com.sun.star.uno.UnoRuntime;
    import com.sun.star.uno.XInterface;
    import com.sun.star.view.XViewSettingsSupplier;
    
    /**
     * Code based on example from http://www.eclipsezone.com/eclipse/forums/t48966.html
     * 
     * @author Aaron digulla
     */
    public class OOoSwtViewer extends Panel
    {
        private static final String RESOURCE_TOOLBAR_TEXTOBJECTBAR = "private:resource/toolbar/textobjectbar";
        private static final String RESOURCE_TOOLBAR_STANDARDBAR = "private:resource/toolbar/standardbar";
        private static final String RESOURCE_MENUBAR = "private:resource/menubar/menubar";
    
        private static final long serialVersionUID = -1408623115735065822L;
    
        private OOoBean aBean;
    
        public OOoSwtViewer()
        {
            super();
            aBean = new OOoBean();
            setLayout(new java.awt.BorderLayout());
            add(aBean, java.awt.BorderLayout.CENTER);
    
            aBean.setAllBarsVisible (false);
        }
    
        public XPropertySet getXPropertySet ()
        {
            return getXPropertySet (getFrame ());
        }
    
        public XPropertySet getXPropertySet (Object o)
        {
            return (XPropertySet)UnoRuntime.queryInterface (XPropertySet.class, o);
        }
    
        public Frame getFrame ()
        {
            try
            {
                return aBean.getFrame ();
            }
            catch (NoConnectionException e)
            {
                throw new OOException ("Error getting frame from bean", e);
            }
        }
    
        public XLayoutManager getXLayoutManager ()
        {
            try
            {
                return (XLayoutManager)UnoRuntime.queryInterface (XLayoutManager.class, getXPropertySet ().getPropertyValue ("LayoutManager"));
            }
            catch (Exception e)
            {
                throw new OOException ("Error getting LayoutManager from bean's properties", e);
            }        
        }
    
        public void setMenuBarVisible (boolean visible)
        {
            if (visible)
                getXLayoutManager ().showElement (RESOURCE_MENUBAR);
            else
                getXLayoutManager ().hideElement (RESOURCE_MENUBAR);
        }
    
        public void setStandardBarVisible (boolean visible)
        {
            if (visible)
                getXLayoutManager ().showElement (RESOURCE_TOOLBAR_STANDARDBAR);
            else
                getXLayoutManager ().hideElement (RESOURCE_TOOLBAR_STANDARDBAR);
        }
    
        public void setTextObjectBarVisible (boolean visible)
        {
            if (visible)
                getXLayoutManager ().showElement (RESOURCE_TOOLBAR_TEXTOBJECTBAR);
            else
                getXLayoutManager ().hideElement (RESOURCE_TOOLBAR_TEXTOBJECTBAR);
        }
    
    
        private Thread loadThread;
        private Exception loadException;
    
        public void setDocument(final String url)
        {
            loadThread = new Thread () {
                public void run() {
                    try
                    {
                        aBean.loadFromURL(url, null);
                        aBean.aquireSystemWindow();
    
                        setTextObjectBarVisible (false);
    
    //                    for (XUIElement e: getXLayoutManager ().getElements ())
    //                    {
    //                        XInterface i = (XInterface)e.getRealInterface ();
    //                        System.out.println (e);
    //                        System.out.println (i);
    //                        printProperties (getXPropertySet (e));
    //                    }
    
                        /*
                        System.out.println ("frame:");
                        printProperties (getXPropertySet ());
    
    frame:
    Title=test - OpenOffice.org Writer 
    IndicatorInterception=Any[Type[com.sun.star.task.XStatusIndicator], null]
    LayoutManager=Any[Type[com.sun.star.frame.XLayoutManager], [Proxy:26506390,717ea70;msci[0];342169f1a1164ee688893a857f65b3e1,Type[com.sun.star.frame.XLayoutManager]]]
    DispatchRecorderSupplier=Any[Type[com.sun.star.frame.XDispatchRecorderSupplier], null]
    IsHidden=false
                        */
    
                        XController controller = aBean.getDocument ().getCurrentController ();
                        /*
                        System.out.println ("controller:");
                        printProperties (getXPropertySet (controller));
    
    controller:
    IsConstantSpellcheck=true
    IsHideSpellMarks=false
    LineCount=1
    PageCount=1
                        */
    
                        /*
                        System.out.println ("layoutManager:");
                        printProperties (getXPropertySet (getXLayoutManager ()));
    
    layoutManager:
    AutomaticToolbars=true
    HideCurrentUI=false
    LockCount=0
    MenuBarCloser=true
    RefreshContextToolbarVisibility=false
                        */
    
                        /*
                        System.out.println ("document:");
                        printProperties (getXPropertySet (aBean.getDocument ()));
                        OfficeDocument doc = aBean.getDocument ();
    ApplyFormDesignMode=false
    ApplyWorkaroundForB6375613=false
    AutomaticControlFocus=false
    BasicLibraries=Any[Type[com.sun.star.script.XLibraryContainer], [Proxy:14806696,73ca178;msci[0];342169f1a1164ee688893a857f65b3e1,Type[com.sun.star.script.XLibraryContainer]]]
    BuildId=680$9310
    CharFontCharSet=1
    CharFontCharSetAsian=1
    CharFontCharSetComplex=1
    CharFontFamily=3
    CharFontFamilyAsian=6
    CharFontFamilyComplex=6
    CharFontName=Times New Roman
    CharFontNameAsian=Arial Unicode MS
    CharFontNameComplex=Tahoma
    CharFontPitch=2
    CharFontPitchAsian=2
    CharFontPitchComplex=2
    CharFontStyleName=
    CharFontStyleNameAsian=
    CharFontStyleNameComplex=
    CharLocale=com.sun.star.lang.Locale@fb6354
    CharacterCount=20
    DialogLibraries=Any[Type[com.sun.star.script.XLibraryContainer], [Proxy:3556929,73a39c0;msci[0];342169f1a1164ee688893a857f65b3e1,Type[com.sun.star.script.XLibraryContainer]]]
    ForbiddenCharacters=Any[Type[com.sun.star.i18n.XForbiddenCharacters], [Proxy:11544872,7669148;msci[0];342169f1a1164ee688893a857f65b3e1,Type[com.sun.star.i18n.XForbiddenCharacters]]]
    HasValidSignatures=false
    HideFieldTips=false
    IndexAutoMarkFileURL=
    LockUpdates=false
    ParagraphCount=1
    RecordChanges=false
    RedlineDisplayType=2
    RedlineProtectionKey=[B@f593af
    RuntimeUID=10
    ShowChanges=true
    TwoDigitYear=1930
    WordCount=5
    WordSeparator=()        
                        */
    
    //                    System.out.println ("viewData:");
    //                    printProperties (getXPropertySet (controller.getFrame ().getContainerWindow ()));
    
                        XViewSettingsSupplier settingsSupplier = (XViewSettingsSupplier)UnoRuntime.queryInterface (XViewSettingsSupplier.class, controller);
    //                    System.out.println ("settingsSupplier:");
    //                    printProperties (settingsSupplier.getViewSettings ());
                        settingsSupplier.getViewSettings ().setPropertyValue ("ShowVertRuler", Boolean.FALSE);
                        settingsSupplier.getViewSettings ().setPropertyValue ("ShowHoriRuler", Boolean.FALSE);
                        // Switch to Web Layout. This layout mode comes without gray border and the page borders automatically adujst to the frame
                        settingsSupplier.getViewSettings ().setPropertyValue ("ShowOnlineLayout", Boolean.TRUE);
    //                    settingsSupplier.getViewSettings ().setPropertyValue ("ShowTextBoundaries", Boolean.TRUE);
    
    //                    XView view = (XView)UnoRuntime.queryInterface (XView.class, getFrame ());
    //                    System.out.println ("drawView="+view);
    //                    printProperties (getXPropertySet (view));
    
                        /*
                        XModel model = (XModel)UnoRuntime.queryInterface (XModel.class, doc);
                        printProperties ("model", model);
    
                        Same as getDocument()
                        */
    
                        /*
                        System.out.println ("Interfaces implemented by aBean.getDocument():");
                        for (Class c: OOoInspector.queryInterface (aBean.getDocument ()))
                            System.out.println ("    "+c.getName ());
        com.sun.star.datatransfer.XTransferable
        com.sun.star.document.XDocumentInfoSupplier
        com.sun.star.document.XDocumentLanguages
        com.sun.star.document.XDocumentSubStorageSupplier
        com.sun.star.document.XEmbeddedScripts
        com.sun.star.document.XEventBroadcaster
        com.sun.star.document.XEventsSupplier
        com.sun.star.document.XLinkTargetSupplier
        com.sun.star.document.XRedlinesSupplier
        com.sun.star.document.XStorageBasedDocument
        com.sun.star.document.XViewDataSupplier
        com.sun.star.drawing.XDrawPageSupplier
        com.sun.star.embed.XVisualObject
        com.sun.star.frame.XLoadable
        com.sun.star.frame.XModel
        com.sun.star.frame.XModel2
        com.sun.star.frame.XModule
        com.sun.star.frame.XStorable
        com.sun.star.frame.XStorable2
        com.sun.star.script.provider.XScriptProviderSupplier
        com.sun.star.style.XAutoStylesSupplier
        com.sun.star.style.XStyleFamiliesSupplier
        com.sun.star.text.XBookmarksSupplier
        com.sun.star.text.XChapterNumberingSupplier
        com.sun.star.text.XDocumentIndexesSupplier
        com.sun.star.text.XEndnotesSupplier
        com.sun.star.text.XFootnotesSupplier
        com.sun.star.text.XLineNumberingProperties
        com.sun.star.text.XNumberingRulesSupplier
        com.sun.star.text.XPagePrintable
        com.sun.star.text.XReferenceMarksSupplier
        com.sun.star.text.XTextDocument
        com.sun.star.text.XTextEmbeddedObjectsSupplier
        com.sun.star.text.XTextFieldsSupplier
        com.sun.star.text.XTextFramesSupplier
        com.sun.star.text.XTextGraphicObjectsSupplier
        com.sun.star.text.XTextSectionsSupplier
        com.sun.star.text.XTextTablesSupplier
        com.sun.star.ui.XUIConfigurationManagerSupplier
        com.sun.star.util.XCloseable
        com.sun.star.util.XCloseBroadcaster
        com.sun.star.util.XLinkUpdate
        com.sun.star.util.XModifiable
        com.sun.star.util.XModifiable2
        com.sun.star.util.XModifyBroadcaster
        com.sun.star.util.XNumberFormatsSupplier
        com.sun.star.util.XRefreshable
        com.sun.star.util.XReplaceable
        com.sun.star.util.XSearchable
        com.sun.star.view.XPrintable
        com.sun.star.view.XPrintJobBroadcaster
        com.sun.star.view.XRenderable
        com.sun.star.xforms.XFormsSupplier
                        */
    
                        /*
                        System.out.println ("Interfaces implemented by controller:");
                        for (Class c: OOoInspector.queryInterface (controller))
                            System.out.println ("    "+c.getName ());
    
        com.sun.star.awt.XUserInputInterception
        com.sun.star.datatransfer.XTransferableSupplier
        com.sun.star.frame.XController
        com.sun.star.frame.XControllerBorder
        com.sun.star.frame.XDispatchInformationProvider
        com.sun.star.frame.XDispatchProvider
        com.sun.star.task.XStatusIndicatorSupplier
        com.sun.star.text.XRubySelection
        com.sun.star.text.XTextViewCursorSupplier
        com.sun.star.ui.XContextMenuInterception
        com.sun.star.view.XControlAccess
        com.sun.star.view.XFormLayerAccess
        com.sun.star.view.XSelectionSupplier
        com.sun.star.view.XViewSettingsSupplier
                        */
    
                        /*
                        System.out.println ("Interfaces implemented by frame:");
                        for (Class c: OOoInspector.queryInterface (getFrame ()))
                            System.out.println ("    "+c.getName ());
    
        com.sun.star.awt.XFocusListener
        com.sun.star.awt.XTopWindowListener
        com.sun.star.awt.XWindowListener
        com.sun.star.document.XActionLockable
        com.sun.star.frame.XComponentLoader
        com.sun.star.frame.XDispatchInformationProvider
        com.sun.star.frame.XDispatchProvider
        com.sun.star.frame.XDispatchProviderInterception
        com.sun.star.frame.XFrame
        com.sun.star.frame.XFramesSupplier
        com.sun.star.task.XStatusIndicatorFactory
        com.sun.star.util.XCloseable
        com.sun.star.util.XCloseBroadcaster
                        */
    
                        /*
                        XFramesSupplier frames = OOoInspector.queryInterface (XFramesSupplier.class, getFrame ());
                        printProperties ("frames", frames);
    
                        for (int i=0; i<frames.getFrames ().getCount (); i++)
                        {
                            XFrame frame = (XFrame)frames.getFrames ().getByIndex (i);
                            printProperties ("Frame "+i, frame);
                        }
    
    frames=[Proxy:16382237,6ace84c;msci[0];342169f1a1164ee688893a857f65b3e1,Type[com.sun.star.frame.XFramesSupplier]]
    Title=test - OpenOffice.org Writer 
    IndicatorInterception=Any[Type[com.sun.star.task.XStatusIndicator], null]
    LayoutManager=Any[Type[com.sun.star.frame.XLayoutManager], [Proxy:22149392,76bd794;msci[0];342169f1a1164ee688893a857f65b3e1,Type[com.sun.star.frame.XLayoutManager]]]
    DispatchRecorderSupplier=Any[Type[com.sun.star.frame.XDispatchRecorderSupplier], null]
    IsHidden=false
                        */
                        XPropertySet p = getXPropertySet (getFrame ());
                        Any any = (Any)p.getPropertyValue ("LayoutManager");
                        System.out.println (any);
                        System.out.println (any.getClass ().getName ());
                        XLayoutManager layoutManager = (XLayoutManager)any.getObject ();
                        printProperties ("layoutManager", layoutManager);
    
    
                        /*
                        printProperties ("containerWindow", getFrame ().getContainerWindow ());
    
    containerWindow=[Proxy:11970262,6d33e60;msci[0];342169f1a1164ee688893a857f65b3e1,Type[com.sun.star.awt.XWindow]]
    null
                        */
    
                        /*
                        printProperties ("componentWindow", getFrame ().getComponentWindow ());
    
    componentWindow=[Proxy:25380515,8657cc4;msci[0];342169f1a1164ee688893a857f65b3e1,Type[com.sun.star.awt.XWindow]]
    null
                        */
                    }
                    catch (Exception e)
                    {
                        e.printStackTrace ();
                    }
                }
            };
            if (1 == 1)
                loadThread.start ();
            else
                loadThread.run ();
        }
    
        /** closes the bean viewer and tries to terminate OOo.
         */
        public void terminate() throws NoConnectionException {
            setVisible(false);
            XDesktop xDesktop = null;
            xDesktop = aBean.getOOoDesktop();
            aBean.stopOOoConnection();
            if (xDesktop != null)
                xDesktop.terminate();
        }
    
        /** closes the bean viewer, leaves OOo running.
         */
        public void close() {
            setVisible(false);
            aBean.stopOOoConnection();
        }
    
        public void printProperties (String name, Object obj)
        {
            System.out.println (name+"="+obj);
            if (obj != null)
                printProperties (getXPropertySet (obj));
        }
    
        public void printProperties (XPropertySet set)
        {
            if (set == null)
            {
                System.out.println ("null");
                return;
            }
    
            for (Property p: set.getPropertySetInfo ().getProperties ())
            {
                try
                {
                    System.out.println (p.Name+"="+set.getPropertyValue (p.Name));
                }
                catch (Exception e)
                {
                    throw new OOException ("Error getting value of property "+p.Name, e);
                }
            }
        }
    
    }
    

    You can use the control like this:

    package ooswtviewer;
    
    import java.awt.BorderLayout;
    import java.awt.Frame;
    import java.awt.Panel;
    import java.io.File;
    
    import javax.swing.JRootPane;
    
    import org.eclipse.swt.SWT;
    import org.eclipse.swt.awt.SWT_AWT;
    import org.eclipse.swt.events.DisposeEvent;
    import org.eclipse.swt.events.DisposeListener;
    import org.eclipse.swt.layout.FillLayout;
    import org.eclipse.swt.widgets.Composite;
    import org.eclipse.swt.widgets.Display;
    import org.eclipse.swt.widgets.Shell;
    
    /**
     * Code based on example from http://www.eclipsezone.com/eclipse/forums/t48966.html
     * 
     * @author Aaron Digulla
     */
    public class OOoSwtSnippet {
        public static void main(String[] args) {
            OOoSwtSnippet obj = new OOoSwtSnippet ();
            try
            {
                obj.run (args);
            }
            catch (Exception e)
            {
                e.printStackTrace ();
            }
        }
    
        public void run (String[] args) throws Exception
        {
            final Display display = new Display();
            final Shell shell = new Shell(display);
            shell.setLayout(new FillLayout());
    
            Composite composite = new Composite(shell, SWT.NO_BACKGROUND
                    | SWT.EMBEDDED);
    
            System.setProperty("sun.awt.noerasebackground", "true");
    
            /* Create and setting up frame */
            Frame frame = SWT_AWT.new_Frame(composite);
            Panel panel = new Panel(new BorderLayout()) {
                public void update(java.awt.Graphics g) {
                    paint(g);
                }
            };
            frame.add(panel);
            JRootPane root = new JRootPane();
            panel.add(root);
            java.awt.Container contentPane = root.getContentPane();
    
            shell.setSize(800, 600);
            final OOoSwtViewer viewer = new OOoSwtViewer();
            contentPane.add(viewer);
    
            // viewer.setDocument(NEW_WRITTER_DOCUMENT);
            File document = new File ("test.odt");
            String url = document.getAbsoluteFile ().toURL ().toString ();
            url = "file:///" + url.substring (6);
            System.out.println ("Loading "+url);
            viewer.setDocument(url);
    
            shell.setText ("OOoSwtSnippet");
            shell.open();
            shell.addDisposeListener(new DisposeListener() {
                public void widgetDisposed(DisposeEvent e) {
                    try {
                        viewer.close();
                    } catch (RuntimeException exception) {
                        exception.printStackTrace();
                    }
                }
    
            });
            while (!shell.isDisposed()) {
                if (!display.readAndDispatch())
                    display.sleep();
            }
            display.dispose();
        }
    
    }
    

    OOException is a RuntimeException:

    package ooswtviewer;
    
    /**
     * Wrapper for all OO exceptions to keep throws clauses in check
     * 
     * @author Aaron Digulla
     */
    public class OOException extends RuntimeException
    {
    
        public OOException ()
        {
            super ();
        }
    
        public OOException (String message, Throwable cause)
        {
            super (message, cause);
        }
    
        public OOException (String message)
        {
            super (message);
        }
    
        public OOException (Throwable cause)
        {
            super (cause);
        }
    
    }
    
    Roland Tepp : Not really - I can't force everyone to install OOo alongside with my application and neither can I expect people to have OOo installed on every desktop (hell, even MS Office is not available on every target desktop)
    Aaron Digulla : I'm not sure how much "install" you'd have to do. It should be enough to have the JAR and all DLLs of the OO install in the application directory, set -Djava.library.path correctly and it should work.
    Roland Tepp : Still, this would pump up the already significant install package even more...
    Aaron Digulla : That you can do something doesn't mean you should. I'm giving you options; you must decide which option fits all those petty details which you didn't mention since you didn't want to spend three days on the question :)
  • http://sites.google.com/site/anshunjain/eclipse-musings/eclipse-hacks/eclipse-richt-text-editor

  • Actuall, I've just found another widget that is quite promising atm:

    http://onpositive.com/richtext

0 comments:

Post a Comment