📄 htmlpane.java
字号:
public class FormActionEvent extends ActionEvent
{ private final String method;
private final String action;
private final String name;
private final Properties data = new Properties();
// modified
private FormActionEvent( String method, String action, String name, String data){
super( HTMLPane.this, 0, "submit" );
this.method = method;
this.action = action;
this.name = name;
try
{
data = UrlUtil.decodeUrlEncoding(data) + "\n" + dataFromContributors();
this.data.load( new ByteArrayInputStream( data.getBytes()) );
}
catch( IOException e )
{ //assert false : "\"Impossible\" IOException";
}
}
/**
* @param method method= attribute to Form tag.
* @param action action= attribute to Form tag.
* @param data Data provided by standard HTML element. Data
* provided by custom tags is appended to this set.
*/
private FormActionEvent( String method, String action, String data )
{ super( HTMLPane.this, 0, "submit" );
this.method = method;
this.action = action;
this.name = null;
try
{
data = UrlUtil.decodeUrlEncoding(data) + "\n" + dataFromContributors();
this.data.load( new ByteArrayInputStream( data.getBytes()) );
}
catch( IOException e )
{ //assert false : "\"Impossible\" IOException";
}
}
/** Return the method= attribute of the <form> tag */
public String method() { return method; }
/** Return the action= attribute of the <form> tag */
public String action() { return action; }
public String name() { return name; }
/** Return the a set of properties representing the name=value
* pairs that would be sent to the server on form submission.
*/
public Properties data() { return data; }
/** Convenience method, works the same as
* <code>(HTMLPane)( event.getSource() )</code>
*/
public HTMLPane source(){ return (HTMLPane)getSource(); }
}
//@form-action-event-end
//-----------------------------------------------------------------
public final void addActionListener( ActionListener listener )
{ actionListeners = AWTEventMulticaster.add(actionListeners, listener);
}
public final void removeActionListener( ActionListener listener )
{ actionListeners = AWTEventMulticaster.remove(actionListeners, listener);
}
private final void handleSubmit(final String method, final String action,
final String data )
{ actionListeners.actionPerformed(
new FormActionEvent
( method,
action,
UrlUtil.decodeUrlEncoding(data)
+ "\n"
+ dataFromContributors()
)
);
}
// modified
private final void handleSubmit(final String method, final String action,
final String name, final String data )
{ actionListeners.actionPerformed(
new FormActionEvent(method, action, name,
UrlUtil.decodeUrlEncoding(data)
+ "\n"
+ dataFromContributors())
);
}
/*package*/ final void handleInputActionTag( final String name )
{ actionListeners.actionPerformed(new FormActionEvent( "", "", "" ));
}
/*** ****************************************************************
* Add support for a custom tag.
* Custom tags may contain arbitrary attributes (which are passed to your
* handler in a {@link Properties} object). Elements specified
* by a custom tag may contain plain-text contents;
* any nested elements are discarded. The end-tag element is
* <u>required</u> in the input, since your handler isn't called
* until the end-tag element is found.
* <p>
* Only one handler can be registered for a given tag.
* If you register more than one, then the most recent registration wins.
* All tag handlers must be installed before the page is loaded
* by {@link #setText}, {@link #setPage}, or equivalent.
* <p>
* The handler can choose to return a JComponent, which will appear on
* the rendered page in place of the tag. By default, the component
* sits on the text baseline. Use {@link JComponent#setAlignmentY}
* to control the placement relative to the text baseline. A value of
* 0.75 will place 75% of the Component above the baseline, for example.
* (a value of .85 is about right for a JLabel in the default font.)
* Here's a custom tag
* <code><holub:label text="<i>TEXT</i>"></code>
* that is replaced by a JLabel that holds the <i>TEXT</i> string:
* <PRE>
* pane.addTag
* ( "myTag",
* new TagHandler()
* { public JComponent
* handleTag(HTMLPane source, Properties attributes)
* { JComponent view = new JLabel(attributes.getProperty("text"));
* view.setAlignmentY(0.85F);
* return view;
* }
* }
* );
* </PRE>
* Tag handlers are shared by <u>all</u> instances of HtmlFrame.
* <p>
* Note that <b>tags are not case sensitive</b>, so regardless
* of how the tag appears in the input (or in the tag argument),
* it's treated as if it's all lower-case characters.
*
* @param tag the tag name (without any "angle" brackets).
* Must be all-upper-case or all-lower-case
* letters or underscores (no colons, no mixed case).
* Normal HTML tags cannot be redefined.
* Because of the way that the {@link JEditorPane} base
* class works,
* you can't use use "p-implied" or "content" as a custom-tag
* name and you can't use "endtag=" as an attribute.
* If assertions are enabled, attempts to register a
* standard tag generate <code>AssertionError</code>s, but if
* assertions are not enabled, the request is logged,
* but is otherwise ignored (and will form a minor memory leak.)
*
* @param handler The handler to call when the tag is encountered.
* @see TagHandler
*/
public final void addTag(String tag, TagHandler handler)
{ tag = tag.toLowerCase();
//assert !isStandardHTMLtag(tag) :
// "Illegal attempt to redefine standard HTML tag <"+tag+">";
//log.info( "Adding custom tag <" + tag + ">" );
tagHandlers.put( tag, handler );
}
/** Remove the handler for a given tag. Once this call is made,
* the tag will be ignored if it's encountered in a newly-loaded
* page. (A warning is logged if an unexpected tag is
* encountered, but this situation is not considered to be a
* run-time error.)
*/
public final void removeTag( String tag )
{ tagHandlers.remove( tag );
}
/** Notify the JComponents associated with the custom tags that
* the user has hit the reset button.
*/
protected void doReset()
{ for( Iterator i = contributors.iterator(); i.hasNext(); )
((TagBehavior) i.next() ).reset();
}
/** Collect form data from the JComponents associated with the
* custom tags and append it to the data set passed to the
* form handlers. This method has been made protected so that
* overrides can do some sort of processing or filtering
* on the data if they need to.
* @return a set of newline-delimited key=value pairs contained
* in a single String.
*/
protected String dataFromContributors()
{
StringBuffer formData = new StringBuffer();
for(Iterator i = contributors.iterator(); i.hasNext(); )
{ formData.append( ((TagBehavior) i.next() ).getFormData() );
formData.append("\n");
}
return formData.toString();
}
/** Workhorse function used by the assertion at the top of
* {@link #addTag}. You can use this method to see
* if a tag is available before attempting to register it.
* Note that the tag must be specified using all-lower-case
* characters.
*
* @return <code>true</code> if the tag argument specifies a standard
* HTML tag or one of the internal tags "p-implied" or "content."
* Otherwise, return <code>false</code>.
*/
public static final boolean isStandardHTMLtag(String tag)
{ HTML.Tag[] allTags = HTML.getAllTags();
if( tag.equals("p-implied") || tag.equals("content") )
return true;
for( int i = 0; i < allTags.length; ++i )
if( allTags[i].toString().toLowerCase().equals(tag) )
return true;
return false;
}
/*******************************************************************
* Provide input preprocessing.
* Use this method to replace the default input filter
* {@link FilterFactory#NULL_FACTORY}.
*
* @see FilterFactory
* @see NamespaceFilterFactory
*/
public void filterInput( FilterFactory provider )
{ filterProvider = provider;
}
/**
* A convenience method for local testing of a UI that will eventually
* be web based. If you map your home URL to a local directory,
* all links to the home URL are automatically replaced by
* links to the directory. For example, given
* <PRE>
* myPane.addHostMapping( "www.holub.com", "file://c:/src/test" );
* </PRE>
* an HTML "anchor" that looks like this:
* <PRE>
* <a href="http://www.holub.com/dir/foo.html">
* </PRE>
* is treated as if you had specified:
* <PRE>
* <a href="file://c:/src/test/dir/foo.html">
* </PRE>
* Multiple mappings are supported. That is, if you call this method
* more than once, then all the mappings you specify apply.
* <p>
* The host mappings are shared by <u>all</u> instances of
* HtmlFrame, including popups created by target=_blank in a
* hyperlink.
*
* @see #removeHostMapping
*/
public static final void addHostMapping( String thisHost,
String mapsToThisLocation )
{ if( hostMap == null )
hostMap = new HashMap();
hostMap.put( thisHost,
new HostMapping(thisHost, mapsToThisLocation));
}
/** Remove a host mapping added by a previous call to
* {@link #addHostMapping}.
* @see #addHostMapping
*/
public static final void removeHostMapping( String thisHost )
{ hostMap.remove(thisHost);
}
/** Map a url if there's an entry for the host portion in the host map.
* @param url a URL specified in terms of the "host" location.
* @return a URL for the mapped location.
* @see #addHostMapping
* @see #removeHostMapping
*/
public URL map(URL url)
{ if( hostMap != null )
{ HostMapping mapping = (HostMapping) hostMap.get(url.getHost());
if( mapping != null )
url = mapping.map( url );
}
return url;
}
/*******************************************************************
* This class handles the host-to-local-file mapping mechanics
* for {@link #addHostMapping}. The {@link #hostMap}
* table is a java.util.Map of objects of this class.
*/
private static final class HostMapping
{ private final String remote;
private final String local;
public HostMapping( String remote, String local )
{ this.remote = ".*://" + remote.replaceAll("\\.", "\\.");
this.local = local ;
}
public URL map( URL page )
{ try
{ String s = page.toExternalForm();
return new URL( s.replaceFirst(remote, local) );
}
catch( MalformedURLException e )
{ log.warning( "Couldn't map " + remote + " to "
+ local + ": " + e.getMessage() );
}
return page;
}
}
//@hyperlink-handler-start
/************************************************************************
* This Hyperlink handler replaces the contents of the current pane
* with whatever's at the indicated link. This code is copied more or
* less verbatim from the Sun documentation. I've also added simple
* support for the mailto: protocol.
*/
private final class HyperlinkHandler implements HyperlinkListener
{ public void hyperlinkUpdate(HyperlinkEvent event)
{ try
{ if( event.getEventType() == HyperlinkEvent.EventType.ACTIVATED )
{
HTMLPane source = (HTMLPane)event.getSource();
String description = event.getDescription();
Element e = event.getSourceElement();
// Get the attributes of the <a ...> tag that got
// us here, then extract the target= attribute. If
// we find target=_blank, then display the page
// in a popup window. I'm assuming that the
// href references an html file, because it wouldn't
// make much sense to use target=_blank if it didn't.
AttributeSet tagAttributes = (AttributeSet)
(e.getAttributes().getAttribute(HTML.Tag.A));
String target = null;
if( tagAttributes != null )
target = (String) tagAttributes.getAttribute(
HTML.Attribute.TARGET);
if( target != null && target.equals("_blank") )
{ popupBrowser( event.getURL() );
return;
}
// Handle http: and file: links. If the description
// doesn't contain a protocol (there's no ':'),
// then assume a "relative" link:
if( description.startsWith("http:")
|| description.startsWith("file:")
|| description.indexOf(":") == -1
)
{
JEditorPane pane = (JEditorPane) event.getSource();
if(event instanceof HTMLFrameHyperlinkEvent)
{ ((HTMLDocument)(source.getDocument())).
processHTMLFrameHyperlinkEvent(
(HTMLFrameHyperlinkEvent)event);
}
else if( !redirect(event.getURL()) )
{ unknownRedirect(event.getDescription());
}
}
else if( description.startsWith("mailto") )
{ if( isWindows() )
Runtime.getRuntime().exec(
"cmd.exe /c start " + description);
else
unknownRedirect(event.getDescription());
}
else
{ unknownRedirect( event.getDescription() );
}
}
}
catch( Exception e )
{ log.warning
( "Unexpected exception caught while processing hyperlink: "
+ e.toString() + "\n"
+ Log.stackTraceAsString( e )
);
}
}
/** Used for anchor tags that include target=_blank attributes. Pop up
* a new instance of an HTMLPanel in a subwindow, displaying the page
* whose URL is specified in the "description" argument.
*/
private final void popupBrowser( URL target )
throws MalformedURLException
{ JFrame popupFrame = new JFrame();
HTMLPane popup = new HTMLPane();
popupFrame.getContentPane(). add( new JScrollPane(popup) );
popup.redirect(target);
Dimension size = popup.getPreferredSize();
Point location = getLocationOnScreen();
popupFrame.setBounds( location.x + 10,
location.y + 10,
size.width,
size.height );
popupFrame.show();
}
/** Mailto support is OS specific, so we need to know the OS.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -