001package org.vishia.gral.cfg; 002 003import java.io.File; 004import java.io.FileInputStream; 005import java.io.IOException; 006import java.io.InputStream; 007import java.text.ParseException; 008import java.util.LinkedList; 009import java.util.List; 010import java.util.Map; 011import java.util.Set; 012import java.util.TreeMap; 013 014import org.vishia.gral.base.GralCurveView; 015import org.vishia.gral.base.GralMouseWidgetAction_ifc; 016import org.vishia.gral.base.GralPos; 017import org.vishia.gral.base.GralWidget; 018import org.vishia.gral.base.GralWindow; 019import org.vishia.gral.cfg.GralCfgElement; 020import org.vishia.gral.ifc.GralColor; 021import org.vishia.gral.ifc.GralMngBuild_ifc; 022import org.vishia.gral.ifc.GralPoint; 023import org.vishia.gral.ifc.GralUserAction; 024import org.vishia.gral.ifc.GralWidget_ifc; 025import org.vishia.msgDispatch.LogMessage; 026import org.vishia.util.CalculatorExpr; 027import org.vishia.util.Debugutil; 028import org.vishia.util.KeyCode; 029 030public class GralCfgBuilder 031{ 032 033 /**Version and history 034 * <ul> 035 * <li>2015-04-27 Hartmut chg: {@link #buildGui(LogMessage, int)} Now regards only one panel in the window, not only tabbed panels. 036 * <li>2014-02-24 Hartmut new element help now also in config. 037 * <li>2012-09-17 Hartmut chg: showMethod now split functionName and parameters. The function name is used to get 038 * the {@link GralUserAction} for {@link GralWidget#setActionShow(GralUserAction, String[])}. The parameter are stored 039 * in {@link GralWidget#cfg} as {@link GralWidget.ConfigData#showParam}. 040 * <li>2011-05-00 Hartmut created, the old ZbnfCfg.. class is obsolte now. 041 * </ul> 042 * 043 * <b>Copyright/Copyleft</b>:<br> 044 * For this source the LGPL Lesser General Public License, 045 * published by the Free Software Foundation is valid. 046 * It means: 047 * <ol> 048 * <li> You can use this source without any restriction for any desired purpose. 049 * <li> You can redistribute copies of this source to everybody. 050 * <li> Every user of this source, also the user of redistribute copies 051 * with or without payment, must accept this license for further using. 052 * <li> But the LPGL is not appropriate for a whole software product, 053 * if this source is only a part of them. It means, the user 054 * must publish this part of source, 055 * but doesn't need to publish the whole source of the own product. 056 * <li> You can study and modify (improve) this source 057 * for own using or for redistribution, but you have to license the 058 * modified sources likewise under this LGPL Lesser General Public License. 059 * You mustn't delete this Copyright/Copyleft inscription in this source file. 060 * </ol> 061 * If you intent to use this source without publishing its usage, you can get 062 * a second license subscribing a special contract with the author. 063 * 064 * @author Hartmut Schorrig = hartmut.schorrig@vishia.de 065 */ 066 public static final int version = 20120303; 067 068 private final GralCfgData cfgData; 069 070 private final GralMngBuild_ifc gralMng; 071 072 /**The current directory is that directory, where the config file is located. 073 * It is used if other files are given with relative path.*/ 074 private final File currentDir; 075 076 private final Map<String, String> indexAlias = new TreeMap<String, String>(); 077 078 079 public GralCfgBuilder(GralCfgData cfgData, GralMngBuild_ifc gui, File currentDir) 080 { 081 this.cfgData = cfgData; 082 this.gralMng = gui; 083 this.currentDir = currentDir; 084 if(currentDir !=null) { 085 String sCanonicalPath = org.vishia.util.FileSystem.getCanonicalPath(currentDir); 086 indexAlias.put("cfg", sCanonicalPath); 087 } 088 } 089 090 091 public GralCfgElement XXXnewCfgElement(GralCfgElement previous) 092 { //GuiCfgElement cfge = new GuiCfgElement(cfgData); 093 GralCfgElement cfge = previous.clone(); 094 cfge.next = previous.next; 095 cfge.previous = previous; 096 previous.next = cfge; 097 return cfge; 098 } 099 100 /**Builds the appearance of the whole graphic with the given {@link GralCfgData} cfgData. 101 * Calls {@link #buildPanel(org.vishia.gral.cfg.GralCfgPanel)} for the any panel 102 * in the {@link GralCfgData#idxPanels}. Fills the panels one after another. 103 * 104 * @param log maybe null, errors and warnings are written 105 * @param msgIdent The message identification for output. 106 * @return null if ok, elsewhere the error hints which maybe written to log too, one per line. 107 */ 108 /** 109 * @param log 110 * @param msgIdent 111 * @return 112 */ 113 public String buildGui(LogMessage log, int msgIdent) 114 { 115 String sError = null; 116 gralMng.getReplacerAlias().addDataReplace(cfgData.dataReplace); 117 118 Set<Map.Entry<String, GralCfgPanel>> setIdxPanels = cfgData.getPanels(); 119 if(setIdxPanels.size()==0){ 120 //gralMng.selectPanel(cfgData.actPanel); 121 //==================> 122 String sErrorPanel = buildPanel(cfgData.actPanel); 123 if(sErrorPanel !=null){ 124 if(log !=null){ 125 log.sendMsg(msgIdent, "GralCfgBuilder - cfg error; %s", sErrorPanel); 126 } 127 if(sError == null){ sError = sErrorPanel; } 128 else { sError += "\n" + sErrorPanel; } 129 } 130 } else { 131 //some panels are given, therefore selects given panels by name or create tabbed panels. 132 for(Map.Entry<String, GralCfgPanel> panelEntry: setIdxPanels){ //cfgData.idxPanels.entrySet()){ 133 GralCfgPanel cfgPanel = panelEntry.getValue(); 134 if(cfgPanel.windTitle !=null) { 135 Debugutil.stop(); 136 GralWindow wind = new GralWindow(cfgPanel.windPos, cfgPanel.name, cfgPanel.windTitle, GralWindow.windOnTop | GralWindow.windResizeable); 137 wind.createImplWidget_Gthread(); 138 } 139 else { 140 //A tab in the main window 141 //==================> 142 gralMng.selectPanel(cfgPanel.name); 143 } 144 String sErrorPanel = buildPanel(cfgPanel); 145 if(sErrorPanel !=null){ 146 if(log !=null){ 147 log.sendMsg(msgIdent, "GralCfgBuilder - cfg error; %s", sErrorPanel); 148 } 149 if(sError == null){ sError = sErrorPanel; } 150 else { sError += "\n" + sErrorPanel; } 151 } else { 152 stop(); 153 } 154 } 155 } 156 return sError; 157 } 158 159 160 /**Builds the appearance of one panel with the given {@link GralCfgPanel} cfgData. 161 * @param cfgDataPanel 162 * @return null if ok, elsewhere the error hints, one per line. 163 */ 164 public String buildPanel(GralCfgPanel cfgDataPanel) 165 { 166 String sError = null; 167 for(GralCfgElement cfge: cfgDataPanel.listElements){ 168 //=================>> 169 String sErrorWidgd; 170 try{ sErrorWidgd = buildWidget(cfge); } 171 catch(ParseException exc) { sErrorWidgd = exc.getMessage(); } 172 if(sErrorWidgd !=null){ 173 if(sError == null){ sError = sErrorWidgd; } 174 else { sError += "\n" + sErrorWidgd; } 175 } 176 } 177 return sError; 178 } 179 180 181 182 /**Builds the graphical widget inclusive its {@link GralWidget} and place it in the GUI. 183 * @param cfge The configuration element data read from config file or set from the GUI-editor. 184 * @return null if OK, an error String for a user info message on warning or error. 185 * It is possible that a named user action is not found etc. 186 */ 187 public String buildWidget(GralCfgElement cfge) 188 throws ParseException 189 { 190 String sError = null; 191 cfge.setPos(gralMng); 192 if(cfge.widgetType.type !=null){ 193 GralCfgData.GuiCfgWidget typeData = cfgData.idxTypes.get(cfge.widgetType.type); 194 if(typeData == null){ 195 throw new IllegalArgumentException("GralCfgBuilder.buildWidget - unknown type; " + cfge.widgetType.type + "; in " + cfge.content); 196 } else { 197 cfge.widgetType.setFromType(typeData); 198 } 199 } 200 201 GralWidget widgd = null; 202 String sName = cfge.widgetType.name; 203 if(sName !=null && sName.equals("msgOfDay")) 204 stop(); 205 206 if(sName ==null && cfge.widgetType.text !=null ){ sName = cfge.widgetType.text; } //text of button etc. 207 if(sName ==null && cfge.widgetType.prompt !=null){ sName = cfgData.actPanel.name + "/" + cfge.widgetType.prompt; } //the prompt as name 208 //the name may be null, then the widget is not registered. 209 // 210 211 String sDataPath = cfge.widgetType.data; 212 //text is the default for a datapath. 213 if(sDataPath ==null && cfge.widgetType.text !=null){ sDataPath = cfge.widgetType.text; } 214 /* 215 if(sDataPath !=null){ 216 //replace a prefix before ':' with its replacement, if the prefix is found. 217 //This is a possibility to shorten data path. 218 int posSep = sDataPath.indexOf(':'); 219 if(posSep > 0){ 220 String sPre = cfge.itsCfgData.dataReplace.get(sDataPath.substring(0, posSep)); 221 if(sPre !=null){ 222 sDataPath = sPre + sDataPath.substring(posSep+1); 223 } 224 } else { 225 String sReplace = cfge.itsCfgData.dataReplace.get(sDataPath); 226 if(sReplace !=null){ 227 sDataPath = sReplace; 228 } 229 } 230 } 231 */ 232 // 233 final GralUserAction userAction; //, mouseAction; 234 final String[] sUserActionArgs; 235 GralWidget_ifc.ActionChangeWhen whenUserAction = null; 236 if(cfge.widgetType.userAction !=null){ 237 String sUserAction = cfge.widgetType.userAction; 238 if(sUserAction.startsWith("@")){ 239 int posEnd = sUserAction.indexOf(':'); 240 if(posEnd < 0) { sError = "GuiCfgBuilder - @m: ':' not found. "; sUserAction = null; } 241 else { 242 for(int ix = 1; ix < posEnd; ++ix){ 243 char whatMouseKey = sUserAction.charAt(ix); 244 switch(whatMouseKey){ 245 case (char)(KeyCode.mouse1Double & 0xff): whenUserAction = GralWidget_ifc.ActionChangeWhen.onMouse1Double; break; 246 } 247 } 248 sUserAction = sUserAction.substring(posEnd+1).trim(); 249 } 250 } 251 if(sUserAction !=null) { 252 String[] sMethod = CalculatorExpr.splitFnNameAndParams(sUserAction); 253 userAction = gralMng.getRegisteredUserAction(sMethod[0]); 254 if(userAction == null){ 255 sError = "GuiCfgBuilder - user action ignored because not found: " + cfge.widgetType.userAction; 256 sUserActionArgs = null; 257 } else { 258 sUserActionArgs = sMethod[1] == null ? null : CalculatorExpr.splitFnParams(sMethod[1]); 259 } 260 261 } else { userAction = null; sUserActionArgs = null; } 262 } else { userAction = null; sUserActionArgs = null; } 263 264 265 266 267 /* 268 if(cfge.widgetType.mouseAction !=null){ 269 mouseAction = gralMng.getRegisteredUserAction(cfge.widgetType.mouseAction); 270 if(mouseAction == null){ 271 sError = "GuiCfgBuilder - mouse action ignored because not found: " + cfge.widgetType.mouseAction; 272 } 273 } else { mouseAction = null; } 274 */ 275 // 276 if(cfge.widgetType instanceof GralCfgData.GuiCfgButton){ 277 GralCfgData.GuiCfgButton wButton = (GralCfgData.GuiCfgButton) cfge.widgetType; 278 if(wButton.bSwitch){ 279 widgd = gralMng.addSwitchButton(cfge.widgetType.name, userAction, cfge.widgetType.cmd 280 , cfge.widgetType.data, cfge.widgetType.text, cfge.widgetType.color0.color, cfge.widgetType.color1.color); 281 } else { 282 widgd = gralMng.addButton(cfge.widgetType.name, userAction, cfge.widgetType.cmd, cfge.widgetType.data, cfge.widgetType.text); 283 } 284 } else if(cfge.widgetType instanceof GralCfgData.GuiCfgText){ 285 GralCfgData.GuiCfgText wText = (GralCfgData.GuiCfgText)cfge.widgetType; 286 final int colorValue; 287 if(wText.color0 !=null){ colorValue = gralMng.getColorValue(wText.color0.color); } 288 //else if(wText.colorName !=null){ colorValue = gralMng.getColorValue(wText.colorName.color);} 289 else{ colorValue = 0; } //black 290 cfge.widgetType.color0 = null; //it is used, don't set background. 291 widgd = gralMng.addText(cfge.widgetType.text); 292 } else if(cfge.widgetType instanceof GralCfgData.GuiCfgLed){ 293 GralCfgData.GuiCfgLed ww = (GralCfgData.GuiCfgLed)cfge.widgetType; 294 widgd = gralMng.addLed(sName, sDataPath); 295 296 } else if(cfge.widgetType instanceof GralCfgData.GuiCfgImage){ 297 GralCfgData.GuiCfgImage wImage = (GralCfgData.GuiCfgImage)cfge.widgetType; 298 File fileImage = new File(currentDir, wImage.file_); 299 if(fileImage.exists()){ 300 try{ InputStream imageStream = new FileInputStream(fileImage); 301 gralMng.addImage(sName, imageStream, 10, 20, "?cmd"); 302 imageStream.close(); 303 } catch(IOException exc){ } 304 305 } 306 307 } else if(cfge.widgetType instanceof GralCfgData.GuiCfgShowField){ 308 //GuiCfgData.GuiCfgShowField wShow = (GuiCfgData.GuiCfgShowField)cfge.widgetType; 309 //char cPromptPosition = cfge.widgetType.promptPosition ==null || cfge.widgetType.promptPosition.length() <1 310 // ? '.' : cfge.widgetType.promptPosition.charAt(0); 311 widgd = gralMng.addTextField(sName, cfge.widgetType.editable, cfge.widgetType.prompt, cfge.widgetType.promptPosition); 312 widgd.setDataPath(sDataPath); 313 } else if(cfge.widgetType instanceof GralCfgData.GuiCfgInputFile){ 314 GralCfgData.GuiCfgInputFile widgt = (GralCfgData.GuiCfgInputFile)cfge.widgetType; 315 final String dirMask; 316 if(widgt.data !=null){ 317 dirMask = replaceAlias(widgt.data); 318 } else { dirMask = ""; } 319 widgd = gralMng.addFileSelectField(sName, null, dirMask, null, "t"); 320 widgd.setDataPath(sDataPath); 321 } else if(cfge.widgetType instanceof GralCfgData.GuiCfgTable){ 322 GralCfgData.GuiCfgTable widgt = (GralCfgData.GuiCfgTable)cfge.widgetType; 323 List<Integer> columns = widgt.getColumnWidths(); 324 int zColumn = columns.size(); 325 int[] aCol = new int[zColumn]; 326 int ix = -1; 327 for(Integer column: columns){ aCol[++ix] = column; } 328 widgd = gralMng.addTable(sName, widgt.height, aCol); 329 widgd.setDataPath(sDataPath); 330 } else if(cfge.widgetType instanceof GralCfgData.GuiCfgCurveview){ 331 GralCfgData.GuiCfgCurveview widgt = (GralCfgData.GuiCfgCurveview)cfge.widgetType; 332 int nrofTracks = widgt.lines.size(); 333 GralCurveView widgc = gralMng.addCurveViewY(sName, widgt.nrofPoints, null); 334 widgc.activate(widgt.activate); 335 for(GralCfgData.GuiCfgCurveLine line: widgt.lines){ 336 String sDataPathLine = line.data; 337 final GralColor colorLine; 338 if(line.color0 !=null){ 339 colorLine = GralColor.getColor(line.color0.color); 340 } else { 341 colorLine = GralColor.getColor(line.colorValue); //maybe 0 = black if not given. 342 } 343 widgc.addTrack(line.name, sDataPathLine, colorLine, 0, line.nullLine, line.scale, line.offset); 344 } 345 widgd = widgc; 346 } else { 347 switch(cfge.widgetType.whatIs){ 348 case 'T':{ 349 widgd = gralMng.addTextField(sName, true, cfge.widgetType.prompt, cfge.widgetType.promptPosition); 350 widgd.setDataPath(sDataPath); 351 } break; 352 case 't':{ 353 char promptPosition = cfge.widgetType.promptPosition == null ? '.' : cfge.widgetType.promptPosition.charAt(0); 354 widgd = gralMng.addTextBox(sName, true, cfge.widgetType.prompt, promptPosition); 355 widgd.setDataPath(sDataPath); 356 } break; 357 case 'U':{ 358 widgd = gralMng.addValueBar(sName, sDataPath); 359 } break; 360 case 'I':{ 361 GralCfgData.GuiCfgLine cfgLine = (GralCfgData.GuiCfgLine)cfge.widgetType; 362 //copy the points from the type GuiCfgCoord to GralPoint 363 List<GralPoint> points = new LinkedList<GralPoint>(); 364 for(GralCfgData.GuiCfgCoord coord: cfgLine.coords){ 365 points.add(new GralPoint(coord.x, coord.y)); 366 } 367 gralMng.addLine(GralColor.getColor(cfgLine.color0.color), points); 368 } break; 369 default: { 370 widgd = null; 371 }//default 372 } 373 374 } 375 if(widgd !=null){ 376 //set common attributes for widgets: 377 //widgd.pos = gui.getPositionInPanel(); 378 String sShowMethod1 = cfge.widgetType.showMethod; 379 if(sShowMethod1 !=null){ 380 String[] sShowMethod = CalculatorExpr.splitFnNameAndParams(sShowMethod1); 381 382 GralUserAction actionShow = gralMng.getRegisteredUserAction(sShowMethod[0]); 383 if(actionShow == null){ 384 sError = "GuiCfgBuilder - show method not found: " + sShowMethod[0]; 385 } else { 386 String[] param = sShowMethod[1] == null ? null : CalculatorExpr.splitFnParams(sShowMethod[1]); 387 widgd.setActionShow(actionShow, param); 388 } 389 } 390 widgd.sCmd = cfge.widgetType.cmd; 391 /* 392 String sCmd = cfge.widgetType.cmd; 393 if(sCmd !=null){ 394 GralUserAction actionCmd = gralMng.getRegisteredUserAction(sCmd); 395 if(actionCmd == null){ 396 sError = "GuiCfgBuilder - cmd action not found: " + sCmd; 397 } else { 398 widgd.setActionChange(actionCmd); 399 } 400 }*/ 401 if(userAction !=null){ 402 if(whenUserAction == null) { widgd.specifyActionChange(cfge.widgetType.userAction, userAction, sUserActionArgs); } 403 else { widgd.specifyActionChange(cfge.widgetType.userAction, userAction, sUserActionArgs, whenUserAction); } 404 } 405 //if(mouseAction !=null){ 406 //fauly type, does not work: widgd.setActionMouse(mouseAction, 0); 407 //} 408 String sFormat = cfge.widgetType.format; 409 if(sFormat !=null){ 410 widgd.setFormat(sFormat); 411 } 412 if(cfge.widgetType.help!=null){ 413 widgd.setHtmlHelp(cfge.widgetType.help); 414 } 415 if(cfge.widgetType.color0 != null){ 416 widgd.setBackColor(GralColor.getColor(cfge.widgetType.color0.color), 0); 417 } 418 if(cfge.widgetType.color1 != null){ 419 widgd.setLineColor(GralColor.getColor(cfge.widgetType.color1.color), 0); 420 } 421 if(cfge.widgetType.dropFiles !=null){ 422 GralUserAction actionDrop = gralMng.getRegisteredUserAction(cfge.widgetType.dropFiles); 423 if(actionDrop == null){ 424 sError = "GuiCfgBuilder - action for drop not found: " + cfge.widgetType.dropFiles; 425 } else { 426 widgd.setDropEnable(actionDrop, KeyCode.dropFiles); 427 } 428 } 429 if(cfge.widgetType.dragFiles !=null){ 430 GralUserAction actionDrag = gralMng.getRegisteredUserAction(cfge.widgetType.dragFiles); 431 if(actionDrag == null){ 432 sError = "GuiCfgBuilder - action for drag not found: " + cfge.widgetType.dragFiles; 433 } else { 434 widgd.setDragEnable(actionDrag, KeyCode.dragFiles); 435 } 436 } 437 if(cfge.widgetType.dragText !=null){ 438 GralUserAction actionDrag = gralMng.getRegisteredUserAction(cfge.widgetType.dragText); 439 if(actionDrag == null){ 440 sError = "GuiCfgBuilder - action for drag not found: " + cfge.widgetType.dragText; 441 } else { 442 widgd.setDragEnable(actionDrag, KeyCode.dragText); 443 } 444 } 445 //save the configuration element as association from the widget. 446 widgd.setCfgElement(cfge); 447 } 448 return sError; 449 } 450 451 452 public void updatePanel(String panelName) 453 { 454 455 } 456 457 458 459 String replaceAlias(String src) 460 { 461 int posSep; 462 if((posSep = src.indexOf("<*")) < 0) { return src; } //unchanged 463 else { 464 StringBuilder u = new StringBuilder(src); 465 do{ 466 int posEnd = u.indexOf(">", posSep+2); 467 String sAlias = src.substring(posSep + 2, posEnd); 468 String sValue = indexAlias.get(sAlias); 469 if(sValue == null){ sValue = "??" + sAlias + "??";} 470 u.replace(posSep, posEnd+1, sValue); 471 } while((posSep = u.indexOf("<*", posSep)) >=0); //note nice side effect: replace in sValue too 472 return u.toString(); 473 } 474 } 475 476 477 void stop(){} 478}