Package de.sciss.common

Source Code of de.sciss.common.BasicMenuFactory$ActionURLViewer

/*
*  MenuFactory.java
*  (de.sciss.common package)
*
*  Copyright (c) 2004-2008 Hanns Holger Rutz. All rights reserved.
*
*  This software is free software; you can redistribute it and/or
*  modify it under the terms of the GNU General Public License
*  as published by the Free Software Foundation; either
*  version 2, june 1991 of the License, or (at your option) any later version.
*
*  This software is distributed in the hope that it will be useful,
*  but WITHOUT ANY WARRANTY; without even the implied warranty of
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
*  General Public License for more details.
*
*  You should have received a copy of the GNU General Public
*  License (gpl.txt) along with this software; if not, write to the Free Software
*  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*
*
*  For further information, please contact Hanns Holger Rutz at
*  contact@sciss.de
*
*
*  Changelog:
*    25-Jan-05  created from de.sciss.meloncillo.gui.MenuFactory
*    02-Aug-05  confirms to new document handler
*    15-Sep-05  openDocument checks if file is already open
*/

package de.sciss.common;

import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;

import javax.swing.Action;
import javax.swing.JFrame;
import javax.swing.KeyStroke;

import net.roydesign.app.AboutJMenuItem;
import net.roydesign.app.PreferencesJMenuItem;
import net.roydesign.app.QuitJMenuItem;
import net.roydesign.mac.MRJAdapter;

import de.sciss.app.AbstractWindow;
import de.sciss.app.DocumentEvent;
import de.sciss.app.DocumentHandler;
import de.sciss.app.DocumentListener;
import de.sciss.gui.AboutBox;
import de.sciss.gui.HelpFrame;
import de.sciss.gui.MenuAction;
import de.sciss.gui.MenuGroup;
import de.sciss.gui.MenuItem;
import de.sciss.gui.MenuRadioGroup;
import de.sciss.gui.MenuRadioItem;
import de.sciss.gui.MenuRoot;
import de.sciss.gui.MenuSeparator;
import de.sciss.gui.PathList;
import de.sciss.io.IOUtil;
import de.sciss.util.Flag;

/**
<code>JMenu</code>s cannot be added to more than
*  one frame. Since on MacOS there's one
*  global menu for all the application windows
*  we need to 'duplicate' a menu prototype.
*  Synchronizing all menus is accomplished
*  by using the same action objects for all
*  menu copies. However when items are added
*  or removed, synchronization needs to be
*  performed manually. That's the point about
*  this class.
<p>
*  There can be only one instance of <code>MenuFactory</code>
*  for the application, and that will be created by the
<code>Main</code> class.
*
@author    Hanns Holger Rutz
@version  0.71, 11-Jun-08
*
@see  de.sciss.eisenkraut.Main#menuFactory
*/
public abstract class BasicMenuFactory
extends MenuRoot
implements DocumentListener
{
  /*
   *  Value: String representing a list of paths
   *  of the recently used session files. See
   *  PathList and MenuFactory.actionOpenRecentClass.<br>
   *  Has default value: no!<br>
   *  Node: root
   */
  private static final String KEY_OPENRECENT= "recent"// string: path list

  /**
   *  <code>KeyStroke</code> modifier mask
   *  representing the platform's default
   *  menu accelerator (e.g. Apple-key on Mac,
   *  Ctrl on Windows).
   *
   *  @see  Toolkit#getMenuShortcutKeyMask()
   */
  public static final int        MENU_SHORTCUT        = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();

  private int              uniqueNumber    = 0;

  // ---- window menu management ----
  private MenuGroup          mgWindow;
  private MenuRadioGroup        mWindowRadioGroup;

  // ---- recent files management ----
  protected MenuGroup          mgRecent;
  protected final PathList      openRecentPaths;

  // ---- misc actions ----
  protected ActionOpenRecent      actionOpenRecent;
  private Action            actionClearRecent;
  private Action            actionCloseAll;

//  private final List          collGlobalKeyCmd  = new ArrayList();
 
//  private static final String      CLIENT_BG  = "de.sciss.gui.BG";  // radio button group

  private final BasicApplication  root;

  /**
   *  The constructor is called only once by
   *  the <code>Main</code> class and will create a prototype
   *  main menu from which all copies are
   *  derived.
   */
  public BasicMenuFactory( BasicApplication app )
  {
    super();
   
    root    = app;
 
    openRecentPaths = new PathList( 8, root.getUserPrefs(), KEY_OPENRECENT );
  }
 
  public void init()
  {
    createActions();
    createProtoType();

    // ---- listeners -----
   
    root.getDocumentHandler().addDocumentListener( this );
  }
 
  public BasicApplication getApplication()
  {
    return root;
  }
 
  public ProcessingThread closeAll( boolean force, Flag confirmed )
  {
    final DocumentHandler  dh  = root.getDocumentHandler();
    BasicDocument      doc;
    ProcessingThread    pt;

    while( dh.getDocumentCount() > 0 ) {
      doc  = (BasicDocument) dh.getDocument( 0 );
//if( doc.getFrame() == null ) {
//  System.err.println( "Yukk, no doc frame for "+doc.getDisplayDescr().file );
//  try {
//    Thread.sleep( 4000 );
//  } catch( InterruptedException e1 ) {}
//  confirmed.set( true );
//  return null;
//}
      pt  = doc.closeDocument( force, confirmed );
      if( pt == null ) {
        if( !confirmed.isSet() ) return null;
      } else {
        return pt;
      }
    }
    confirmed.set( true );
    return null;
  }

  private void createActions()
  {
    // --- file menu ---
    actionOpenRecent  = createOpenRecentAction( getResourceString( "menuOpenRecent" ), null );
    actionClearRecent = new ActionClearRecent( getResourceString( "menuClearRecent" ), null );
    actionCloseAll    = new ActionCloseAll( getResourceString( "menuCloseAll" ), null );
  }
 
  protected ActionOpenRecent createOpenRecentAction( String name, File path )
  {
    return new ActionOpenRecent( name, path );
  }
 
  // @todo  this should eventually read the tree from an xml file
  private void createProtoType()
  {
    MenuGroup        mg; //, smg;
//    MenuCheckItem      mci;
//    MenuRadioGroup      rg;
    Action          a;
//    BooleanPrefsMenuAction  ba;
//    IntPrefsMenuAction    ia;
//    Preferences        prefs;
   
    // Ctrl on Mac / Ctrl+Alt on PC
//    final int myCtrl = MENU_SHORTCUT == KeyEvent.CTRL_MASK ? KeyEvent.CTRL_MASK | KeyEvent.ALT_MASK : KeyEvent.CTRL_MASK;

    // --- file menu ---
   
    mg  = new MenuGroup( "file", getResourceString( "menuFile" ));
//    smg = new MenuGroup( "new", getResourceString( "menuNew" ));
//    smg.add( new MenuItem( "empty", actionNewEmpty ));
//    smg.add( new MenuItem( "fromSelection", getResourceString( "menuNewFromSelection" )));
//    mg.add( smg );
    mg.add( new MenuItem( "open", getOpenAction() ));
//    mg.add( new MenuItem( "openMultipleMono", actionOpenMM ));
    mgRecent = new MenuGroup( "openRecent", actionOpenRecent );
    if( openRecentPaths.getPathCount() > 0 ) {
      for( int i = 0; i < openRecentPaths.getPathCount(); i++ ) {
        mgRecent.add( new MenuItem( String.valueOf( uniqueNumber++ ), createOpenRecentAction( null, openRecentPaths.getPath( i ))));
      }
      actionOpenRecent.setPath( openRecentPaths.getPath( 0 ));
      actionOpenRecent.setEnabled( true );
      actionClearRecent.setEnabled( true );
    }
    mgRecent.addSeparator();
    mgRecent.add( new MenuItem( "clearRecent", actionClearRecent ));
    mg.add( mgRecent );
    if( root.getDocumentHandler().isMultiDocumentApplication() ) {
      mg.add( new MenuItem( "close", getResourceString( "menuClose" ), KeyStroke.getKeyStroke( KeyEvent.VK_W, MENU_SHORTCUT )));
      mg.add( new MenuItem( "closeAll", actionCloseAll ));
    }
    mg.add( new MenuSeparator() );
//    smg  = new MenuGroup( "import", getResourceString( "menuImport" ));
//    smg.add( new MenuItem( "markers", getResourceString( "menuImportMarkers" )));
//    mg.add( smg );
//    mg.addSeparator();
    mg.add( new MenuItem( "save", getResourceString( "menuSave" ), KeyStroke.getKeyStroke( KeyEvent.VK_S, MENU_SHORTCUT )));
    mg.add( new MenuItem( "saveAs", getResourceString( "menuSaveAs" ), KeyStroke.getKeyStroke( KeyEvent.VK_S, MENU_SHORTCUT + InputEvent.SHIFT_MASK )));
    mg.add( new MenuItem( "saveCopyAs", getResourceString( "menuSaveCopyAs" )));
//    mg.add( new MenuItem( "saveSelectionAs", getResourceString( "menuSaveSelectionAs" )));
    if( QuitJMenuItem.isAutomaticallyPresent() ) {
      root.getQuitJMenuItem().setAction( root.getQuitAction() );
    } else {
      mg.addSeparator();
      mg.add( new MenuItem( "quit", root.getQuitAction() ));
    }
    add( mg );

    // --- edit menu ---
    mg  = new MenuGroup( "edit", getResourceString( "menuEdit" ));
    mg.add( new MenuItem( "undo", getResourceString( "menuUndo" ), KeyStroke.getKeyStroke( KeyEvent.VK_Z, MENU_SHORTCUT )));
    mg.add( new MenuItem( "redo", getResourceString( "menuRedo" ), KeyStroke.getKeyStroke( KeyEvent.VK_Z, MENU_SHORTCUT + InputEvent.SHIFT_MASK )));
    mg.addSeparator();
    mg.add( new MenuItem( "cut", getResourceString( "menuCut" ), KeyStroke.getKeyStroke( KeyEvent.VK_X, MENU_SHORTCUT )));
    mg.add( new MenuItem( "copy", getResourceString( "menuCopy" ), KeyStroke.getKeyStroke( KeyEvent.VK_C, MENU_SHORTCUT )));
    mg.add( new MenuItem( "paste", getResourceString( "menuPaste" ), KeyStroke.getKeyStroke( KeyEvent.VK_V, MENU_SHORTCUT )));
    mg.add( new MenuItem( "clear", getResourceString( "menuClear" ), KeyStroke.getKeyStroke( KeyEvent.VK_BACK_SPACE, 0 )));
    mg.addSeparator();
    mg.add( new MenuItem( "selectAll", getResourceString( "menuSelectAll" ), KeyStroke.getKeyStroke( KeyEvent.VK_A, MENU_SHORTCUT )));
    a  = new ActionPreferences( getResourceString( "menuPreferences" ), KeyStroke.getKeyStroke( KeyEvent.VK_COMMA, MENU_SHORTCUT ));
    if( PreferencesJMenuItem.isAutomaticallyPresent() ) {
      root.getPreferencesJMenuItem().setAction( a );
    } else {
      mg.addSeparator();
      mg.add( new MenuItem( "preferences", a ));
    }
    add( mg );
   
    // --- window menu ---
    mWindowRadioGroup = new MenuRadioGroup();
    mgWindow = new MenuGroup( "window", getResourceString( "menuWindow" ));
//    mgWindow.add( new MenuItem( "ioSetup", new actionIOSetupClass( getResourceString( "frameIOSetup" ), null )));
//    mgWindow.addSeparator();
//    mgWindow.add( new MenuItem( "main", new actionShowWindowClass( getResourceString( "frameMain" ), null, Main.COMP_MAIN )));
//    mgWindow.add( new MenuItem( "observer", new actionObserverClass( getResourceString( "paletteObserver" ), KeyStroke.getKeyStroke( KeyEvent.VK_NUMPAD3, MENU_SHORTCUT ))));
//    mgWindow.add( new MenuItem( "ctrlRoom", new actionCtrlRoomClass( getResourceString( "paletteCtrlRoom" ), KeyStroke.getKeyStroke( KeyEvent.VK_NUMPAD2, MENU_SHORTCUT ))));
    mgWindow.addSeparator();
    mgWindow.add( new MenuItem( "collect", ((BasicWindowHandler) root.getWindowHandler()).getCollectAction() ));
    mgWindow.addSeparator();
    add( mgWindow );

    // --- help menu ---
    mg  = new MenuGroup( "help", getResourceString( "menuHelp" ));
    // this is pretty weird, but it works at least on german keyboards: command+questionmark is defaut help shortcut
    // on mac os x. KeyEvent.VK_QUESTION_MARK doesn't exist, plus apple's vm ignore german keyboard layout, therefore the
    // the question mark becomes a minus. however it's wrongly displayed in the menu...
    mg.add( new MenuItem( "manual", new ActionURLViewer( getResourceString( "menuHelpManual" ), KeyStroke.getKeyStroke( KeyEvent.VK_MINUS, MENU_SHORTCUT + InputEvent.SHIFT_MASK ), "index", false )));
    mg.add( new MenuItem( "shortcuts", new ActionURLViewer( getResourceString( "menuHelpShortcuts" ), null, "Shortcuts", false )));
    mg.addSeparator();
    mg.add( new MenuItem( "website", new ActionURLViewer( getResourceString( "menuHelpWebsite" ), null, getResourceString( "appURL" ), true )));
    a = new ActionAbout( getResourceString( "menuAbout" ), null );
    if( AboutJMenuItem.isAutomaticallyPresent() ) {
      root.getAboutJMenuItem().setAction( a );
    } else {
      mg.addSeparator();
      mg.add( new MenuItem( "about", a ));
    }

    add( mg );
   
    addMenuItems();
  }
 
  protected abstract void addMenuItems();
  public abstract void showPreferences();
  protected abstract Action getOpenAction();

  public abstract void openDocument( File f );

 
  // adds a file to the top of
  // the open recent menu of all menubars
  // and the prototype. calls
  // openRecentPaths.addPathToHead() and
  // thus updates the preferences settings
  // iteratively calls addRecent( JMenuBar, File, boolean )
  public void addRecent( File path )
  {
    int  i;
   
    i = openRecentPaths.indexOf( path );
    if( i == 0 ) return// this path is already topmost item
    if( (i == -1) && (openRecentPaths.getCapacity() == openRecentPaths.getPathCount()) ) {  // not in list and list too big now
      i = openRecentPaths.getPathCount() - 1// remove last item
    }
    if( i > 0 ) {  // remove an item
      openRecentPaths.remove( path );
      mgRecent.remove( i );
    }
    // add new item to top
    openRecentPaths.addPathToHead( path );
    actionOpenRecent.setPath( path );
    actionClearRecent.setEnabled( true );
    mgRecent.add( new MenuItem( String.valueOf( uniqueNumber++ ), createOpenRecentAction( null, path )), 0 );
  }
 
  public String getResourceString( String key )
  {
    return root.getResourceString( key );
  }

  public void addToWindowMenu( Action a )
  {
    final String id = "window" + String.valueOf( uniqueNumber++ );

//System.err.println( "add "+a.hashCode() );
    mgWindow.add( new MenuRadioItem( mWindowRadioGroup, id, a ));
  }
 
  public void removeFromWindowMenu( Action a )
  {
    final MenuRadioItem mri = (MenuRadioItem) mgWindow.getByAction( a );   
    mgWindow.remove( mri );
  }
 

  public void setSelectedWindow( Action a )
  {
//System.err.println( "select "+a.hashCode() );
    final MenuRadioItem mri = (MenuRadioItem) mgWindow.getByAction( a );
    mri.setSelected( true );
  }
 
// ---------------- DocumentListener interface ----------------
 
  public void documentAdded( DocumentEvent e )
  {
    if( !actionCloseAll.isEnabled() ) actionCloseAll.setEnabled( true );
  }
 
  public void documentRemoved( DocumentEvent e )
  {
    if( root.getDocumentHandler().getDocumentCount() == 0 ) {
      actionCloseAll.setEnabled( false );
    }
  }

  public void documentFocussed( de.sciss.app.DocumentEvent e ) { /* empty */ }

// ---------------- Action objects for file (session) operations ----------------

  // action for the Open-Recent menu
  protected class ActionOpenRecent
  extends MenuAction
  {
    private File  path;

    // new action with path set to null
    public ActionOpenRecent( String text, File path )
    {
      super( text == null ? IOUtil.abbreviate( path.getAbsolutePath(), 40 ) : text );
      setPath( path );
    }
   
    // set the path of the action. this
    // is the file that will be loaded
    // if the action is performed
    protected void setPath( File path )
    {
      this.path  = path;
      setEnabled( (path != null) && path.isFile() );
    }
   
    /**
     *  If a path was set for the
     *  action and the user confirms
     *  an intermitting confirm-unsaved-changes
     *  dialog, the new session will be loaded
     */
    public void actionPerformed( ActionEvent e )
    {
      openDocument( path );
    }
  } // class actionOpenRecentClass

  // action for clearing the Open-Recent menu
  private class ActionClearRecent
  extends MenuAction
  {
    protected ActionClearRecent( String text, KeyStroke shortcut )
    {
      super( text, shortcut );
      setEnabled( false );
    }
   
    public void actionPerformed( ActionEvent e )
    {
      for( int i = openRecentPaths.getPathCount() - 1; i >= 0; i-- ) {
        mgRecent.remove( i );
      }
      openRecentPaths.clear();
      actionOpenRecent.setPath( null );
      setEnabled( false );
    }
  } // class actionClearRecentClass
 
  // action for the Save-Session menu item
  private class ActionCloseAll
  extends MenuAction
  implements ProcessingThread.Listener
  {
    protected ActionCloseAll( String text, KeyStroke shortcut )
    {
      super( text, shortcut );
      setEnabled( false )// initially no docs open
    }

    public void actionPerformed( ActionEvent e )
    {
      perform();
    }
   
    private void perform()
    {
      final ProcessingThread pt = closeAll( false, new Flag( false ));
      if( pt != null ) {
        pt.addListener( this )// ok, let's save the shit and re-try to close all after that
        ((BasicDocument) pt.getClientArg( "doc" )).start( pt );
      }
    }
   
    public void processStarted( ProcessingThread.Event e ) { /* empty */ }

    // if the saving was successfull, we will call closeAll again
    public void processStopped( ProcessingThread.Event e )
    {
      if( e.isDone() ) {
        perform();
      }
    }
  }

  /**
   *  Action to be attached to
   *  the Preference item of the Edit menu.
   *  Will bring up the Preferences frame
   *  when the action is performed.
   */
  public class ActionPreferences
  extends MenuAction
  {
    protected ActionPreferences( String text, KeyStroke shortcut )
    {
      super( text, shortcut );
    }

    public void actionPerformed( ActionEvent e )
    {
      perform();
    }
   
    /**
     *  Opens the preferences frame
     */
    public void perform()
    {
      showPreferences();
    }
  }
 
// ---------------- Action objects for window operations ----------------

  // action for the About menu item
  private class ActionAbout
  extends MenuAction
  {
    protected ActionAbout( String text, KeyStroke shortcut )
    {
      super( text, shortcut );
    }

    /**
     *  Brings up the About-Box
     */
    public void actionPerformed( ActionEvent e )
    {
      JFrame aboutBox = (JFrame) getApplication().getComponent( AboutBox.COMP_ABOUTBOX );
   
      if( aboutBox == null ) {
        aboutBox = new AboutBox();
      }
      aboutBox.setVisible( true );
      aboutBox.toFront();
    }
  }

  // generic action for bringing up
  // a window which is identified by
  // a component object. the frame is
  // looked up using the Main's getComponent()
  // method.
  public class ActionShowWindow extends MenuAction
  {
    private final Object component;
 
    // @param   component   the key for getting the
    //            component using Main.getComponent()
    public ActionShowWindow( String text, KeyStroke shortcut, Object component )
    {
      super( text, shortcut );
     
      this.component = component;
    }

    /**
     *  Tries to find the component using
     *  the <code>Main</code> class' <code>getComponent</code>
     *  method. It does not instantiate a
     *  new object if the component is not found.
     *  If the window is already open, this
     *  method will bring it to the front.
     */
    public void actionPerformed( ActionEvent e )
    {
      AbstractWindow w = (AbstractWindow) getApplication().getComponent( component );
      if( w != null ) {
        w.setVisible( true );
        w.toFront();
      }
    }
  }

  // generic action for bringing up
  // a html document either in the
  // help viewer or the default web browser
  private class ActionURLViewer extends MenuAction
  {
    private final String  theURL;
    private final boolean  openWebBrowser;
 
    // @param  theURL      what file to open ; when using the
    //              help viewer, that's the relative help file name
    //              without .html extension. when using web browser,
    //              that's the complete URL!
    // @param   openWebBrowser  if true, use the default web browser,
    //              if false use internal help viewer
    protected ActionURLViewer( String text, KeyStroke shortcut, String theURL, boolean openWebBrowser )
    {
      super( text, shortcut );
     
      this.theURL      = theURL;
      this.openWebBrowser  = openWebBrowser;
    }

    /**
     *  Tries to find the component using
     *  the <code>Main</code> class' <code>getComponent</code>
     *  method. It does not instantiate a
     *  new object if the component is not found.
     *  If the window is already open, this
     *  method will bring it to the front.
     */
    public void actionPerformed( ActionEvent e )
    {
      if( openWebBrowser ) {
        try {
          MRJAdapter.openURL( theURL );
        }
        catch( IOException e1 ) {
          BasicWindowHandler.showErrorDialog( null, e1, NAME );
        }
      } else {
        HelpFrame.openViewerAndLoadHelpFile( theURL );
      }
    }
  }
}
TOP

Related Classes of de.sciss.common.BasicMenuFactory$ActionURLViewer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.