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}