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}