Launch Tasks

After reading this section, you should understand what a LaunchTask is and how it is used in the context of an ApplicationFrame.

Basically speaking, LaunchTask implementations are used to decouple application-specific initialization code from the ApplicationFrame class, instances of which are needed for any standalone application based on the Skeleton framework.

Even though the ApplicationFrame class acts like a controller, it should not be extended with specialized initialization code, which is only required once during application startup.

The ApplicationFrame-derived class SwingApplicationFrame decouples this specialized code by reading a list of “launch tasks” when starting up and executing them sequentially. You may override the getLauncherTask() method of the parent SwingApplicationFrame class and return a new list. Otherwise, you may use the default list without any modification.

The Skeleton provides a basic framework for all currently required LaunchTask types. There are LaunchTasks to load files (LoadFileLaunchTask), LaunchTasks to create and initialize ToolBars (AbstractToolBarLaunchTask), Menus (AbstractMenuLaunchTask), as well as LaunchTasks to create Commands (CommandLaunchTask). Either use these superclasses as the basis for your own implementations or extend the default LaunchTask implementations of the Skeleton framework.

The following sections show the LaunchTask implementations used in this chapter's sample application.

Loading Preferences

Application preferences may differ for every Skeleton-based application you create. These preferences are configured in an xml file and the preference dialog of you running application will reflect the structure of this file. You will return the file's path in the method getDefaultPreferences() of class CustomPreferencesLaunchTask. You can always count on a similar look and navigation structure when setting preferences.

In order to store the settings of a user, a file needs to be written to a directory on the host system. This file is located in the directory {user.home} and the file name is returned in the method getFilename(). The following code snippet shows how these customizations are made in the sample application.

Example 9.4. Overridden Methods for Files

/**
 * This LauchTask specifies the preferences to use. 
 */
public class CustomPreferencesLaunchTask 
    extends LoadPreferencesLaunchTask
{

  /**
   * Constructor specifying the ApplicationFrame.
   * @param applicationFrame the ApplicationFrame
   */
  public CustomPreferencesLaunchTask(ApplicationFrame applicationFrame)
  {
    super(applicationFrame);
  }
    
  /**
   * Returns the path to the initial preference file relative
   * to the load class specified in method getLoadClass().
   */
  protected String getDefaultPreferences()
  {
    return "../resource/xml/defaultprefs.xml";
  }
    
  /**
   * All changes to the initial application settings get written
   * out to this file. It will be located in folder {user.home}.
   */
  protected String getFilename()
  {
    return "skeleton_example.prefs";
  }
    
  /**
   * The class to use to load the initial preference file.
   */
  protected Class getLoadClass()
  {
    return Main.class;
  }   
}

You may wish to refer to the Framework Manual for more information on this special topic.

Setting Up Commands

In order to create custom commands for a Skeleton-based application you can provide your own CommandLaunchTask implementation and override the perform() or postCreate() method. In our sample application we used the latter approach:

Example 9.5. Overridden postCreate() Method

// this method is called after the common commands were set up
public void postCreate(Object object)
{
    // register circular...
    new LayoutCommand(getApplicationFrame(), LAYOUT_CIRCULAR_IDS,
            GraphLayoutController.Circular);

    // ..., energy layout...
    new LayoutCommand(getApplicationFrame(), LAYOUT_ENERGY_IDS,
            GraphLayoutController.Energy);
    
    // ...and hierarchical top-bottom 
    new LayoutCommand(getApplicationFrame(), LAYOUT_HIERARCHICAL_IDS,
        GraphLayoutController.HierarchicalToBottom);        
    
    // toggle the edge types used in edge creation mode
    new ToggleAssignEdgeModeCommand(
            getApplicationFrame(),
            ToggleAssignEdgeModeCommand.MENU_EDIT_TOGGLE_EDGE_MODE);
}

Menu and Toolbar

The Skeleton creates a set of default menus called “File”, “Edit”, “View”, “Tools”, “Window” and “Help”. Each of these menus has a corresponding default command. To add a custom command to the menubar or toolbar of the application, you have to create it first. In the next step, you can add the command to the UI. For the sample application this is done in class CustomToolbarLaunchTask.

Example 9.6. Adding Commands to the Toolbar

// Add edge creation mode toggle button to toolbar.
public void postCreate(Object object)
{
  SwingToolBar toolbar= 
      (SwingToolBar) getApplicationFrame().getToolBars()[0];

  // Edge creation mode  
  final String ecIconURL ="icons/menu/edgeCreation.png";  
  addToToolBarToggleButton(
    toolbar,
    GUIItem.MENU_EDIT_TOGGLE_EDGE_CREATION,
    ecIconURL, ecIconURL, ecIconURL, ecIconURL,
    GUIResource.class);
  
  final String conModeIconURL = "../resource/images/con_mode.png";
  final String conModePressedIconURL = "../resource/images/ass_mode.png";
  
  addToToolBarToggleButton(
      toolbar,
      ToggleAssignEdgeModeCommand.MENU_EDIT_TOGGLE_EDGE_MODE,
      conModeIconURL, 
      conModePressedIconURL, 
      conModeIconURL, 
      conModePressedIconURL,
      Main.class);    
}

Element Definitions

The element definitions describing geometries, styles and rules are specified in a file which is loaded in the method perform() of class LoadElementDefinitionsLaunchTask.

Example 9.7. Loading Element Definitions

/**
 * Loads the graph element definitions from the resource file 
 * "geometry.xml", "styles.xml", "rules.xml" and "elements.xml". 
 * Afterwards the corresponding pools are filled with this definitions.
 */
public class LoadElementDefinitionsLaunchTask extends LaunchTask
{
  private final static String RES_PATH = "../resource/xml/";
  private ApplicationFrame appFrame;
  
  /**
   * Constructor specifying the ApplicationFrame to use.
   * @param applicationFrame Application context for this LaunchTask.
   */
  public LoadElementDefinitionsLaunchTask(
          ApplicationFrame applicationFrame)
  {
    this.appFrame= applicationFrame;
  }
  
  public String getName()
  {
    return "Loading Element Definitions";
  }

  public int run() throws Exception
  {
    GraphGeometryService.loadGeometriesFromResource(
      RES_PATH + "geometry.xml", ExampleApplicationFrame.class);

    StyleService.loadStylesFromResource(
        RES_PATH + "styles.xml", ExampleApplicationFrame.class);

    RuleRegistry rr =RuleRegistry.loadRulesFromResource(
        RES_PATH + "rules.xml", ExampleApplicationFrame.class);
    
    ((SwingApplicationFrame)appFrame).setRuleRegistry(rr);

    ElementService.loadElementsFromResource(
        RES_PATH + "elements.xml", ExampleApplicationFrame.class);
    
    return LAUNCHTASKSTATUS_SUCCESS;
  }
}

Populating the Repository Container

After the element definitions have been loaded, they can be placed inside a so-called RepositoryItemPanel. The following code snippet shows you how to do this. Please have a look at class CustomRepositoryLaunchTask in the sample application as well.

Example 9.8. 

// Create Custom Node Repository
RepositoryItemPanel nodeRepIP= new RepositoryItemPanel(2, 2);
nodeRepIP.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
    
final UIManager uiMan = getApplicationFrame().getUIManager();
    
VisualGraphObjectRepositoryItem tmpRepItem =
    new VisualGraphObjectRepositoryItem(
        getApplicationFrame().getDragContext(),
        ElementPool.get("AppNodeElement"),
        uiMan.getText("node.appl"));
tmpRepItem.setIcon(SwingUtil.getResourceIcon(
            "../resource/images/app_node.png",
            Main.class,
            tmpRepItem, false));
nodeRepIP.addJComponent(tmpRepItem);