package textbender.a.b.rhinohideDemo._; // Copyright 2007, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Textbender Software"), to deal in the Textbender Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Textbender Software, and to permit persons to whom the Textbender Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Textbender Software. THE TEXTBENDER SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TEXTBENDER SOFTWARE OR THE USE OR OTHER DEALINGS IN THE TEXTBENDER SOFTWARE. import java.awt.BorderLayout; import java.awt.EventQueue; import java.util.concurrent.atomic.AtomicBoolean; import javax.swing.*; import javax.swing.border.EmptyBorder; import org.w3c.dom.*; import org.w3c.dom.ranges.Range; import textbender.g.lang.*; import textbender.g.util.logging.LoggerX; import textbender.o.Browser; import textbender.o.rhinohide.*; import textbender.o.rhinohide._.*; import textbender.o.rhinohide.ranges.RhiSelection; /** Demo of Rhinohide, text selection and Range Level 2. * * @see http://reluk.ca/var/cache/rhinohide-demo/textbender/a/b/rhinohideDemo/Range_2_Demo.xht */ public @ThreadRestricted("AWT event dispatch") final class Range_2_Demo extends JApplet implements Runnable { // - A p p l e t ---------------------------------------------------------------------- private final AtomicBoolean isStartedA = new AtomicBoolean(); public @ThreadSafe void start() { if( isStartedA.getAndSet( true )) return; // start once only LoggerX.i(getClass()).info( "starting" ); EventQueue.invokeLater( Range_2_Demo.this ); // in AWT event dispatch thread } private final AtomicBoolean isDestroyedA = new AtomicBoolean(); public @ThreadSafe void destroy() { if( isDestroyedA.getAndSet( true )) { assert false; return; } LoggerX.i(getClass()).info( "destroying" ); if( window != null ) window.release(); } // - R u n n a b l e ------------------------------------------------------------------ /** Runs this test applet. */ public void run() { reportList.addElement( "starting..." ); ThreadSafe.U.disableChecking(); // I lack permission, I'm an unsigned applet { // Construct GUI. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - JList reportListV = new JList( reportList ); reportListV.setBorder( new EmptyBorder( /*top*/2, /*left*/2, /*bottom*/2, /*right*/0 )); JScrollPane scrollPane = new JScrollPane( reportListV ); scrollPane.setBorder( null ); getContentPane().add( scrollPane, BorderLayout.CENTER ); } try { window = RhiWindow.createWindow( Range_2_Demo.this ); // Query DOM implementation. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - final Document document = window.getDocument(); reportList.addElement ( "browser supports Range Level 2... " + // document.isSupported( "Range", "2.0" ) //////// this is better, because isSupported() assumes level 2 document.getImplementation().hasFeature( "Range", "2.0" ) ); // Start a selection change detector. // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - final Thread thread = new Thread // To poll. Might instead listen for events (mouse and key) if you want to assume event support. ( new SelectionChangeDetector(), "selection change detector" ); thread.setDaemon( true ); thread.setPriority( Thread.NORM_PRIORITY - 1 ); thread.start(); // - - - reportList.addElement( "successfully started" ); } catch( StunnedRhinoException xSR ) { LoggerX.i(getClass()).info( Browser.pageExitSupressionMessage( xSR )); } // in case the page exits, from beneath us catch( Exception x ) // all, checked or not { LoggerX.i(getClass()).log( LoggerX.WARNING, /*message*/"", x ); } } //// P r i v a t e /////////////////////////////////////////////////////////////////////// @ThreadRestricted( "AWT event dispatch" ) private final DefaultListModel reportList = new DefaultListModel(); private volatile RhiWindow window; // final after init // ==================================================================================== private final class SelectionChangeDetector implements Runnable { SelectionChangeDetector() { final Document document = window.getDocument(); // Initialize early, where possible (let caller handle exceptions). // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text_getCommonAncestorContainer = (Text) document.getElementById( "R-getCommonAncestorContainer" ).getFirstChild(); text_getStartContainer = (Text) document.getElementById( "R-getStartContainer" ).getFirstChild(); text_getStartOffset = (Text) document.getElementById( "R-getStartOffset" ).getFirstChild(); text_getCollapsed = (Text) document.getElementById( "R-getCollapsed" ).getFirstChild(); text_getEndContainer = (Text) document.getElementById( "R-getEndContainer" ).getFirstChild(); text_getEndOffset = (Text) document.getElementById( "R-getEndOffset" ).getFirstChild(); text_toString = (Text) document.getElementById( "R-toString" ).getFirstChild(); } private final Text text_getCommonAncestorContainer; private final Text text_getStartContainer; private final Text text_getStartOffset; private final Text text_getCollapsed; private final Text text_getEndContainer; private final Text text_getEndOffset; private final Text text_toString; // - R u n n a b l e -------------------------------------------------------------- public void run() { endless: for( ;; ) { ThreadX.trySleep( 1000/*ms*/ ); if( isDestroyedA.get() ) break endless; // if we get this far, probably no page exit is impending, and the following is unlikely to throw a StunnedRhinoException final Range range; { RhiSelection selection = window.getSelection(); if( selection == null || selection.getRangeCount() == 0 ) range = null; else range = selection.getRangeAt( 0 ); } Text text; text = text_getCommonAncestorContainer; if( range == null ) text.setData( "" ); else text.setData( range.getCommonAncestorContainer().toString() ); text = text_getStartContainer; if( range == null ) text.setData( "" ); else text.setData( range.getStartContainer().toString() ); text = text_getStartOffset; if( range == null ) text.setData( "" ); else text.setData( Integer.toString( range.getStartOffset() )); text = text_getCollapsed; if( range == null ) text.setData( "" ); else text.setData( Boolean.toString( range.getCollapsed() )); text = text_getEndContainer; if( range == null ) text.setData( "" ); else text.setData( range.getEndContainer().toString() ); text = text_getEndOffset; if( range == null ) text.setData( "" ); else text.setData( Integer.toString( range.getEndOffset() )); text = text_toString; if( range == null ) text.setData( "" ); else text.setData( range.toString() ); } } } }