1 /* 2 * Copyright (c) 2016 Helmut Neemann 3 * Use of this source code is governed by the GPL v3 license 4 * that can be found in the LICENSE file. 5 */ 6 package de.neemann.digital.core.element; 7 8 import de.neemann.digital.lang.Lang; 9 10 import java.io.File; 11 12 /** 13 * Class is used to define the keys used to access the models attributes 14 * 15 * @param <VALUE> the keys value type 16 */ 17 public class Key<VALUE> { 18 private final String key; 19 private final DefaultFactory<VALUE> defFactory; 20 private final String langKey; 21 private boolean groupEditAllowed = false; 22 private Key dependsOn; 23 private CheckEnabled checkEnabled; 24 private boolean isSecondary; 25 private boolean requiresRestart = false; 26 private boolean requiresRepaint = false; 27 private String panelId; 28 29 // Both values are always null in digital. 30 // Both are only used within a custom implemented component. 31 private String name; 32 private String description; 33 private boolean adaptiveIntFormat; 34 35 /** 36 * Creates a new Key. 37 * Use this constructor only if the def value is not mutable! 38 * 39 * @param key the key 40 * @param def the default value 41 */ Key(String key, VALUE def)42 public Key(String key, VALUE def) { 43 this(key, () -> def); 44 if (def == null) 45 throw new NullPointerException(); 46 } 47 48 /** 49 * Creates a new Key. 50 * Use this constructor if the def value is mutable! 51 * 52 * @param key the key 53 * @param defFactory the factory to create a default value 54 */ Key(String key, DefaultFactory<VALUE> defFactory)55 public Key(String key, DefaultFactory<VALUE> defFactory) { 56 this.key = key; 57 langKey = "key_" + key.replace(" ", ""); 58 if (defFactory == null) 59 throw new NullPointerException(); 60 this.defFactory = defFactory; 61 } 62 63 /** 64 * Returns the attributes key 65 * 66 * @return the key 67 */ getKey()68 public String getKey() { 69 return key; 70 } 71 72 /** 73 * Returns the attributes display name 74 * 75 * @return the name of the key 76 */ getName()77 public String getName() { 78 if (name != null) 79 return name; 80 else 81 return Lang.get(langKey); 82 } 83 84 /** 85 * @return the default value of this key 86 */ getDefault()87 public VALUE getDefault() { 88 return defFactory.createDefault(); 89 } 90 91 /** 92 * @return The values class 93 */ getValueClass()94 public Class getValueClass() { 95 return getDefault().getClass(); 96 } 97 98 @Override toString()99 public String toString() { 100 return getName(); 101 } 102 103 /** 104 * @return the keys description 105 */ getDescription()106 public String getDescription() { 107 if (description != null) 108 return description; 109 else { 110 String d = Lang.getNull(langKey + "_tt"); 111 if (d != null) 112 return d; 113 else 114 return getName(); 115 } 116 } 117 118 /** 119 * @return the language key 120 */ getLangKey()121 public String getLangKey() { 122 return langKey; 123 } 124 125 /** 126 * @return true if group edit is allowed 127 */ isGroupEditAllowed()128 public boolean isGroupEditAllowed() { 129 return groupEditAllowed; 130 } 131 132 /** 133 * Allows this attribute in group edit. 134 * 135 * @return this for chained calls 136 */ allowGroupEdit()137 public Key<VALUE> allowGroupEdit() { 138 this.groupEditAllowed = true; 139 return this; 140 } 141 142 /** 143 * Sets the name of this key. 144 * Is not used in Digital at all. 145 * This method can be used to define custom keys in custom java components. 146 * 147 * @param name the name of the key 148 * @return this for chained calls 149 */ setName(String name)150 public Key<VALUE> setName(String name) { 151 this.name = name; 152 return this; 153 } 154 155 /** 156 * Sets the description of this key. 157 * Is not used in Digital at all. 158 * This method can be used to define custom keys in custom java components. 159 * 160 * @param description the name of the key 161 * @return this for chained calls 162 */ setDescription(String description)163 public Key<VALUE> setDescription(String description) { 164 this.description = description; 165 return this; 166 } 167 168 /** 169 * @return returns the key this key depends on 170 */ getDependsOn()171 public Key getDependsOn() { 172 return dependsOn; 173 } 174 175 /** 176 * @return true if dependency is inverted 177 */ getCheckEnabled()178 public CheckEnabled getCheckEnabled() { 179 return checkEnabled; 180 } 181 182 /** 183 * Sets a bool dependency for this key 184 * 185 * @param key the key which this key depends on 186 * @return this for chained calls 187 */ setDependsOn(Key<Boolean> key)188 public Key<VALUE> setDependsOn(Key<Boolean> key) { 189 return setDependsOn(key, o -> o); 190 } 191 192 /** 193 * Sets the key this key depends on. 194 * 195 * @param key the key where this key depends on 196 * @param checkEnabled function which determines if the editor is enabled or not 197 * @param <KV> type of key which this key depends on 198 * @return this for chained calls 199 */ setDependsOn(Key<KV> key, CheckEnabled<KV> checkEnabled)200 public <KV> Key<VALUE> setDependsOn(Key<KV> key, CheckEnabled<KV> checkEnabled) { 201 this.dependsOn = key; 202 this.checkEnabled = checkEnabled; 203 return this; 204 } 205 206 /** 207 * @return true is this is a secondary attribute 208 */ isSecondary()209 public boolean isSecondary() { 210 return isSecondary; 211 } 212 213 /** 214 * Makes this attribute to be a secondary attribute 215 * 216 * @return this for chained calls 217 */ setSecondary()218 public Key<VALUE> setSecondary() { 219 isSecondary = true; 220 return this; 221 } 222 223 /** 224 * Called if the modification of this setting needs a restart. 225 * 226 * @return this for chained calls 227 */ setRequiresRestart()228 public Key<VALUE> setRequiresRestart() { 229 requiresRestart = true; 230 return this; 231 } 232 233 /** 234 * @return true if changing this value needs a restart 235 */ getRequiresRestart()236 public boolean getRequiresRestart() { 237 return requiresRestart; 238 } 239 240 /** 241 * Called if this setting needs a repaint. 242 * This means, that the circuit graphics became invalid 243 * if this setting has changed. 244 * 245 * @return this for chained calls 246 */ setRequiresRepaint()247 public Key<VALUE> setRequiresRepaint() { 248 requiresRepaint = true; 249 return this; 250 } 251 252 /** 253 * @return true if changing this value needs a repaint 254 */ getRequiresRepaint()255 public boolean getRequiresRepaint() { 256 return requiresRepaint; 257 } 258 259 /** 260 * Enables an adaptive int format in number editors. 261 * This means that the string representation of the number is controlled 262 * by the IntFormat stored in the elements attributes. 263 * 264 * @return this for chained calls 265 */ setAdaptiveIntFormat()266 public Key<VALUE> setAdaptiveIntFormat() { 267 adaptiveIntFormat = true; 268 return this; 269 } 270 271 /** 272 * @return true if adaptive int format is required 273 */ isAdaptiveIntFormat()274 public boolean isAdaptiveIntFormat() { 275 return adaptiveIntFormat; 276 } 277 278 /** 279 * Moves this key to the panel with the given id 280 * 281 * @param panelId the panel id 282 * @return this for chained calls 283 */ setPanelId(String panelId)284 public Key<VALUE> setPanelId(String panelId) { 285 this.panelId = panelId; 286 return this; 287 } 288 289 /** 290 * @return the panel id, null if no panel is set 291 */ getPanelId()292 public String getPanelId() { 293 return panelId; 294 } 295 296 /** 297 * A integer attribute. 298 * Stores additional combo box values 299 */ 300 public static class KeyInteger extends Key<Integer> { 301 private int[] values; 302 private int min = Integer.MIN_VALUE; 303 private int max = Integer.MAX_VALUE; 304 305 /** 306 * Creates a new instance 307 * 308 * @param key the key to use 309 * @param def the default value 310 */ KeyInteger(String key, Integer def)311 public KeyInteger(String key, Integer def) { 312 super(key, def); 313 } 314 315 /** 316 * Sets the values to use in the combo box. 317 * 318 * @param values the values 319 * @return this for chained calls 320 */ setComboBoxValues(int... values)321 public KeyInteger setComboBoxValues(int... values) { 322 this.values = values; 323 return this; 324 } 325 326 /** 327 * Sets the minimal value which is allowed. 328 * 329 * @param min the minimal value allowed 330 * @return this for chained calls 331 */ setMin(int min)332 public KeyInteger setMin(int min) { 333 this.min = min; 334 return this; 335 } 336 337 /** 338 * Sets the maximal value which is allowed. 339 * 340 * @param max the maximal value allowed 341 * @return this for chained calls 342 */ setMax(int max)343 public KeyInteger setMax(int max) { 344 this.max = max; 345 return this; 346 } 347 348 /** 349 * @return the values to show in the combo box 350 */ getComboBoxValues()351 public int[] getComboBoxValues() { 352 return values; 353 } 354 355 /** 356 * @return the min value 357 */ getMin()358 public int getMin() { 359 return min; 360 } 361 362 /** 363 * @return the max value 364 */ getMax()365 public int getMax() { 366 return max; 367 } 368 } 369 370 /** 371 * A bits attribute. 372 * Stores additional combo box values 373 */ 374 public static final class KeyBits extends KeyInteger { 375 376 /** 377 * Creates a new bits key 378 * 379 * @param key the key 380 * @param def the default value 381 */ KeyBits(String key, Integer def)382 public KeyBits(String key, Integer def) { 383 super(key, def); 384 setMin(1); 385 setMax(64); 386 setComboBoxValues(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 32); 387 allowGroupEdit(); 388 } 389 } 390 391 /** 392 * Stores a file 393 */ 394 public static final class KeyFile extends Key<File> { 395 396 private boolean directoryOnly; 397 398 /** 399 * Creates a new file key 400 * 401 * @param key the key 402 * @param def the default file 403 */ KeyFile(String key, File def)404 public KeyFile(String key, File def) { 405 super(key, def); 406 setDirectoryOnly(false); 407 } 408 409 /** 410 * Set the directory only mode 411 * 412 * @param directoryOnly if true you can select only directories 413 * @return this for chained calls 414 */ setDirectoryOnly(boolean directoryOnly)415 public KeyFile setDirectoryOnly(boolean directoryOnly) { 416 this.directoryOnly = directoryOnly; 417 return this; 418 } 419 420 /** 421 * @return true if you can select only directories 422 */ isDirectoryOnly()423 public boolean isDirectoryOnly() { 424 return directoryOnly; 425 } 426 } 427 428 /** 429 * Used to store enum values 430 * 431 * @param <E> the enum type 432 */ 433 public static final class KeyEnum<E extends Enum> extends Key<E> { 434 private final E[] values; 435 private final String[] names; 436 private final boolean toString; 437 438 /** 439 * Creates a new emum key 440 * 441 * @param key the key 442 * @param def the default value 443 * @param values the possible values 444 */ KeyEnum(String key, E def, E[] values)445 public KeyEnum(String key, E def, E[] values) { 446 this(key, def, values, false); 447 } 448 449 /** 450 * Creates a new emum key 451 * 452 * @param key the key 453 * @param def the default value 454 * @param values the possible values 455 * @param toString if true, the names are not taken from the language file but created by calling toString() 456 */ KeyEnum(String key, E def, E[] values, boolean toString)457 public KeyEnum(String key, E def, E[] values, boolean toString) { 458 super(key, def); 459 this.values = values; 460 this.toString = toString; 461 462 names = new String[values.length]; 463 if (toString) 464 for (int i = 0; i < values.length; i++) 465 names[i] = values[i].toString(); 466 else 467 for (int i = 0; i < values.length; i++) 468 names[i] = Lang.get(getLangKey(values[i])); 469 allowGroupEdit(); 470 } 471 472 /** 473 * creates the language key for the enum values 474 * 475 * @param value the value 476 * @return the language key 477 */ getLangKey(E value)478 public String getLangKey(E value) { 479 return getLangKey() + "_" + value.name(); 480 } 481 482 /** 483 * @return the enums values 484 */ getValues()485 public E[] getValues() { 486 return values; 487 } 488 489 /** 490 * @return the enums translated names 491 */ getNames()492 public String[] getNames() { 493 return names; 494 } 495 496 /** 497 * @return true if this enum key uses toString to create the display names 498 */ usesToString()499 public boolean usesToString() { 500 return toString; 501 } 502 } 503 504 /** 505 * A special string key to flag long multi line strings. 506 */ 507 public static final class LongString extends Key<String> { 508 private int rows = 6; 509 private int columns = 0; 510 private boolean lineNumbers = false; 511 512 /** 513 * Creates a new Key 514 * 515 * @param key the key 516 */ LongString(String key)517 public LongString(String key) { 518 super(key, ""); 519 } 520 521 /** 522 * Creates a new Key 523 * 524 * @param key the key 525 * @param def the default value 526 */ LongString(String key, String def)527 public LongString(String key, String def) { 528 super(key, def); 529 } 530 531 /** 532 * @return the rows of the editor field 533 */ getRows()534 public int getRows() { 535 return rows; 536 } 537 538 /** 539 * Sets the rows in the editor 540 * 541 * @param rows the number ow rows 542 * @return this for chained calls 543 */ setRows(int rows)544 public LongString setRows(int rows) { 545 this.rows = rows; 546 return this; 547 } 548 549 /** 550 * @return the coloums of the editor field 551 */ getColumns()552 public int getColumns() { 553 return columns; 554 } 555 556 557 /** 558 * Sets the columns in the editor 559 * 560 * @param columns the number ow rows 561 * @return this for chained calls 562 */ setColumns(int columns)563 public LongString setColumns(int columns) { 564 this.columns = columns; 565 return this; 566 } 567 568 /** 569 * Sets the line numbers attribute 570 * 571 * @param lineNumbers true if line numbers should be visibla 572 * @return this for chained calls 573 */ setLineNumbers(boolean lineNumbers)574 public LongString setLineNumbers(boolean lineNumbers) { 575 this.lineNumbers = lineNumbers; 576 return this; 577 } 578 579 /** 580 * @return true if line numbers are visible 581 */ getLineNumbers()582 public boolean getLineNumbers() { 583 return lineNumbers; 584 } 585 } 586 587 /** 588 * Interface to define a dependancy of a key from an other key 589 * 590 * @param <T> the type of the key 591 */ 592 public interface CheckEnabled<T> { 593 /** 594 * Returns true if editor is enabled 595 * 596 * @param t the value the editor depends on 597 * @return true if editor is enabled 598 */ isEnabled(T t)599 boolean isEnabled(T t); 600 } 601 602 /** 603 * Used to provide a default value if the value is mutable. 604 * 605 * @param <VALUE> the type of the value 606 */ 607 public interface DefaultFactory<VALUE> { 608 /** 609 * Called to create a new default value. 610 * 611 * @return the default value 612 */ createDefault()613 VALUE createDefault(); 614 } 615 } 616