    001package org.vishia.gral.base;
    002
    003import java.util.Map;
    004
    005import org.vishia.gral.ifc.GralUserAction;
    006import org.vishia.util.Assert;
    007import org.vishia.util.IndexMultiTable;
    008
    009/**Super class of Menu root wrappers of the graphic implementation layer independent of the implementation graphic.
    010 * This class is the super class either for the menu bar of a window
    011 * or for the context menu for any widget.
    012 * <br><br>
    013 * It refers the window or the widget. The graphical implementation layer associates
    014 * an event listener to any menu item created with {@link #addMenuItem(String, String, GralUserAction)}.
    015 * That event listener invokes the {@link GralUserAction} given as parameter.
    016 * <ul>
    017 * <li>To add a menu bar to a window call {@link GralWindow#addMenuBarItemGThread(String, String, GralUserAction)}.
    018 * <li>To get or create a context menu to a widget call {@link GralWidget#getContextMenu()}
    019 * <li>To add a menu item to the context menu or any menu item call
    020 *   {@link #addMenuItem(String, String, GralUserAction)}.
    021 * </ul>
    022 * One should call this methods only in the graphic thread in the initalizing phase.
    023 * <br><br>
    024 * The basic idea for menus is: A path is used instead a tree of menus.
    025 * To add any new menu item to a window's pull-down menu bar, use
    026 * {@link GralWindow#addMenuBarItemGThread(String, String, GralUserAction)}.
    027 * You needn't have knowledge about the tree structure of the menu.
    028 * The String sMenuPath should consist of the parts of the menu tree,
    029 * for example "&File/&Check/rest&Ore". For this example a menu bar entry "File" is created
    030 * or the yet existing is used. Then the sub menu entry "Check" is created or the existing is used.
    031 * There the entry "restOre" is created with 'O' as hot-key.
    032 *
    033 *
    034 *
    035 * @author Hartmut Schorrig
    036 *
    037 */
    038public class GralMenu //extends GralWidget
    039{
    040
    041  /**Version, history and license.
    042   * <ul>
    043   * <li>2016-11-04 Hartmut new {@link MenuEntry#widgg} to transport the widget to the implementation widget.
    044   * <li>2016-09 Hartmut redesign: Now the GralMenu is an implementation-independent class.
    045   *   It contains references to the implementation menu instances and supports creation of them if the graphic implementation is present.
    046   *   {@link _GraphicImpl} is now the base class for the {@link org.vishia.gral.swt.SwtMenu}.
    047   * <li>2014-06-23 Hartmut new: Now the {@link #widgg} is stored here. It is used for all sub menus
    048   *  to refer it for calling {@link GralUserAction#exec(int, org.vishia.gral.ifc.GralWidget_ifc, Object...)}.
    049   *  The {@link #addMenuItem(String, String, GralUserAction)} is now deprecated, the String is not used.
    050   *  But only that method creates an instance of {@link org.vishia.gral.swt.SwtWidgetMenu} which is deprecated too
    051   *  and only used to held a reference to the GralWidget.
    052   *  The {@link #addMenuItem(String, GralUserAction)} is the new one to use.
    053   *
    054   * <li>2012-03-17 Hartmut new: {@link #addMenuItem(String, String, GralUserAction)} returns now
    055   *   a {@link GralWidget}. It is necessary to add some information to the menu-widget, which can be used
    056   *   if the {@link GralUserAction#userActionGui(int, GralWidget, Object...)} for this menu is called.
    057   *   The second param of this method is that menu-GralWidget.
    058   * <li>2011-11-00 Hartmut created, menus are present not only in the main window. Context menu etc.
    059   * <ul>
    060   *
    061   * <b>Copyright/Copyleft</b>:
    062   * For this source the LGPL Lesser General Public License,
    063   * published by the Free Software Foundation is valid.
    064   * It means:
    065   * <ol>
    066   * <li> You can use this source without any restriction for any desired purpose.
    067   * <li> You can redistribute copies of this source to everybody.
    068   * <li> Every user of this source, also the user of redistribute copies
    069   *    with or without payment, must accept this license for further using.
    070   * <li> But the LPGL ist not appropriate for a whole software product,
    071   *    if this source is only a part of them. It means, the user
    072   *    must publish this part of source,
    073   *    but don't need to publish the whole source of the own product.
    074   * <li> You can study and modify (improve) this source
    075   *    for own using or for redistribution, but you have to license the
    076   *    modified sources likewise under this LGPL Lesser General Public License.
    077   *    You mustn't delete this Copyright/Copyleft inscription in this source file.
    078   * </ol>
    079   * If you are indent to use this sources without publishing its usage, you can get
    080   * a second license subscribing a special contract with the author.
    081   *
    082   * @author Hartmut Schorrig = hartmut.schorrig@vishia.de
    083   *
    084   *
    085   */
    086  public final static int version = 20120317;
    087
    088  /**This class wraps a menu entry of the implementation. It knows all sub menu entries
    089   * in a implementation-independent way. So searching entries with given name is possible.
    090   */
    091  public static class MenuEntry
    092  {
    093    public String name;
    094
    095    public char cAccelerator;
    096
    097    /**If it is a superior menu item, the menu below. Else null. */
    098    public Object menuImpl;
    099
    100    /**All menu entries of this menu item. */
    101    public Map<String, MenuEntry> subMenu;
    102
    103    public GralWidget widgg;
    104
    105    public GralUserAction action;
    106
    107    public MenuEntry(){}
    108  }
    109
    110  protected Map<String, MenuEntry> menus = new IndexMultiTable<String, MenuEntry>(IndexMultiTable.providerString);
    111
    112
    113  _GraphicImpl _impl;
    114
    115
    116  public GralMenu() {
    117  }
    118
    119
    120  /**Adds any menu item
    121   * @param name name of the menu, it is used as widget name.
    122   * @param sMenuPath Menu position. Use slash as separator, use & for hot key.
    123   *   For example "&edit/&search/co&ntinue" creates a menu 'edit' or uses the existing one in the top level (menu bar),
    124   *   then creates the search menu item as pull down in menu bar, and then 'continue' with 'n' as hot key as sub-menu.
    125   *   It is stored in {@link GralWidget#sDataPath}
    126   * @param action called on menu activation.
    127   */
    128  public final void addMenuItem(String name, String sMenuPath, GralUserAction action) {
    129    addMenuItem(null, name, sMenuPath, action);
    130    //if(_impl !=null) { _impl.addMenuItemGthread(name, sMenuPath, action); }
    131  }
    132
    133
    134
    135  /**Adds with a given widget.
    136   * Note the widget is in difference with the {@link #widgg} of the constructor.
    137   * Check what is happen. Therefore:
    138   * @param widggMenu the widget can be used to add any {@link GralWidget#setContentInfo(Object)} etc.
    139   *   It is provided in the action method.
    140   * @param name
    141   * @param sMenuPath
    142   * @param action
    143   */
    144  public final void addMenuItem(GralWidget widggP, String nameWidgg, String sMenuPath, GralUserAction action){
    145    String[] names = sMenuPath.split("/");
    146    Map<String, GralMenu.MenuEntry> menustore = this.menus;
    147    int ii;
    148    MenuEntry menuEntry = null;
    149    for(ii=0; ii<names.length; ++ii){
    150      //search all pre-menu entries before /. It may be existing, otherwise create it.
    151      String name = names[ii];
    152      final char cAccelerator;
    153      final int posAccelerator = name.indexOf('?');
    154      if(posAccelerator >=0){
    155        cAccelerator = Character.toUpperCase(name.charAt(posAccelerator));
    156        name = name.replace("&", "");
    157      } else {
    158        cAccelerator = 0;
    159      }
    160      menuEntry = menustore.get(name);
    161      if(menuEntry == null){
    162        //create it.
    163        menuEntry = new MenuEntry();
    164        menustore.put(name, menuEntry);
    165        menuEntry.name = name;
    166        menuEntry.cAccelerator = cAccelerator;
    167        if(ii < names.length -1) {
    168          menuEntry.subMenu = new IndexMultiTable<String, MenuEntry>(IndexMultiTable.providerString);
    169        }
    170      }
    171      menustore = menuEntry.subMenu;
    172    }
    173    assert(menuEntry !=null);  //null if sMenuPath will be empty.
    174    menuEntry.action = action;  //store to the last one.
    175    menuEntry.widgg = widggP;
    176    if(_impl !=null) { _impl._implMenu(); }
    177
    178  }
    179
    180
    181
    182
    183
    184  /**Adds any menu item.
    185   * @param sMenuPath Menu position. Use slash as separator, use & for hot key.
    186   *   For example "&edit/&search/co&ntinue" creates a menu 'edit' or uses the existing one in the top level (menu bar),
    187   *   then creates the search menu item as pull down in menu bar, and then 'continue' with 'n' as hot key as sub-menu.
    188   *   It is stored in {@link GralWidget#sDataPath}
    189   * @param action called on menu activation.
    190   */
    191  public final void addMenuItem(String sMenuPath, GralUserAction gralAction){
    192    //if(_impl !=null) { _impl.addMenuItemGthread(sMenuPath, gralAction); }
    193    addMenuItem(null, null, sMenuPath, gralAction);;
    194  }
    195
    196  public final void setVisible(){
    197    if(_impl !=null) { _impl.setVisible(); }
    198  }
    199
    200
    201  public boolean hasImplementation(){ return _impl !=null; }
    202
    203  /**Returns the implementation instance for the menu. */
    204  public final Object getMenuImpl(){
    205    if(_impl !=null) { return _impl.getMenuImpl(); }
    206    else return null;
    207  }
    208
    209
    210
    211
    212
    213  public abstract class _GraphicImpl
    214  {
    215
    216    protected final GralWidget widgg;
    217
    218
    219
    220  /**Creates a new menu wrapper. This is called as super(widgg, mng) in the derived class.
    221   * @param widgg The gral widget which is the parent. If it is a context menu it is that
    222   *   widget where {@link GralWidget#getContextMenu()} was called.
    223   *   If it is is a menu bar, the window is used.
    224   * @param mng The mng
    225   */
    226    protected _GraphicImpl(GralWidget widgg)
    227  {
    228      this.widgg = widgg;
    229      GralMenu.this._impl = this;
    230  }
    231
    232
    233
    234    /**Creates all necessary new implementation instances for this GralMenu. */
    235    public final void _implMenu() {
    236      _implMenu(getMenuImpl(), menus);
    237    }
    238
    239
    240    private final void _implMenu(Object parentMenu, Map<String, MenuEntry> menusP) {
    241      for(Map.Entry<String, MenuEntry> e: menusP.entrySet()) {
    242        MenuEntry child = e.getValue();
    243        if(child.menuImpl == null) {
    244          _implMenuItem(parentMenu, child);
    245        }
    246        if(child.subMenu !=null) {
    247          _implMenu(child.menuImpl, child.subMenu);
    248        }
    249      }
    250    }
    251
    252
    253
    254    /**This method should be implemented in the implementation layer.
    255     * @param oParentMenu
    256     * @param gralEntry
    257     */
    258    protected abstract void _implMenuItem(Object oParentMenu, GralMenu.MenuEntry gralEntry);
    259
    260  /**Adds any menu item
    261   * @param name name of the menu, it is used as widget name.
    262   * @param sMenuPath Menu position. Use slash as separator, use & for hot key.
    263   *   For example "&edit/&search/co&ntinue" creates a menu 'edit' or uses the existing one in the top level (menu bar),
    264   *   then creates the search menu item as pull down in menu bar, and then 'continue' with 'n' as hot key as sub-menu.
    265   *   It is stored in {@link GralWidget#sDataPath}
    266   * @param action called on menu activation.
    267   */
    268  //public abstract void addMenuItemGthread(String name, String sMenuPath, GralUserAction action);
    269
    270
    271  /**Adds with a given widget.
    272   * Note the widget is in difference with the {@link #widgg} of the constructor.
    273   * Check what is happen. Therefore:
    274   * @deprecated The {@link GralWidget#getContextMenu()} creates a menu for the graphic implementation layer widget
    275   *   and assigns it to the widget as GralMenu. The GralMenu has the association {@link #widgg} to the widget already.
    276   *   In the adequate case {@link GralWindow#getMenuBar()} adds the menu to the window and stores the window's reference
    277   *   in {@link #widgg}. That is proper to use. This method was create before the {@link #widgg} are set
    278   *   in this constructor.
    279   * @param widggMenu the widget can be used to add any {@link GralWidget#setContentInfo(Object)} etc.
    280   *   It is provided in the action method.
    281   * @param name
    282   * @param sMenuPath
    283   * @param action
    284   */
    285  @Deprecated
    286  //public abstract void addMenuItemGthread(GralWidget widggMenu, String name, String sMenuPath, GralUserAction action);
    287
    288
    289
    290
    291
    292  /**Adds any menu item.
    293   * @param sMenuPath Menu position. Use slash as separator, use & for hot key.
    294   *   For example "&edit/&search/co&ntinue" creates a menu 'edit' or uses the existing one in the top level (menu bar),
    295   *   then creates the search menu item as pull down in menu bar, and then 'continue' with 'n' as hot key as sub-menu.
    296   *   It is stored in {@link GralWidget#sDataPath}
    297   * @param action called on menu activation.
    298   */
    299  //public abstract void addMenuItemGthread(String sMenuPath, GralUserAction gralAction);
    300
    301  public abstract void setVisible();
    302
    303  /**Returns the implementation instance for the menu. */
    304  public abstract Object getMenuImpl();
    305  }
    306}