Showing posts with label JavaFX. Show all posts
Showing posts with label JavaFX. Show all posts

Thursday, June 18, 2009

How to Fix Java Web Start After Installing Java for Mac OS X 10.5 Update 4

Java for Mac OS X 10.5 Update 4 was more than a security fix. It also moved the Java Web Start application from the /Applications/Utilities/Java/ directory to /System/Library/CoreServices/. This broke all Java Web Start applications that were launched from anywhere other than the command line. (The command line application for Java Web Start can be found at /usr/bin/javaws.)

In order to restore the desired behavior for Java and JavaFX web applications that are launched either from a web browser or from a previously downloaded Web Start file, the Finder needs to be told what to do with JNLP files. One way to do this is to Get Info on any JNLP file in the Finder, as shown here:



The image above shows that TextEdit is the application that will open the JNLP file. Instead of TextEdit.app, the combo box should show Java Web Start.app as the default application to open all JNLP files, as shown here:



Click on the combo box and select “Other...”, then navigate to the “System” folder, then the “Library” folder, then the “CoreServices” folder, and select the “Java Web Start.app” file as shown here:



Select the “Always Open With” checkbox. Click the “Add” button and close the “Get Info” window.

That's it! Now all Web Start applications will launch correctly either from a web browser or from the Finder.


Monday, May 25, 2009

Lazy Loading of JavaFX Applets

Web pages that contain large applets or many small applets can load slowly. This annoyance is one of the reasons that applets have historically been unpopular. Sun has been pushing to revive the applet by improving its reliability and encouraging its use for JavaFX deployment. Despite these improvements, JavaFX applets can produce unacceptable page load times. Java Web Start is an alternative deployment mechanism and is currently regarded as a more robust solution. Web Start avoids browser compatibility issues by running apps in a separate window from the browser, thus not affecting page load time. But there are times when it is preferable to run an application from directly within a web page.

I will show how to keep an applet within the browser window and delay applet loading until the user performs some input action, like pressing a button. The applet is said to be loaded lazily, rather than loaded eagerly at page load time. Solutions for both JavaFX and traditional Java applets will be presented.


JavaFX Applets
One way to insert a JavaFX applet into a web page is to include two JavaScripts. The first links to Sun's dtfx.js JavaScript file:
<script src="http://dl.javafx.com/1.1/dtfx.js" type="text/javascript"></script>
The second script passes the applet's launch parameters to the “javafx” function defined in the dftx.js file. Here is an example:
<script type="text/javascript">
  javafx({
    archive: "http://jfreechartscaler.appspot.com/fx/jfreechart-scalable-scrollable.jar",
    draggable: true,
    width: 466,
    height: 288,
    code: "jfreechartscalablescrollable.Main",
    name: "jfreechart-scalable-scrollable"
  });
</script>
The “javafx” function above reads in the applet launch parameters and embeds the <APPLET> tag into the HTML of the web page. The applet tag is customized for best appearance on various operating systems and web browsers.

Rather than calling the “javafx” function to insert the applet tag into the web page, we will instead call the “javafxString” function and save the generated HTML into a string. The entire operation will be placed in a JavaScript function called startApplet(). So the JavaScript above will be replaced with the following script:
<script type="text/javascript">
  <!--
  function startApplet() {
    fxstring = javafxString({
      archive: "http://jfreechartscaler.appspot.com/fx/jfreechart-scalable-scrollable.jar",
      draggable: true,
      width: 466,
      height: 288,
      code: "jfreechartscalablescrollable.Main",
      name: "jfreechart-scalable-scrollable"
    });
    document.getElementById('appletdiv').innerHTML=fxstring;
  }
  //-->
</script>
The function refers to a <div> named ‘appletdiv’ that will contain the applet. When the function is executed, the inner HTML of the <div> will be replaced with the <APPLET> tag returned from the javafxString function. The function is called when the user clicks on an image that is initially placed in the <div> when the page loads:
<div id="appletdiv" style="text-align: center;">
  <input type="image" src="http://patrickwebster.synthasite.com/resources/scroller-applet.png" alt="Click here to start applet." onmousedown="startApplet()" />
</div>
I chose to fill the div with an image that is the same size as the applet. This prevents the layout of the page from changing when the applet loads, providing a seamless transition. If you are not concerned with minimizing the intrusiveness of loading the applet, you may initiate applet loading with a button press using this more simple div:
<div id="appletdiv" style="text-align: center;">
  <input type="button" value="Click here to start applet" onclick="startApplet()" />
</div>
If all this applet stuff is new to you, I recommend that you first try lazy loading of applets using the simple button press. After everything is working correctly, you may replace the button with a custom image if desired.

A live example of using a clickable image to load a JavaFX applet can be found here. The applet on that page can be embedded into almost any web page by pasting this snippet into the HTML code:
<script src="http://dl.javafx.com/1.1/dtfx.js" type="text/javascript"></script>
<script type="text/javascript">
  <!--
  function startApplet() {
    fxstring = javafxString(
      {
      archive: "http://jfreechartscaler.appspot.com/fx/jfreechart-scalable-scrollable.jar",
      draggable: true,
      width: 466,
      height: 288,
      code: "jfreechartscalablescrollable.Main",
      name: "jfreechart-scalable-scrollable"
      }
    );
    document.getElementById('appletdiv').innerHTML=fxstring;
  }
  //-->
</script>

<div id="appletdiv" style="text-align: center;">
  <input type="image" src="http://patrickwebster.synthasite.com/resources/scroller-applet.png" alt="Click here to start applet." onmousedown="startApplet()" />
</div>


Java Applets
Lazy loading of standard Java applets is easier than with JavaFX applets because the applet code is known without having to query a server-side JavaScript function. The concept of using a div as a placeholder is the same as in the JavaFX applet case. An example of a traditional eager-loading applet is here:
<applet codebase="http://apppspot.appspot.com/java/lib" archive="jfreechart-magnifier-applet.jar,jfreechart-1.0.12.jar,jcommon-1.0.15.jar,jxlayer.jar" code="JFreeChartMagnifierApplet" alt="Dude, like you totally need Java SE 6 or later to run this applet." height="288" width="466">
 <param name="draggable" value="true">
 <param name="java_arguments" value="-Djnlp.packEnabled=true">
</applet>
To transform this eager applet into a lazy one, we will again write a JavaScript function to build the <applet> tag into string form. But we will concatenate it manually because it is already known. Building the <applet> tag above into a string is achieved with this JavaScript:
<script type="text/javascript">
  <!--
  function startApplet() {
    appletsource='<applet code="JFreeChartMagnifierApplet" codebase="http://apppspot.appspot.com/java/lib" archive="jfreechart-magnifier-applet.jar,jfreechart-1.0.12.jar,jcommon-1.0.15.jar,jxlayer.jar" alt="Dude, like you totally need Java SE 6 or later to run this applet." height="288" width="466">\n'; 
    appletsource+='<param name="draggable" value="true">\n';
    appletsource+='<param name="java_arguments" value="-Djnlp.packEnabled=true">\n'; 
    appletsource+='</applet>\n'; 
    document.getElementById('appletdiv').innerHTML=appletsource;
  }
  //-->
</script>
The string appletsource is set equal to the inner HTML of the div named ‘appletdiv’. The div is nearly identical to the JavaFX case presented earlier:
<div id="appletdiv" style="text-align: center;">
  <input type="image" src="http://patrickwebster.synthasite.com/resources/magnifier-applet.png" alt="Click here to start applet." onmousedown="startApplet()" />
</div>
To view the live example of this lazy loading java applet, click here.


Conclusion
To provide the best user experience, page load times should be minimized. Lazy loading of applets ensures that applet load time does not contribute to initial page load time. The user only incurs the penalty of waiting for a potentially slow-loading applet after choosing to do so. The user has the right to not load the applet, and in doing so is charged a minimal cost. The only cost is the initial page load time, which can be quite small compared to the time to load a large applet. By amortizing the page load and applet load times, the perceived wait time is decreased, resulting in an overall improved experience. It is polite to put the user in control. It is rude to force the user to wait a long time for a page to load without giving any warning. So please, be lazy!


Friday, April 24, 2009

JavaFX Brush for SyntaxHighlighter

I wanted to display well-formatted JavaFX Script source code using Alex Gorbatchev's SyntaxHighlighter. But the current version of SyntaxHighlighter does not include a JavaFX brush. So I wrote one. A sample of highlighted code is below. Note that some of the code is a bit silly. It is written that way to exemplify the highlighting features.
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.Scene;
import javafx.scene.shape.Circle;
import javafx.scene.transform.Scale;
import javafx.stage.Stage;

/**
 * @author Patrick Webster
 */
var mouseX: Number;
var mouseY: Number;
var scale: Float = (-2.3 - 1.0) * -1.;
var egg: Circle;

Stage {
   title: "Easing Raw Egg"
   scene: Scene {
      fill: Color.BLACK
      height: 0x2EB  width: 0X30C
      content:
         egg = Circle {
            fill: Color.WHITE
            centerX: bind mouseX
            centerY: bind mouseY
            radius: 323.456e-02
            transforms: Scale {
               // Egg eases to moving mouse cursor
               pivotX: bind mouseX
               pivotY: bind mouseY
               x: bind scale * .02298E3
               y: bind scale *  32.56789
            }

            onMouseMoved: function( me: MouseEvent ) {
               updateMousePosition(me);
            }
            onMouseWheelMoved: function( we: MouseEvent ) {
               updateMousePosition(we);
               updateScale(we);
            }
         }
   }
}

function updateMousePosition(me : MouseEvent) : Void {
   mouseX = me.x;
   mouseY = me.y;
}

function updateScale(we: MouseEvent) : Float {
   var newScale = scale + (we.wheelRotation * -0.1);
   if (newScale < 1.0)
      return scale = 1.0000000e+00;
   return scale = newScale;
}

There are others who have written JavaFX brushes for SyntaxHighlighter, but they all lack the functionality that I desire. My implementation differs from others by including these additional features:
  1. Negative signs for constants are highlighted.
  2. Constants with scientific notation are highlighted.
  3. Leading and trailing decimal points are highlighted.
  4. Keywords are up-to-date for JavaFX 1.1.1.
  5. Deprecated keywords are supported.
  6. JavaFX built-in data types are highlighted differently than keywords.


Constants
In order to recognize all number formats for constants in JavaFX Script, I wrote the following horrendous regular expression:
/(-?\.?)(\b(\d*\.?\d+|\d+\.?\d*)(e[+-]?\d+)?|0x[a-f\d]+)\b\.?/gi
I am not going to explain every little detail of the above mess, but I will say that there are basically three parts. The first part matches on regular numbers with an optional leading negative sign and decimal point. The middle part looks for scientific notation. The last part checks for hexadecimal format. The major assumption is that correct JavaFX Script is the input. It is possible for the above expression to match on illegal code, but the hope is that people will not be highlighting incorrect code on their blogs. One small problem is that the above regular expression will match on the binary subtraction operator if there is no space between the operator and the subtrahend:
def b: Double = a-4;
I would prefer if the minus operator were not highlighted. But in general, it is good style to surround binary operators with a space, so this is really not a big problem.


Keywords
As the JavaFX Script language evolves, keywords come and go. The latest JavaFX Script 1.1.1 keywords are listed here. I'm sure this list will need to be updated again after the next language revision. I also include a separate list of deprecated keywords that is easily commented-out if desired.


Built-In Types
There are several built-in data types in JavaFX Script. I chose to highlight the following types in their own distinct color:
Boolean Byte Character Double Duration Float Integer Long Number Short String Void


Blogger Usage
If you followed my instructions on how to install SyntaxHighlighter into Blogger, then getting the JavaFX highlighter working will be a simple task. First you will need to download my brush file here. Expand it and place it somewhere on the internet. Then insert a link to the brush file in your Blogger HTML template. For example, I inserted this line in my template:
<script src='http://patrickwebster.googlepages.com/shBrushJavaFX.js' type='text/javascript'/>
I do not recommend that you link to my file because the file may move or change names at any time. Once the template is edited, new JavaFX code can be highlighted by wrapping it in the <pre class="brush: javafx"> and </pre> tags. The brush aliases 'jfx' or 'javafx' may be used within the <pre> tag.


Update: May 3rd, 2009
The newly released version 2.0.320 of SyntaxHighlighter includes a JavaFX brush! So you do not need to download my script. Simply upgrade to the new release and wrap your JavaFX code in the <pre> tag as explained above. You can link to the hosted version of the brush file by adding the following line to the appropriate section of your Blogger template:
<script src='http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJavaFX.js' type='text/javascript'/>



Wednesday, March 4, 2009

Scalable JFreeChart JavaFX Applet

A previous post demonstrated an applet written in Swing that showed how to scale a JFreeChart using the mouse wheel. That applet lacked the ability to scroll and explore the chart when zoomed-in. This post presents a similar applet, but with the added scrolling ability. Rather than using Swing exclusively, the applet below harnesses the power of JavaFX. The mouse wheel adjusts the zoom level. When zoomed in, the chart moves continuously with the pointer, allowing the user to scroll to any part of the chart simply by moving the mouse. Holding down the Control and Shift keys while moving the scroll wheel down returns the chart to unity scale. Mac users may have to click the “Trust” or “Allow” button in order to use the applet. Less-paranoid operating systems like Linux and Windows do not require any security confirmations. Scaling may not work in Safari on a Mac.

Note: Windows users may need to right-click on the chart to enable the mouse wheel adjustments. For some reason, the popup menu that appears after a right-click causes the applet to start listening to mouse wheel events. This is most noticeable when dragging the applet out of the browser and then closing the dragged-out applet and returning to the browser window. This problem does not occur on Mac or Linux.

The Code
Unlike my previous Swing applets, the code for this applet consumes only one file. JavaFX gets a lot done in only a few lines of code. I'll run through the highlights of the code. The complete source file is available to download.

First, constants and variables are declared:
def SCALING_INCREMENT: Number = 0.1;
def MINIMUM_SCALE: Number = 1.0;

var mouseX: Number;
var mouseY: Number;
var scale: Number = 1.0;

var chartPanel = new ChartPanel(createChart());
chartPanel.setPreferredSize(new Dimension(466, 288));
var chartComponent = SwingComponent.wrap(chartPanel);
The ChartPanel is declared just as in Swing, but with type var. The key to getting the Swing ChartPanel component into JavaFX is wrapping it in the SwingComponent.wrap() method. This wrapped component will be the only content in a Group, which is the only content of the Scene as shown here:
Stage {
  title: "JFreeChart in JavaFX"
  scene: Scene {
    content: [
      Group {
        content: {
          chartComponent
        }
        transforms: Scale {
          // Bind pivot point to mouse position so
          // zooming is always centered at the pointer
          pivotX: bind mouseX
          pivotY: bind mouseY
          x: bind scale
          y: bind scale
        }
        onMouseMoved: function( me: MouseEvent ) {
          updateMousePosition(me);
        }
        onMouseWheelMoved: function( we: MouseEvent ) {
          updateMousePosition(we);
          updateScale(we);
        }
        onMouseEntered: function( me: MouseEvent ) {
          windowsHack();
        }
      }
    ]
  }
}
The power of JavaFX is revealed in the bindings within the transforms variable of the Group. These four bindings update the scale of the chart whenever the variable scale changes. Scaling is centered about a pivot point which is the current pointer position. So zooming is always centered at the mouse pointer.

There are two mouse handlers that update the bindings. The onMouseMoved and onMouseWheelMoved handlers call appropriate functions to maintain the mouse position and scaling factor variables. These functions are:
function updateMousePosition(me : MouseEvent) {
  mouseX = me.x;
  mouseY = me.y;
}

function updateScale(we: MouseEvent) : Void {
  var rotation = we.wheelRotation;
  // Control-Shift zoom-out scales back to MINIMUM_SCALE
  if (we.controlDown and we.shiftDown and rotation > 0) {
    scale = MINIMUM_SCALE;
    return;
  }
  var newScale = scale - (rotation * SCALING_INCREMENT);
  // Don't allow scaling below MINIMUM_SCALE
  if (newScale < MINIMUM_SCALE) {
    scale = MINIMUM_SCALE;
    return;
  }
  scale = newScale;
}
The mouse coordinates mouseX and mouseY are kept up-to-date in updateMousePosition(). The updateScale() function adjusts the scaling factor according to the sign and number of mouse wheel rotation units. If the Control and Shift key modifiers are enabled on the mouse event, scale is reset to its minimum value. A check ensures that less-than-unity scaling is not allowed.

Unfortunately, JavaFX applets are not immune to the annoying Windows behavior that plagued my previous applets. When the applet is first displayed on a page, it won't listen to mouse wheel events. But if a tooltip or popup menu is displayed and then hidden, mouse wheel events become active. Here is the hack to get this working:
function windowsHack() {
  // simulate right-mouse click to bring-up popup menu
  chartPanel.mousePressed(new java.awt.event.MouseEvent(chartPanel,
    0, 0,
    java.awt.event.InputEvent.BUTTON3_DOWN_MASK,
    0, 0, 1, true));
  // immediately hide the popup menu
  chartPanel.getPopupMenu().setVisible(false);
}
The onMouseEntered function calls the above method the first time the mouse hovers over the applet. A right-click is simulated and a popup menu is shown and immediately hidden. This hack only fixes the situation when the applet is first displayed in the browser. If the applet is dragged out of the browser and then returned to the browser, the user will have to manually right-click and dismiss the popup menu in order to restore correct mouse wheel behavior.

JavaFX
Unlike my previous applets, this applet does not require Java SE 6. I do not use JXLayer or any other nifty Swing libraries; therefore, it will run on J2SE 5.0. This is the latest version of Java that will run applets within a web browser on my 32-bit Intel Mac, so testing is much easier than before. Performance does suffer on Java versions prior to SE 6 update 10, but not significantly due to the simplicity of this applet.

As a first impression, I'm quite pleased with the ability to get so much out of so few lines of code. It is nice not having to write listeners and just letting the bindings take care of everything. I look forward to working with JavaFX on future projects.

Resources
To build the applet, you will need these:
  1. My code
  2. JFreeChart library (I use version 1.0.12)
  3. JFree JCommon library (I use version 1.0.15)