Ticket #2410: categories.patch
File categories.patch, 65.8 KB (added by boyanl, 15 years ago) |
---|
-
modules/org.sophie2.base.model.book/src/main/java/org/sophie2/base/model/book/interfaces/ResourceFrame.java
### Eclipse Workspace Patch 1.0 #P sophie
7 7 import org.sophie2.base.model.book.resource.ResourceFilesUtil; 8 8 import org.sophie2.base.model.resources.r4.LocationPrefix; 9 9 import org.sophie2.base.model.resources.r4.ResourceRefR4; 10 import org.sophie2.base.model.resources.r4.keys.Category; 10 11 import org.sophie2.base.model.resources.r4.keys.DeeplyCopied; 11 12 import org.sophie2.base.model.resources.r4.keys.TemplatedResourceRefKey; 13 import org.sophie2.base.model.resources.r4.keys.Category.Categories; 12 14 import org.sophie2.base.persistence.commons.PersistenceOptions; 13 15 import org.sophie2.base.persistence.commons.PersistenceUtil; 14 16 import org.sophie2.base.persistence.ref.ValueRef; … … 26 28 * A key holding a reference to the main resource of the frame. 27 29 */ 28 30 @DeeplyCopied 31 @Category(Categories.CATEGORY_CONTENT) 29 32 public static final TemplatedResourceRefKey KEY_MAIN_RESOURCE = 30 33 new TemplatedResourceRefKey("main-resource", ResourceRefR4.class, 31 34 ResourceRefR4.NONE_REF){ -
modules/org.sophie2.base.model.book/src/main/java/org/sophie2/base/model/book/interfaces/StyledElement.java
15 15 import org.sophie2.base.persistence.commons.PersistenceOptions; 16 16 import org.sophie2.base.persistence.ref.ValueRef; 17 17 import org.sophie2.base.persistence.storage.Storage; 18 import org.sophie2.base.model.resources.r4.keys.Category; 19 import org.sophie2.base.model.resources.r4.keys.Category.Categories; 18 20 19 21 /** 20 22 * An element with a style. Provides keys for background and border. 21 23 * 22 24 * @author boyan 23 25 */ 26 @Category(Categories.CATEGORY_APPEARANCE) 24 27 public interface StyledElement { 25 28 26 29 // ---------- Default constants. ---------- // -
modules/org.sophie2.base.model.book/src/main/java/org/sophie2/base/model/book/interfaces/MemberElement.java
10 10 import org.sophie2.base.persistence.persister.MasterPersister; 11 11 import org.sophie2.base.persistence.ref.ValueRef; 12 12 import org.sophie2.base.persistence.storage.Storage; 13 import org.sophie2.base.model.resources.r4.keys.Category; 14 import org.sophie2.base.model.resources.r4.keys.Category.Categories; 13 15 14 16 /** 15 17 * An element that is contained in another element. Provides keys for location … … 34 36 * The location of the element. Relative to the coordinate system of the 35 37 * parent element. 36 38 */ 39 @Category(Categories.CATEGORY_PROPERTIES) 37 40 public static final TemplatedKey<LocationChannel> KEY_LOCATION = 38 41 new TemplatedKey<LocationChannel>("location", LocationChannel.class, LocationChannel.DEFAULT) { 39 42 private static final String STORAGE_NAME = "content-location"; … … 51 54 /** 52 55 * The angle at which the element is rotated. 53 56 */ 57 @Category(Categories.CATEGORY_PROPERTIES) 54 58 public static final TemplatedKey<Double> KEY_ROTATION_ANGLE = 55 59 new TemplatedKey<Double>("rotation", Double.class, DEFAULT_ROTATION_ANGLE){ 56 60 private static final String STORAGE_NAME = "rotation-angle"; -
modules/org.sophie2.main.app.halos/src/main/java/org/sophie2/main/app/halos/shared/AddTemplateHaloButton.java
36 36 import org.sophie2.core.mvc.OperationDef; 37 37 import org.sophie2.core.mvc.events.EventR3; 38 38 import org.sophie2.main.app.commons.book.BookView; 39 import org.sophie2.main.app.commons.dialogs. TemplateDialog;39 import org.sophie2.main.app.commons.dialogs.CreateTemplateDialog; 40 40 import org.sophie2.main.app.commons.dialogs.TemplateInfo; 41 41 import org.sophie2.main.app.commons.element.ElementView; 42 42 import org.sophie2.main.app.commons.frame.FrameView; … … 153 153 } 154 154 155 155 TemplateInfo info = new TemplateInfo(title, templateElemH, false); 156 TemplateDialog.Input input = newTemplateDialog.Input(pwa.swingComponent().get(), info);156 CreateTemplateDialog.Input input = new CreateTemplateDialog.Input(pwa.swingComponent().get(), info); 157 157 158 158 159 159 TemplateInfo res = DialogManager.get().showDialog(input); -
modules/org.sophie2.base.model.resources.r4/src/main/java/org/sophie2/base/model/resources/r4/resources/ResourceR4.java
1 1 package org.sophie2.base.model.resources.r4.resources; 2 2 3 3 import java.io.IOException; 4 import java.lang.reflect.Field; 4 5 import java.net.URI; 5 6 import java.util.ArrayList; 6 7 import java.util.HashMap; 8 import java.util.LinkedList; 7 9 import java.util.List; 8 10 import java.util.Map; 11 import java.util.Queue; 12 import java.util.Map.Entry; 9 13 10 14 import org.sophie2.base.model.resources.r4.BaseModelResourcesR4Module; 11 15 import org.sophie2.base.model.resources.r4.ResourceInfoProvider; … … 13 17 import org.sophie2.base.model.resources.r4.BaseModelResourcesR4Module.ResourceR4Point; 14 18 import org.sophie2.base.model.resources.r4.ResourceInfoProvider.KeyEntry; 15 19 import org.sophie2.base.model.resources.r4.access.ResourceAccess; 20 import org.sophie2.base.model.resources.r4.keys.Category; 16 21 import org.sophie2.base.model.resources.r4.keys.ChildrenKey; 17 22 import org.sophie2.base.model.resources.r4.keys.Key; 18 23 import org.sophie2.base.model.resources.r4.keys.MetaKey; … … 231 236 } 232 237 233 238 /** 239 * Gets the keys of a given resource class that are annotated with the {@link Category} as pairs of <category-name, list-of-keys>. 240 * @param <R> 241 * Type of the resource. 242 * @param resourceClass 243 * The class of the resource. 244 * @return A map of the keys and their names. 245 */ 246 public static <R extends ResourceR4> Map<String, List<Key<?>>> getCategorizedKeys (Class<R> resourceClass) { 247 Map<String, List<Key<?>>> res = new HashMap<String, List<Key<?>>>(); 248 Map<Key<?>, String> keysCategories = new HashMap<Key<?>, String> (); 249 250 /* 251 * Perform a breadth-first search on the hierarchy and iterate the declared fields in each class/interface. We need to do this because 252 * the annotation could be placed at class level too, and we need to consider its declared fields annotated accordingly. 253 */ 254 Queue <Class <?>> parentsQueue = new LinkedList<Class<?>> (); 255 parentsQueue.add(resourceClass); 256 257 while (!parentsQueue.isEmpty()) { 258 Class<?> currentClass = parentsQueue.poll(); 259 260 Category classAnnotation = currentClass.getAnnotation (Category.class); 261 for (Field field : currentClass.getDeclaredFields()) { 262 try { 263 if (field.getName().startsWith("KEY_") ) { 264 Category fieldAnnotation = field.getAnnotation(Category.class), 265 annotationToPut = (fieldAnnotation == null ? classAnnotation : fieldAnnotation); 266 Key<?> key = (Key<?>) field.get (null); 267 268 if (annotationToPut != null) 269 keysCategories.put(key, annotationToPut.value()); 270 } 271 } 272 catch (IllegalAccessException e) { 273 throw new RuntimeException("Cannot access a KEY field (" 274 + field.getName() + ") in the resource class " 275 + resourceClass + "."); 276 } 277 } 278 279 for (Class<? extends ResourceR4> superInterface : currentClass.getInterfaces()) { 280 parentsQueue.add(superInterface); 281 } 282 Class<?> superClass = currentClass.getSuperclass(); 283 if (superClass != null) { 284 parentsQueue.add (superClass); 285 } 286 } 287 288 for (Entry<Key<?>, String> entry : keysCategories.entrySet ()) { 289 List<Key<?>> categoryKeys = res.get(entry.getValue()); 290 291 if (categoryKeys == null) { 292 categoryKeys = new LinkedList <Key<?>> (); 293 } 294 295 categoryKeys.add(entry.getKey ()); 296 res.put(entry.getValue(), categoryKeys); 297 } 298 299 return res; 300 } 301 302 /** 234 303 * Gets the keys of a given resource class that should be deeply copied. 235 304 * @param <R> Type of the resource. 236 305 * -
modules/org.sophie2.extra.func.browser/src/main/java/org/sophie2/extra/func/browser/model/BrowserFrameR4.java
1 1 package org.sophie2.extra.func.browser.model; 2 2 3 3 import org.sophie2.base.model.book.frame.FrameR4; 4 import org.sophie2.base.model.resources.r4.keys.Key; 5 import org.sophie2.base.model.resources.r4.keys.SimpleKey; 4 import org.sophie2.base.model.resources.r4.keys.Category; 5 import org.sophie2.base.model.resources.r4.keys.TemplatedKey; 6 import org.sophie2.base.model.resources.r4.keys.Category.Categories; 6 7 7 8 /** 8 9 * A frame with an embedded browser. … … 33 34 /** 34 35 * The url the browser frame shows. 35 36 */ 36 public static final Key<String> KEY_URL = 37 new SimpleKey<String>("url", String.class, DEFAULT_BROWSER_TARGET); 37 @Category(Categories.CATEGORY_CONTENT) 38 public static final TemplatedKey<String> KEY_URL = 39 new TemplatedKey<String>("url", String.class, DEFAULT_BROWSER_TARGET); 38 40 } -
modules/org.sophie2.base.model.book/src/main/java/org/sophie2/base/model/book/frame/FrameR4.java
11 11 import org.sophie2.base.model.book.interfaces.ResizableElement; 12 12 import org.sophie2.base.model.book.interfaces.StyledElement; 13 13 import org.sophie2.base.model.book.resource.r4.ElementR4; 14 import org.sophie2.base.model.resources.r4.keys.Category; 14 15 import org.sophie2.base.model.resources.r4.keys.CompositeKey; 15 16 import org.sophie2.base.model.resources.r4.keys.TemplatedKey; 17 import org.sophie2.base.model.resources.r4.keys.Category.Categories; 16 18 import org.sophie2.base.model.resources.r4.resources.ResourceR4; 17 19 import org.sophie2.base.persistence.commons.PersistenceOptions; 18 20 import org.sophie2.base.persistence.commons.PersistenceUtil; … … 29 31 * 30 32 * @author boyan 31 33 */ 34 @Category(Categories.CATEGORY_APPEARANCE) 32 35 public abstract class FrameR4 extends ElementR4 implements MemberElement, StyledElement, ResizableElement { 33 36 34 37 /** … … 131 134 /** 132 135 * The margins of the frame. 133 136 */ 137 @Category(Categories.CATEGORY_PROPERTIES) 134 138 public static final TemplatedKey<ImmInsets> KEY_MARGIN_INSETS = 135 139 new TemplatedKey<ImmInsets>("margin-insets", ImmInsets.class, DEFAULT_MARGIN_INSETS) { 136 140 @Override … … 146 150 /** 147 151 * The paddings of the frame. 148 152 */ 153 @Category(Categories.CATEGORY_PROPERTIES) 149 154 public static final TemplatedKey<ImmInsets> KEY_PADDING_INSETS = 150 155 new TemplatedKey<ImmInsets>("padding-insets", ImmInsets.class, DEFAULT_PADDING_INSETS) { 151 156 -
modules/org.sophie2.base.model.book/src/main/java/org/sophie2/base/model/book/interfaces/ResizableElement.java
3 3 import java.io.IOException; 4 4 5 5 import org.sophie2.base.commons.util.position.ImmSize; 6 import org.sophie2.base.model.resources.r4.keys.Category; 6 7 import org.sophie2.base.model.resources.r4.keys.TemplatedKey; 8 import org.sophie2.base.model.resources.r4.keys.Category.Categories; 7 9 import org.sophie2.base.persistence.commons.PersistenceOptions; 8 10 import org.sophie2.base.persistence.commons.PersistenceUtil; 9 11 import org.sophie2.base.persistence.persister.MasterPersister; … … 25 27 /** 26 28 * The size (width and height in pixels) of the element. 27 29 */ 30 @Category(Categories.CATEGORY_PROPERTIES) 28 31 public static final TemplatedKey<ImmSize> KEY_SIZE = 29 32 new TemplatedKey<ImmSize>("size", ImmSize.class, ResizableElement.DEFAULT_SIZE) { 30 33 @Override -
modules/org.sophie2.main.func.media/src/main/java/org/sophie2/main/func/media/model/contents/MediaFrameR4.java
4 4 5 5 import org.sophie2.base.model.book.frame.FrameR4; 6 6 import org.sophie2.base.model.book.interfaces.ResourceFrame; 7 import org.sophie2.base.model.resources.r4.keys.Category; 7 8 import org.sophie2.base.model.resources.r4.keys.CompositeKey; 8 9 import org.sophie2.base.model.resources.r4.keys.Key; 9 10 import org.sophie2.base.model.resources.r4.keys.SimpleKey; 10 11 import org.sophie2.base.model.resources.r4.keys.TemplatedKey; 12 import org.sophie2.base.model.resources.r4.keys.Category.Categories; 11 13 import org.sophie2.base.persistence.commons.PersistenceOptions; 12 14 import org.sophie2.base.persistence.ref.ValueRef; 13 15 import org.sophie2.base.persistence.storage.Storage; … … 42 44 /** 43 45 * A key that holds informations whether the media clip is looped or not. 44 46 */ 45 public static final Key<Boolean> KEY_LOOP = new SimpleKey<Boolean>("loop", Boolean.class, true); 47 @Category(Categories.CATEGORY_CONTENT) 48 public static final TemplatedKey<Boolean> KEY_LOOP = new TemplatedKey<Boolean>("loop", Boolean.class, true); 46 49 47 50 /** 48 51 * A key that holds informations whether the media clip is clipped, and the -
modules/org.sophie2.main.app.commons/src/main/java/org/sophie2/main/app/commons/MainAppModule.java
44 44 import org.sophie2.main.app.commons.dialogs.FontChooserDialog; 45 45 import org.sophie2.main.app.commons.dialogs.MessageDialog; 46 46 import org.sophie2.main.app.commons.dialogs.StringDialog; 47 import org.sophie2.main.app.commons.dialogs. TemplateDialog;47 import org.sophie2.main.app.commons.dialogs.CreateTemplateDialog; 48 48 import org.sophie2.main.app.commons.dialogs.BookPropertiesDialog.BookPropertiesSwingDialog; 49 49 import org.sophie2.main.app.commons.dialogs.BookPropertiesDialog.BookPropertiesSwingDialog.BookPropertiesDialogLogic; 50 50 import org.sophie2.main.app.commons.element.ElementLogic; … … 159 159 res.add(new SimpleSophieExtension<Dialog>(Dialog.class, 160 160 new AudioCaptureDialog())); 161 161 res.add(new SimpleSophieExtension<Dialog>(Dialog.class, 162 new TemplateDialog()));162 new CreateTemplateDialog())); 163 163 164 164 AutoVisualProvider.fillExtensions(res, BookPropertiesSwingDialog.class); 165 165 … … 204 204 SimpleOperation.fillExtensions(res, MainWindowLogic.class); 205 205 SimpleOperation.fillExtensions(res, BookPanelLogic.class); 206 206 207 SimpleOperation.fillExtensions(res, TemplateDialog.DialogLogic.class);207 SimpleOperation.fillExtensions(res, CreateTemplateDialog.DialogLogic.class); 208 208 209 209 SimpleResourceViewProvider.register(res, AppMainWindow.class, 210 210 BookDocView.class, BookR4.KIND, "kkk-book-doc-view"); -
modules/org.sophie2.base.model.resources.r4/src/main/java/org/sophie2/base/model/resources/r4/keys/Category.java
1 package org.sophie2.base.model.resources.r4.keys; 2 3 import java.lang.annotation.Retention; 4 import java.lang.annotation.RetentionPolicy; 5 6 7 /** 8 * Annotation for declaring that a given keys belongs to a given category. Used for 9 * categorizing keys in the create template dialog. 10 * 11 * @author boyanl 12 */ 13 @Retention(RetentionPolicy.RUNTIME) 14 public @interface Category { 15 /** 16 * @return 17 * The value of the annotation 18 */ 19 String value(); 20 21 22 /** 23 * A class containing the names of the pre-defined categories. 24 * @author boyanl 25 */ 26 public static class Categories 27 { 28 /** 29 * Name of the Appearance category. 30 */ 31 public static final String CATEGORY_APPEARANCE = "Appearance"; 32 /** 33 * Name of the Content category. 34 */ 35 public static final String CATEGORY_CONTENT = "Content"; 36 /** 37 * Name of the Properties category. 38 */ 39 public static final String CATEGORY_PROPERTIES = "Properties"; 40 /** 41 * Name of the Other category. 42 */ 43 public static final String CATEGORY_OTHER = "Other"; 44 } 45 } 46 No newline at end of file -
modules/org.sophie2.main.app.commons/src/main/java/org/sophie2/main/app/commons/dialogs/CreateTemplateDialog.java
3 3 import java.awt.BorderLayout; 4 4 import java.awt.Component; 5 5 import java.awt.Dimension; 6 import java.awt.FlowLayout; 6 7 import java.awt.Rectangle; 7 8 import java.awt.event.ActionEvent; 8 9 import java.awt.event.ActionListener; … … 14 15 import java.util.Collection; 15 16 import java.util.Enumeration; 16 17 import java.util.HashMap; 18 import java.util.LinkedList; 17 19 import java.util.List; 18 20 import java.util.Map; 19 21 import java.util.Map.Entry; … … 32 34 import javax.swing.tree.TreeModel; 33 35 import javax.swing.tree.TreePath; 34 36 37 import org.sophie2.base.bound.BoundCheckBox; 38 import org.sophie2.base.bound.BoundControl; 39 import org.sophie2.base.bound.BoundControl.EventIds; 35 40 import org.sophie2.base.commons.gui.TriStateCheckBox; 36 41 import org.sophie2.base.commons.gui.TriStateCheckBox.State; 37 42 import org.sophie2.base.commons.util.ImmList; … … 43 48 import org.sophie2.base.model.resources.r4.keys.CompositeKey; 44 49 import org.sophie2.base.model.resources.r4.keys.Key; 45 50 import org.sophie2.base.model.resources.r4.keys.TemplatedKey; 51 import org.sophie2.base.model.resources.r4.keys.Category.Categories; 46 52 import org.sophie2.base.model.resources.r4.resources.ResourceR4; 47 53 import org.sophie2.core.mvc.EventFilterBuilder; 48 54 import org.sophie2.core.mvc.OperationDef; 49 55 import org.sophie2.core.mvc.events.EventR3; 50 import org.sophie2.main.app.commons.dialogs.TemplateDialog.CheckBoxTree.CheckNode; 56 import org.sophie2.core.prolib.interfaces.RwProp; 57 import org.sophie2.main.app.commons.dialogs.CreateTemplateDialog.CheckBoxTree.CheckNode; 51 58 52 59 /** 53 60 * The dialog which is used to add a page or frame as a template. It generates a … … 58 65 * @author boyanl 59 66 */ 60 67 @SuppressWarnings("unchecked") 61 public class TemplateDialog extends Dialog<TemplateDialog.Input, TemplateInfo> {68 public class CreateTemplateDialog extends Dialog<CreateTemplateDialog.Input, TemplateInfo> { 62 69 63 70 /** 64 71 * A check-box tree class which extends (and internally uses) JTree. … … 155 162 this.setState (this.state.getState()); 156 163 } 157 164 165 /* 166 * Recalculates the state of the node based on its children. Called after removing a child for example. 167 */ 168 private void recalculateState () { 169 170 /* 171 * If either all the nodes are selected or all are de-selected, change the state 172 * to selected/de-selected. Otherwise, partial 173 */ 174 175 boolean allSelected = true, noneSelected = true; 176 Enumeration<CheckNode> e = this.children.elements(); 177 178 while (e.hasMoreElements()) { 179 CheckNode node = e.nextElement(); 180 if (node.getState () != TriStateCheckBox.State.CHECKED) { 181 allSelected = false; 182 } 183 if (node.getState () != TriStateCheckBox.State.UNCHECKED) { 184 noneSelected = false; 185 } 186 if (!allSelected && !noneSelected) { 187 break; 188 } 189 } 190 State newState; 191 if (allSelected) { 192 newState = State.CHECKED; 193 } else if (noneSelected) { 194 newState = State.UNCHECKED; 195 } else { 196 newState = State.PARTIAL; 197 } 198 199 if (newState != this.state.getState()) { 200 this.setState(newState); 201 ((DefaultTreeModel) this.backlink.getModel ()).nodeChanged (this); 202 } 203 } 158 204 /** 159 205 * Sets the state of the node. Propagates the changes to children/parent if necessary. 160 206 * … … 181 227 182 228 //propagate to parent (if any) 183 229 if (this.getParent() != null) { 184 185 /* 186 * If either all the nodes are selected or all are de-selected, change the state 187 * to selected/de-selected. Otherwise, partial 188 */ 189 boolean allSelected = true, noneSelected = true; 190 Enumeration<CheckNode> e = this.parent.children(); 191 192 while (e.hasMoreElements()) { 193 CheckNode node = e.nextElement(); 194 if (node.getState () != TriStateCheckBox.State.CHECKED) { 195 allSelected = false; 196 } 197 if (node.getState () != TriStateCheckBox.State.UNCHECKED) { 198 noneSelected = false; 199 } 200 if (!allSelected && !noneSelected) { 201 break; 202 } 203 } 204 205 if (allSelected) { 206 ((CheckNode) this.parent).setState (TriStateCheckBox.State.CHECKED); 207 ((DefaultTreeModel) this.backlink.getModel ()).nodeChanged (this.parent); 208 } else if (noneSelected) { 209 ((CheckNode) this.parent).setState (TriStateCheckBox.State.UNCHECKED); 210 ((DefaultTreeModel) this.backlink.getModel ()).nodeChanged (this.parent); 211 } else { 212 ((CheckNode) this.parent).setState (TriStateCheckBox.State.PARTIAL); 213 ((DefaultTreeModel) this.backlink.getModel ()).nodeChanged (this.parent); 214 } 230 ((CheckNode)this.getParent()).recalculateState(); 215 231 } 216 232 } 217 233 234 @Override 235 public void removeFromParent () { 236 CheckNode nodeParent = (CheckNode) this.parent; 237 super.removeFromParent(); 238 239 if(nodeParent != null) 240 nodeParent.recalculateState(); 241 } 242 218 243 /** 219 244 * Changes the node and updates children/parent accordingly. 220 245 * … … 406 431 NaiveImmList.<TemplatedKey<?>>getEmpty(); 407 432 408 433 /** 409 * Input for {@link TemplateDialog}.434 * Input for {@link CreateTemplateDialog}. 410 435 */ 411 436 public static class Input extends DialogInput<TemplateInfo> { 412 437 … … 457 482 } 458 483 459 484 /** 460 * The swing representation of the {@link TemplateDialog}. Singleton. 485 * A {@link BoundCheckBox} with a simple model. 486 */ 487 static class StandardCheckBox extends BoundCheckBox { 488 /** 489 * The model for the control. 490 * 491 * @return The property. 492 */ 493 RwProp<Boolean> value() { 494 return getBean().makeValueProp("value", Boolean.class, false); 495 } 496 497 498 @Override 499 protected Boolean computeModelData() { 500 return value().get(); 501 } 502 503 @Override 504 public int computeAlignment() { 505 return FlowLayout.LEFT; 506 } 507 } 508 509 /** 510 * The swing representation of the {@link CreateTemplateDialog}. Singleton. 461 511 * 462 512 * @author jani 513 * @author boyanl 463 514 */ 464 515 protected static class SwingDialog { 465 516 466 517 /** 467 * An inner class that serves as a key in the map that matches a given key from a given resource to a node in the tree .518 * An inner class that serves as a key in the map that matches a given key from a given resource to a node in the tree 468 519 */ 469 520 private static class NodeCacheKey { 470 471 521 private Key<?> key; 472 522 private ResourceRefR4 resourceRef; 473 523 … … 483 533 result = prime * result + ((this.key == null) ? 0 : this.key.hashCode()); 484 534 result = prime * result 485 535 + ((this.resourceRef == null) ? 0 : this.resourceRef.hashCode()); 486 487 536 return result; 488 537 } 489 538 … … 501 550 502 551 NodeCacheKey other = (NodeCacheKey) obj; 503 552 if (this.key == null) { 504 if (other.key != null) {553 if (other.key != null) 505 554 return false; 506 } 507 } else if (!this.key.equals(other.key)) { 555 } else if (!this.key.equals(other.key)) 508 556 return false; 509 }510 557 if (this.resourceRef == null) { 511 558 if (other.resourceRef != null) { 512 559 return false; … … 514 561 } else if (!this.resourceRef.equals(other.resourceRef)) { 515 562 return false; 516 563 } 517 518 564 return true; 519 565 } 520 566 521 567 522 568 } 523 569 524 525 570 /** 526 571 * The instance of the book template helper. 527 572 */ … … 540 585 541 586 private JDialog dialog = null; 542 587 private JPanel mainPanel; 588 private JPanel checkBoxPanel; 543 589 private JLabel titleLabel = null; 544 590 private JScrollPane treeScrollPane = null; 545 591 private JTextField titleField = null; … … 548 594 private JButton selectAllButton = null; 549 595 private JButton selectNoneButton = null; 550 596 private CheckBoxTree checkBoxTree = null; 551 552 597 private Map <NodeCacheKey, CheckNode> keyToNodeMap = null; 553 598 private Map <ResourceRefR4, Map <Key<?>, Boolean>> keyStates = null; 554 599 private Map <ResourceRefR4, CheckNode> resourcesCheckNodes; 555 private CheckNode isDefaultTemplateNode= null;600 private StandardCheckBox isDefaultTemplateCheckbox = null; 556 601 557 602 /** 558 603 * Holds the initial info for the dialog. … … 567 612 private TemplateInfo resultInfo; 568 613 569 614 /** 570 * Construct the swing representation of the {@link TemplateDialog}.615 * Construct the swing representation of the {@link CreateTemplateDialog}. 571 616 * 572 617 */ 573 618 private SwingDialog() { … … 597 642 if (this.mainPanel == null) { 598 643 this.mainPanel = new JPanel(); 599 644 this.mainPanel.setLayout(new BorderLayout()); 645 this.checkBoxPanel = new JPanel (); 600 646 601 647 JPanel top = new JPanel(); 602 648 top.add(getTitleLabel()); 603 649 top.add(getTitleField()); 604 top.setPreferredSize(new Dimension(250, 60));650 top.setPreferredSize(new Dimension(250, 85)); 605 651 top.add(getSelectAllButton()); 606 652 top.add(getSelectNoneButton()); 653 top.add (this.checkBoxPanel); 607 654 608 655 JPanel bottom = new JPanel(); 609 656 bottom.add(getOkButton()); 610 657 bottom.add(getCancelButton()); 611 658 612 this.treeScrollPane = new JScrollPane(); 613 659 this.treeScrollPane = new JScrollPane(); 660 614 661 this.mainPanel.add(top, BorderLayout.NORTH); 615 662 this.mainPanel.add(this.treeScrollPane, BorderLayout.CENTER); 616 663 this.mainPanel.add(bottom, BorderLayout.SOUTH); … … 682 729 @SuppressWarnings("synthetic-access") 683 730 public void actionPerformed(final ActionEvent e) { 684 731 String title = getTitleField().getText(); 685 boolean isDefault = SwingDialog.this.isDefaultTemplate Node.isSelected();732 boolean isDefault = SwingDialog.this.isDefaultTemplateCheckbox.value().get(); 686 733 687 734 for (Entry<ResourceRefR4, Map<Key<?>, Boolean>> entry : SwingDialog.this.keyStates.entrySet()) { 688 735 for (Entry<Key<?>, Boolean> keyEntry : entry.getValue().entrySet()) { … … 694 741 } 695 742 696 743 List<ResourceRefR4> toDelete = new ArrayList<ResourceRefR4> (); 697 for (Entry<ResourceRefR4, CheckNode> entry : SwingDialog.this.resourcesCheckNodes.entrySet ()) {744 for (Entry<ResourceRefR4, CheckNode> entry : SwingDialog.this.resourcesCheckNodes.entrySet ()) { 698 745 if (entry.getValue().getState() == State.UNCHECKED) { 699 746 toDelete.add(entry.getKey ()); 700 747 } … … 773 820 return this.selectNoneButton; 774 821 } 775 822 823 /* 824 * Constructs the checkbox tree with a given element, its parent node (if it's null, the element is considered a root), 825 * and a ResourceRef to the root element. 826 */ 776 827 private void constructTreeFromElement (ElementH element, CheckNode parentNode, ResourceRefR4 rootRef) { 777 828 ResourceRefR4 resourceRef = element.getRef(); 778 829 779 830 /* 780 * Hold an absolute reference to the root element and relative references to the sub-elements .831 * Hold an absolute reference to the root element and relative references to the sub-elements 781 832 */ 782 833 if (!resourceRef.equals(rootRef)) { 783 834 resourceRef = ResourceRefR4.getRelativeRef (rootRef, resourceRef); 784 835 } 785 786 836 String title = element.getTitle(); 787 837 CheckNode thisNode; 788 838 789 839 if (parentNode == null) { //if no parent node is given, create a new tree and set up its scroll pane 790 this.checkBoxTree = new CheckBoxTree (this.initialInfo.getTitle()); 791 this.isDefaultTemplateNode = this.checkBoxTree.addNode (DEFAULT_TEMPLATE_CHECK_BOX_LABEL); 840 this.checkBoxTree = new CheckBoxTree (this.initialInfo.getTitle()); 792 841 this.treeScrollPane.setViewportView(this.checkBoxTree); 793 794 842 thisNode = this.checkBoxTree.getRootNode(); 795 843 } 796 844 else { 797 845 thisNode = this.checkBoxTree.addNode (title, parentNode); 798 846 } 799 847 800 this.resourcesCheckNodes.put(resourceRef, thisNode); 848 this.resourcesCheckNodes.put(resourceRef, thisNode); 849 801 850 Class <? extends ResourceR4> resourceClass = ResourceR4.getClassByKind( element.getKind() ); 802 851 Collection<Key<?>> elementKeys = ResourceR4.getKnownKeys(resourceClass).values(); 803 852 853 Map<String, List<Key<?>>> annotatedKeys = ResourceR4.getCategorizedKeys(resourceClass); 854 Map<Key<?>, String> keysCategory = new HashMap<Key<?>, String>(); 804 855 Map<Key<?>, Boolean> elementKeyMap = new HashMap <Key<?>, Boolean> (); 856 805 857 this.keyStates.put(resourceRef, elementKeyMap); 806 858 807 859 boolean initialKeyState = true; … … 809 861 elementKeyMap.put(key, true); 810 862 } 811 863 812 for (Key<?> key: elementKeys) { 813 addKey (thisNode, resourceRef, key, initialKeyState, this.keyToNodeMap, elementKeys, 1); 864 String otherCategoryText = Categories.CATEGORY_OTHER, contentCategoryText = Categories.CATEGORY_CONTENT; 865 CheckNode otherCategory = null, contentCategory = null; 866 867 // insert those special categories in the map beforehand so they can be added to the tree 868 if (annotatedKeys.get(otherCategoryText) == null) { 869 annotatedKeys.put (otherCategoryText, new LinkedList<Key<?>>()); 814 870 } 815 871 816 List<ElementH> childElems = element.getSubElements (); 817 for (ElementH child: childElems) 818 constructTreeFromElement(child, thisNode, rootRef); 872 if (annotatedKeys.get(contentCategoryText) == null) { 873 annotatedKeys.put (contentCategoryText, new LinkedList<Key<?>>()); 874 } 875 876 877 for (Entry<String, List<Key<?>>> categoryEntry : annotatedKeys.entrySet()) { 878 String categoryName = categoryEntry.getKey(); 879 CheckNode categoryNode = this.checkBoxTree.addNode (categoryName, thisNode); 880 881 if (categoryName.equals(otherCategoryText)) { 882 otherCategory = categoryNode; 883 } 884 else if (categoryName.equals(contentCategoryText)) { 885 contentCategory = categoryNode; 886 } 887 888 for (Key<?> key : categoryEntry.getValue()) { 889 addKey (categoryNode, resourceRef, key, initialKeyState, this.keyToNodeMap, elementKeys, 1); 890 keysCategory.put(key, categoryName); 891 } 892 893 //delete leaf categories, but don't remove the ones that will be needed later 894 if (categoryNode.isLeaf() && (categoryNode != otherCategory) && (categoryNode != contentCategory)) 895 categoryNode.removeFromParent(); 896 } 897 898 for (Key<?> key : elementKeys) { 899 if (keysCategory.get(key) == null) { 900 if (otherCategory == null) { 901 otherCategory = this.checkBoxTree.addNode(otherCategoryText, thisNode); 902 } 903 904 addKey (otherCategory, resourceRef, key, initialKeyState, this.keyToNodeMap, elementKeys, 1); 905 } 906 } 907 908 if (otherCategory != null && otherCategory.isLeaf()) { 909 otherCategory.removeFromParent(); 910 } 911 912 List<ElementH> children = element.getSubElements(); 913 if (children.size() > 0) 914 { 915 for (ElementH child: children) { 916 constructTreeFromElement(child, contentCategory, rootRef); 917 } 918 } 919 920 if (contentCategory != null && contentCategory.isLeaf()) { 921 contentCategory.removeFromParent(); 922 } 819 923 } 820 924 821 925 /** 822 926 * Performs setup of the dialog - sets up the title and the panel with 823 927 * the checkboxes. … … 830 934 this.resourcesCheckNodes = new HashMap<ResourceRefR4, CheckNode> (); 831 935 832 936 ElementH rootElement = this.initialInfo.getRootElement (); 833 constructTreeFromElement (rootElement, null, rootElement.getRef()); 937 constructTreeFromElement (rootElement, null, rootElement.getRef()); 938 939 this.isDefaultTemplateCheckbox = new StandardCheckBox() { 940 @Override 941 public String computeTitle() { 942 return DEFAULT_TEMPLATE_CHECK_BOX_LABEL; 943 } 944 }; 945 946 this.checkBoxPanel.removeAll(); 947 this.checkBoxPanel.add(this.isDefaultTemplateCheckbox.swingComponent().get()); 834 948 this.checkBoxTree.expandPath(new TreePath (this.checkBoxTree.getRootNode())); 835 949 } 836 950 … … 917 1031 */ 918 1032 ON_SUBMIT { 919 1033 public void defineFilter(EventFilterBuilder filter) { 920 /* 921 * Don't define any filter 922 */ 923 } 1034 filter.setEventId(BoundControl.EventIds.SUBMIT); 1035 filter.setSourceClass(StandardCheckBox.class); 1036 } 924 1037 925 public boolean handle(EventR3 event) { 926 /* 927 * Do nothing in the handle method too 928 */ 929 return false; 930 } 1038 public boolean handle(EventR3 event) { 1039 StandardCheckBox source = event.getSource(StandardCheckBox.class); 1040 Boolean input = event.getEventParam(EventIds.INPUT_PARAM_INDEX, 1041 Boolean.class); 1042 source.value().set(input); 1043 return true; 1044 } 931 1045 }; 932 1046 } 933 1047 934 1048 @Override 935 1049 public Class<Input> getInputClass() { 936 return TemplateDialog.Input.class;1050 return CreateTemplateDialog.Input.class; 937 1051 } 938 1052 939 1053 @Override -
modules/org.sophie2.base.model.book/src/main/java/org/sophie2/base/model/book/resource/r4/ElementR4.java
6 6 import org.sophie2.base.model.book.timelines.VisibleChannel; 7 7 import org.sophie2.base.model.resources.r4.keys.TemplatedKey; 8 8 import org.sophie2.base.model.resources.r4.resources.ResourceR4; 9 import org.sophie2.base.model.resources.r4.keys.Category; 10 import org.sophie2.base.model.resources.r4.keys.Category.Categories; 9 11 10 12 /** 11 13 * An element is defined as an entity that holds a timeline. Such entities are … … 16 18 * 17 19 * @author boyan 18 20 */ 21 @Category(Categories.CATEGORY_PROPERTIES) 19 22 public class ElementR4 extends ResourceR4 { 20 23 21 24 /** -
modules/org.sophie2.main.func.text/src/main/java/org/sophie2/main/func/text/model/TextTemplateR4.java
4 4 import org.sophie2.base.model.resources.r4.keys.Key; 5 5 import org.sophie2.base.model.resources.r4.keys.TemplatedKey; 6 6 import org.sophie2.main.func.text.chaining.ChainingMode; 7 import org.sophie2.base.model.resources.r4.keys.Category; 8 import org.sophie2.base.model.resources.r4.keys.Category.Categories; 7 9 8 10 /** 9 11 * This is needed before implementation of the templating of sub-elements. … … 21 23 * The chain mode of the chain this frame is head of. A string representing 22 24 * whether chaining is automatic, manual or none. By default there is no chaining. 23 25 */ 26 @Category(Categories.CATEGORY_CONTENT) 24 27 public static final Key<ChainingMode> KEY_CHAIN_MODE = 25 28 new TemplatedKey<ChainingMode>("chain-mode", ChainingMode.class, ChainingMode.NO_CHAINING); 26 29 -
modules/org.sophie2.main.app.commons/src/main/java/org/sophie2/main/app/commons/dialogs/TemplateDialog.java
1 package org.sophie2.main.app.commons.dialogs;2 3 import java.awt.BorderLayout;4 import java.awt.Component;5 import java.awt.Dimension;6 import java.awt.Rectangle;7 import java.awt.event.ActionEvent;8 import java.awt.event.ActionListener;9 import java.awt.event.FocusEvent;10 import java.awt.event.FocusListener;11 import java.awt.event.MouseAdapter;12 import java.awt.event.MouseEvent;13 import java.util.ArrayList;14 import java.util.Collection;15 import java.util.Enumeration;16 import java.util.HashMap;17 import java.util.List;18 import java.util.Map;19 import java.util.Map.Entry;20 21 import javax.swing.JButton;22 import javax.swing.JDialog;23 import javax.swing.JFrame;24 import javax.swing.JLabel;25 import javax.swing.JPanel;26 import javax.swing.JScrollPane;27 import javax.swing.JTextField;28 import javax.swing.JTree;29 import javax.swing.tree.DefaultMutableTreeNode;30 import javax.swing.tree.DefaultTreeModel;31 import javax.swing.tree.TreeCellRenderer;32 import javax.swing.tree.TreeModel;33 import javax.swing.tree.TreePath;34 35 import org.sophie2.base.commons.gui.TriStateCheckBox;36 import org.sophie2.base.commons.gui.TriStateCheckBox.State;37 import org.sophie2.base.commons.util.ImmList;38 import org.sophie2.base.commons.util.NaiveImmList;39 import org.sophie2.base.dialogs.Dialog;40 import org.sophie2.base.dialogs.DialogInput;41 import org.sophie2.base.model.book.ElementH;42 import org.sophie2.base.model.resources.r4.ResourceRefR4;43 import org.sophie2.base.model.resources.r4.keys.CompositeKey;44 import org.sophie2.base.model.resources.r4.keys.Key;45 import org.sophie2.base.model.resources.r4.keys.TemplatedKey;46 import org.sophie2.base.model.resources.r4.resources.ResourceR4;47 import org.sophie2.core.mvc.EventFilterBuilder;48 import org.sophie2.core.mvc.OperationDef;49 import org.sophie2.core.mvc.events.EventR3;50 import org.sophie2.main.app.commons.dialogs.TemplateDialog.CheckBoxTree.CheckNode;51 52 /**53 * The dialog which is used to add a page or frame as a template. It generates a54 * tree-like structure of checkboxes which represent the {@link Key}s of the55 * object to create a template from.56 *57 * @author jani58 * @author boyanl59 */60 @SuppressWarnings("unchecked")61 public class TemplateDialog extends Dialog<TemplateDialog.Input, TemplateInfo> {62 63 /**64 * A check-box tree class which extends (and internally uses) JTree.65 *66 * @author boyanl67 */68 public final static class CheckBoxTree extends JTree {69 70 /**71 * Serial version ID for the class.72 */73 private static final long serialVersionUID = -6024233811457619061L;74 75 /**76 * A node class for the tree. It contains just the logical data of a node (in this case, a tricheck model),77 * and a backlink to the container tree (needed for the update logic).78 *79 * @author boyanl80 */81 public static class CheckNode extends DefaultMutableTreeNode {82 83 /**84 * Serial version ID for the class.85 */86 private static final long serialVersionUID = 1701971996798985354L;87 88 /**89 * A backlink to the containing tree.90 * Needed for the visual update of items in setState91 */92 private JTree backlink;93 94 private TriStateCheckBox.TriStateModel state;95 96 /**97 * Default constructor.98 */99 public CheckNode() {100 this(null);101 }102 103 /**104 * Setter of the backlink.105 *106 * @param link107 * The backlink value to set to.108 */109 void setBacklink(JTree link) {110 this.backlink = link;111 }112 113 /**114 * Constructs the node from a user-defined object (just String is used here, for the label text).115 *116 * @param userObject117 * The user-defined object to construct from.118 */119 public CheckNode(Object userObject) {120 this(userObject, true, false);121 }122 123 /**124 * Constructs the node from a user-defined object and sets whether the node allows children and is selected.125 *126 * @param userObject127 * The user-passed object.128 * @param allowsChildren129 * A parameter indicating whether the node allows children.130 * @param isSelected131 * A parameter indicating whether the node is selected.132 */133 public CheckNode(Object userObject, boolean allowsChildren,134 boolean isSelected) {135 super(userObject, allowsChildren);136 this.state = new TriStateCheckBox.TriStateModel(TriStateCheckBox.State.UNCHECKED);137 this.state.setSelected(isSelected);138 }139 140 /**141 * Gets the state of the check node.142 *143 * @return144 * The state of the node145 */146 public TriStateCheckBox.State getState () {147 return this.state.getState();148 }149 150 /**151 * Changes the state of the node to the "next" state.152 */153 public void nextState () {154 this.state.setPressed(true);155 this.setState (this.state.getState());156 }157 158 /**159 * Sets the state of the node. Propagates the changes to children/parent if necessary.160 *161 * @param newState162 * The new state.163 */164 public void setState (TriStateCheckBox.State newState) {165 this.state.setState (newState);166 ((DefaultTreeModel) this.backlink.getModel ()).nodeChanged (this);167 168 // propagate changes to children (if any)169 if (this.children != null && newState != TriStateCheckBox.State.PARTIAL) {170 Enumeration<CheckNode> e = this.children.elements();171 172 while (e.hasMoreElements()) {173 CheckNode node = e.nextElement();174 175 if (node.getState() != newState) {176 node.setState(newState);177 ((DefaultTreeModel) this.backlink.getModel ()).nodeChanged (node);178 }179 }180 }181 182 //propagate to parent (if any)183 if (this.getParent() != null) {184 185 /*186 * If either all the nodes are selected or all are de-selected, change the state187 * to selected/de-selected. Otherwise, partial188 */189 boolean allSelected = true, noneSelected = true;190 Enumeration<CheckNode> e = this.parent.children();191 192 while (e.hasMoreElements()) {193 CheckNode node = e.nextElement();194 if (node.getState () != TriStateCheckBox.State.CHECKED) {195 allSelected = false;196 }197 if (node.getState () != TriStateCheckBox.State.UNCHECKED) {198 noneSelected = false;199 }200 if (!allSelected && !noneSelected) {201 break;202 }203 }204 205 if (allSelected) {206 ((CheckNode) this.parent).setState (TriStateCheckBox.State.CHECKED);207 ((DefaultTreeModel) this.backlink.getModel ()).nodeChanged (this.parent);208 } else if (noneSelected) {209 ((CheckNode) this.parent).setState (TriStateCheckBox.State.UNCHECKED);210 ((DefaultTreeModel) this.backlink.getModel ()).nodeChanged (this.parent);211 } else {212 ((CheckNode) this.parent).setState (TriStateCheckBox.State.PARTIAL);213 ((DefaultTreeModel) this.backlink.getModel ()).nodeChanged (this.parent);214 }215 }216 }217 218 /**219 * Changes the node and updates children/parent accordingly.220 *221 * @param isSelected222 * The new state of the node.223 */224 public void setSelected(boolean isSelected) {225 this.state.setSelected(isSelected);226 setState (this.state.getState());227 }228 229 /**230 * Getter for the isSelected field.231 *232 * @return233 * The field.234 */235 public boolean isSelected() {236 return this.state.isSelected();237 }238 }239 240 /**241 * A renderer class for the tree. Contains the checkbox242 * and the label used to visualize a node in the tree.243 *244 * @author boyanl245 */246 class CheckRenderer extends JPanel implements TreeCellRenderer {247 248 /**249 * Serial Version ID for the class.250 */251 private static final long serialVersionUID = 6223558893132192535L;252 253 private TriStateCheckBox check;254 private JLabel label;255 256 /**257 * Default constructor, initializes the checkbox and the label.258 */259 CheckRenderer() {260 setLayout(null);261 add(this.check = new TriStateCheckBox());262 add(this.label = new JLabel ());263 }264 265 public Component getTreeCellRendererComponent(266 JTree tree, Object value,267 boolean isSelected, boolean expanded, boolean leaf, int row,268 boolean hasFocus) {269 270 String stringValue = tree.convertValueToText(value, isSelected,271 expanded, leaf, row, hasFocus);272 setEnabled(tree.isEnabled());273 this.label.setText (stringValue);274 this.check.setState(((CheckNode) value).getState());275 276 return this;277 }278 279 @Override280 public Dimension getPreferredSize() {281 Dimension d_check = this.check.getPreferredSize();282 Dimension d_label = this.label.getPreferredSize();283 284 return new Dimension(d_check.width + d_label.width,285 (d_check.height < d_label.height ? d_label.height286 : d_check.height));287 }288 289 /**290 * Layouts the component, centering the label and the checkbox height-wise291 */292 @Override293 public void doLayout() {294 Dimension d_check = this.check.getPreferredSize();295 Dimension d_label = this.label.getPreferredSize();296 297 int y_check = 0;298 int y_label = 0;299 if (d_check.height < d_label.height) {300 y_check = (d_label.height - d_check.height) / 2;301 } else {302 y_label = (d_check.height - d_label.height) / 2;303 }304 305 this.check.setLocation(0, y_check);306 this.check.setBounds(0, y_check, d_check.width, d_check.height);307 this.label.setLocation(d_check.width, y_label);308 this.label.setBounds(d_check.width, y_label, d_label.width, d_label.height);309 }310 }311 312 private class NodeSelectionListener extends MouseAdapter {313 private JTree tree;314 315 NodeSelectionListener(final JTree tree) {316 this.tree = tree;317 }318 319 @Override320 public void mouseClicked (MouseEvent e) {321 int x = e.getX ();322 int y = e.getY ();323 int row = this.tree.getRowForLocation(x, y);324 325 TreePath path = this.tree.getPathForRow (row);326 327 if (path != null) {328 CheckNode node = (CheckNode) path.getLastPathComponent();329 node.nextState();330 }331 }332 }333 334 /**335 * Adds a new node with a given text and parent node to the tree.336 *337 * @param nodeText338 * The text.339 * @param parent340 * The parent.341 * @return342 * The added node.343 */344 public CheckNode addNode (String nodeText, CheckNode parent) {345 CheckNode node = new CheckNode (nodeText);346 if (parent != null){347 parent.add(node);348 }349 node.setBacklink(this);350 351 return node;352 }353 /**354 * Adds a new node with a given text and parent node to the tree.355 *356 * @param nodeText357 * The text358 * @return359 * The added node360 */361 public CheckNode addNode (String nodeText) {362 CheckNode node = new CheckNode (nodeText);363 CheckNode root = ((CheckNode) this.getModel ().getRoot ());364 if (root!= null) {365 root.add(node);366 }367 368 node.setBacklink(this);369 return node;370 }371 372 /**373 * Getter of the root node.374 *375 * @return376 * The root node of the tree.377 */378 public CheckNode getRootNode () {379 return (CheckNode) this.getModel().getRoot();380 }381 382 /**383 * Constructs a tree with a given root name.384 *385 * @param rootName386 * The root name.387 */388 public CheckBoxTree (final String rootName) {389 super();390 391 CheckNode root = new CheckNode (rootName);392 TreeModel model = new DefaultTreeModel (root);393 setModel (model);394 395 root.setBacklink (this);396 setCellRenderer (new CheckRenderer());397 addMouseListener(new NodeSelectionListener(this));398 }399 }400 401 402 /*403 * Keys that will be omitted in the checkbox list.404 */405 private final static ImmList<TemplatedKey<?>> HIDDEN_KEYS =406 NaiveImmList.<TemplatedKey<?>>getEmpty();407 408 /**409 * Input for {@link TemplateDialog}.410 */411 public static class Input extends DialogInput<TemplateInfo> {412 413 private Component parent;414 private TemplateInfo info;415 416 /**417 * Constructs an input for the book properties dialog.418 *419 * @param parent420 * The parent which contains the dialog.421 * @param info422 * The initial information for the dialog. Should not be423 * null!424 */425 public Input(Component parent, TemplateInfo info) {426 assert info != null;427 428 this.parent = parent;429 this.info = info;430 }431 432 @Override433 public Class<TemplateInfo> getResponseClass() {434 return TemplateInfo.class;435 }436 437 /**438 * Gets the {@link TemplateInfo} for the dialog.439 *440 * @return441 * The {@link TemplateInfo} for the dialog.442 */443 public TemplateInfo getInfo() {444 return this.info;445 }446 447 /**448 * The parent of the dialog.449 *450 * @return451 * The parent {@link Component} of the dialog.452 */453 public Component getParent() {454 return this.parent;455 }456 457 }458 459 /**460 * The swing representation of the {@link TemplateDialog}. Singleton.461 *462 * @author jani463 */464 protected static class SwingDialog {465 466 /**467 * An inner class that serves as a key in the map that matches a given key from a given resource to a node in the tree.468 */469 private static class NodeCacheKey {470 471 private Key<?> key;472 private ResourceRefR4 resourceRef;473 474 NodeCacheKey (Key<?> key, ResourceRefR4 resourceRef) {475 this.key = key;476 this.resourceRef = resourceRef;477 }478 479 @Override480 public int hashCode() {481 final int prime = 31;482 int result = 1;483 result = prime * result + ((this.key == null) ? 0 : this.key.hashCode());484 result = prime * result485 + ((this.resourceRef == null) ? 0 : this.resourceRef.hashCode());486 487 return result;488 }489 490 @Override491 public boolean equals(Object obj) {492 if (this == obj) {493 return true;494 }495 if (obj == null) {496 return false;497 }498 if (this.getClass() != obj.getClass()) {499 return false;500 }501 502 NodeCacheKey other = (NodeCacheKey) obj;503 if (this.key == null) {504 if (other.key != null) {505 return false;506 }507 } else if (!this.key.equals(other.key)) {508 return false;509 }510 if (this.resourceRef == null) {511 if (other.resourceRef != null) {512 return false;513 }514 } else if (!this.resourceRef.equals(other.resourceRef)) {515 return false;516 }517 518 return true;519 }520 521 522 }523 524 525 /**526 * The instance of the book template helper.527 */528 529 private static SwingDialog instance;530 531 private static final int PANEL_HEIGHT = 300;532 private static final int PANEL_WIDTH = 300;533 private static final String DIALOG_TITLE = "Add template";534 private static final String OK_BUTTON_LABEL = "Add";535 private static final String CANCEL_BUTTON_LABEL = "Cancel";536 private static final String SELECT_ALL_BUTTON_LABEL = "Select all";537 private static final String SELECT_NONE_BUTTON_LABEL = "Select none";538 private static final String TITLE_LABEL = "Template name: ";539 private static final String DEFAULT_TEMPLATE_CHECK_BOX_LABEL = "Use as a default template";540 541 private JDialog dialog = null;542 private JPanel mainPanel;543 private JLabel titleLabel = null;544 private JScrollPane treeScrollPane = null;545 private JTextField titleField = null;546 private JButton okButton = null;547 private JButton cancelButton = null;548 private JButton selectAllButton = null;549 private JButton selectNoneButton = null;550 private CheckBoxTree checkBoxTree = null;551 552 private Map <NodeCacheKey, CheckNode> keyToNodeMap = null;553 private Map <ResourceRefR4, Map <Key<?>, Boolean>> keyStates = null;554 private Map <ResourceRefR4, CheckNode> resourcesCheckNodes;555 private CheckNode isDefaultTemplateNode = null;556 557 /**558 * Holds the initial info for the dialog.559 */560 private TemplateInfo initialInfo;561 562 /**563 * The result info for the dialog.564 *565 * Updated when the user clicks cancel or ok.566 */567 private TemplateInfo resultInfo;568 569 /**570 * Construct the swing representation of the {@link TemplateDialog}.571 *572 */573 private SwingDialog() {574 // nothing575 }576 577 /**578 * Gets the only instance of the swing dialog.579 *580 * @return581 * The instance.582 */583 public static SwingDialog get() {584 if (instance == null) {585 instance = new SwingDialog();586 }587 return instance;588 }589 590 /**591 * Getter for the main panel.592 *593 * @return594 * The main JPanel.595 */596 public JPanel getMainPanel() {597 if (this.mainPanel == null) {598 this.mainPanel = new JPanel();599 this.mainPanel.setLayout(new BorderLayout());600 601 JPanel top = new JPanel();602 top.add(getTitleLabel());603 top.add(getTitleField());604 top.setPreferredSize(new Dimension(250, 60));605 top.add(getSelectAllButton());606 top.add(getSelectNoneButton());607 608 JPanel bottom = new JPanel();609 bottom.add(getOkButton());610 bottom.add(getCancelButton());611 612 this.treeScrollPane = new JScrollPane();613 614 this.mainPanel.add(top, BorderLayout.NORTH);615 this.mainPanel.add(this.treeScrollPane, BorderLayout.CENTER);616 this.mainPanel.add(bottom, BorderLayout.SOUTH);617 618 this.mainPanel.setSize(PANEL_WIDTH, PANEL_HEIGHT);619 }620 621 return this.mainPanel;622 }623 624 /**625 * Getter for the title field.626 *627 * @return628 * The title {@link JTextField}.629 */630 public JTextField getTitleField() {631 if (this.titleField == null) {632 this.titleField = new JTextField();633 this.titleField.setColumns(15);634 this.titleField.setText(this.initialInfo.getTitle());635 this.titleField.addFocusListener(new FocusListener() {636 637 @SuppressWarnings("synthetic-access")638 public void focusLost(FocusEvent e) {639 SwingDialog.this.titleField.select(0, 0);640 641 }642 643 @SuppressWarnings("synthetic-access")644 public void focusGained(FocusEvent e) {645 SwingDialog.this.titleField.select(0,646 SwingDialog.this.titleField.getText().length());647 648 }649 });650 }651 return this.titleField;652 }653 654 /**655 * Getter for the title label.656 *657 * @return658 * The title {@link JLabel}.659 */660 public JLabel getTitleLabel() {661 if (this.titleLabel == null) {662 this.titleLabel = new JLabel();663 this.titleLabel.setText(TITLE_LABEL);664 }665 666 return this.titleLabel;667 }668 669 /**670 * Getter for the ok button.671 *672 * @return673 * The "OK" {@link JButton}.674 */675 public JButton getOkButton() {676 if (this.okButton == null) {677 678 this.okButton = new JButton();679 this.okButton.setText(OK_BUTTON_LABEL);680 this.okButton.addActionListener(new ActionListener() {681 682 @SuppressWarnings("synthetic-access")683 public void actionPerformed(final ActionEvent e) {684 String title = getTitleField().getText();685 boolean isDefault = SwingDialog.this.isDefaultTemplateNode.isSelected();686 687 for (Entry<ResourceRefR4, Map<Key<?>, Boolean>> entry : SwingDialog.this.keyStates.entrySet()) {688 for (Entry<Key<?>, Boolean> keyEntry : entry.getValue().entrySet()) {689 CheckNode node = SwingDialog.this.keyToNodeMap.get (new NodeCacheKey (keyEntry.getKey(), entry.getKey()) );690 if (node != null) {691 keyEntry.setValue(node.isSelected());692 }693 }694 }695 696 List<ResourceRefR4> toDelete = new ArrayList<ResourceRefR4> ();697 for (Entry<ResourceRefR4, CheckNode> entry : SwingDialog.this.resourcesCheckNodes.entrySet()) {698 if (entry.getValue().getState() == State.UNCHECKED) {699 toDelete.add(entry.getKey ());700 }701 }702 703 TemplateInfo res = new TemplateInfo(title, SwingDialog.this.keyStates, toDelete, isDefault);704 705 setResultInfo(res);706 SwingDialog.this.dialog.setVisible(false);707 }708 709 });710 711 }712 return this.okButton;713 }714 715 /**716 * Getter for the cancel button.717 *718 * @return719 * The "Cancel" {@link JButton}.720 */721 public JButton getCancelButton() {722 if (this.cancelButton == null) {723 this.cancelButton = new JButton();724 this.cancelButton.setText(CANCEL_BUTTON_LABEL);725 this.cancelButton.addActionListener(new ActionListener() {726 727 @SuppressWarnings("synthetic-access")728 public void actionPerformed(final ActionEvent e) {729 setResultInfo(null);730 SwingDialog.this.dialog.setVisible(false);731 }732 });733 734 }735 return this.cancelButton;736 }737 738 /**739 * Getter for the "select all" button.740 *741 * @return742 * The "select all" {@link JButton}.743 */744 public JButton getSelectAllButton() {745 if (this.selectAllButton == null) {746 this.selectAllButton = new JButton(SELECT_ALL_BUTTON_LABEL);747 this.selectAllButton.addActionListener(new ActionListener() {748 @SuppressWarnings("synthetic-access")749 public void actionPerformed(final ActionEvent e) {750 SwingDialog.this.checkBoxTree.getRootNode().setSelected(true);751 }752 });753 }754 return this.selectAllButton;755 }756 757 /**758 * Getter for the "select none" button.759 *760 * @return761 * The "select none" {@link JButton}.762 */763 public JButton getSelectNoneButton() {764 if (this.selectNoneButton == null) {765 this.selectNoneButton = new JButton(SELECT_NONE_BUTTON_LABEL);766 this.selectNoneButton.addActionListener(new ActionListener() {767 @SuppressWarnings("synthetic-access")768 public void actionPerformed(final ActionEvent e) {769 SwingDialog.this.checkBoxTree.getRootNode().setSelected(false);770 }771 });772 }773 return this.selectNoneButton;774 }775 776 private void constructTreeFromElement (ElementH element, CheckNode parentNode, ResourceRefR4 rootRef) {777 ResourceRefR4 resourceRef = element.getRef();778 779 /*780 * Hold an absolute reference to the root element and relative references to the sub-elements.781 */782 if (!resourceRef.equals(rootRef)) {783 resourceRef = ResourceRefR4.getRelativeRef (rootRef, resourceRef);784 }785 786 String title = element.getTitle();787 CheckNode thisNode;788 789 if (parentNode == null) { //if no parent node is given, create a new tree and set up its scroll pane790 this.checkBoxTree = new CheckBoxTree (this.initialInfo.getTitle());791 this.isDefaultTemplateNode = this.checkBoxTree.addNode (DEFAULT_TEMPLATE_CHECK_BOX_LABEL);792 this.treeScrollPane.setViewportView(this.checkBoxTree);793 794 thisNode = this.checkBoxTree.getRootNode();795 }796 else {797 thisNode = this.checkBoxTree.addNode (title, parentNode);798 }799 800 this.resourcesCheckNodes.put(resourceRef, thisNode);801 Class <? extends ResourceR4> resourceClass = ResourceR4.getClassByKind( element.getKind() );802 Collection<Key<?>> elementKeys = ResourceR4.getKnownKeys(resourceClass).values();803 804 Map<Key<?>, Boolean> elementKeyMap = new HashMap <Key<?>, Boolean> ();805 this.keyStates.put(resourceRef, elementKeyMap);806 807 boolean initialKeyState = true;808 for (Key<?> key : elementKeys) {809 elementKeyMap.put(key, true);810 }811 812 for (Key<?> key: elementKeys) {813 addKey (thisNode, resourceRef, key, initialKeyState, this.keyToNodeMap, elementKeys, 1);814 }815 816 List<ElementH> childElems = element.getSubElements ();817 for (ElementH child: childElems)818 constructTreeFromElement(child, thisNode, rootRef);819 }820 821 /**822 * Performs setup of the dialog - sets up the title and the panel with823 * the checkboxes.824 */825 private void setup() {826 getTitleField().setText(this.initialInfo.getTitle());827 828 this.keyToNodeMap = new HashMap<NodeCacheKey, CheckNode>();829 this.keyStates = new HashMap<ResourceRefR4, Map<Key<?>, Boolean>> ();830 this.resourcesCheckNodes = new HashMap<ResourceRefR4, CheckNode> ();831 832 ElementH rootElement = this.initialInfo.getRootElement ();833 constructTreeFromElement (rootElement, null, rootElement.getRef());834 this.checkBoxTree.expandPath(new TreePath (this.checkBoxTree.getRootNode()));835 }836 837 /*838 * Helper method to build the tree,839 * adds the key if he's at the appropriate level840 */841 @SuppressWarnings("synthetic-access")842 843 private void addKey (CheckNode parent, final ResourceRefR4 resource, final Key<?> key, final Boolean state, Map<NodeCacheKey, CheckNode> keyControlMap, Collection<Key<?>> keys, int level) {844 if (key instanceof TemplatedKey) {845 if (!HIDDEN_KEYS.contains((TemplatedKey) key) && key.getParts().size() == level) {846 ImmList<String> parts = key.getParts();847 String text = parts.get(parts.size() - 1);848 849 CheckNode node = this.checkBoxTree.addNode(text, parent);850 node.setSelected(state);851 keyControlMap.put(new NodeCacheKey(key, resource), node);852 }853 }854 855 else if (key instanceof CompositeKey && key.getParts().size() == level) {856 ImmList<String> parts = key.getParts();857 String text = parts.get(parts.size() - 1);858 859 CheckNode node = this.checkBoxTree.addNode(text, parent);860 861 String prefix = key.getId().concat(Key.SEPARATOR);862 for (final Key<?> subKey : keys) {863 String keyId = subKey.getId();864 if (keyId.startsWith(prefix)) {865 Boolean subState = this.keyStates.get(resource).get(subKey);866 addKey(node, resource, subKey, subState, this.keyToNodeMap, keys, level + 1);867 }868 }869 }870 }871 872 /**873 * Gets the result info (after the user has clicked ok/cancel).874 *875 * @return876 * The result info or null if canceled.877 */878 public TemplateInfo getResult() {879 return this.resultInfo;880 }881 882 private void setResultInfo(TemplateInfo resultInfo) {883 this.resultInfo = resultInfo;884 }885 886 /**887 * Shows this dialog as a modal to the user.888 *889 * @param input890 * The initial values for the fields.891 * @param parentFrame892 * The frame that this dialog corresponds to.893 */894 public void show(TemplateInfo input, JFrame parentFrame) {895 this.initialInfo = input;896 if (this.dialog == null) {897 this.dialog = new JDialog(parentFrame, true);898 this.dialog.getContentPane().add(getMainPanel());899 this.dialog.setTitle(DIALOG_TITLE);900 this.dialog.setBounds(new Rectangle(0, 0, PANEL_WIDTH, PANEL_HEIGHT));901 this.dialog.setLocationRelativeTo(parentFrame);902 this.dialog.setResizable(false);903 this.dialog.getRootPane().setDefaultButton(getOkButton());904 }905 this.setup();906 this.dialog.setVisible(true);907 }908 }909 910 /**911 * Controller for dialog.912 */913 public static enum DialogLogic implements OperationDef {914 915 /**916 * The submit action.917 */918 ON_SUBMIT {919 public void defineFilter(EventFilterBuilder filter) {920 /*921 * Don't define any filter922 */923 }924 925 public boolean handle(EventR3 event) {926 /*927 * Do nothing in the handle method too928 */929 return false;930 }931 };932 }933 934 @Override935 public Class<Input> getInputClass() {936 return TemplateDialog.Input.class;937 }938 939 @Override940 public Class<TemplateInfo> getOutputClass() {941 return TemplateInfo.class;942 }943 944 @SuppressWarnings("synthetic-access")945 @Override946 protected TemplateInfo show(Input input) {947 assert input != null;948 949 SwingDialog.get().show(input.getInfo(), null);950 951 TemplateInfo result = SwingDialog.get().getResult();952 953 SwingDialog.get().setResultInfo(null);954 955 return result;956 }957 }958 No newline at end of file -
modules/org.sophie2.main.app.commons/src/main/java/org/sophie2/main/app/commons/dialogs/TemplateInfo.java
15 15 * the value of the key should be applied when the template itself is applied. 16 16 * 17 17 * @author jani 18 * @author boyanl 18 19 */ 19 20 @Immutable 20 21 public class TemplateInfo {