From d37b882b2b60e3d63b12f48fce3a721c53486962 Mon Sep 17 00:00:00 2001 From: tms08012 Date: Wed, 6 Jun 2012 22:34:42 -0400 Subject: [PATCH] violet framework --- VioletFramework/VioletFramework/.project | 11 + .../horstmann.eclipse.code.formatter.xml | 246 +++++ VioletFramework/VioletFramework/pom.xml | 96 ++ .../framework/dialog/DialogFactory.java | 341 +++++++ .../framework/dialog/DialogFactory.properties | 11 + .../dialog/DialogFactoryListener.java | 42 + .../framework/dialog/DialogFactoryMode.java | 15 + .../dialog/DialogFactory_fr.properties | 11 + .../violet/framework/file/GraphFile.java | 336 +++++++ .../framework/file/GraphFile.properties | 5 + .../framework/file/GraphFile_fr.properties | 5 + .../violet/framework/file/IFile.java | 10 + .../violet/framework/file/IGraphFile.java | 83 ++ .../framework/file/IGraphFileListener.java | 22 + .../violet/framework/file/LocalFile.java | 74 ++ .../file/chooser/IFileChooserService.java | 84 ++ .../file/chooser/JFileChooserService.java | 224 +++++ .../chooser/JFileChooserService.properties | 3 + .../chooser/JFileChooserService_fr.properties | 3 + .../file/chooser/JNLPFileChooserService.java | 176 ++++ .../file/export/FileExportService.java | 122 +++ .../file/naming/ExtensionFilter.java | 67 ++ .../file/naming/FileNamingService.java | 161 +++ .../file/naming/FileNamingService.properties | 4 + .../naming/FileNamingService_fr.properties | 4 + .../CustomPersistenceDelegate.java | 62 ++ .../persistence/IFilePersistenceService.java | 35 + .../file/persistence/IFileReader.java | 55 + .../file/persistence/IFileWriter.java | 49 + .../file/persistence/JFileReader.java | 38 + .../file/persistence/JFileWriter.java | 43 + .../file/persistence/JNLPFileReader.java | 48 + .../file/persistence/JNLPFileWriter.java | 46 + .../StandardJavaFilePersistenceService.java | 266 +++++ .../Violet016BackportFormatService.java | 201 ++++ .../XStreamBasedPersistenceService.java | 79 ++ .../injection/bean/ManiocFramework.java | 903 +++++++++++++++++ .../resources/ResourceBundleConstant.java | 32 + .../resources/ResourceBundleInjector.java | 218 ++++ .../resources/ResourceBundleUtils.java | 43 + .../injection/resources/ResourceFactory.java | 296 ++++++ .../annotation/ResourceBundleBean.java | 14 + .../framework/plugin/AbstractPlugin.java | 28 + .../framework/plugin/IDiagramPlugin.java | 34 + .../violet/framework/plugin/PluginLoader.java | 127 +++ .../framework/plugin/PluginRegistry.java | 48 + .../Violet016FileFilterExtensionPoint.java | 24 + .../violet/framework/printer/PrintEngine.java | 48 + .../violet/framework/printer/PrintPanel.java | 328 ++++++ .../framework/printer/PrintPanel.properties | 12 + .../printer/PrintPanel_fr.properties | 12 + .../propertyeditor/CustomPropertyEditor.java | 413 ++++++++ .../CustomPropertyEditorLayout.java | 99 ++ .../CustomPropertyEditorSupport.java | 63 ++ .../propertyeditor/ICustomPropertyEditor.java | 31 + .../AbstractDiagramLinkEditor.java | 185 ++++ .../customeditor/ArrowHeadEditor.java | 60 ++ .../customeditor/BentStyleEditor.java | 63 ++ .../customeditor/ChoiceListEditor.java | 70 ++ .../customeditor/ColorEditor.java | 291 ++++++ .../customeditor/LineStyleEditor.java | 57 ++ .../customeditor/MultiLineStringEditor.java | 104 ++ .../customeditor/StringEditor.java | 19 + .../swingextension/CustomToggleButton.java | 180 ++++ .../swingextension/CustomToggleButtonUI.java | 142 +++ .../swingextension/IconButtonUI.java | 214 ++++ .../swingextension/LinkButtonUI.java | 62 ++ .../swingextension/RolloverButtonUI.java | 138 +++ .../swingextension/TinyScrollBarUI.java | 182 ++++ .../violet/framework/theme/AbstractTheme.java | 154 +++ .../violet/framework/theme/BasicTheme.java | 280 ++++++ .../framework/theme/ClassicMetalTheme.java | 222 ++++ .../framework/theme/DarkAmbianceTheme.java | 355 +++++++ .../violet/framework/theme/ITheme.java | 108 ++ .../violet/framework/theme/ThemeInfo.java | 31 + .../violet/framework/theme/ThemeManager.java | 173 ++++ .../framework/theme/VistaBlueTheme.java | 219 ++++ .../AppletUserPreferencesDao.java | 40 + .../DefaultUserPreferencesDao.java | 65 ++ .../userpreferences/IUserPreferencesDao.java | 52 + .../JNLPUserPreferencesDao.java | 142 +++ .../userpreferences/PreferencesConstant.java | 123 +++ .../PreferencesServiceFactory.java | 62 ++ .../userpreferences/PreferredFile.java | 129 +++ .../UserPreferencesService.java | 221 ++++ .../framework/util/BrowserLauncher.java | 80 ++ .../violet/framework/util/ClipboardPipe.java | 102 ++ .../violet/framework/util/GeometryUtils.java | 47 + .../violet/framework/util/GrabberUtils.java | 29 + .../violet/framework/util/NanoHTTPD.java | 698 +++++++++++++ .../violet/framework/util/PropertyUtils.java | 79 ++ .../util/SerializableEnumeration.java | 98 ++ .../framework/util/ShortestPathConverter.java | 111 ++ .../util/StringFilterOutputStream.java | 108 ++ .../framework/util/UniqueIDGenerator.java | 11 + .../violet/framework/util/VersionChecker.java | 101 ++ .../diagram/abstracts/AbstractGraph.java | 329 ++++++ .../product/diagram/abstracts/Direction.java | 137 +++ .../diagram/abstracts/GraphRegistry.java | 61 ++ .../product/diagram/abstracts/IGraph.java | 151 +++ .../diagram/abstracts/IIdentifiable.java | 44 + .../violet/product/diagram/abstracts/Id.java | 85 ++ .../diagram/abstracts/edge/AbstractEdge.java | 226 +++++ .../abstracts/edge/AbstractEdgeBeanInfo.java | 46 + .../product/diagram/abstracts/edge/IEdge.java | 140 +++ .../abstracts/edge/SegmentedLineEdge.java | 456 +++++++++ .../diagram/abstracts/edge/ShapeEdge.java | 64 ++ .../diagram/abstracts/node/AbstractNode.java | 306 ++++++ .../abstracts/node/AbstractNodeBeanInfo.java | 44 + .../abstracts/node/EllipticalNode.java | 72 ++ .../product/diagram/abstracts/node/INode.java | 201 ++++ .../abstracts/node/IResizableNode.java | 8 + .../abstracts/node/RectangularNode.java | 175 ++++ .../node/RectangularNodeBeanInfo.java | 41 + .../product/diagram/abstracts/package.html | 100 ++ .../diagram/abstracts/property/ArrowHead.java | 136 +++ .../diagram/abstracts/property/BentStyle.java | 278 ++++++ .../abstracts/property/ChoiceList.java | 91 ++ .../diagram/abstracts/property/LineStyle.java | 93 ++ .../abstracts/property/MultiLineString.java | 297 ++++++ .../product/diagram/common/DiagramLink.java | 132 +++ .../diagram/common/DiagramLinkNode.java | 167 ++++ .../product/diagram/common/ImageNode.java | 237 +++++ .../product/diagram/common/NoteEdge.java | 66 ++ .../product/diagram/common/NoteNode.java | 196 ++++ .../product/diagram/common/PointNode.java | 80 ++ .../violet/workspace/IWorkspace.java | 65 ++ .../violet/workspace/IWorkspaceListener.java | 53 + .../horstmann/violet/workspace/Workspace.java | 341 +++++++ .../violet/workspace/WorkspacePanel.java | 105 ++ .../workspace/editorpart/EditorPart.java | 277 +++++ .../editorpart/EditorPartBehaviorManager.java | 146 +++ .../EditorPartSelectionHandler.java | 161 +++ .../workspace/editorpart/EditorPartUI.java | 47 + .../workspace/editorpart/EmptyGrid.java | 61 ++ .../workspace/editorpart/IEditorPart.java | 90 ++ .../IEditorPartBehaviorManager.java | 62 ++ .../IEditorPartSelectionHandler.java | 45 + .../violet/workspace/editorpart/IGrid.java | 65 ++ .../workspace/editorpart/PlainGrid.java | 201 ++++ .../behavior/AbstractEditorPartBehavior.java | 170 ++++ .../editorpart/behavior/AddEdgeBehavior.java | 173 ++++ .../editorpart/behavior/AddNodeBehavior.java | 97 ++ .../behavior/ChangeToolByWeelBehavior.java | 47 + .../behavior/CutCopyPasteBehavior.java | 492 +++++++++ .../behavior/DragGraphBehavior.java | 137 +++ .../behavior/DragSelectedBehavior.java | 125 +++ .../behavior/EditSelectedBehavior.java | 161 +++ .../behavior/EditSelectedBehavior.properties | 3 + .../EditSelectedBehavior_fr.properties | 4 + .../behavior/FileCouldBeSavedBehavior.java | 58 ++ .../behavior/IEditorPartBehavior.java | 60 ++ .../behavior/ResizeNodeBehavior.java | 64 ++ .../behavior/SelectAllBehavior.java | 61 ++ .../behavior/SelectByClickBehavior.java | 240 +++++ .../behavior/SelectByDistanceBehavior.java | 116 +++ .../behavior/SelectByLassoBehavior.java | 140 +++ .../ShowMenuOnRightClickBehavior.java | 188 ++++ .../ShowMenuOnRightClickBehavior.properties | 31 + ...ShowMenuOnRightClickBehavior_fr.properties | 31 + .../behavior/SwingRepaintingBehavior.java | 173 ++++ .../behavior/UndoRedoCompoundBehavior.java | 242 +++++ .../behavior/UndoRedoOnAddBehavior.java | 226 +++++ .../behavior/UndoRedoOnDragBehavior.java | 166 +++ .../behavior/UndoRedoOnEditBehavior.java | 112 +++ .../behavior/UndoRedoOnRemoveBehavior.java | 199 ++++ .../behavior/ZoomByWheelBehavior.java | 37 + .../violet/workspace/sidebar/ISideBar.java | 58 ++ .../workspace/sidebar/ISideBarElement.java | 29 + .../sidebar/ScrollPaneNavigatorPanel.java | 232 +++++ .../violet/workspace/sidebar/SideBar.java | 113 +++ .../workspace/sidebar/SideBar.properties | 29 + .../violet/workspace/sidebar/SideBarUI.java | 84 ++ .../workspace/sidebar/SideBar_fr.properties | 27 + .../sidebar/editortools/EditorToolsPanel.java | 270 +++++ .../editortools/EditorToolsPanelUI.java | 150 +++ .../sidebar/graphtools/GraphTool.java | 215 ++++ .../sidebar/graphtools/GraphToolsBar.java | 279 ++++++ .../graphtools/GraphToolsBarButton.java | 46 + .../graphtools/GraphToolsBarPanel.java | 302 ++++++ .../graphtools/GraphToolsBarPanelUI.java | 33 + .../sidebar/graphtools/IGraphToolsBar.java | 83 ++ .../graphtools/IGraphToolsBarListener.java | 19 + .../optionaltools/OptionalToolsPanel.java | 126 +++ .../optionaltools/OptionalToolsPanelUI.java | 92 ++ .../properties/FileStrings.properties | 11 + .../properties/FileStrings_fr.properties | 11 + .../properties/NodeAndEdgeStrings.properties | 19 + .../NodeAndEdgeStrings_fr.properties | 19 + .../properties/OtherStrings.properties | 13 + .../properties/OtherStrings_fr.properties | 13 + .../SyntaxDiagramStrings.properties | 6 + .../src/main/resources/violet.jnlp.template | 24 + .../src/main/resources/xsl/violet2xmi.xsl | 944 ++++++++++++++++++ .../src/main/resources/xsl/xmi2htmldoc.xsl | 781 +++++++++++++++ .../src/main/resources/xsl/xmi2java.xsl | 218 ++++ .../src/main/resources/xsl/xmi2mpd.xsl | 670 +++++++++++++ .../src/main/resources/xsl/xmi2python.xsl | 288 ++++++ .../src/main/resources/xsl/xmi2ruby.xsl | 237 +++++ .../src/main/resources/xsl/xmi2sql.xsl | 517 ++++++++++ .../src/main/resources/xsl/xmi2xmi.xsl | 67 ++ 201 files changed, 27214 insertions(+) create mode 100644 VioletFramework/VioletFramework/.project create mode 100644 VioletFramework/VioletFramework/horstmann.eclipse.code.formatter.xml create mode 100644 VioletFramework/VioletFramework/pom.xml create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactory.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactory.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactoryListener.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactoryMode.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactory_fr.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/GraphFile.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/GraphFile.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/GraphFile_fr.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/IFile.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/IGraphFile.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/IGraphFileListener.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/LocalFile.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/IFileChooserService.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JFileChooserService.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JFileChooserService.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JFileChooserService_fr.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JNLPFileChooserService.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/export/FileExportService.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/ExtensionFilter.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/FileNamingService.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/FileNamingService.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/FileNamingService_fr.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/CustomPersistenceDelegate.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/IFilePersistenceService.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/IFileReader.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/IFileWriter.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JFileReader.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JFileWriter.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JNLPFileReader.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JNLPFileWriter.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/StandardJavaFilePersistenceService.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/Violet016BackportFormatService.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/XStreamBasedPersistenceService.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/bean/ManiocFramework.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceBundleConstant.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceBundleInjector.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceBundleUtils.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceFactory.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/annotation/ResourceBundleBean.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/AbstractPlugin.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/IDiagramPlugin.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/PluginLoader.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/PluginRegistry.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/extensionpoint/Violet016FileFilterExtensionPoint.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintEngine.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintPanel.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintPanel.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintPanel_fr.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/CustomPropertyEditor.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/CustomPropertyEditorLayout.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/CustomPropertyEditorSupport.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/ICustomPropertyEditor.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/AbstractDiagramLinkEditor.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/ArrowHeadEditor.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/BentStyleEditor.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/ChoiceListEditor.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/ColorEditor.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/LineStyleEditor.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/MultiLineStringEditor.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/StringEditor.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/CustomToggleButton.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/CustomToggleButtonUI.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/IconButtonUI.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/LinkButtonUI.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/RolloverButtonUI.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/TinyScrollBarUI.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/AbstractTheme.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/BasicTheme.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ClassicMetalTheme.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/DarkAmbianceTheme.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ITheme.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ThemeInfo.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ThemeManager.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/VistaBlueTheme.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/AppletUserPreferencesDao.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/DefaultUserPreferencesDao.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/IUserPreferencesDao.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/JNLPUserPreferencesDao.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/PreferencesConstant.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/PreferencesServiceFactory.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/PreferredFile.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/UserPreferencesService.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/BrowserLauncher.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/ClipboardPipe.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/GeometryUtils.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/GrabberUtils.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/NanoHTTPD.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/PropertyUtils.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/SerializableEnumeration.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/ShortestPathConverter.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/StringFilterOutputStream.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/UniqueIDGenerator.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/VersionChecker.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/AbstractGraph.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/Direction.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/GraphRegistry.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/IGraph.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/IIdentifiable.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/Id.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/AbstractEdge.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/AbstractEdgeBeanInfo.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/IEdge.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/SegmentedLineEdge.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/ShapeEdge.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/AbstractNode.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/AbstractNodeBeanInfo.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/EllipticalNode.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/INode.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/IResizableNode.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/RectangularNode.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/RectangularNodeBeanInfo.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/package.html create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/ArrowHead.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/BentStyle.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/ChoiceList.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/LineStyle.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/MultiLineString.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/DiagramLink.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/DiagramLinkNode.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/ImageNode.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/NoteEdge.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/NoteNode.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/PointNode.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/IWorkspace.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/IWorkspaceListener.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/Workspace.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/WorkspacePanel.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPart.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPartBehaviorManager.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPartSelectionHandler.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPartUI.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EmptyGrid.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IEditorPart.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IEditorPartBehaviorManager.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IEditorPartSelectionHandler.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IGrid.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/PlainGrid.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/AbstractEditorPartBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/AddEdgeBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/AddNodeBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ChangeToolByWeelBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/CutCopyPasteBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/DragGraphBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/DragSelectedBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/EditSelectedBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/EditSelectedBehavior.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/EditSelectedBehavior_fr.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/FileCouldBeSavedBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/IEditorPartBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ResizeNodeBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectAllBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectByClickBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectByDistanceBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectByLassoBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ShowMenuOnRightClickBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ShowMenuOnRightClickBehavior.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ShowMenuOnRightClickBehavior_fr.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SwingRepaintingBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoCompoundBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnAddBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnDragBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnEditBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnRemoveBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ZoomByWheelBehavior.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/ISideBar.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/ISideBarElement.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/ScrollPaneNavigatorPanel.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBar.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBar.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBarUI.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBar_fr.properties create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/editortools/EditorToolsPanel.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/editortools/EditorToolsPanelUI.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphTool.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBar.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBarButton.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBarPanel.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBarPanelUI.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/IGraphToolsBar.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/IGraphToolsBarListener.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/optionaltools/OptionalToolsPanel.java create mode 100644 VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/optionaltools/OptionalToolsPanelUI.java create mode 100644 VioletFramework/VioletFramework/src/main/resources/properties/FileStrings.properties create mode 100644 VioletFramework/VioletFramework/src/main/resources/properties/FileStrings_fr.properties create mode 100644 VioletFramework/VioletFramework/src/main/resources/properties/NodeAndEdgeStrings.properties create mode 100644 VioletFramework/VioletFramework/src/main/resources/properties/NodeAndEdgeStrings_fr.properties create mode 100644 VioletFramework/VioletFramework/src/main/resources/properties/OtherStrings.properties create mode 100644 VioletFramework/VioletFramework/src/main/resources/properties/OtherStrings_fr.properties create mode 100644 VioletFramework/VioletFramework/src/main/resources/properties/SyntaxDiagramStrings.properties create mode 100644 VioletFramework/VioletFramework/src/main/resources/violet.jnlp.template create mode 100644 VioletFramework/VioletFramework/src/main/resources/xsl/violet2xmi.xsl create mode 100644 VioletFramework/VioletFramework/src/main/resources/xsl/xmi2htmldoc.xsl create mode 100644 VioletFramework/VioletFramework/src/main/resources/xsl/xmi2java.xsl create mode 100644 VioletFramework/VioletFramework/src/main/resources/xsl/xmi2mpd.xsl create mode 100644 VioletFramework/VioletFramework/src/main/resources/xsl/xmi2python.xsl create mode 100644 VioletFramework/VioletFramework/src/main/resources/xsl/xmi2ruby.xsl create mode 100644 VioletFramework/VioletFramework/src/main/resources/xsl/xmi2sql.xsl create mode 100644 VioletFramework/VioletFramework/src/main/resources/xsl/xmi2xmi.xsl diff --git a/VioletFramework/VioletFramework/.project b/VioletFramework/VioletFramework/.project new file mode 100644 index 0000000..75c3699 --- /dev/null +++ b/VioletFramework/VioletFramework/.project @@ -0,0 +1,11 @@ + + + VioletFramework + + + + + + + + diff --git a/VioletFramework/VioletFramework/horstmann.eclipse.code.formatter.xml b/VioletFramework/VioletFramework/horstmann.eclipse.code.formatter.xml new file mode 100644 index 0000000..bef0923 --- /dev/null +++ b/VioletFramework/VioletFramework/horstmann.eclipse.code.formatter.xml @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/VioletFramework/VioletFramework/pom.xml b/VioletFramework/VioletFramework/pom.xml new file mode 100644 index 0000000..92fda6f --- /dev/null +++ b/VioletFramework/VioletFramework/pom.xml @@ -0,0 +1,96 @@ + + + 4.0.0 + com.horstmann.violet.framework + com.horstmann.violet.framework + Violet UML Editor Framework + 2.0.0-SNAPSHOT + + jar + + + antlr + antlr + 2.7.5 + + + jaxme + jaxmejs + 0.5 + + + javax.javaws + javaws + 1.6.0 + + + net.java.dev.pgslookandfeel + pgslookandfeel + 1.1.1 + + + com.l2fprod.common + l2fprod-common-tasks + 7.3 + + + commons-vfs + commons-vfs + 1.0 + + + com.thoughtworks.xstream + xstream + 1.4.2 + + + batik + batik-swing + 1.6-1 + + + + + + src/main/resources + + + src/main/java + + **/*.gif + **/*.jpg + **/*.png + **/*.txt + **/*.properties + **/*.xml + + + + + + maven-compiler-plugin + 2.0.2 + + 1.6 + 1.6 + + + + + + + web.sourceforge.net + Violet's Maven Repository + sftp://web.sourceforge.net/home/groups/v/vi/violet/htdocs/maven2/repo/ + + + + + violet.repo + Violet's Maven repository (public access) + http://violet.sourceforge.net/maven2/repo/ + + + diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactory.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactory.java new file mode 100644 index 0000000..499a5bd --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactory.java @@ -0,0 +1,341 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.dialog; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.Frame; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.ImageIcon; +import javax.swing.JDialog; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; + +import com.horstmann.violet.framework.injection.bean.ManiocFramework.ManagedBean; +import com.horstmann.violet.framework.injection.resources.ResourceBundleInjector; +import com.horstmann.violet.framework.injection.resources.annotation.ResourceBundleBean; +import com.horstmann.violet.framework.theme.ThemeManager; + +/** + * This factory manages dialog. It could create dialogs or call a listener if the dialog constrction is delegated to an external + * program such as an Eclipse plugin + * + * @author Alexandre de Pellegrin + * + */ +@ManagedBean(registeredManually=true) +public class DialogFactory +{ + + /** + * Default constructor. + */ + public DialogFactory(DialogFactoryMode factoryMode) + { + this.factoryMode = factoryMode; + ResourceBundleInjector.getInjector().inject(this); + } + + public static DialogFactory getInstance() { + throw new RuntimeException("Java method no longer reachable. Code needs to be refactored.") +; } + + + /** + * Adds a listener that could be invoked if the factory is set to DELEGATED_MODE + * + * @param listener + */ + public void setListener(DialogFactoryListener listener) + { + this.listener = listener; + performCachedDialogs(); + } + + /** + * If the dialog factory is configured to DELEGATED_MODE and does not still have a listener that listens and display dialogs, + * all dialogs are put into a kind of cache. Then the listener is registered, all cached dialogs are sent to it to be displayed. + */ + private void performCachedDialogs() + { + while (!this.delegatedDialogCache.isEmpty()) + { + Object[] object = (Object[]) this.delegatedDialogCache.get(0); + JOptionPane optionPane = (JOptionPane) object[0]; + String title = (String) object[1]; + Boolean isModal = (Boolean) object[2]; + invokeListener(optionPane, title, isModal.booleanValue()); + this.delegatedDialogCache.remove(0); + } + } + + public void showErrorDialog(String message) + { + JOptionPane optionPane = new JOptionPane(); + JLabel label = new JLabel(message); + label.setFont(label.getFont().deriveFont(Font.PLAIN)); + optionPane.setIcon(this.genericErrorImageIcon); + optionPane.setMessage(label); + showDialog(optionPane, this.genericErrorTitle, true); + } + + public void showWarningDialog(String message) + { + JOptionPane optionPane = new JOptionPane(); + JLabel label = new JLabel(message); + label.setFont(label.getFont().deriveFont(Font.PLAIN)); + optionPane.setIcon(this.genericWarningImageIcon); + optionPane.setMessage(label); + showDialog(optionPane, this.genericWarningTitle, true); + } + + public void showInformationDialog(String message) + { + JOptionPane optionPane = new JOptionPane(); + JLabel label = new JLabel(message); + label.setFont(label.getFont().deriveFont(Font.PLAIN)); + optionPane.setIcon(this.genericInfoImageIcon); + optionPane.setMessage(label); + showDialog(optionPane, this.genericInfoTitle, true); + } + + /** + * According to the choosen mode : creates and shows a dialog or invokes via the listeners external program that must create and + * display this dialog. + * + * @param optionPane + * @param title + * @param isModal + */ + public void showDialog(JOptionPane optionPane, String title, boolean isModal) + { + updateBackgroundColor(optionPane); + if (this.factoryMode.equals(DialogFactoryMode.INTERNAL)) + { + createDialog(optionPane, title, isModal); + } + if (this.factoryMode.equals(DialogFactoryMode.DELEGATED)) + { + invokeListener(optionPane, title, isModal); + } + } + + /** + * Creates (and displays) a dialog + * + * @param optionPane + * @param title + * @param isModal + */ + private void createDialog(final JOptionPane optionPane, String title, boolean isModal) + { + Frame mainFrame = getDialogOwner(); + final JDialog dialog = new JDialog(mainFrame, title, isModal); + dialog.setTitle(title); + dialog.getContentPane().add(optionPane); + dialog.pack(); + dialog.setResizable(false); + optionPane.addPropertyChangeListener(new PropertyChangeListener() + { + public void propertyChange(PropertyChangeEvent event) + { + if (dialog.isVisible() && (event.getPropertyName().equals(JOptionPane.VALUE_PROPERTY)) + && event.getNewValue() != null && event.getNewValue() != JOptionPane.UNINITIALIZED_VALUE) + { + dialog.setVisible(false); + dialog.dispose(); + } + } + }); + centerDialog(dialog, mainFrame); + dialog.setVisible(true); + } + + /** + * Updates the jOptionPane background color for all its components + * + * @param optionPane + */ + private void updateBackgroundColor(Component component) + { + + // Replace color if only its color is the default background panel color + // (Could be really optimized) + if (component.getBackground().equals(JPANEL_BG_COLOR)) + { + component.setBackground(ThemeManager.getInstance().getTheme().getBackgroundColor()); + } + + if (component instanceof Container) + { + Container container = (Container) component; + for (int i = 0, ub = container.getComponentCount(); i < ub; ++i) + updateBackgroundColor(container.getComponent(i)); + } + + } + + /** + * Set dialog location to the center of the owner frame repscting the screen bound + * + * @param dialog + * @param owner + */ + private void centerDialog(JDialog dialog, Frame owner) + { + Rectangle b = owner.getBounds(); + + double x = b.getX() + b.getWidth() / 2 - dialog.getWidth() / 2; + double y = b.getY() + b.getHeight() / 2 - dialog.getHeight() / 2; + Dimension screenSize = owner.getToolkit().getScreenSize(); + if (x + dialog.getWidth() > screenSize.getWidth()) + { + x = screenSize.getWidth() - dialog.getWidth(); + } + if (y + dialog.getHeight() > screenSize.getHeight()) + { + y = screenSize.getHeight() - dialog.getHeight(); + } + Point newLocation = new Point(Math.max((int) x, 0), Math.max((int) y, 0)); + dialog.setLocation(newLocation); + } + + /** + * Calls listener that must display a dialog + * + * @param optionPane + * @param title + * @param isModal + */ + private void invokeListener(JOptionPane optionPane, String title, boolean isModal) + { + if (listener != null) + { + listener.mustDisplayPanel(optionPane, title, isModal); + } + if (listener == null) + { + this.delegatedDialogCache.add(new Object[] + { + optionPane, + title, + new Boolean(isModal) + }); + } + } + + /** + * Sets the dialog owner. This argument is mandatory. Anyway, I think that, as it usually never changes, it is not necessary to + * give it as argument each time we construct a dialog. Only one time is efficient. + * + * @param owner + */ + public void setDialogOwner(Frame owner) + { + this.dialogOwner = owner; + } + + /** + * @return current's main frame + */ + private Frame getDialogOwner() { + if (this.dialogOwner != null) { + return this.dialogOwner; + } + return getEmergencyDialogOwner(); + } + + /** + * Return a temporary frame if the real frame owner is not yet set + * (for example, + */ + private Frame getEmergencyDialogOwner() + { + JFrame emergencyFrame = new JFrame(); + Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); + int screenWidth = (int) screenSize.getWidth(); + int screenHeight = (int) screenSize.getHeight(); + emergencyFrame.setBounds(screenWidth / 16, screenHeight / 16, screenWidth * 7 / 8, screenHeight * 7 / 8); + return emergencyFrame; + } + + + + + /** + * Listener that have the responsability to create and display dialogs Note that ther is one and only one listener. Why? Because + * of the Eclipse framework that create an instance of the application for each document opened, and in the same JVM. + */ + private DialogFactoryListener listener; + + /** + * Working mode + */ + private DialogFactoryMode factoryMode; + + /** + * The frame that owns all dialogs + */ + private Frame dialogOwner; + + /** + * JPanel default background color + */ + private static final Color JPANEL_BG_COLOR = (new JPanel()).getBackground(); + + /** + * Delegated dialog cache (Used if the dialog mode is set to delegated and no listener is registered. So, the foactory stores + * doalogs that will be displayed when a new listener will be registered + */ + private List delegatedDialogCache = new ArrayList(); + + @ResourceBundleBean(key="dialog.generic.error.icon") + private ImageIcon genericErrorImageIcon; + + @ResourceBundleBean(key="dialog.generic.error.title") + private String genericErrorTitle; + + @ResourceBundleBean(key="dialog.generic.warning.icon") + private ImageIcon genericWarningImageIcon; + + @ResourceBundleBean(key="dialog.generic.warning.title") + private String genericWarningTitle; + + @ResourceBundleBean(key="dialog.generic.information.icon") + private ImageIcon genericInfoImageIcon; + + @ResourceBundleBean(key="dialog.generic.information.title") + private String genericInfoTitle; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactory.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactory.properties new file mode 100644 index 0000000..bc5ca9a --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactory.properties @@ -0,0 +1,11 @@ +dialog.properties.title=Properties +dialog.properties.empty_bean_message=No property to edit. +dialog.error_version.title=Error +dialog.error_version.text=You need Java version {0} +dialog.generic.error.title=Error +dialog.generic.error.icon=/icons/64x64/error.png +dialog.generic.warning.title=Warning +dialog.generic.warning.icon=/icons/64x64/warning.png +dialog.generic.information.title=Information +dialog.generic.information.icon=/icons/64x64/information.png + diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactoryListener.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactoryListener.java new file mode 100644 index 0000000..77466a0 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactoryListener.java @@ -0,0 +1,42 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.dialog; + +import javax.swing.JOptionPane; + +/** + * This interface should be implemented if the dialog of this application must be delegate to anoter system (for an Eclipse plugin + * for example...) + * + * @author Alexandre de Pellegrin + * + */ +public interface DialogFactoryListener +{ + + /** + * Invoked when the listener should display this JPanel given in argument + * + */ + void mustDisplayPanel(JOptionPane optionPane, String title, boolean isModal); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactoryMode.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactoryMode.java new file mode 100644 index 0000000..ee517c9 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactoryMode.java @@ -0,0 +1,15 @@ +package com.horstmann.violet.framework.dialog; + +/** + * Indicates if dialog boxes are managed by the program or are delegated to a container + * + * To understand it, consider that dialogs could be created internally or could be delegated to external + * code. For example, it will be delegated if the program is embedded into an Eclipse plugin that uses the SWT API. + * + * @author Alexandre de Pellegrin + * + */ +public enum DialogFactoryMode +{ + INTERNAL, DELEGATED +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactory_fr.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactory_fr.properties new file mode 100644 index 0000000..cdba209 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/dialog/DialogFactory_fr.properties @@ -0,0 +1,11 @@ +dialog.properties.title=Propri\u00E9t\u00E9s +dialog.properties.empty_bean_message=Aucune propri\u00E9t\u00E9 \u00E0 \u00E9diter. +dialog.error_version.title=Erreur +dialog.error_version.text=Vous devez avoir Java version {0} +dialog.generic.error.title=Erreur +dialog.generic.error.icon=/icons/64x64/error.png +dialog.generic.warning.title=Attention +dialog.generic.warning.icon=/icons/64x64/warning.png +dialog.generic.information.title=Information +dialog.generic.information.icon=/icons/64x64/information.png + diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/GraphFile.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/GraphFile.java new file mode 100644 index 0000000..340f1e4 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/GraphFile.java @@ -0,0 +1,336 @@ +package com.horstmann.violet.framework.file; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.text.MessageFormat; +import java.util.ArrayList; +import java.util.List; + +import javax.imageio.ImageIO; +import javax.swing.ImageIcon; +import javax.swing.JOptionPane; + +import com.horstmann.violet.framework.dialog.DialogFactory; +import com.horstmann.violet.framework.file.chooser.IFileChooserService; +import com.horstmann.violet.framework.file.export.FileExportService; +import com.horstmann.violet.framework.file.naming.ExtensionFilter; +import com.horstmann.violet.framework.file.naming.FileNamingService; +import com.horstmann.violet.framework.file.persistence.IFilePersistenceService; +import com.horstmann.violet.framework.file.persistence.IFileReader; +import com.horstmann.violet.framework.file.persistence.IFileWriter; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.BeanInjector; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.framework.injection.resources.ResourceBundleInjector; +import com.horstmann.violet.framework.injection.resources.annotation.ResourceBundleBean; +import com.horstmann.violet.framework.printer.PrintEngine; +import com.horstmann.violet.product.diagram.abstracts.IGraph; + +public class GraphFile implements IGraphFile +{ + /** + * Creates a new graph file with a new graph instance + * + * @param graphClass + */ + public GraphFile(Class graphClass) + { + ResourceBundleInjector.getInjector().inject(this); + BeanInjector.getInjector().inject(this); + try + { + this.graph = graphClass.newInstance(); + } + catch (Exception e) + { + DialogFactory.getInstance().showErrorDialog(e.getMessage()); + throw new RuntimeException(e); + } + } + + /** + * Constructs a graph file from an existing file + * + * @param file + * + */ + public GraphFile(IFile file) throws IOException + { + ResourceBundleInjector.getInjector().inject(this); + BeanInjector.getInjector().inject(this); + IFileReader fileOpener = fileChooserService.getFileReader(file); + if (fileOpener == null) + { + throw new IOException("Open file action cancelled by user"); + } + InputStream in = fileOpener.getInputStream(); + if (in != null) + { + this.graph = this.filePersistenceService.read(in); + this.currentFilename = fileOpener.getFileDefinition().getFilename(); + this.currentDirectory = fileOpener.getFileDefinition().getDirectory(); + } + else + { + throw new IOException("Unable to read file " + fileOpener.getFileDefinition().getFilename() + " from location " + fileOpener.getFileDefinition().getDirectory()); + } + } + + @Override + public IGraph getGraph() + { + return this.graph; + } + + @Override + public String getFilename() + { + return this.currentFilename; + } + + @Override + public String getDirectory() + { + return this.currentDirectory; + } + + @Override + public void setSaveRequired() + { + this.isSaveRequired = true; + fireGraphModified(); + } + + @Override + public boolean isSaveRequired() + { + return this.isSaveRequired; + } + + /** + * Indicates if this file is new + * @return b + */ + private boolean isNewFile() { + if (this.currentFilename == null && this.currentDirectory == null) { + return true; + } + return false; + } + + @Override + public void save() + { + if (this.isNewFile()) { + saveToNewLocation(); + return; + } + try + { + IFileWriter fileSaver = getFileSaver(false); + OutputStream outputStream = fileSaver.getOutputStream(); + this.filePersistenceService.write(this.graph, outputStream); + this.isSaveRequired = false; + fireGraphSaved(); + this.currentFilename = fileSaver.getFileDefinition().getFilename(); + this.currentDirectory = fileSaver.getFileDefinition().getDirectory(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + @Override + public void saveToNewLocation() + { + try + { + IFileWriter fileSaver = getFileSaver(true); + if (fileSaver == null) { + // This appends when the action is cancelled + return; + } + OutputStream outputStream = fileSaver.getOutputStream(); + this.filePersistenceService.write(this.graph, outputStream); + this.isSaveRequired = false; + this.currentFilename = fileSaver.getFileDefinition().getFilename(); + this.currentDirectory = fileSaver.getFileDefinition().getDirectory(); + fireGraphSaved(); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + /** + * Returns a IFileSaver instance. Then, this object allows to save graph content. If the graph has never been saved, the + * FileChooserService
+ * will open a dialog box to select a location. If not, the returned IFileSaver will automatically be bound to the last saving + * location.
+ * You can also force the FileChooserService to open the dialog box with the given argument.
+ * + * @param isAskedForNewLocation if true, then the FileChooser will open a dialog box to allow to choice a new location + * @return f + */ + private IFileWriter getFileSaver(boolean isAskedForNewLocation) + { + try + { + if (isAskedForNewLocation) + { + ExtensionFilter extensionFilter = this.fileNamingService.getExtensionFilter(this.graph); + ExtensionFilter[] array = + { + extensionFilter + }; + return this.fileChooserService.chooseAndGetFileWriter(array); + } + return this.fileChooserService.getFileWriter(this); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + @Override + public void addListener(IGraphFileListener listener) + { + synchronized (listeners) + { + listeners.add(listener); + } + } + + @Override + public void removeListener(IGraphFileListener listener) + { + synchronized (listeners) + { + listeners.remove(listener); + } + } + + /** + * Sends an event to listeners each time the graph is modified + */ + private void fireGraphModified() + { + synchronized (listeners) + { + for (IGraphFileListener listener : listeners) { + listener.onFileModified(); + } + } + } + + /** + * Sends an event to listeners when the graph has been saved + */ + private void fireGraphSaved() + { + synchronized (listeners) + { + for (IGraphFileListener listener : listeners) { + listener.onFileSaved(); + } + } + } + + @Override + public void exportToClipboard() + { + FileExportService.exportToclipBoard(this.graph); + JOptionPane optionPane = new JOptionPane(); + optionPane.setIcon(this.clipBoardDialogIcon); + optionPane.setMessage(this.clipBoardDialogMessage); + optionPane.setName(this.clipBoardDialogTitle); + this.dialogFactory.showDialog(optionPane, this.clipBoardDialogTitle, true); + } + + @Override + public void exportImage(OutputStream out, String format) + { + if (!ImageIO.getImageWritersByFormatName(format).hasNext()) + { + MessageFormat formatter = new MessageFormat(this.exportImageErrorMessage); + String message = formatter.format(new Object[] + { + format + }); + JOptionPane optionPane = new JOptionPane(); + optionPane.setMessage(message); + this.dialogFactory.showDialog(optionPane, this.exportImageDialogTitle, true); + return; + } + try + { + + try + { + ImageIO.write(FileExportService.getImage(this.graph), format, out); + } + finally + { + out.close(); + } + } + catch (Exception e) + { + throw new RuntimeException(e); + } + } + + @Override + public void exportToPrinter() + { + PrintEngine engine = new PrintEngine(this.graph); + engine.start(); + } + + private IGraph graph; + + /** + * Needed to identify the physical file used to save the graph + */ + private String currentFilename; + + /** + * Needed to identify the physical file used to save the graph + */ + private String currentDirectory; + + private boolean isSaveRequired = false; + + @ResourceBundleBean(key = "dialog.export_to_clipboard.icon") + private ImageIcon clipBoardDialogIcon; + + @ResourceBundleBean(key = "dialog.export_to_clipboard.title") + private String clipBoardDialogTitle; + + @ResourceBundleBean(key = "dialog.export_to_clipboard.ok") + private String clipBoardDialogMessage; + + @ResourceBundleBean(key = "dialog.error.unsupported_image") + private String exportImageErrorMessage; + + @ResourceBundleBean(key = "dialog.error.title") + private String exportImageDialogTitle; + + @InjectedBean + private IFileChooserService fileChooserService; + + @InjectedBean + private FileNamingService fileNamingService; + + @InjectedBean + private IFilePersistenceService filePersistenceService; + + @InjectedBean + private DialogFactory dialogFactory; + + private List listeners = new ArrayList(); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/GraphFile.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/GraphFile.properties new file mode 100644 index 0000000..b6c31d8 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/GraphFile.properties @@ -0,0 +1,5 @@ +dialog.export_to_clipboard.title=To clipboard... +dialog.export_to_clipboard.ok=Current diagram image exported to clipboard +dialog.export_to_clipboard.icon=/icons/64x64/exporttoclipboard.png +dialog.error.unsupported_image={0} not supported +dialog.error.title=Error \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/GraphFile_fr.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/GraphFile_fr.properties new file mode 100644 index 0000000..221c482 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/GraphFile_fr.properties @@ -0,0 +1,5 @@ +dialog.export_to_clipboard.title=Vers le presse papier... +dialog.export_to_clipboard.ok=Image du diagramme en cours export\u00e9e vers le presse papier +dialog.export_to_clipboard.icon=/icons/64x64/exporttoclipboard.png +dialog.error.unsupported_image={0} non support\u00e9 +dialog.error.title=Erreur \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/IFile.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/IFile.java new file mode 100644 index 0000000..f7a413e --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/IFile.java @@ -0,0 +1,10 @@ +package com.horstmann.violet.framework.file; + +public interface IFile +{ + + public abstract String getFilename(); + + public abstract String getDirectory(); + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/IGraphFile.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/IGraphFile.java new file mode 100644 index 0000000..9bd598b --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/IGraphFile.java @@ -0,0 +1,83 @@ +package com.horstmann.violet.framework.file; + +import java.io.OutputStream; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; + +public interface IGraphFile extends IFile +{ + + /** + * @return associated graph + */ + public abstract IGraph getGraph(); + + + /** + * Must be triggered to allow file saving + */ + public abstract void setSaveRequired(); + + /** + * To know if the graph needs to be saved + * @return true if save is required + */ + public abstract boolean isSaveRequired(); + + + /** + * Saves the graph + */ + public abstract void save(); + + /** + * Saves the graph to a new URI (thiw will open a file chooser) + */ + public abstract void saveToNewLocation(); + + /** + * Adds a listener to be informed each time the graph is modified or saved + * + * @param listener + */ + public abstract void addListener(IGraphFileListener listener); + + /** + * Removes a listener + * + * @param listener + */ + public abstract void removeListener(IGraphFileListener listener); + + /** + * Exports the graph as an image to the clipboard and shows a dialog box on the parent frame when it is done. + * + */ + public abstract void exportToClipboard(); + + /** + * Exports the current graph to an image file. + * + * @param out the output stream + * @param format the desired file format + */ + public abstract void exportImage(OutputStream out, String format); + + /** + * Prints the graph + * + */ + public abstract void exportToPrinter(); + + + /** + * @return current file name (or null if not saved yet) + */ + public String getFilename(); + + /** + * @return graph file location (or null if not saved yet) + */ + public String getDirectory(); + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/IGraphFileListener.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/IGraphFileListener.java new file mode 100644 index 0000000..ef2a66f --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/IGraphFileListener.java @@ -0,0 +1,22 @@ +package com.horstmann.violet.framework.file; + +/** + * GraphFile listener + * + * @author Alexandre de Pellegrin + * + */ +public interface IGraphFileListener +{ + + /** + * Invoked when any modification is made on the graph + */ + public void onFileModified(); + + /** + * Invoked when graph has been saved + */ + public void onFileSaved(); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/LocalFile.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/LocalFile.java new file mode 100644 index 0000000..1235012 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/LocalFile.java @@ -0,0 +1,74 @@ +package com.horstmann.violet.framework.file; + +import java.io.File; +import java.io.IOException; + + +/** + * Represents a file on the local hard disk + * + * @author Alexandre de Pellegrin + * + */ +public class LocalFile implements IFile +{ + + /** + * Construct an instance from a java file + * + * @param fullPath + * @throws IOException is not found on disk + */ + public LocalFile(File file) throws IOException + { + init(file.getAbsolutePath()); + } + + /** + * Builds an instance from an IFile + * + * @param aFile + * @throws IOException if not found on disk + */ + public LocalFile(IFile aFile) throws IOException + { + String fullPath = aFile.getDirectory() + File.separator + aFile.getFilename(); + init(fullPath); + } + + private void init(String fullPath) throws IOException + { + File aFile = new File(fullPath); + if (!aFile.exists() || (!aFile.exists() && !aFile.isFile())) + { + throw new IOException("Unable to locate file"); + } + this.directory = aFile.getParent(); + this.filename = aFile.getName(); + } + + /** + * @return a real file instance + */ + public File toFile() { + String fullPath = this.directory + File.separator + this.filename; + return new File(fullPath); + } + + @Override + public String getDirectory() + { + return this.directory; + } + + @Override + public String getFilename() + { + return this.filename; + } + + private String directory; + + private String filename; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/IFileChooserService.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/IFileChooserService.java new file mode 100644 index 0000000..232fa29 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/IFileChooserService.java @@ -0,0 +1,84 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.file.chooser; + +import java.io.IOException; + +import com.horstmann.violet.framework.file.IFile; +import com.horstmann.violet.framework.file.naming.ExtensionFilter; +import com.horstmann.violet.framework.file.persistence.IFileReader; +import com.horstmann.violet.framework.file.persistence.IFileWriter; + + +/** + * Services needed to choose a file on the filesystem when opening or saving a graph. This service + * could seem bizarre but, for example, the dialog box will be different when the program + * is started from Eclipse than when it is started from JavaWebStart. M'kay!!!... + * @author Alexandre de Pellegrin + * + */ +public interface IFileChooserService +{ + + /** + * Tests whether the service is provided by WebStart + * + * @return true if this service is provided by WebStart + */ + public boolean isWebStart(); + + /** + * Pick up a file and return a file handler + * + * @return the Open object for the selected file + * @throws IOException + */ + public IFileReader chooseAndGetFileReader() throws IOException; + + /** + * Returns a file handler to read the given file + * + * @param file : existing file definition + * @return the Open object for the selected file + * @throws IOException + */ + public IFileReader getFileReader(IFile file) throws IOException; + + /** + * Choose a file and return a file handler to save content + * + * @param acceptable file filters + * @return the Save object for the selected file + * @throws IOException + */ + public IFileWriter chooseAndGetFileWriter(ExtensionFilter... extensions) throws IOException; + + /** + * Returns a file handler to save the given file + * + * @param extensions acceptable extension filters + * @return the Save object for the selected file + * @throws IOException + */ + public IFileWriter getFileWriter(IFile file) throws IOException; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JFileChooserService.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JFileChooserService.java new file mode 100644 index 0000000..6a549c5 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JFileChooserService.java @@ -0,0 +1,224 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.file.chooser; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.Set; + +import javax.swing.ImageIcon; +import javax.swing.JFileChooser; +import javax.swing.JOptionPane; + +import com.horstmann.violet.framework.dialog.DialogFactory; +import com.horstmann.violet.framework.file.IFile; +import com.horstmann.violet.framework.file.LocalFile; +import com.horstmann.violet.framework.file.naming.ExtensionFilter; +import com.horstmann.violet.framework.file.naming.FileNamingService; +import com.horstmann.violet.framework.file.persistence.IFileReader; +import com.horstmann.violet.framework.file.persistence.IFileWriter; +import com.horstmann.violet.framework.file.persistence.JFileReader; +import com.horstmann.violet.framework.file.persistence.JFileWriter; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.BeanInjector; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.ManagedBean; +import com.horstmann.violet.framework.injection.resources.ResourceBundleInjector; +import com.horstmann.violet.framework.injection.resources.annotation.ResourceBundleBean; +import com.horstmann.violet.framework.userpreferences.UserPreferencesService; + +/** + * This class implements a FileService with a JFileChooser + */ +@ManagedBean(registeredManually=true) +public class JFileChooserService implements IFileChooserService +{ + + public JFileChooserService() + { + ResourceBundleInjector.getInjector().inject(this); + BeanInjector.getInjector().inject(this); + } + + /** + * @return the last opened file directory or the current directory if no one is found + */ + private File getLastOpenedDir() + { + Set recentFiles = this.userPreferencesService.getRecentFiles(); + for (IFile aFile : recentFiles) { + try { + LocalFile localFile = new LocalFile(aFile); + File lastDir = new File(localFile.getDirectory()); + return lastDir; + } catch (IOException e) { + // File deleted ? Ok, let's take the next one. + } + } + File currentDir = new File("."); + return currentDir; + } + + @Override + public boolean isWebStart() + { + return false; + } + + @Override + public IFileReader getFileReader(IFile file) throws FileNotFoundException + { + try + { + LocalFile localFile = new LocalFile(file); + File physicalFile = localFile.toFile(); + if (physicalFile.exists() && physicalFile.isFile()) + { + IFileReader foh = new JFileReader(physicalFile); + return foh; + } + else + { + throw new FileNotFoundException("File " + file.getFilename() + " not reachable from " + file.getDirectory()); + } + } + catch (IOException e1) + { + throw new FileNotFoundException(e1.getLocalizedMessage()); + } + } + + @Override + public IFileReader chooseAndGetFileReader() throws FileNotFoundException + { + JFileChooser fileChooser = new JFileChooser(); + File initialDirectory = getLastOpenedDir(); + fileChooser.setCurrentDirectory(initialDirectory); + ExtensionFilter[] filters = fileNamingService.getFileFilters(); + for (int i = 0; i < filters.length; i++) + { + fileChooser.addChoosableFileFilter(filters[i]); + } + int response = fileChooser.showOpenDialog(null); + File selectedFile = null; + if (response == JFileChooser.APPROVE_OPTION) + { + selectedFile = fileChooser.getSelectedFile(); + } + if (selectedFile == null) + { + return null; + } + IFileReader foh = new JFileReader(selectedFile); + return foh; + } + + @Override + public IFileWriter getFileWriter(IFile file) throws FileNotFoundException + { + try + { + LocalFile localFile = new LocalFile(file); + IFileWriter fsh = new JFileWriter(localFile.toFile()); + return fsh; + } + catch (IOException e) + { + throw new FileNotFoundException(e.getLocalizedMessage()); + } + } + + @Override + public IFileWriter chooseAndGetFileWriter(ExtensionFilter... filters) throws FileNotFoundException + { + JFileChooser fileChooser = new JFileChooser(); + File initialDirectory = getLastOpenedDir(); + fileChooser.setCurrentDirectory(initialDirectory); + fileChooser.setAcceptAllFileFilterUsed(false); + for (int i = 0; i < filters.length; i++) + { + ExtensionFilter aFilter = filters[i]; + fileChooser.addChoosableFileFilter(aFilter); + fileChooser.setFileFilter(aFilter); + } + int response = fileChooser.showSaveDialog(null); + File selectedFile = null; + if (response == JFileChooser.APPROVE_OPTION) + { + selectedFile = fileChooser.getSelectedFile(); + ExtensionFilter selectedFilter = (ExtensionFilter) fileChooser.getFileFilter(); + String fullPath = selectedFile.getAbsolutePath(); + String extension = selectedFilter.getExtension(); + if (!fullPath.toLowerCase().endsWith(extension)) { + fullPath = fullPath + extension; + selectedFile = new File(fullPath); + } + if (selectedFile.exists()) + { + JOptionPane optionPane = new JOptionPane(); + optionPane.setMessage(this.overwriteDialogBoxMessage); + optionPane.setOptionType(JOptionPane.YES_NO_OPTION); + optionPane.setIcon(this.overwriteDialogBoxIcon); + this.dialogFactory.showDialog(optionPane, this.overwriteDialogBoxTitle, true); + + int result = JOptionPane.NO_OPTION; + if (!JOptionPane.UNINITIALIZED_VALUE.equals(optionPane.getValue())) + { + result = ((Integer) optionPane.getValue()).intValue(); + } + + if (result == JOptionPane.NO_OPTION) + { + selectedFile = null; + } + } + } + if (selectedFile == null) + { + return null; + } + IFileWriter fsh = new JFileWriter(selectedFile); + return fsh; + } + + + @InjectedBean + private UserPreferencesService userPreferencesService; + + @InjectedBean + private FileNamingService fileNamingService; + + @InjectedBean + private DialogFactory dialogFactory; + + @ResourceBundleBean(key="dialog.overwrite.ok") + private String overwriteDialogBoxMessage; + + @ResourceBundleBean(key="dialog.overwrite.title") + private String overwriteDialogBoxTitle; + + @ResourceBundleBean(key="dialog.overwrite.icon") + private ImageIcon overwriteDialogBoxIcon; + + + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JFileChooserService.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JFileChooserService.properties new file mode 100644 index 0000000..25db758 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JFileChooserService.properties @@ -0,0 +1,3 @@ +dialog.overwrite.title=Warning +dialog.overwrite.ok=This file already exists.\nDo you want to overwrite it? +dialog.overwrite.icon=/icons/64x64/save.png \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JFileChooserService_fr.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JFileChooserService_fr.properties new file mode 100644 index 0000000..ded0065 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JFileChooserService_fr.properties @@ -0,0 +1,3 @@ +dialog.overwrite.title=Attention +dialog.overwrite.ok=Ce fichier existe d\u00E9j\u00E0.\nVoulez-vous l'\u00E9craser? +dialog.overwrite.icon=/icons/64x64/save.png \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JNLPFileChooserService.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JNLPFileChooserService.java new file mode 100644 index 0000000..b6b92c8 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/chooser/JNLPFileChooserService.java @@ -0,0 +1,176 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.file.chooser; + +import java.io.IOException; +import java.util.ArrayList; + +import javax.jnlp.FileContents; +import javax.jnlp.FileOpenService; +import javax.jnlp.FileSaveService; +import javax.jnlp.ServiceManager; +import javax.jnlp.UnavailableServiceException; + +import com.horstmann.violet.framework.file.IFile; +import com.horstmann.violet.framework.file.naming.ExtensionFilter; +import com.horstmann.violet.framework.file.naming.FileNamingService; +import com.horstmann.violet.framework.file.persistence.IFileReader; +import com.horstmann.violet.framework.file.persistence.IFileWriter; +import com.horstmann.violet.framework.file.persistence.JNLPFileReader; +import com.horstmann.violet.framework.file.persistence.JNLPFileWriter; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.BeanInjector; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.ManagedBean; + +/** + * This class provides a FileService for Java Web Start. Note that file saving is strange under Web Start. You first save the data, + * and the dialog is only displayed when the output stream is closed. Therefore, the file name is not available until after the file + * has been written. + */ +@ManagedBean(registeredManually=true) +public class JNLPFileChooserService implements IFileChooserService +{ + + /** + * Default constructor + * + * @param namingService + */ + public JNLPFileChooserService() + { + BeanInjector.getInjector().inject(this); + try + { + openService = (FileOpenService) ServiceManager.lookup("javax.jnlp.FileOpenService"); + saveService = (FileSaveService) ServiceManager.lookup("javax.jnlp.FileSaveService"); + } + catch (UnavailableServiceException ex) + { + ex.printStackTrace(); + } + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.file.chooser.IFileChooserService#isWebStart() + */ + public boolean isWebStart() + { + return true; + } + + @Override + public IFileReader getFileReader(IFile file) throws IOException + { + String currentDirectory = file.getDirectory(); + String filename = file.getFilename(); + ExtensionFilter[] filters = this.fileNamingService.getFileFilters(); + ArrayList fileExtensions = new ArrayList(); + for (ExtensionFilter aFilter : filters) + { + String aFilterExtension = aFilter.getExtension(); + if (filename.endsWith(aFilterExtension)) { + fileExtensions.add(aFilterExtension); + } + } + String[] fileExtensionsStrings = (String[]) fileExtensions.toArray(new String[fileExtensions.size()]); + final FileContents contents = openService.openFileDialog(currentDirectory, fileExtensionsStrings); + return new JNLPFileReader(contents); + } + + @Override + public IFileReader chooseAndGetFileReader() throws IOException + { + String currentDirectory = "."; + ArrayList fileExtensions = new ArrayList(); + ExtensionFilter[] filters = this.fileNamingService.getFileFilters(); + for (int i = 0; i < filters.length; i++) + { + ExtensionFilter aFilter = filters[i]; + String filterExtension = aFilter.getExtension(); + fileExtensions.add(filterExtension); + } + String[] fileExtensionsStrings = (String[]) fileExtensions.toArray(new String[fileExtensions.size()]); + final FileContents contents = openService.openFileDialog(currentDirectory, fileExtensionsStrings); + return new JNLPFileReader(contents); + } + + @Override + public IFileWriter getFileWriter(IFile file) throws IOException + { + + String defaultDirectory = file.getDirectory(); + ArrayList fileExtensions = new ArrayList(); + ExtensionFilter[] filters = this.fileNamingService.getFileFilters(); + for (int i = 0; i < filters.length; i++) + { + ExtensionFilter aFilter = filters[i]; + String filterExtension = aFilter.getExtension(); + fileExtensions.add(filterExtension); + } + String[] fileExtensionsStrings = (String[]) fileExtensions.toArray(new String[fileExtensions.size()]); + FileContents contents = saveService.saveAsFileDialog(defaultDirectory, fileExtensionsStrings, null); + if (contents == null) + { + return null; + } + return new JNLPFileWriter(contents); + } + + @Override + public IFileWriter chooseAndGetFileWriter(final ExtensionFilter... filters) throws IOException + { + String defaultDirectory = "."; + ArrayList fileExtensions = new ArrayList(); + for (int i = 0; i < filters.length; i++) + { + ExtensionFilter aFilter = filters[i]; + String filterExtension = aFilter.getExtension(); + fileExtensions.add(filterExtension); + } + String[] fileExtensionsStrings = (String[]) fileExtensions.toArray(new String[fileExtensions.size()]); + FileContents contents = saveService.saveAsFileDialog(defaultDirectory, fileExtensionsStrings, null); + if (contents == null) + { + return null; + } + return new JNLPFileWriter(contents); + } + + /** + * JNLP service + */ + private FileOpenService openService; + + /** + * JNLP service + */ + private FileSaveService saveService; + + /** + * Handle file names + */ + @InjectedBean + private FileNamingService fileNamingService; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/export/FileExportService.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/export/FileExportService.java new file mode 100644 index 0000000..b33a2b9 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/export/FileExportService.java @@ -0,0 +1,122 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.file.export; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Toolkit; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.OutputStream; + +import com.horstmann.violet.framework.util.ClipboardPipe; +import com.horstmann.violet.product.diagram.abstracts.IGraph; + +public class FileExportService +{ + + /** + * Return the image correspondiojng to the graph + * + * @param graph + * @author Alexandre de Pellegrin + * @return bufferedImage. To convert it into an image, use the syntax : + * Toolkit.getDefaultToolkit().createImage(bufferedImage.getSource()); + */ + public static BufferedImage getImage(IGraph graph) + { + Rectangle2D bounds = graph.getClipBounds(); + BufferedImage image = new BufferedImage((int) bounds.getWidth() + 1, (int) bounds.getHeight() + 1, + BufferedImage.TYPE_INT_RGB); + Graphics2D g2 = (Graphics2D) image.getGraphics(); + g2.translate(-bounds.getX(), -bounds.getY()); + g2.setColor(Color.WHITE); + g2.fill(new Rectangle2D.Double(bounds.getX(), bounds.getY(), bounds.getWidth() + 1, bounds.getHeight() + 1)); + g2.setColor(Color.BLACK); + g2.setBackground(Color.WHITE); + graph.draw(g2); + return image; + } + + /** + * Export graph to clipboard (Do not merge with exportToClipBoard(). Used in Eclipse plugin) + * + * @author Alexandre de Pellegrin + * @param graph + */ + public static void exportToclipBoard(IGraph graph) + { + BufferedImage bImage = getImage(graph); + ClipboardPipe pipe = new ClipboardPipe(bImage); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(pipe, null); + } + + /** + * Auteur : a.depellegrin
+ * D�finition : Exports class diagram graph to xmi
+ * + * @param graph to export + * @param out to write result + */ + public static void exportToXMI(IGraph graph, OutputStream out) + { + // if (!(graph instanceof ClassDiagramGraph)) + // { + // // Only exports class diagrams + // return; + // } + // try + // { + // // Gets xsl files + // ResourceBundle fileResourceBundle = ResourceBundle.getBundle(ResourceBundleConstant.FILE_STRINGS, Locale.getDefault()); + // URL xslResource1 = FileExportService.class.getResource(fileResourceBundle.getString("files.xmi.step1.xsl")); + // URL xslResource2 = FileExportService.class.getResource(fileResourceBundle.getString("files.xmi.step2.xsl")); + // // Converts graph to Violet's XML + // ByteArrayOutputStream graphOut = new ByteArrayOutputStream(); + // FileExportService.write(graph, graphOut); + // ByteArrayInputStream graphIn = new ByteArrayInputStream(graphOut.toByteArray()); + // // XSL transform - step 1 + // ByteArrayOutputStream xmiOut = new ByteArrayOutputStream(); + // InputStream xslResource1InputStream = xslResource1.openStream(); + // TransformerFactory factory = TransformerFactory.newInstance(); + // Transformer transformer = factory.newTransformer(new StreamSource(xslResource1InputStream)); + // transformer.transform(new StreamSource(graphIn), new StreamResult(xmiOut)); + // // XSL transform - step 2 + // ByteArrayInputStream xmiIn = new ByteArrayInputStream(xmiOut.toByteArray()); + // InputStream xslResource2InputStream = xslResource2.openStream(); + // transformer = factory.newTransformer(new StreamSource(xslResource2InputStream)); + // transformer.transform(new StreamSource(xmiIn), new StreamResult(out)); + // // Closes unused streams + // xslResource1InputStream.close(); + // xslResource2InputStream.close(); + // graphOut.close(); + // graphIn.close(); + // xmiOut.close(); + // xmiIn.close(); + // } + // catch (Exception e) + // { + // // Well... we tried! + // } + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/ExtensionFilter.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/ExtensionFilter.java new file mode 100644 index 0000000..8381dab --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/ExtensionFilter.java @@ -0,0 +1,67 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.file.naming; + +import java.io.File; + +import javax.swing.filechooser.FileFilter; + +/** + * A file filter that accepts all files with a given set of extensions. + */ +public class ExtensionFilter extends FileFilter +{ + + + /** + * Constructs an extension file filter. + * + * @param description the description (e.g. "Woozle files") + * @param extensions the accepted extension(e.g.".woozle") + */ + public ExtensionFilter(String description, String extension) + { + this.description = description; + this.extension = extension; + } + + public boolean accept(File f) + { + if (f.isDirectory()) return true; + String fname = f.getName().toLowerCase(); + if (fname.endsWith(extension.toLowerCase())) return true; + return false; + } + + public String getDescription() + { + return description; + } + + public String getExtension() + { + return extension; + } + + private String description; + private String extension; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/FileNamingService.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/FileNamingService.java new file mode 100644 index 0000000..9d74ecb --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/FileNamingService.java @@ -0,0 +1,161 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.file.naming; + +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.ManagedBean; +import com.horstmann.violet.framework.injection.resources.ResourceBundleInjector; +import com.horstmann.violet.framework.injection.resources.annotation.ResourceBundleBean; +import com.horstmann.violet.framework.plugin.IDiagramPlugin; +import com.horstmann.violet.framework.plugin.PluginRegistry; +import com.horstmann.violet.product.diagram.abstracts.IGraph; + +/** + * This file provides common file services + * + * @author Alexandre de Pellegrin + * + */ +@ManagedBean +public class FileNamingService +{ + + /** + * Default constructor + */ + public FileNamingService() + { + ResourceBundleInjector.getInjector().inject(this); + } + + + /** + * Edits the file path so that it ends in the desired extension. + * + * @param original the file to use as a starting point + * @param toBeRemoved the extension that is to be removed before adding the desired extension. Use null if nothing needs to be + * removed. + * @param desired the desired extension (e.g. ".png"), or a | separated list of extensions + * @return original if it already has the desired extension, or a new file with the edited file path + */ + public String changeFileExtension(String original, String toBeRemoved, String desired) + { + if (original == null) return null; + int n = desired.indexOf('|'); + if (n >= 0) desired = desired.substring(0, n); + String path = original; + if (!path.toLowerCase().endsWith(desired.toLowerCase())) + { + if (toBeRemoved != null && path.toLowerCase().endsWith(toBeRemoved.toLowerCase())) path = path.substring(0, path + .length() + - toBeRemoved.length()); + path = path + desired; + } + return path; + } + + + /** + * @return a map of extension filter per diagram type + */ + private Map, ExtensionFilter> getExtensionFilters() + { + Map, ExtensionFilter> result = new LinkedHashMap, ExtensionFilter>(); + List diagramPlugins = this.pluginRegistry.getDiagramPlugins(); + Collections.sort(diagramPlugins, new Comparator() { + @Override + public int compare(IDiagramPlugin o1, IDiagramPlugin o2) + { + return o1.getFileExtensionName().compareToIgnoreCase(o2.getFileExtensionName()); + } + }); + for (IDiagramPlugin aPlugin : diagramPlugins) { + Class graphClass = aPlugin.getGraphClass(); + String name = aPlugin.getFileExtensionName(); + String extension = aPlugin.getFileExtension(); + ExtensionFilter fileFilter = new ExtensionFilter(name, extension); + result.put(graphClass, fileFilter); + } + ExtensionFilter defaultFileFilter = new ExtensionFilter(this.defaultFileFilterName, this.defaultFileExtension); + result.put(IGraph.class, defaultFileFilter); + return result; + } + + /** + * @return all kind of extension file filters + */ + public ExtensionFilter[] getFileFilters() + { + Map, ExtensionFilter> filters = getExtensionFilters(); + Collection values = filters.values(); + return (ExtensionFilter[]) values.toArray(new ExtensionFilter[values.size()]); + } + + /** + * @param graph + * @return the file filter specific to a graph type + */ + public ExtensionFilter getExtensionFilter(IGraph graph) + { + Map, ExtensionFilter> filters = getExtensionFilters(); + ExtensionFilter filter = filters.get(graph.getClass()); + if (filter != null) + { + return filter; + } + return filters.get(IGraph.class); + } + + /** + * @return the extension filter for image export + */ + public ExtensionFilter getImageExtensionFilter() + { + return new ExtensionFilter(this.imageFileFilterName, this.imageFileExtension); + } + + + @ResourceBundleBean(key="files.image.name") + private String imageFileFilterName; + + @ResourceBundleBean(key="files.image.extension") + private String imageFileExtension; + + @ResourceBundleBean(key="files.global.name") + private String defaultFileFilterName; + + @ResourceBundleBean(key="files.global.extension") + private String defaultFileExtension; + + @InjectedBean + private PluginRegistry pluginRegistry; + + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/FileNamingService.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/FileNamingService.properties new file mode 100644 index 0000000..60426a1 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/FileNamingService.properties @@ -0,0 +1,4 @@ +files.global.name=All Violet Files +files.global.extension=.violet +files.image.name=Image Files (JPEG) +files.image.extension=.jpg \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/FileNamingService_fr.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/FileNamingService_fr.properties new file mode 100644 index 0000000..091610a --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/naming/FileNamingService_fr.properties @@ -0,0 +1,4 @@ +files.global.name=Fichiers Violet +files.global.extension=.violet +files.image.name=Fichiers image (JPEG) +files.image.extension=.jpg \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/CustomPersistenceDelegate.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/CustomPersistenceDelegate.java new file mode 100644 index 0000000..4562ec7 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/CustomPersistenceDelegate.java @@ -0,0 +1,62 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.file.persistence; + +import java.beans.DefaultPersistenceDelegate; +import java.beans.Encoder; +import java.beans.Expression; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +public class CustomPersistenceDelegate extends DefaultPersistenceDelegate +{ + + protected Expression instantiate(Object oldInstance, Encoder out) + { + try + { + Class cl = oldInstance.getClass(); + Field[] fields = cl.getFields(); + for (int i = 0; i < fields.length; i++) + { + if (Modifier.isStatic(fields[i].getModifiers()) && fields[i].get(null) == oldInstance) + { + return new Expression(fields[i], "get", new Object[] + { + null + }); + } + } + } + catch (IllegalAccessException ex) + { + ex.printStackTrace(); + } + return null; + } + + protected boolean mutatesTo(Object oldInstance, Object newInstance) + { + return oldInstance == newInstance; + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/IFilePersistenceService.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/IFilePersistenceService.java new file mode 100644 index 0000000..77f2fae --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/IFilePersistenceService.java @@ -0,0 +1,35 @@ +package com.horstmann.violet.framework.file.persistence; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; + +/** + * Services dedicated to read/save a graph content from/to an input/outputstream + * @author Alexandre de Pellegrin + * + */ +public interface IFilePersistenceService +{ + + /** + * Writes the given graph in an outputstream. We use long-term bean persistence to save the program data. See + * http://java.sun.com/products/jfc/tsc/articles/persistence4/index.html for an overview. + * + * @param out the stream for saving + */ + public void write(IGraph graph, OutputStream out); + + + /** + * Reads a graph file + * + * @param in the input stream to read + * @return the graph that is read in + */ + public IGraph read(InputStream in) throws IOException; + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/IFileReader.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/IFileReader.java new file mode 100644 index 0000000..68f076d --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/IFileReader.java @@ -0,0 +1,55 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.file.persistence; + +import java.io.IOException; +import java.io.InputStream; + +import com.horstmann.violet.framework.file.IFile; + +/** + * An FileOpenHandler object handles the stream and name of the file that the user selected for reading. + */ +public interface IFileReader +{ + + + /** + * Gets the input stream corresponding to the user selection. + * + * @return the input stream + */ + public InputStream getInputStream() throws IOException; + + /** + * Gets file definition + * + * @return the file name + */ + public IFile getFileDefinition() throws IOException; + + + + + + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/IFileWriter.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/IFileWriter.java new file mode 100644 index 0000000..67b5017 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/IFileWriter.java @@ -0,0 +1,49 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.file.persistence; + +import java.io.IOException; +import java.io.OutputStream; + +import com.horstmann.violet.framework.file.IFile; + +/** + * A FileSaverHandler object handles the stream and name of the file that the user selected for writing. + */ +public interface IFileWriter +{ + + /** + * Gets the output stream needed to write this file content + * + * @return the output stream + */ + public OutputStream getOutputStream() throws IOException; + + /** + * Gets file definition + * + * @return the file name + */ + public IFile getFileDefinition() throws IOException; + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JFileReader.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JFileReader.java new file mode 100644 index 0000000..3694892 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JFileReader.java @@ -0,0 +1,38 @@ +package com.horstmann.violet.framework.file.persistence; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +import com.horstmann.violet.framework.file.IFile; +import com.horstmann.violet.framework.file.LocalFile; + +public class JFileReader implements IFileReader +{ + + public JFileReader(File f) throws FileNotFoundException + { + this.f = f; + this.in = new FileInputStream(f); + } + + @Override + public InputStream getInputStream() + { + return in; + } + + @Override + public IFile getFileDefinition() throws IOException + { + return new LocalFile(this.f); + } + + private InputStream in; + + private File f; + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JFileWriter.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JFileWriter.java new file mode 100644 index 0000000..41e9a7f --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JFileWriter.java @@ -0,0 +1,43 @@ +package com.horstmann.violet.framework.file.persistence; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; + +import com.horstmann.violet.framework.file.IFile; +import com.horstmann.violet.framework.file.LocalFile; +/** + * Standard Java FileSaver implementation + * + * @author Alexandre de Pellegrin + * + */ +public class JFileWriter implements IFileWriter +{ + + public JFileWriter(File f) throws FileNotFoundException + { + this.f = f; + this.out = new FileOutputStream(f); + } + + + @Override + public OutputStream getOutputStream() + { + return out; + } + + @Override + public IFile getFileDefinition() throws IOException + { + return new LocalFile(this.f); + } + + + private File f; + private OutputStream out; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JNLPFileReader.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JNLPFileReader.java new file mode 100644 index 0000000..6357dcb --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JNLPFileReader.java @@ -0,0 +1,48 @@ +package com.horstmann.violet.framework.file.persistence; + +import java.io.IOException; +import java.io.InputStream; + +import javax.jnlp.FileContents; + +import com.horstmann.violet.framework.file.IFile; + +public class JNLPFileReader implements IFileReader +{ + + public JNLPFileReader(FileContents contents) { + this.contents = contents; + } + + @Override + public InputStream getInputStream() throws IOException + { + return contents.getInputStream(); + } + + @Override + public IFile getFileDefinition() throws IOException + { + final String name = contents.getName(); + return new IFile() { + @Override + public String getDirectory() + { + return null; + } + + @Override + public String getFilename() + { + return name; + } + }; + } + + + private FileContents contents; + + + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JNLPFileWriter.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JNLPFileWriter.java new file mode 100644 index 0000000..8c6b1b3 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/JNLPFileWriter.java @@ -0,0 +1,46 @@ +package com.horstmann.violet.framework.file.persistence; + +import java.io.IOException; +import java.io.OutputStream; + +import javax.jnlp.FileContents; + +import com.horstmann.violet.framework.file.IFile; + +public class JNLPFileWriter implements IFileWriter +{ + + public JNLPFileWriter(FileContents contents) { + this.contents = contents; + } + + + @Override + public OutputStream getOutputStream() throws IOException + { + boolean isOverwriteAllowed = true; + return contents.getOutputStream(isOverwriteAllowed); + } + + @Override + public IFile getFileDefinition() throws IOException + { + final String name = contents.getName(); + return new IFile() { + @Override + public String getDirectory() + { + return null; + } + + @Override + public String getFilename() + { + return name; + } + }; + } + + private FileContents contents; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/StandardJavaFilePersistenceService.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/StandardJavaFilePersistenceService.java new file mode 100644 index 0000000..638c1f5 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/StandardJavaFilePersistenceService.java @@ -0,0 +1,266 @@ +package com.horstmann.violet.framework.file.persistence; + +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.beans.DefaultPersistenceDelegate; +import java.beans.Encoder; +import java.beans.ExceptionListener; +import java.beans.Statement; +import java.beans.XMLDecoder; +import java.beans.XMLEncoder; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import com.horstmann.violet.framework.injection.bean.ManiocFramework.ManagedBean; +import com.horstmann.violet.product.diagram.abstracts.AbstractGraph; +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.Id; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.AbstractNode; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.product.diagram.abstracts.property.ArrowHead; +import com.horstmann.violet.product.diagram.abstracts.property.BentStyle; +import com.horstmann.violet.product.diagram.abstracts.property.LineStyle; +import com.horstmann.violet.product.diagram.common.ImageNode; + +/** + * Standard Java implementation of IFilePersistenceService + * @author alex + * + */ +@ManagedBean(registeredManually=true) +public class StandardJavaFilePersistenceService implements IFilePersistenceService +{ + + + @Override + public void write(IGraph graph, OutputStream out) + { + XMLEncoder encoder = getXMLEncoder(Violet016BackportFormatService.convertToViolet016(out)); + encoder.writeObject(graph); + encoder.close(); + } + + + @Override + public IGraph read(InputStream in) throws IOException + { + XMLDecoder reader = new XMLDecoder(Violet016BackportFormatService.convertFromViolet016(in), null, new ExceptionListener() + { + public void exceptionThrown(Exception e) + { + e.printStackTrace(); + } + }); + IGraph graph = (IGraph) reader.readObject(); + in.close(); + return graph; + } + + + /** + * Creates a new instance of XML Encoder pre-configured for Violet beans serailization + * + * @param out + * @return configured encoder + */ + private XMLEncoder getXMLEncoder(OutputStream out) + { + XMLEncoder encoder = new XMLEncoder(out); + + encoder.setExceptionListener(new ExceptionListener() + { + public void exceptionThrown(Exception ex) + { + ex.printStackTrace(); + } + }); + configure(encoder); + return encoder; + } + + /** + * Configures the given XML encoder by setting custom persistenceDelegate + * @param encoder + */ + private void configure(XMLEncoder encoder) + { + encoder.setPersistenceDelegate(Point2D.Double.class, new DefaultPersistenceDelegate() + { + protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) + { + super.initialize(type, oldInstance, newInstance, out); + Point2D p = (Point2D) oldInstance; + out.writeStatement(new Statement(oldInstance, "setLocation", new Object[] + { + new Double(p.getX()), + new Double(p.getY()) + })); + } + }); + encoder.setPersistenceDelegate(Line2D.class, new DefaultPersistenceDelegate() + { + protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) + { + super.initialize(type, oldInstance, newInstance, out); + Line2D l = (Line2D) oldInstance; + out.writeStatement(new Statement(oldInstance, "setLine", new Object[] + { + new Double(l.getX1()), + new Double(l.getY1()), + new Double(l.getX2()), + new Double(l.getY2()) + })); + } + }); + encoder.setPersistenceDelegate(Rectangle2D.class, new DefaultPersistenceDelegate() + { + protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) + { + super.initialize(type, oldInstance, newInstance, out); + Rectangle2D r = (Rectangle2D) oldInstance; + out.writeStatement(new Statement(oldInstance, "setRect", new Object[] + { + new Double(r.getX()), + new Double(r.getY()), + new Double(r.getWidth()), + new Double(r.getHeight()) + })); + } + }); + + encoder.setPersistenceDelegate(BentStyle.class, new CustomPersistenceDelegate()); + encoder.setPersistenceDelegate(LineStyle.class, new CustomPersistenceDelegate()); + encoder.setPersistenceDelegate(ArrowHead.class, new CustomPersistenceDelegate()); + encoder.setPersistenceDelegate(URL.class, new DefaultPersistenceDelegate(new String[] + { + "protocol", + "host", + "port", + "file" + })); + encoder.setPersistenceDelegate(Map.class, new DefaultPersistenceDelegate() + { + protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) + { + super.initialize(type, oldInstance, newInstance, out); + Map map = (Map) oldInstance; + for (Iterator it = map.keySet().iterator(); it.hasNext();) + { + Object key = it.next(); + Object value = map.get(key); + out.writeStatement(new Statement(oldInstance, "put", new Object[] + { + key, + value + })); + } + } + }); + encoder.setPersistenceDelegate(AbstractNode.class, new DefaultPersistenceDelegate() + { + protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) + { + super.initialize(type, oldInstance, newInstance, out); + INode n = (INode) oldInstance; + List children = new ArrayList(n.getChildren()); + for (int i = 0; i < children.size(); i++) + { + INode c = (INode) children.get(i); + Point2D p = c.getLocation(); + out.writeStatement(new Statement(oldInstance, "addChild", new Object[] + { + c, + p + })); + } + boolean isWriteId = false; // Keep for further refinement + if (isWriteId) + { + Id id = n.getId(); + out.writeStatement(new Statement(oldInstance, "setId", new Object[] + { + id + })); + } + } + }); + encoder.setPersistenceDelegate(AbstractGraph.class, new DefaultPersistenceDelegate() + { + protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) + { + super.initialize(type, oldInstance, newInstance, out); + AbstractGraph g = (AbstractGraph) oldInstance; + + for ( INode n : g.getAllNodes()) + { + INode parent = n.getParent(); + if (parent != null) continue; + Point2D p = n.getLocation(); + out.writeStatement(new Statement(oldInstance, "addNode", new Object[] + { + n, + p + })); + } + for (IEdge e : g.getAllEdges()) + { + out.writeStatement(new Statement(oldInstance, "connect", new Object[] + { + e, + e.getStart(), + e.getStartLocation(), + e.getEnd(), + e.getEndLocation() + })); + } + } + }); +// encoder.setPersistenceDelegate(AbstractEdge.class, new DefaultPersistenceDelegate() +// { +// protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) +// { +// super.initialize(type, oldInstance, newInstance, out); +// IEdge e = (IEdge) oldInstance; +// out.writeStatement(new Statement(oldInstance, "connect", new Object[] +// { +// e.getStart(), +// e.getEnd() +// })); +// } +// }); + encoder.setPersistenceDelegate(ImageNode.class, new DefaultPersistenceDelegate() + { + protected void initialize(Class type, Object oldInstance, Object newInstance, Encoder out) + { + super.initialize(type, oldInstance, newInstance, out); + ImageNode n = (ImageNode) oldInstance; + try + { + String imageContent = n.getImageContent(); + int width = n.getImageWidth(); + int height = n.getImageHeight(); + out.writeStatement(new Statement(oldInstance, "setImageContent", new Object[] + { + imageContent, + width, + height + })); + } + catch (InterruptedException e) + { + throw new RuntimeException("Error while serializing ImageNode", e); + } + } + }); + } + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/Violet016BackportFormatService.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/Violet016BackportFormatService.java new file mode 100644 index 0000000..9ed2f3f --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/Violet016BackportFormatService.java @@ -0,0 +1,201 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.file.persistence; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import com.horstmann.violet.framework.util.StringFilterOutputStream; +import com.horstmann.violet.product.diagram.abstracts.property.ArrowHead; +import com.horstmann.violet.product.diagram.abstracts.property.BentStyle; +import com.horstmann.violet.product.diagram.abstracts.property.LineStyle; +import com.horstmann.violet.product.diagram.common.DiagramLinkNode; +import com.horstmann.violet.product.diagram.common.NoteEdge; +import com.horstmann.violet.product.diagram.common.NoteNode; +import com.horstmann.violet.product.diagram.common.PointNode; + +/** + * This class provides file format services + * + * @author Alexandre de Pellegrin + * + */ +public class Violet016BackportFormatService +{ + + /** + * This filter guarantees compatibility for Violet 0.16 file format + * + * @param in raw input stream + * @return converted input stream + */ + public static InputStream convertFromViolet016(InputStream in) + { + + Map replaceMap = new Hashtable(); + replaceMap.putAll(violet016CompatibilityMap); + + // fix framework elements + replaceMap.put("com.horstmann.violet.BentStyle", BentStyle.class.getName()); + replaceMap.put("com.horstmann.violet.LineStyle", LineStyle.class.getName()); + replaceMap.put("com.horstmann.violet.ArrowHead", ArrowHead.class.getName()); + + // fix common elements package + replaceMap.put("com.horstmann.violet.DiagramLinkNode", DiagramLinkNode.class.getName()); + replaceMap.put("com.horstmann.violet.NoteEdge", NoteEdge.class.getName()); + replaceMap.put("com.horstmann.violet.NoteNode", NoteNode.class.getName()); + replaceMap.put("com.horstmann.violet.PointNode", PointNode.class.getName()); + + String original = getInputStreamContent(in); + String replaced = replaceAll(original, replaceMap); + try + { + return new ByteArrayInputStream(replaced.getBytes("UTF-8")); + } + catch (UnsupportedEncodingException ex) + { + ex.printStackTrace(); + return new ByteArrayInputStream(replaced.getBytes()); + } + } + + /** + * Converts inputStream to String + * + * @param in stream + * @return string + */ + private static String getInputStreamContent(InputStream in) + { + try + { + InputStreamReader isr = new InputStreamReader(in, "UTF-8"); + StringBuffer buffer = new StringBuffer(); + int len = 1024; + char buf[] = new char[len]; + int numRead; + while ((numRead = isr.read(buf, 0, len)) != -1) + { + buffer.append(buf, 0, numRead); + } + isr.close(); + return buffer.toString(); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + /** + * Filters a string and replaces all key courrences issed from the map by its valye + * + * @param input string + * @param replaceMap key = searchedString / value = replaceString + * @return filtered string + */ + private static String replaceAll(String input, Map replaceMap) + { + Set set = replaceMap.keySet(); + for (Iterator iter = set.iterator(); iter.hasNext();) + { + String searchedStr = iter.next(); + String replaceStr = replaceMap.get(searchedStr); + input = input.replaceAll(searchedStr, replaceStr); + } + return input; + } + + /** + * This filter guarantees compatibility for Violet 0.16 file format + * + * @param out raw output stream + * @return converted output stream + */ + public static OutputStream convertToViolet016(OutputStream out) + { + + Map replaceMap = new Hashtable(); + replaceMap.putAll(getReversedMap(violet016CompatibilityMap)); + + // fix framework elements + replaceMap.put(BentStyle.class.getName(), "com.horstmann.violet.BentStyle"); + replaceMap.put(LineStyle.class.getName(), "com.horstmann.violet.LineStyle"); + replaceMap.put(ArrowHead.class.getName(), "com.horstmann.violet.ArrowHead"); + // fix common elements package + replaceMap.put(DiagramLinkNode.class.getName(), "com.horstmann.violet.DiagramLinkNode"); + replaceMap.put(NoteEdge.class.getName(), "com.horstmann.violet.NoteEdge"); + replaceMap.put(NoteNode.class.getName(), "com.horstmann.violet.NoteNode"); + replaceMap.put(PointNode.class.getName(), "com.horstmann.violet.PointNode"); + + StringFilterOutputStream filteredOutputStream = new StringFilterOutputStream(out, replaceMap); + return filteredOutputStream; + + } + + /** + * Registers new keys/values to keep Violet 0.16 compatibility. Keys are Violet 0.16 strings. Map values are real values + * corresponding to their old Violet 0.16 string. + * + * @param entries + */ + public static void addViolet016CompatibilityEntries(Map entries) + { + violet016CompatibilityMap.putAll(entries); + } + + /** + * Reverse a map. Be carefull to have unique values as they will become keys! + * + * @param mapToReverse + * @return + */ + private static Map getReversedMap(Map mapToReverse) + { + Map result = new HashMap(); + for (String aKey : mapToReverse.keySet()) + { + String aValue = mapToReverse.get(aKey); + if (aValue != null && !result.containsKey(aValue)) + { + result.put(aValue, aKey); + } + } + return result; + } + + /** + * Violet 0.16 compatibility map . Keys are Violet 0.16 strings. Map values are real values corresponding to their old Violet + * 0.16 string. + */ + private static Map violet016CompatibilityMap = new HashMap(); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/XStreamBasedPersistenceService.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/XStreamBasedPersistenceService.java new file mode 100644 index 0000000..cf2ed80 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/file/persistence/XStreamBasedPersistenceService.java @@ -0,0 +1,79 @@ +package com.horstmann.violet.framework.file.persistence; + +import java.awt.geom.Point2D; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Collection; +import java.util.List; + +import com.horstmann.violet.framework.injection.bean.ManiocFramework.BeanInjector; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.ManagedBean; +import com.horstmann.violet.framework.plugin.IDiagramPlugin; +import com.horstmann.violet.framework.plugin.PluginRegistry; +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.thoughtworks.xstream.XStream; + +@ManagedBean(registeredManually=true) +public class XStreamBasedPersistenceService implements IFilePersistenceService { + + @InjectedBean + private PluginRegistry pluginRegistry; + + public XStreamBasedPersistenceService() { + BeanInjector.getInjector().inject(this); + } + + @Override + public IGraph read(InputStream in) throws IOException { + XStream xStream = new XStream(); + xStream = getConfiguredXStream(xStream); + Object fromXML = xStream.fromXML(in); + IGraph graph = (IGraph) fromXML; + Collection allNodes = graph.getAllNodes(); + for (INode aNode : allNodes) { + aNode.setGraph(graph); + } + return graph; + } + + @Override + public void write(IGraph graph, OutputStream out) { + XStream xStream = new XStream(); + xStream = getConfiguredXStream(xStream); + xStream.toXML(graph, out); + } + + private XStream getConfiguredXStream(XStream xStream) { + xStream.autodetectAnnotations(true); + xStream.setMode(XStream.ID_REFERENCES); + xStream.useAttributeFor(Point2D.Double.class, "x"); + xStream.useAttributeFor(Point2D.Double.class, "y"); + xStream.alias("Point2D.Double", Point2D.Double.class); + List diagramPlugins = this.pluginRegistry.getDiagramPlugins(); + for (IDiagramPlugin aPlugin : diagramPlugins) { + Class graphClass = aPlugin.getGraphClass(); + xStream.alias(graphClass.getSimpleName(), graphClass); + try { + IGraph aDummyGraph = graphClass.newInstance(); + List edgePrototypes = aDummyGraph.getEdgePrototypes(); + List nodePrototypes = aDummyGraph.getNodePrototypes(); + for (IEdge anEdgePrototype : edgePrototypes) { + Class edgeClass = anEdgePrototype.getClass(); + xStream.alias(edgeClass.getSimpleName(), anEdgePrototype.getClass()); + } + for (INode aNodePrototype : nodePrototypes) { + Class nodeClass = aNodePrototype.getClass(); + xStream.alias(nodeClass.getSimpleName(), aNodePrototype.getClass()); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } + return xStream; + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/bean/ManiocFramework.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/bean/ManiocFramework.java new file mode 100644 index 0000000..39a15e1 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/bean/ManiocFramework.java @@ -0,0 +1,903 @@ +package com.horstmann.violet.framework.injection.bean; + +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Manioc is a very very (VERY!) light and basic injection framework.
+ * IT ONLY CONTAINS ONE JAVA SOURCE FILE!!!. Just copy it into your project.
+ *
+ * Source code version : 0.0.1-SNAPSHOT
+ *
+ * Release note :
+ * Version 0.0.1 (07-21-2011) :
+ * + contains a BeanFactory and a BeanInjector
+ * + can manage beans annotated with \@ManagedBean
+ * + supports injection on fields with \@Inject
+ * + supports bean construction on specific constructor or factory method with \@Construct + *
+ * + automatically injects beans on constructor and factory method without + * specifying any annotation
+ * + supports \@PostConstruct and \@PreDestroy
+ * + supports multiple injection contexts with hierarchy with + * \@ManagedBean(context)
+ * + supports bean scope (singleton and prototype) with \@ManagedBean(scope)
+ * + supports injection by annotation on existing beans
+ * + supports manual registration of pre-construct beans on the bean factory + * with BeanFactory.register()
+ * + * Version 0.0.2 (10-07-2011) :
+ * + bug fix on manual bean registration with interface type
+ * + * @author Alexandre de Pellegrin + * + */ +public class ManiocFramework { + /** + * This injector is made to perform dependency injection on objects. This + * class was inspired from the excellent Apache Wicket framework. + * + * @author Alexandre de Pellegrin + * + */ + public static class BeanInjector { + + /** + * Singleton instance + */ + private static BeanInjector instance; + + /** + * Singleton constructor + */ + private BeanInjector() { + // Singleton pattern + } + + /** + * @return the only object instance + */ + public static BeanInjector getInjector() { + if (BeanInjector.instance == null) { + BeanInjector.instance = new BeanInjector(); + } + return BeanInjector.instance; + } + + /** + * Injects beans on fields annotated with \@InjectedBean
+ * For managed beans, you can specified the application context you want
+ * directly in the \@ManagedBean annotation. + * + * @param o + */ + public void inject(Object o) { + Class implementationType = o.getClass(); + ManagedBean annotation = implementationType.getAnnotation(ManagedBean.class); + if (annotation != null) { + Class context = annotation.applicationContext(); + this.inject(o, context); + return; + } + if (annotation == null) { + this.inject(o, DefaultApplicationContext.class); + } + } + + /** + * Injects beans in fields annotated with \@InjectedBean.
+ * Beans are taken from the specified application context.
+ * + * @param o + * @param context + */ + public void inject(Object o, Class context) { + List> classAndSuperClasses = getClassAndSuperClasses(o); + for (Class aClass : classAndSuperClasses) { + // Injects on fields (only if they haven't any value set) + for (Field aField : aClass.getDeclaredFields()) { + InjectedBean propertyAnnotation = aField.getAnnotation(InjectedBean.class); + if (propertyAnnotation != null) { + aField.setAccessible(true); + if (!isFieldNUll(aField, o)) { + continue; + } + Class beanType = aField.getType(); + boolean isInterface = beanType.isInterface(); + if (isInterface) { + boolean isImplementationFound = false; + // Step 1 : take default implementation + ImplementedBy defaultImplementationAnnotation = beanType.getAnnotation(ImplementedBy.class); + if (defaultImplementationAnnotation != null) { + beanType = defaultImplementationAnnotation.value(); + isImplementationFound = true; + } + // Step 2 : overwrite it by another one if declared + // directly on the field + if (!Object.class.equals(propertyAnnotation.implementation())) { + beanType = propertyAnnotation.implementation(); + isImplementationFound = true; + } + // Step 3 : look for default impletation in factory + if (BeanFactory.getFactory(context).getBean(beanType) != null) { + isImplementationFound = true; + } + if (!isImplementationFound) { + throw new ManiocException("Unable to find which implementation to use for a bean of type " + aField.getType().getName() + " . Application context is " + context.getSimpleName()); + } + } + + Object beanToInject = BeanFactory.getFactory(context).getBean(beanType); + if (beanToInject == null) { + if (isInterface) { + throw new ManiocException("Unable to inject a bean which implements " + aField.getType().getName() + " . Implementation excepted is " + beanType.getName() + ". Application context is " + context.getSimpleName()); + } + if (!isInterface) { + throw new ManiocException("Unable to inject a bean of type " + beanType.getName() + " . No such bean found. Application context is " + context.getSimpleName()); + } + } + try { + aField.set(o, beanToInject); + } catch (IllegalArgumentException e) { + throw new ManiocException("Error while setting field value of bean managed by the BeanFactory Application context is " + context.getSimpleName(), e); + } catch (IllegalAccessException e) { + throw new ManiocException("Error while setting field value of bean managed by the BeanFactory Application context is " + context.getSimpleName(), e); + } + } + } + } + } + + /** + * Takes an objet and returns its class and all its inherited classes + * + * @param o + * @return + */ + private List> getClassAndSuperClasses(Object o) { + List> result = new ArrayList>(); + List> fifo = new ArrayList>(); + fifo.add(o.getClass()); + while (!fifo.isEmpty()) { + Class aClass = fifo.remove(0); + ; + Class aSuperClass = aClass.getSuperclass(); + if (aSuperClass != null) { + fifo.add(aSuperClass); + } + result.add(aClass); + } + return result; + } + + /** + * Checks if a field is empty (null value) + * + * @param aField + * @param o + * @return true if null + */ + private boolean isFieldNUll(Field aField, Object o) { + try { + Object currentValue = aField.get(o); + if (currentValue == null) { + return true; + } + } catch (IllegalArgumentException e1) { + throw new ManiocException("Error while getting field value of bean managed by the BeanFactory. Field = " + aField.getName(), e1); + } catch (IllegalAccessException e1) { + throw new ManiocException("Error while getting field value of bean managed by the BeanFactory. Field = " + aField.getName(), e1); + } + return false; + } + + } + + public static class BeanFactory { + + private Map, Object> singletonsMap = new HashMap, Object>(); + + private Class context; + + private static Map, BeanFactory> factoryList = new HashMap, BeanFactory>(); + + private static Map> managedBeansOnEveryContexts = new HashMap>(); + + /** + * Listen to JVM shutdown to free resources on all managed beans + */ + static { + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + Set beanSet = BeanFactory.managedBeansOnEveryContexts.keySet(); + for (Object aBean : beanSet) { + Class beanContext = BeanFactory.managedBeansOnEveryContexts.get(aBean); + BeanFactory.preDestroy(aBean, beanContext); + } + } + }); + } + + /** + * Singleton constructor + */ + private BeanFactory() { + // Singleton pattern + } + + /** + * @return the default factory + */ + public static BeanFactory getFactory() { + return BeanFactory.getFactory(DefaultApplicationContext.class); + } + + /** + * @param context + * @return factory from the specified scope + */ + public static BeanFactory getFactory(Class context) { + if (!BeanFactory.factoryList.containsKey(context)) { + BeanFactory newInstance = new BeanFactory(); + newInstance.context = context; + BeanFactory.factoryList.put(context, newInstance); + } + return BeanFactory.factoryList.get(context); + } + + /** + * Checks if the current factory has already worked whith a bean of the + * specified type + * + * @param + * @param classType + * @return true if found + */ + public boolean contains(Class classType) { + Set beanSet = BeanFactory.managedBeansOnEveryContexts.keySet(); + for (Object aBean : beanSet) { + Class beanContext = BeanFactory.managedBeansOnEveryContexts.get(aBean); + if (!beanContext.equals(this.context)) { + continue; + } + if (classType.isInstance(aBean)) { + return true; + } + } + return false; + } + + /** + * Returns the bean of the specified type + * + * @param + * @param classType + * @return + */ + public T getBean(Class classType) { + boolean isRegisteredManually = isRegisteredManually(classType); + checkVisibilityFromCurrentContext(classType); + T bean = getAlreadyExistingBean(classType); + if (isRegisteredManually) { + if (bean == null) { + throw new ManiocException("Unable to find a bean of type " + classType.getName() + " which is annotated with @" + ManagedBean.class.getSimpleName() + "(registeredManually=true). Application context is " + + this.context.getSimpleName()); + } + } + if (bean != null) { + return bean; + } + if (!classType.isInterface()) { + return createBean(classType); + } + return null; + } + + /** + * Returns the creation context of the given bean or null if no bean + * found + * + * @param bean + * @return the context class if bean found + */ + public static Class getBeanContext(Object bean) { + Class beanContext = BeanFactory.managedBeansOnEveryContexts.get(bean); + return beanContext; + } + + /** + * Checks if this kind of class is reachable from the context of the + * factory + * + * @param + * @param classType + */ + private void checkVisibilityFromCurrentContext(Class classType) { + ManagedBean annotation = classType.getAnnotation(ManagedBean.class); + if (annotation == null) { + return; + } + Class reachableContext = annotation.applicationContext(); + boolean isClassContextIsChildOfCurrent = reachableContext.isAssignableFrom(this.context); + if (!isClassContextIsChildOfCurrent) { + throw new ManiocException("Cannot get a bean type " + classType.getName() + " from context " + this.context.getSimpleName() + " because it has been created in " + reachableContext.getSimpleName() + " which is not a child of " + + this.context.getSimpleName()); + } + } + + /** + * Checks if the factory should contain only one instance of this class.
+ * There's two cases : \@ManagerBean.scope() is set to 'singleton' or + * \@ManagedBean.registerManually() is set to true + * + * @param + * @param classType + * @return true if singleton + */ + private boolean isSingleton(Class classType) { + ManagedBean annotation = classType.getAnnotation(ManagedBean.class); + if (annotation == null) { + return false; + } + BeanScope beanScope = annotation.scope(); + if (beanScope.equals(BeanScope.SINGLETON)) { + return true; + } + if (annotation.registeredManually()) { + return true; + } + return false; + } + + /** + * Checks if this kind of bean should be or not created by the factory + * + * @param + * @param classType + * @return true if the class is annotated with + * \@ManagedBean(registeredManually=true) + */ + private boolean isRegisteredManually(Class classType) { + ManagedBean annotation = classType.getAnnotation(ManagedBean.class); + if (annotation == null) { + return false; + } + return annotation.registeredManually(); + } + + /** + * Looks for an existing bean inside all the contexts hierarchy + * + * @param + * @param classType + * @return + */ + private T getAlreadyExistingBean(Class classType) { + if (singletonsMap.containsKey(classType)) { + Object object = singletonsMap.get(classType); + Class implementationType = object.getClass(); + ManagedBean annotation = implementationType.getAnnotation(ManagedBean.class); + if (annotation == null) { + throw new ManiocException("Error on " + implementationType.getName() + " . All the class instances you want to inject must have the annotation @" + ManagedBean.class.getSimpleName() + " declared. Application context is " + + this.context.getSimpleName()); + } + return (T) object; + } + BeanFactory parentBeanFactory = getParentBeanFactory(); + if (parentBeanFactory != null) { + return parentBeanFactory.getAlreadyExistingBean(classType); + } + return null; + } + + /** + * @return the parent bean factory or null if this context has no parent + */ + private BeanFactory getParentBeanFactory() { + Class parentContext = getParentContext(); + if (parentContext == null) { + return null; + } + return BeanFactory.factoryList.get(parentContext); + } + + /** + * Looks for the parent context of this context if it exists + * + * @param childContext + * @return the parent context or null if there's no parent + */ + private Class getParentContext() { + Class aSuperClass = this.context.getSuperclass(); + if (aSuperClass != null && aSuperClass.isAssignableFrom(DefaultApplicationContext.class)) { + return (Class) aSuperClass; + } + return null; + } + + /** + * Determines all the parent contexts that the given context can see + * + * @param childContext + * @return a list of contexts from the child to the oldest parent + */ + private List> getContextHierarchy(Class childContext) { + List> result = new ArrayList>(); + List> fifo = new ArrayList>(); + fifo.add(childContext); + while (!fifo.isEmpty()) { + Class aClass = fifo.remove(0); + Class aSuperClass = aClass.getSuperclass(); + if (aSuperClass != null && aSuperClass.isAssignableFrom(DefaultApplicationContext.class)) { + fifo.add((Class) aSuperClass); + } + result.add(aClass); + } + return result; + } + + /** + * Register the given bean in the current BeanFactory. If this bean's + * fields are annotated with @Inject, they would be injected too.
+ * + * @param classType + * (prefer using the interface here) + * @param aBean + */ + public void register(Class classType, T bean) { + boolean isInterface = classType.isInterface(); + if (!isInterface) { + checkIfBeanIsManageable(classType); + boolean isSingleton = isSingleton(classType); + if (isSingleton) { + if (singletonsMap.containsKey(classType)) { + throw new ManiocException("Duplicate beans of type " + classType.getName() + " in the context " + this.context.getSimpleName()); + } + singletonsMap.put(classType, bean); + } + } + if (isInterface) { + checkIfBeanIsManageable(bean.getClass()); + boolean isSingleton = isSingleton(bean.getClass()); + if (isSingleton) { + if (singletonsMap.containsKey(classType)) { + throw new ManiocException("Duplicate beans of type " + classType.getName() + " in the context " + this.context.getSimpleName()); + } + if (singletonsMap.containsKey(bean.getClass())) { + throw new ManiocException("Duplicate beans of type " + bean.getClass().getName() + " in the context " + this.context.getSimpleName()); + } + singletonsMap.put(classType, bean); + singletonsMap.put(bean.getClass(), bean); + } + } + BeanInjector beanInjector = BeanInjector.getInjector(); + try { + beanInjector.inject(bean); + } catch (ManiocException re) { + singletonsMap.remove(classType); + throw new ManiocException("Error while registering a bean of type " + classType.getName() + " . Application context is " + this.context.getSimpleName(), re); + } + BeanFactory.managedBeansOnEveryContexts.put(bean, this.context); + } + + /** + * Creates a new class instance (on the desired context if specified) + * + * @param + * @param classType + * @return the newly created bean + */ + private T createBean(Class classType) { + checkIfBeanIsManageable(classType); + boolean isRegisteredManually = isRegisteredManually(classType); + if (isRegisteredManually) { + throw new ManiocException("Cannot create a bean of type " + classType.getName() + " because it has the annotation @" + ManagedBean.class.getSimpleName() + ".registeredManually() set to true. Current context is " + + this.context.getSimpleName()); + } + ManagedBean annotation = classType.getAnnotation(ManagedBean.class); + Class classTypeContext = annotation.applicationContext(); + BeanFactory classTypeFactory = BeanFactory.getFactory(classTypeContext); + T newInstance = classTypeFactory.createBeanFromAnnotatedConstructor(classType); + if (newInstance == null) { + newInstance = classTypeFactory.createBeanFromAnnotatedFactoryMethod(classType); + } + if (newInstance == null) { + newInstance = classTypeFactory.createBeanFromDefaultConstructor(classType); + } + if (newInstance != null) { + classTypeFactory.register(classType, newInstance); + classTypeFactory.postConstruct(classType, newInstance); + return newInstance; + } + throw new ManiocException("BeanFactory unexpected error. Failed to create bean of type " + classType.getName()); + } + + /** + * @param + * @param classType + * @return newly created bean + */ + private T createBeanFromDefaultConstructor(Class classType) { + Constructor defaultConstructor = getDefaultConstructor(classType); + if (defaultConstructor != null) { + try { + T newInstance = classType.newInstance(); + return newInstance; + } catch (Exception e) { + throw new ManiocException("BeanFactory failed to create bean of type " + classType.getName() + " from its default constructor. Application context is " + this.context.getSimpleName(), e); + } + } + return null; + } + + /** + * @param + * @param classType + * @return newly created bean + */ + private T createBeanFromAnnotatedFactoryMethod(Class classType) { + Method annotatedFactoryMethod = getAnnotatedFactoryMethod(classType); + if (annotatedFactoryMethod != null) { + Class[] parameterTypes = annotatedFactoryMethod.getParameterTypes(); + Object[] parameterBeans = BeanFactory.getBeans(parameterTypes, this.context); + try { + T newInstance = (T) annotatedFactoryMethod.invoke(null, parameterBeans); + return newInstance; + } catch (Exception e) { + throw new ManiocException("BeanFactory failed to create bean of type " + classType.getName() + " from its method " + annotatedFactoryMethod.getName() + ". Application context is " + this.context.getSimpleName(), e); + } + } + return null; + } + + /** + * @param + * @param classType + * @return a nealy created bean + */ + private T createBeanFromAnnotatedConstructor(Class classType) { + Constructor annotatedConstructor = getAnnotatedConstructor(classType); + if (annotatedConstructor != null) { + Class[] parameterTypes = annotatedConstructor.getParameterTypes(); + Object[] parameterBeans = BeanFactory.getBeans(parameterTypes, this.context); + try { + T newInstance = annotatedConstructor.newInstance(parameterBeans); + return newInstance; + } catch (Exception e) { + throw new ManiocException("BeanFactory failed to create bean of type " + classType.getName() + " from its annotated constructor . Application context is " + this.context.getSimpleName(), e); + } + } + return null; + } + + /** + * Calls all methods annotated with \@PostConstruct. Injects parameters + * bean when it's possible (or inject a null value).
+ * This is used to initialize beans after construction.
+ * + * @param + * @param beanType + * @param bean + * which need to be initialized by calling some post + * construction methods + */ + private void postConstruct(Class beanType, Object bean) { + Method[] methods = beanType.getMethods(); + for (Method aMethod : methods) { + PostConstruct annotation = aMethod.getAnnotation(PostConstruct.class); + if (annotation == null) { + continue; + } + Class[] parameterTypes = aMethod.getParameterTypes(); + Object[] parameterBeans = BeanFactory.getBeans(parameterTypes, this.context); + try { + aMethod.invoke(bean, parameterBeans); + } catch (Exception e) { + throw new ManiocException("BeanFactory failed to invoke the post construction method '" + aMethod.getName() + "' on the bean of type " + beanType.getName() + ". Application context is " + this.context.getSimpleName(), e); + } + } + } + + /** + * Calls all methods annotated with \@PreDetroy. Injects parameters bean + * when it's possible (or inject a null value).
+ * This is used to free resources on beans on JVM shutdown.
+ * + * @param bean + * @param context + */ + private static void preDestroy(Object bean, Class context) { + Class beanType = bean.getClass(); + Method[] methods = beanType.getMethods(); + for (Method aMethod : methods) { + PreDestroy annotation = aMethod.getAnnotation(PreDestroy.class); + if (annotation == null) { + continue; + } + Class[] parameterTypes = aMethod.getParameterTypes(); + Object[] parameterBeans = BeanFactory.getBeans(parameterTypes, context); + try { + aMethod.invoke(bean, parameterBeans); + } catch (Exception e) { + throw new ManiocException("BeanFactory failed to invoke the pre detroy method '" + aMethod.getName() + "' on the bean of type " + beanType.getName() + ". Application context is " + context.getName(), e); + } + } + } + + /** + * Takes an array of classes + a context and return an array containing + * beans from this context.
+ * Useful to get beans to inject in a constructor or a method.
+ * + * @param classTypes + * @param context + * @return an array of beans. Could contain null value if no bean is + * found. + */ + private static Object[] getBeans(Class[] classTypes, Class context) { + BeanFactory contextFactory = factoryList.get(context); + Object[] beans = new Object[classTypes.length]; + for (int i = 0; i < classTypes.length; i++) { + Class aClassType = classTypes[i]; + Object beanToInject = contextFactory.getBean(aClassType); + beans[i] = beanToInject; + } + return beans; + } + + /** + * Verifies if this class is annotated with \@ManagedBean
+ * which means that this factory can handle it + * + * @param + * @param classType + * @throws ManiocException + * if the class is not annotated with \@ManagedBean + */ + private void checkIfBeanIsManageable(Class classType) throws ManiocException { + ManagedBean annotation = classType.getAnnotation(ManagedBean.class); + if (annotation == null) { + throw new ManiocException("Error on " + classType + " . All the class instances you want to inject must have the annotation @" + ManagedBean.class.getSimpleName() + " declared. Application context is " + this.context.getSimpleName()); + } + } + + /** + * @param + * @param classType + * @return the constructor annotated with \@Constructor or null if no + * one is found + */ + private Constructor getAnnotatedConstructor(Class classType) { + Constructor[] constructors = (Constructor[]) classType.getConstructors(); + List> result = new ArrayList>(); + for (Constructor aConstructor : constructors) { + int modifiers = aConstructor.getModifiers(); + if ((modifiers & Modifier.PUBLIC) != 0) { + continue; + } + Construct annotation = aConstructor.getAnnotation(Construct.class); + if (annotation == null) { + continue; + } + result.add(aConstructor); + } + if (result.size() == 1) { + return result.get(0); + } + if (result.size() > 1) { + throw new ManiocException("BeanFactory cannot determine which constructor to use on " + classType.getName() + " because there's more than one constructor annotated with @" + Construct.class.getSimpleName()); + } + return null; + } + + /** + * @param + * @param classType + * @return the method annotated with \@Constructor or null if no one is + * found + */ + private Method getAnnotatedFactoryMethod(Class classType) { + Method[] methods = classType.getMethods(); + List result = new ArrayList(); + for (Method aMethod : methods) { + Class returnType = aMethod.getReturnType(); + if (!returnType.equals(classType)) { + continue; + } + int modifiers = aMethod.getModifiers(); + if ((modifiers & Modifier.PUBLIC & Modifier.STATIC) != 0) { + continue; + } + Construct annotation = aMethod.getAnnotation(Construct.class); + if (annotation == null) { + continue; + } + result.add(aMethod); + } + if (result.size() == 1) { + return result.get(0); + } + if (result.size() > 1) { + throw new ManiocException("BeanFactory cannot determine which method to use to construct a bean on " + classType.getName() + " because there's more than one method annotated with @" + Construct.class.getSimpleName()); + } + return null; + } + + /** + * @param classType + * @return the constructor with no parameter or null if no one if found + */ + private Constructor getDefaultConstructor(Class classType) { + Constructor[] constructors = (Constructor[]) classType.getConstructors(); + for (Constructor aConstructor : constructors) { + Class[] parameterTypes = aConstructor.getParameterTypes(); + if (parameterTypes.length == 0) { + return aConstructor; + } + } + return null; + } + + } + + /** + * Allows to isolate beans on different scopes. All scopes need to + * inheritate from this interface. A child scope can see beans from its + * parents. + * + * @author Alexandre de Pellegrin + * + */ + public static class DefaultApplicationContext { + + } + + /** + * Annotation used to mark methods to call to instance a class + * + * @author Alexandre de Pellegrin + * + */ + @Target({ METHOD, CONSTRUCTOR }) + @Retention(value = RetentionPolicy.RUNTIME) + public @interface Construct { + + } + + /** + * Annotation used to inject beans managed by ManiocFactory + * + * @author Alexandre de Pellegrin + * + */ + @Target({ FIELD }) + @Retention(value = RetentionPolicy.RUNTIME) + public @interface InjectedBean { + + /** + * @return the desired implementation. Optional. If not precised, it + * takes the field's type + */ + public Class implementation() default Object.class; + + } + + /** + * Annotation used to specify a default implementation on an interface + * + * @author Alexandre de Pellegrin + * + */ + @Target({ TYPE }) + @Retention(value = RetentionPolicy.RUNTIME) + public @interface ImplementedBy { + + /** + * @return the desired implementation. Optional. If not precised, it + * takes the field's type + */ + public Class value(); + + } + + /** + * Annotation used to auto create beans with the BeanFactory + * + * @author Alexandre de Pellegrin + * + */ + @Target({ TYPE }) + @Retention(value = RetentionPolicy.RUNTIME) + public @interface ManagedBean { + + /** + * @return true if the bean instance is automatically created by the + * BeanFactory or registered manually + */ + public boolean registeredManually() default false; + + /** + * @return the context of the bean (which is linked to its visibility + * with other contexts) + */ + public Class applicationContext() default DefaultApplicationContext.class; + + /** + * @return the scope of the bean : one instance per context (singleton) + * or construction of a new instance on each request to the bean + * factory
+ */ + public BeanScope scope() default BeanScope.SINGLETON; + + } + + /** + * Scope of a bean. Indicates if a bean is a singleton on its context or
+ * if a new instance should be constructed on each request to the factory.
+ * + * @author Alexandre de Pellegrin + * + */ + public enum BeanScope { + SINGLETON, PROTOTYPE + } + + /** + * Annotation used to mark methods to call on JVM shutdown + * + * @author Alexandre de Pellegrin + * + */ + @Target({ METHOD }) + @Retention(value = RetentionPolicy.RUNTIME) + public @interface PostConstruct { + + } + + /** + * Annotation used to mark methods to call on JVM shutdown + * + * @author Alexandre de Pellegrin + * + */ + @Target({ METHOD }) + @Retention(value = RetentionPolicy.RUNTIME) + public @interface PreDestroy { + + } + + /** + * Just an unchecked exception you can trap if you want + * + * @author Alexandre de Pellegrin + * + */ + public static class ManiocException extends RuntimeException { + + public ManiocException(String msg, Throwable t) { + super(msg, t); + } + + public ManiocException(String msg) { + super(msg); + } + + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceBundleConstant.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceBundleConstant.java new file mode 100644 index 0000000..b1e28ea --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceBundleConstant.java @@ -0,0 +1,32 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.injection.resources; + +public interface ResourceBundleConstant +{ + + public static final String OTHER_STRINGS = "properties.OtherStrings"; + public static final String FILE_STRINGS = "properties.FileStrings"; + public static final String GENERIC_STRINGS = "properties.%Strings"; + public static final String NODE_AND_EDGE_STRINGS = "properties.NodeAndEdgeStrings"; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceBundleInjector.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceBundleInjector.java new file mode 100644 index 0000000..5298c71 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceBundleInjector.java @@ -0,0 +1,218 @@ +package com.horstmann.violet.framework.injection.resources; + +import java.awt.Image; +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.util.Locale; +import java.util.ResourceBundle; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JMenu; +import javax.swing.JMenuItem; + +import com.horstmann.violet.framework.injection.resources.annotation.ResourceBundleBean; + +/** + * This class can inject external values into object attributes. To do that, it uses the {@link ResourceBundleBean} + * annotation. Injected objects are created using the {@link ResourceFactory} + * + * + * @author Alexandre de Pellegrin + * + */ +public class ResourceBundleInjector +{ + + /** + * Unique instance + */ + private static ResourceBundleInjector instance; + + /** + * Singleton constructor + */ + private ResourceBundleInjector() + { + // Singleton pattern + } + + /** + * @return injector instance + */ + public static ResourceBundleInjector getInjector() + { + if (ResourceBundleInjector.instance == null) + { + ResourceBundleInjector.instance = new ResourceBundleInjector(); + } + return ResourceBundleInjector.instance; + } + + /** + * Injects values into object attributes if they are correctly annoted + * + * @param o which need injection + */ + public void inject(Object o) + { + ResourceBundle classResourceBundle = getPropertyFile(o); + Class referencedClass = getReferencedClass(o); + ResourceFactory classResourceFactory = new ResourceFactory(classResourceBundle, referencedClass); + // Injects on constructors + for (Constructor aConstructor : o.getClass().getDeclaredConstructors()) + { + for (Annotation annotation : aConstructor.getAnnotations()) + { + Class annotationType = annotation.annotationType(); + if (annotationType.equals(ResourceBundleBean.class)) + { + ResourceBundleBean constructorAnnotation = (ResourceBundleBean) annotation; + injectState(o, constructorAnnotation, classResourceFactory); + } + } + } + // Injects on fields + for (Field aField : o.getClass().getDeclaredFields()) + { + for (Annotation annotation : aField.getAnnotations()) + { + Class annotationType = annotation.annotationType(); + if (annotationType.equals(ResourceBundleBean.class)) + { + ResourceBundleBean propertyAnnotation = (ResourceBundleBean) annotation; + try + { + aField.setAccessible(true); + if (!propertyAnnotation.resourceReference().equals(Object.class)) + { + ResourceBundle fieldResourceBundle = ResourceBundle.getBundle(propertyAnnotation.resourceReference() + .getName(), Locale.getDefault()); + ResourceFactory fieldResourceFactory = new ResourceFactory(fieldResourceBundle, o.getClass()); + injectValue(o, aField, propertyAnnotation, fieldResourceFactory); + } + else + { + injectValue(o, aField, propertyAnnotation, classResourceFactory); + } + } + catch (IllegalAccessException e) + { + throw new RuntimeException("Error while injecting external property values", e); + } + } + } + } + } + + /** + * Gets the correct property file. + * + * @param o object which need injection + * @return resource bundle found + */ + private ResourceBundle getPropertyFile(Object o) + { + Class referencedClass = getReferencedClass(o); + return ResourceBundle.getBundle(referencedClass.getName(), Locale.getDefault()); + } + + /** + * Gets the referenced class + * + * @param o object which need injection + * @return the corresponding class or the class declared in ResourceBundleBean.resourceReference() + */ + private Class getReferencedClass(Object o) + { + for (Annotation annotation : o.getClass().getAnnotations()) + { + Class annotationType = annotation.annotationType(); + if (annotationType.equals(ResourceBundleBean.class)) + { + ResourceBundleBean propertyAnnotation = (ResourceBundleBean) annotation; + Class ref = propertyAnnotation.resourceReference(); + if (!Object.class.equals(ref)) + { + return ref; + } + } + } + return o.getClass(); + } + + /** + * Injects values + * + * @param concernedObject = object which need injection + * @param field = field to inject + * @param annotation = current field annotation + * @param resourceFactory = resource factory belonging to the concernedObject + * @throws IllegalAccessException if field is not accessible + */ + private void injectValue(Object concernedObject, Field field, ResourceBundleBean annotation, + ResourceFactory resourceFactory) throws IllegalAccessException + { + String propertyPrefix = (!"".equals(annotation.key()) ? annotation.key() : field.getName()); + Class fieldType = field.getType(); + if (fieldType.equals(String.class)) + { + String value = resourceFactory.createString(propertyPrefix); + field.set(concernedObject, value); + } + if (fieldType.equals(ImageIcon.class)) + { + ImageIcon value = resourceFactory.createIcon(propertyPrefix); + field.set(concernedObject, value); + } + if (fieldType.equals(Image.class)) + { + Image value = resourceFactory.createImage(propertyPrefix); + field.set(concernedObject, value); + } + if (fieldType.equals(JButton.class)) + { + JButton value = resourceFactory.createButton(propertyPrefix); + field.set(concernedObject, value); + } + if (fieldType.equals(JMenu.class)) + { + JMenu value = resourceFactory.createMenu(propertyPrefix); + field.set(concernedObject, value); + } + if (fieldType.equals(JMenuItem.class)) + { + JMenuItem value = resourceFactory.createMenuItem(propertyPrefix); + field.set(concernedObject, value); + } + if (fieldType.equals(JCheckBoxMenuItem.class)) + { + JMenuItem value = resourceFactory.createCheckBoxMenuItem(propertyPrefix); + field.set(concernedObject, value); + } + } + + /** + * Configures an object + * + * @param concernedObject + * @param annotation + * @param resourceFactory + */ + private void injectState(Object concernedObject, ResourceBundleBean annotation, ResourceFactory resourceFactory) + { + Class clazz = concernedObject.getClass(); + String propertyPrefix = annotation.key(); + if ("".equals(propertyPrefix)) + { + return; + } + if (JMenu.class.isAssignableFrom(clazz)) + { + resourceFactory.configureMenu((JMenu) concernedObject, propertyPrefix); + } + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceBundleUtils.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceBundleUtils.java new file mode 100644 index 0000000..4625227 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceBundleUtils.java @@ -0,0 +1,43 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.injection.resources; + +import java.util.Locale; +import java.util.ResourceBundle; + +public class ResourceBundleUtils +{ + + /** + * @param obj that we need to look for resource + * @return the corresponding resource bundle. Ex : ClassDiagramGraph.class -> ClassDiagramGraphStrings.properties + */ + public static ResourceBundle getStringsResourceBundleForObject(Object obj) + { + String fullClassName = obj.getClass().getName(); + String packageClassName = obj.getClass().getPackage().getName(); + String shortClassName = fullClassName.substring(packageClassName.length() + 1); + return ResourceBundle.getBundle(ResourceBundleConstant.GENERIC_STRINGS.replaceFirst("%", shortClassName), Locale + .getDefault()); + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceFactory.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceFactory.java new file mode 100644 index 0000000..edc32ed --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/ResourceFactory.java @@ -0,0 +1,296 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.injection.resources; + +import java.awt.Font; +import java.awt.FontFormatException; +import java.awt.Image; +import java.io.IOException; +import java.io.InputStream; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JRadioButtonMenuItem; +import javax.swing.KeyStroke; + +public class ResourceFactory +{ + public ResourceFactory(ResourceBundle bundle) + { + this.bundle = bundle; + this.referenceClass = Object.class; + } + + public ResourceFactory(ResourceBundle bundle, Class referenceClass) + { + this.bundle = bundle; + this.referenceClass = referenceClass; + } + + public ResourceBundle getResourceBundle() { + return this.bundle; + } + + public String createString(String key) { + String text = bundle.getString(key); + return text; + } + + public JMenuItem createMenuItem(String prefix) + { + String text = bundle.getString(prefix + ".text"); + JMenuItem menuItem = new JMenuItem(text); + updateMenuItem(menuItem, prefix); + return menuItem; + } + + public JMenuItem createCheckBoxMenuItem(String prefix) + { + String text = bundle.getString(prefix + ".text"); + JMenuItem menuItem = new JCheckBoxMenuItem(text); + updateMenuItem(menuItem, prefix); + return menuItem; + } + + public JMenuItem createRadioButtonMenuItem(String prefix) + { + String text = bundle.getString(prefix + ".text"); + JMenuItem menuItem = new JRadioButtonMenuItem(text); + updateMenuItem(menuItem, prefix); + return menuItem; + } + + private void updateMenuItem(JMenuItem menuItem, String prefix) + { + menuItem.setName(prefix); + try + { + String text = bundle.getString(prefix + ".text"); + menuItem.setText(text); + } + catch (MissingResourceException exception) + { + // ok not to set mnemonic + } + try + { + String mnemonic = bundle.getString(prefix + ".mnemonic"); + menuItem.setMnemonic(mnemonic.charAt(0)); + } + catch (MissingResourceException exception) + { + // ok not to set mnemonic + } + + try + { + String accelerator = bundle.getString(prefix + ".accelerator"); + menuItem.setAccelerator(KeyStroke.getKeyStroke(accelerator)); + } + catch (MissingResourceException exception) + { + // ok not to set accelerator + } + + try + { + String tooltip = bundle.getString(prefix + ".tooltip"); + menuItem.setToolTipText(tooltip); + } + catch (MissingResourceException exception) + { + // ok not to set tooltip + } + + try + { + String iconPath = bundle.getString(prefix + ".icon"); + if (iconPath != null) + { + ImageIcon icon = new ImageIcon(this.referenceClass.getResource(iconPath)); + menuItem.setIcon(icon); + } + } + catch (MissingResourceException exception) + { + // ok not to set tooltip + } + } + + public JMenu createMenu(String prefix) + { + String text = bundle.getString(prefix + ".text"); + JMenu menu = new JMenu(text); + configureMenu(menu, prefix); + return menu; + } + + /** + * Updates menu configuration + * + * @param menu + * @param prefix (in properties file) + */ + public void configureMenu(JMenu menu, String prefix) + { + menu.setName(prefix); + String text = bundle.getString(prefix + ".text"); + menu.setText(text); + try + { + String mnemonic = bundle.getString(prefix + ".mnemonic"); + menu.setMnemonic(mnemonic.charAt(0)); + } + catch (MissingResourceException exception) + { + // ok not to set mnemonic + } + + try + { + String tooltip = bundle.getString(prefix + ".tooltip"); + menu.setToolTipText(tooltip); + } + catch (MissingResourceException exception) + { + // ok not to set tooltip + } + + try + { + String iconPath = bundle.getString(prefix + ".icon"); + if (iconPath != null) + { + ImageIcon icon = new ImageIcon(this.referenceClass.getResource(iconPath)); + menu.setIcon(icon); + } + } + catch (MissingResourceException exception) + { + // ok not to set tooltip + } + } + + public JButton createButton(String prefix) + { + JButton button = new JButton(); + try + { + String text = bundle.getString(prefix + ".text"); + button.setText(text); + } + catch (MissingResourceException exception) + { + // ok not to set text + } + try + { + ImageIcon icon = new ImageIcon(this.referenceClass.getResource(this.bundle.getString(prefix + ".icon"))); + button.setIcon(icon); + } + catch (MissingResourceException exception) + { + // ok not to set text + } + try + { + String mnemonic = bundle.getString(prefix + ".mnemonic"); + button.setMnemonic(mnemonic.charAt(0)); + } + catch (MissingResourceException exception) + { + // ok not to set mnemonic + } + + try + { + String tooltip = bundle.getString(prefix + ".tooltip"); + button.setToolTipText(tooltip); + } + catch (MissingResourceException exception) + { + // ok not to set tooltip + } + return button; + } + + + /** + * Creates a font from its correponding true type file + * + * @param fontResource ttf file + * @return new Font + */ + public Font createFont(String fontResource) + { + try + { + InputStream is = this.referenceClass.getResourceAsStream(bundle.getString(fontResource)); + Font ttfBase; + ttfBase = Font.createFont(Font.TRUETYPE_FONT, is); + return ttfBase; + } + catch (FontFormatException e) + { + throw new RuntimeException(e); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + /** + * Creates an image + * + * @param resourceName image file path + * @return image + */ + public Image createImage(String resourceName) + { + ImageIcon icon = createIcon(resourceName); + return icon.getImage(); + } + + /** + * Creates an icon image + * + * @param resourceName file path + * @return icon + */ + public ImageIcon createIcon(String resourceName) + { + return new ImageIcon(this.referenceClass.getResource(bundle.getString(resourceName))); + } + + private ResourceBundle bundle; + + /** + * Class used as reference to classpath ressources such as icons + */ + private Class referenceClass; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/annotation/ResourceBundleBean.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/annotation/ResourceBundleBean.java new file mode 100644 index 0000000..a350b52 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/injection/resources/annotation/ResourceBundleBean.java @@ -0,0 +1,14 @@ +package com.horstmann.violet.framework.injection.resources.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(value = RetentionPolicy.RUNTIME) +public @interface ResourceBundleBean +{ + + public String key() default ""; + + public Class resourceReference() default Object.class; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/AbstractPlugin.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/AbstractPlugin.java new file mode 100644 index 0000000..a42e9b1 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/AbstractPlugin.java @@ -0,0 +1,28 @@ +package com.horstmann.violet.framework.plugin; + +/** + * This interfaces describes mandatory properties for all plugins + * + * @author Alexandre de Pellegrin + * + */ +interface AbstractPlugin +{ + + /** + * @return very short plugin description (ex : Class diagram XMI extension) + */ + public abstract String getDescription(); + + /** + * + * @return plugin's provider or authors + */ + public abstract String getProvider(); + + /** + * @return plugin's version. Please, keep the pattern aa.bb.cc as major-version.minor-version.patch. (ex : 1.20.0) + */ + public abstract String getVersion(); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/IDiagramPlugin.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/IDiagramPlugin.java new file mode 100644 index 0000000..a65f49b --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/IDiagramPlugin.java @@ -0,0 +1,34 @@ +package com.horstmann.violet.framework.plugin; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; + +/** + * Describes a Violet's plugin embedding a new kind of diagram. + * + * @author Alexandre de Pellegrin + * + */ +public interface IDiagramPlugin extends AbstractPlugin +{ + + /** + * @return graph type name (ex : Class Diagram) + */ + public abstract String getName(); + + /** + * @return file extension associated to this graph (ex : .class.violet) + */ + public abstract String getFileExtension(); + + /** + * @return file extension textual name (ex : Class Diagram Files) + */ + public abstract String getFileExtensionName(); + + /** + * @return corresponding graph class + */ + public abstract Class getGraphClass(); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/PluginLoader.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/PluginLoader.java new file mode 100644 index 0000000..37c56f4 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/PluginLoader.java @@ -0,0 +1,127 @@ +package com.horstmann.violet.framework.plugin; + +import java.io.File; +import java.io.FileFilter; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +import org.apache.commons.vfs.FileObject; +import org.apache.commons.vfs.FileSystemManager; +import org.apache.commons.vfs.VFS; +import org.apache.commons.vfs.impl.VFSClassLoader; + +import com.horstmann.violet.framework.file.persistence.IFilePersistenceService; +import com.horstmann.violet.framework.file.persistence.Violet016BackportFormatService; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.ManagedBean; +import com.horstmann.violet.framework.plugin.extensionpoint.Violet016FileFilterExtensionPoint; + +@ManagedBean +public class PluginLoader extends ClassLoader +{ + + + + + public void installPlugins() + { + ServiceLoader list = ServiceLoader.load(IDiagramPlugin.class, this.getClass().getClassLoader()); + for (IDiagramPlugin aPlugin : list) + { + this.pluginRegistry.register(aPlugin); + if (aPlugin instanceof Violet016FileFilterExtensionPoint) + { + Violet016FileFilterExtensionPoint extensionPoint = (Violet016FileFilterExtensionPoint) aPlugin; + Map mappingToKeepViolet016Compatibility = extensionPoint.getMappingToKeepViolet016Compatibility(); + Violet016BackportFormatService.addViolet016CompatibilityEntries(mappingToKeepViolet016Compatibility); + } + } + } + + private ClassLoader getExternalClassLoader() + { + String pluginDirName = System.getProperty("violet.plugin.dir"); + if (pluginDirName == null) return new URLClassLoader(new URL[0]); + File pluginDir = new File(pluginDirName); + File[] pluginJars = pluginDir.listFiles(new FileFilter() + { + public boolean accept(File pathname) + { + return pathname.toString().endsWith(".jar"); + } + }); + + URL[] pluginJarUrls = new URL[pluginJars.length]; + for (int i = 0; i < pluginJars.length; i++) + try + { + pluginJarUrls[i] = pluginJars[i].toURI().toURL(); + } + catch (MalformedURLException ex) + { + ex.printStackTrace(); + } + + return new URLClassLoader(pluginJarUrls); + } + + private ClassLoader getJarInJarClassLoader() + { + try + { + FileSystemManager fsManager = VFS.getManager(); + List innerJarFiles = new ArrayList(); + String classPathContent = System.getProperty("java.class.path"); + String[] classPathFiles = classPathContent.split(File.pathSeparator); + for (String aClassPathFile : classPathFiles) + { + File testingFile = new File(aClassPathFile); + if (!testingFile.exists()) + { + continue; + } + if (testingFile.isDirectory()) + { + continue; + } + if (testingFile.getName().toLowerCase().endsWith(".jar")) + { + continue; + } + JarFile jarFile = new JarFile(testingFile); + Enumeration jarEntries = jarFile.entries(); + while (jarEntries.hasMoreElements()) + { + JarEntry entry = jarEntries.nextElement(); + if (entry.isDirectory()) continue; + if (!entry.getName().toLowerCase().endsWith(".jar")) continue; + FileObject innetJarFile = fsManager.resolveFile("jar:" + entry.getName()); + innerJarFiles.add(innetJarFile); + } + } + VFSClassLoader cl = new VFSClassLoader(innerJarFiles.toArray(new FileObject[innerJarFiles.size()]), fsManager); + return cl; + } + catch (Exception e1) + { + throw new RuntimeException(e1); + } + } + + /** Registry where we register loaded plugins */ + @InjectedBean + private PluginRegistry pluginRegistry; + + /** Service to convert IGraph to XML content (and XML to IGraph of course) */ + @InjectedBean + private IFilePersistenceService filePersistenceService; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/PluginRegistry.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/PluginRegistry.java new file mode 100644 index 0000000..9f5d492 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/PluginRegistry.java @@ -0,0 +1,48 @@ +package com.horstmann.violet.framework.plugin; + +import java.util.ArrayList; +import java.util.List; + +import com.horstmann.violet.framework.injection.bean.ManiocFramework.ManagedBean; + +/** + * Plugin registry + * + * @author Alexandre de Pellegrin + * + */ +@ManagedBean +public class PluginRegistry +{ + + + /** + * Private constructor + */ + public PluginRegistry() + { + // Singleton + } + + /** + * Registers a new diagram plugin + * + * @param newDiagramPlugin + */ + public void register(IDiagramPlugin newDiagramPlugin) + { + this.diagramPlugins.add(newDiagramPlugin); + } + + /** + * @return diagram plugin list + */ + public List getDiagramPlugins() { + return this.diagramPlugins; + } + + /** diagram plugins */ + private List diagramPlugins = new ArrayList(); + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/extensionpoint/Violet016FileFilterExtensionPoint.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/extensionpoint/Violet016FileFilterExtensionPoint.java new file mode 100644 index 0000000..bc06e62 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/plugin/extensionpoint/Violet016FileFilterExtensionPoint.java @@ -0,0 +1,24 @@ +package com.horstmann.violet.framework.plugin.extensionpoint; + +import java.util.Map; + +/** + * Plugin extension point. By implementing this interfaces, this diagram plugin will + * keep compatibility with the old Violet 0.16 release. + * + * @author Alexandre de Pellegrin + * + */ +public interface Violet016FileFilterExtensionPoint +{ + /** + * @return mapping to convert Violet 0.16 input/output streams. + * Keys contain Violet 0.16 strings + * Values contain the real string to read the stream. + * So, how does it work? When reading a 0.16 document, we look for all this keys + * and replace them all by their correct value. When saving the document, we replace + * all values by their 0.16 key. + */ + public Map getMappingToKeepViolet016Compatibility(); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintEngine.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintEngine.java new file mode 100644 index 0000000..e13b72b --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintEngine.java @@ -0,0 +1,48 @@ +package com.horstmann.violet.framework.printer; + +import javax.swing.JOptionPane; +import javax.swing.border.EmptyBorder; + +import com.horstmann.violet.framework.dialog.DialogFactory; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.BeanInjector; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.framework.injection.resources.ResourceBundleInjector; +import com.horstmann.violet.framework.injection.resources.annotation.ResourceBundleBean; +import com.horstmann.violet.product.diagram.abstracts.IGraph; + +@ResourceBundleBean(resourceReference = PrintPanel.class) +public class PrintEngine +{ + + public PrintEngine(IGraph graph) + { + BeanInjector.getInjector().inject(this); + ResourceBundleInjector.getInjector().inject(this); + this.graph = graph; + } + + public void start() + { + PrintPanel printPanel = new PrintPanel(this.graph); + JOptionPane optionPane = new JOptionPane(); + optionPane.setOptions(new String[] + { + this.printCloseText + }); + optionPane.setMessage(printPanel); + optionPane.setBorder(new EmptyBorder(0, 0, 10, 0)); + this.dialogFactory.showDialog(optionPane, this.printTitle, true); + } + + @ResourceBundleBean(key = "dialog.print.close.text", resourceReference = PrintPanel.class) + private String printCloseText; + + @ResourceBundleBean(key = "dialog.print.print.text", resourceReference = PrintPanel.class) + private String printTitle; + + private IGraph graph; + + @InjectedBean + private DialogFactory dialogFactory; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintPanel.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintPanel.java new file mode 100644 index 0000000..79b8be1 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintPanel.java @@ -0,0 +1,328 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.printer; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; +import java.awt.print.Book; +import java.awt.print.PageFormat; +import java.awt.print.Printable; +import java.awt.print.PrinterException; +import java.awt.print.PrinterJob; + +import javax.print.attribute.HashPrintRequestAttributeSet; +import javax.print.attribute.PrintRequestAttributeSet; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +import com.horstmann.violet.framework.injection.resources.ResourceBundleInjector; +import com.horstmann.violet.framework.injection.resources.annotation.ResourceBundleBean; +import com.horstmann.violet.framework.swingextension.RolloverButtonUI; +import com.horstmann.violet.framework.theme.ITheme; +import com.horstmann.violet.framework.theme.ThemeManager; +import com.horstmann.violet.product.diagram.abstracts.IGraph; + +/** + * This class implements a dialog for previewing and printing a graph. + */ +public class PrintPanel extends JPanel +{ + /** + * Constructs a print dialog. + * + * @param gr the graph to be printed + */ + public PrintPanel(IGraph gr) + { + ResourceBundleInjector.getInjector().inject(this); + this.graph = gr; + PrinterJob job = PrinterJob.getPrinterJob(); + pageFormat = job.defaultPage(); + attributes = new HashPrintRequestAttributeSet(); + layoutUI(); + } + + /** + * Lays out the UI of the dialog. + */ + public void layoutUI() + { + setLayout(new BorderLayout()); + + JPanel buttonPanel = new JPanel(); + buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS)); + buttonPanel.setBorder(new EmptyBorder(1, 1, 1, 1)); + ITheme cLAF = ThemeManager.getInstance().getTheme(); + buttonPanel.setBackground(cLAF.getRolloverButtonDefaultColor()); + + RolloverButtonUI buttonUI = new RolloverButtonUI(cLAF.getRolloverButtonRolloverColor(), cLAF + .getRolloverButtonRolloverBorderColor(), cLAF.getRolloverButtonDefaultColor()); + + this.moreButton.setUI(buttonUI); + buttonPanel.add(this.moreButton); + this.moreButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + scaleGraph *= Math.sqrt(2); + canvas.repaint(); + } + }); + + this.fewerButton.setUI(buttonUI); + buttonPanel.add(this.fewerButton); + this.fewerButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + scaleGraph /= Math.sqrt(2); + canvas.repaint(); + } + }); + + this.onePageButton.setUI(buttonUI); + buttonPanel.add(this.onePageButton); + this.onePageButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + while (getRows() * getCols() > 1) + scaleGraph /= Math.sqrt(2); + canvas.repaint(); + } + }); + + this.pageSetupButton.setUI(buttonUI); + buttonPanel.add(this.pageSetupButton); + this.pageSetupButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + PrinterJob job = PrinterJob.getPrinterJob(); + PageFormat newPageFormat = job.pageDialog(attributes); + if (newPageFormat != null) pageFormat = newPageFormat; + canvas.repaint(); + } + }); + + this.printButton.setUI(buttonUI); + this.printButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + try + { + PrinterJob job = PrinterJob.getPrinterJob(); + job.setPageable(makeBook()); + if (job.printDialog(attributes)) + { + pageFormat = job.validatePage(pageFormat); + job.print(attributes); + } + } + catch (PrinterException e2) + { + // TODO = display error + } + } + }); + buttonPanel.add(this.printButton); + + add(buttonPanel, BorderLayout.NORTH); + + canvas = new PrintPreviewCanvas(); + JPanel canvasPanel = new JPanel(); + canvasPanel.setBorder(new EmptyBorder(10, 0, 0, 0)); + canvasPanel.setOpaque(false); + canvasPanel.setLayout(new BorderLayout()); + canvasPanel.add(canvas, BorderLayout.CENTER); + + add(canvasPanel, BorderLayout.CENTER); + + } + + /** + * Makes a book consisting of the pages to be printed. + * + * @return the book to be printed + */ + private Book makeBook() + { + Book book = new Book(); + final int pageCount = getRows() * getCols(); + Printable printable = new Printable() + { + public int print(Graphics g, PageFormat pf, int page) throws PrinterException + { + Graphics2D g2 = (Graphics2D) g; + if (page > pageCount) return Printable.NO_SUCH_PAGE; + g2.translate(pf.getImageableX(), pf.getImageableY()); + drawPage(g2, pf, page); + return Printable.PAGE_EXISTS; + } + + public void drawPage(Graphics2D g2, PageFormat pf, int page) + { + int cols = getCols(); + int row = page / cols; + int col = page % cols; + double px = pageFormat.getImageableWidth(); + double py = pageFormat.getImageableHeight(); + g2.clip(new Rectangle2D.Double(0, 0, px, py)); + g2.translate(-col * px, -row * py); + g2.scale((float) scaleGraph, (float) scaleGraph); + g2.translate((float) -bounds.getX(), (float) -bounds.getY()); + g2.setColor(Color.BLACK); + g2.setBackground(Color.WHITE); + graph.draw(g2); + } + }; + + book.append(printable, pageFormat, pageCount); + return book; + } + + /** + * Gets the number of columns currently required for the printout + * + * @return the number of columns (>= 1) + */ + private int getCols() + { + return (int) Math.max(1, Math.ceil(bounds.getWidth() * scaleGraph / pageFormat.getImageableWidth())); + } + + /** + * Gets the number of rows currently required for the printout + * + * @return the number of rows (>= 1) + */ + private int getRows() + { + return (int) Math.max(1, Math.ceil(bounds.getHeight() * scaleGraph / pageFormat.getImageableHeight())); + } + + /** + * The component for displaying the print preview. + */ + class PrintPreviewCanvas extends JComponent + { + public Dimension getPreferredSize() + { + return new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT); + } + + public void paintComponent(Graphics g) + { + Graphics2D g2 = (Graphics2D) g; + bounds = graph.getClipBounds(); + + double xoff; // x offset of page start in window + double yoff; // y offset of page start in window + double scalePagesToCanvas; // scale factor to fit pages in canvas + + double px = pageFormat.getImageableWidth(); + double py = pageFormat.getImageableHeight(); + + int cols = getCols(); + int rows = getRows(); + + double dx = px * getCols(); + double dy = py * getRows(); + + double sx = getWidth() - 1; + double sy = getHeight() - 1; + if (dx / dy < sx / sy) // center horizontally + { + scalePagesToCanvas = sy / dy; + xoff = 0.5 * (sx - scalePagesToCanvas * dx); + yoff = 0; + } + else + // center vertically + { + scalePagesToCanvas = sx / dx; + xoff = 0; + yoff = 0.5 * (sy - scalePagesToCanvas * dy); + } + g2.translate((float) xoff, (float) yoff); + g2.scale((float) scalePagesToCanvas, (float) scalePagesToCanvas); + // draw page backgrounds + Rectangle2D pages = new Rectangle2D.Double(0, 0, px * cols, py * rows); + g2.setPaint(Color.WHITE); + g2.fill(pages); + g2.setPaint(Color.BLACK); + + AffineTransform oldTransform = g2.getTransform(); + + g2.scale((float) scaleGraph, (float) scaleGraph); + g2.translate((float) -bounds.getX(), (float) -bounds.getY()); + graph.draw(g2); + + g2.setTransform(oldTransform); + // draw page outlines (ignoring margins) + g2.setPaint(Color.LIGHT_GRAY); + for (int i = 0; i < cols; i++) + for (int j = 0; j < rows; j++) + { + Rectangle2D page = new Rectangle2D.Double(i * px, j * py, px, py); + g2.draw(page); + } + } + + private static final int DEFAULT_WIDTH = 450; + private static final int DEFAULT_HEIGHT = 300; + } + + private PrintPreviewCanvas canvas; + private PageFormat pageFormat; + private PrintRequestAttributeSet attributes; + private IGraph graph; + private Rectangle2D bounds; + private double scaleGraph = 1; + + @ResourceBundleBean(key = "dialog.print.more") + private JButton moreButton; + + @ResourceBundleBean(key = "dialog.print.fewer") + private JButton fewerButton; + + @ResourceBundleBean(key = "dialog.print.one") + private JButton onePageButton; + + @ResourceBundleBean(key = "dialog.print.page") + private JButton pageSetupButton; + + @ResourceBundleBean(key = "dialog.print.print") + private JButton printButton; + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintPanel.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintPanel.properties new file mode 100644 index 0000000..6ab1697 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintPanel.properties @@ -0,0 +1,12 @@ +dialog.print.more.text=More pages +dialog.print.more.mnemonic=M +dialog.print.fewer.text=Fewer pages +dialog.print.fewer.mnemonic=F +dialog.print.one.text=One page +dialog.print.one.mnemonic=O +dialog.print.page.text=Page setup +dialog.print.page.mnemonic=S +dialog.print.print.text=Print +dialog.print.print.mnemonic=P +dialog.print.close.text=Close +dialog.print.close.mnemonic=C \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintPanel_fr.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintPanel_fr.properties new file mode 100644 index 0000000..2a21075 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/printer/PrintPanel_fr.properties @@ -0,0 +1,12 @@ +dialog.print.more.text=Plus de pages +dialog.print.more.mnemonic=P +dialog.print.fewer.text=Moins de pages +dialog.print.fewer.mnemonic=M +dialog.print.one.text=Une page +dialog.print.one.mnemonic=U +dialog.print.page.text=Configuration +dialog.print.page.mnemonic=C +dialog.print.print.text=Imprimer +dialog.print.print.mnemonic=I +dialog.print.close.text=Fermer +dialog.print.close.mnemonic=F \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/CustomPropertyEditor.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/CustomPropertyEditor.java new file mode 100644 index 0000000..5e12dd7 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/CustomPropertyEditor.java @@ -0,0 +1,413 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.propertyeditor; + +import java.awt.Component; +import java.awt.Font; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyDescriptor; +import java.beans.PropertyEditor; +import java.beans.PropertyEditorManager; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; +import java.util.MissingResourceException; +import java.util.ResourceBundle; +import java.util.Set; + +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTextField; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import com.horstmann.violet.framework.injection.resources.ResourceBundleConstant; +import com.horstmann.violet.framework.propertyeditor.customeditor.AbstractDiagramLinkEditor; +import com.horstmann.violet.framework.propertyeditor.customeditor.ArrowHeadEditor; +import com.horstmann.violet.framework.propertyeditor.customeditor.BentStyleEditor; +import com.horstmann.violet.framework.propertyeditor.customeditor.ChoiceListEditor; +import com.horstmann.violet.framework.propertyeditor.customeditor.ColorEditor; +import com.horstmann.violet.framework.propertyeditor.customeditor.LineStyleEditor; +import com.horstmann.violet.framework.propertyeditor.customeditor.MultiLineStringEditor; +import com.horstmann.violet.framework.propertyeditor.customeditor.StringEditor; +import com.horstmann.violet.framework.util.SerializableEnumeration; +import com.horstmann.violet.product.diagram.abstracts.property.ArrowHead; +import com.horstmann.violet.product.diagram.abstracts.property.BentStyle; +import com.horstmann.violet.product.diagram.abstracts.property.ChoiceList; +import com.horstmann.violet.product.diagram.abstracts.property.LineStyle; +import com.horstmann.violet.product.diagram.abstracts.property.MultiLineString; +import com.horstmann.violet.product.diagram.common.DiagramLink; + +/** + * A component filled with editors for all editable properties of an object. + */ +public class CustomPropertyEditor implements ICustomPropertyEditor +{ + /** + * Constructs a property sheet that shows the editable properties of a given object. + * + * @param object the object whose properties are being edited + * @param parent the parent component + */ + public CustomPropertyEditor(Object bean) + { + panel = new JPanel(); + try + { + Introspector.flushFromCaches(bean.getClass()); + BeanInfo info = Introspector.getBeanInfo(bean.getClass()); + PropertyDescriptor[] descriptors = (PropertyDescriptor[]) info.getPropertyDescriptors().clone(); + Arrays.sort(descriptors, new Comparator() + { + public int compare(PropertyDescriptor d1, PropertyDescriptor d2) + { + Integer p1 = (Integer) d1.getValue("priority"); + Integer p2 = (Integer) d2.getValue("priority"); + if (p1 == null && p2 == null) return 0; + if (p1 == null) return 1; + if (p2 == null) return -1; + return p1.intValue() - p2.intValue(); + } + }); + + panel.setLayout(new CustomPropertyEditorLayout()); + + ResourceBundle rs = ResourceBundle.getBundle(ResourceBundleConstant.NODE_AND_EDGE_STRINGS, Locale.getDefault()); + for (int i = 0; i < descriptors.length; i++) + { + PropertyEditor editor = getEditor(bean, descriptors[i]); + if (editor != null) + { + // Try to extract title from resource bundle + String title = descriptors[i].getName(); + try + { + String translatedTitle = rs.getString(title.toLowerCase()); + if (translatedTitle != null) title = translatedTitle; + } + catch (MissingResourceException e) + { + // Nothing to do + } + + // Upper case the first character + title = title.substring(0, Math.min(1, title.length())).toUpperCase() + + title.substring(Math.min(1, title.length()), title.length()); + + JLabel label = new JLabel(title); + label.setFont(label.getFont().deriveFont(Font.PLAIN)); + panel.add(label); + panel.add(getEditorComponent(editor)); + this.isEditable = true; + } + } + } + catch (IntrospectionException exception) + { + exception.printStackTrace(); + } + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.IPropertyEditor#getAWTComponent() + */ + public JComponent getAWTComponent() + { + return panel; + } + + /** + * Gets the property editor for a given property, and wires it so that it updates the given object. + * + * @param bean the object whose properties are being edited + * @param descriptor the descriptor of the property to be edited + * @return a property editor that edits the property with the given descriptor and updates the given object + */ + private PropertyEditor getEditor(final Object bean, final PropertyDescriptor descriptor) + { + try + { + Method getter = descriptor.getReadMethod(); + if (getter == null) return null; + final Method setter = descriptor.getWriteMethod(); + if (setter == null) return null; + Class type = descriptor.getPropertyType(); + final PropertyEditor editor; + Class editorClass = descriptor.getPropertyEditorClass(); + if (editorClass == null && editors.containsKey(type)) editorClass = (Class) editors.get(type); + if (editorClass != null) editor = (PropertyEditor) editorClass.newInstance(); + else editor = PropertyEditorManager.findEditor(type); + if (editor == null) return null; + + Object value = getter.invoke(bean); + editor.setValue(value); + + if (!isKnownImmutable(type)) + { + try + { + value = value.getClass().getMethod("clone").invoke(value); + } + catch (Throwable t) + { + // we tried + } + } + final Object oldValue = value; + editor.addPropertyChangeListener(new PropertyChangeListener() + { + public void propertyChange(PropertyChangeEvent event) + { + try + { + Object newValue = editor.getValue(); + setter.invoke(bean, newValue); + firePropertyStateChanged(new PropertyChangeEvent(bean, descriptor.getName(), oldValue, newValue)); + } + catch (IllegalAccessException exception) + { + exception.printStackTrace(); + } + catch (InvocationTargetException exception) + { + exception.printStackTrace(); + } + } + }); + return editor; + } + catch (InstantiationException exception) + { + exception.printStackTrace(); + return null; + } + catch (IllegalAccessException exception) + { + exception.printStackTrace(); + return null; + } + catch (InvocationTargetException exception) + { + exception.printStackTrace(); + return null; + } + } + + private static boolean isKnownImmutable(Class type) + { + if (type.isPrimitive()) return true; + if (knownImmutables.contains(type)) return true; + if (SerializableEnumeration.class.isAssignableFrom(type)) return true; + return false; + } + + /** + * Wraps a property editor into a component. + * + * @param editor the editor to wrap + * @return a button (if there is a custom editor), combo box (if the editor has tags), or text field (otherwise) + */ + private Component getEditorComponent(final PropertyEditor editor) + { + String[] tags = editor.getTags(); + String text = editor.getAsText(); + if (editor.supportsCustomEditor()) + { + return editor.getCustomEditor(); + /* + * // Make a button that pops up the custom editor final JButton button = new JButton(); // if the editor is paintable, + * have it paint an icon if (editor.isPaintable()) { button.setIcon(new Icon() { public int getIconWidth() { return + * WIDTH - 8; } public int getIconHeight() { return HEIGHT - 8; } + * + * public void paintIcon(Component c, Graphics g, int x, int y) { g.translate(x, y); Rectangle r = new Rectangle(0, 0, + * getIconWidth(), getIconHeight()); Color oldColor = g.getColor(); g.setColor(Color.BLACK); editor.paintValue(g, r); + * g.setColor(oldColor); g.translate(-x, -y); } }); } else button.setText(buttonText(text)); // pop up custom editor + * when button is clicked button.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) + * { final Component customEditor = editor.getCustomEditor(); + * + * JOptionPane.showMessageDialog(parent, customEditor); // This should really be showInternalMessageDialog, // but then + * you get awful focus behavior with JDK 5.0 // (i.e. the property sheet retains focus). In // particular, the color + * dialog never works. + * + * if (editor.isPaintable()) button.repaint(); else button.setText(buttonText(editor.getAsText())); } }); return button; + */ + } + else if (tags != null) + { + // make a combo box that shows all tags + final JComboBox comboBox = new JComboBox(tags); + comboBox.setSelectedItem(text); + comboBox.addItemListener(new ItemListener() + { + public void itemStateChanged(ItemEvent event) + { + if (event.getStateChange() == ItemEvent.SELECTED) editor.setAsText((String) comboBox.getSelectedItem()); + } + }); + return comboBox; + } + else + { + final JTextField textField = new JTextField(text, 10); + textField.getDocument().addDocumentListener(new DocumentListener() + { + public void insertUpdate(DocumentEvent e) + { + try + { + editor.setAsText(textField.getText()); + } + catch (IllegalArgumentException exception) + { + } + } + + public void removeUpdate(DocumentEvent e) + { + try + { + editor.setAsText(textField.getText()); + } + catch (IllegalArgumentException exception) + { + } + } + + public void changedUpdate(DocumentEvent e) + { + } + }); + return textField; + } + } + + /* + * Formats text for the button that pops up a custom editor. + * + * @param text the property value as text @return the text to put on the button private static String buttonText(String text) { + * if (text == null || text.equals("")) return " "; if (text.length() > MAX_TEXT_LENGTH) return text.substring(0, + * MAX_TEXT_LENGTH) + "..."; return text; } + */ + + /* + * (non-Javadoc) + * + * @see + * com.horstmann.violet.framework.display.clipboard.IPropertyEditor#addPropertyChangeListener(java.beans.PropertyChangeListener) + */ + public void addPropertyChangeListener(PropertyChangeListener listener) + { + synchronized (listeners) + { + listeners.add(listener); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.horstmann.violet.framework.display.clipboard.IPropertyEditor#removePropertyChangeListener(java.beans.PropertyChangeListener + * ) + */ + public void removePropertyChangeListener(PropertyChangeListener listener) + { + synchronized (listeners) + { + listeners.remove(listener); + } + } + + /** + * Notifies all listeners of a state change. + * + * @param event the event to propagate + */ + private void firePropertyStateChanged(PropertyChangeEvent event) + { + synchronized (listeners) + { + for (PropertyChangeListener listener : listeners) + listener.propertyChange(event); + } + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.IPropertyEditor#isEditable() + */ + public boolean isEditable() + { + return this.isEditable; + } + + private ArrayList listeners = new ArrayList(); + + /** + * Flag indicating that the bean has a minimum of one editable property + */ + private boolean isEditable = false; + + private JPanel panel; + + private static Map, Class> editors; + + static + { + editors = new HashMap, Class>(); + editors.put(ArrowHead.class, ArrowHeadEditor.class); + editors.put(BentStyle.class, BentStyleEditor.class); + editors.put(ChoiceList.class, ChoiceListEditor.class); + editors.put(java.awt.Color.class, ColorEditor.class); + editors.put(DiagramLink.class, AbstractDiagramLinkEditor.class); + editors.put(LineStyle.class, LineStyleEditor.class); + editors.put(MultiLineString.class, MultiLineStringEditor.class); + editors.put(String.class, StringEditor.class); + } + + private static Set> knownImmutables = new HashSet>(); + + static + { + knownImmutables.add(String.class); + knownImmutables.add(Integer.class); + knownImmutables.add(Boolean.class); + knownImmutables.add(Double.class); + } +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/CustomPropertyEditorLayout.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/CustomPropertyEditorLayout.java new file mode 100644 index 0000000..1be1159 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/CustomPropertyEditorLayout.java @@ -0,0 +1,99 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.propertyeditor; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.Insets; +import java.awt.LayoutManager; + +/** + * A layout manager that lays out components along a central axis + */ +class CustomPropertyEditorLayout implements LayoutManager +{ + public Dimension preferredLayoutSize(Container parent) + { + Component[] components = parent.getComponents(); + left = 0; + right = 0; + height = 0; + for (int i = 0; i < components.length; i += 2) + { + Component cleft = components[i]; + Component cright = components[i + 1]; + + Dimension dleft = cleft.getPreferredSize(); + Dimension dright = cright.getPreferredSize(); + left = Math.max(left, dleft.width); + right = Math.max(right, dright.width); + height = height + Math.max(dleft.height, dright.height); + } + return new Dimension(left + GAP + right, height); + } + + public Dimension minimumLayoutSize(Container parent) + { + return preferredLayoutSize(parent); + } + + public void layoutContainer(Container parent) + { + preferredLayoutSize(parent); // sets left, right + + Component[] components = parent.getComponents(); + + Insets insets = parent.getInsets(); + int xcenter = insets.left + left; + int y = insets.top; + + for (int i = 0; i < components.length; i += 2) + { + Component cleft = components[i]; + Component cright = components[i + 1]; + + Dimension dleft = cleft.getPreferredSize(); + Dimension dright = cright.getPreferredSize(); + + int height = Math.max(dleft.height, dright.height); + + cleft.setBounds(xcenter - dleft.width, y + (height - dleft.height) / 2, dleft.width, dleft.height); + + cright.setBounds(xcenter + GAP, y + (height - dright.height) / 2, dright.width, dright.height); + y += height; + } + } + + public void addLayoutComponent(String name, Component comp) + { + } + + public void removeLayoutComponent(Component comp) + { + } + + private int left; + private int right; + private int height; + private static final int GAP = 6; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/CustomPropertyEditorSupport.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/CustomPropertyEditorSupport.java new file mode 100644 index 0000000..6e7ff29 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/CustomPropertyEditorSupport.java @@ -0,0 +1,63 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.propertyeditor; + +import java.beans.PropertyEditorSupport; + +/** + * A helper class for showing names of objects in a property sheet that allows the user to pick one of a finite set of named values. + */ +public class CustomPropertyEditorSupport extends PropertyEditorSupport +{ + /** + * Constructs a selector that correlates names and objects. + * + * @param n the strings to display in a combo box + * @param v the corresponding object values + */ + public CustomPropertyEditorSupport(String[] n, Object[] v) + { + names = n; + values = v; + } + + public String[] getTags() + { + return names; + } + + public String getAsText() + { + for (int i = 0; i < values.length; i++) + if (getValue().equals(values[i])) return names[i]; + return null; + } + + public void setAsText(String s) + { + for (int i = 0; i < names.length; i++) + if (s.equals(names[i])) setValue(values[i]); + } + + private String[] names; + private Object[] values; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/ICustomPropertyEditor.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/ICustomPropertyEditor.java new file mode 100644 index 0000000..86d3388 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/ICustomPropertyEditor.java @@ -0,0 +1,31 @@ +package com.horstmann.violet.framework.propertyeditor; + +import java.beans.PropertyChangeListener; + +import javax.swing.JComponent; + +public interface ICustomPropertyEditor +{ + + public abstract JComponent getAWTComponent(); + + /** + * Adds a property change listener to the list of listeners. + * + * @param listener the listener to add + */ + public abstract void addPropertyChangeListener(PropertyChangeListener listener); + + /** + * Adds a property change listener to the list of listeners. + * + * @param listener the listener to add + */ + public abstract void removePropertyChangeListener(PropertyChangeListener listener); + + /** + * @return true if the bean has a minimum of one editable property + */ + public abstract boolean isEditable(); + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/AbstractDiagramLinkEditor.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/AbstractDiagramLinkEditor.java new file mode 100644 index 0000000..52bd599 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/AbstractDiagramLinkEditor.java @@ -0,0 +1,185 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.propertyeditor.customeditor; + +import java.awt.FontMetrics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyEditorSupport; + +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JPanel; + +import com.horstmann.violet.framework.file.GraphFile; +import com.horstmann.violet.framework.file.IFile; +import com.horstmann.violet.framework.file.IGraphFile; +import com.horstmann.violet.framework.file.chooser.IFileChooserService; +import com.horstmann.violet.framework.file.persistence.IFileReader; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.framework.injection.resources.ResourceBundleInjector; +import com.horstmann.violet.product.diagram.common.DiagramLink; +import com.horstmann.violet.workspace.IWorkspace; +import com.horstmann.violet.workspace.Workspace; + +/** + * A PropertyEditor for FileLink objects that lets the user select a file and/or open it. + * + * @author Alexandre de Pellegrin + */ +public abstract class AbstractDiagramLinkEditor extends PropertyEditorSupport +{ + + /** The file chooser used for selecting files */ + protected JFileChooser m_FileChooser; + + /** The panel displayed */ + private JPanel m_Panel; + + /** The file chooser to use with with menu */ + @InjectedBean + private IFileChooserService fileChooserService; + + + + + /** + * Returns a representation of the current property value as java source. + * + * @return a value of type 'String' + */ + public String getJavaInitializationString() + { + + DiagramLink fl = (DiagramLink) getValue(); + if (fl == null) + { + return "null"; + } + return "new File(\"" + fl.getURL().getFile() + "\")"; + } + + /** + * Returns true because we do support a custom editor. + * + * @return true + */ + public boolean supportsCustomEditor() + { + return true; + } + + /** + * Gets the custom editor component. + * + * @return a value of type 'java.awt.Component' + */ + public java.awt.Component getCustomEditor() + { + if (this.m_Panel == null) + { + ResourceBundleInjector.getInjector().inject(this); + JButton chooseButton = new JButton(); + chooseButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + try { + IFileReader fileOpener = fileChooserService.chooseAndGetFileReader(); + IFile selectedFile = fileOpener.getFileDefinition(); + if (selectedFile == null) return; + DiagramLink diagramLink = (DiagramLink) AbstractDiagramLinkEditor.this.getValue(); + diagramLink.setFile(selectedFile); + AbstractDiagramLinkEditor.this.setValue(diagramLink); + firePropertyChange(); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + }); + + //this.getResourceBundle().getString("file.link.open.text") + JButton goButton = new JButton(); + goButton.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + try { + DiagramLink diagramLink = (DiagramLink) AbstractDiagramLinkEditor.this.getValue(); + IFile file = diagramLink.getFile(); + if (file == null) return; + IGraphFile graphFile = new GraphFile(file); + IWorkspace workspace = new Workspace(graphFile); + show(workspace); + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + }); + this.m_Panel = new JPanel(); + this.m_Panel.add(chooseButton); + this.m_Panel.add(goButton); + } + return this.m_Panel; + + } + + /** + * Needs to be implemented to display the workspace created when a file is opened + * @param workspace + */ + public abstract void show(IWorkspace workspace); + + /** + * Returns true since this editor is paintable. + * + * @return true. + */ + public boolean isPaintable() + { + return true; + } + + /** + * Paints a representation of the current Object. + * + * @param gfx the graphics context to use + * @param box the area we are allowed to paint into + */ + public void paintValue(java.awt.Graphics gfx, java.awt.Rectangle box) + { + + FontMetrics fm = gfx.getFontMetrics(); + int vpad = (box.height - fm.getHeight()) / 2; + DiagramLink fl = (DiagramLink) getValue(); + String val = "No file"; + IFile file = fl.getFile(); + if (fl != null && file != null) + { + val = file.getFilename(); + } + gfx.drawString(val, 2, fm.getHeight() + vpad); + } + + + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/ArrowHeadEditor.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/ArrowHeadEditor.java new file mode 100644 index 0000000..64316e1 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/ArrowHeadEditor.java @@ -0,0 +1,60 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.propertyeditor.customeditor; + +import com.horstmann.violet.framework.propertyeditor.CustomPropertyEditorSupport; +import com.horstmann.violet.product.diagram.abstracts.property.ArrowHead; + +/** + * A property editor for the ArrowHead type. + */ +public class ArrowHeadEditor extends CustomPropertyEditorSupport +{ + + /** + * Default constructor + */ + public ArrowHeadEditor() + { + super(names, values); + } + + /** Arrows labels */ + private static final String[] names = + { + "None", + "Triangle", + "V", + "Diamond", + "Black Diamond" + }; + + /** Arrows technical values */ + private static final Object[] values = + { + ArrowHead.NONE, + ArrowHead.TRIANGLE, + ArrowHead.V, + ArrowHead.DIAMOND, + ArrowHead.BLACK_DIAMOND + }; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/BentStyleEditor.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/BentStyleEditor.java new file mode 100644 index 0000000..f128b20 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/BentStyleEditor.java @@ -0,0 +1,63 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.propertyeditor.customeditor; + +import com.horstmann.violet.framework.propertyeditor.CustomPropertyEditorSupport; +import com.horstmann.violet.product.diagram.abstracts.property.BentStyle; + +/** + * A property editor for the BentStyle type. + */ +public class BentStyleEditor extends CustomPropertyEditorSupport +{ + + /** + * Default constructor + * + */ + public BentStyleEditor() + { + super(names, values); + } + + /** bent styme labels */ + private static final String[] names = + { + "Auto", + "Straight", + "HV", + "VH", + "HVH", + "VHV" + }; + + /** bent style technical values */ + private static final Object[] values = + { + BentStyle.AUTO, + BentStyle.STRAIGHT, + BentStyle.HV, + BentStyle.VH, + BentStyle.HVH, + BentStyle.VHV + }; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/ChoiceListEditor.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/ChoiceListEditor.java new file mode 100644 index 0000000..ef761e4 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/ChoiceListEditor.java @@ -0,0 +1,70 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.propertyeditor.customeditor; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.beans.PropertyEditorSupport; + +import javax.swing.JComboBox; + +import com.horstmann.violet.product.diagram.abstracts.property.ChoiceList; + +/** + * A property editor for the ChoiceList type. + * + * @author Alexandre de Pellegrin + */ +public class ChoiceListEditor extends PropertyEditorSupport +{ + + public boolean supportsCustomEditor() + { + return true; + } + + /* + * (non-Javadoc) + * + * @see java.beans.PropertyEditorSupport#getCustomEditor() + */ + public Component getCustomEditor() + { + ChoiceList list = (ChoiceList) getValue(); + final JComboBox comboBox = new JComboBox(list.getList()); + comboBox.setSelectedItem(list.getSelectedItem()); + comboBox.addActionListener(new ActionListener() + { + + public void actionPerformed(ActionEvent e) + { + String selected = (String) comboBox.getSelectedItem(); + ChoiceList list = (ChoiceList) getValue(); + list.setSelectedItem(selected); + } + + }); + return comboBox; + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/ColorEditor.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/ColorEditor.java new file mode 100644 index 0000000..e53c262 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/ColorEditor.java @@ -0,0 +1,291 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.propertyeditor.customeditor; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Rectangle; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.beans.PropertyEditorSupport; +import java.util.Arrays; +import java.util.Comparator; + +import javax.swing.Icon; +import javax.swing.JComboBox; + +/** + * A property editor for the MultiLineString type. + */ +public class ColorEditor extends PropertyEditorSupport +{ + public ColorEditor() + { + combo = new JComboBox(colors); + combo.addItemListener(new ItemListener() + { + public void itemStateChanged(ItemEvent event) + { + Color value = ((ColorIcon) combo.getSelectedItem()).getColor(); + setValue(value); + } + }); + } + + public boolean supportsCustomEditor() + { + return true; + } + + public Component getCustomEditor() + { + final ColorIcon value = new ColorIcon((Color) getValue()); + int i = Arrays.binarySearch(colors, value, new ColorComparator()); + if (i < 0) i = -i - 1; + combo.setSelectedIndex(i); + return combo; + } + + static class ColorIcon implements Icon + { + public ColorIcon(Color color) + { + this.color = color; + } + + public Color getColor() + { + return color; + } + + public int getIconWidth() + { + return WIDTH; + } + + public int getIconHeight() + { + return HEIGHT; + } + + public void paintIcon(Component c, Graphics g, int x, int y) + { + Rectangle r = new Rectangle(x, y, WIDTH - 1, HEIGHT - 1); + Graphics2D g2 = (Graphics2D) g; + Color oldColor = g2.getColor(); + g2.setColor(color); + g2.fill(r); + g2.setColor(Color.BLACK); + g2.draw(r); + g2.setColor(oldColor); + } + + private Color color; + private static final int WIDTH = 40; + private static final int HEIGHT = 15; + } + + private JComboBox combo; + + private static int[] colorValues = + { + // standard web colors + 0xFFFFFF, + 0xFFFF00, + 0xE6E6FA, + 0xADFF2F, + 0x7FFF00, + 0x00FF7F, + 0xFFFAFA, + 0xFFD700, + 0xFFC0CB, + 0x9ACD32, + 0x7CFC00, + 0x00FA9A, + 0xF8F8FF, + 0xFFA500, + 0xFFB6C1, + 0x808000, + 0x32CD32, + 0x00FF00, + 0xF5F5F5, + 0xFF8C00, + 0xFF69B4, + 0x8FBC8F, + 0x228B22, + 0x008000, + 0xFFF5EE, + 0xF4A460, + 0xD8BFD8, + 0x90EE90, + 0x6B8E23, + 0x006400, + 0xFFFAF0, + 0xE9967A, + 0xDDA0DD, + 0x98FB98, + 0x556B2F, + 0x008B8B, + 0xFDF5E6, + 0xFFA07A, + 0xEE82EE, + 0xF0FFF0, + 0x2E8B57, + 0x008080, + 0xFAF0E6, + 0xD2691E, + 0xFF00FF, + 0xF5FFFA, + 0x3CB371, + 0x00CED1, + 0xF5F5DC, + 0xFF4500, + 0xFF1493, + 0xF0FFFF, + 0x66CDAA, + 0x00FFFF, + 0xFFF8DC, + 0xB22222, + 0xC71585, + 0xE0FFFF, + 0x20B2AA, + 0x00BFFF, + 0xFFEFD5, + 0x8B0000, + 0xCD5C5C, + 0xAFEEEE, + 0x48D1CC, + 0x6495ED, + 0xFAEBD7, + 0x800000, + 0xDB7093, + 0xB0C4DE, + 0x40E0D0, + 0x1E90FF, + 0xFFEBCD, + 0xA52A2A, + 0xF08080, + 0xFF00FF, + 0x7FFFD4, + 0x4169E1, + 0xFFE4C4, + 0xA0522D, + 0xFA8072, + 0xDA70D6, + 0xB0E0E6, + 0x6A5ACD, + 0xFFDEAD, + 0x8B4513, + 0xFF7F50, + 0x9370DB, + 0xADD8E6, + 0x7B68EE, + 0xFFE4B5, + 0xB8860B, + 0xFF6347, + 0xBA55D3, + 0x87CEEB, + 0x0000FF, + 0xF5DEB3, + 0xDAA520, + 0xDC143C, + 0x9932CC, + 0x87CEFA, + 0x0000CD, + 0xFFDAB9, + 0xCD853F, + 0x8A2BE2, + 0x4682B4, + 0x483D8B, + 0xEEE8AA, + 0xBDB76B, + 0xFFE4E1, + 0x9400D3, + 0x5F9EA0, + 0x00008B, + 0xFAFAD2, + 0xBC8F8F, + 0xFFF0F5, + 0x8B008B, + 0x778899, + 0x000080, + 0xFFFACD, + 0xDEB887, + 0xDCDCDC, + 0x800080, + 0x708090, + 0x191970, + 0xFFFFE0, + 0xD2B48C, + 0xD3D3D3, + 0xA9A9A9, + 0x696969, + 0x4B0082, + 0xFFFFF0, + 0xF0E68C, + 0xC0C0C0, + 0x808080, + 0x2F4F4F, + 0x000000, + // note color + 0xE6E699 + }; + + static ColorIcon[] colors; + + static + { + colors = new ColorIcon[colorValues.length]; + for (int i = 0; i < colorValues.length; i++) + colors[i] = new ColorIcon(new Color(colorValues[i])); + + Arrays.sort(colors, new ColorComparator()); + } + + static class ColorComparator implements Comparator + { + public int compare(ColorIcon i1, ColorIcon i2) + { + Color c1 = i1.getColor(); + Color c2 = i2.getColor(); + Color.RGBtoHSB(c1.getRed(), c1.getGreen(), c1.getBlue(), hsb); + float hue1 = hsb[0]; + float sat1 = hsb[1]; + float bri1 = hsb[2]; + Color.RGBtoHSB(c2.getRed(), c2.getGreen(), c2.getBlue(), hsb); + float hue2 = hsb[0]; + float sat2 = hsb[1]; + float bri2 = hsb[2]; + if (hue1 < hue2) return 1; + if (hue1 > hue2) return -1; + if (sat1 < sat2) return 1; + if (sat1 > sat2) return -1; + if (bri1 < bri2) return 1; + if (bri1 > bri2) return -1; + return 0; + } + + private static float[] hsb = new float[3]; + } +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/LineStyleEditor.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/LineStyleEditor.java new file mode 100644 index 0000000..1940dd8 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/LineStyleEditor.java @@ -0,0 +1,57 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.propertyeditor.customeditor; + +import com.horstmann.violet.framework.propertyeditor.CustomPropertyEditorSupport; +import com.horstmann.violet.product.diagram.abstracts.property.LineStyle; + +/** + * A property editor for the LineStyle type. + */ +public class LineStyleEditor extends CustomPropertyEditorSupport +{ + /** + * Default constructor + */ + public LineStyleEditor() + { + super(names, values); + } + + /** + * Type names + */ + private static final String[] names = + { + "Solid", + "Dotted" + }; + + /** + * Corresponding objects + */ + private static final Object[] values = + { + LineStyle.SOLID, + LineStyle.DOTTED + }; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/MultiLineStringEditor.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/MultiLineStringEditor.java new file mode 100644 index 0000000..8cfa647 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/MultiLineStringEditor.java @@ -0,0 +1,104 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.propertyeditor.customeditor; + +import java.awt.Component; +import java.awt.KeyboardFocusManager; +import java.beans.PropertyEditorSupport; +import java.util.HashSet; +import java.util.Set; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.KeyStroke; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +import com.horstmann.violet.product.diagram.abstracts.property.MultiLineString; + +/** + * A property editor for the MultiLineString type. + */ +public class MultiLineStringEditor extends PropertyEditorSupport +{ + public boolean supportsCustomEditor() + { + return true; + } + + public Component getCustomEditor() + { + this.source = (MultiLineString) getValue(); + final JPanel panel = new JPanel(); + panel.add(getTextEditorComponent()); + return panel; + } + + private JComponent getTextEditorComponent() + { + if (this.textEditorComponent == null) + { + final JTextArea textArea = new JTextArea(ROWS, COLUMNS); + + textArea.setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, tab); + textArea.setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, shiftTab); + + textArea.setText(source.getText()); + textArea.getDocument().addDocumentListener(new DocumentListener() + { + public void insertUpdate(DocumentEvent e) + { + source.setText(textArea.getText()); + firePropertyChange(); + } + + public void removeUpdate(DocumentEvent e) + { + source.setText(textArea.getText()); + firePropertyChange(); + } + + public void changedUpdate(DocumentEvent e) + { + } + }); + this.textEditorComponent = new JScrollPane(textArea); + } + return this.textEditorComponent; + } + + private MultiLineString source; + private JComponent textEditorComponent; + + private static final int ROWS = 5; + private static final int COLUMNS = 30; + + private static Set tab = new HashSet(1); + private static Set shiftTab = new HashSet(1); + static + { + tab.add(KeyStroke.getKeyStroke("TAB")); + shiftTab.add(KeyStroke.getKeyStroke("shift TAB")); + } +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/StringEditor.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/StringEditor.java new file mode 100644 index 0000000..6b8145a --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/propertyeditor/customeditor/StringEditor.java @@ -0,0 +1,19 @@ +/** + * + */ +package com.horstmann.violet.framework.propertyeditor.customeditor; + +import java.beans.PropertyEditorSupport; + +public class StringEditor extends PropertyEditorSupport +{ + public String getAsText() + { + return (String) getValue(); + } + + public void setAsText(String s) + { + setValue(s); + } +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/CustomToggleButton.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/CustomToggleButton.java new file mode 100644 index 0000000..d2df894 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/CustomToggleButton.java @@ -0,0 +1,180 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.swingextension; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridLayout; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import javax.swing.BoxLayout; +import javax.swing.Icon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; + +import com.horstmann.violet.framework.theme.ThemeManager; + +/** + * This class as the same behavoiuyr of an toggle button but its state (selected or not) directly acts on it graphical rendering. + * + * @author Alexandre de Pellegrin + * + */ +public class CustomToggleButton extends JPanel +{ + + /** + * Default constructor + * + * @param text label text + * @param icon associated icon + * @param selectedColor + * @param selectedBorderColor + * @param unselectedColor + */ + public CustomToggleButton(String text, Icon icon, Color selectedColor, Color selectedBorderColor, Color unselectedColor) + { + this.iconLabel.setIcon(icon); + this.textLabel.setText(text); + this.textLabel.setFont(ThemeManager.getInstance().getTheme().getToggleButtonFont()); + this.iconLabel.setBorder(new EmptyBorder(VGAP, HGAP, VGAP, HGAP)); + this.textLabel.setBorder(new EmptyBorder(VGAP, 0, VGAP, HGAP)); + setToolTipText(text); + + addMouseListener(new MouseAdapter() + { + public void mouseClicked(MouseEvent arg0) + { + isSelected = true; + repaint(); + } + + }); + + setDefaultLayout(); + this.add(this.iconLabel); + this.add(this.textLabel); + setUI(new CustomToggleButtonUI(selectedColor, selectedBorderColor, unselectedColor)); + this.textLabel.setPreferredSize(new Dimension(MAX_TEXT_WIDTH, (int) getBounds().getHeight())); + setDefaultPreferredSize(); + } + + /* (non-Javadoc) + * @see javax.swing.JComponent#setPreferredSize(java.awt.Dimension) + */ + public void setPreferredSize(Dimension p) + { + super.setPreferredSize(p); + this.textLabel.setPreferredSize(new Dimension((int) p.getWidth(), (int) getBounds().getHeight())); + } + + /** + * Sets default preferred size to ensure a constant width for all buttons + */ + private void setDefaultPreferredSize() { + setPreferredSize(new Dimension(MAX_TEXT_WIDTH, (int) getPreferredSize().getHeight())); + } + + /** + * Sets default layout (used when both icon and text are visible) + */ + private void setDefaultLayout() { + this.setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS)); + } + + /** + * Setter for text + * + * @param text + */ + public void setText(String text) + { + this.textLabel.setText(text); + } + + /** + * Setter for icon + * + * @param icon + */ + public void setIcon(Icon icon) + { + this.iconLabel.setIcon(icon); + } + + /** + * @return true if state is 'selected', false if not + */ + public boolean isSelected() + { + return isSelected; + } + + /** + * Setter for state + * + * @param isSelected + */ + public void setSelected(boolean isSelected) + { + this.isSelected = isSelected; + repaint(); + } + + /** + * Shows/Hides button text + * + * @param isVisible + */ + public void setTextVisible(boolean isVisible) { + this.textLabel.setVisible(isVisible); + if (isVisible) { + setDefaultPreferredSize(); + setDefaultLayout(); + } + if (!isVisible) { + super.setPreferredSize(null); + FlowLayout layout = new FlowLayout(FlowLayout.CENTER, 0, 0); + setLayout(layout); + } + } + + /** + * @return true is button's text is visible. + */ + public boolean isTextVisible() { + return this.textLabel.isVisible(); + } + + private boolean isSelected = false; + private JLabel iconLabel = new JLabel();; + private JLabel textLabel = new JLabel(); + + private static final int HGAP = 3; + private static final int VGAP = 1; + private static final int MAX_TEXT_WIDTH = 150; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/CustomToggleButtonUI.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/CustomToggleButtonUI.java new file mode 100644 index 0000000..646121d --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/CustomToggleButtonUI.java @@ -0,0 +1,142 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.swingextension; + +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.plaf.basic.BasicPanelUI; + +/** + * UI for CustomToggleButton + * + * @author Alexandre de Pellegrin + * + */ +public class CustomToggleButtonUI extends BasicPanelUI +{ + + /** + * Default constructor + * + * @param selectedColor + * @param selectedBorderColor + * @param unselectedColor + */ + public CustomToggleButtonUI(Color selectedColor, Color selectedBorderColor, Color unselectedColor) + { + this.unselectedColor = unselectedColor; + this.selectedColor = selectedColor; + this.selectedBorderColor = selectedBorderColor; + } + + /* + * (non-Javadoc) + * + * @see javax.swing.plaf.basic.BasicPanelUI#installDefaults(javax.swing.JPanel) + */ + protected void installDefaults(JPanel p) + { + p.setOpaque(false); + + } + + /* + * (non-Javadoc) + * + * @see javax.swing.plaf.ComponentUI#paint(java.awt.Graphics, javax.swing.JComponent) + */ + public void paint(Graphics g, JComponent c) + { + if (c instanceof CustomToggleButton) + { + boolean isSelected = ((CustomToggleButton) c).isSelected(); + if (isSelected) + { + paintSelectedBg(g, c); + paintSelectedBorder(g, c); + } + if (!isSelected) + { + paintUnselectedBg(g, c); + } + } + super.paint(g, c); + } + + /** + * Paints background for state set as 'selected' + * + * @param g + * @param c + */ + private void paintSelectedBg(Graphics g, JComponent c) + { + + Graphics2D g2 = (Graphics2D) g; + Paint currentPaint = g2.getPaint(); + GradientPaint paint = new GradientPaint(c.getWidth() / 2, -c.getHeight() / 4, selectedColor.brighter().brighter(), c + .getWidth() / 2, c.getHeight() + c.getHeight() / 4, selectedColor); + g2.setPaint(paint); + g2.fillRect(0, 0, c.getWidth(), c.getHeight()); + g2.setPaint(currentPaint); + } + + /** + * Paints background for state set as 'NOT selected' + * + * @param g + * @param c + */ + private void paintUnselectedBg(Graphics g, JComponent c) + { + Graphics2D g2 = (Graphics2D) g; + Color currentColor = g2.getColor(); + g2.setColor(unselectedColor); + g2.fillRect(0, 0, c.getWidth(), c.getHeight()); + g2.setColor(currentColor); + } + + /** + * Paints border for state set as 'selected' + * + * @param g + * @param c + */ + private void paintSelectedBorder(Graphics g, JComponent c) + { + Color currentColor = g.getColor(); + g.setColor(selectedBorderColor); + g.drawRect(0, 0, c.getWidth() - 1, c.getHeight() - 1); + g.setColor(currentColor); + } + + private Color unselectedColor; + private Color selectedColor; + private Color selectedBorderColor; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/IconButtonUI.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/IconButtonUI.java new file mode 100644 index 0000000..52cffb6 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/IconButtonUI.java @@ -0,0 +1,214 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.swingextension; + +import java.awt.Graphics; +import java.awt.Image; +import java.awt.Toolkit; +import java.awt.image.AreaAveragingScaleFilter; +import java.awt.image.BufferedImage; +import java.awt.image.FilteredImageSource; +import java.awt.image.ImageFilter; +import java.awt.image.ImageProducer; +import java.awt.image.RGBImageFilter; +import java.awt.image.ReplicateScaleFilter; + +import javax.swing.AbstractButton; +import javax.swing.ButtonModel; +import javax.swing.Icon; +import javax.swing.ImageIcon; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.IconUIResource; +import javax.swing.plaf.basic.BasicButtonUI; + +public class IconButtonUI extends BasicButtonUI +{ + + public IconButtonUI() + { + super(); + } + + public IconButtonUI(double scalingValue) { + this.scalingValue = scalingValue; + } + + protected void installDefaults(AbstractButton b) + { + super.installDefaults(b); + b.setOpaque(false); + b.setBorderPainted(false); + b.setRolloverEnabled(true); + b.setBorder(new EmptyBorder(0, 0, 0, 0)); + prepareIcons(b); + } + + @Override + public void installUI(JComponent c) + { + AbstractButton b = (AbstractButton) c; + this.originalIcon = b.getIcon(); + super.installUI(c); + } + + @Override + public void uninstallUI(JComponent c) + { + AbstractButton b = (AbstractButton) c; + b.setIcon(this.originalIcon); + super.uninstallUI(c); + } + + public void paint(Graphics g, JComponent c) + { + AbstractButton b = (AbstractButton) c; + ButtonModel model = b.getModel(); + + if (model.isArmed() && model.isPressed()) + { + b.setIcon(darkerIcon); + } + else if (model.isRollover()) + { + b.setIcon(brighterIcon); + } + else + { + b.setIcon(baseIcon); + } + super.paint(g, c); + } + + /** + * Prepares icons to simulate button behaviour + */ + private void prepareIcons(AbstractButton b) + { + baseIcon = b.getIcon(); + + Image baseImage = getImage(baseIcon); + if (this.scalingValue > 0) { + baseImage = getScaledImage(baseImage,(int) (baseIcon.getIconWidth() * this.scalingValue), (int) (baseIcon.getIconHeight() * this.scalingValue)); + baseIcon = getIcon(baseImage); + } + Image brightenImage = changeBrightness(baseImage, true, 30); + Image darkenImage = changeBrightness(baseImage, false, 20); + + brighterIcon = getIcon(brightenImage); + darkerIcon = getIcon(darkenImage); + } + + /** + * Creates an image from an icon. + */ + private Image getImage(Icon icon) + { + if (icon instanceof ImageIcon) + { + return ((ImageIcon) icon).getImage(); + } + else + { + BufferedImage buffer = new BufferedImage(icon.getIconWidth(), icon.getIconHeight(), BufferedImage.TYPE_INT_ARGB); + Graphics g = buffer.getGraphics(); + icon.paintIcon(new JLabel(), g, 0, 0); + g.dispose(); + return buffer; + } + } + + /** + * @param img + * @return an icon from an image + */ + private Icon getIcon(Image img) + { + return new IconUIResource(new ImageIcon(img)); + } + + /** + * @param img to scale + * @param newWidth desired width + * @param newHeight desired height + * @return a scaled image + */ + private Image getScaledImage(Image img, int newWidth, int newHeight) { + ImageFilter filter = new AreaAveragingScaleFilter(newWidth, newHeight); + ImageProducer prod = new FilteredImageSource(img.getSource(), filter); + return Toolkit.getDefaultToolkit().createImage(prod); + } + + /** + * Change the brightness of an image + */ + private Image changeBrightness(Image img, final boolean brighten, final int percent) + { + ImageFilter filter = new RGBImageFilter() + { + + /* + * (non-Javadoc) + * + * @see java.awt.image.RGBImageFilter#filterRGB(int, int, int) + */ + public int filterRGB(int x, int y, int rgb) + { + return (rgb & 0xff000000) | (filter(rgb >> 16) << 16) | (filter(rgb >> 8) << 8) | (filter(rgb)); + } + + /** + * Brighens or darkens a single r/g/b value. + */ + private int filter(int color) + { + color = color & 0xff; + if (brighten) + { + color = (255 - ((255 - color) * (100 - percent) / 100)); + } + else + { + color = (color * (100 - percent) / 100); + } + if (color < 0) color = 0; + if (color > 255) color = 255; + return color; + } + + }; + ImageProducer prod = new FilteredImageSource(img.getSource(), filter); + return Toolkit.getDefaultToolkit().createImage(prod); + } + + private Icon baseIcon; + private Icon darkerIcon; + private Icon brighterIcon; + private Icon originalIcon; + + /** + * Allows to scale icon image + */ + private double scalingValue = -1; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/LinkButtonUI.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/LinkButtonUI.java new file mode 100644 index 0000000..317b83f --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/LinkButtonUI.java @@ -0,0 +1,62 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.swingextension; + +import java.awt.Cursor; +import java.awt.Font; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.AbstractButton; +import javax.swing.ButtonModel; +import javax.swing.plaf.basic.BasicButtonUI; + +public class LinkButtonUI extends BasicButtonUI +{ + + public LinkButtonUI() + { + super(); + } + + protected void installDefaults(AbstractButton b) + { + super.installDefaults(b); + b.setOpaque(false); + b.setBorderPainted(false); + b.setRolloverEnabled(true); + b.setFont(b.getFont().deriveFont(Font.PLAIN)); + } + + protected void paintText(Graphics g, AbstractButton b, Rectangle textRect, String text) + { + super.paintText(g, b, textRect, text); + ButtonModel model = b.getModel(); + if (model.isRollover()) + { + g.drawLine((int) (textRect.getX() + 1), (int) (textRect.getY() + textRect.getHeight() - 1), (int) (textRect.getX() + + textRect.getWidth() - 1), (int) (textRect.getY() + textRect.getHeight() - 1)); + b.setCursor(new Cursor(Cursor.HAND_CURSOR)); + } + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/RolloverButtonUI.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/RolloverButtonUI.java new file mode 100644 index 0000000..4832815 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/RolloverButtonUI.java @@ -0,0 +1,138 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.swingextension; + +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.geom.Rectangle2D; + +import javax.swing.AbstractButton; +import javax.swing.ButtonModel; +import javax.swing.JComponent; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.basic.BasicButtonUI; + +import com.horstmann.violet.framework.theme.ThemeManager; + +public class RolloverButtonUI extends BasicButtonUI +{ + + public RolloverButtonUI(Color rolloverColor, Color rolloverBorderColor, Color defaultColor) + { + super(); + this.defaultColor = defaultColor; + this.rolloverColor = rolloverColor; + this.rolloverBorderColor = rolloverBorderColor; + } + + protected void installDefaults(AbstractButton b) + { + super.installDefaults(b); + b.setOpaque(true); + b.setBorderPainted(false); + b.setRolloverEnabled(true); + b.setFont(b.getFont().deriveFont(Font.PLAIN)); + b.setBorder(new EmptyBorder(VGAP, HGAP, VGAP, HGAP)); + } + + // public Dimension getPreferredSize(JComponent c) + // { + // Dimension preferredSize = super.getPreferredSize(c); + // return new Dimension((int) preferredSize.getWidth() + 2 * HGAP, (int) preferredSize.getHeight() + 2 * VGAP); + // } + + public void paint(Graphics g, JComponent c) + { + AbstractButton b = (AbstractButton) c; + ButtonModel model = b.getModel(); + clearTextShiftOffset(); + + Color borderColor = defaultColor; + Color startColor = defaultColor; + Color endColor = defaultColor; + + if (model.isArmed() && model.isPressed()) + { + borderColor = rolloverBorderColor; + startColor = rolloverColor; + endColor = rolloverColor.brighter().brighter(); + } + else if (model.isRollover()) + { + borderColor = rolloverBorderColor; + startColor = rolloverColor.brighter().brighter(); + endColor = rolloverColor; + } + + paintBackground(g, startColor, endColor, c); + paintBorder(g, borderColor, c); + paintText(g, c); + + } + + private void paintText(Graphics g, JComponent c) + { + AbstractButton b = (AbstractButton) c; + String text = b.getText(); + Color currentColor = g.getColor(); + g.setColor(rolloverBorderColor); + FontMetrics fontMetrics = g.getFontMetrics(); + Rectangle2D stringBounds = fontMetrics.getStringBounds(text, g); + g.setFont(ThemeManager.getInstance().getTheme().getToggleButtonFont()); + g.drawString(text, (int) (c.getWidth() / 2 - stringBounds.getWidth() / 2), (int) (c.getHeight() / 2 + + stringBounds.getHeight() / 2 - 2)); + g.setColor(currentColor); + } + + private void paintBackground(Graphics g, Color startColor, Color endColor, JComponent c) + { + + Graphics2D g2 = (Graphics2D) g; + Paint currentPaint = g2.getPaint(); + GradientPaint paint = new GradientPaint(c.getWidth() / 2, -c.getHeight() * 2, startColor, c.getWidth() / 2, c.getHeight() + + c.getHeight() / 4, endColor); + g2.setPaint(paint); + g2.fillRect(0, 0, c.getWidth(), c.getHeight()); + g2.setPaint(currentPaint); + } + + private void paintBorder(Graphics g, Color borderColor, JComponent c) + { + Color currentColor = g.getColor(); + g.setColor(borderColor); + g.drawRect(0, 0, c.getWidth() - 1, c.getHeight() - 1); + g.setColor(currentColor); + } + + private Color defaultColor; + private Color rolloverColor; + private Color rolloverBorderColor; + + private static final int HGAP = 6; + private static final int VGAP = 2; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/TinyScrollBarUI.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/TinyScrollBarUI.java new file mode 100644 index 0000000..7e1c986 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/swingextension/TinyScrollBarUI.java @@ -0,0 +1,182 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2008 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.swingextension; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Rectangle; + +import javax.swing.JComponent; +import javax.swing.JScrollBar; +import javax.swing.SwingUtilities; +import javax.swing.plaf.basic.BasicScrollBarUI; + +import com.horstmann.violet.framework.theme.ThemeManager; + +/** + * Scroll bar style used for status and tool bars + * + * @author Alexandre de Pellegrin + * + */ +public class TinyScrollBarUI extends BasicScrollBarUI +{ + + /* + * (non-Javadoc) + * + * @see javax.swing.plaf.basic.BasicScrollBarUI#installUI(javax.swing.JComponent) + */ + @Override + public void installUI(JComponent c) + { + super.installUI(c); + scrollbar.setUnitIncrement(DEFAULT_SCROLLUNIT); + } + + /* + * (non-Javadoc) + * + * @see javax.swing.plaf.basic.BasicScrollBarUI#getPreferredSize(javax.swing.JComponent) + */ + public Dimension getPreferredSize(JComponent c) + { + if (scrollbar.getOrientation() == JScrollBar.VERTICAL) + { + return new Dimension(DEFAULT_THICKNESS, scrollbar.getHeight()); + } + else + { + return new Dimension(scrollbar.getWidth(), DEFAULT_THICKNESS); + } + } + + /* + * (non-Javadoc) + * + * @see javax.swing.plaf.basic.BasicScrollBarUI#layoutVScrollbar(javax.swing.JScrollBar) + */ + @Override + protected void layoutVScrollbar(JScrollBar sb) + { + Rectangle vr = new Rectangle(); + SwingUtilities.calculateInnerArea(sb, vr); + decrButton.setBounds(vr.x, vr.y, 0, 0); + incrButton.setBounds(vr.x, vr.height, 0, 0); + decrButton.setPreferredSize(new Dimension(0, 0)); + incrButton.setPreferredSize(new Dimension(0, 0)); + trackRect.setBounds(vr.x, vr.y, vr.width, vr.height); + + int max = sb.getMaximum(); + int min = sb.getMinimum(); + int value = sb.getValue(); + int extent = sb.getVisibleAmount(); + if (max == min) + { + thumbRect.x = trackRect.x; + thumbRect.y = trackRect.y; + thumbRect.width = trackRect.width; + thumbRect.height = getMinimumThumbSize().height; + } + else + { + thumbRect.x = trackRect.x; + thumbRect.y = trackRect.y + value * trackRect.height / (max - min); + thumbRect.width = trackRect.width; + thumbRect.height = extent * trackRect.height / (max - min); + } + } + + /* + * (non-Javadoc) + * + * @see javax.swing.plaf.basic.BasicScrollBarUI#layoutHScrollbar(javax.swing.JScrollBar) + */ + @Override + protected void layoutHScrollbar(JScrollBar sb) + { + Rectangle vr = new Rectangle(); + SwingUtilities.calculateInnerArea(sb, vr); + decrButton.setBounds(vr.x, vr.y, 0, 0); + incrButton.setBounds(vr.width, vr.y, 0, 0); + decrButton.setPreferredSize(new Dimension(0, 0)); + incrButton.setPreferredSize(new Dimension(0, 0)); + trackRect.setBounds(vr.x, vr.y, vr.width, vr.height); + + int max = sb.getMaximum(); + int min = sb.getMinimum(); + int value = sb.getValue(); + int extent = sb.getVisibleAmount(); + if (max == min) + { + thumbRect.x = trackRect.x; + thumbRect.y = trackRect.y; + thumbRect.width = getMinimumThumbSize().width; + thumbRect.height = trackRect.height; + } + else + { + thumbRect.x = trackRect.x + value * trackRect.width / (max - min); + ; + thumbRect.y = trackRect.y; + thumbRect.width = extent * trackRect.width / (max - min); + thumbRect.height = trackRect.height; + } + } + + /* + * (non-Javadoc) + * + * @see javax.swing.plaf.basic.BasicScrollBarUI#paintThumb(java.awt.Graphics, javax.swing.JComponent, java.awt.Rectangle) + */ + @Override + protected void paintThumb(Graphics g, JComponent c, Rectangle thumbBounds) + { + if (thumbBounds.isEmpty() || !scrollbar.isEnabled()) + { + return; + } + + int w = thumbBounds.width; + int h = thumbBounds.height; + + g.translate(thumbBounds.x, thumbBounds.y); + + Color thumbCustomColor = ThemeManager.getInstance().getTheme().getToggleButtonSelectedBorderColor(); + g.setColor(thumbCustomColor); + g.drawRect(0, 0, w, h); + g.fillRect(0, 0, w, h); + + g.translate(-thumbBounds.x, -thumbBounds.y); + } + + /** + * Default scroll unit when using mouse wheel upon this scroll bar + */ + private static final int DEFAULT_SCROLLUNIT = 20; + + /** + * Default bar tickness + */ + private static final int DEFAULT_THICKNESS = 4; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/AbstractTheme.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/AbstractTheme.java new file mode 100644 index 0000000..33b2a2a --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/AbstractTheme.java @@ -0,0 +1,154 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.theme; + +import java.awt.Frame; +import java.awt.Insets; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.SwingUtilities; +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.UnsupportedLookAndFeelException; + +import com.horstmann.violet.framework.injection.bean.ManiocFramework.BeanInjector; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.framework.userpreferences.UserPreferencesService; +import com.l2fprod.common.swing.plaf.LookAndFeelAddons; +import com.l2fprod.common.swing.plaf.windows.WindowsLookAndFeelAddons; + +/** + * Abstract GUI theme with commons operations + * + * @author Alexandre de Pellegrin + * + */ +public abstract class AbstractTheme implements ITheme +{ + + /** + * Configure the look and feel here + */ + protected abstract void configure(); + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.laf.CustomLookAndFeel#activate() + */ + public void activate() + { + configure(); + try + { + String laf = getThemeInfo().getLookAndFeelClass().getName(); + UIManager.setLookAndFeel(laf); + + Frame[] frames = Frame.getFrames(); + for (int i = 0; i < frames.length; i++) + { + SwingUtilities.updateComponentTreeUI(frames[i]); + } + + } + catch (UnsupportedLookAndFeelException e) + { + abortWithError(e); + } + catch (ClassNotFoundException e) + { + abortWithError(e); + } + catch (InstantiationException e) + { + abortWithError(e); + } + catch (IllegalAccessException e) + { + abortWithError(e); + } + updateTaskPaneUI(); + updateTabbedPaneUI(); + } + + /** + * Inits tabbed pane look and feel + */ + private void updateTabbedPaneUI() { + UIManager.put("TabbedPane.contentBorderInsets", new Insets(1, 1, 1, 1)); + } + + /** + * Inits collapsible side panel look and feel + */ + private void updateTaskPaneUI() + { + LookAndFeelAddons addons = new WindowsLookAndFeelAddons(); + LookAndFeelAddons.setAddon(addons); + + UIDefaults defaults = UIManager.getDefaults(); + Map m = new HashMap(); + m.put("TaskPaneGroup.background", getSidebarElementBackgroundColor()); + m.put("TaskPaneGroup.titleBackgroundGradientStart", getSidebarElementTitleBackgroundStartColor()); + m.put("TaskPaneGroup.titleBackgroundGradientEnd", getSidebarElementTitleBackgroundEndColor()); + m.put("TaskPaneGroup.titleForeground", getSidebarElementForegroundColor()); + m.put("TaskPaneGroup.titleOver", getSidebarElementTitleOverColor()); + m.put("TaskPaneGroup.borderColor", getSidebarElementBackgroundColor()); + m.put("TaskPane.useGradient", Boolean.TRUE); + m.put("TaskPane.backgroundGradientStart", getSidebarBackgroundStartColor()); + m.put("TaskPane.backgroundGradientEnd", getSidebarBackgroundEndColor()); + + defaults.putAll(m); + } + + + + /** + * Informs on error and exits + * + * @param e exception raised + */ + private void abortWithError(Exception e) + { + System.err.println("Fatal error when loading look and feel! -> " + this.lafClassName); + e.printStackTrace(); + System.err.println("Stopping..."); + exitWithErrors(); + } + + /** + * Exit with error status ad, before, reset preferences + */ + private void exitWithErrors() + { + BeanInjector.getInjector().inject(this); + this.userPreferencesService.reset(); + System.exit(-1); + } + + private String lafClassName; + + @InjectedBean + private UserPreferencesService userPreferencesService; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/BasicTheme.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/BasicTheme.java new file mode 100644 index 0000000..c71dd61 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/BasicTheme.java @@ -0,0 +1,280 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.theme; + +import java.awt.Color; +import java.awt.Font; + +import javax.swing.JFrame; +import javax.swing.JMenu; +import javax.swing.JMenuBar; +import javax.swing.JProgressBar; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.LookAndFeel; + +/** + * Currently supported JVM theme. This themes fakes swing composants to extract current look and feel theme colors + * + * @author ALexandre de Pellegrin + */ +public class BasicTheme extends AbstractTheme +{ + + /** + * Default constructor + * + * @param className + */ + public BasicTheme(String className) throws ClassNotFoundException + { + this.lookAndFeelClass = (Class) Class.forName(className); + } + + @Override + public ThemeInfo getThemeInfo() { + return new ThemeInfo("Basic", BasicTheme.class, this.lookAndFeelClass); + } + + + @Override + protected void configure() + { + this.defaultGridColor = new Color(220, 220, 220); + + JMenuBar menuBar = new JMenuBar(); + this.basicMenubarBackground = menuBar.getBackground(); + this.basicMenubarForeground = menuBar.getForeground(); + this.basicMenubarFont = menuBar.getFont(); + + JMenu menu = new JMenu(); + this.basicMenuBackground = menu.getBackground(); + this.basicMenuForeground = menu.getForeground(); + this.basicMenuFont = menu.getFont(); + + JTextField textArea = new JTextField(); + this.basicWhite = textArea.getBackground(); + this.basicBlack = textArea.getForeground(); + + JFrame frame = new JFrame(); + this.basicFrameBackground = frame.getBackground(); + frame.dispose(); + + JProgressBar progressBar = new JProgressBar(); + this.basicProgressbarForeground = progressBar.getForeground(); + + JTabbedPane pane = new JTabbedPane(); + this.basicTabbedPaneBackground = pane.getBackground(); + } + + public Color getBlackColor() + { + return this.basicBlack; + } + + public Color getWhiteColor() + { + return this.basicWhite; + } + + public Color getGridColor() + { + return this.defaultGridColor; + } + + public Color getBackgroundColor() + { + return this.basicMenuBackground; + } + + public Font getMenubarFont() + { + return this.basicMenubarFont; + } + + public Color getMenubarBackgroundColor() + { + return this.basicMenubarBackground; + } + + public Color getMenubarForegroundColor() + { + return this.basicMenubarForeground; + } + + public Color getRolloverButtonDefaultColor() + { + return this.basicMenuBackground; + } + + public Color getRolloverButtonRolloverBorderColor() + { + return this.basicMenuForeground; + } + + public Color getRolloverButtonRolloverColor() + { + return this.basicMenuBackground; + } + + public Color getSidebarBackgroundEndColor() + { + return this.basicTabbedPaneBackground; + } + + public Color getSidebarBackgroundStartColor() + { + return this.basicTabbedPaneBackground; + } + + public Color getSidebarBorderColor() + { + return this.basicMenuBackground; + } + + public Color getSidebarElementBackgroundColor() + { + return this.basicMenuBackground; + } + + public Color getSidebarElementTitleBackgroundEndColor() + { + return this.basicMenuBackground; + } + + public Color getSidebarElementTitleBackgroundStartColor() + { + return this.basicMenuBackground.darker(); + } + + public Color getSidebarElementForegroundColor() + { + return this.basicFrameBackground; + } + + public Color getSidebarElementTitleOverColor() + { + return this.basicFrameBackground.brighter(); + } + + public Color getStatusbarBackgroundColor() + { + return this.basicMenuBackground; + } + + public Color getStatusbarBorderColor() + { + return this.basicMenuBackground; + } + + public Font getToggleButtonFont() + { + return this.basicMenuFont.deriveFont(Font.PLAIN); + } + + public Color getToggleButtonSelectedBorderColor() + { + return this.basicTabbedPaneBackground.darker(); + } + + public Color getToggleButtonSelectedColor() + { + return this.basicTabbedPaneBackground; + } + + public Color getToggleButtonUnselectedColor() + { + return this.basicMenuBackground; + } + + public Font getWelcomeSmallFont() + { + return this.basicMenuFont.deriveFont((float) 12.0).deriveFont(Font.PLAIN); + } + + public Font getWelcomeBigFont() + { + return this.basicMenuFont.deriveFont((float) 28.0); + } + + public Color getWelcomeBackgroundEndColor() + { + return this.basicMenuBackground; + } + + public Color getWelcomeBackgroundStartColor() + { + return this.basicMenuBackground.brighter(); + } + + public Color getWelcomeBigForegroundColor() + { + return this.basicProgressbarForeground; + } + + public Color getWelcomeBigRolloverForegroundColor() + { + return getWelcomeBigForegroundColor().darker(); + } + + /** Look and feel class name */ + private Class lookAndFeelClass; + + /** Default grid color */ + private Color defaultGridColor; + + /** Menubar bg color */ + private Color basicMenubarBackground; + + /** Menubar fg color */ + private Color basicMenubarForeground; + + /** Menubar font */ + private Font basicMenubarFont; + + /** Menu bg color */ + private Color basicMenuBackground; + + /** Menu fg color */ + private Color basicMenuForeground; + + /** Menu font */ + private Font basicMenuFont; + + /** Default white */ + private Color basicWhite; + + /** Default black */ + private Color basicBlack; + + /** Frame bg color */ + private Color basicFrameBackground; + + /** Progress bar color */ + private Color basicProgressbarForeground; + + /** Tabbed pane background color */ + private Color basicTabbedPaneBackground; + + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ClassicMetalTheme.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ClassicMetalTheme.java new file mode 100644 index 0000000..c531472 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ClassicMetalTheme.java @@ -0,0 +1,222 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.theme; + +import java.awt.Color; +import java.awt.Font; + +import javax.swing.plaf.metal.DefaultMetalTheme; +import javax.swing.plaf.metal.MetalLookAndFeel; +import javax.swing.plaf.metal.MetalTheme; + +/** + * Java Metal based theme + * + * @author ALexandre de Pellegrin + * + */ +public class ClassicMetalTheme extends AbstractTheme +{ + + /** + * Default constructor + */ + public ClassicMetalTheme() + { + initializeLookAndFeel(); + } + + @Override + public ThemeInfo getThemeInfo() { + return new ThemeInfo("Metal", ClassicMetalTheme.class, MetalLookAndFeel.class); + } + + + /** + * Inits Metal laf + */ + private void initializeLookAndFeel() + { + MetalTheme defaultMetalTheme = new DefaultMetalTheme(); + MetalLookAndFeel.setCurrentTheme(defaultMetalTheme); + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.theme.AbstractTheme#setup() + */ + protected void configure() + { + } + + public Color getBlackColor() + { + return MetalLookAndFeel.getBlack(); + } + + public Color getWhiteColor() + { + return MetalLookAndFeel.getWhite(); + } + + public Color getGridColor() + { + return new Color(220, 220, 220); + } + + public Color getBackgroundColor() + { + return MetalLookAndFeel.getMenuBackground(); + } + + public Font getMenubarFont() + { + return MetalLookAndFeel.getMenuTextFont(); + } + + public Color getMenubarBackgroundColor() + { + return MetalLookAndFeel.getMenuBackground(); + } + + public Color getMenubarForegroundColor() + { + return MetalLookAndFeel.getMenuForeground(); + } + + public Color getRolloverButtonDefaultColor() + { + return MetalLookAndFeel.getMenuBackground(); + } + + public Color getRolloverButtonRolloverBorderColor() + { + return MetalLookAndFeel.getMenuForeground(); + } + + public Color getRolloverButtonRolloverColor() + { + return MetalLookAndFeel.getMenuBackground(); + } + + public Color getSidebarBackgroundEndColor() + { + return MetalLookAndFeel.getMenuSelectedBackground(); + } + + public Color getSidebarBackgroundStartColor() + { + return MetalLookAndFeel.getMenuSelectedBackground(); + } + + public Color getSidebarBorderColor() + { + return MetalLookAndFeel.getMenuBackground(); + } + + public Color getSidebarElementBackgroundColor() + { + return MetalLookAndFeel.getMenuBackground(); + } + + public Color getSidebarElementTitleBackgroundEndColor() + { + return MetalLookAndFeel.getMenuBackground(); + } + + public Color getSidebarElementTitleBackgroundStartColor() + { + return MetalLookAndFeel.getMenuBackground().darker(); + } + + public Color getSidebarElementForegroundColor() + { + return MetalLookAndFeel.getWindowBackground(); + } + + public Color getSidebarElementTitleOverColor() + { + return MetalLookAndFeel.getWindowBackground().brighter(); + } + + public Color getStatusbarBackgroundColor() + { + return MetalLookAndFeel.getMenuBackground(); + } + + public Color getStatusbarBorderColor() + { + return MetalLookAndFeel.getMenuBackground(); + } + + public Font getToggleButtonFont() + { + return MetalLookAndFeel.getMenuTextFont().deriveFont(Font.PLAIN); + } + + public Color getToggleButtonSelectedBorderColor() + { + return MetalLookAndFeel.getMenuSelectedBackground(); + } + + public Color getToggleButtonSelectedColor() + { + return MetalLookAndFeel.getMenuSelectedBackground(); + } + + public Color getToggleButtonUnselectedColor() + { + return MetalLookAndFeel.getMenuBackground(); + } + + public Font getWelcomeSmallFont() + { + return MetalLookAndFeel.getWindowTitleFont().deriveFont((float) 12.0).deriveFont(Font.PLAIN); + } + + public Font getWelcomeBigFont() + { + return MetalLookAndFeel.getWindowTitleFont().deriveFont((float) 28.0); + } + + public Color getWelcomeBackgroundEndColor() + { + return MetalLookAndFeel.getMenuBackground(); + } + + public Color getWelcomeBackgroundStartColor() + { + return MetalLookAndFeel.getMenuBackground().brighter(); + } + + public Color getWelcomeBigForegroundColor() + { + return MetalLookAndFeel.getMenuSelectedBackground(); + } + + public Color getWelcomeBigRolloverForegroundColor() + { + return getWelcomeBigForegroundColor().darker(); + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/DarkAmbianceTheme.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/DarkAmbianceTheme.java new file mode 100644 index 0000000..96d1d00 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/DarkAmbianceTheme.java @@ -0,0 +1,355 @@ +/* +Violet - A program for editing UML diagrams. + +Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.theme; + +import java.awt.Color; +import java.awt.Font; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.UIDefaults; +import javax.swing.UIManager; +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.metal.MetalLookAndFeel; + +import com.pagosoft.plaf.PgsLookAndFeel; +import com.pagosoft.plaf.PgsTheme; +import com.pagosoft.plaf.PlafOptions; + +/** + * Implements Vista Blue theme + * + * @author Alexandre de Pellegrin + * + */ +public class DarkAmbianceTheme extends AbstractTheme +{ + + @Override + public ThemeInfo getThemeInfo() { + return new ThemeInfo("Dark Ambiance", DarkAmbianceTheme.class, PgsLookAndFeel.class); + } + + @Override + protected void configure() + { + UIDefaults defaults = UIManager.getDefaults(); + Map m = new HashMap(); + m.put("MenuItem.background", new Color(255, 255, 255)); + m.put("MenuBar.background", new Color(255, 255, 255)); + defaults.putAll(m); + BlackTheme vistaTheme = new BlackTheme() + { + public ColorUIResource getMenuBackground() + { + return new ColorUIResource(new Color(255, 255, 255)); + } + + public ColorUIResource getSecondary3() + { + return new ColorUIResource(new Color(224, 231, 242)); + } + }; + + PgsLookAndFeel.setCurrentTheme(vistaTheme); + } + + + + + + + + public Color getWhiteColor() + { + return Color.WHITE; + } + + public Color getBlackColor() + { + return Color.BLACK; + } + + public Color getGridColor() + { + return new Color(240, 240, 240); + } + + public Color getBackgroundColor() + { + return new Color(242, 241, 240); + } + + public Font getMenubarFont() + { + return MetalLookAndFeel.getMenuTextFont(); + } + + public Color getMenubarBackgroundColor() + { + return new Color(242, 241, 240); + } + + public Color getMenubarForegroundColor() + { + return Color.WHITE; + } + + public Color getRolloverButtonDefaultColor() + { + return getMenubarBackgroundColor(); + } + + public Color getRolloverButtonRolloverBorderColor() + { + return getMenubarForegroundColor(); + } + + public Color getRolloverButtonRolloverColor() + { + return getMenubarBackgroundColor(); + } + + public Color getSidebarBackgroundEndColor() + { + return new Color(50, 50, 50); + } + + public Color getSidebarBackgroundStartColor() + { + return new Color(90, 90, 90); + } + + public Color getSidebarBorderColor() + { + return getBackgroundColor(); + } + + public Color getSidebarElementBackgroundColor() + { + return getBackgroundColor(); + } + + public Color getSidebarElementTitleBackgroundEndColor() + { + return new Color(110, 110, 110); + } + + public Color getSidebarElementTitleBackgroundStartColor() + { + return new Color(130, 130, 130); + } + + public Color getSidebarElementForegroundColor() + { + return getBackgroundColor(); + } + + public Color getSidebarElementTitleOverColor() + { + return getBackgroundColor().brighter(); + } + + public Color getStatusbarBackgroundColor() + { + return new Color(100, 100, 100); + } + + public Color getStatusbarBorderColor() + { + return getMenubarBackgroundColor(); + } + + public Font getToggleButtonFont() + { + return MetalLookAndFeel.getMenuTextFont().deriveFont(Font.PLAIN); + } + + public Color getToggleButtonSelectedBorderColor() + { + return new Color(247, 154, 24); + } + + public Color getToggleButtonSelectedColor() + { + return new Color(255, 203, 107); + } + + public Color getToggleButtonUnselectedColor() + { + return getSidebarElementBackgroundColor(); + } + + public Font getWelcomeBigFont() + { + return MetalLookAndFeel.getWindowTitleFont().deriveFont((float) 28.0); + } + + public Font getWelcomeSmallFont() + { + return MetalLookAndFeel.getWindowTitleFont().deriveFont((float) 12.0).deriveFont(Font.PLAIN); + } + + public Color getWelcomeBackgroundEndColor() + { + return new Color(120, 120, 120); + } + + public Color getWelcomeBackgroundStartColor() + { + return new Color(150, 150, 150); + } + + public Color getWelcomeBigForegroundColor() + { + return Color.WHITE; + } + + public Color getWelcomeBigRolloverForegroundColor() + { + return new Color(255, 203, 151); + } + + + private class BlackTheme extends PgsTheme + { + public BlackTheme() + { + super("Black"); + + setSecondary3(new ColorUIResource(new Color(224, 231, 242))); + setSecondary2(new ColorUIResource(0xFDFDFD)); + setSecondary1(new ColorUIResource(0x8E8F8F)); + + setPrimary1(new ColorUIResource(0x3c7fb1)); + setPrimary2(new ColorUIResource(0xaadcf8)); + setPrimary3(new ColorUIResource(0xdff2fc)); + + setBlack(new ColorUIResource(Color.BLACK)); + setWhite(new ColorUIResource(Color.WHITE)); + + PlafOptions.setOfficeScrollBarEnabled(true); + PlafOptions.setVistaStyle(true); + PlafOptions.useBoldFonts(false); + + setDefaults(new Object[] + { + "MenuBar.isFlat", + Boolean.FALSE, + "MenuBar.gradientStart", + new ColorUIResource(70, 70, 70), + "MenuBar.gradientMiddle", + new ColorUIResource(70, 70, 70), + "MenuBar.gradientEnd", + new ColorUIResource(70, 70, 70), + + "MenuBarMenu.isFlat", + Boolean.FALSE, + "MenuBarMenu.foreground", + getWhite(), + "MenuBarMenu.rolloverBackground.gradientStart", + new ColorUIResource(130, 130, 130), + "MenuBarMenu.rolloverBackground.gradientMiddle", + new ColorUIResource(140, 140, 140), + "MenuBarMenu.rolloverBackground.gradientEnd", + new ColorUIResource(150, 150, 150), + "MenuBarMenu.selectedBackground.gradientStart", + new ColorUIResource(130, 130, 130), + "MenuBarMenu.selectedBackground.gradientMiddle", + new ColorUIResource(140, 140, 140), + "MenuBarMenu.selectedBackground.gradientEnd", + new ColorUIResource(150, 150, 150), + "MenuBarMenu.rolloverBorderColor", + getPrimary3(), + "MenuBarMenu.selectedBorderColor", + getPrimary3(), + + "Menu.gradientStart", + getPrimary3(), + "Menu.gradientEnd", + getPrimary2(), + "Menu.gradientMiddle", + getPrimary3(), + "Menu.isFlat", + Boolean.FALSE, + + "MenuItem.gradientStart", + getPrimary3(), + "MenuItem.gradientEnd", + getPrimary2(), + "MenuItem.gradientMiddle", + getPrimary3(), + "MenuItem.isFlat", + Boolean.FALSE, + + "CheckBoxMenuItem.gradientStart", + getPrimary3(), + "CheckBoxMenuItem.gradientEnd", + getPrimary2(), + "CheckBoxMenuItem.gradientMiddle", + getPrimary3(), + "CheckBoxMenuItem.isFlat", + Boolean.FALSE, + + "RadioButtonMenuItem.gradientStart", + getPrimary3(), + "RadioButtonMenuItem.gradientEnd", + getPrimary2(), + "RadioButtonMenuItem.gradientMiddle", + getPrimary3(), + "RadioButtonMenuItem.isFlat", + Boolean.FALSE, + + "Button.rolloverGradientStart", + getPrimary3(), + "Button.rolloverGradientEnd", + getPrimary2(), + "Button.selectedGradientStart", + getPrimary3(), + "Button.selectedGradientEnd", + getPrimary1(), + "Button.rolloverVistaStyle", + Boolean.TRUE, + "glow", + getPrimary1(), + + "ToggleButton.rolloverGradientStart", + getPrimary3(), + "ToggleButton.rolloverGradientEnd", + getPrimary2(), + "ToggleButton.selectedGradientStart", + getPrimary3(), + "ToggleButton.selectedGradientEnd", + getPrimary1(), + + "TabbedPane.selected", + new ColorUIResource(253, 236, 178), + "TabbedPane.background", + new ColorUIResource(Color.WHITE), + "TabbedPane.selectedForeground", + new ColorUIResource(Color.BLACK), + + }); + } + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ITheme.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ITheme.java new file mode 100644 index 0000000..3bca21e --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ITheme.java @@ -0,0 +1,108 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.theme; + +import java.awt.Color; +import java.awt.Font; + +/** + * GUI theme. It includes look and feel and colors + * + * @author Alexandre de Pellegrin + * + */ +public interface ITheme +{ + + /** + * Method called to apply look and feel theme + */ + public void activate(); + + public abstract ThemeInfo getThemeInfo(); + + public abstract Color getWhiteColor(); + + public abstract Color getBlackColor(); + + public abstract Color getGridColor(); + + public abstract Color getBackgroundColor(); + + public abstract Font getWelcomeBigFont(); + + public abstract Font getWelcomeSmallFont(); + + public abstract Color getWelcomeBackgroundStartColor(); + + public abstract Color getWelcomeBackgroundEndColor(); + + public abstract Color getWelcomeBigForegroundColor(); + + public abstract Color getWelcomeBigRolloverForegroundColor(); + + public abstract Font getMenubarFont(); + + public abstract Color getMenubarBackgroundColor(); + + public abstract Color getMenubarForegroundColor(); + + public abstract Color getSidebarBackgroundStartColor(); + + public abstract Color getSidebarBackgroundEndColor(); + + public abstract Color getSidebarElementBackgroundColor(); + + public abstract Color getSidebarElementTitleBackgroundStartColor(); + + public abstract Color getSidebarElementTitleBackgroundEndColor(); + + public abstract Color getSidebarElementForegroundColor(); + + public abstract Color getSidebarElementTitleOverColor(); + + public abstract Color getSidebarBorderColor(); + + public abstract Color getStatusbarBackgroundColor(); + + public abstract Color getStatusbarBorderColor(); + + public abstract Color getToggleButtonSelectedColor(); + + public abstract Color getToggleButtonSelectedBorderColor(); + + public abstract Color getToggleButtonUnselectedColor(); + + public abstract Font getToggleButtonFont(); + + public abstract Color getRolloverButtonDefaultColor(); + + public abstract Color getRolloverButtonRolloverColor(); + + public abstract Color getRolloverButtonRolloverBorderColor(); + + public interface WelcomePanelTheme + { + + } + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ThemeInfo.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ThemeInfo.java new file mode 100644 index 0000000..33aa235 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ThemeInfo.java @@ -0,0 +1,31 @@ +package com.horstmann.violet.framework.theme; + +import javax.swing.LookAndFeel; + +public class ThemeInfo { + + private String name; + + private Class themeClass; + + private Class lafClass; + + public ThemeInfo(String name, Class themeClass, Class lafClass) { + this.name = name; + this.themeClass = themeClass; + this.lafClass = lafClass; + } + + public String getName() { + return name; + } + + public Class getThemeClass() { + return themeClass; + } + + public Class getLookAndFeelClass() { + return lafClass; + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ThemeManager.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ThemeManager.java new file mode 100644 index 0000000..f57d37f --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/ThemeManager.java @@ -0,0 +1,173 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.theme; + +import java.util.ArrayList; +import java.util.List; + +import com.horstmann.violet.framework.injection.bean.ManiocFramework.BeanInjector; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.ManagedBean; +import com.horstmann.violet.framework.userpreferences.UserPreferencesService; + +/** + * Manages GUI themes + * + * @author Alexandre de Pellegrin + * + */ +@ManagedBean(registeredManually=true) +public class ThemeManager +{ + + /** + * Private constructor for singleton pattern + */ + public ThemeManager() + { + BeanInjector.getInjector().inject(this); + ThemeManager.setInstance(this); + } + + /** + * Sets unique instance + * + * @param t + */ + private static void setInstance(ThemeManager t) + { + ThemeManager.instance = t; + } + + /** + * @return a single ptivate instance + */ + public static ThemeManager getInstance() + { + return ThemeManager.instance; + } + + /** + * @return installed look and feels infos + */ + public ThemeInfo[] getInstalledThemeInfos() + { + List infos = new ArrayList(); + for (ITheme aTheme : this.installedThemes) + { + infos.add(aTheme.getThemeInfo()); + } + return infos.toArray(new ThemeInfo[infos.size()]); + } + + /** + * Applies prefered theme. Only for standalone mode (not Eclipse plugin mode for example) + * + */ + public void applyPreferedTheme() + { + String className = getPreferedLookAndFeel(); + switchToTheme(className); + } + + public void setPreferedLookAndFeel(String className) + { + this.userPreferencesServices.setPreferedLookAndFeel(className); + } + + public String getPreferedLookAndFeel() + { + return this.userPreferencesServices.getPreferedLookAndFeel(); + } + + private void switchToTheme(String themeClassName) + { + for (ITheme aTheme : this.installedThemes) + { + if (themeClassName.equals(aTheme.getClass().getName())) + { + switchToTheme(aTheme); + return; + } + } + // Default case + switchToTheme(this.installedThemes.get(this.installedThemes.size() -1)); + } + + /** + * Switch to a specific theme + * + * @param aTheme t + */ + public void switchToTheme(ITheme aTheme) + { + currentTheme = aTheme; + currentTheme.activate(); + } + + /** + * @return current theme + */ + public ITheme getTheme() + { + return currentTheme; + } + + /** + * @return installed themes + */ + public List getInstalledThemes() + { + return installedThemes; + } + + /** + * @param installedThemes + */ + public void setInstalledThemes(List installedThemes) + { + this.installedThemes = installedThemes; + } + + public void setUserPreferencesService(UserPreferencesService service) { + this.userPreferencesServices = service; + } + + /** + * Single instance + */ + private static ThemeManager instance; + + /** + * Current graphical theme + */ + private ITheme currentTheme; + + /** + * Installed Themes + */ + private List installedThemes = new ArrayList(); + + @InjectedBean + private UserPreferencesService userPreferencesServices; + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/VistaBlueTheme.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/VistaBlueTheme.java new file mode 100644 index 0000000..dcdc008 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/theme/VistaBlueTheme.java @@ -0,0 +1,219 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.theme; + +import java.awt.Color; +import java.awt.Font; + +import javax.swing.plaf.ColorUIResource; +import javax.swing.plaf.metal.MetalLookAndFeel; + +import com.pagosoft.plaf.PgsLookAndFeel; +import com.pagosoft.plaf.themes.VistaTheme; + +/** + * Implements Vista Blue theme + * + * @author Alexandre de Pellegrin + * + */ +public class VistaBlueTheme extends AbstractTheme +{ + + + @Override + public ThemeInfo getThemeInfo() { + return new ThemeInfo("Vista Blue", VistaBlueTheme.class, PgsLookAndFeel.class); + } + + + @Override + protected void configure() + { + VistaTheme vistaTheme = new VistaTheme() + { + public ColorUIResource getMenuBackground() + { + return new ColorUIResource(new Color(255, 255, 255)); + } + + public ColorUIResource getSecondary3() + { + return new ColorUIResource(new Color(224, 231, 242)); + } + }; + PgsLookAndFeel.setCurrentTheme(vistaTheme); + } + + + + public Color getWhiteColor() + { + return Color.WHITE; + } + + public Color getBlackColor() + { + return Color.BLACK; + } + + public Color getGridColor() + { + return new Color(240, 240, 240); + } + + public Color getBackgroundColor() + { + return new Color(224, 231, 242); + } + + public Font getMenubarFont() + { + return MetalLookAndFeel.getMenuTextFont(); + } + + public Color getMenubarBackgroundColor() + { + return new Color(73, 103, 145); + } + + public Color getMenubarForegroundColor() + { + return Color.WHITE; + } + + public Color getRolloverButtonDefaultColor() + { + return getMenubarBackgroundColor(); + } + + public Color getRolloverButtonRolloverBorderColor() + { + return getMenubarForegroundColor(); + } + + public Color getRolloverButtonRolloverColor() + { + return getMenubarBackgroundColor(); + } + + public Color getSidebarBackgroundEndColor() + { + return new Color(125, 156, 201); + } + + public Color getSidebarBackgroundStartColor() + { + return getMenubarBackgroundColor(); + } + + public Color getSidebarBorderColor() + { + return getGridColor(); + } + + public Color getSidebarElementBackgroundColor() + { + return getBackgroundColor(); + } + + public Color getSidebarElementTitleBackgroundEndColor() + { + return getMenubarBackgroundColor(); + } + + public Color getSidebarElementTitleBackgroundStartColor() + { + return getMenubarBackgroundColor().darker(); + } + + public Color getSidebarElementForegroundColor() + { + return getBackgroundColor(); + } + + public Color getSidebarElementTitleOverColor() + { + return getBackgroundColor().brighter(); + } + + public Color getStatusbarBackgroundColor() + { + return getMenubarBackgroundColor(); + } + + public Color getStatusbarBorderColor() + { + return getMenubarBackgroundColor(); + } + + public Font getToggleButtonFont() + { + return MetalLookAndFeel.getMenuTextFont().deriveFont(Font.PLAIN); + } + + public Color getToggleButtonSelectedBorderColor() + { + return new Color(247, 154, 24); + } + + public Color getToggleButtonSelectedColor() + { + return new Color(255, 203, 107); + } + + public Color getToggleButtonUnselectedColor() + { + return getSidebarElementBackgroundColor(); + } + + public Font getWelcomeBigFont() + { + return MetalLookAndFeel.getWindowTitleFont().deriveFont((float) 28.0); + } + + public Font getWelcomeSmallFont() + { + return MetalLookAndFeel.getWindowTitleFont().deriveFont((float) 12.0).deriveFont(Font.PLAIN); + } + + public Color getWelcomeBackgroundEndColor() + { + return getSidebarBackgroundStartColor(); + } + + public Color getWelcomeBackgroundStartColor() + { + return getSidebarBackgroundEndColor().brighter(); + } + + public Color getWelcomeBigForegroundColor() + { + return Color.WHITE; + } + + public Color getWelcomeBigRolloverForegroundColor() + { + return new Color(255, 203, 151); + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/AppletUserPreferencesDao.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/AppletUserPreferencesDao.java new file mode 100644 index 0000000..68631b7 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/AppletUserPreferencesDao.java @@ -0,0 +1,40 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.userpreferences; + +public class AppletUserPreferencesDao implements IUserPreferencesDao +{ + + public String get(PreferencesConstant key, String defval) + { + return defval; + } + + public void put(PreferencesConstant key, String value) + { + } + + public void reset() + { + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/DefaultUserPreferencesDao.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/DefaultUserPreferencesDao.java new file mode 100644 index 0000000..83526cc --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/DefaultUserPreferencesDao.java @@ -0,0 +1,65 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.userpreferences; + +import java.util.prefs.Preferences; + +import com.horstmann.violet.framework.injection.bean.ManiocFramework.ManagedBean; + +/** + * The default preferences service that uses the java.util.prefs API. + */ +@ManagedBean(registeredManually=true) +public class DefaultUserPreferencesDao implements IUserPreferencesDao +{ + + /** + * Gets an instance of the service. + * + * @return an instance of the service + */ + public DefaultUserPreferencesDao() + { + prefs = Preferences.userNodeForPackage(DefaultUserPreferencesDao.class); + } + + public String get(PreferencesConstant key, String defval) + { + return prefs.get(key.toString(), defval); + } + + public void put(PreferencesConstant key, String defval) + { + prefs.put(key.toString(), defval); + } + + public void reset() + { + for (int i = 0; i < PreferencesConstant.LIST.length; i++) + { + prefs.remove(PreferencesConstant.LIST[i].toString()); + } + } + + private Preferences prefs; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/IUserPreferencesDao.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/IUserPreferencesDao.java new file mode 100644 index 0000000..58b8218 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/IUserPreferencesDao.java @@ -0,0 +1,52 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.userpreferences; + +/** + * Interface for services storing and loading user preferences. + */ +public interface IUserPreferencesDao +{ + + /** + * Gets a previously stored string from the service. + * + * @param key the key of the string + * @param defval the value to return if no matching value was found + * @return the value stored with the given key, or defval if none was found + */ + public abstract String get(PreferencesConstant key, String defval); + + /** + * Saves a key/value pair for later retrieval. + * + * @param key the key of the string to be stored + * @param value the value to to be stored + */ + public abstract void put(PreferencesConstant key, String value); + + /** + * Resets all preferences + */ + public abstract void reset(); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/JNLPUserPreferencesDao.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/JNLPUserPreferencesDao.java new file mode 100644 index 0000000..6331289 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/JNLPUserPreferencesDao.java @@ -0,0 +1,142 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.userpreferences; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import javax.jnlp.BasicService; +import javax.jnlp.FileContents; +import javax.jnlp.PersistenceService; +import javax.jnlp.ServiceManager; +import javax.jnlp.UnavailableServiceException; + +/** + * A preferences service that uses WebStart "muffins". + */ +public class JNLPUserPreferencesDao implements IUserPreferencesDao +{ + + public String get(PreferencesConstant key, String defval) + { + try + { + BasicService basic = (BasicService) ServiceManager.lookup("javax.jnlp.BasicService"); + URL codeBase = basic.getCodeBase(); + + PersistenceService service = (PersistenceService) ServiceManager.lookup("javax.jnlp.PersistenceService"); + URL keyURL = new URL(codeBase, key.toString()); + + FileContents contents = service.get(keyURL); + InputStream in = contents.getInputStream(); + BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8")); + String r = reader.readLine(); + if (r != null) return r; + } + catch (UnavailableServiceException e) + { + e.printStackTrace(); + } + catch (MalformedURLException e) + { + e.printStackTrace(); + } + catch (IOException e) + { + e.printStackTrace(); + } + return defval; + } + + public void put(PreferencesConstant key, String value) + { + try + { + BasicService basic = (BasicService) ServiceManager.lookup("javax.jnlp.BasicService"); + URL codeBase = basic.getCodeBase(); + + PersistenceService service = (PersistenceService) ServiceManager.lookup("javax.jnlp.PersistenceService"); + URL keyURL = new URL(codeBase, key.toString()); + try + { + service.delete(keyURL); + } + catch (Exception ex) + { + } + byte[] bytes = value.getBytes("UTF-8"); + service.create(keyURL, bytes.length); + FileContents contents = service.get(keyURL); + OutputStream out = contents.getOutputStream(true); + out.write(bytes); + out.close(); + } + catch (UnavailableServiceException e) + { + e.printStackTrace(); + } + catch (MalformedURLException e) + { + e.printStackTrace(); + } + catch (IOException e) + { + e.printStackTrace(); + } + } + + public void reset() + { + try + { + BasicService basic = (BasicService) ServiceManager.lookup("javax.jnlp.BasicService"); + URL codeBase = basic.getCodeBase(); + + PersistenceService service = (PersistenceService) ServiceManager.lookup("javax.jnlp.PersistenceService"); + for (int i = 0; i < PreferencesConstant.LIST.length; i++) + { + URL keyURL = new URL(codeBase, PreferencesConstant.LIST[i].toString()); + try + { + service.delete(keyURL); + } + catch (Exception ex) + { + } + } + } + catch (UnavailableServiceException e) + { + e.printStackTrace(); + } + catch (MalformedURLException e) + { + e.printStackTrace(); + } + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/PreferencesConstant.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/PreferencesConstant.java new file mode 100644 index 0000000..e0ff759 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/PreferencesConstant.java @@ -0,0 +1,123 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.userpreferences; + +/** + * Preferences constants. (Pattern to avoid use of string to store preferences) + * + * @author Alexandre de Pellegrin + * + */ +public class PreferencesConstant +{ + + /** + * Default constructor + * + * @param key + */ + private PreferencesConstant(String key) + { + this.key = key; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + public String toString() + { + return this.key; + } + + /** + * Constant key + */ + private String key; + + /** + * Key to store prefered look and feel + */ + public static final PreferencesConstant LOOK_AND_FEEL = new PreferencesConstant("look_and_feel"); + + /** + * Key to store recently opened files + */ + public static final PreferencesConstant RECENT_FILES = new PreferencesConstant("recent"); + + /** + * Key to store files that are currently opened (usefull to restore workspace state on next session + */ + public static final PreferencesConstant OPENED_FILES_ON_WORKSPACE = new PreferencesConstant("opened"); + + /** + * Constant just used as file separator for files + */ + public static final PreferencesConstant FILE_SEPARATOR = new PreferencesConstant("\n"); + + /** + * Constant to separate directory and filename + */ + public static final PreferencesConstant PATH_SEPARATOR = new PreferencesConstant(" -> "); + + /** + * Key to store selected file on workspace + */ + public static final PreferencesConstant ACTIVE_FILE = new PreferencesConstant("active"); + + /** + * Key to store user id for peer-to-peer mode. Only for for host config (in other words, program instance hosting shared + * documents) + */ + public static final PreferencesConstant NETWORK_HOSTCONFIG_USERID = new PreferencesConstant("network.hostconfig.userid"); + + /** + * Key to store user id for peer-to-peer mode. Only for for guest config (in other words, to access to remote documents) + */ + public static final PreferencesConstant NETWORK_GUESTCONFIG_USERID = new PreferencesConstant("network.guestconfig.userid"); + + /** + * Key to store SERVER http url for peer-to-peer mode. Only for for guest config (in other words, to access to remote documents) + */ + public static final PreferencesConstant NETWORK_GUESTCONFIG_HTTP_SERVERURL = new PreferencesConstant( + "network.guestconfig.http.serverurl"); + + + /** + * Preference constants list + */ + public static final PreferencesConstant[] LIST; + + static + { + LIST = new PreferencesConstant[8]; + LIST[0] = LOOK_AND_FEEL; + LIST[1] = RECENT_FILES; + LIST[2] = OPENED_FILES_ON_WORKSPACE; + LIST[3] = ACTIVE_FILE; + LIST[4] = NETWORK_HOSTCONFIG_USERID; + LIST[5] = NETWORK_GUESTCONFIG_USERID; + LIST[6] = NETWORK_GUESTCONFIG_HTTP_SERVERURL; + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/PreferencesServiceFactory.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/PreferencesServiceFactory.java new file mode 100644 index 0000000..4d2b3fd --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/PreferencesServiceFactory.java @@ -0,0 +1,62 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.userpreferences; + +/** + * A service for storing and loading user preferences. This service uses either the standard Java preferences API or the WebStart + * persistence service ("muffins"). + */ +public abstract class PreferencesServiceFactory +{ + /** + * Gets an instance of the service, suitable for the package of the given class. + * + * @return an instance of the service + */ + public static IUserPreferencesDao getInstance() + { + if (service != null) return service; + try + { + service = new DefaultUserPreferencesDao(); + return service; + } + catch (SecurityException exception) + { + // that happens when we run under Web Start + } + try + { + // we load this lazily so that the JAR can load without WebStart + service = (IUserPreferencesDao) Class.forName("com.horstmann.violet.framework.JNLPPreferencesService").newInstance(); + return service; + } + catch (Throwable exception) + { + // that happens when we are an applet + } + + return new AppletUserPreferencesDao(); + } + + private static IUserPreferencesDao service; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/PreferredFile.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/PreferredFile.java new file mode 100644 index 0000000..21d39e4 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/PreferredFile.java @@ -0,0 +1,129 @@ +package com.horstmann.violet.framework.userpreferences; + +import java.io.IOException; + +import com.horstmann.violet.framework.file.IFile; + +/** + * This class allows to wrap a file definition (composed by
+ * a filename and its location which is called directory)
+ * into a string to be stored into user local preferences. + * + * + * @author Alexandre de Pellegrin + * + */ +public class PreferredFile implements IFile +{ + + /** + * Creates a wrapper from a filename and its directory + * + * @param filename + * @param directory + */ + public PreferredFile(String directory, String filename) + { + this.filename = filename; + this.directory = directory; + } + + /** + * Constructs an instance from a generic IFile. Allows to wrap an IFile into a PreferredFile instance + * + * @param aFile + */ + public PreferredFile(IFile aFile) + { + this.filename = aFile.getFilename(); + this.directory = aFile.getDirectory(); + } + + /** + * Creates a wrapper from a string from user preferences + * + * @param userPreferenceString + * @throws IOException if unable to parse input String + */ + public PreferredFile(String userPreferenceString) throws IOException + { + String[] strings = userPreferenceString.split(PreferencesConstant.PATH_SEPARATOR.toString()); + if (strings.length != 2) + { + throw new IOException("Unable to parse file path from user preferences"); + } + this.directory = strings[0]; + this.filename = strings[1]; + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.preference.IFile#getFilename() + */ + public String getFilename() + { + return filename; + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.preference.IFile#getDirectory() + */ + public String getDirectory() + { + return directory; + } + + @Override + public String toString() + { + return this.directory + PreferencesConstant.PATH_SEPARATOR.toString() + this.filename; + } + + + + @Override + public int hashCode() + { + final int prime = 31; + int result = 1; + result = prime * result + ((directory == null) ? 0 : directory.hashCode()); + result = prime * result + ((filename == null) ? 0 : filename.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + PreferredFile other = (PreferredFile) obj; + if (directory == null) + { + if (other.directory != null) return false; + } + else if (!directory.equals(other.directory)) return false; + if (filename == null) + { + if (other.filename != null) return false; + } + else if (!filename.equals(other.filename)) return false; + return true; + } + + + + /** + * The file name + */ + private String filename; + + /** + * Its location + */ + private String directory; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/UserPreferencesService.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/UserPreferencesService.java new file mode 100644 index 0000000..fef40e5 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/userpreferences/UserPreferencesService.java @@ -0,0 +1,221 @@ +package com.horstmann.violet.framework.userpreferences; + +import java.io.IOException; +import java.util.HashSet; +import java.util.Set; + +import com.horstmann.violet.framework.file.IFile; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.ManagedBean; +import com.horstmann.violet.framework.theme.DarkAmbianceTheme; + + +/** + * Manages all user preferences + * + * @author alexandre de pellegrin + * + */ +@ManagedBean +public class UserPreferencesService +{ + + + public void setPreferedLookAndFeel(String className) + { + IUserPreferencesDao pService = PreferencesServiceFactory.getInstance(); + pService.put(PreferencesConstant.LOOK_AND_FEEL, className); + } + + public String getPreferedLookAndFeel() + { + IUserPreferencesDao pService = PreferencesServiceFactory.getInstance(); + String preferedLAF = pService.get(PreferencesConstant.LOOK_AND_FEEL, DarkAmbianceTheme.class.getName()); + return preferedLAF; + } + + + /** + * @return the list of lastest opened files (as path strings) + */ + public Set getRecentFiles() + { + Set recentFiles = new HashSet(); + String recent = this.dao.get(PreferencesConstant.RECENT_FILES, "").trim(); + String[] strings = recent.split(PreferencesConstant.FILE_SEPARATOR.toString()); + for (String anEntry : strings) + { + try + { + PreferredFile aRecentFile = new PreferredFile(anEntry); + recentFiles.add(aRecentFile); + } + catch (IOException e) + { + // Nothing to do, will be automatically deleted on dao.put() action + } + } + updateRecentFileList(recentFiles); + return new HashSet(recentFiles); + } + + + /** + * add recently opened file into user preferences + * + * @param opened file + */ + public void addRecentFile(IFile aFile) + { + PreferredFile newPreferredFile = new PreferredFile(aFile); + Set recentFileList = new HashSet(); + for (IFile file : getRecentFiles()) { + recentFileList.add(new PreferredFile(file)); + } + recentFileList.add(newPreferredFile); + while (recentFileList.size() > DEFAULT_MAX_RECENT_FILES) { + recentFileList.remove(0); + } + updateRecentFileList(recentFileList); + } + + /** + * Update user preferences + * @param recentFiles + */ + private void updateRecentFileList(Set recentFiles) { + StringBuilder result = new StringBuilder(""); + for (IFile aFile : recentFiles) { + PreferredFile aPreferredFile = new PreferredFile(aFile); + result.append(aPreferredFile.toString()).append(PreferencesConstant.FILE_SEPARATOR.toString()); + } + this.dao.put(PreferencesConstant.RECENT_FILES, result.toString()); + } + + /** + * Gets opened files on last session. Used to restore workspace after restart + * + * @return file list + */ + public Set getOpenedFilesDuringLastSession() + { + String list = this.dao.get(PreferencesConstant.OPENED_FILES_ON_WORKSPACE, ""); + String[] strings = list.split(PreferencesConstant.FILE_SEPARATOR.toString()); + Set result = new HashSet(); + for (String anEntry : strings) + { + try { + PreferredFile aFile = new PreferredFile(anEntry); + result.add(aFile); + } catch (IOException a) { + // We should purge list from unparsable entries + } + } + updateOpenedFileList(result); + return new HashSet(result); + } + + /** + * Saves newly opened file path into user preferences + * + * @param path file path (should be relative or absolute) + */ + public void addOpenedFile(IFile aFile) + { + PreferredFile newPreferredFile = new PreferredFile(aFile); + Set openedFileList = new HashSet(); + for (IFile file : getOpenedFilesDuringLastSession()) { + openedFileList.add(new PreferredFile(file)); + } + openedFileList.add(newPreferredFile); + updateOpenedFileList(openedFileList); + } + + + /** + * Removes newly closed file from user preferences + * + * @param path file path (could be relative or absolute) + */ + public void removeOpenedFile(IFile aFile) + { + PreferredFile newPreferredFile = new PreferredFile(aFile); + Set openedFileList = new HashSet(); + for (IFile file : getOpenedFilesDuringLastSession()) { + openedFileList.add(new PreferredFile(file)); + } + openedFileList.remove(newPreferredFile); + updateOpenedFileList(openedFileList); + } + + + /** + * Update user preferences + * @param recentFiles + */ + private void updateOpenedFileList(Set openedFiles) { + StringBuilder result = new StringBuilder(""); + for (PreferredFile aPreferredFile : openedFiles) { + result.append(aPreferredFile.toString()).append(PreferencesConstant.FILE_SEPARATOR.toString()); + } + this.dao.put(PreferencesConstant.OPENED_FILES_ON_WORKSPACE, result.toString()); + } + + + + + + + + /** + * Indicates which diagram is currently focused on workspace and saves it into user preferences + * + * @param path file path (could be relative or absolute) + */ + public void setActiveDiagramFile(IFile aFile) + { + if (aFile != null) + { + PreferredFile preferredFile = new PreferredFile(aFile); + this.dao.put(PreferencesConstant.ACTIVE_FILE, preferredFile.toString()); + } + } + + /** + * Gets from user preferences which diagram was setted as focused + * + * @return file path (could be relative or absolute). Returns null by default. + */ + public IFile getActiveDiagramFile() + { + String entry = this.dao.get(PreferencesConstant.ACTIVE_FILE, ""); + IFile aFile = null; + try + { + aFile = new PreferredFile(entry); + } + catch (IOException e) + { + // TODO : logger needed + } + return aFile; + } + + /** + * Clear user preferences + */ + public void reset() { + this.dao.reset(); + } + + /** + * Allows to store and retrieve preferences + */ + @InjectedBean + private IUserPreferencesDao dao; + + /** + * Recent opened files list capacity + */ + private static final int DEFAULT_MAX_RECENT_FILES = 5; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/BrowserLauncher.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/BrowserLauncher.java new file mode 100644 index 0000000..c6c2c9b --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/BrowserLauncher.java @@ -0,0 +1,80 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.util; + +import java.lang.reflect.Method; + +public class BrowserLauncher +{ + + public static boolean openURL(String url) + { + String osName = System.getProperty("os.name"); + try + { + if (osName.startsWith("Mac OS")) + { + Class fileMgr = Class.forName("com.apple.eio.FileManager"); + Method openURL = fileMgr.getDeclaredMethod("openURL", new Class[] + { + String.class + }); + openURL.invoke(null, new Object[] + { + url + }); + } + else if (osName.startsWith("Windows")) Runtime.getRuntime().exec("rundll32 url.dll,FileProtocolHandler " + url); + else + { // assume Unix or Linux + String[] browsers = + { + "firefox", + "opera", + "konqueror", + "epiphany", + "mozilla", + "netscape" + }; + String browser = null; + for (int count = 0; count < browsers.length && browser == null; count++) + if (Runtime.getRuntime().exec(new String[] + { + "which", + browsers[count] + }).waitFor() == 0) browser = browsers[count]; + if (browser == null) throw new Exception("Could not find web browser"); + else Runtime.getRuntime().exec(new String[] + { + browser, + url + }); + } + return true; + } + catch (Exception e) + { + return false; + } + } + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/ClipboardPipe.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/ClipboardPipe.java new file mode 100644 index 0000000..d3e2289 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/ClipboardPipe.java @@ -0,0 +1,102 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.util; + +import java.awt.Image; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; + +/** + * This class is used to hold an image or a text while on the clipboard. + * + * @author Alexandre de Pellegrin + */ +public class ClipboardPipe implements Transferable +{ + private Image image; + private String text; + + public ClipboardPipe(Image image) + { + this.image = image; + } + + public ClipboardPipe(String text) + { + this.text = text; + } + + // Returns supported flavors + public DataFlavor[] getTransferDataFlavors() + { + if (this.image != null) + { + return new DataFlavor[] + { + DataFlavor.imageFlavor + }; + } + if (this.text != null) + { + return new DataFlavor[] + { + DataFlavor.stringFlavor + }; + } + return new DataFlavor[] { + + }; + } + + // Returns true if flavor is supported + public boolean isDataFlavorSupported(DataFlavor flavor) + { + if (this.image != null) + { + return DataFlavor.imageFlavor.equals(flavor); + } + if (this.text != null) + { + return DataFlavor.stringFlavor.equals(flavor); + } + return false; + } + + // Returns image or text + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException + { + if (DataFlavor.imageFlavor.equals(flavor) && this.image != null) + { + return this.image; + } + else if (DataFlavor.stringFlavor.equals(flavor) && this.text != null) + { + return this.text; + } + else + { + throw new UnsupportedFlavorException(flavor); + } + } +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/GeometryUtils.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/GeometryUtils.java new file mode 100644 index 0000000..7fd076d --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/GeometryUtils.java @@ -0,0 +1,47 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.util; + +import java.awt.geom.Point2D; +import java.awt.geom.RectangularShape; + +/** + * Miscellaneous geometry utilities. + */ +public class GeometryUtils +{ + /** + * Moves this rectangle by a given x- and y-offset + * @param r the rectangle to move + * @param dx the x-offset + * @param dy the y-offset + */ + public static void translate(RectangularShape r, double dx, double dy) + { + r.setFrame(r.getX() + dx, r.getY() + dy, r.getWidth(), r.getHeight()); + } + + public static Point2D getMin(RectangularShape r) + { + return new Point2D.Double(r.getX(), r.getY()); + } +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/GrabberUtils.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/GrabberUtils.java new file mode 100644 index 0000000..961ba18 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/GrabberUtils.java @@ -0,0 +1,29 @@ +package com.horstmann.violet.framework.util; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; + + +public class GrabberUtils +{ + + /** + * Draws a single "grabber", a filled square + * + * @param g2 the graphics context + * @param x the x coordinate of the center of the grabber + * @param y the y coordinate of the center of the grabber + */ + public static void drawGrabber(Graphics2D g2, double x, double y) + { + final int SIZE = 5; + Color oldColor = g2.getColor(); + g2.setColor(GrabberUtils.PURPLE); + g2.fill(new Rectangle2D.Double(x - SIZE / 2, y - SIZE / 2, SIZE, SIZE)); + g2.setColor(oldColor); + } + + private static final Color PURPLE = new Color(0.7f, 0.4f, 0.7f); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/NanoHTTPD.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/NanoHTTPD.java new file mode 100644 index 0000000..ca2b76c --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/NanoHTTPD.java @@ -0,0 +1,698 @@ +package com.horstmann.violet.framework.util; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.ServerSocket; +import java.net.Socket; +import java.net.URLEncoder; +import java.util.Date; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Locale; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.TimeZone; + +/** + * A simple, tiny, nicely embeddable HTTP 1.0 server in Java + * + *

+ * NanoHTTPD version 1.1, Copyright © 2001,2005-2007 Jarno Elonen (elonen@iki.fi, http://iki.fi/elonen/) + * + *

+ * Features + limitations: + *

    + * + *
  • Only one Java file
  • + *
  • Java 1.1 compatible
  • + *
  • Released as open source, Modified BSD licence
  • + *
  • No fixed config files, logging, authorization etc. (Implement yourself if you need them.)
  • + *
  • Supports parameter parsing of GET and POST methods
  • + *
  • Supports both dynamic content and file serving
  • + *
  • Never caches anything
  • + *
  • Doesn't limit bandwidth, request time or simultaneous connections
  • + *
  • Default code serves files and shows all HTTP parameters and headers
  • + *
  • File server supports directory listing, index.html and index.htm
  • + *
  • File server does the 301 redirection trick for directories without '/'
  • + *
  • File server supports simple skipping for files (continue download)
  • + *
  • File server uses current directory as a web root
  • + *
  • File server serves also very long files without memory overhead
  • + *
  • Contains a built-in list of most common mime types
  • + *
  • All header names are converted lowercase so they don't vary between browsers/clients
  • + * + *
+ * + *

+ * Ways to use: + *

    + * + *
  • Run as a standalone app, serves files from current directory and shows requests
  • + *
  • Subclass serve() and embed to your own program
  • + *
  • Call serveFile() from serve() with your own base directory
  • + * + *
+ * + * See the end of the source file for distribution license (Modified BSD licence) + */ +public class NanoHTTPD +{ + // ================================================== + // API parts + // ================================================== + + /** + * Override this to customize the server. + *

+ * + * (By default, this delegates to serveFile() and allows directory listing.) + * + * @parm uri Percent-decoded URI without parameters, for example "/index.cgi" + * @parm method "GET", "POST" etc. + * @parm parms Parsed, percent decoded parameters from URI and, in case of POST, data. + * @parm header Header entries, percent decoded + * @return HTTP response, see class Response for details + */ + public Response serve(String uri, String method, Properties header, Properties parms) + { + // System.out.println(method + " '" + uri + "' "); + + // Enumeration e = header.propertyNames(); + // while (e.hasMoreElements()) + // { + // String value = (String) e.nextElement(); + // System.out.println(" HDR: '" + value + "' = '" + header.getProperty(value) + "'"); + // } + // e = parms.propertyNames(); + // while (e.hasMoreElements()) + // { + // String value = (String) e.nextElement(); + // System.out.println(" PRM: '" + value + "' = '" + parms.getProperty(value) + "'"); + // } + + return serveFile(uri, header, new File("."), true); + } + + /** + * HTTP response. Return one of these from serve(). + */ + public class Response + { + /** + * Default constructor: response = HTTP_OK, data = mime = 'null' + */ + public Response() + { + this.status = HTTP_OK; + } + + /** + * Basic constructor. + */ + public Response(String status, String mimeType, InputStream data) + { + this.status = status; + this.mimeType = mimeType; + this.data = data; + } + + /** + * Convenience method that makes an InputStream out of given text. + */ + public Response(String status, String mimeType, String txt) + { + this.status = status; + this.mimeType = mimeType; + this.data = new ByteArrayInputStream(txt.getBytes()); + } + + /** + * Adds given line to the header. + */ + public void addHeader(String name, String value) + { + header.put(name, value); + } + + /** + * HTTP status code after processing, e.g. "200 OK", HTTP_OK + */ + public String status; + + /** + * MIME type of content, e.g. "text/html" + */ + public String mimeType; + + /** + * Data of the response, may be null. + */ + public InputStream data; + + /** + * Headers for the HTTP response. Use addHeader() to add lines. + */ + public Properties header = new Properties(); + } + + /** + * Some HTTP response status codes + */ + public static final String HTTP_OK = "200 OK", HTTP_REDIRECT = "301 Moved Permanently", HTTP_FORBIDDEN = "403 Forbidden", + HTTP_NOTFOUND = "404 Not Found", HTTP_BADREQUEST = "400 Bad Request", HTTP_INTERNALERROR = "500 Internal Server Error", + HTTP_NOTIMPLEMENTED = "501 Not Implemented"; + + /** + * Common mime types for dynamic content + */ + public static final String MIME_PLAINTEXT = "text/plain", MIME_HTML = "text/html", + MIME_DEFAULT_BINARY = "application/octet-stream"; + + // ================================================== + // Socket & server code + // ================================================== + + /** + * Starts a HTTP server to given port. + *

+ * Throws an IOException if the socket is already in use + */ + public NanoHTTPD(int port) throws IOException + { + myTcpPort = port; + + final ServerSocket ss = new ServerSocket(myTcpPort); + Thread t = new Thread(new Runnable() + { + public void run() + { + try + { + while (true) + new HTTPSession(ss.accept()); + } + catch (IOException ioe) + { + } + } + }); + t.setDaemon(true); + t.start(); + } + + /** + * Starts as a standalone file server and waits for Enter. + */ + public static void main(String[] args) + { + System.out.println("NanoHTTPD 1.1 (C) 2001,2005-2007 Jarno Elonen\n" + "(Command line options: [port] [--licence])\n"); + + // Show licence if requested + int lopt = -1; + for (int i = 0; i < args.length; ++i) + if (args[i].toLowerCase().endsWith("licence")) + { + lopt = i; + System.out.println(LICENCE + "\n"); + } + + // Change port if requested + int port = 80; + if (args.length > 0 && lopt != 0) port = Integer.parseInt(args[0]); + + if (args.length > 1 && args[1].toLowerCase().endsWith("licence")) System.out.println(LICENCE + "\n"); + + NanoHTTPD nh = null; + try + { + nh = new NanoHTTPD(port); + } + catch (IOException ioe) + { + System.err.println("Couldn't start server:\n" + ioe); + System.exit(-1); + } + nh.myFileDir = new File(""); + + System.out.println("Now serving files in port " + port + " from \"" + new File("").getAbsolutePath() + "\""); + System.out.println("Hit Enter to stop.\n"); + + try + { + System.in.read(); + } + catch (Throwable t) + { + } + ; + } + + /** + * Handles one session, i.e. parses the HTTP request and returns the response. + */ + private class HTTPSession implements Runnable + { + public HTTPSession(Socket s) + { + mySocket = s; + Thread t = new Thread(this); + t.setDaemon(true); + t.start(); + } + + public void run() + { + try + { + InputStream is = mySocket.getInputStream(); + if (is == null) return; + BufferedReader in = new BufferedReader(new InputStreamReader(is)); + + // Read the request line + StringTokenizer st = new StringTokenizer(in.readLine()); + if (!st.hasMoreTokens()) sendError(HTTP_BADREQUEST, "BAD REQUEST: Syntax error. Usage: GET /example/file.html"); + + String method = st.nextToken(); + + if (!st.hasMoreTokens()) sendError(HTTP_BADREQUEST, "BAD REQUEST: Missing URI. Usage: GET /example/file.html"); + + String uri = decodePercent(st.nextToken()); + + // Decode parameters from the URI + Properties parms = new Properties(); + int qmi = uri.indexOf('?'); + if (qmi >= 0) + { + decodeParms(uri.substring(qmi + 1), parms); + uri = decodePercent(uri.substring(0, qmi)); + } + + // If there's another token, it's protocol version, + // followed by HTTP headers. Ignore version but parse headers. + // NOTE: this now forces header names uppercase since they are + // case insensitive and vary by client. + Properties header = new Properties(); + if (st.hasMoreTokens()) + { + String line = in.readLine(); + while (line.trim().length() > 0) + { + int p = line.indexOf(':'); + header.put(line.substring(0, p).trim().toLowerCase(), line.substring(p + 1).trim()); + line = in.readLine(); + } + } + + // If the method is POST, there may be parameters + // in data section, too, read it: + if (method.equalsIgnoreCase("POST")) + { + long size = 0x7FFFFFFFFFFFFFFFl; + String contentLength = header.getProperty("content-length"); + if (contentLength != null) + { + try + { + size = Integer.parseInt(contentLength); + } + catch (NumberFormatException ex) + { + } + } + String postLine = ""; + char buf[] = new char[512]; + int read = in.read(buf); + while (read >= 0 && size > 0 && !postLine.endsWith("\r\n")) + { + size -= read; + postLine += String.valueOf(buf, 0, read); + if (size > 0) read = in.read(buf); + } + postLine = postLine.trim(); + decodeParms(postLine, parms); + } + + // Ok, now do the serve() + Response r = serve(uri, method, header, parms); + if (r == null) sendError(HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: Serve() returned a null response."); + else sendResponse(r.status, r.mimeType, r.header, r.data); + + in.close(); + } + catch (IOException ioe) + { + try + { + sendError(HTTP_INTERNALERROR, "SERVER INTERNAL ERROR: IOException: " + ioe.getMessage()); + } + catch (Throwable t) + { + } + } + catch (InterruptedException ie) + { + // Thrown by sendError, ignore and exit the thread. + } + } + + /** + * Decodes the percent encoding scheme.
For example: "an+example%20string" -> "an example string" + */ + private String decodePercent(String str) throws InterruptedException + { + try + { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < str.length(); i++) + { + char c = str.charAt(i); + switch (c) + { + case '+': + sb.append(' '); + break; + case '%': + sb.append((char) Integer.parseInt(str.substring(i + 1, i + 3), 16)); + i += 2; + break; + default: + sb.append(c); + break; + } + } + return new String(sb.toString().getBytes()); + } + catch (Exception e) + { + sendError(HTTP_BADREQUEST, "BAD REQUEST: Bad percent-encoding."); + return null; + } + } + + /** + * Decodes parameters in percent-encoded URI-format ( e.g. "name=Jack%20Daniels&pass=Single%20Malt" ) and adds them to given + * Properties. + */ + private void decodeParms(String parms, Properties p) throws InterruptedException + { + if (parms == null) return; + + StringTokenizer st = new StringTokenizer(parms, "&"); + while (st.hasMoreTokens()) + { + String e = st.nextToken(); + int sep = e.indexOf('='); + if (sep >= 0) p.put(decodePercent(e.substring(0, sep)).trim(), decodePercent(e.substring(sep + 1))); + } + } + + /** + * Returns an error message as a HTTP response and throws InterruptedException to stop furhter request processing. + */ + private void sendError(String status, String msg) throws InterruptedException + { + sendResponse(status, MIME_PLAINTEXT, null, new ByteArrayInputStream(msg.getBytes())); + throw new InterruptedException(); + } + + /** + * Sends given response to the socket. + */ + private void sendResponse(String status, String mime, Properties header, InputStream data) + { + try + { + if (status == null) throw new Error("sendResponse(): Status can't be null."); + + OutputStream out = mySocket.getOutputStream(); + PrintWriter pw = new PrintWriter(out); + pw.print("HTTP/1.0 " + status + " \r\n"); + + if (mime != null) pw.print("Content-Type: " + mime + "\r\n"); + + if (header == null || header.getProperty("Date") == null) pw.print("Date: " + gmtFrmt.format(new Date()) + "\r\n"); + + if (header != null) + { + Enumeration e = header.keys(); + while (e.hasMoreElements()) + { + String key = (String) e.nextElement(); + String value = header.getProperty(key); + pw.print(key + ": " + value + "\r\n"); + } + } + + pw.print("\r\n"); + pw.flush(); + + if (data != null) + { + byte[] buff = new byte[2048]; + while (true) + { + int read = data.read(buff, 0, 2048); + if (read <= 0) break; + out.write(buff, 0, read); + } + } + out.flush(); + out.close(); + if (data != null) data.close(); + } + catch (IOException ioe) + { + // Couldn't write? No can do. + try + { + mySocket.close(); + } + catch (Throwable t) + { + } + } + } + + private Socket mySocket; + }; + + /** + * URL-encodes everything between "/"-characters. Encodes spaces as '%20' instead of '+'. + */ + private String encodeUri(String uri) + { + String newUri = ""; + StringTokenizer st = new StringTokenizer(uri, "/ ", true); + while (st.hasMoreTokens()) + { + String tok = st.nextToken(); + if (tok.equals("/")) newUri += "/"; + else if (tok.equals(" ")) newUri += "%20"; + else + { + // Deprecated Java 1.1 compliant code -> newUri += URLEncoder.encode( tok ); + // For Java 1.4 you'll want to use this instead: + try + { + newUri += URLEncoder.encode(tok, "UTF-8"); + } + catch (UnsupportedEncodingException uee) + { + // Well, we tried... + } + } + } + return newUri; + } + + private int myTcpPort; + File myFileDir; + + // ================================================== + // File server code + // ================================================== + + /** + * Serves file from homeDir and its' subdirectories (only). Uses only URI, ignores all headers and HTTP parameters. + */ + public Response serveFile(String uri, Properties header, File homeDir, boolean allowDirectoryListing) + { + // Make sure we won't die of an exception later + if (!homeDir.isDirectory()) return new Response(HTTP_INTERNALERROR, MIME_PLAINTEXT, + "INTERNAL ERRROR: serveFile(): given homeDir is not a directory."); + + // Remove URL arguments + uri = uri.trim().replace(File.separatorChar, '/'); + if (uri.indexOf('?') >= 0) uri = uri.substring(0, uri.indexOf('?')); + + // Prohibit getting out of current directory + if (uri.startsWith("..") || uri.endsWith("..") || uri.indexOf("../") >= 0) return new Response(HTTP_FORBIDDEN, + MIME_PLAINTEXT, "FORBIDDEN: Won't serve ../ for security reasons."); + + File f = new File(homeDir, uri); + if (!f.exists()) return new Response(HTTP_NOTFOUND, MIME_PLAINTEXT, "Error 404, file not found."); + + // List the directory, if necessary + if (f.isDirectory()) + { + // Browsers get confused without '/' after the + // directory, send a redirect. + if (!uri.endsWith("/")) + { + uri += "/"; + Response r = new Response(HTTP_REDIRECT, MIME_HTML, "Redirected: " + uri + + ""); + r.addHeader("Location", uri); + return r; + } + + // First try index.html and index.htm + if (new File(f, "index.html").exists()) f = new File(homeDir, uri + "/index.html"); + else if (new File(f, "index.htm").exists()) f = new File(homeDir, uri + "/index.htm"); + + // No index file, list the directory + else if (allowDirectoryListing) + { + String[] files = f.list(); + String msg = "

Directory " + uri + "


"; + + if (uri.length() > 1) + { + String u = uri.substring(0, uri.length() - 1); + int slash = u.lastIndexOf('/'); + if (slash >= 0 && slash < u.length()) msg += "..
"; + } + + for (int i = 0; i < files.length; ++i) + { + File curFile = new File(f, files[i]); + boolean dir = curFile.isDirectory(); + if (dir) + { + msg += ""; + files[i] += "/"; + } + + msg += "" + files[i] + ""; + + // Show file size + if (curFile.isFile()) + { + long len = curFile.length(); + msg += "  ("; + if (len < 1024) msg += curFile.length() + " bytes"; + else if (len < 1024 * 1024) msg += curFile.length() / 1024 + "." + (curFile.length() % 1024 / 10 % 100) + + " KB"; + else msg += curFile.length() / (1024 * 1024) + "." + curFile.length() % (1024 * 1024) / 10 % 100 + " MB"; + + msg += ")"; + } + msg += "
"; + if (dir) msg += "
"; + } + return new Response(HTTP_OK, MIME_HTML, msg); + } + else + { + return new Response(HTTP_FORBIDDEN, MIME_PLAINTEXT, "FORBIDDEN: No directory listing."); + } + } + + try + { + // Get MIME type from file name extension, if possible + String mime = null; + int dot = f.getCanonicalPath().lastIndexOf('.'); + if (dot >= 0) mime = (String) theMimeTypes.get(f.getCanonicalPath().substring(dot + 1).toLowerCase()); + if (mime == null) mime = MIME_DEFAULT_BINARY; + + // Support (simple) skipping: + long startFrom = 0; + String range = header.getProperty("Range"); + if (range != null) + { + if (range.startsWith("bytes=")) + { + range = range.substring("bytes=".length()); + int minus = range.indexOf('-'); + if (minus > 0) range = range.substring(0, minus); + try + { + startFrom = Long.parseLong(range); + } + catch (NumberFormatException nfe) + { + } + } + } + + FileInputStream fis = new FileInputStream(f); + fis.skip(startFrom); + Response r = new Response(HTTP_OK, mime, fis); + r.addHeader("Content-length", "" + (f.length() - startFrom)); + r.addHeader("Content-range", "" + startFrom + "-" + (f.length() - 1) + "/" + f.length()); + return r; + } + catch (IOException ioe) + { + return new Response(HTTP_FORBIDDEN, MIME_PLAINTEXT, "FORBIDDEN: Reading file failed."); + } + } + + /** + * Hashtable mapping (String)FILENAME_EXTENSION -> (String)MIME_TYPE + */ + private static Hashtable theMimeTypes = new Hashtable(); + static + { + StringTokenizer st = new StringTokenizer("htm text/html " + "html text/html " + "txt text/plain " + + "asc text/plain " + "gif image/gif " + "jpg image/jpeg " + "jpeg image/jpeg " + + "png image/png " + "mp3 audio/mpeg " + "m3u audio/mpeg-url " + "pdf application/pdf " + + "doc application/msword " + "ogg application/x-ogg " + "zip application/octet-stream " + + "exe application/octet-stream " + "class application/octet-stream "); + while (st.hasMoreTokens()) + theMimeTypes.put(st.nextToken(), st.nextToken()); + } + + /** + * GMT date formatter + */ + private static java.text.SimpleDateFormat gmtFrmt; + static + { + gmtFrmt = new java.text.SimpleDateFormat("E, d MMM yyyy HH:mm:ss 'GMT'", Locale.US); + gmtFrmt.setTimeZone(TimeZone.getTimeZone("GMT")); + } + + /** + * The distribution licence + */ + private static final String LICENCE = "Copyright (C) 2001,2005 by Jarno Elonen \n" + "\n" + + "Redistribution and use in source and binary forms, with or without\n" + + "modification, are permitted provided that the following conditions\n" + "are met:\n" + "\n" + + "Redistributions of source code must retain the above copyright notice,\n" + + "this list of conditions and the following disclaimer. Redistributions in\n" + + "binary form must reproduce the above copyright notice, this list of\n" + + "conditions and the following disclaimer in the documentation and/or other\n" + + "materials provided with the distribution. The name of the author may not\n" + + "be used to endorse or promote products derived from this software without\n" + + "specific prior written permission. \n" + " \n" + + "THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\n" + + "IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n" + + "OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\n" + + "IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,\n" + + "INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n" + + "NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,\n" + + "DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY\n" + + "THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT\n" + + "(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\n" + + "OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/PropertyUtils.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/PropertyUtils.java new file mode 100644 index 0000000..a67d944 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/PropertyUtils.java @@ -0,0 +1,79 @@ +package com.horstmann.violet.framework.util; + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +public class PropertyUtils +{ + + public static void copyProperties(Object fromBean, Object toBean) { + try + { + BeanInfo info = Introspector.getBeanInfo(fromBean.getClass()); + PropertyDescriptor[] descriptors = (PropertyDescriptor[]) info.getPropertyDescriptors().clone(); + for (int i = 0; i < descriptors.length; i++) { + PropertyDescriptor prop = descriptors[i]; + Method getter = prop.getReadMethod(); + Method setter = prop.getWriteMethod(); + if (getter != null && setter != null) { + Object value = getter.invoke(fromBean, new Object[] {}); + setter.invoke(toBean, new Object[] {value}); + } + } + } + catch (IntrospectionException e) + { + e.printStackTrace(); + } + catch (IllegalArgumentException e) + { + e.printStackTrace(); + } + catch (IllegalAccessException e) + { + e.printStackTrace(); + } + catch (InvocationTargetException e) + { + e.printStackTrace(); + } + } + + public static void setProperty(Object bean, String propertyName, Object propertyValue) + { + try + { + BeanInfo info = Introspector.getBeanInfo(bean.getClass()); + for (PropertyDescriptor prop : info.getPropertyDescriptors()) + { + if (prop.getName().equals(propertyName)) + { + Method setter = prop.getWriteMethod(); + if (setter != null) + setter.invoke(bean, propertyValue); + return; + } + } + } + catch (IntrospectionException e) + { + e.printStackTrace(); + } + catch (IllegalArgumentException e) + { + e.printStackTrace(); + } + catch (IllegalAccessException e) + { + e.printStackTrace(); + } + catch (InvocationTargetException e) + { + e.printStackTrace(); + } + } +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/SerializableEnumeration.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/SerializableEnumeration.java new file mode 100644 index 0000000..13ca6d3 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/SerializableEnumeration.java @@ -0,0 +1,98 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.util; + +import java.io.ObjectStreamException; +import java.io.Serializable; +import java.lang.reflect.Field; + +import com.thoughtworks.xstream.annotations.XStreamAsAttribute; + +/** + * This class is a superclass for enumerated types that can be serialized. Subclass like this: + * + *
+ *   public class MyEnumeration extends SerializableEnumeration
+ *   {
+ *   private MyEnumeration() {}
+ *   public static final MyEnumeration FOO = new MyEnumeration();
+ *   public static final MyEnumeration BAR = new MyEnumeration();
+ *   . . .
+ *   }
+ * 
+ * + * This defines instances MyEnumeration.FOO and MyEnumeration.BAR that are guaranteed to be preserved + * through serialization and deserialization. Conveniently, the toString method yields the name (such as + * "FOO"). + */ +public class SerializableEnumeration implements Serializable +{ + protected Object writeReplace() throws ObjectStreamException + { + if (name == null) toString(); + if (name == null) throw new ObjectStreamException("No matching field") + { + }; + return this; + } + + public String toString() + { + if (name != null) return getClass().getName() + "." + name; + try + { + Field[] fields = getClass().getFields(); + for (int i = 0; i < fields.length; i++) + { + if (fields[i].get(this) == this) + { + name = fields[i].getName(); + return toString(); + } + } + } + catch (IllegalAccessException ex) + { + } + return super.toString(); + } + + protected Object readResolve() throws ObjectStreamException + { + try + { + return getClass().getField(name).get(null); + } + catch (IllegalAccessException ex) + { + } + catch (NoSuchFieldException ex) + { + } + throw new ObjectStreamException("No matching field") + { + }; + } + + @XStreamAsAttribute + private String name; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/ShortestPathConverter.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/ShortestPathConverter.java new file mode 100644 index 0000000..7099af8 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/ShortestPathConverter.java @@ -0,0 +1,111 @@ +/* + * Projet : + * Package : com.horstmann.violet.framework.util + * Auteur : a.depellegrin + * Cr le : 26 mai 08 + */ +package com.horstmann.violet.framework.util; + +import java.awt.geom.GeneralPath; +import java.awt.geom.PathIterator; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.List; + +public class ShortestPathConverter +{ + + public static GeneralPath getShortestPath(GeneralPath path) + { + + List points = extractPointsFromGeneralPath(path); + List sortedPoints = new ArrayList(); + + while (points.size() > 0) + { + Point2D currentPoint = null; + if (sortedPoints.size() > 0) + { + currentPoint = sortedPoints.get(sortedPoints.size() - 1); + } + else + { + currentPoint = points.get(0); + } + points.remove(currentPoint); + Point2D closestPoint = getClosestPoint(currentPoint, points); + if (closestPoint != null) + { + sortedPoints.add(closestPoint); + currentPoint = closestPoint; + } + } + + GeneralPath newPath = buildPath(sortedPoints); + return newPath; + + } + + private static List extractPointsFromGeneralPath(GeneralPath path) + { + List points = new ArrayList(); + double seg[] = new double[6]; + for (PathIterator i = path.getPathIterator(null); !i.isDone(); i.next()) + { + int segType = i.currentSegment(seg); + switch (segType) + { + case PathIterator.SEG_MOVETO: + points.add(new Point2D.Double(seg[0], seg[1])); + break; + case PathIterator.SEG_LINETO: + points.add(new Point2D.Double(seg[0], seg[1])); + break; + } + } + return points; + } + + private static Point2D getClosestPoint(Point2D refPoint, List pointList) + { + Point2D closestPoint = null; + double shortestDistance = -1; + for (Point2D aPoint : pointList) + { + double distance = refPoint.distance(aPoint); + if (shortestDistance < 0 || distance < shortestDistance) + { + closestPoint = aPoint; + shortestDistance = distance; + } + } + return closestPoint; + } + + private static GeneralPath buildPath(List points) + { + GeneralPath newPath = null; + if (points == null) + { + return new GeneralPath(); + } + if (points.isEmpty()) + { + return new GeneralPath(); + } + for (Point2D aPoint : points) + { + if (newPath == null) + { + newPath = new GeneralPath(); + newPath.moveTo((float) aPoint.getX(), (float) aPoint.getY()); + } + else + { + newPath.lineTo((float) aPoint.getX(), (float) aPoint.getY()); + } + } + return newPath; + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/StringFilterOutputStream.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/StringFilterOutputStream.java new file mode 100644 index 0000000..50e9495 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/StringFilterOutputStream.java @@ -0,0 +1,108 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.util; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +/** + * This class is an outputstream filter. It means that it will filter the given inputstream and replace all the key ocurrences + * issued form the map by its value. + * + * Very important note : due to outputstream behaviour, it can be filtered only after having retreived all data. In other words, + * when close() method is called. So, filtering and writing to outputstream is done during close(). + * + * @author Alexandre de Pellegrin + * + */ +public class StringFilterOutputStream extends ByteArrayOutputStream +{ + + /** + * Default constructor + * + * @param out outputstream to filter + * @param replaceMap pairs of key/value to filter + */ + public StringFilterOutputStream(OutputStream out, Map replaceMap) + { + super(); + this.replaceMap = replaceMap; + this.originalOutputStream = out; + } + + /* + * (non-Javadoc) + * + * @see java.io.ByteArrayOutputStream#close() + */ + public void close() throws IOException + { + String filteredContent = getFilteredContent(this.toString("UTF-8"), this.replaceMap); + try + { + this.originalOutputStream.write(filteredContent.getBytes("UTF-8")); + } + catch (UnsupportedEncodingException ex) + { + ex.printStackTrace(); + this.originalOutputStream.write(filteredContent.getBytes()); + } + this.originalOutputStream.close(); + super.close(); + } + + /** + * Filters a string and replaces all key courrences issed from the map by its valye + * + * @param input string + * @param replaceMap key = searchedString / value = replaceString + * @return filtered string + */ + private String getFilteredContent(String input, Map replaceMap) + { + Set set = replaceMap.keySet(); + for (Iterator iter = set.iterator(); iter.hasNext();) + { + String searchedStr = iter.next(); + String replaceStr = replaceMap.get(searchedStr); + input = input.replaceAll(searchedStr, replaceStr); + } + return input; + } + + /** + * Map containing pairs of key/value as searchedString/replaceString + */ + private Map replaceMap; + + /** + * The outputstream to filter + */ + private OutputStream originalOutputStream; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/UniqueIDGenerator.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/UniqueIDGenerator.java new file mode 100644 index 0000000..57deb84 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/UniqueIDGenerator.java @@ -0,0 +1,11 @@ +package com.horstmann.violet.framework.util; + +import java.util.UUID; + +public class UniqueIDGenerator +{ + public static synchronized String getNewId() { + return UUID.randomUUID().toString(); + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/VersionChecker.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/VersionChecker.java new file mode 100644 index 0000000..ff927e8 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/framework/util/VersionChecker.java @@ -0,0 +1,101 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.framework.util; + +import java.text.MessageFormat; +import java.util.Locale; +import java.util.ResourceBundle; +import java.util.StringTokenizer; + +import javax.swing.JOptionPane; + +import com.horstmann.violet.framework.dialog.DialogFactory; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.ManagedBean; +import com.horstmann.violet.framework.injection.resources.ResourceBundleConstant; + +/** + * Checks if the Java version of the current VM is at least a given version. + */ +@ManagedBean +public class VersionChecker +{ + + /** Minimum compliant Java version */ + private static final String JAVA_VERSION = "1.6"; + + /** Needed to display dialog boxes */ + @InjectedBean + private DialogFactory dialogFactory; + + + /** + * Checks if the current VM has at least the given version, and exits the program with an error dialog if not. + * + */ + public void check() + { + String actualVersion = System.getProperty("java.version"); + boolean versionOk; + try + { + versionOk = versionCompare(actualVersion, JAVA_VERSION) >= 0; + } + catch (NumberFormatException exception) + { + versionOk = false; + } + if (!versionOk) + { + ResourceBundle resources = ResourceBundle.getBundle(ResourceBundleConstant.OTHER_STRINGS, Locale.getDefault()); + String versionError = resources.getString("dialog.error_version.text"); + MessageFormat formatter = new MessageFormat(versionError); + String message = formatter.format(new Object[] + { + JAVA_VERSION + }); + String title = resources.getString("dialog.error_version.title"); + JOptionPane optionPane = new JOptionPane(); + optionPane.setMessage(message); + this.dialogFactory.showDialog(optionPane, title, true); + System.exit(1); + } + } + + public static int versionCompare(String v1, String v2) + { + StringTokenizer t1 = new StringTokenizer(v1, "._"); + StringTokenizer t2 = new StringTokenizer(v2, "._"); + while (t1.hasMoreTokens()) + { + if (!t2.hasMoreTokens()) return 1; + int n1 = Integer.parseInt(t1.nextToken()); + int n2 = Integer.parseInt(t2.nextToken()); + int d = n1 - n2; + if (d != 0) return d; + } + return t2.hasMoreTokens() ? -1 : 0; + } + + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/AbstractGraph.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/AbstractGraph.java new file mode 100644 index 0000000..44bfe9d --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/AbstractGraph.java @@ -0,0 +1,329 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts; + +import java.awt.Graphics2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.product.diagram.abstracts.node.RectangularNode; +import com.horstmann.violet.product.diagram.common.NoteNode; +import com.horstmann.violet.workspace.editorpart.EmptyGrid; +import com.horstmann.violet.workspace.editorpart.IGrid; + +/** + * A graph consisting of selectable nodes and edges. + */ +public abstract class AbstractGraph implements Serializable, Cloneable, IGraph +{ + /** + * Constructs a graph with no nodes or edges. + */ + public AbstractGraph() + { + nodes = new ArrayList(); + edges = new ArrayList(); + } + + @Override + public INode findNode(Point2D p) + { + for (INode n : getAllNodes()) + { + Point2D locationOnGraph = n.getLocationOnGraph(); + Rectangle2D bounds = n.getBounds(); + Rectangle2D boundsToCheck = new Rectangle2D.Double(locationOnGraph.getX(), locationOnGraph.getY(), bounds.getWidth(), + bounds.getHeight()); + if (boundsToCheck.contains(p)) + { + return n; + } + } + return null; + } + + @Override + public INode findNode(Id id) + { + for (INode n : getAllNodes()) + { + if (n.getId().equals(id)) return n; + } + return null; + } + + @Override + public IEdge findEdge(Point2D p) + { + for (IEdge e : edges) + { + if (e.contains(p)) return e; + } + return null; + } + + @Override + public IEdge findEdge(Id id) + { + for (IEdge e : edges) + { + if (e.getId().equals(id)) return e; + } + return null; + } + + @Override + public void draw(Graphics2D g2) + { + List specialNodes = new ArrayList(); + + int count = 0; + int z = 0; + Collection nodes = getAllNodes(); + while (count < nodes.size()) + { + for (INode n : nodes) + { + + if (n.getZ() == z) + { + if (n instanceof NoteNode) + { + specialNodes.add(n); + } + else + { + n.draw(g2); + } + count++; + } + } + z++; + } + + for (int i = 0; i < edges.size(); i++) + { + IEdge e = (IEdge) edges.get(i); + e.draw(g2); + } + // Special nodes are always drawn upon other elements + for (INode n : specialNodes) + { + // Translate g2 if node has parent + INode p = n.getParent(); + Point2D nodeLocationOnGraph = n.getLocationOnGraph(); + Point2D nodeLocation = n.getLocation(); + Point2D g2Location = new Point2D.Double(nodeLocationOnGraph.getX() - nodeLocation.getX(), nodeLocationOnGraph.getY() + - nodeLocation.getY()); + g2.translate(g2Location.getX(), g2Location.getY()); + n.draw(g2); + // Restore g2 original location + g2.translate(-g2Location.getX(), -g2Location.getY()); + } + + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.product.diagram.abstracts.IGraph#getBounds() + */ + public Rectangle2D getClipBounds() + { + Rectangle2D r = minBounds; + for (INode n : nodes) + { + Rectangle2D b = n.getBounds(); + if (r == null) r = b; + else r.add(b); + } + for (IEdge e : edges) + { + r.add(e.getBounds()); + } + return r == null ? new Rectangle2D.Double() : new Rectangle2D.Double(r.getX(), r.getY(), r.getWidth() + + RectangularNode.SHADOW_GAP, r.getHeight() + RectangularNode.SHADOW_GAP); + } + + @Override + public void setBounds(Rectangle2D newValue) + { + minBounds = newValue; + } + + @Override + public abstract List getNodePrototypes(); + + @Override + public abstract List getEdgePrototypes(); + + @Override + public Collection getAllNodes() + { + List fifo = new ArrayList(); + List allNodes = new ArrayList(); + fifo.addAll(nodes); + allNodes.addAll(nodes); + while (!fifo.isEmpty()) + { + INode nodeToInspect = fifo.remove(0); + List children = nodeToInspect.getChildren(); + fifo.addAll(children); + allNodes.addAll(children); + } + // Let's have children first + Collections.reverse(allNodes); + return Collections.unmodifiableCollection(allNodes); + } + + @Override + public Collection getAllEdges() + { + return Collections.unmodifiableCollection(edges); + } + + @Override + public boolean addNode(INode newNode, Point2D p) + { + newNode.setId(new Id()); + newNode.setGraph(this); + // Case 1 : Note node always attached to the graph + if (newNode instanceof NoteNode) + { + newNode.setLocation(p); + nodes.add(newNode); + return true; + } + // Case 2 : attached to an existing node + INode potentialParentNode = findNode(p); + if (potentialParentNode != null) + { + Point2D parentLocationOnGraph = potentialParentNode.getLocationOnGraph(); + Point2D relativeLocation = new Point2D.Double(p.getX() - parentLocationOnGraph.getX(), p.getY() + - parentLocationOnGraph.getY()); + return potentialParentNode.addChild(newNode, relativeLocation); + } + // Case 3 : attached directly to the graph + newNode.setLocation(p); + newNode.setParent(null); + nodes.add(newNode); + return true; + } + + @Override + public void removeNode(INode... nodesToRemove) + { + // Step 1a : Remove nodes directly attach to the graph + for (INode aNodeToRemove : nodesToRemove) + { + if (this.nodes.contains(aNodeToRemove)) + { + this.nodes.remove(aNodeToRemove); + } + } + // Step 1b : Remove nodes attach to other nodes as children + for (INode aNode : getAllNodes()) + { + for (INode aNodeToRemove : nodesToRemove) + { + List children = aNode.getChildren(); + if (children.contains(aNodeToRemove)) + { + aNode.removeChild(aNodeToRemove); + } + } + } + // Step 2 : Disconnect edges + List edgesToRemove = new ArrayList(); + Collection allNodes = getAllNodes(); + for (IEdge anEdge : this.edges) + { + INode startingNode = anEdge.getStart(); + INode endingNode = anEdge.getEnd(); + boolean isEdgeStillConnected = (allNodes.contains(startingNode) && allNodes.contains(endingNode)); + if (!isEdgeStillConnected) + { + edgesToRemove.add(anEdge); + } + } + IEdge[] edgesToRemoveAsArray = edgesToRemove.toArray(new IEdge[edgesToRemove.size()]); + removeEdge(edgesToRemoveAsArray); + } + + @Override + public boolean connect(IEdge e, INode start, Point2D startLocation, INode end, Point2D endLocation) + { + // Step 1 : find if nodes exist + Collection allNodes = getAllNodes(); + if (start != null && !allNodes.contains(start)) { + addNode(start, start.getLocation()); + } + if (end != null && !allNodes.contains(end)) { + addNode(end, end.getLocation()); + } + e.setStart(start); + e.setStartLocation(startLocation); + e.setEnd(end); + e.setEndlocation(endLocation); + if (start.addConnection(e)) + { + e.setId(new Id()); + edges.add(e); + return true; + } + return false; + } + + @Override + public void removeEdge(IEdge... edgesToRemove) + { + for (IEdge anEdgeToRemove : edgesToRemove) + { + INode startingNode = anEdgeToRemove.getStart(); + INode endingNode = anEdgeToRemove.getEnd(); + startingNode.removeConnection(anEdgeToRemove); + endingNode.removeConnection(anEdgeToRemove); + this.edges.remove(anEdgeToRemove); + } + } + + @Override + public IGrid getGrid() + { + if (this.grid == null) { + this.grid = new EmptyGrid(); + } + return grid; + } + + private ArrayList nodes; + private ArrayList edges; + private transient Rectangle2D minBounds; + private transient IGrid grid; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/Direction.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/Direction.java new file mode 100644 index 0000000..11d80c9 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/Direction.java @@ -0,0 +1,137 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts; + +import java.awt.geom.Point2D; + +/** + * This class describes a direction in the 2D plane. A direction is a vector of length 1 with an angle between 0 (inclusive) and 360 + * degrees (exclusive). There is also a degenerate direction of length 0. + */ +public class Direction +{ + /** + * Constructs a direction (normalized to length 1). + * + * @param dx the x-value of the direction + * @param dy the corresponding y-value of the direction + */ + public Direction(double dx, double dy) + { + x = dx; + y = dy; + double length = Math.sqrt(x * x + y * y); + if (length == 0) return; + x = x / length; + y = y / length; + } + + /** + * Constructs a direction between two points + * + * @param p the starting point + * @param q the ending point + */ + public Direction(Point2D p, Point2D q) + { + this(q.getX() - p.getX(), q.getY() - p.getY()); + } + + /** + * Turns this direction by an angle. + * + * @param angle the angle in degrees + */ + public Direction turn(double angle) + { + double a = Math.toRadians(angle); + return new Direction(x * Math.cos(a) - y * Math.sin(a), x * Math.sin(a) + y * Math.cos(a)); + } + + /** + * Gets the x-component of this direction + * + * @return the x-component (between -1 and 1) + */ + public double getX() + { + return x; + } + + /** + * Gets the y-component of this direction + * + * @return the y-component (between -1 and 1) + */ + public double getY() + { + return y; + } + + /** + * Gets the nearest direction corresponding to a cardinal one (NORTH, SOUTH, EAST, WEST) + * + * @return Direction.NORTH or Direction.SOUTH or Direction.EAST or Direction.WEST + */ + public Direction getNearestCardinalDirection() + { + long privateX = Math.round(this.getX()); + long privateY = Math.round(this.getY()); + if (Math.abs(privateX) == 1 && Math.abs(privateY) == 1) + { + if (Math.abs(this.getX()) >= Math.abs(this.getY())) + { + privateY = 0; + } + if (Math.abs(this.getX()) < Math.abs(this.getY())) + { + privateX = 0; + } + } + if (privateX == 0 && privateY == -1) + { + return Direction.NORTH; + } + if (privateX == 0 && privateY == 1) + { + return Direction.SOUTH; + } + if (privateX == 1 && privateY == 0) + { + return Direction.EAST; + } + if (privateX == -1 && privateY == 0) + { + return Direction.WEST; + } + // Default case returns no change (should never heppen) + return this; + } + + private double x; + private double y; + + public static final Direction NORTH = new Direction(0, -1); + public static final Direction SOUTH = new Direction(0, 1); + public static final Direction EAST = new Direction(1, 0); + public static final Direction WEST = new Direction(-1, 0); +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/GraphRegistry.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/GraphRegistry.java new file mode 100644 index 0000000..103ad1d --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/GraphRegistry.java @@ -0,0 +1,61 @@ +package com.horstmann.violet.product.diagram.abstracts; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import com.horstmann.violet.framework.plugin.IDiagramPlugin; + +/** + * Graph type registry. Each graph should be registered here to be accessible. + * + * @author Alexandre de Pellegrin + * + */ +public class GraphRegistry +{ + + /** + * Singleton instance + */ + private static GraphRegistry instance; + + /** + * Resgistry map + */ + private Map, IDiagramPlugin> registry = new HashMap, IDiagramPlugin>(); + + /** + * Singleton constructor + */ + private GraphRegistry() { + // Singleton + } + + /** + * @return registry instance + */ + public static GraphRegistry getInstance() { + if (instance == null) { + instance = new GraphRegistry(); + } + return instance; + } + + /** + * Registers a new graph type + * @param newGraphType t + */ + public void registerGraphType(IDiagramPlugin newGraphType) { + this.registry.put(newGraphType.getGraphClass(), newGraphType); + } + + /** + * @return already registered graph types + */ + public Collection getRegisteredGraphTypes() { + Collection values = this.registry.values(); + return values; + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/IGraph.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/IGraph.java new file mode 100644 index 0000000..bd4f016 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/IGraph.java @@ -0,0 +1,151 @@ +package com.horstmann.violet.product.diagram.abstracts; + +import java.awt.Graphics2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Collection; +import java.util.List; + +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.IGrid; + +public interface IGraph +{ + + /** + * Gets the node types of a particular graph type. + * + * @return a list of node prototypes + */ + public abstract List getNodePrototypes(); + + + /** + * Gets the edge types of a particular graph type. + * + * @return a list of edge prototypes + */ + public abstract List getEdgePrototypes(); + + + /** + * Gets ALL the nodes of this graph. + * + * @return an unmodifiable collection of the nodes + */ + public abstract Collection getAllNodes(); + + + /** + * Gets ALL the edges of this graph. + * + * @return an unmodifiable collection of the edges + */ + public abstract Collection getAllEdges(); + + + /** + * Removes one or more edges from this graph. + * + * @param edgesToRemove + */ + public abstract void removeEdge(IEdge... edgesToRemove); + + /** + * Removes one or more nodes from this graph. + * + * @param nodesToRemove + */ + public abstract void removeNode(INode... nodesToRemove); + + /** + * Adds a node to the graph so that the top left corner of the bounding rectangle is at the given point. + * This method is called by a decoder when reading a data file. + * + * @param n the node to add + * @param p the desired location + */ + public abstract boolean addNode(INode n, Point2D p); + + + /** + * Adds an edge to this graph. + * + * @param e the new edge to add (don't forget to populate it withs the following nodes and points!) + * @param start the start node of the edge + * @param startLocation the point inside the start node where the edge begins + * @param end the end node of the edge + * @param endLocation the point inside the end node where the edge ends + * @return isOK as true if successfully connected + */ + public abstract boolean connect(IEdge e, INode start, Point2D startLocation, INode end, Point2D endLocation); + + + /** + * Finds a node by its id. This internal method should only be used by network features (for the moment because + * node ids are still generated automatically) + * + * @param id + * @return the found node or null if no one found + */ + public abstract INode findNode(Id id); + + + /** + * Finds a node containing the given point. + * + * @param p a point + * @return a node containing p or null if no nodes contain p + */ + public abstract INode findNode(Point2D p); + + /** + * Finds an adge by its id. This internal method should only be used by network features (for the moment because + * edge ids are still generated automatically) + * + * @param id + * @return the found edge or null if no one found + */ + public abstract IEdge findEdge(Id id); + + + /** + * Finds an edge containing the given point. + * + * @param p a point + * @return an edge containing p or null if no edges contain p + */ + public abstract IEdge findEdge(Point2D p); + + /** + * Draws the graph + * + * @param g2 the graphics context + */ + public abstract void draw(Graphics2D g2); + + + /** + * Gets the smallest rectangle enclosing the graph + * + * @return the bounding rectangle + */ + public abstract Rectangle2D getClipBounds(); + + + /** + * Sets desired bound + * + * @param newValue + */ + public abstract void setBounds(Rectangle2D newValue); + + + /** + * Return the grid used to snap elements on the graph + */ + public IGrid getGrid(); + + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/IIdentifiable.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/IIdentifiable.java new file mode 100644 index 0000000..e1fcac5 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/IIdentifiable.java @@ -0,0 +1,44 @@ +package com.horstmann.violet.product.diagram.abstracts; + +/** + * Indicates that this object can be identified over an unique id and a revision number + * + * + * @author Alexandre de Pellegrin + * + */ +public interface IIdentifiable +{ + + /** + * Returns a unique id of this node to make it easier to identify + * + * @return a unique id + */ + Id getId(); + + /** + * Sets unique id to this node to make it easier to identify + * + * @param id new unique id + */ + void setId(Id id); + + /** + * Returns current node revision + */ + Integer getRevision(); + + /** + * Updates current node revision number + * + * @param newRevisionNumber n + */ + void setRevision(Integer newRevisionNumber); + + /** + * Increments revision number + */ + void incrementRevision(); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/Id.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/Id.java new file mode 100644 index 0000000..fb9b6d1 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/Id.java @@ -0,0 +1,85 @@ +package com.horstmann.violet.product.diagram.abstracts; + +import com.horstmann.violet.framework.util.UniqueIDGenerator; +import com.thoughtworks.xstream.annotations.XStreamAsAttribute; + +/** + * This class represents an Id. To advantage to transport an id in a specific object rather than in a "standard" object like String + * or Integer/Long is in the fact that it can be identified by its class. So, it will never be confused with other object attributes + * (for example, name or age for a person). + * + * But be careful and don't abuse of this programming pattern + * + * @author Alexandre de Pellegrin + * + */ +public class Id +{ + + /** + * Default constructor + */ + public Id() + { + this.value = UniqueIDGenerator.getNewId(); + } + + /** + * @return id value + */ + public String getValue() + { + return value; + } + + /** + * Sets id value + * + * @param value v + */ + public void setValue(String value) + { + this.value = value; + } + + @Override + public boolean equals(Object obj) + { + if (obj == null) return false; + if (value != null) return value.equals(((Id) obj).value); + return false; + } + + @Override + public int hashCode() + { + if (value != null) + { + return value.hashCode(); + } + return super.hashCode(); + } + + @Override + public String toString() + { + if (value != null) + { + return value; + } + return super.toString(); + } + + @Override + public Id clone() throws CloneNotSupportedException { + Id clone = new Id(); + clone.value = this.value + ""; + return clone; + } + + /** + * Id value + */ + @XStreamAsAttribute + private String value; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/AbstractEdge.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/AbstractEdge.java new file mode 100644 index 0000000..6c16f5b --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/AbstractEdge.java @@ -0,0 +1,226 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.edge; + +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import com.horstmann.violet.product.diagram.abstracts.Direction; +import com.horstmann.violet.product.diagram.abstracts.Id; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.thoughtworks.xstream.annotations.XStreamAsAttribute; + +/** + * A class that supplies convenience implementations for a number of methods in the Edge interface + */ +public abstract class AbstractEdge implements IEdge +{ + public AbstractEdge() + { + // Nothing to do + } + + @Override + public void setStart(INode startingNode) + { + this.start = startingNode; + } + + @Override + public INode getStart() + { + return start; + } + + @Override + public void setEnd(INode endingNode) + { + this.end = endingNode; + } + + @Override + public INode getEnd() + { + return end; + } + + @Override + public void setStartLocation(Point2D startLocation) + { + this.startLocation = startLocation; + } + + @Override + public Point2D getStartLocation() + { + return startLocation; + } + + @Override + public void setEndlocation(Point2D endLocation) + { + this.endLocation = endLocation; + } + + @Override + public Point2D getEndLocation() + { + return this.endLocation; + } + + @Override + public Rectangle2D getBounds() + { + Line2D conn = getConnectionPoints(); + Rectangle2D r = new Rectangle2D.Double(); + r.setFrameFromDiagonal(conn.getX1(), conn.getY1(), conn.getX2(), conn.getY2()); + return r; + } + + @Override + public Direction getDirection(INode node) { + Rectangle2D startBounds = start.getBounds(); + Rectangle2D endBounds = end.getBounds(); + Point2D startLocationOnGraph = start.getLocationOnGraph(); + Point2D endLocationOnGraph = end.getLocationOnGraph(); + Point2D startCenter = new Point2D.Double(startLocationOnGraph.getX() + startBounds.getWidth() / 2, startLocationOnGraph.getY() + startBounds.getHeight() / 2); + Point2D endCenter = new Point2D.Double(endLocationOnGraph.getX() + endBounds.getWidth() / 2, endLocationOnGraph.getY() + endBounds.getHeight() / 2); + if (node.equals(start)) { + Direction fromStart = new Direction(endCenter, startCenter); + return fromStart; + } + if (node.equals(end)) { + Direction toEnd = new Direction(startCenter, endCenter); + return toEnd; + } + return null; + } + + @Override + public Line2D getConnectionPoints() + { + INode startingNode = getStart(); + INode endingNode = getEnd(); + Point2D startingNodeLocation = startingNode.getLocation(); + Point2D endingNodeLocation = endingNode.getLocation(); + Point2D startingNodeLocationOnGraph = startingNode.getLocationOnGraph(); + Point2D endingNodeLocationOnGraph = endingNode.getLocationOnGraph(); + Point2D relativeStartingConnectionPoint = startingNode.getConnectionPoint(this); + Point2D relativeEndingConnectionPoint = endingNode.getConnectionPoint(this); + Point2D absoluteStartingConnectionPoint = new Point2D.Double(startingNodeLocationOnGraph.getX() - startingNodeLocation.getX() + relativeStartingConnectionPoint.getX(), startingNodeLocationOnGraph.getY() - startingNodeLocation.getY() + relativeStartingConnectionPoint.getY()); + Point2D absoluteEndingConnectionPoint = new Point2D.Double(endingNodeLocationOnGraph.getX() - endingNodeLocation.getX() + relativeEndingConnectionPoint.getX(), endingNodeLocationOnGraph.getY() - endingNodeLocation.getY() + relativeEndingConnectionPoint.getY()); + return new Line2D.Double(absoluteStartingConnectionPoint, absoluteEndingConnectionPoint); + } + + @Override + public Id getId() + { + if (this.id == null) { + this.id = new Id(); + } + return this.id; + } + + @Override + public void setId(Id id) + { + this.id = id; + } + + @Override + public AbstractEdge clone() + { + try + { + AbstractEdge cloned = (AbstractEdge) super.clone(); + cloned.id = new Id(); + return cloned; + } + catch (CloneNotSupportedException ex) + { + return null; + } + } + + @Override + public Integer getRevision() + { + if (this.revision == null) { + this.revision = new Integer(0); + } + return this.revision; + } + + @Override + public void setRevision(Integer newRevisionNumber) + { + this.revision = newRevisionNumber; + } + + public void incrementRevision() + { + int i = getRevision().intValue(); + i++; + this.revision = new Integer(i); + } + + /** + * Sets edge tool tip + * + * @param s + */ + public void setToolTip(String s) + { + this.toolTip = s; + } + + @Override + public String getToolTip() + { + if (this.toolTip == null) { + this.toolTip = ""; + } + return this.toolTip; + } + + /** The node where the edge starts */ + private INode start; + + /** The node where the edge ends */ + private INode end; + + /** The point inside the starting node where this edge begins */ + private Point2D startLocation; + + /** The point inside the ending node where this edge ends */ + private Point2D endLocation; + + /** Edge's current id (unique in all the graph) */ + private Id id; + + /** Edge's current revision */ + private Integer revision; + + /** Edge tool tip */ + private transient String toolTip; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/AbstractEdgeBeanInfo.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/AbstractEdgeBeanInfo.java new file mode 100644 index 0000000..d457d27 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/AbstractEdgeBeanInfo.java @@ -0,0 +1,46 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.edge; + +import java.beans.PropertyDescriptor; +import java.beans.SimpleBeanInfo; + +/** + * The bean info for the AbstractEdge type. This hides the ID. + * + * @author Cay Horstmann + */ +public class AbstractEdgeBeanInfo extends SimpleBeanInfo +{ + /* + * (non-Javadoc) + * + * @see java.beans.BeanInfo#getPropertyDescriptors() + */ + public PropertyDescriptor[] getPropertyDescriptors() + { + return new PropertyDescriptor[] + { + }; + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/IEdge.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/IEdge.java new file mode 100644 index 0000000..1d311c9 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/IEdge.java @@ -0,0 +1,140 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.edge; + +import java.awt.Graphics2D; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.Serializable; + +import com.horstmann.violet.product.diagram.abstracts.Direction; +import com.horstmann.violet.product.diagram.abstracts.IIdentifiable; +import com.horstmann.violet.product.diagram.abstracts.node.INode; + +/** + * An edge in a graph. + */ +public interface IEdge extends Serializable, Cloneable, IIdentifiable +{ + /** + * Sets the starting node + * + * @param startingNode + */ + void setStart(INode startingNode); + + /** + * Gets the starting node. + * + * @return the starting node + */ + INode getStart(); + + /** + * Sets the ending node + * + * @param endingNode + */ + void setEnd(INode endingNode); + + /** + * Gets the ending node. + * + * @return the ending node + */ + INode getEnd(); + + /** + * Sets the point from where this edge begins (relative to the starting node) + * + * @param startingLocation + */ + void setStartLocation(Point2D startingLocation); + + /** + * @return the point from where this end begins location (relative to the starting node) + */ + Point2D getStartLocation(); + + /** + * Sets the point where this node ends (relative to the ending node) + * + * @param endingLocation + */ + void setEndlocation(Point2D endingLocation); + + /** + * @return the point where this node ends (relative to the ending node) + */ + Point2D getEndLocation(); + + /** + * Gets the points at which this edge is connected to its nodes. + * + * @return a line joining the two connection points + */ + Line2D getConnectionPoints(); + + /** + * Tests whether the edge contains a point. + * + * @param aPoint the point to test + * @return true if this edge contains aPoint + */ + boolean contains(Point2D aPoint); + + /** + * Gets the smallest rectangle that bounds this edge. The bounding rectangle contains all labels. + * + * @return the bounding rectangle + */ + Rectangle2D getBounds(); + + + /** + * Gets edge's direction for this node + * + * @return direction or null if this edge is not connected to this node + */ + Direction getDirection(INode node); + + /** + * Draw the edge. + * + * @param g2 the graphics context + */ + void draw(Graphics2D g2); + + /** + * Gets current edge tool tip + * + * @return s + */ + String getToolTip(); + + /** + * @return a deep copy of this object + */ + IEdge clone(); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/SegmentedLineEdge.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/SegmentedLineEdge.java new file mode 100644 index 0000000..d486c10 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/SegmentedLineEdge.java @@ -0,0 +1,456 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.edge; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.GeneralPath; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.util.ArrayList; + +import javax.swing.JLabel; + +import com.horstmann.violet.product.diagram.abstracts.Direction; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.product.diagram.abstracts.property.ArrowHead; +import com.horstmann.violet.product.diagram.abstracts.property.BentStyle; +import com.horstmann.violet.product.diagram.abstracts.property.LineStyle; + +/** + * An edge that is composed of multiple line segments + */ +public abstract class SegmentedLineEdge extends ShapeEdge +{ + /** + * Constructs an edge with no adornments. + */ + public SegmentedLineEdge() + { + startLabel = ""; + middleLabel = ""; + endLabel = ""; + } + + /** + * Sets the bentStyle property + * + * @param newValue the bent style + */ + public void setBentStyle(BentStyle newValue) + { + this.bentStyle = newValue; + } + + /** + * Gets the bentStyle property + * + * @return the bent style + */ + public BentStyle getBentStyle() + { + if (this.bentStyle == null) + { + this.bentStyle = BentStyle.AUTO; + } + return bentStyle; + } + + /** + * Sets the line style property. + * + * @param newValue the new value + */ + public void setLineStyle(LineStyle newValue) + { + this.lineStyle = newValue; + } + + /** + * Gets the line style property. + * + * @return the line style + */ + public LineStyle getLineStyle() + { + if (this.lineStyle == null) + { + this.lineStyle = LineStyle.SOLID; + } + return this.lineStyle; + } + + /** + * Sets the start arrow head property + * + * @param newValue the new value + */ + public void setStartArrowHead(ArrowHead newValue) + { + this.startArrowHead = newValue; + } + + /** + * Gets the start arrow head property + * + * @return the start arrow head style + */ + public ArrowHead getStartArrowHead() + { + if (this.startArrowHead == null) + { + this.startArrowHead = ArrowHead.NONE; + } + return this.startArrowHead; + } + + /** + * Sets the end arrow head property + * + * @param newValue the new value + */ + public void setEndArrowHead(ArrowHead newValue) + { + this.endArrowHead = newValue; + } + + /** + * Gets the end arrow head property + * + * @return the end arrow head style + */ + public ArrowHead getEndArrowHead() + { + if (this.endArrowHead == null) + { + this.endArrowHead = ArrowHead.NONE; + } + return this.endArrowHead; + } + + /** + * Sets the start label property + * + * @param newValue the new value + */ + public void setStartLabel(String newValue) + { + startLabel = newValue; + } + + /** + * Gets the start label property + * + * @return the label at the start of the edge + */ + public String getStartLabel() + { + return startLabel; + } + + /** + * Sets the middle label property + * + * @param newValue the new value + */ + public void setMiddleLabel(String newValue) + { + middleLabel = newValue; + } + + /** + * Gets the middle label property + * + * @return the label at the middle of the edge + */ + public String getMiddleLabel() + { + return middleLabel; + } + + /** + * Sets the end label property + * + * @param newValue the new value + */ + public void setEndLabel(String newValue) + { + endLabel = newValue; + } + + /** + * Gets the end label property + * + * @return the label at the end of the edge + */ + public String getEndLabel() + { + return endLabel; + } + + /** + * Draws the edge. + * + * @param g2 the graphics context + */ + public void draw(Graphics2D g2) + { + ArrayList points = getPoints(); + + Stroke oldStroke = g2.getStroke(); + g2.setStroke(getLineStyle().getStroke()); + g2.draw(getSegmentPath()); + g2.setStroke(oldStroke); + getStartArrowHead().draw(g2, (Point2D) points.get(1), (Point2D) points.get(0)); + getEndArrowHead().draw(g2, (Point2D) points.get(points.size() - 2), (Point2D) points.get(points.size() - 1)); + + drawString(g2, (Point2D) points.get(1), (Point2D) points.get(0), getStartArrowHead(), startLabel, false); + drawString(g2, (Point2D) points.get(points.size() / 2 - 1), (Point2D) points.get(points.size() / 2), null, middleLabel, + true); + drawString(g2, (Point2D) points.get(points.size() - 2), (Point2D) points.get(points.size() - 1), getEndArrowHead(), + endLabel, false); + } + + /** + * Draws a string. + * + * @param g2 the graphics context + * @param p an endpoint of the segment along which to draw the string + * @param q the other endpoint of the segment along which to draw the string + * @param s the string to draw + * @param center true if the string should be centered along the segment + */ + private void drawString(Graphics2D g2, Point2D p, Point2D q, ArrowHead arrow, String s, boolean center) + { + if (s == null || s.length() == 0) return; + label.setText("" + s + ""); + label.setFont(g2.getFont()); + Dimension d = label.getPreferredSize(); + label.setBounds(0, 0, d.width, d.height); + + Rectangle2D b = getStringBounds(p, q, arrow, s, center); + + g2.translate(b.getX(), b.getY()); + label.paint(g2); + g2.translate(-b.getX(), -b.getY()); + } + + /** + * Computes the attachment point for drawing a string. + * + * @param p an endpoint of the segment along which to draw the string + * @param q the other endpoint of the segment along which to draw the string + * @param b the bounds of the string to draw + * @param center true if the string should be centered along the segment + * @return the point at which to draw the string + */ + private static Point2D getAttachmentPoint(Point2D p, Point2D q, ArrowHead arrow, Dimension d, boolean center) + { + final int GAP = 3; + double xoff = GAP; + double yoff = -GAP - d.getHeight(); + Point2D attach = q; + if (center) + { + if (p.getX() > q.getX()) + { + return getAttachmentPoint(q, p, arrow, d, center); + } + attach = new Point2D.Double((p.getX() + q.getX()) / 2, (p.getY() + q.getY()) / 2); + if (p.getY() < q.getY()) yoff = -GAP - d.getHeight(); + else if (p.getY() == q.getY()) xoff = -d.getWidth() / 2; + else yoff = GAP; + } + else + { + if (p.getX() < q.getX()) + { + xoff = -GAP - d.getWidth(); + } + if (p.getY() > q.getY()) + { + yoff = GAP; + } + if (arrow != null) + { + Rectangle2D arrowBounds = arrow.getPath(p, q).getBounds2D(); + if (p.getX() < q.getX()) + { + xoff -= arrowBounds.getWidth(); + } + else + { + xoff += arrowBounds.getWidth(); + } + } + } + return new Point2D.Double(attach.getX() + xoff, attach.getY() + yoff); + } + + /** + * Computes the extent of a string that is drawn along a line segment. + * + * @param g2 the graphics context + * @param p an endpoint of the segment along which to draw the string + * @param q the other endpoint of the segment along which to draw the string + * @param s the string to draw + * @param center true if the string should be centered along the segment + * @return the rectangle enclosing the string + */ + private static Rectangle2D getStringBounds(Point2D p, Point2D q, ArrowHead arrow, String s, boolean center) + { + BufferedImage dummy = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); + // need a dummy image to get a Graphics to + // measure the size + Graphics2D g2 = (Graphics2D) dummy.getGraphics(); + if (s == null || s.equals("")) return new Rectangle2D.Double(q.getX(), q.getY(), 0, 0); + label.setText("" + s + ""); + label.setFont(g2.getFont()); + Dimension d = label.getPreferredSize(); + Point2D a = getAttachmentPoint(p, q, arrow, d, center); + return new Rectangle2D.Double(a.getX(), a.getY(), d.getWidth(), d.getHeight()); + } + + @Override + public Rectangle2D getBounds() + { + ArrayList points = getPoints(); + Rectangle2D r = super.getBounds(); + r.add(getStringBounds((Point2D) points.get(1), (Point2D) points.get(0), getStartArrowHead(), startLabel, false)); + r.add(getStringBounds((Point2D) points.get(points.size() / 2 - 1), (Point2D) points.get(points.size() / 2), null, + middleLabel, true)); + r.add(getStringBounds((Point2D) points.get(points.size() - 2), (Point2D) points.get(points.size() - 1), getEndArrowHead(), + endLabel, false)); + return r; + } + + @Override + public Shape getShape() + { + GeneralPath path = getSegmentPath(); + ArrayList points = getPoints(); + path.append(getStartArrowHead().getPath((Point2D) points.get(1), (Point2D) points.get(0)), false); + path.append(getEndArrowHead().getPath((Point2D) points.get(points.size() - 2), (Point2D) points.get(points.size() - 1)), + false); + return path; + } + + private GeneralPath getSegmentPath() + { + ArrayList points = getPoints(); + + GeneralPath path = new GeneralPath(); + Point2D p = (Point2D) points.get(points.size() - 1); + path.moveTo((float) p.getX(), (float) p.getY()); + for (int i = points.size() - 2; i >= 0; i--) + { + p = (Point2D) points.get(i); + path.lineTo((float) p.getX(), (float) p.getY()); + } + return path; + } + + /** + * Gets the corner points of this segmented line edge + * + * @return an array list of Point2D objects, containing the corner points + */ + public ArrayList getPoints() + { + Line2D connectionPoints = getConnectionPoints(); + Point2D startingPoint = connectionPoints.getP1(); + Point2D endingPoint = connectionPoints.getP2(); + if (!BentStyle.AUTO.equals(getBentStyle())) + { + return getBentStyle().getPath(startingPoint, endingPoint); + } + + Direction startingCardinalDirection = getDirection(getStart()).getNearestCardinalDirection(); + Direction endingCardinalDirection = getDirection(getEnd()).getNearestCardinalDirection(); + if ((Direction.NORTH.equals(startingCardinalDirection) || Direction.SOUTH.equals(startingCardinalDirection)) + && (Direction.NORTH.equals(endingCardinalDirection) || Direction.SOUTH.equals(endingCardinalDirection))) + { + return BentStyle.VHV.getPath(startingPoint, endingPoint); + } + if ((Direction.NORTH.equals(startingCardinalDirection) || Direction.SOUTH.equals(startingCardinalDirection)) + && (Direction.EAST.equals(endingCardinalDirection) || Direction.WEST.equals(endingCardinalDirection))) + { + return BentStyle.VH.getPath(startingPoint, endingPoint); + } + if ((Direction.EAST.equals(startingCardinalDirection) || Direction.WEST.equals(startingCardinalDirection)) + && (Direction.NORTH.equals(endingCardinalDirection) || Direction.SOUTH.equals(endingCardinalDirection))) + { + return BentStyle.HV.getPath(startingPoint, endingPoint); + } + if ((Direction.EAST.equals(startingCardinalDirection) || Direction.WEST.equals(startingCardinalDirection)) + && (Direction.EAST.equals(endingCardinalDirection) || Direction.WEST.equals(endingCardinalDirection))) + { + return BentStyle.HVH.getPath(startingPoint, endingPoint); + } + return BentStyle.STRAIGHT.getPath(startingPoint, endingPoint); + } + + @Override + public Direction getDirection(INode node) + { + Direction straightDirection = super.getDirection(node); + double x = straightDirection.getX(); + double y = straightDirection.getY(); + if (node.equals(getStart())) + { + if (BentStyle.HV.equals(getBentStyle()) || BentStyle.HVH.equals(getBentStyle())) + { + return (x >= 0) ? Direction.EAST : Direction.WEST; + } + if (BentStyle.VH.equals(getBentStyle()) || BentStyle.VHV.equals(getBentStyle())) + { + return (y >= 0) ? Direction.SOUTH : Direction.NORTH; + } + } + if (node.equals(getEnd())) + { + if (BentStyle.HV.equals(getBentStyle()) || BentStyle.VHV.equals(getBentStyle())) + { + return (y >= 0) ? Direction.SOUTH : Direction.NORTH; + } + if (BentStyle.VH.equals(getBentStyle()) || BentStyle.HVH.equals(getBentStyle())) + { + return (x >= 0) ? Direction.EAST : Direction.WEST; + } + } + return straightDirection; + } + + private transient LineStyle lineStyle; + private transient ArrowHead startArrowHead; + private transient ArrowHead endArrowHead; + private BentStyle bentStyle; + private String startLabel; + private String middleLabel; + private String endLabel; + + private static JLabel label = new JLabel(); +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/ShapeEdge.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/ShapeEdge.java new file mode 100644 index 0000000..8c145cf --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/edge/ShapeEdge.java @@ -0,0 +1,64 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.edge; + +import java.awt.BasicStroke; +import java.awt.Shape; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +/** + * A class that assumes that an edge can yield its shape and then takes advantage of the fact that containment testing can be done + * by stroking the shape with a fat stroke. NOTE: Ideally, you should be able to draw the same shape that is used for containment + * testing. However, in JDK 1.4, BasicStroke.createStrokedShape returns shitty-looking shapes. + */ +public abstract class ShapeEdge extends AbstractEdge +{ + /** + * Returns the path that should be stroked to draw this edge. The path does not include arrow tips or labels. + * + * @return a path along the edge + */ + public abstract Shape getShape(); + + @Override + public Rectangle2D getBounds() + { + return getShape().getBounds(); + } + + public boolean contains(Point2D aPoint) + { + final double MAX_DIST = 3; + + // the end points may contain small nodes, so don't + // match them + Line2D conn = getConnectionPoints(); + if (aPoint.distance(conn.getP1()) <= MAX_DIST || aPoint.distance(conn.getP2()) <= MAX_DIST) return false; + + Shape p = getShape(); + BasicStroke fatStroke = new BasicStroke((float) (2 * MAX_DIST)); + Shape fatPath = fatStroke.createStrokedShape(p); + return fatPath.contains(aPoint); + } +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/AbstractNode.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/AbstractNode.java new file mode 100644 index 0000000..43a93ed --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/AbstractNode.java @@ -0,0 +1,306 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.node; + +import java.awt.Shape; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.List; + +import com.horstmann.violet.product.diagram.abstracts.AbstractGraph; +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.Id; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; + +/** + * A class that supplies convenience implementations for a number of methods in the Node interface + * + * @author Cay Horstmann + */ +public abstract class AbstractNode implements INode +{ + /** + * Constructs a node with no parents or children at location (0, 0). + */ + public AbstractNode() + { + // Nothing to do + } + + /** + * @return currently connected edges + */ + protected List getConnectedEdges() + { + List connectedEdges = new ArrayList(); + IGraph currentGraph = getGraph(); + for (IEdge anEdge : currentGraph.getAllEdges()) + { + INode start = anEdge.getStart(); + INode end = anEdge.getEnd(); + if (this.equals(start) || this.equals(end)) + { + connectedEdges.add(anEdge); + } + } + return connectedEdges; + } + + @Override + public Point2D getLocation() + { + if (this.location == null) { + this.location = new Point2D.Double(0, 0); + } + return this.location; + } + + @Override + public Point2D getLocationOnGraph() + { + INode parentNode = getParent(); + if (parentNode == null) + { + return getLocation(); + } + Point2D parentLocationOnGraph = parentNode.getLocationOnGraph(); + Point2D relativeLocation = getLocation(); + Point2D result = new Point2D.Double(parentLocationOnGraph.getX() + relativeLocation.getX(), parentLocationOnGraph.getY() + + relativeLocation.getY()); + return result; + } + + @Override + public void setLocation(Point2D aPoint) + { + this.location = aPoint; + } + + @Override + public Id getId() + { + if (this.id == null) { + this.id = new Id(); + } + return this.id; + } + + @Override + public void setId(Id id) + { + this.id = id; + } + + @Override + public Integer getRevision() + { + if (this.revision == null) { + this.revision = new Integer(0); + } + return this.revision; + } + + @Override + public void setRevision(Integer newRevisionNumber) + { + this.revision = newRevisionNumber; + } + + @Override + public void incrementRevision() + { + int i = getRevision().intValue(); + i++; + this.revision = new Integer(i); + } + + @Override + public void translate(double dx, double dy) + { + Point2D newLocation = new Point2D.Double(getLocation().getX() + dx, getLocation().getY() + dy); + setLocation(newLocation); + } + + @Override + public boolean addConnection(IEdge e) + { + return e.getEnd() != null; + } + + @Override + public void removeConnection(IEdge e) + { + } + + @Override + public void removeChild(INode node) + { + if (node.getParent() != this) return; + getChildren().remove(node); + } + + @Override + public boolean addChild(INode n, Point2D p) + { + return false; + } + + @Override + public INode getParent() + { + return parent; + } + + @Override + public void setParent(INode node) + { + parent = node; + } + + @Override + public List getChildren() + { + if (this.children == null) { + this.children = new ArrayList(); + } + return children; + } + + @Override + public boolean addChild(INode node, int index) + { + INode oldParent = node.getParent(); + if (oldParent != null) oldParent.removeChild(node); + getChildren().add(index, node); + node.setParent(this); + node.setGraph(getGraph()); + return true; + } + + /** + * @return the shape to be used for computing the drop shadow + */ + public Shape getShape() + { + return new Rectangle2D.Double(0, 0, 0, 0); + } + + @Override + public AbstractNode clone() + { + try + { + AbstractNode cloned = (AbstractNode) super.clone(); + cloned.id = getId().clone(); + cloned.children = new ArrayList(); + cloned.location = (Point2D.Double) getLocation().clone(); + + for (INode child : getChildren()) + { + INode clonedChild = child.clone(); + cloned.children.add(clonedChild); + clonedChild.setParent(cloned); + } + return cloned; + } + catch (CloneNotSupportedException exception) + { + return null; + } + } + + @Override + public void setGraph(IGraph g) + { + this.graph = g; + for (INode aChild : getChildren()) { + aChild.setGraph(g); + } + } + + @Override + public IGraph getGraph() + { + if (this.graph == null) { + this.graph = new AbstractGraph() + { + @Override + public List getNodePrototypes() + { + return new ArrayList(); + } + + @Override + public List getEdgePrototypes() + { + return new ArrayList(); + } + }; + } + return this.graph; + } + + @Override + public int getZ() + { + return z; + } + + @Override + public void setZ(int z) + { + this.z = z; + } + + /** + * Sets node tool tip + * + * @param label + */ + public void setToolTip(String s) + { + this.toolTip = s; + } + + @Override + public String getToolTip() + { + if (this.toolTip == null) { + this.toolTip = ""; + } + return this.toolTip; + } + + private ArrayList children; + private INode parent; + private transient IGraph graph; + private Point2D location; + private transient String toolTip; + private transient int z; + + /** Node's current id (unique in all the graph) */ + private Id id; + + /** Node's current revision */ + private Integer revision; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/AbstractNodeBeanInfo.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/AbstractNodeBeanInfo.java new file mode 100644 index 0000000..9e847b9 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/AbstractNodeBeanInfo.java @@ -0,0 +1,44 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.node; + +import java.beans.PropertyDescriptor; +import java.beans.SimpleBeanInfo; + +/** + * The bean info for the AbstractNode type. This hides all. + * + * @author Cay Horstmann + */ +public class AbstractNodeBeanInfo extends SimpleBeanInfo +{ + /* + * (non-Javadoc) + * + * @see java.beans.BeanInfo#getPropertyDescriptors() + */ + public PropertyDescriptor[] getPropertyDescriptors() + { + return new PropertyDescriptor[] {}; + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/EllipticalNode.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/EllipticalNode.java new file mode 100644 index 0000000..e70b3f6 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/EllipticalNode.java @@ -0,0 +1,72 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.node; + +import java.awt.Shape; +import java.awt.geom.Ellipse2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import com.horstmann.violet.product.diagram.abstracts.Direction; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.RectangularNode; + +/** + * An elliptical (or circular) node. + */ +public abstract class EllipticalNode extends RectangularNode +{ + + @Override + public Point2D getConnectionPoint(IEdge e) + { + Direction d = e.getDirection(this).turn(180); + Rectangle2D bounds = getBounds(); + double a = bounds.getWidth() / 2; + double b = bounds.getHeight() / 2; + double x = d.getX(); + double y = d.getY(); + double cx = bounds.getCenterX(); + double cy = bounds.getCenterY(); + + if (a != 0 && b != 0 && !(x == 0 && y == 0)) + { + double t = Math.sqrt((x * x) / (a * a) + (y * y) / (b * b)); + return new Point2D.Double(cx + x / t, cy + y / t); + } + else + { + return new Point2D.Double(cx, cy); + } + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.AbstractNode#getShape() + */ + public Shape getShape() + { + return new Ellipse2D.Double(getBounds().getX(), getBounds().getY(), getBounds().getWidth() - 1, getBounds().getHeight() - 1); + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/INode.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/INode.java new file mode 100644 index 0000000..068b36b --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/INode.java @@ -0,0 +1,201 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.node; + +import java.awt.Graphics2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.Serializable; +import java.util.List; + +import com.horstmann.violet.product.diagram.abstracts.Direction; +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.IIdentifiable; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; + +/** + * A node in a graph. To be more precise, a node is an graphical entity that represents a class, a sequence, a state or all other + * type of entities that can or not handle edges. + * + * @author Cay Horstmann + */ +public interface INode extends Serializable, Cloneable, IIdentifiable +{ + + /** + * Checks whether to add an edge that originates at this node. + * + * @param e the edge to add + * @return true if the edge was added + */ + boolean addConnection(IEdge e); + + /** + * Notifies this node that an edge is being removed. + * + * @param g the ambient graph + * @param e the edge to be removed + */ + void removeConnection(IEdge e); + + /** + * Adds a node as a child node to this node. + * + * @param n the child node + * @param p the point at which the node is being added + * @return true if this node accepts the given node as a child + */ + boolean addChild(INode n, Point2D p); + + /** + * Adds a child node and fires the graph modification event. + * @param node the child node to add + * @param index the position at which to add the child + * @return true if this node accepts the given node as a child + */ + boolean addChild(INode node, int index); + + /** + * Notifies this node that a node is being removed. + * + * @param g the ambient graph + * @param n the node to be removed + */ + void removeChild(INode n); + + /** + * Gets the children of this node. + * + * @return an unmodifiable list of the children + */ + List getChildren(); + + /** + * Gets the parent of this node. + * + * @return the parent node, or null if the node has no parent + */ + INode getParent(); + + /** + * Sets node's parent (for decoder) + * + * @param parentNode p + */ + void setParent(INode parentNode); + + /** + * Sets the graph that contains this node. + * @param g the graph + */ + void setGraph(IGraph g); + + /** + * Gets the graph that contains this node, or null if this node is not contained in any graph. + * @return + */ + IGraph getGraph(); + + /** + * Translates the node by a given amount + * + * @param dx the amount to translate in the x-direction + * @param dy the amount to translate in the y-direction + */ + void translate(double dx, double dy); + + /** + * Tests whether the node contains a point. + * + * @param aPoint the point to test + * @return true if this node contains aPoint + */ + boolean contains(Point2D aPoint); + + /** + * Get the best connection point to connect this node with another node. This should be a point on the boundary of the shape of + * this node. + * + * @param d the direction from the center of the bounding rectangle towards the boundary + * @return the recommended connection point + */ + Point2D getConnectionPoint(IEdge e); + + /** + * Set or change node location + * @param aPoint + */ + void setLocation(Point2D aPoint); + + /** + * Gets the location of this node on its parent (i.e. relative location) + * @return the location + */ + Point2D getLocation(); + + /** + * Gets the location of this node on the whole graph. (i.e. absolute location) + * @return + */ + Point2D getLocationOnGraph(); + + /** + * Get the visual bounding rectangle of the shape of this node + * + * @return the bounding rectangle + */ + Rectangle2D getBounds(); + + /** + * Draw the node. + * + * @param g2 the graphics context + * @param grid the grid to snap to + */ + void draw(Graphics2D g2); + + + /** + + * Gets the z-order. Nodes with higher z-order are drawn above those with lower z-order. + * @return the z-order. + */ + int getZ(); + + /** + * Sets the z-order. + * @param z the desired z-order. + */ + void setZ(int z); + + + /** + * Gets current node tool tip + * @return + */ + String getToolTip(); + + /** + * @return a deep copy of this object + */ + INode clone(); +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/IResizableNode.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/IResizableNode.java new file mode 100644 index 0000000..5a20ebb --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/IResizableNode.java @@ -0,0 +1,8 @@ +package com.horstmann.violet.product.diagram.abstracts.node; + +import java.awt.geom.Rectangle2D; + +public interface IResizableNode +{ + public void setWantedSize(Rectangle2D size); +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/RectangularNode.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/RectangularNode.java new file mode 100644 index 0000000..0f3d955 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/RectangularNode.java @@ -0,0 +1,175 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.node; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import javax.swing.UIManager; + +import com.horstmann.violet.product.diagram.abstracts.Direction; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; + +/** + * A node that has a rectangular shape. + */ +public abstract class RectangularNode extends AbstractNode +{ + + public RectangularNode() + { + super(); + } + + public boolean contains(Point2D p) + { + return getBounds().contains(p); + } + + + /** + * List edges connected to the same side + * + * @param edge + * @return ordered list of edges + */ + private List getEdgesOnSameSide(IEdge edge) { + // Step 1 : look for edges + List result = new ArrayList(); + Direction d = edge.getDirection(this); + if (d == null) return result; + Direction cardinalDirectionToSearch = d.getNearestCardinalDirection(); + for (IEdge anEdge : getConnectedEdges()) { + Direction edgeDirection = anEdge.getDirection(this); + Direction nearestCardinalDirection = edgeDirection.getNearestCardinalDirection(); + if (cardinalDirectionToSearch.equals(nearestCardinalDirection)) { + result.add(anEdge); + } + } + // Step 2: sort them + if (Direction.NORTH.equals(cardinalDirectionToSearch) || Direction.SOUTH.equals(cardinalDirectionToSearch)) { + Collections.sort(result, new Comparator() { + @Override + public int compare(IEdge e1, IEdge e2) { + Direction d1 = e1.getDirection(RectangularNode.this); + Direction d2 = e2.getDirection(RectangularNode.this); + double x1 = d1.getX(); + double x2 = d2.getX(); + return Double.compare(x1, x2); + } + }); + } + if (Direction.EAST.equals(cardinalDirectionToSearch) || Direction.WEST.equals(cardinalDirectionToSearch)) { + Collections.sort(result, new Comparator() { + @Override + public int compare(IEdge e1, IEdge e2) { + Direction d1 = e1.getDirection(RectangularNode.this); + Direction d2 = e2.getDirection(RectangularNode.this); + double y1 = d1.getY(); + double y2 = d2.getY(); + return Double.compare(y1, y2); + } + }); + } + return result; + } + + + + public Point2D getConnectionPoint(IEdge e) + { + List edgesOnSameSide = getEdgesOnSameSide(e); + int position = edgesOnSameSide.indexOf(e); + int size = edgesOnSameSide.size(); + + Rectangle2D b = getBounds(); + + double x = b.getCenterX(); + double y = b.getCenterY(); + + Direction d = e.getDirection(this); + + Direction nearestCardinalDirection = d.getNearestCardinalDirection(); + if (Direction.NORTH.equals(nearestCardinalDirection)) { + x = b.getMaxX() - (b.getWidth() / (size + 1)) * (position + 1); + y = b.getMaxY(); + } + if (Direction.SOUTH.equals(nearestCardinalDirection)) { + x = b.getMaxX() - (b.getWidth() / (size + 1)) * (position + 1); + y = b.getMinY(); + } + if (Direction.EAST.equals(nearestCardinalDirection)) { + x = b.getMinX(); + y = b.getMaxY() - (b.getHeight() / (size + 1)) * (position + 1); + } + if (Direction.WEST.equals(nearestCardinalDirection)) { + x = b.getMaxX(); + y = b.getMaxY() - (b.getHeight() / (size + 1)) * (position + 1); + } + return new Point2D.Double(x, y); + } + + + + + + + + + public Shape getShape() + { + return getBounds(); + } + + @Override + public void draw(Graphics2D g2) + { + Shape shape = getShape(); + Color oldColor = g2.getColor(); + g2.translate(SHADOW_GAP, SHADOW_GAP); + g2.setColor(SHADOW_COLOR); + g2.fill(shape); + g2.translate(-SHADOW_GAP, -SHADOW_GAP); + g2.setColor(BACKGROUND_COLOR); + g2.fill(shape); + g2.setColor(oldColor); + } + + + + + + + + + private static final Color SHADOW_COLOR = new Color(210,210,210); + protected static Color BACKGROUND_COLOR = UIManager.getColor("TextPane.background"); + public static final double SHADOW_GAP = 4; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/RectangularNodeBeanInfo.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/RectangularNodeBeanInfo.java new file mode 100644 index 0000000..079181f --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/node/RectangularNodeBeanInfo.java @@ -0,0 +1,41 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.node; + +import java.beans.PropertyDescriptor; +import java.beans.SimpleBeanInfo; + +/** + * The bean info for the RectangularNode type. This hides the bounds property. + */ +public class RectangularNodeBeanInfo extends SimpleBeanInfo +{ + /* + * (non-Javadoc) + * + * @see java.beans.BeanInfo#getPropertyDescriptors() + */ + public PropertyDescriptor[] getPropertyDescriptors() + { + return new PropertyDescriptor[] {}; + } +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/package.html b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/package.html new file mode 100644 index 0000000..4fbf9bd --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/package.html @@ -0,0 +1,100 @@ + + + + + + + + +

Architectural Notes for the Diagram Framework

+ +

A Violet graph consists of an unordered collection of nodes and an + unordered collection of directed edges. Each node and edge can be + customized by setting JavaBeans properties. In addition, each node has a + Point2D location and an ordered collection of child nodes. There + is no other persistent information.

+ +

A graph is saved using an XMLEncoder, by writing statements + that add nodes, add children, connect nodes with edges, and set node and + edge properties. An XMLDecoder reconstructs the graph by + executing these statements.

+ +

After a graph is read in, and after each editing mutation, the graph is + laid out. The layout may modify the positions, bounds, and z-order of + nodes, but it should not make structural modifications. In particular, + layout should not change node or edge properties, and it should not add or + remove child nodes.

+ +

The Graph class simply calls layout on each top-level + node. Parents are responsible for laying out their children.

+ +

After layout is complete, the graph is drawn. Nodes are opaque, so + there is a need for controlling the order in which they are drawn. The + Graph class calls draw on all nodes in increasing + z-order, first drawing the nodes with z = 0, then with z = 1, etc., and + then calls draw on all edges.

+ +

A graph can be mutated by the following editing operations:

+ +
    +
  • A node can be added at a suggested Point2D location. The + add request is offered to all nodes whose bounds contain the given + location, in decreasing z-order, until one node accepts the request. + That node can then carry out arbitrary processing. (Nodes that do not + accept the request should not carry out any processing.) If no node + accepts the add request, the new node is added as a top-level node. (A + Graph subclass can refine this.)
  • + +
  • An edge can be added at a suggested Point2D start and end + location. The Graph class looks up the points with the highest + z-order containing the end points and sets them as the end points to the + edge. Then it notifies the start node of the add request. The start node + can carry out arbitrary processing at this point. If the start node + accepts the request, the edge is added.
  • + +
  • A node or edge can be deleted. All nodes are notified and can carry + out arbitrary actions, in particular, delete other nodes or edges. After + all node actions have completed, all children of deleted nodes are + unlinked from their parents (but not deleted), all Node-valued + node properties that point to deleted nodes are set to null, + and all edges that are incident with deleted nodes are deleted.
  • + +
  • A node position can be moved.
  • + +
  • A set of nodes can be offered to a node for pasting. Typically, the + node would incorporate them as children.
  • +
+ +

Note that edges are always innocent bystanders. They do not participate + in the decision making, and they cannot be moved.

+ +

The graph mutations will give rise to a sequence of atomic + operations:

+ +
    +
  • adding or removind a node or edge
  • + +
  • adding or removing a child node at a given position
  • + +
  • moving a node position
  • + +
  • changing a node or edge property
  • +
+ +

The UI controller should call the editing operations, not the atomic + operations. The atomic operations are intended for system-level tasks such + as undo, load, and network synchronization.

+ +

When any atomic operations occurs, the graph fires events to registered + GraphModificationListener instances. The undo mechanism is one + such listener. It simply reverses the atomic operations, each of which is + trivially reversible.

+ +

Implementors of graph and node subclasses need to be mindful of the + event firing. The Graph and AbstractNode class fire the + correct events when adding or removing nodes or children, and when moving + nodes through setBounds or translate. However, when + setting graph properties, the events need to be generated manually.

+ + \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/ArrowHead.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/ArrowHead.java new file mode 100644 index 0000000..76b1210 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/ArrowHead.java @@ -0,0 +1,136 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.property; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; + +import com.horstmann.violet.framework.util.SerializableEnumeration; + +/** + * This class defines arrow heads of various shapes. + */ +public class ArrowHead extends SerializableEnumeration +{ + + /** + * Draws the arrowhead. + * + * @param g2 the graphics context + * @param p a point on the axis of the arrow head + * @param q the end point of the arrow head + */ + public void draw(Graphics2D g2, Point2D p, Point2D q) + { + GeneralPath path = getPath(p, q); + Color oldColor = g2.getColor(); + if (this != V && this != HALF_V && this != NONE) + { + if (this == BLACK_DIAMOND || this == BLACK_TRIANGLE) + g2.setColor(Color.BLACK); + else + g2.setColor(Color.WHITE); + g2.fill(path); + } + + g2.setColor(oldColor); + g2.draw(path); + } + + /** + * Gets the path of the arrowhead + * + * @param p a point on the axis of the arrow head + * @param q the end point of the arrow head + * @return the path + */ + public GeneralPath getPath(Point2D p, Point2D q) + { + GeneralPath path = new GeneralPath(); + if (this == NONE) return path; + final double ARROW_ANGLE = Math.PI / 6; + final double ARROW_LENGTH = 10; + + double dx = q.getX() - p.getX(); + double dy = q.getY() - p.getY(); + double angle = Math.atan2(dy, dx); + double x1 = q.getX() - ARROW_LENGTH * Math.cos(angle + ARROW_ANGLE); + double y1 = q.getY() - ARROW_LENGTH * Math.sin(angle + ARROW_ANGLE); + double x2 = q.getX() - ARROW_LENGTH * Math.cos(angle - ARROW_ANGLE); + double y2 = q.getY() - ARROW_LENGTH * Math.sin(angle - ARROW_ANGLE); + + if (this == V) + { + path.moveTo((float) x1, (float) y1); + path.lineTo((float) q.getX(), (float) q.getY()); + path.lineTo((float) x2, (float) y2); + path.lineTo((float) q.getX(), (float) q.getY()); + path.lineTo((float) x1, (float) y1); + path.closePath(); + } + else if (this == TRIANGLE || this == BLACK_TRIANGLE) + { + path.moveTo((float) q.getX(), (float) q.getY()); + path.lineTo((float) x1, (float) y1); + path.lineTo((float) x2, (float) y2); + path.closePath(); + } + else if (this == DIAMOND || this == BLACK_DIAMOND) + { + path.moveTo((float) q.getX(), (float) q.getY()); + path.lineTo((float) x1, (float) y1); + double x3 = x2 - ARROW_LENGTH * Math.cos(angle + ARROW_ANGLE); + double y3 = y2 - ARROW_LENGTH * Math.sin(angle + ARROW_ANGLE); + path.lineTo((float) x3, (float) y3); + path.lineTo((float) x2, (float) y2); + path.closePath(); + } + return path; + } + + /** Array head type : this head has no shape */ + public static final ArrowHead NONE = new ArrowHead(); + + /** Array head type : this head is a triangle */ + public static final ArrowHead TRIANGLE = new ArrowHead(); + + /** Array head type : this head is a black filled triangle */ + public static final ArrowHead BLACK_TRIANGLE = new ArrowHead(); + + /** Array head type : this head is a V */ + public static final ArrowHead V = new ArrowHead(); + + /** Array head type : this head is a half V */ + public static final ArrowHead HALF_V = new ArrowHead(); + + /** Array head type : this head is a diamond */ + public static final ArrowHead DIAMOND = new ArrowHead(); + + /** Array head type : this head is black filled diamond */ + public static final ArrowHead BLACK_DIAMOND = new ArrowHead(); + + /** Internal Java UID */ + private static final long serialVersionUID = -3824887997763775890L; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/BentStyle.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/BentStyle.java new file mode 100644 index 0000000..f467059 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/BentStyle.java @@ -0,0 +1,278 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.property; + +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; + +import com.horstmann.violet.framework.util.SerializableEnumeration; + +/** + * A style for a segmented line that indicates the number and sequence of bends. + */ +public class BentStyle extends SerializableEnumeration +{ + + /** + * Default constructor + */ + private BentStyle() + { + } + + /** + * Gets the four connecting points at which a bent line connects to a rectangle. + */ + private Point2D[] connectionPoints(Rectangle2D r) + { + Point2D[] a = new Point2D[4]; + a[0] = new Point2D.Double(r.getX(), r.getCenterY()); + a[1] = new Point2D.Double(r.getMaxX(), r.getCenterY()); + a[2] = new Point2D.Double(r.getCenterX(), r.getY()); + a[3] = new Point2D.Double(r.getCenterX(), r.getMaxY()); + return a; + } + + /** + * Gets the points at which a line joining two rectangles is bent according to a bent style. + * + * @param startingRectangle the starting rectangle + * @param endingRectangle the ending rectangle + * @return an array list of points at which to bend the segmented line joining the two rectangles + */ + public ArrayList getPath(Point2D startingPoint, Point2D endingPoint) + { + ArrayList r = null; + + // Try to get current path + if (this == STRAIGHT) r = getStraightPath(startingPoint, endingPoint); + else if (this == HV) r = getHVPath(startingPoint, endingPoint); + else if (this == VH) r = getVHPath(startingPoint, endingPoint); + else if (this == HVH) r = getHVHPath(startingPoint, endingPoint); + else if (this == VHV) r = getVHVPath(startingPoint, endingPoint); + if (r != null) return r; + + // Try to inverse path + if (startingPoint.equals(endingPoint)) r = getSelfPath(startingPoint); + else if (this == HVH) r = getVHVPath(startingPoint, endingPoint); + else if (this == VHV) r = getHVHPath(startingPoint, endingPoint); + else if (this == HV) r = getVHPath(startingPoint, endingPoint); + else if (this == VH) r = getHVPath(startingPoint, endingPoint); + if (r != null) return r; + + // Return default path + return getStraightPath(startingPoint, endingPoint); + } + + /** + * Gets an Vertical-Horizontal-Vertival path + * + * @param startingRectangle + * @param endingRectangle + * @return an array list of points + */ + private ArrayList getVHVPath(Point2D startingPoint, Point2D endingPoint) + { + + ArrayList r = new ArrayList(); + double x1 = startingPoint.getX(); + double x2 = endingPoint.getX(); + double y1; + double y2; + if (startingPoint.getY() + 2 * MIN_SEGMENT <= endingPoint.getY()) + { + y1 = startingPoint.getY(); + y2 = endingPoint.getY(); + } + else if (endingPoint.getY() + 2 * MIN_SEGMENT <= startingPoint.getY()) + { + y1 = startingPoint.getY(); + y2 = endingPoint.getY(); + + } + else return null; + if (Math.abs(x1 - x2) <= MIN_SEGMENT) + { + r.add(new Point2D.Double(x2, y1)); + r.add(new Point2D.Double(x2, y2)); + } + else + { + r.add(new Point2D.Double(x1, y1)); + r.add(new Point2D.Double(x1, (y1 + y2) / 2)); + r.add(new Point2D.Double(x2, (y1 + y2) / 2)); + r.add(new Point2D.Double(x2, y2)); + } + return r; + } + + /** + * Gets an Horizontal-Vertical-Horizontal path + * + * @param startingRectangle + * @param endingRectangle + * @return an array list of points + */ + private ArrayList getHVHPath(Point2D startingPoint, Point2D endingPoint) + { + ArrayList r = new ArrayList(); + double x1; + double x2; + double y1 = startingPoint.getY(); + double y2 = endingPoint.getY(); + if (startingPoint.getX() + 2 * MIN_SEGMENT <= endingPoint.getX()) + { + x1 = startingPoint.getX(); + x2 = endingPoint.getX(); + } + else if (endingPoint.getX() + 2 * MIN_SEGMENT <= startingPoint.getX()) + { + x1 = startingPoint.getX(); + x2 = endingPoint.getX(); + } + else return null; + if (Math.abs(y1 - y2) <= MIN_SEGMENT) + { + r.add(new Point2D.Double(x1, y2)); + r.add(new Point2D.Double(x2, y2)); + } + else + { + r.add(new Point2D.Double(x1, y1)); + r.add(new Point2D.Double((x1 + x2) / 2, y1)); + r.add(new Point2D.Double((x1 + x2) / 2, y2)); + r.add(new Point2D.Double(x2, y2)); + } + return r; + } + + /** + * Gets a Vertical-Horizontal path + * + * @param startingRectangle + * @param endingRectangle + * @return an array list of points + */ + private ArrayList getVHPath(Point2D startingPoint, Point2D endingPoint) + { + ArrayList r = new ArrayList(); + double x1 = startingPoint.getX(); + double x2; + double y1; + double y2 = endingPoint.getY(); + if (x1 + MIN_SEGMENT <= endingPoint.getX()) x2 = endingPoint.getX(); + else if (x1 - MIN_SEGMENT >= endingPoint.getX()) x2 = endingPoint.getX(); + else return null; + if (y2 + MIN_SEGMENT <= startingPoint.getY()) y1 = startingPoint.getY(); + else if (y2 - MIN_SEGMENT >= startingPoint.getY()) y1 = startingPoint.getY(); + else return null; + r.add(new Point2D.Double(x1, y1)); + r.add(new Point2D.Double(x1, y2)); + r.add(new Point2D.Double(x2, y2)); + return r; + } + + /** + * Gets an Horizontal-Vertical path + * + * @param startingRectangle + * @param endingRectangle + * @return an array list of points + */ + private ArrayList getHVPath(Point2D startingPoint, Point2D endingPoint) + { + ArrayList r = new ArrayList(); + double x1; + double x2 = endingPoint.getX(); + double y1 = startingPoint.getY(); + double y2; + if (x2 + MIN_SEGMENT <= startingPoint.getX()) x1 = startingPoint.getX(); + else if (x2 - MIN_SEGMENT >= startingPoint.getX()) x1 = startingPoint.getX(); + else return null; + if (y1 + MIN_SEGMENT <= endingPoint.getY()) y2 = endingPoint.getY(); + else if (y1 - MIN_SEGMENT >= endingPoint.getY()) y2 = endingPoint.getY(); + else return null; + r.add(new Point2D.Double(x1, y1)); + r.add(new Point2D.Double(x2, y1)); + r.add(new Point2D.Double(x2, y2)); + return r; + } + + /** + * Gets a straight path + * + * @param startingRectangle + * @param endingRectangle + * @return an array list of points + */ + private ArrayList getStraightPath(Point2D startingPoint, Point2D endingPoint) + { + ArrayList r = new ArrayList(); + r.add(startingPoint); + r.add(endingPoint); + return r; + } + + /** + * Gets the points at which a line joining two rectangles is bent according to a bent style. + * + * @param s the starting and ending rectangle + */ + private ArrayList getSelfPath(Point2D p) + { + ArrayList r = new ArrayList(); + double x1 = p.getX() + SELF_WIDTH * 3 / 4; + double y1 = p.getY(); + double y2 = p.getY() - SELF_HEIGHT; + double x2 = p.getX() + 2 * SELF_WIDTH; + double y3 = p.getY() + SELF_HEIGHT / 4; + double x3 = p.getX() + SELF_WIDTH; + r.add(new Point2D.Double(x1, y1)); + r.add(new Point2D.Double(x1, y2)); + r.add(new Point2D.Double(x2, y2)); + r.add(new Point2D.Double(x2, y3)); + r.add(new Point2D.Double(x3, y3)); + return r; + } + + /** minimum segment size */ + private static final int MIN_SEGMENT = 10; + /** width on self path */ + private static final int SELF_WIDTH = 30; + /** height on self path */ + private static final int SELF_HEIGHT = 25; + + /** straight bent style */ + public static final BentStyle STRAIGHT = new BentStyle(); + /** Horizontal-Vertical bent style */ + public static final BentStyle HV = new BentStyle(); + /** Vertical-Horizontal bent style */ + public static final BentStyle VH = new BentStyle(); + /** Horizontal-Vertical-Horizontal bent style */ + public static final BentStyle HVH = new BentStyle(); + /** Vertical-Horizontal-Vertical bent style */ + public static final BentStyle VHV = new BentStyle(); + /** Automatic bent style */ + public static final BentStyle AUTO = new BentStyle(); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/ChoiceList.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/ChoiceList.java new file mode 100644 index 0000000..b374d6d --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/ChoiceList.java @@ -0,0 +1,91 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.property; + +/** + * Represents a static list for user choices. + * + * @author Alexandre de Pellegrin + * + */ +public class ChoiceList +{ + /** + * Default constructor + * + * @param list items to select + */ + public ChoiceList(String[] list) + { + this.list = list; + this.selectedPos = DEFAULT_SELECT; + } + + /** + * @return the item list + */ + public String[] getList() + { + return list; + } + + /** + * Selects an item in the list + * + * @param selected + */ + public void setSelectedItem(String selected) + { + for (int i = 0; i < list.length; i++) + { + if (list[i].equals(selected)) + { + selectedPos = i; + return; + } + } + } + + /** + * @return current selected item + */ + public String getSelectedItem() + { + return list[selectedPos]; + } + + /** + * Item list for selection + */ + private String[] list; + + /** + * Index for current selected item + */ + private int selectedPos; + + /** + * Default selected item index + */ + private static final int DEFAULT_SELECT = 0; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/LineStyle.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/LineStyle.java new file mode 100644 index 0000000..ac5780f --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/LineStyle.java @@ -0,0 +1,93 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.property; + +import java.awt.BasicStroke; +import java.awt.Stroke; + +import com.horstmann.violet.framework.util.SerializableEnumeration; + +/** + * This class defines line styles of various shapes. + */ +public class LineStyle extends SerializableEnumeration +{ + /** + * Private constructor. Note that this class is nearly construct as a singleton. + */ + private LineStyle() + { + } + + /** + * Gets a stroke with which to draw this line style. + * + * @return the stroke object that strokes this line style + */ + public Stroke getStroke() + { + if (this == DOTTED) return getDottedStroke(); + return getSolidStroke(); + } + + /** + * @return a dotted stroke + */ + private Stroke getDottedStroke() + { + if (dottedStroke == null) + { + float[] dash = new float[] + { + 3.0f, + 3.0f + }; + dottedStroke = new BasicStroke(1.0f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 10.0f, dash, 0.0f); + } + return dottedStroke; + } + + /** + * @return a solid stroke + */ + private Stroke getSolidStroke() + { + if (solidStroke == null) + { + solidStroke = new BasicStroke(); + } + return solidStroke; + } + + /** a solid stroke */ + private transient Stroke solidStroke; + + /** a dotted stroke */ + private transient Stroke dottedStroke; + + /** The unique solid linestyle instance */ + public static final LineStyle SOLID = new LineStyle(); + + /** The unique dotted linestyle instance */ + public static final LineStyle DOTTED = new LineStyle(); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/MultiLineString.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/MultiLineString.java new file mode 100644 index 0000000..f1d84e0 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/abstracts/property/MultiLineString.java @@ -0,0 +1,297 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.abstracts.property; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.io.Serializable; +import java.util.Arrays; +import java.util.List; +import java.util.StringTokenizer; + +import javax.swing.JLabel; + +import com.thoughtworks.xstream.annotations.XStreamAsAttribute; + +/** + * A string that can extend over multiple lines. + */ +public class MultiLineString implements Serializable, Cloneable +{ + /** + * Constructs an empty, centered, normal size multiline string that is not underlined. + */ + public MultiLineString() + { + text = ""; + justification = CENTER; + size = NORMAL; + underlined = false; + } + + /** + * Sets the value of the text property. + * + * @param newValue the text of the multiline string + */ + public void setText(String newValue) + { + text = newValue; + setLabelText(); + isBoundsDirty = true; + } + + /** + * Gets the value of the text property. + * + * @return the text of the multiline string + */ + public String getText() + { + return text; + } + + /** + * Sets the value of the justification property. + * + * @param newValue the justification, one of LEFT, CENTER, RIGHT + */ + public void setJustification(int newValue) + { + justification = newValue; + setLabelText(); + isBoundsDirty = true; + } + + /** + * Gets the value of the justification property. + * + * @return the justification, one of LEFT, CENTER, RIGHT + */ + public int getJustification() + { + return justification; + } + + /** + * Gets the value of the underlined property. + * + * @return true if the text is underlined + */ + public boolean isUnderlined() + { + return underlined; + } + + /** + * Sets the value of the underlined property. + * + * @param newValue true to underline the text + */ + public void setUnderlined(boolean newValue) + { + underlined = newValue; + setLabelText(); + isBoundsDirty = true; + } + + /** + * Sets the value of the size property. + * + * @param newValue the size, one of SMALL, NORMAL, LARGE + */ + public void setSize(int newValue) + { + size = newValue; + setLabelText(); + isBoundsDirty = true; + } + + /** + * Gets the value of the size property. + * + * @return the size, one of SMALL, NORMAL, LARGE + */ + public int getSize() + { + return size; + } + + public String toString() + { + return text.replace('\n', '|'); + } + + private void setLabelText() + { + StringBuffer prefix = new StringBuffer(); + StringBuffer suffix = new StringBuffer(); + StringBuffer htmlText = new StringBuffer(); + prefix.append(" "); + suffix.insert(0, " "); + if (underlined) + { + prefix.append(""); + suffix.insert(0, ""); + } + if (size == LARGE) + { + prefix.append(""); + suffix.insert(0, ""); + } + if (size == SMALL) + { + prefix.append(""); + suffix.insert(0, ""); + } + htmlText.append(""); + StringTokenizer tokenizer = new StringTokenizer(text, "\n"); + boolean first = true; + while (tokenizer.hasMoreTokens()) + { + if (first) first = false; + else htmlText.append("
"); + htmlText.append(prefix); + htmlText.append(tokenizer.nextToken()); + htmlText.append(suffix); + } + htmlText.append(""); + + // replace any < that are not followed by {u, i, b, tt, font, br} with < + + List dontReplace = Arrays.asList(new String[] + { + "u", + "i", + "b", + "tt", + "font", + "br" + }); + + int ltpos = 0; + while (ltpos != -1) + { + ltpos = htmlText.indexOf("<", ltpos + 1); + if (ltpos != -1 && !(ltpos + 1 < htmlText.length() && htmlText.charAt(ltpos + 1) == '/')) + { + int end = ltpos + 1; + while (end < htmlText.length() && Character.isLetter(htmlText.charAt(end))) + end++; + if (!dontReplace.contains(htmlText.substring(ltpos + 1, end))) htmlText.replace(ltpos, ltpos + 1, "<"); + } + } + + getLabel().setText(htmlText.toString()); + if (justification == LEFT) getLabel().setHorizontalAlignment(JLabel.LEFT); + else if (justification == CENTER) getLabel().setHorizontalAlignment(JLabel.CENTER); + else if (justification == RIGHT) getLabel().setHorizontalAlignment(JLabel.RIGHT); + + } + + + /** + * Gets the bounding rectangle for this multiline string. + * + * @param g2 the graphics context + * @return the bounding rectangle (with top left corner (0,0)) + */ + private Rectangle2D getBounds(Graphics2D g2) + { + setLabelText(); + getLabel().setFont(g2.getFont()); + getLabel().validate(); + if (text.length() == 0) return new Rectangle2D.Double(0, 0, 0, 0); + Dimension dim = getLabel().getPreferredSize(); + return new Rectangle2D.Double(0, 0, dim.getWidth(), dim.getHeight()); + } + + /** + * Gets the bounding rectangle for this multiline string. + * + * @return the bounding rectangle (with top left corner (0,0)) + */ + public Rectangle2D getBounds() + { + if (this.isBoundsDirty || this.bounds == null) + { + BufferedImage image = new BufferedImage(1000, 1000, BufferedImage.TYPE_INT_RGB); + Graphics2D g2 = (Graphics2D) image.getGraphics(); + this.bounds = getBounds(g2); + this.isBoundsDirty = false; + } + return this.bounds; + } + + /** + * Draws this multiline string inside a given rectangle + * + * @param g2 the graphics context + * @param r the rectangle into which to place this multiline string + */ + public void draw(Graphics2D g2, Rectangle2D r) + { + getLabel().setFont(g2.getFont()); + getLabel().setBounds(0, 0, (int) r.getWidth(), (int) r.getHeight()); + g2.translate(r.getX(), r.getY()); + getLabel().paint(g2); + g2.translate(-r.getX(), -r.getY()); + } + + public MultiLineString clone() + { + MultiLineString cloned = new MultiLineString(); + cloned.text = text; + cloned.justification = justification; + cloned.size = size; + cloned.underlined = underlined; + cloned.setLabelText(); + return cloned; + } + + private JLabel getLabel() { + if (this.label == null) { + this.label = new JLabel(); + } + return this.label; + } + + public static final int LEFT = 0; + public static final int CENTER = 1; + public static final int RIGHT = 2; + public static final int LARGE = 3; + public static final int NORMAL = 4; + public static final int SMALL = 5; + + private String text; + @XStreamAsAttribute + private int justification; + @XStreamAsAttribute + private int size; + @XStreamAsAttribute + private boolean underlined; + private transient JLabel label; + private transient boolean isBoundsDirty = true; + private transient Rectangle2D bounds; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/DiagramLink.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/DiagramLink.java new file mode 100644 index 0000000..a51c833 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/DiagramLink.java @@ -0,0 +1,132 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.common; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URL; + +import com.horstmann.violet.framework.file.IFile; +import com.horstmann.violet.framework.file.LocalFile; + +/** + * This class is a link to a physical file. It is used to perform links between diagrams. Tested successfully in applet mode + * + * @author Alexandre de Pellegrin + * + */ +public class DiagramLink +{ + + /** + * The file + */ + private IFile file; + + /** + * Flag to indicate if file needs to be opened + */ + private Boolean openFlag = new Boolean(false); + + public DiagramLink() + { + super(); + } + + /** + * Get linked file + * + * @return + */ + public IFile getFile() + { + return this.file; + } + + /** + * Set file a link + * + * @param path + */ + public void setFile(IFile file) + { + this.file = file; + } + + /** + * Return true if file needs to be opened + * + * @return + */ + public Boolean getOpenFlag() + { + return this.openFlag; + } + + /** + * Set flag to indicate if file needs to be opened + * + * @param openFlag + */ + public void setOpenFlag(Boolean flag) + { + this.openFlag = flag; + } + + /** + * @deprecated kept for compatibility + */ + public URL getURL() + { + if (this.file == null) { + return null; + } + try { + LocalFile localFile = new LocalFile(this.file); + File fileImpl = localFile.toFile(); + if (fileImpl.exists()) { + return fileImpl.toURL(); + } + return null; + } catch (IOException e) { + throw new RuntimeException (e); + } + } + + /** + * @deprecated kept for compatibility + */ + public void setURL(URL url) + { + try { + URI uri = url.toURI(); + File fileImpl = new File(uri); + if (fileImpl.exists()) { + this.file = new LocalFile(fileImpl); + } + } catch (Exception e) { + throw new RuntimeException (e); + } + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/DiagramLinkNode.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/DiagramLinkNode.java new file mode 100644 index 0000000..192002e --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/DiagramLinkNode.java @@ -0,0 +1,167 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.common; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Locale; +import java.util.ResourceBundle; + +import com.horstmann.violet.framework.injection.resources.ResourceBundleConstant; +import com.horstmann.violet.product.diagram.abstracts.node.RectangularNode; +import com.horstmann.violet.product.diagram.abstracts.property.MultiLineString; + +/** + * An link node in a diagram. + */ +public class DiagramLinkNode extends RectangularNode +{ + + /** + * Construct a link ode with a default size + */ + public DiagramLinkNode() + { + this.label = new MultiLineString(); + } + + @Override + public Rectangle2D getBounds() + { + Rectangle2D top = new Rectangle2D.Double(0, 0, DEFAULT_WIDTH, DEFAULT_HEIGHT); + Rectangle2D bot = getLabel().getBounds(); + Point2D currentLocation = getLocation(); + double x = currentLocation.getX(); + double y = currentLocation.getY(); + double w = Math.max(top.getWidth(), bot.getWidth()); + double h = top.getHeight() + bot.getHeight(); + Rectangle2D currentBounds = new Rectangle2D.Double(x, y, w, h); + Rectangle2D snapperBounds = getGraph().getGrid().snap(currentBounds); + return snapperBounds; + } + + + public void draw(Graphics2D g2) + { + Rectangle2D bounds = getBounds(); + GeneralPath path1 = new GeneralPath(); + float x1 = (float) (bounds.getCenterX() - DEFAULT_SIZE / 2 + DEFAULT_SIZE / 8); + float y1 = (float) (bounds.getCenterY() - DEFAULT_SIZE / 2); + float x2 = x1 + DEFAULT_SIZE / 4; + float y2 = y1; + float x3 = x2 + DEFAULT_SIZE / 4; + float y3 = y2 + DEFAULT_SIZE / 4; + float x4 = x3; + float y4 = y3 + DEFAULT_SIZE / 4; + float x5 = x2; + float y5 = y4 + DEFAULT_SIZE / 4; + float x6 = x1; + float y6 = y5; + path1.moveTo(x1, y1); + path1.lineTo(x2, y2); + path1.lineTo(x3, y3); + path1.lineTo(x4, y4); + path1.lineTo(x5, y5); + path1.lineTo(x6, y6); + Rectangle2D rec1 = new Rectangle2D.Float(); + rec1.setRect(x1 - DEFAULT_SIZE / 2, y1 - DEFAULT_SIZE / 12, DEFAULT_SIZE / 2, y6 - y1 + 2 * DEFAULT_SIZE / 12); + x1 = (float) (2 * bounds.getCenterX() - x1); + x2 = (float) (2 * bounds.getCenterX() - x2); + x3 = (float) (2 * bounds.getCenterX() - x3); + x4 = (float) (2 * bounds.getCenterX() - x4); + x5 = (float) (2 * bounds.getCenterX() - x5); + x6 = (float) (2 * bounds.getCenterX() - x6); + GeneralPath path2 = new GeneralPath(); + path2.moveTo(x1, y1); + path2.lineTo(x2, y2); + path2.lineTo(x3, y3); + path2.lineTo(x4, y4); + path2.lineTo(x5, y5); + path2.lineTo(x6, y6); + Rectangle2D rec2 = new Rectangle2D.Float(); + rec2.setRect(x1, y1 - DEFAULT_SIZE / 12, DEFAULT_SIZE / 2, y6 - y1 + 2 * DEFAULT_SIZE / 12); + + Color backupcolor = g2.getColor(); + g2.setColor(Color.WHITE); + g2.fill(rec1); + g2.fill(rec2); + g2.setColor(backupcolor); + g2.draw(path1); + g2.draw(path2); + g2.draw(rec1); + g2.draw(rec2); + + // Draw name + Rectangle2D bot = getLabel().getBounds(); + + Rectangle2D namebox = new Rectangle2D.Double(bounds.getX() + +(bounds.getWidth() - bot.getWidth()) / 2, bounds.getY() + + bounds.getHeight() - bot.getHeight(), bot.getWidth(), bot.getHeight()); + getLabel().draw(g2, namebox); + } + + public DiagramLink getDiagramLink() + { + return diagramLink; + } + + public void setDiagramLink(DiagramLink fLink) + { + this.diagramLink = fLink; + } + + private MultiLineString getLabel() + { + if (this.label == null) + { + this.label = new MultiLineString(); + } + DiagramLink dl = this.getDiagramLink(); + if (dl != null && dl.getFile() != null) + { + StringBuffer linktext = new StringBuffer() + .append( + ResourceBundle.getBundle(ResourceBundleConstant.OTHER_STRINGS, Locale.getDefault()).getString( + "file.link.text")).append(" ").append(dl.getFile().getFilename()); + this.label.setText(linktext.toString()); + } + return this.label; + } + + /** Label */ + private MultiLineString label; + + /** Linked diagram */ + private DiagramLink diagramLink; + + /** Default bounding rectangle width */ + private static int DEFAULT_WIDTH = 48; + + /** Default bounding rectangle height */ + private static int DEFAULT_HEIGHT = 32; + + /** Default tool icon size */ + private static int DEFAULT_SIZE = 32; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/ImageNode.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/ImageNode.java new file mode 100644 index 0000000..e5a8f00 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/ImageNode.java @@ -0,0 +1,237 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.common; + +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Shape; +import java.awt.Toolkit; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.awt.image.MemoryImageSource; +import java.awt.image.PixelGrabber; +import java.util.StringTokenizer; + +import javax.swing.ImageIcon; + +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.RectangularNode; +import com.horstmann.violet.product.diagram.abstracts.property.MultiLineString; + +/** + * A node in a diagram represented by an image + */ +public class ImageNode extends RectangularNode +{ + /** + * Default construct a note node with a default size and color + */ + public ImageNode(Image img) + { + text = new MultiLineString(); + text.setJustification(MultiLineString.RIGHT); + this.setImage(img); + } + + /** + * For internal use only. + */ + public ImageNode() + { + text = new MultiLineString(); + text.setJustification(MultiLineString.RIGHT); + } + + /** + * Sets current image + * + * @param img + */ + public void setImage(Image img) + { + this.imageIcon = new ImageIcon(img); + } + + @Override + public Rectangle2D getBounds() + { + Rectangle2D b = text.getBounds(); + Point2D currentLocation = getLocation(); + double x = currentLocation.getX(); + double y = currentLocation.getY(); + double w = Math.max(b.getWidth(), this.imageIcon.getIconWidth()); + double h = b.getHeight() + this.imageIcon.getIconHeight(); + Rectangle2D currentBounds = new Rectangle2D.Double(x, y, w, h); + Rectangle2D snapperBounds = getGraph().getGrid().snap(currentBounds); + return snapperBounds; + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.product.diagram.abstracts.AbstractNode#checkRemoveEdge(com.horstmann.violet.product.diagram.abstracts.Edge) + */ + public void removeConnection(IEdge e) + { + if (e.getStart() == this) getGraph().removeNode(e.getEnd()); + } + + + /** + * Gets the value of the text property. + * + * @return the text inside the note + */ + public MultiLineString getText() + { + return text; + } + + /** + * Sets the value of the text property. + * + * @param newValue the text inside the note + */ + public void setText(MultiLineString newValue) + { + text = newValue; + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.product.diagram.abstracts.AbstractNode#draw(java.awt.Graphics2D) + */ + public void draw(Graphics2D g2) + { + Rectangle2D bounds = getBounds(); + g2.drawImage(this.imageIcon.getImage(), (int) bounds.getCenterX() - this.imageIcon.getIconWidth() / 2, (int) bounds.getY(), + this.imageIcon.getImageObserver()); + Rectangle2D b = text.getBounds(); + Rectangle2D textBounds = new Rectangle2D.Double(bounds.getX(), bounds.getY() + this.imageIcon.getIconHeight(), + b.getWidth(), b.getHeight()); + text.draw(g2, textBounds); + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.product.diagram.abstracts.RectangularNode#getShape() + */ + public Shape getShape() + { + Rectangle2D bounds = getBounds(); + GeneralPath path = new GeneralPath(); + path.moveTo((float) bounds.getX(), (float) bounds.getY()); + path.lineTo((float) bounds.getMaxX(), (float) bounds.getY()); + path.lineTo((float) bounds.getMaxX(), (float) bounds.getY()); + path.lineTo((float) bounds.getMaxX(), (float) bounds.getMaxY()); + path.lineTo((float) bounds.getX(), (float) bounds.getMaxY()); + path.closePath(); + return path; + } + + /** + * This method should be kept as private as long as it is used for serialization purpose + * + * @return image content as an array + * @throws InterruptedException + */ + public String getImageContent() throws InterruptedException + { + Image img = this.imageIcon.getImage(); + int width = this.imageIcon.getIconWidth(); + int height = this.imageIcon.getIconHeight(); + int[] pixels = new int[width * height]; + PixelGrabber pg = new PixelGrabber(img, 0, 0, width, height, pixels, 0, width); + pg.grabPixels(); + StringBuilder result = new StringBuilder(); + for (int i : pixels) + { + result.append(i).append(PIXEL_SEPARATOR); + } + result.deleteCharAt(result.length() - 1); + return result.toString(); + } + + /** + * @return current image width + */ + public int getImageWidth() { + return this.imageIcon.getIconWidth(); + } + + /** + * @return current image height + */ + public int getImageHeight() { + return this.imageIcon.getIconHeight(); + } + + + /** + * This method should be kept as private as long as it is used for serialization purpose. Replaces current imageIcon by a new + * one created with the image content guven is parameters + * + * @param pixels image content + * @param width image width + * @param height image height + */ + @SuppressWarnings("unused") + public void setImageContent(String imageContent, int width, int height) + { + StringTokenizer tokenizer = new StringTokenizer(imageContent, PIXEL_SEPARATOR); + int[] pixels = new int[tokenizer.countTokens()]; + int counter = 0; + while (tokenizer.hasMoreTokens()) + { + String aPixel = tokenizer.nextToken(); + pixels[counter] = Integer.parseInt(aPixel); + counter++; + } + MemoryImageSource mis = new MemoryImageSource(width, height, pixels, 0, width); + Toolkit tk = Toolkit.getDefaultToolkit(); + Image img = tk.createImage(mis); + this.setImage(img); + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.product.diagram.abstracts.RectangularNode#clone() + */ + public ImageNode clone() + { + ImageNode cloned = (ImageNode) super.clone(); + cloned.text = text.clone(); + cloned.imageIcon = imageIcon; + return cloned; + } + + + + private MultiLineString text; + private ImageIcon imageIcon; + private static final String PIXEL_SEPARATOR = ":"; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/NoteEdge.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/NoteEdge.java new file mode 100644 index 0000000..aa2a388 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/NoteEdge.java @@ -0,0 +1,66 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.common; + +import java.awt.BasicStroke; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.Stroke; +import java.awt.geom.GeneralPath; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; + +import com.horstmann.violet.product.diagram.abstracts.edge.ShapeEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; + +/** + * A dotted line that connects a note to its attachment. + */ +public class NoteEdge extends ShapeEdge +{ + + @Override + public void draw(Graphics2D g2) + { + Stroke oldStroke = g2.getStroke(); + g2.setStroke(DOTTED_STROKE); + g2.draw(getConnectionPoints()); + g2.setStroke(oldStroke); + } + + + @Override + public Shape getShape() + { + GeneralPath path = new GeneralPath(); + Line2D conn = getConnectionPoints(); + path.moveTo((float) conn.getX1(), (float) conn.getY1()); + path.lineTo((float) conn.getX2(), (float) conn.getY2()); + return path; + } + + private static Stroke DOTTED_STROKE = new BasicStroke(1.0f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 0.0f, new float[] + { + 3.0f, + 3.0f + }, 0.0f); +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/NoteNode.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/NoteNode.java new file mode 100644 index 0000000..a858883 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/NoteNode.java @@ -0,0 +1,196 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.common; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.Shape; +import java.awt.geom.GeneralPath; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.RectangularNode; +import com.horstmann.violet.product.diagram.abstracts.property.MultiLineString; + +/** + * A note node in a UML diagram. + * + * FIXME : manage Z order + * + * for (IEdge e : getGraph().getEdges()) + * { + * if (e.getStart() == this) + * { + * INode end = e.getEnd(); + * Point2D endPoint = end.getLocation(); + * INode n = getGraph().findNode(endPoint); + * if (n != end) end.setZ(n.getZ() + 1); + * } + * } + * + */ +public class NoteNode extends RectangularNode +{ + /** + * Construct a note node with a default size and color + */ + public NoteNode() + { + text = new MultiLineString(); + text.setJustification(MultiLineString.LEFT); + color = DEFAULT_COLOR; + } + + + @Override + public int getZ() + { + // Ensures that this kind of nodes is always on top + return INFINITE_Z_LEVEL; + } + + @Override + public boolean addConnection(IEdge e) + { + if (e.getStart() == e.getEnd()) { + return false; + } + return super.addConnection(e); + } + + + @Override + public Rectangle2D getBounds() + { + Rectangle2D b = text.getBounds(); + Point2D currentLocation = getLocation(); + double x = currentLocation.getX(); + double y = currentLocation.getY(); + double w = Math.max(b.getWidth(), DEFAULT_WIDTH); + double h = Math.max(b.getHeight(), DEFAULT_HEIGHT); + Rectangle2D currentBounds = new Rectangle2D.Double(x, y, w, h); + Rectangle2D snapperBounds = getGraph().getGrid().snap(currentBounds); + return snapperBounds; + } + + + + /** + * Gets the value of the text property. + * + * @return the text inside the note + */ + public MultiLineString getText() + { + return text; + } + + /** + * Sets the value of the text property. + * + * @param newValue the text inside the note + */ + public void setText(MultiLineString newValue) + { + text = newValue; + } + + /** + * Gets the value of the color property. + * + * @return the background color of the note + */ + public Color getColor() + { + return color; + } + + /** + * Sets the value of the color property. + * + * @param newValue the background color of the note + */ + public void setColor(Color newValue) + { + color = newValue; + } + + @Override + public void draw(Graphics2D g2) + { + super.draw(g2); + Color oldColor = g2.getColor(); + g2.setColor(color); + + Shape path = getShape(); + g2.fill(path); + g2.setColor(oldColor); + g2.draw(path); + + Rectangle2D bounds = getBounds(); + GeneralPath fold = new GeneralPath(); + fold.moveTo((float) (bounds.getMaxX() - FOLD_X), (float) bounds.getY()); + fold.lineTo((float) bounds.getMaxX() - FOLD_X, (float) bounds.getY() + FOLD_X); + fold.lineTo((float) bounds.getMaxX(), (float) (bounds.getY() + FOLD_Y)); + fold.closePath(); + oldColor = g2.getColor(); + g2.setColor(g2.getBackground()); + g2.fill(fold); + g2.setColor(oldColor); + g2.draw(fold); + + text.draw(g2, getBounds()); + } + + @Override + public Shape getShape() + { + Rectangle2D bounds = getBounds(); + GeneralPath path = new GeneralPath(); + path.moveTo((float) bounds.getX(), (float) bounds.getY()); + path.lineTo((float) (bounds.getMaxX() - FOLD_X), (float) bounds.getY()); + path.lineTo((float) bounds.getMaxX(), (float) (bounds.getY() + FOLD_Y)); + path.lineTo((float) bounds.getMaxX(), (float) bounds.getMaxY()); + path.lineTo((float) bounds.getX(), (float) bounds.getMaxY()); + path.closePath(); + return path; + } + + @Override + public NoteNode clone() + { + NoteNode cloned = (NoteNode)super.clone(); + cloned.text = text.clone(); + return cloned; + } + + private MultiLineString text; + private Color color; + + private static int DEFAULT_WIDTH = 60; + private static int DEFAULT_HEIGHT = 40; + private static Color DEFAULT_COLOR = new Color(255, 228, 181); // very pale pink + private static int FOLD_X = 8; + private static int FOLD_Y = 8; + private static int INFINITE_Z_LEVEL = 10000; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/PointNode.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/PointNode.java new file mode 100644 index 0000000..65d9b2d --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/product/diagram/common/PointNode.java @@ -0,0 +1,80 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.product.diagram.common; + +import java.awt.Graphics2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.AbstractNode; + +/** + * An inivisible node that is used in the toolbar to draw an edge, and in notes to serve as an end point of the node connector. + */ +public class PointNode extends AbstractNode +{ + public boolean contains(Point2D p) + { + final double THRESHOLD = 5; + return getLocation().distance(p) < THRESHOLD; + } + + public Rectangle2D getBounds() + { + return new Rectangle2D.Double(getLocation().getX(), getLocation().getY(), 0, 0); + } + + @Override + public Point2D getLocation() + { + if (tempLocation != null) return tempLocation; + else return super.getLocation(); + } + + public void setBounds(Rectangle2D bounds) + { + if (tempLocation != null) tempLocation.setLocation(bounds.getX(), bounds.getY()); + } + + public Point2D getConnectionPoint(IEdge e) + { + return getLocation(); + } + + @Override + public void translate(double dx, double dy) + { + super.translate(dx, dy); + tempLocation = null; + } + + private Point2D.Double tempLocation = new Point2D.Double(); + // Legacy grief--some versions of the XML encoder wrote calls to setBounds + // We use the location set by setBounds until the first call to translate. + + @Override + public void draw(Graphics2D g2) + { + // Invisible node + } +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/IWorkspace.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/IWorkspace.java new file mode 100644 index 0000000..a70a1ed --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/IWorkspace.java @@ -0,0 +1,65 @@ +package com.horstmann.violet.workspace; + +import java.awt.Component; + +import com.horstmann.violet.framework.file.IGraphFile; +import com.horstmann.violet.product.diagram.abstracts.Id; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.sidebar.ISideBar; + +public interface IWorkspace +{ + + /** + * @return graph file + */ + public IGraphFile getGraphFile(); + + /** + * @return graph editor + */ + public IEditorPart getEditorPart(); + + /** + * @return current side bar + */ + public ISideBar getSideBar(); + + /** + * @return current diagram's title + */ + public String getTitle(); + + /** + * Gets the fileName property. + * + * @return the file path + */ + public String getFilePath(); + + /** + * Sets the fileName property. + * + * @param path the file path + */ + public void setFilePath(String path); + + /** + * Registers a listener on this diagram panel to capture events + * + * @param l + */ + public void addListener(IWorkspaceListener l); + + /** + * @return unique id + */ + public Id getId(); + + + /** + * @return the awt component representing this workspace + */ + public Component getAWTComponent(); + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/IWorkspaceListener.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/IWorkspaceListener.java new file mode 100644 index 0000000..a388c0b --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/IWorkspaceListener.java @@ -0,0 +1,53 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.workspace; + +import com.horstmann.violet.framework.file.IFile; + +/** + * Interface that represents a listener to the diagram workspace. For exemple, allow Eclipse Plugin to detect Graph panel events + * + * @author Alexandre de Pellegrin + * + */ +public interface IWorkspaceListener +{ + + /** + * Graph Panel is informed that a file needs to be opened + * + * @param file + */ + public void mustOpenfile(IFile file); + + /** + * Graph Panel contains a graph that can be saved + */ + public void graphCouldBeSaved(); + + /** + * Indicates that graph title has been updated + * @param newTitle + */ + public void titleChanged(String newTitle); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/Workspace.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/Workspace.java new file mode 100644 index 0000000..51829ce --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/Workspace.java @@ -0,0 +1,341 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.workspace; + +import java.io.File; +import java.util.List; +import java.util.Vector; + +import com.horstmann.violet.framework.file.IFile; +import com.horstmann.violet.framework.file.IGraphFile; +import com.horstmann.violet.framework.file.IGraphFileListener; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.BeanInjector; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.framework.plugin.IDiagramPlugin; +import com.horstmann.violet.framework.plugin.PluginRegistry; +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.Id; +import com.horstmann.violet.workspace.editorpart.EditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPartBehaviorManager; +import com.horstmann.violet.workspace.editorpart.behavior.AddEdgeBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.AddNodeBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.ChangeToolByWeelBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.CutCopyPasteBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.DragGraphBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.DragSelectedBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.EditSelectedBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.FileCouldBeSavedBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.ResizeNodeBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.SelectAllBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.SelectByClickBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.SelectByDistanceBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.SelectByLassoBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.ShowMenuOnRightClickBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.SwingRepaintingBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.UndoRedoCompoundBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.ZoomByWheelBehavior; +import com.horstmann.violet.workspace.sidebar.ISideBar; +import com.horstmann.violet.workspace.sidebar.SideBar; +import com.horstmann.violet.workspace.sidebar.graphtools.GraphTool; +import com.horstmann.violet.workspace.sidebar.graphtools.IGraphToolsBarListener; + +/** + * Diagram workspace. It is a kind of package composed by a diagram put in a scroll panel, a side bar for tools and a status bar. + * This the class to use when you want to work with diagrams outside from Violet (in Eclipse or NetBeans for example) + * + * @author Alexandre de Pellegrin + */ +public class Workspace implements IWorkspace +{ + /** + * Constructs a diagram panel with the specified graph + * + * @param graphFile + */ + public Workspace(IGraphFile graphFile) + { + this.graphFile = graphFile; + init(); + } + + /** + * Constructs a diagram panel with the specified graph and a specified id + * + * @param graphFile + * @param id unique id + */ + public Workspace(IGraphFile graphFile, Id id) + { + this.graphFile = graphFile; + this.id = id; + init(); + } + + private void init() + { + BeanInjector.getInjector().inject(this); + setTitle(getGraphName()); + this.graphFile.addListener(new IGraphFileListener() + { + public void onFileModified() + { + updateTitle(true); + fireSaveNeeded(); + } + + public void onFileSaved() + { + setTitle(getGraphName()); + updateTitle(false); + } + }); + getAWTComponent().prepareLayout(); + } + + /** + * @return graph filename or the corresponding diagram name if the graph
+ * hasn't been saved yet. + */ + private String getGraphName() { + String filename = this.graphFile.getFilename(); + if (filename != null) { + return filename; + } + List diagramPlugins = this.pluginRegistry.getDiagramPlugins(); + Class searchedClass = this.graphFile.getGraph().getClass(); + for (IDiagramPlugin aDiagramPlugin : diagramPlugins) + { + if (aDiagramPlugin.getGraphClass().equals(searchedClass)) + { + return aDiagramPlugin.getName(); + } + } + return "Unknown"; + } + + + @Override + public IGraphFile getGraphFile() { + return this.graphFile; + } + + @Override + public IEditorPart getEditorPart() + { + if (this.graphEditor == null) + { + this.graphEditor = new EditorPart(this.graphFile.getGraph()); + IEditorPartBehaviorManager behaviorManager = this.graphEditor.getBehaviorManager(); + behaviorManager.addBehavior(new SelectByLassoBehavior(this.graphEditor, this.getSideBar().getGraphToolsBar())); + behaviorManager.addBehavior(new SelectByClickBehavior(this.graphEditor, this.getSideBar().getGraphToolsBar())); + behaviorManager.addBehavior(new SelectByDistanceBehavior(this.graphEditor)); + behaviorManager.addBehavior(new SelectAllBehavior(this.graphEditor, this.getSideBar().getGraphToolsBar())); + behaviorManager.addBehavior(new AddNodeBehavior(this.graphEditor, this.getSideBar().getGraphToolsBar())); + behaviorManager.addBehavior(new AddEdgeBehavior(this.graphEditor, this.getSideBar().getGraphToolsBar())); + behaviorManager.addBehavior(new DragSelectedBehavior(this.graphEditor, this.getSideBar().getGraphToolsBar())); + behaviorManager.addBehavior(new DragGraphBehavior(this)); + behaviorManager.addBehavior(new SwingRepaintingBehavior(this.graphEditor)); + behaviorManager.addBehavior(new EditSelectedBehavior(this.graphEditor)); + behaviorManager.addBehavior(new FileCouldBeSavedBehavior(this.getGraphFile())); + behaviorManager.addBehavior(new ResizeNodeBehavior(this.graphEditor, this.getSideBar().getGraphToolsBar())); + behaviorManager.addBehavior(new ZoomByWheelBehavior(this.getEditorPart())); + behaviorManager.addBehavior(new ChangeToolByWeelBehavior(this.getSideBar().getGraphToolsBar())); + behaviorManager.addBehavior(new ShowMenuOnRightClickBehavior(this.graphEditor)); + behaviorManager.addBehavior(new UndoRedoCompoundBehavior(this.graphEditor)); + behaviorManager.addBehavior(new CutCopyPasteBehavior(this.graphEditor)); + } + return this.graphEditor; + } + + @Override + public ISideBar getSideBar() + { + if (this.sideBar == null) + { + + this.sideBar = new SideBar(this); + this.sideBar.getGraphToolsBar().addListener(new IGraphToolsBarListener() + { + public void toolSelectionChanged(GraphTool tool) + { + getEditorPart().getSelectionHandler().setSelectedTool(tool); + } + }); + } + return this.sideBar; + } + + + @Override + public String getTitle() + { + return title; + } + + /** + * Set graph title + * + * @param newValue + */ + private void setTitle(String newValue) + { + title = newValue; + fireTitleChanged(newValue); + } + + /** + * Fires a event to indicate that the title has been changed + * + * @param newTitle + */ + private void fireTitleChanged(String newTitle) + { + Vector tl = cloneListeners(); + int size = tl.size(); + if (size == 0) return; + + for (int i = 0; i < size; ++i) + { + IWorkspaceListener aListener = (IWorkspaceListener) tl.elementAt(i); + aListener.titleChanged(newTitle); + } + } + + + /** + * Set a status indicating that the graph needs to be saved + * + * @param isSaveNeeded + */ + private void updateTitle(boolean isSaveNeeded) + { + String aTitle = getTitle(); + if (isSaveNeeded) + { + if (!aTitle.endsWith("*")) + { + setTitle(aTitle + "*"); + } + } + if (!isSaveNeeded) + { + if (aTitle.endsWith("*")) + { + setTitle(aTitle.substring(0, aTitle.length() - 1)); + } + } + } + + @Override + public String getFilePath() + { + return filePath; + } + + @Override + public void setFilePath(String path) + { + filePath = path; + File file = new File(path); + setTitle(file.getName()); + } + + + @Override + public synchronized void addListener(IWorkspaceListener l) + { + if (!this.listeners.contains(l)) + { + this.listeners.addElement(l); + } + } + + @SuppressWarnings("unchecked") + private synchronized Vector cloneListeners() + { + return (Vector) this.listeners.clone(); + } + + /** + * Fire an event to all listeners by calling + */ + public void fireMustOpenFile(IFile aFile) + { + Vector tl = cloneListeners(); + int size = tl.size(); + if (size == 0) return; + for (int i = 0; i < size; ++i) + { + IWorkspaceListener l = (IWorkspaceListener) tl.elementAt(i); + l.mustOpenfile(aFile); + } + } + + /** + * Fire an event to all listeners by calling + */ + private void fireSaveNeeded() + { + Vector tl = cloneListeners(); + int size = tl.size(); + if (size == 0) return; + for (int i = 0; i < size; ++i) + { + IWorkspaceListener l = (IWorkspaceListener) tl.elementAt(i); + l.graphCouldBeSaved(); + } + } + + @Override + public Id getId() + { + if (this.id == null) + { + this.id = new Id(); + } + return this.id; + } + + @Override + public WorkspacePanel getAWTComponent() + { + if (this.workspacePanel == null) + { + this.workspacePanel = new WorkspacePanel(this); + } + return this.workspacePanel; + } + + public WorkspacePanel workspacePanel; + private IGraphFile graphFile; + private IEditorPart graphEditor; + private ISideBar sideBar; + private String filePath; + private String title; + private Vector listeners = new Vector(); + private Id id; + + @InjectedBean + private PluginRegistry pluginRegistry; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/WorkspacePanel.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/WorkspacePanel.java new file mode 100644 index 0000000..f75bcb1 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/WorkspacePanel.java @@ -0,0 +1,105 @@ +package com.horstmann.violet.workspace; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.LayoutManager; + +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.EmptyBorder; +import javax.swing.border.MatteBorder; + +import com.horstmann.violet.framework.swingextension.TinyScrollBarUI; +import com.horstmann.violet.framework.theme.ThemeManager; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.sidebar.ISideBar; + +public class WorkspacePanel extends JPanel +{ + + public WorkspacePanel(IWorkspace workspace) + { + this.workspace = workspace; + } + + public void prepareLayout() + { + LayoutManager layout = new BorderLayout(); + setLayout(layout); + JScrollPane scrollGPanel = getScrollableEditorPart(); + add(scrollGPanel, BorderLayout.CENTER); + JScrollPane scrollSideBarPanel = getScrollableSideBar(); + add(scrollSideBarPanel, BorderLayout.EAST); +// JScrollPane scrollStatusBarPanel = getScrollableStatusBar(); +// add(scrollStatusBarPanel, BorderLayout.SOUTH); + refreshDisplay(); + } + + + + + /** + * @return the scrollable panel containing the editor + */ + public JScrollPane getScrollableEditorPart() + { + if (this.scrollableEditorPart == null) + { + final IEditorPart editorPart = this.workspace.getEditorPart(); + final Component panel = editorPart.getSwingComponent(); + this.scrollableEditorPart = new JScrollPane(); + this.scrollableEditorPart.getViewport().setView(panel); + this.scrollableEditorPart.setBackground(ThemeManager.getInstance().getTheme().getWhiteColor()); + this.scrollableEditorPart.setBorder(new EmptyBorder(0, 0, 0, 0)); + this.scrollableEditorPart.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + } + return this.scrollableEditorPart; + } + + /** + * @param workspace TODO + * @return scrollpane containing sidebar + */ + public JScrollPane getScrollableSideBar() + { + if (this.scrollableSideBar == null) + { + ISideBar sideBar = this.workspace.getSideBar(); + this.scrollableSideBar = new JScrollPane(sideBar.getAWTComponent()); + this.scrollableSideBar.setAlignmentY(Component.TOP_ALIGNMENT); + this.scrollableSideBar.getHorizontalScrollBar().setUI(new TinyScrollBarUI()); + this.scrollableSideBar.getVerticalScrollBar().setUI(new TinyScrollBarUI()); + this.scrollableSideBar.setBorder(new MatteBorder(0, 1, 0, 0, ThemeManager.getInstance().getTheme() + .getSidebarBorderColor())); + this.scrollableSideBar.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + this.scrollableSideBar.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + } + return this.scrollableSideBar; + } + + + + public void refreshDisplay() + { + SwingUtilities.invokeLater(new Runnable() + { + public void run() + { + WorkspacePanel.this.revalidate(); + WorkspacePanel.this.doLayout(); + WorkspacePanel.this.repaint(); + } + }); + } + + + + private IWorkspace workspace; + private JScrollPane scrollableSideBar; + private JScrollPane scrollableEditorPart; + private JScrollPane scrollableStatusBar; + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPart.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPart.java new file mode 100644 index 0000000..df2ffd5 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPart.java @@ -0,0 +1,277 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.workspace.editorpart; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.awt.geom.Rectangle2D; +import java.awt.geom.Rectangle2D.Double; +import java.util.List; + +import javax.swing.JComponent; +import javax.swing.JPanel; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.behavior.IEditorPartBehavior; + +/** + * Graph editor + */ +public class EditorPart extends JPanel implements IEditorPart +{ + + /** + * Default constructor + * + * @param aGraph graph which will be drawn in this editor part + */ + public EditorPart(IGraph aGraph) + { + this.graph = aGraph; + this.zoom = 1; + this.grid = new PlainGrid(this); + addMouseListener(new MouseAdapter() + { + + public void mousePressed(MouseEvent event) + { + behaviorManager.fireOnMousePressed(event); + } + + public void mouseReleased(MouseEvent event) + { + behaviorManager.fireOnMouseReleased(event); + } + + public void mouseClicked(MouseEvent event) + { + behaviorManager.fireOnMouseClicked(event); + } + }); + + addMouseWheelListener(new MouseWheelListener() + { + @Override + public void mouseWheelMoved(MouseWheelEvent e) + { + behaviorManager.fireOnMouseWheelMoved(e); + } + }); + + addMouseMotionListener(new MouseMotionAdapter() + { + public void mouseDragged(MouseEvent event) + { + behaviorManager.fireOnMouseDragged(event); + } + + @Override + public void mouseMoved(MouseEvent event) + { + behaviorManager.fireOnMouseMoved(event); + } + }); + setBounds(0,0,0,0); + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.IEditorPart#getGraph() + */ + public IGraph getGraph() + { + return this.graph; + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.IEditorPart#removeSelected() + */ + public void removeSelected() + { + this.behaviorManager.fireBeforeRemovingSelectedElements(); + try + { + List selectedNodes = selectionHandler.getSelectedNodes(); + List selectedEdges = selectionHandler.getSelectedEdges(); + IEdge[] edgesArray = selectedEdges.toArray(new IEdge[selectedEdges.size()]); + INode[] nodesArray = selectedNodes.toArray(new INode[selectedNodes.size()]); + graph.removeNode(nodesArray); + graph.removeEdge(edgesArray); + } + finally + { + this.selectionHandler.clearSelection(); + this.behaviorManager.fireAfterRemovingSelectedElements(); + } + } + + public List getSelectedNodes() + { + return selectionHandler.getSelectedNodes(); + } + + public void clearSelection() + { + selectionHandler.clearSelection(); + } + + public void selectElement(INode node) + { + selectionHandler.addSelectedElement(node); + } + + @Override + public Dimension getPreferredSize() + { + Dimension parentSize = getParent().getSize(); + Rectangle2D bounds = graph.getClipBounds(); + int width = Math.max((int) (zoom * bounds.getMaxX()), (int) parentSize.getWidth()); + int height = Math.max((int) (zoom * bounds.getMaxY()), (int) parentSize.getHeight()); + return new Dimension(width, height); + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.IEditorPart#changeZoom(int) + */ + public void changeZoom(int steps) + { + final double FACTOR = Math.sqrt(Math.sqrt(2)); + for (int i = 1; i <= steps; i++) + zoom *= FACTOR; + for (int i = 1; i <= -steps; i++) + zoom /= FACTOR; + revalidate(); + repaint(); + } + + @Override + public double getZoomFactor() + { + return this.zoom; + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.product.workspace.editorpart.IEditorPart#getGrid() + */ + public IGrid getGrid() + { + return this.grid; + } + + /* + * (non-Javadoc) + * + * @seecom.horstmann.violet.framework.workspace.editorpart.IEditorPart# growDrawingArea() + */ + public void growDrawingArea() + { + IGraph g = getGraph(); + Rectangle2D bounds = g.getClipBounds(); + bounds.add(getBounds()); + g.setBounds(new Double(0, 0, GROW_SCALE_FACTOR * bounds.getWidth(), GROW_SCALE_FACTOR * bounds.getHeight())); + invalidate(); + repaint(); + } + + /* + * (non-Javadoc) + * + * @seecom.horstmann.violet.framework.workspace.editorpart.IEditorPart# clipDrawingArea() + */ + public void clipDrawingArea() + { + IGraph g = getGraph(); + g.setBounds(null); + invalidate(); + repaint(); + } + + public JComponent getSwingComponent() + { + return this; + } + + /* + * (non-Javadoc) + * + * @see javax.swing.JComponent#paintComponent(java.awt.Graphics) + */ + public void paintComponent(Graphics g) + { + setBackground(Color.WHITE); + super.paintComponent(g); + Graphics2D g2 = (Graphics2D) g; + g2.scale(zoom, zoom); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + if (grid.isVisible()) grid.paint(g2); + graph.draw(g2); + for (IEditorPartBehavior behavior : this.behaviorManager.getBehaviors()) + { + behavior.onPaint(g2); + } + } + + @Override + public IEditorPartSelectionHandler getSelectionHandler() + { + return this.selectionHandler; + } + + @Override + public IEditorPartBehaviorManager getBehaviorManager() + { + return this.behaviorManager; + } + + private IGraph graph; + + private IGrid grid; + + private double zoom; + + private IEditorPartSelectionHandler selectionHandler = new EditorPartSelectionHandler(); + + /** + * Scale factor used to grow drawing area + */ + private static final double GROW_SCALE_FACTOR = Math.sqrt(2); + + private IEditorPartBehaviorManager behaviorManager = new EditorPartBehaviorManager(); + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPartBehaviorManager.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPartBehaviorManager.java new file mode 100644 index 0000000..b4ef841 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPartBehaviorManager.java @@ -0,0 +1,146 @@ +package com.horstmann.violet.workspace.editorpart; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.geom.Point2D; +import java.beans.PropertyChangeEvent; +import java.util.ArrayList; +import java.util.List; + +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.behavior.IEditorPartBehavior; + +public class EditorPartBehaviorManager implements IEditorPartBehaviorManager +{ + + + private List behaviors = new ArrayList(); + + + public void addBehavior(IEditorPartBehavior newBehavior) { + this.behaviors.add(newBehavior); + } + + + public List getBehaviors() { + return this.behaviors; + } + + @Override + public List getBehaviors(Class type) + { + List result = new ArrayList(); + for (IEditorPartBehavior aBehavior : this.behaviors) { + if (aBehavior.getClass().isAssignableFrom(type)) { + result.add((T) aBehavior); + } + } + return result; + } + + + @Override + public void fireOnMousePressed(MouseEvent event) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.onMousePressed(event); + } + + @Override + public void fireOnMouseDragged(MouseEvent event) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.onMouseDragged(event); + } + + @Override + public void fireOnMouseReleased(MouseEvent event) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.onMouseReleased(event); + } + + @Override + public void fireOnMouseClicked(MouseEvent event) + { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.onMouseClicked(event); + } + + @Override + public void fireOnMouseMoved(MouseEvent event) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.onMouseMoved(event); + } + + @Override + public void fireOnMouseWheelMoved(MouseWheelEvent event) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.onMouseWheelMoved(event); + } + + @Override + public void fireBeforeEditingNode(INode node) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.beforeEditingNode(node); + } + + @Override + public void fireWhileEditingNode(INode node, PropertyChangeEvent event) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.whileEditingNode(node, event); + } + + @Override + public void fireAfterEditingNode(INode node) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.afterEditingNode(node); + } + + @Override + public void fireBeforeEditingEdge(IEdge edge) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.beforeEditingEdge(edge); + } + + + @Override + public void fireWhileEditingEdge(IEdge edge, PropertyChangeEvent event) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.whileEditingEdge(edge, event); + } + + @Override + public void fireAfterEditingEdge(IEdge edge) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.afterEditingEdge(edge); + } + + @Override + public void fireBeforeRemovingSelectedElements() { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.beforeRemovingSelectedElements(); + } + + @Override + public void fireAfterRemovingSelectedElements() { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.afterRemovingSelectedElements(); + } + + @Override + public void fireBeforeAddingNodeAtPoint(INode node, Point2D location) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.beforeAddingNodeAtPoint(node, location); + } + + @Override + public void fireAfterAddingNodeAtPoint(INode node, Point2D location) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.afterAddingNodeAtPoint(node, location); + } + + @Override + public void fireBeforeAddingEdgeAtPoints(IEdge edge, Point2D startPoint, Point2D endPoint) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.beforeAddingEdgeAtPoints(edge, startPoint, endPoint); + } + + @Override + public void fireAfterAddingEdgeAtPoints(IEdge edge, Point2D startPoint, Point2D endPoint) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.afterAddingEdgeAtPoints(edge, startPoint, endPoint); + } + + @Override + public void fireOnEdgeSelected(IEdge edge) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.onEdgeSelected(edge); + } + + + @Override + public void fireOnNodeSelected(INode node) { + for (IEditorPartBehavior aBehavior : this.behaviors) aBehavior.onNodeSelected(node); + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPartSelectionHandler.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPartSelectionHandler.java new file mode 100644 index 0000000..5e78f4b --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPartSelectionHandler.java @@ -0,0 +1,161 @@ +package com.horstmann.violet.workspace.editorpart; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.sidebar.graphtools.GraphTool; + +public class EditorPartSelectionHandler implements IEditorPartSelectionHandler +{ + + public void setSelectedElement(INode node) + { + this.selectedNodes.clear(); + this.selectedEdges.clear(); + addSelectedElement(node); + } + + public void setSelectedElement(IEdge edge) + { + this.selectedNodes.clear(); + this.selectedEdges.clear(); + addSelectedElement(edge); + } + + public void updateSelectedElements(INode[] nodes) + { + for (int i = 0; i < nodes.length; i++) + { + if (isElementAlreadySelected(nodes[i])) + { + addSelectedElement(nodes[i]); + } + } + } + + public void updateSelectedElements(IEdge[] edges) + { + for (int i = 0; i < edges.length; i++) + { + if (isElementAlreadySelected(edges[i])) + { + addSelectedElement(edges[i]); + } + } + } + + public void addSelectedElement(INode node) + { + if (this.selectedNodes.contains(node)) + { + this.removeElementFromSelection(node); + } + this.selectedNodes.add(node); + } + + public void addSelectedElement(IEdge edge) + { + if (this.selectedEdges.contains(edge)) + { + this.removeElementFromSelection(edge); + } + this.selectedEdges.add(edge); + } + + public void removeElementFromSelection(INode node) + { + if (this.selectedNodes.contains(node)) + { + int i = this.selectedNodes.indexOf(node); + this.selectedNodes.remove(i); + } + } + + public void removeElementFromSelection(IEdge edge) + { + if (this.selectedEdges.contains(edge)) + { + int i = this.selectedEdges.indexOf(edge); + this.selectedEdges.remove(i); + } + } + + public boolean isElementAlreadySelected(INode node) + { + if (this.selectedNodes.contains(node)) return true; + return false; + } + + public boolean isElementAlreadySelected(IEdge edge) + { + if (this.selectedEdges.contains(edge)) return true; + return false; + } + + public void clearSelection() + { + this.selectedNodes.clear(); + this.selectedEdges.clear(); + } + + public INode getLastSelectedNode() + { + return getLastElement(this.selectedNodes); + } + + public IEdge getLastSelectedEdge() + { + return getLastElement(this.selectedEdges); + } + + public boolean isNodeSelectedAtLeast() + { + return this.selectedNodes.size() > 0; + } + + public boolean isEdgeSelectedAtLeast() + { + return this.selectedEdges.size() > 0; + } + + public List getSelectedNodes() + { + return Collections.unmodifiableList(selectedNodes); + } + + public List getSelectedEdges() + { + return Collections.unmodifiableList(selectedEdges); + } + + @Override + public GraphTool getSelectedTool() + { + return this.selectedTool; + } + + @Override + public void setSelectedTool(GraphTool graphTool) + { + this.selectedTool = graphTool; + } + + private T getLastElement(List list) + { + int size = list.size(); + if (size <= 0) + { + return null; + } + return list.get(size - 1); + } + + private List selectedNodes = new ArrayList(); + private List selectedEdges = new ArrayList(); + + private GraphTool selectedTool; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPartUI.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPartUI.java new file mode 100644 index 0000000..0312aa6 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EditorPartUI.java @@ -0,0 +1,47 @@ +package com.horstmann.violet.workspace.editorpart; + +import java.awt.Color; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; + +import javax.swing.JComponent; +import javax.swing.plaf.PanelUI; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.workspace.editorpart.behavior.IEditorPartBehavior; + +public class EditorPartUI extends PanelUI +{ + + @Override + public void installUI(JComponent c) + { + super.installUI(c); + c.setBackground(Color.WHITE); + } + + + + + @Override + public void paint(Graphics g, JComponent c) + { + IEditorPart editor = (IEditorPart) c; + IGraph graph = editor.getGraph(); + double zoom = editor.getZoomFactor(); + IGrid grid = editor.getGrid(); + super.paint(g, c); + Graphics2D g2 = (Graphics2D) g; + grid.paint(g2); + g2.scale(zoom, zoom); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + graph.draw(g2); + for (IEditorPartBehavior paintableBehaviour : editor.getBehaviorManager().getBehaviors()) { + paintableBehaviour.onPaint(g2); + } + } + + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EmptyGrid.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EmptyGrid.java new file mode 100644 index 0000000..a1cac08 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/EmptyGrid.java @@ -0,0 +1,61 @@ +package com.horstmann.violet.workspace.editorpart; + +import java.awt.Graphics2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + + +public class EmptyGrid implements IGrid +{ + + @Override + public void changeGridSize(int steps) + { + // TODO Auto-generated method stub + + } + + @Override + public double getSnappingHeight() + { + return 0; + } + + @Override + public double getSnappingWidth() + { + return 0; + } + + @Override + public boolean isVisible() + { + return false; + } + + @Override + public void paint(Graphics2D g2) + { + // TODO Auto-generated method stub + + } + + @Override + public void setVisible(boolean isVisible) + { + // TODO Auto-generated method stub + } + + @Override + public Point2D snap(Point2D p) + { + return p; + } + + @Override + public Rectangle2D snap(Rectangle2D r) + { + return r; + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IEditorPart.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IEditorPart.java new file mode 100644 index 0000000..92ad92b --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IEditorPart.java @@ -0,0 +1,90 @@ +package com.horstmann.violet.workspace.editorpart; + +import java.util.List; + +import javax.swing.JComponent; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.node.INode; + +/** + * Defines the editor behaviour (an editor is something embedding an IGraph) + * + * @author Alexandre de Pellegrin + * + */ +public interface IEditorPart +{ + + /** + * Returns the graph handled by the editor + */ + public abstract IGraph getGraph(); + + /** + * Removes the selected nodes or edges. + */ + public abstract void removeSelected(); + + + /** + * @return currently selected nodes + */ + public abstract List getSelectedNodes(); + + /** + * Clears nodes and edges selection + */ + public void clearSelection(); + + /** + * Selects a node + * @param node + */ + public void selectElement(INode node); + + /** + * Changes the zoom of this editor. The zoom is 1 by default and is multiplied by sqrt(2) for each positive stem or divided by + * sqrt(2) for each negative step. + * + * @param steps the number of steps by which to change the zoom. A positive value zooms in, a negative value zooms out. + */ + public abstract void changeZoom(int steps); + + /** + * @return current zoom factor + */ + public double getZoomFactor(); + + /** + * @return the grid used to keep elements aligned + */ + public IGrid getGrid(); + + /** + * Grows drawing area + */ + public void growDrawingArea(); + + /** + * Clips drawing area + */ + public void clipDrawingArea(); + + /** + * @return the awt object displaying this editor part + */ + public JComponent getSwingComponent(); + + /** + * @return object that manages nodes and edges selection + */ + public IEditorPartSelectionHandler getSelectionHandler(); + + /** + * @return manager used to declare new editor behaviors and how to send events between behaviors + */ + public IEditorPartBehaviorManager getBehaviorManager(); + + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IEditorPartBehaviorManager.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IEditorPartBehaviorManager.java new file mode 100644 index 0000000..826499c --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IEditorPartBehaviorManager.java @@ -0,0 +1,62 @@ +package com.horstmann.violet.workspace.editorpart; + +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.geom.Point2D; +import java.beans.PropertyChangeEvent; +import java.util.List; + +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.behavior.IEditorPartBehavior; + +public interface IEditorPartBehaviorManager +{ + + public abstract void addBehavior(IEditorPartBehavior newBehavior); + + public abstract List getBehaviors(); + + public abstract List getBehaviors(Class type); + + public abstract void fireOnMousePressed(MouseEvent event); + + public abstract void fireOnMouseDragged(MouseEvent event); + + public abstract void fireOnMouseReleased(MouseEvent event); + + public abstract void fireOnMouseClicked(MouseEvent event); + + public abstract void fireOnMouseMoved(MouseEvent event); + + public abstract void fireOnMouseWheelMoved(MouseWheelEvent event); + + public abstract void fireBeforeEditingNode(INode node); + + public abstract void fireWhileEditingNode(INode node, PropertyChangeEvent event); + + public abstract void fireAfterEditingNode(INode node); + + public abstract void fireBeforeEditingEdge(IEdge edge); + + public abstract void fireWhileEditingEdge(IEdge edge, PropertyChangeEvent event); + + public abstract void fireAfterEditingEdge(IEdge edge); + + public abstract void fireBeforeRemovingSelectedElements(); + + public abstract void fireAfterRemovingSelectedElements(); + + public abstract void fireBeforeAddingNodeAtPoint(INode node, Point2D location); + + public abstract void fireAfterAddingNodeAtPoint(INode node, Point2D location); + + public abstract void fireBeforeAddingEdgeAtPoints(IEdge edge, Point2D startPoint, Point2D endPoint); + + public abstract void fireAfterAddingEdgeAtPoints(IEdge edge, Point2D startPoint, Point2D endPoint); + + public abstract void fireOnNodeSelected(INode node); + + public abstract void fireOnEdgeSelected(IEdge edge); + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IEditorPartSelectionHandler.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IEditorPartSelectionHandler.java new file mode 100644 index 0000000..54eab95 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IEditorPartSelectionHandler.java @@ -0,0 +1,45 @@ +package com.horstmann.violet.workspace.editorpart; + +import java.util.List; + +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.sidebar.graphtools.GraphTool; + +public interface IEditorPartSelectionHandler { + + public abstract void setSelectedElement(INode node); + + public abstract void setSelectedElement(IEdge edge); + + public abstract void addSelectedElement(INode node); + + public abstract void addSelectedElement(IEdge edge); + + public abstract void removeElementFromSelection(INode node); + + public abstract void removeElementFromSelection(IEdge edge); + + public abstract boolean isElementAlreadySelected(INode node); + + public abstract boolean isElementAlreadySelected(IEdge edge); + + public abstract void clearSelection(); + + public abstract INode getLastSelectedNode(); + + public abstract IEdge getLastSelectedEdge(); + + public abstract boolean isNodeSelectedAtLeast(); + + public abstract boolean isEdgeSelectedAtLeast(); + + public abstract List getSelectedNodes(); + + public abstract List getSelectedEdges(); + + public abstract void setSelectedTool(GraphTool graphTool); + + public abstract GraphTool getSelectedTool(); + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IGrid.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IGrid.java new file mode 100644 index 0000000..70696ea --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/IGrid.java @@ -0,0 +1,65 @@ +package com.horstmann.violet.workspace.editorpart; + +import java.awt.Graphics2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + + +public interface IGrid +{ + + /** + * Changes grid visibility + * + * @param isVisible + */ + public abstract void setVisible(boolean isVisible); + + /** + * @return true id the grid is visible + */ + public abstract boolean isVisible(); + + /** + * Change grid size + * + * @param steps the number of steps by which to change the zoom. A positive value zooms in, a negative value zooms out. + */ + public abstract void changeGridSize(int steps); + + /** + * Draws this grid inside a rectangle. + * + * @param g2 the graphics context + * @param bounds the bounding rectangle + */ + public abstract void paint(Graphics2D g2); + + /** + * Snaps a point to the nearest grid point + * + * @param p the point to snap. After the call, the coordinates of p are changed so that p falls on the grid. + * @return the point after modification + */ + public abstract Point2D snap(Point2D p); + + /** + * Snaps a rectangle to the nearest grid points + * + * @param r the rectangle to snap. After the call, the coordinates of r are changed so that all of its corners falls on the + * grid. + * @return r (for convenience) + */ + public abstract Rectangle2D snap(Rectangle2D r); + + /** + * @return width of a grid portion (depending on the grid global size) + */ + public abstract double getSnappingWidth(); + + /** + * @return height of a grid portion (depending on the grid global size) + */ + public abstract double getSnappingHeight(); + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/PlainGrid.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/PlainGrid.java new file mode 100644 index 0000000..1ff5631 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/PlainGrid.java @@ -0,0 +1,201 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.workspace.editorpart; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics2D; +import java.awt.Stroke; +import java.awt.geom.AffineTransform; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; + +import com.horstmann.violet.framework.theme.ThemeManager; +import com.horstmann.violet.product.diagram.abstracts.IGraph; + +/** + * A grid to which points and rectangles can be "snapped". The snapping operation moves a point to the nearest grid point. + */ +public class PlainGrid implements IGrid +{ + /** + * Constructs a grid with its grid size. + * + * @param snappingWidth the grid point distance in x-direction + * @param snappingHeight the grid point distance in y-direction + */ + public PlainGrid(IEditorPart editorPart) + { + this.editorPart = editorPart; + this.snappingWidth = DEFAULT_GRID_SIZE; + this.snappingHeight = DEFAULT_GRID_SIZE; + } + + /* (non-Javadoc) + * @see com.horstmann.violet.product.diagram.abstracts.IGrid#setVisible(boolean) + */ + @Override + public void setVisible(boolean isVisible) + { + this.isVisible = isVisible; + } + + /* (non-Javadoc) + * @see com.horstmann.violet.product.diagram.abstracts.IGrid#isVisible() + */ + @Override + public boolean isVisible() + { + return this.isVisible; + } + + /* (non-Javadoc) + * @see com.horstmann.violet.product.diagram.abstracts.IGrid#changeGridSize(int) + */ + @Override + public void changeGridSize(int steps) + { + final double FACTOR = Math.sqrt(Math.sqrt(2)); + for (int i = 1; i <= steps; i++) { + snappingWidth *= FACTOR; + snappingHeight *= FACTOR; + } + for (int i = 1; i <= -steps; i++) { + snappingWidth /= FACTOR; + snappingHeight /= FACTOR; + } + } + + /** + * @return the grid size according to the graph size, the editor size and its zoom factor (scaleX and scaleY) + */ + private Rectangle2D.Double getBounds(Graphics2D g2) + { + Component editorPartComponent = editorPart.getSwingComponent(); + IGraph graph = editorPart.getGraph(); + + Rectangle2D bounds = editorPartComponent.getBounds(); + Rectangle2D graphBounds = graph.getClipBounds(); + double scaleX = 1; + double scaleY = 1; + AffineTransform transform = g2.getTransform(); + if (transform != null) { + scaleX = transform.getScaleX(); + scaleY = transform.getScaleY(); + } + return new Rectangle2D.Double(0, 0, Math.max(bounds.getMaxX() / scaleX, graphBounds.getMaxX()), Math.max(bounds + .getMaxY() + / scaleY, graphBounds.getMaxY())); + } + + @Override + public void paint(Graphics2D g2) + { + if (snappingWidth == 0 || snappingHeight == 0) return; + Color oldColor = g2.getColor(); + g2.setColor(ThemeManager.getInstance().getTheme().getGridColor()); + Stroke oldStroke = g2.getStroke(); + Rectangle2D.Double bounds = getBounds(g2); + for (double x = bounds.getX(); x < bounds.getMaxX(); x += snappingWidth) + g2.draw(new Line2D.Double(x, bounds.getY(), x, bounds.getMaxY())); + for (double y = bounds.getY(); y < bounds.getMaxY(); y += snappingHeight) + g2.draw(new Line2D.Double(bounds.getX(), y, bounds.getMaxX(), y)); + g2.setStroke(oldStroke); + g2.setColor(oldColor); + } + + /* (non-Javadoc) + * @see com.horstmann.violet.product.diagram.abstracts.IGrid#snap(java.awt.geom.Point2D) + */ + @Override + public Point2D snap(Point2D p) + { + double x; + if (snappingWidth == 0) x = p.getX(); + else x = Math.round(p.getX() / snappingWidth) * snappingWidth; + double y; + if (snappingHeight == 0) y = p.getY(); + else y = Math.round(p.getY() / snappingHeight) * snappingHeight; + p.setLocation(x, y); + return p; + } + + /* (non-Javadoc) + * @see com.horstmann.violet.product.diagram.abstracts.IGrid#snap(java.awt.geom.Rectangle2D) + */ + @Override + public Rectangle2D snap(Rectangle2D r) + { + double x; + double w; + if (snappingWidth == 0) + { + x = r.getX(); + w = r.getWidth(); + } + else + { + x = Math.round(r.getX() / snappingWidth) * snappingWidth; + w = Math.ceil(r.getWidth() / (2 * snappingWidth)) * (2 * snappingWidth); + } + double y; + double h; + if (snappingHeight == 0) + { + y = r.getY(); + h = r.getHeight(); + } + else + { + y = Math.round(r.getY() / snappingHeight) * snappingHeight; + h = Math.ceil(r.getHeight() / (2 * snappingHeight)) * (2 * snappingHeight); + } + + r.setFrame(x, y, w, h); + return r; + } + + /* (non-Javadoc) + * @see com.horstmann.violet.product.diagram.abstracts.IGrid#getSnappingWidth() + */ + @Override + public double getSnappingWidth() + { + return snappingWidth; + } + + /* (non-Javadoc) + * @see com.horstmann.violet.product.diagram.abstracts.IGrid#getSnappingHeight() + */ + @Override + public double getSnappingHeight() + { + return snappingHeight; + } + + private double snappingWidth; + private double snappingHeight; + private boolean isVisible = true; + public static final int DEFAULT_GRID_SIZE = 10; + private IEditorPart editorPart; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/AbstractEditorPartBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/AbstractEditorPartBehavior.java new file mode 100644 index 0000000..4bfcd92 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/AbstractEditorPartBehavior.java @@ -0,0 +1,170 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.geom.Point2D; +import java.beans.PropertyChangeEvent; + +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.sidebar.graphtools.GraphTool; + + +public abstract class AbstractEditorPartBehavior implements IEditorPartBehavior +{ + @Override + public void afterAddingEdgeAtPoints(IEdge edge, Point2D startPoint, Point2D endPoint) + { + // TODO Auto-generated method stub + + } + + @Override + public void afterAddingNodeAtPoint(INode node, Point2D location) + { + // TODO Auto-generated method stub + + } + + @Override + public void afterEditingEdge(IEdge edge) + { + // TODO Auto-generated method stub + + } + + @Override + public void afterEditingNode(INode node) + { + // TODO Auto-generated method stub + + } + + @Override + public void afterRemovingSelectedElements() + { + // TODO Auto-generated method stub + + } + + @Override + public void beforeAddingEdgeAtPoints(IEdge edge, Point2D startPoint, Point2D endPoint) + { + // TODO Auto-generated method stub + + } + + @Override + public void beforeAddingNodeAtPoint(INode node, Point2D location) + { + // TODO Auto-generated method stub + + } + + @Override + public void beforeEditingEdge(IEdge edge) + { + // TODO Auto-generated method stub + + } + + @Override + public void beforeEditingNode( INode node) + { + // TODO Auto-generated method stub + + } + + @Override + public void whileEditingEdge(IEdge edge, PropertyChangeEvent event) + { + // TODO Auto-generated method stub + + } + + @Override + public void whileEditingNode(INode node, PropertyChangeEvent event) + { + // TODO Auto-generated method stub + + } + + @Override + public void beforeRemovingSelectedElements() + { + // TODO Auto-generated method stub + + } + + @Override + public void onMouseDragged(MouseEvent event) + { + // TODO Auto-generated method stub + + } + + @Override + public void onMousePressed(MouseEvent event) + { + // TODO Auto-generated method stub + + } + + @Override + public void onMouseReleased(MouseEvent event) + { + // TODO Auto-generated method stub + + } + + @Override + public void onMouseClicked(MouseEvent event) + { + // TODO Auto-generated method stub + + } + + @Override + public void onMouseMoved(MouseEvent event) + { + // TODO Auto-generated method stub + + } + + @Override + public void onMouseWheelMoved(MouseWheelEvent event) + { + // TODO Auto-generated method stub + + } + + @Override + public void onToolSelected(GraphTool selectedTool) + { + // TODO Auto-generated method stub + + } + + @Override + public void onEdgeSelected(IEdge edge) + { + // TODO Auto-generated method stub + + } + + @Override + public void onNodeSelected(INode node) + { + // TODO Auto-generated method stub + + } + + @Override + public void onPaint(Graphics2D g2) + { + // TODO Auto-generated method stub + + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/AddEdgeBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/AddEdgeBehavior.java new file mode 100644 index 0000000..8086959 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/AddEdgeBehavior.java @@ -0,0 +1,173 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.Id; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPartBehaviorManager; +import com.horstmann.violet.workspace.editorpart.IEditorPartSelectionHandler; +import com.horstmann.violet.workspace.sidebar.graphtools.GraphTool; +import com.horstmann.violet.workspace.sidebar.graphtools.IGraphToolsBar; + +public class AddEdgeBehavior extends AbstractEditorPartBehavior +{ + + public AddEdgeBehavior(IEditorPart editorPart, IGraphToolsBar graphToolsBar) + { + this.editorPart = editorPart; + this.graph = editorPart.getGraph(); + this.selectionHandler = editorPart.getSelectionHandler(); + this.behaviorManager = editorPart.getBehaviorManager(); + this.graphToolsBar = graphToolsBar; + } + + @Override + public void onMousePressed(MouseEvent event) + { + if (event.getClickCount() > 1) + { + return; + } + if (event.getButton() != MouseEvent.BUTTON1) { + return; + } + if (GraphTool.SELECTION_TOOL.equals(this.graphToolsBar.getSelectedTool())) + { + return; + } + GraphTool selectedTool = this.selectionHandler.getSelectedTool(); + if (!IEdge.class.isInstance(selectedTool.getNodeOrEdge())) + { + return; + } + double zoom = editorPart.getZoomFactor(); + final Point2D mousePoint = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); + INode targetNode = graph.findNode(mousePoint); + this.isDraggingInProgress = (targetNode != null); + mouseDownPoint = mousePoint; + lastMousePoint = mousePoint; + } + + @Override + public void onMouseDragged(MouseEvent event) + { + if (!isDraggingInProgress) + { + return; + } + double zoom = editorPart.getZoomFactor(); + Point2D mousePoint = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); + lastMousePoint = mousePoint; + } + + @Override + public void onMouseReleased(MouseEvent event) + { + if (!isDraggingInProgress) + { + return; + } + GraphTool selectedTool = this.selectionHandler.getSelectedTool(); + if (!IEdge.class.isInstance(selectedTool.getNodeOrEdge())) + { + isDraggingInProgress = false; + return; + } + IEdge prototype = (IEdge) selectedTool.getNodeOrEdge(); + IEdge newEdge = (IEdge) prototype.clone(); + newEdge.setId(new Id()); + + boolean added = addEdgeAtPoints(newEdge, mouseDownPoint, lastMousePoint); + if (added) + { + selectionHandler.setSelectedElement(newEdge); + isDraggingInProgress = false; + } + } + + /** + * Adds an edge at a specific location + * + * @param newEdge + * @param startPoint + * @param endPoint + * @return true id the edge has been added + */ + public boolean addEdgeAtPoints(IEdge newEdge, Point2D startPoint, Point2D endPoint) + { + boolean isAdded = false; + if (startPoint.distance(endPoint) > CONNECT_THRESHOLD) + { + this.behaviorManager.fireBeforeAddingEdgeAtPoints(newEdge, startPoint, endPoint); + try + { + INode startNode = graph.findNode(startPoint); + INode endNode = graph.findNode(endPoint); + Point2D relativeStartPoint = null; + Point2D relativeEndPoint = null; + if (startNode != null) { + Point2D startNodeLocationOnGraph = startNode.getLocationOnGraph(); + double relativeStartX = startPoint.getX() - startNodeLocationOnGraph.getX(); + double relativeStartY = startPoint.getY() - startNodeLocationOnGraph.getY(); + relativeStartPoint = new Point2D.Double(relativeStartX, relativeStartY); + } + if (endNode != null) { + Point2D endNodeLocationOnGraph = endNode.getLocationOnGraph(); + double relativeEndX = endPoint.getX() - endNodeLocationOnGraph.getX(); + double relativeEndY = endPoint.getY() - endNodeLocationOnGraph.getY(); + relativeEndPoint = new Point2D.Double(relativeEndX, relativeEndY); + } + if (graph.connect(newEdge, startNode, relativeStartPoint, endNode, relativeEndPoint)); + { + newEdge.incrementRevision(); + isAdded = true; + } + } + finally + { + this.behaviorManager.fireAfterAddingEdgeAtPoints(newEdge, startPoint, endPoint); + } + } + return isAdded; + } + + @Override + public void onPaint(Graphics2D g2) + { + if (!isDraggingInProgress) + { + return; + } + Color oldColor = g2.getColor(); + g2.setColor(PURPLE); + g2.draw(new Line2D.Double(mouseDownPoint, lastMousePoint)); + g2.setColor(oldColor); + } + + private static final Color PURPLE = new Color(0.7f, 0.4f, 0.7f); + + private static final int CONNECT_THRESHOLD = 8; + + private Point2D mouseDownPoint = null; + + private Point2D lastMousePoint = null; + + private IEditorPart editorPart; + + private IGraph graph; + + private IEditorPartSelectionHandler selectionHandler; + + private IEditorPartBehaviorManager behaviorManager; + + private IGraphToolsBar graphToolsBar; + + private boolean isDraggingInProgress = false; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/AddNodeBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/AddNodeBehavior.java new file mode 100644 index 0000000..0c8066a --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/AddNodeBehavior.java @@ -0,0 +1,97 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.Id; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPartBehaviorManager; +import com.horstmann.violet.workspace.editorpart.IEditorPartSelectionHandler; +import com.horstmann.violet.workspace.editorpart.IGrid; +import com.horstmann.violet.workspace.sidebar.graphtools.GraphTool; +import com.horstmann.violet.workspace.sidebar.graphtools.IGraphToolsBar; + +public class AddNodeBehavior extends AbstractEditorPartBehavior +{ + + public AddNodeBehavior(IEditorPart editorPart, IGraphToolsBar graphToolsBar) + { + this.editorPart = editorPart; + this.graph = editorPart.getGraph(); + this.selectionHandler = editorPart.getSelectionHandler(); + this.behaviorManager = editorPart.getBehaviorManager(); + this.graphToolsBar = graphToolsBar; + } + + @Override + public void onMouseClicked(MouseEvent event) + { + if (event.getClickCount() > 1) + { + return; + } + if (event.getButton() != MouseEvent.BUTTON1) { + return; + } + if (GraphTool.SELECTION_TOOL.equals(this.graphToolsBar.getSelectedTool())) + { + return; + } + GraphTool selectedTool = this.selectionHandler.getSelectedTool(); + if (!INode.class.isInstance(selectedTool.getNodeOrEdge())) + { + return; + } + double zoom = editorPart.getZoomFactor(); + final Point2D mousePoint = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); + IGrid grid = editorPart.getGrid(); + Point2D newNodeLocation = grid.snap(mousePoint); + INode prototype = (INode) selectedTool.getNodeOrEdge(); + INode newNode = (INode) prototype.clone(); + newNode.setId(new Id()); + + boolean added = addNodeAtPoint(newNode, newNodeLocation); + if (added) + { + selectionHandler.setSelectedElement(newNode); + } + } + + /** + * Adds a new at a precise location + * + * @param newNode to be added + * @param location + * @return true if the node has been added + */ + public boolean addNodeAtPoint(INode newNode, Point2D location) + { + boolean isAdded = false; + this.behaviorManager.fireBeforeAddingNodeAtPoint(newNode, location); + try + { + if (graph.addNode(newNode, location)) + { + newNode.incrementRevision(); + isAdded = true; + } + } + finally + { + this.behaviorManager.fireAfterAddingNodeAtPoint(newNode, location); + } + return isAdded; + } + + private IEditorPart editorPart; + + private IGraph graph; + + private IEditorPartSelectionHandler selectionHandler; + + private IEditorPartBehaviorManager behaviorManager; + + private IGraphToolsBar graphToolsBar; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ChangeToolByWeelBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ChangeToolByWeelBehavior.java new file mode 100644 index 0000000..7f10a7d --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ChangeToolByWeelBehavior.java @@ -0,0 +1,47 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; + +import com.horstmann.violet.workspace.sidebar.graphtools.IGraphToolsBar; + +public class ChangeToolByWeelBehavior extends AbstractEditorPartBehavior +{ + + private IGraphToolsBar graphToolsBar; + + public ChangeToolByWeelBehavior(IGraphToolsBar graphToolsBar) + { + this.graphToolsBar = graphToolsBar; + } + + + @Override + public void onMouseWheelMoved(MouseWheelEvent event) + { + boolean isCtrl = (event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0; + if (isCtrl) { + return; + } + int scroll = event.getUnitsToScroll(); + if (scroll > 0) + { + this.graphToolsBar.selectNextTool(); + } + if (scroll < 0) + { + this.graphToolsBar.selectPreviousTool(); + } + } + + @Override + public void onMouseClicked(MouseEvent event) + { + if (event.getButton() == MouseEvent.BUTTON3) + { + this.graphToolsBar.reset(); + } + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/CutCopyPasteBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/CutCopyPasteBehavior.java new file mode 100644 index 0000000..f412a7a --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/CutCopyPasteBehavior.java @@ -0,0 +1,492 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.Toolkit; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoableEdit; + +import com.horstmann.violet.framework.file.GraphFile; +import com.horstmann.violet.framework.file.IGraphFile; +import com.horstmann.violet.framework.file.persistence.IFilePersistenceService; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.BeanInjector; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.Id; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPartBehaviorManager; +import com.horstmann.violet.workspace.editorpart.IEditorPartSelectionHandler; + +public class CutCopyPasteBehavior extends AbstractEditorPartBehavior +{ + + /** + * The concerned workspace + */ + private IEditorPart editorPart; + + /** + * Used to convert graph to XML and to get graph back from XML + */ + @InjectedBean + private IFilePersistenceService persistenceService; + + /** + * Keep mouse location to paste on just above the current mouse location + */ + private Point2D lastMouseLocation = new Point2D.Double(0, 0); + + /** + * Default constructor + * + * @param editorPart + */ + public CutCopyPasteBehavior(IEditorPart editorPart) + { + BeanInjector.getInjector().inject(this); + this.editorPart = editorPart; + } + + @Override + public void onMousePressed(MouseEvent event) + { + double zoom = editorPart.getZoomFactor(); + this.lastMouseLocation = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); + } + + /** + * Cuts selected graph elements + */ + public void cut() + { + copy(); + editorPart.removeSelected(); + editorPart.getSwingComponent().repaint(); + } + + /** + * Copies selected graph elements to system clipboard + */ + public void copy() + { + IGraph graph = editorPart.getGraph(); + Class graphClass = graph.getClass(); + IGraphFile newGraphFile = new GraphFile(graphClass); + IGraph newGraph = newGraphFile.getGraph(); + IEditorPartSelectionHandler selectionHandler = editorPart.getSelectionHandler(); + List selectedNodes = selectionHandler.getSelectedNodes(); + // We use an mapper for ids because they are changed when each node is added to the newGraph + Map idMapper = new HashMap(); + for (INode aSelectedNode : selectedNodes) + { + if (isAncestorInCollection(aSelectedNode, selectedNodes)) { + // In this case, id doesn't change. It only changes on newGraph.addNode() + Id currentId = aSelectedNode.getId(); + idMapper.put(currentId, currentId); + continue; + } + INode clone = aSelectedNode.clone(); + Id oldId = clone.getId(); + Point2D locationOnGraph = aSelectedNode.getLocationOnGraph(); + newGraph.addNode(clone, locationOnGraph); + Id newId = clone.getId(); + idMapper.put(oldId, newId); + } + List selectedEdges = selectionHandler.getSelectedEdges(); + for (IEdge aSelectedEdge : selectedEdges) + { + IEdge clone = aSelectedEdge.clone(); + Point2D startLocation = clone.getStartLocation(); + Point2D endLocation = clone.getEndLocation(); + Id oldStartId = clone.getStart().getId(); + Id oldEndId = clone.getEnd().getId(); + Id newStartId = idMapper.get(oldStartId); + Id newEndId = idMapper.get(oldEndId); + INode startNode = newGraph.findNode(newStartId); + INode endNode = newGraph.findNode(newEndId); + if (startNode != null && endNode != null) + { + newGraph.connect(clone, startNode, startLocation, endNode, endLocation); + } + } + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + persistenceService.write(newGraph, byteArrayOutputStream); + byteArrayOutputStream.toString(); + String xmlContent = byteArrayOutputStream.toString(); + pushContentToSystemClipboard(xmlContent); + } + + /** + * Paste elements from system wide clipboard to graph + */ + public void paste() + { + IGraph graph = this.editorPart.getGraph(); + try + { + String xmlContent = getContentFromSystemClipboard(); + if (xmlContent == null) + { + return; // If no content, we stop here + } + ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(xmlContent.getBytes()); + IGraph deserializedGraph = persistenceService.read(byteArrayInputStream); + deserializedGraph = translateToMouseLocation(deserializedGraph, this.lastMouseLocation); + + Collection nodesFromClipboard = deserializedGraph.getAllNodes(); + List nodes = filterOnNodePrototypes(nodesFromClipboard); + List nodesReallyPasted = new ArrayList(); + for (INode aNode : nodes) + { + if (isAncestorInCollection(aNode, nodes)) continue; + boolean isAdded = graph.addNode(aNode, aNode.getLocationOnGraph()); + if (isAdded) + { + nodesReallyPasted.add(aNode); + } + } + + Collection edgesFromClipboard = deserializedGraph.getAllEdges(); + List edges = filterOnEdgePrototypes(edgesFromClipboard); + List edgesReallyPasted = new ArrayList(); + for (IEdge anEdge : edges) + { + Point2D startLocation = anEdge.getStartLocation(); + Point2D endLocation = anEdge.getEndLocation(); + INode startNode = graph.findNode(anEdge.getStart().getId()); + INode endNode = graph.findNode(anEdge.getEnd().getId()); + if (startNode != null && endNode != null) + { + boolean isConnected = graph.connect(anEdge, startNode, startLocation, endNode, endLocation); + if (isConnected) + { + edgesReallyPasted.add(anEdge); + } + } + } + + addUndoRedoSupport(nodesReallyPasted, edgesReallyPasted); + selectPastedElements(nodesReallyPasted, edgesReallyPasted); + + editorPart.getSwingComponent().invalidate(); + editorPart.getSwingComponent().repaint(); + } + catch (IOException e) + { + // Nothing to do + } + } + + /** + * Adds Undo/Redo support to copy pastes + * + * @param nodesPasted + * @param edgesPasted + */ + private void addUndoRedoSupport(List nodesPasted, List edgesPasted) + { + IEditorPartBehaviorManager behaviorManager = this.editorPart.getBehaviorManager(); + List found = behaviorManager.getBehaviors(UndoRedoCompoundBehavior.class); + if (found.size() != 1) + { + return; + } + UndoRedoCompoundBehavior undoRedoBehavior = found.get(0); + + undoRedoBehavior.startHistoryCapture(); + CompoundEdit capturedEdit = undoRedoBehavior.getCurrentCapturedEdit(); + for (final INode aNode : nodesPasted) + { + UndoableEdit edit = new AbstractUndoableEdit() + { + @Override + public void undo() throws CannotUndoException + { + IGraph graph = editorPart.getGraph(); + graph.removeNode(aNode); + super.undo(); + } + + @Override + public void redo() throws CannotRedoException + { + super.redo(); + IGraph graph = editorPart.getGraph(); + graph.addNode(aNode, aNode.getLocationOnGraph()); + } + }; + capturedEdit.addEdit(edit); + } + + for (final IEdge anEdge : edgesPasted) + { + UndoableEdit edit = new AbstractUndoableEdit() + { + @Override + public void undo() throws CannotUndoException + { + IGraph graph = editorPart.getGraph(); + graph.removeEdge(anEdge); + super.undo(); + } + + @Override + public void redo() throws CannotRedoException + { + super.redo(); + IGraph graph = editorPart.getGraph(); + graph.connect(anEdge, anEdge.getStart(), anEdge.getStartLocation(), anEdge.getEnd(), anEdge.getEndLocation()); + } + }; + capturedEdit.addEdit(edit); + } + undoRedoBehavior.stopHistoryCapture(); + } + + private void selectPastedElements(List nodesPasted, List edgesPasted) + { + IEditorPartSelectionHandler selectionHandler = this.editorPart.getSelectionHandler(); + selectionHandler.clearSelection(); + for (final INode aNode : nodesPasted) + { + selectionHandler.addSelectedElement(aNode); + } + for (final IEdge anEdge : edgesPasted) + { + selectionHandler.addSelectedElement(anEdge); + } + } + + /** + * As we can copy/paste on many diagrams, we ensure that we paste only node types acceptable for the current diagram + * + * @param nodes from clipboard + * @return nodes acceptable for the current diagram + */ + private List filterOnNodePrototypes(Collection nodes) + { + IGraph currentGraph = this.editorPart.getGraph(); + List nodePrototypes = currentGraph.getNodePrototypes(); + List> classPrototypes = new ArrayList>(); + for (INode aNodePrototype : nodePrototypes) + { + classPrototypes.add(aNodePrototype.getClass()); + } + + List result = new ArrayList(); + for (INode aNode : nodes) + { + Class nodeClass = aNode.getClass(); + if (classPrototypes.contains(nodeClass)) + { + result.add(aNode); + } + } + return result; + } + + /** + * As we can copy/paste on many diagrams, we ensure that we paste only edge types acceptable for the current diagram + * + * @param edges from clipboard + * @return edges acceptable for the current diagram + */ + private List filterOnEdgePrototypes(Collection edges) + { + IGraph currentGraph = this.editorPart.getGraph(); + List edgePrototypes = currentGraph.getEdgePrototypes(); + List> classPrototypes = new ArrayList>(); + for (IEdge aEdgePrototype : edgePrototypes) + { + classPrototypes.add(aEdgePrototype.getClass()); + } + + List result = new ArrayList(); + for (IEdge aEdge : edges) + { + Class nodeClass = aEdge.getClass(); + if (classPrototypes.contains(nodeClass)) + { + result.add(aEdge); + } + } + return result; + } + + /** + * Moves all the nodes of a graph to a location + * + * @param graph + * @param mouseLocation + * @return the modified graph + */ + private IGraph translateToMouseLocation(IGraph graph, Point2D mouseLocation) + { + Rectangle2D clipBounds = graph.getClipBounds(); + double dx = mouseLocation.getX() - clipBounds.getX(); + double dy = mouseLocation.getY() - clipBounds.getY(); + Collection nodes = graph.getAllNodes(); + for (INode aNode : nodes) + { + boolean hasParent = (aNode.getParent() != null); + if (!hasParent) + { + aNode.translate(dx, dy); + } + } + return graph; + } + + /** + * Deals with system wide clipboard + * + * @param content + */ + private void pushContentToSystemClipboard(String content) + { + StringSelection dataToClip = new StringSelection(content); + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + clipboard.setContents(dataToClip, dataToClip); + } + + /** + * Deals with system wide clipboard + * + * @return + */ + private String getContentFromSystemClipboard() + { + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + Transferable clipData = clipboard.getContents(clipboard); + if (clipData != null) + { + try + { + if (clipData.isDataFlavorSupported(DataFlavor.stringFlavor)) + { + String s = (String) (clipData.getTransferData(DataFlavor.stringFlavor)); + return s; + } + } + catch (UnsupportedFlavorException ufe) + { + return null; + } + catch (IOException ioe) + { + return null; + } + } + return null; + } + + /** + * Converts a string into a bytebuffer + * + * @param msg + * @return + */ + private ByteBuffer convertToByteBuffer(String msg) + { + return ByteBuffer.wrap(msg.getBytes()); + } + + /** + * Converts a bytebuffer into a string + * + * @param bytebuffer + * @return + */ + private String convertToString(ByteBuffer bytebuffer) + { + byte[] bytearray = new byte[bytebuffer.remaining()]; + bytebuffer.get(bytearray); + String s = new String(bytearray); + return s; + } + + /** + * Checks if the given list contains an ancestor of the given node + * + * @param childNode + * @param ancestorList + * @return b + */ + private boolean isAncestorInCollection(INode childNode, Collection ancestorList) + { + for (INode anAncestorNode : ancestorList) + { + boolean ancestorRelationship = isAncestorRelationship(childNode, anAncestorNode); + if (ancestorRelationship) return true; + } + return false; + } + + /** + * Checks if ancestorNode is a parent node of child node + * + * @param childNode + * @param ancestorNode + * @return b + */ + private boolean isAncestorRelationship(INode childNode, INode ancestorNode) + { + INode parent = childNode.getParent(); + if (parent == null) + { + return false; + } + List fifo = new ArrayList(); + fifo.add(parent); + while (!fifo.isEmpty()) + { + INode aParentNode = fifo.get(0); + fifo.remove(0); + if (aParentNode.equals(ancestorNode)) + { + return true; + } + INode aGranParent = aParentNode.getParent(); + if (aGranParent != null) + { + fifo.add(aGranParent); + } + } + return false; + } + + private List getFamily(INode aParentNode) { + List family = new ArrayList(); + List fifo = new ArrayList(); + fifo.addAll(aParentNode.getChildren()); + while (!fifo.isEmpty()) { + INode aFamilyMember = fifo.get(0); + family.add(aFamilyMember); + fifo.addAll(aFamilyMember.getChildren()); + fifo.remove(0); + } + return family; + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/DragGraphBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/DragGraphBehavior.java new file mode 100644 index 0000000..8dc90f8 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/DragGraphBehavior.java @@ -0,0 +1,137 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.Cursor; +import java.awt.Point; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; + +import javax.swing.JScrollBar; +import javax.swing.JScrollPane; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.Workspace; +import com.horstmann.violet.workspace.WorkspacePanel; +import com.horstmann.violet.workspace.editorpart.IEditorPart; + +public class DragGraphBehavior extends AbstractEditorPartBehavior +{ + + private Workspace workspace = null; + + private Point2D initialMousePoint = null; + + private Cursor initialCursor = null; + + private Cursor dragCursor = new Cursor(Cursor.HAND_CURSOR); + + private int initialHorizontalScrollBarValue = -1; + + private int initialVerticalScrollBarValue = -1; + + private boolean isReadyForDragging = false; + + public DragGraphBehavior(Workspace workspace) + { + this.workspace = workspace; + } + + + @Override + public void onMousePressed(MouseEvent event) + { + if (event.getClickCount() > 1) + { + return; + } + if (event.getButton() != MouseEvent.BUTTON1) { + return; + } + + boolean isCtrl = (event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0; + if (!isCtrl) { + return; + } + + IEditorPart editorPart = this.workspace.getEditorPart(); + double zoom = editorPart.getZoomFactor(); + final Point2D mousePointOnGraph = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); + if (!isMouseOnNode(mousePointOnGraph) && !isMouseOnEdge(mousePointOnGraph)) + { + WorkspacePanel workspacePanel = this.workspace.getAWTComponent(); + JScrollPane scrollableEditorPart = workspacePanel.getScrollableEditorPart(); + JScrollBar verticalScrollBar = scrollableEditorPart.getVerticalScrollBar(); + JScrollBar horizontalScrollBar = scrollableEditorPart.getHorizontalScrollBar(); + + this.isReadyForDragging = true; + this.initialMousePoint = event.getLocationOnScreen(); + this.initialHorizontalScrollBarValue = horizontalScrollBar.getValue(); + this.initialVerticalScrollBarValue = verticalScrollBar.getValue(); + this.initialCursor = scrollableEditorPart.getCursor(); + + scrollableEditorPart.setCursor(this.dragCursor); + } + } + + private boolean isMouseOnNode(Point2D mouseLocation) + { + IEditorPart editorPart = this.workspace.getEditorPart(); + IGraph graph = editorPart.getGraph(); + INode node = graph.findNode(mouseLocation); + if (node == null) + { + return false; + } + return true; + } + + private boolean isMouseOnEdge(Point2D mouseLocation) + { + IEditorPart editorPart = this.workspace.getEditorPart(); + IGraph graph = editorPart.getGraph(); + IEdge edge = graph.findEdge(mouseLocation); + if (edge == null) + { + return false; + } + return true; + } + + @Override + public void onMouseDragged(MouseEvent event) + { + if (!isReadyForDragging) + { + return; + } + + Point mousePoint = event.getLocationOnScreen(); + double dx = mousePoint.getX() - initialMousePoint.getX(); + double dy = mousePoint.getY() - initialMousePoint.getY(); + + WorkspacePanel workspacePanel = this.workspace.getAWTComponent(); + JScrollPane scrollableEditorPart = workspacePanel.getScrollableEditorPart(); + JScrollBar verticalScrollBar = scrollableEditorPart.getVerticalScrollBar(); + JScrollBar horizontalScrollBar = scrollableEditorPart.getHorizontalScrollBar(); + horizontalScrollBar.setValue(this.initialHorizontalScrollBarValue - (int) dx); + verticalScrollBar.setValue(this.initialVerticalScrollBarValue - (int) dy); + } + + @Override + public void onMouseReleased(MouseEvent event) + { + WorkspacePanel workspacePanel = this.workspace.getAWTComponent(); + JScrollPane scrollableEditorPart = workspacePanel.getScrollableEditorPart(); + scrollableEditorPart.setCursor(this.initialCursor); + + this.initialMousePoint = null; + this.initialHorizontalScrollBarValue = -1; + this.initialVerticalScrollBarValue = -1; + this.isReadyForDragging = false; + this.initialCursor = null; + } + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/DragSelectedBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/DragSelectedBehavior.java new file mode 100644 index 0000000..834b611 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/DragSelectedBehavior.java @@ -0,0 +1,125 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.List; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPartSelectionHandler; +import com.horstmann.violet.workspace.editorpart.IGrid; +import com.horstmann.violet.workspace.sidebar.graphtools.GraphTool; +import com.horstmann.violet.workspace.sidebar.graphtools.IGraphToolsBar; + +public class DragSelectedBehavior extends AbstractEditorPartBehavior +{ + + public DragSelectedBehavior(IEditorPart editorPart, IGraphToolsBar graphToolsBar) + { + this.editorPart = editorPart; + this.graph = editorPart.getGraph(); + this.selectionHandler = editorPart.getSelectionHandler(); + this.graphToolsBar = graphToolsBar; + } + + @Override + public void onMousePressed(MouseEvent event) + { + if (event.getClickCount() > 1) + { + return; + } + if (event.getButton() != MouseEvent.BUTTON1) { + return; + } + if (!GraphTool.SELECTION_TOOL.equals(this.graphToolsBar.getSelectedTool())) + { + return; + } + double zoom = editorPart.getZoomFactor(); + final Point2D mousePoint = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); + if (isMouseOnNode(mousePoint)) + { + isReadyForDragging = true; + lastMousePoint = mousePoint; + } + } + + private boolean isMouseOnNode(Point2D mouseLocation) + { + INode node = this.graph.findNode(mouseLocation); + if (node == null) + { + return false; + } + return true; + } + + @Override + public void onMouseDragged(MouseEvent event) + { + if (!isReadyForDragging) + { + return; + } + double zoom = editorPart.getZoomFactor(); + Point2D mousePoint = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); + // TODO : behaviorManager.fireOnElementsDragged(selectionHandler.getSelectedNodes(), selectionHandler.getSelectedEdges()); + INode lastNode = selectionHandler.getLastSelectedNode(); + if (lastNode == null) { + return; + } + Rectangle2D bounds = lastNode.getBounds(); + double dx = mousePoint.getX() - lastMousePoint.getX(); + double dy = mousePoint.getY() - lastMousePoint.getY(); + + // we don't want to drag nodes into negative coordinates + // particularly with multiple selection, we might never be + // able to get them back. + List selectedNodes = selectionHandler.getSelectedNodes(); + for (INode n : selectedNodes) + bounds.add(n.getBounds()); + dx = Math.max(dx, -bounds.getX()); + dy = Math.max(dy, -bounds.getY()); + + boolean isAtLeastOneNodeMoved = false; + IGrid grid = editorPart.getGrid(); + for (INode n : selectedNodes) + { + if (selectedNodes.contains(n.getParent())) continue; // parents are responsible for translating their children + Point2D currentNodeLocation = n.getLocation(); + Point2D futureNodeLocation = new Point2D.Double(currentNodeLocation.getX() + dx, currentNodeLocation.getY() + dy); + Point2D fixedFutureNodeLocation = grid.snap(futureNodeLocation); + if (!currentNodeLocation.equals(fixedFutureNodeLocation)) { + n.setLocation(fixedFutureNodeLocation); + isAtLeastOneNodeMoved = true; + } + } + if (isAtLeastOneNodeMoved) { + lastMousePoint = grid.snap(mousePoint); + } + } + + @Override + public void onMouseReleased(MouseEvent event) + { + lastMousePoint = null; + isReadyForDragging = false; + editorPart.getSwingComponent().revalidate(); + editorPart.getSwingComponent().repaint(); + } + + private IGraph graph; + + private Point2D lastMousePoint = null; + + private IEditorPartSelectionHandler selectionHandler; + + private IEditorPart editorPart; + + private IGraphToolsBar graphToolsBar; + + private boolean isReadyForDragging = false; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/EditSelectedBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/EditSelectedBehavior.java new file mode 100644 index 0000000..cabc3d3 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/EditSelectedBehavior.java @@ -0,0 +1,161 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.Font; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import javax.swing.JLabel; +import javax.swing.JOptionPane; + +import com.horstmann.violet.framework.dialog.DialogFactory; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.BeanInjector; +import com.horstmann.violet.framework.injection.bean.ManiocFramework.InjectedBean; +import com.horstmann.violet.framework.injection.resources.ResourceBundleInjector; +import com.horstmann.violet.framework.injection.resources.annotation.ResourceBundleBean; +import com.horstmann.violet.framework.propertyeditor.CustomPropertyEditor; +import com.horstmann.violet.framework.propertyeditor.ICustomPropertyEditor; +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.product.diagram.common.DiagramLinkNode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPartBehaviorManager; +import com.horstmann.violet.workspace.editorpart.IEditorPartSelectionHandler; + +public class EditSelectedBehavior extends AbstractEditorPartBehavior +{ + + public EditSelectedBehavior(IEditorPart editorPart) + { + BeanInjector.getInjector().inject(this); + ResourceBundleInjector.getInjector().inject(this); + this.editorPart = editorPart; + this.graph = editorPart.getGraph(); + this.selectionHandler = editorPart.getSelectionHandler(); + this.behaviorManager = editorPart.getBehaviorManager(); + } + + @Override + public void onMouseClicked(MouseEvent event) + { + boolean isButton1Clicked = (event.getButton() == MouseEvent.BUTTON1); + if (event.getClickCount() > 1 && isButton1Clicked) + { + double zoom = editorPart.getZoomFactor(); + Point2D mouseLocation = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); + this.selectionHandler.clearSelection(); + INode node = this.graph.findNode(mouseLocation); + IEdge edge = this.graph.findEdge(mouseLocation); + if (node != null) { + this.selectionHandler.setSelectedElement(node); + } else if (edge != null) { + this.selectionHandler.addSelectedElement(edge); + } + editSelected(); + } + } + + public void editSelected() + { + final Object edited = selectionHandler.isNodeSelectedAtLeast() ? selectionHandler.getLastSelectedNode() : selectionHandler.getLastSelectedEdge(); + if (edited == null) + { + return; + } + final ICustomPropertyEditor sheet = new CustomPropertyEditor(edited); + + sheet.addPropertyChangeListener(new PropertyChangeListener() + { + public void propertyChange(final PropertyChangeEvent event) + { + // TODO : fix open file event + if (event.getSource() instanceof DiagramLinkNode) + { + // DiagramLinkNode ln = (DiagramLinkNode) event.getSource(); + // DiagramLink dl = ln.getDiagramLink(); + // if (dl != null && dl.getOpenFlag().booleanValue()) + // { + // diagramPanel.fireMustOpenFile(dl.getFile()); + // dl.setOpenFlag(new Boolean(false)); + // } + } + + if (edited instanceof INode) + { + behaviorManager.fireWhileEditingNode((INode) edited, event); + } + if (edited instanceof IEdge) + { + behaviorManager.fireWhileEditingEdge((IEdge) edited, event); + } + } + }); + + JOptionPane optionPane = new JOptionPane(); + optionPane.setOpaque(true); + optionPane.addPropertyChangeListener(new PropertyChangeListener() + { + public void propertyChange(PropertyChangeEvent event) + { + if ((event.getPropertyName().equals(JOptionPane.VALUE_PROPERTY)) && event.getNewValue() != null && event.getNewValue() != JOptionPane.UNINITIALIZED_VALUE) + { + if (sheet.isEditable()) + { + // This manages optionPane submits through a property + // listener because, as dialog display could be + // delegated + // (to Eclipse for example), host system can work in + // other threads + if (edited instanceof INode) + { + behaviorManager.fireAfterEditingNode((INode) edited); + } + if (edited instanceof IEdge) + { + behaviorManager.fireAfterEditingEdge((IEdge) edited); + } + } + } + } + }); + + if (sheet.isEditable()) + { + if (edited instanceof INode) + { + this.behaviorManager.fireBeforeEditingNode((INode) edited); + } + if (edited instanceof IEdge) + { + this.behaviorManager.fireBeforeEditingEdge((IEdge) edited); + } + optionPane.setMessage(sheet.getAWTComponent()); + } + if (!sheet.isEditable()) + { + JLabel label = new JLabel(this.uneditableBeanMessage); + label.setFont(label.getFont().deriveFont(Font.PLAIN)); + optionPane.setMessage(label); + } + this.dialogFactory.showDialog(optionPane, this.dialogTitle, true); + } + + + + private IEditorPartSelectionHandler selectionHandler; + private IEditorPart editorPart; + private IGraph graph; + private IEditorPartBehaviorManager behaviorManager; + + @InjectedBean + private DialogFactory dialogFactory; + + @ResourceBundleBean(key = "edit.properties.title") + private String dialogTitle; + + @ResourceBundleBean(key = "edit.properties.empty_bean_message") + private String uneditableBeanMessage; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/EditSelectedBehavior.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/EditSelectedBehavior.properties new file mode 100644 index 0000000..9df5177 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/EditSelectedBehavior.properties @@ -0,0 +1,3 @@ +edit.properties.title=Properties +edit.properties.empty_bean_message=No property to edit. + diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/EditSelectedBehavior_fr.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/EditSelectedBehavior_fr.properties new file mode 100644 index 0000000..12e7b1f --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/EditSelectedBehavior_fr.properties @@ -0,0 +1,4 @@ +edit.properties.title=Propri\u00E9t\u00E9s +edit.properties.empty_bean_message=Aucune propri\u00E9t\u00E9 \u00E0 \u00E9diter. + + diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/FileCouldBeSavedBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/FileCouldBeSavedBehavior.java new file mode 100644 index 0000000..5d1487d --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/FileCouldBeSavedBehavior.java @@ -0,0 +1,58 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; + +import com.horstmann.violet.framework.file.IGraphFile; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; + +public class FileCouldBeSavedBehavior extends AbstractEditorPartBehavior +{ + + public FileCouldBeSavedBehavior(IGraphFile graphFile) + { + this.graphFile = graphFile; + } + + @Override + public void afterAddingEdgeAtPoints(IEdge edge, Point2D startPoint, Point2D endPoint) + { + graphFile.setSaveRequired(); + } + + @Override + public void afterAddingNodeAtPoint(INode node, Point2D location) + { + graphFile.setSaveRequired(); + } + + @Override + public void afterEditingEdge(IEdge edge) + { + graphFile.setSaveRequired(); + } + + @Override + public void afterEditingNode(INode node) + { + graphFile.setSaveRequired(); + } + + @Override + public void afterRemovingSelectedElements() + { + graphFile.setSaveRequired(); + } + + + @Override + public void onMouseDragged(MouseEvent event) + { + graphFile.setSaveRequired(); + // FIXME : when behaviorHandler will manage new events such as onNodeDragged + } + + private IGraphFile graphFile; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/IEditorPartBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/IEditorPartBehavior.java new file mode 100644 index 0000000..c4ffb7a --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/IEditorPartBehavior.java @@ -0,0 +1,60 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.geom.Point2D; +import java.beans.PropertyChangeEvent; + +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.sidebar.graphtools.GraphTool; + +public interface IEditorPartBehavior +{ + + public void onMousePressed(MouseEvent event); + + public void onMouseDragged(MouseEvent event); + + public void onMouseReleased(MouseEvent event); + + public void onMouseClicked(MouseEvent event); + + public void onMouseMoved(MouseEvent event); + + public void onMouseWheelMoved(MouseWheelEvent event); + + public void onToolSelected(GraphTool selectedTool); + + public void onNodeSelected(INode node); + + public void onEdgeSelected(IEdge edge); + + public void beforeEditingNode(INode node); + + public void whileEditingNode(INode node, PropertyChangeEvent event); + + public void afterEditingNode(INode node); + + public void beforeEditingEdge(IEdge edge); + + public void whileEditingEdge(IEdge edge, PropertyChangeEvent event); + + public void afterEditingEdge(IEdge edge); + + public void beforeRemovingSelectedElements(); + + public void afterRemovingSelectedElements(); + + public void beforeAddingNodeAtPoint(INode node, Point2D location); + + public void afterAddingNodeAtPoint(INode node, Point2D location); + + public void beforeAddingEdgeAtPoints(IEdge edge, Point2D startPoint, Point2D endPoint); + + public void afterAddingEdgeAtPoints(IEdge edge, Point2D startPoint, Point2D endPoint); + + public void onPaint(Graphics2D g2); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ResizeNodeBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ResizeNodeBehavior.java new file mode 100644 index 0000000..4d1283a --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ResizeNodeBehavior.java @@ -0,0 +1,64 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.Cursor; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.List; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.product.diagram.abstracts.node.IResizableNode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPartSelectionHandler; +import com.horstmann.violet.workspace.sidebar.graphtools.IGraphToolsBar; + +public class ResizeNodeBehavior extends AbstractEditorPartBehavior +{ + + public ResizeNodeBehavior(IEditorPart editorPart, IGraphToolsBar graphToolsBar) + { + this.editorPart = editorPart; + this.graph = editorPart.getGraph(); + this.selectionHandler = editorPart.getSelectionHandler(); + this.graphToolsBar = graphToolsBar; + } + + @Override + public void onMouseMoved(MouseEvent event) + { + List selectedNodes = selectionHandler.getSelectedNodes(); + List selectedEdges = selectionHandler.getSelectedEdges(); + boolean isOnlyOneNodeSelected = (selectedNodes.size() == 1 && selectedEdges.size() == 0); + if (!isOnlyOneNodeSelected) { + return; + } + INode selectedNode = selectedNodes.get(0); + if (!selectedNode.getClass().isAssignableFrom(IResizableNode.class)) { + return; + } + double zoom = editorPart.getZoomFactor(); + final Point2D mousePoint = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); + Rectangle2D bounds = selectedNode.getBounds(); + if (bounds.contains(mousePoint)) { + editorPart.getSwingComponent().setCursor(Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR)); + } else { + editorPart.getSwingComponent().setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR)); + } + } + + + + private IGraph graph; + + private Point2D lastMousePoint = null; + + private IEditorPartSelectionHandler selectionHandler; + + private IEditorPart editorPart; + + private IGraphToolsBar graphToolsBar; + + private boolean isReadyForDragging = false; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectAllBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectAllBehavior.java new file mode 100644 index 0000000..f5d9415 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectAllBehavior.java @@ -0,0 +1,61 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.util.ArrayList; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPartBehaviorManager; +import com.horstmann.violet.workspace.editorpart.IEditorPartSelectionHandler; +import com.horstmann.violet.workspace.sidebar.graphtools.IGraphToolsBar; + +public class SelectAllBehavior extends AbstractEditorPartBehavior +{ + + + public SelectAllBehavior(IEditorPart editorPart, IGraphToolsBar graphToolsBar) + { + this.graph = editorPart.getGraph(); + this.selectionHandler = editorPart.getSelectionHandler(); + this.behaviorManager = editorPart.getBehaviorManager(); + this.graphToolsBar = graphToolsBar; + } + + + /** + * Selects all graph element. + * + */ + public void selectAllGraphElements() + { + ArrayList selectables = new ArrayList(); + selectables.addAll(graph.getAllNodes()); + selectables.addAll(graph.getAllEdges()); + if (selectables.size() == 0) return; + selectionHandler.clearSelection(); + for (Object toSelect : selectables) { + if (toSelect instanceof INode) + { + selectionHandler.addSelectedElement((INode) toSelect); + behaviorManager.fireOnNodeSelected((INode) toSelect); + } + if (toSelect instanceof IEdge) + { + selectionHandler.addSelectedElement((IEdge) toSelect); + behaviorManager.fireOnEdgeSelected((IEdge) toSelect); + } + } + graphToolsBar.reset(); + } + + private IGraph graph; + + private IEditorPartSelectionHandler selectionHandler; + + private IEditorPartBehaviorManager behaviorManager; + + private IGraphToolsBar graphToolsBar; + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectByClickBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectByClickBehavior.java new file mode 100644 index 0000000..40992fc --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectByClickBehavior.java @@ -0,0 +1,240 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.Graphics2D; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.geom.Line2D; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.List; + +import com.horstmann.violet.framework.util.GrabberUtils; +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPartSelectionHandler; +import com.horstmann.violet.workspace.sidebar.graphtools.GraphTool; +import com.horstmann.violet.workspace.sidebar.graphtools.IGraphToolsBar; + +public class SelectByClickBehavior extends AbstractEditorPartBehavior +{ + + public SelectByClickBehavior(IEditorPart editorPart, IGraphToolsBar graphToolsBar) + { + this.editorPart = editorPart; + this.graph = editorPart.getGraph(); + this.selectionHandler = editorPart.getSelectionHandler(); + this.graphToolsBar = graphToolsBar; + } + + @Override + public void onMousePressed(MouseEvent event) + { + resetEventAttributes(); + + if (event.getClickCount() > 1) { + return; + } + if (event.getButton() != MouseEvent.BUTTON1) { + return; + } + if (!GraphTool.SELECTION_TOOL.equals(this.graphToolsBar.getSelectedTool())) { + return; + } + double zoom = editorPart.getZoomFactor(); + Point2D mousePoint = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); + boolean isCtrl = (event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0; + boolean isOnNodeOrEdge = isMouseOnNodeOrEdge(mousePoint); + if (!isOnNodeOrEdge && !isCtrl) + { + resetSelectedElements(); + return; + } + if (isOnNodeOrEdge && !isCtrl) + { + processSelection(mousePoint, true); + return; + } + if (isOnNodeOrEdge && isCtrl) + { + processSelection(mousePoint, false); + return; + } + } + + + @Override + public void onMouseDragged(MouseEvent event) + { + this.isDragGesture = true; + } + + @Override + public void onMouseReleased(MouseEvent event) + { + if (event.getClickCount() > 1) { + return; + } + if (event.getButton() != MouseEvent.BUTTON1) { + return; + } + if (this.isDragGesture) { + return; + } + boolean isCtrl = (event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0; + if (isCtrl) { + processSelectionInConflictWithDraggingEvents(false); + } + if (!isCtrl) { + processSelectionInConflictWithDraggingEvents(true); + } + } + + private void resetEventAttributes() + { + this.isDragGesture = false; + this.unprocessedNode = null; + this.unprocessedEdge = null; + } + + + private boolean isMouseOnNodeOrEdge(Point2D mouseLocation) + { + INode node = this.graph.findNode(mouseLocation); + IEdge edge = this.graph.findEdge(mouseLocation); + if (node == null && edge == null) + { + return false; + } + return true; + } + + private void resetSelectedElements() + { + this.selectionHandler.clearSelection(); + } + + /** + * Here, we add or remove the selected node or edge to the global selection. Under the wood, we can't remove anything. + * This can only made on 'mouse released' to avoid conflict to any dragging event. + * + * @param mouseLocation + * @param isResetSelectionFirst + */ + private void processSelection(Point2D mouseLocation, boolean isResetSelectionFirst) + { + INode node = this.graph.findNode(mouseLocation); + IEdge edge = this.graph.findEdge(mouseLocation); + if (edge != null) + { + if (this.selectionHandler.isElementAlreadySelected(edge)) + { + // This edge will be removed only on mouse button released + // to avoid conflicts with dragging events + this.unprocessedEdge = edge; + } + else + { + if (isResetSelectionFirst) { + resetSelectedElements(); + } + this.selectionHandler.addSelectedElement(edge); + } + return; + } + if (node != null) + { + if (this.selectionHandler.isElementAlreadySelected(node)) + { + // This node will be removed only on mouse button released + // to avoid conflicts with dragging events + this.unprocessedNode = node; + } + else + { + if (isResetSelectionFirst) { + resetSelectedElements(); + } + this.selectionHandler.addSelectedElement(node); + } + return; + } + } + + /** + * We process nodes or edges on 'mouse released' event because it's not possible to remove nodes or edges + * from selection on 'mouse pressed' because it can be part of a dragging intention from the user. So, + * unprocessed elements are removed from selection only on 'mouse released' if the user is pressing CTRL. + * They are set as unique selection if the user isn't pressing CTRL. + * + * @param isResetSelectionFirst + */ + private void processSelectionInConflictWithDraggingEvents(boolean isResetSelectionFirst) { + if (this.unprocessedNode == null && this.unprocessedEdge == null) { + return; + } + if (isResetSelectionFirst) { + resetSelectedElements(); + if (this.unprocessedNode != null) { + this.selectionHandler.addSelectedElement(this.unprocessedNode); + } + if (this.unprocessedEdge != null) { + this.selectionHandler.addSelectedElement(this.unprocessedEdge); + } + } + if (!isResetSelectionFirst) { + if (this.unprocessedNode != null) { + this.selectionHandler.removeElementFromSelection(this.unprocessedNode); + } + if (this.unprocessedEdge != null) { + this.selectionHandler.removeElementFromSelection(this.unprocessedEdge); + } + } + } + + @Override + public void onPaint(Graphics2D g2) + { + List nodes = selectionHandler.getSelectedNodes(); + for (INode n : nodes) + { + if (graph.getAllNodes().contains(n)) + { + Point2D nodeLocationOnGraph = n.getLocationOnGraph(); + Rectangle2D nodeBounds = n.getBounds(); + GrabberUtils.drawGrabber(g2, nodeLocationOnGraph.getX(), nodeLocationOnGraph.getY()); + GrabberUtils.drawGrabber(g2, nodeLocationOnGraph.getX(), nodeLocationOnGraph.getY() + nodeBounds.getHeight()); + GrabberUtils.drawGrabber(g2, nodeLocationOnGraph.getX() + nodeBounds.getWidth(), nodeLocationOnGraph.getY()); + GrabberUtils.drawGrabber(g2, nodeLocationOnGraph.getX() + nodeBounds.getWidth(), nodeLocationOnGraph.getY() + nodeBounds.getHeight()); + } + } + List edges = selectionHandler.getSelectedEdges(); + for (IEdge e : edges) + { + if (graph.getAllEdges().contains(e)) + { + Line2D line = e.getConnectionPoints(); + GrabberUtils.drawGrabber(g2, line.getX1(), line.getY1()); + GrabberUtils.drawGrabber(g2, line.getX2(), line.getY2()); + } + } + } + + + private IEditorPart editorPart; + + private IGraph graph; + + private IEditorPartSelectionHandler selectionHandler; + + private IGraphToolsBar graphToolsBar; + + private boolean isDragGesture = false; + + private INode unprocessedNode = null; + + private IEdge unprocessedEdge = null; + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectByDistanceBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectByDistanceBehavior.java new file mode 100644 index 0000000..16594dc --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectByDistanceBehavior.java @@ -0,0 +1,116 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.ArrayList; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPartBehaviorManager; +import com.horstmann.violet.workspace.editorpart.IEditorPartSelectionHandler; + +/** + * Allows to select the next or the previous element (node or edge) by specifying a distance with the current one + * + * @author Alexandre de Pellegrin + * + */ +public class SelectByDistanceBehavior extends AbstractEditorPartBehavior +{ + + public SelectByDistanceBehavior(IEditorPart editorPart) + { + this.graph = editorPart.getGraph(); + this.selectionHandler = editorPart.getSelectionHandler(); + this.behaviorManager = editorPart.getBehaviorManager(); + } + + + /** + * Selects another graph element. + * + * @param distanceFormCurrentElement distance from the currently selected element. For example : -1 for the previous element and + * +1 for the next one. + */ + public void selectAnotherGraphElement(int distanceFromCurrentElement) + { + ArrayList selectables = new ArrayList(); + selectables.addAll(graph.getAllNodes()); + selectables.addAll(graph.getAllEdges()); + if (selectables.size() == 0) return; + java.util.Collections.sort(selectables, new java.util.Comparator() + { + public int compare(Object obj1, Object obj2) + { + double x1; + double y1; + if (obj1 instanceof INode) + { + Rectangle2D bounds = ((INode) obj1).getBounds(); + x1 = bounds.getX(); + y1 = bounds.getY(); + } + else + { + Point2D start = ((IEdge) obj1).getConnectionPoints().getP1(); + x1 = start.getX(); + y1 = start.getY(); + } + double x2; + double y2; + if (obj2 instanceof INode) + { + Rectangle2D bounds = ((INode) obj2).getBounds(); + x2 = bounds.getX(); + y2 = bounds.getY(); + } + else + { + Point2D start = ((IEdge) obj2).getConnectionPoints().getP1(); + x2 = start.getX(); + y2 = start.getY(); + } + if (y1 < y2) return -1; + if (y1 > y2) return 1; + if (x1 < x2) return -1; + if (x1 > x2) return 1; + return 0; + } + }); + int index; + Object lastSelected = null; + if (selectionHandler.isNodeSelectedAtLeast()) + { + lastSelected = selectionHandler.getLastSelectedNode(); + } + if (selectionHandler.isEdgeSelectedAtLeast()) + { + lastSelected = selectionHandler.getLastSelectedEdge(); + } + if (lastSelected == null) index = 0; + else index = selectables.indexOf(lastSelected) + distanceFromCurrentElement; + while (index < 0) + index += selectables.size(); + index %= selectables.size(); + Object toSelect = selectables.get(index); + if (toSelect instanceof INode) + { + selectionHandler.setSelectedElement((INode) toSelect); + behaviorManager.fireOnNodeSelected((INode) toSelect); + } + if (toSelect instanceof IEdge) + { + selectionHandler.setSelectedElement((IEdge) toSelect); + behaviorManager.fireOnEdgeSelected((IEdge) toSelect); + } + } + + private IGraph graph; + + private IEditorPartSelectionHandler selectionHandler; + + private IEditorPartBehaviorManager behaviorManager; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectByLassoBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectByLassoBehavior.java new file mode 100644 index 0000000..d3e9849 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SelectByLassoBehavior.java @@ -0,0 +1,140 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.event.InputEvent; +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Iterator; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPartSelectionHandler; +import com.horstmann.violet.workspace.sidebar.graphtools.GraphTool; +import com.horstmann.violet.workspace.sidebar.graphtools.IGraphToolsBar; + +public class SelectByLassoBehavior extends AbstractEditorPartBehavior +{ + + public SelectByLassoBehavior(IEditorPart editorPart, IGraphToolsBar graphToolsBar) + { + this.editorPart = editorPart; + this.graph = editorPart.getGraph(); + this.selectionHandler = editorPart.getSelectionHandler(); + this.graphToolsBar = graphToolsBar; + } + + @Override + public void onMousePressed(MouseEvent event) + { + if (event.getClickCount() > 1) { + return; + } + if (event.getButton() != MouseEvent.BUTTON1) { + return; + } + if (!GraphTool.SELECTION_TOOL.equals(this.graphToolsBar.getSelectedTool())) { + return; + } + double zoom = editorPart.getZoomFactor(); + final Point2D mousePoint = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); + if (!isMouseOnNodeOrEdge(mousePoint)) + { + resetSelectedElements(); + mouseDownPoint = mousePoint; + lastMousePoint = mousePoint; + } + } + + @Override + public void onMouseDragged(MouseEvent event) + { + if (mouseDownPoint == null) + { + return; + } + double zoom = editorPart.getZoomFactor(); + Point2D mousePoint = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); + lastMousePoint = mousePoint; + boolean isCtrl = (event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0; + double x1 = mouseDownPoint.getX(); + double y1 = mouseDownPoint.getY(); + double x2 = mousePoint.getX(); + double y2 = mousePoint.getY(); + Rectangle2D.Double lasso = new Rectangle2D.Double(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2)); + Iterator iter = graph.getAllNodes().iterator(); + while (iter.hasNext()) + { + INode n = (INode) iter.next(); + Rectangle2D bounds = n.getBounds(); + if (!isCtrl && !lasso.contains(bounds)) + { + selectionHandler.removeElementFromSelection(n); + } + else if (lasso.contains(bounds)) + { + selectionHandler.addSelectedElement(n); + } + } + } + + @Override + public void onMouseReleased(MouseEvent event) + { + mouseDownPoint = null; + lastMousePoint = null; + } + + private boolean isMouseOnNodeOrEdge(Point2D mouseLocation) + { + INode node = this.graph.findNode(mouseLocation); + IEdge edge = this.graph.findEdge(mouseLocation); + if (node == null && edge == null) + { + return false; + } + return true; + } + + private void resetSelectedElements() + { + this.selectionHandler.clearSelection(); + } + + + @Override + public void onPaint(Graphics2D g2) + { + if (mouseDownPoint == null || lastMousePoint == null) + { + return; + } + Color oldColor = g2.getColor(); + g2.setColor(PURPLE); + double x1 = mouseDownPoint.getX(); + double y1 = mouseDownPoint.getY(); + double x2 = lastMousePoint.getX(); + double y2 = lastMousePoint.getY(); + Rectangle2D.Double lasso = new Rectangle2D.Double(Math.min(x1, x2), Math.min(y1, y2), Math.abs(x1 - x2), Math.abs(y1 - y2)); + g2.draw(lasso); + g2.setColor(oldColor); + } + + private static final Color PURPLE = new Color(0.7f, 0.4f, 0.7f); + + private Point2D mouseDownPoint = null; + + private Point2D lastMousePoint = null; + + private IGraph graph; + + private IEditorPartSelectionHandler selectionHandler; + + private IEditorPart editorPart; + + private IGraphToolsBar graphToolsBar; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ShowMenuOnRightClickBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ShowMenuOnRightClickBehavior.java new file mode 100644 index 0000000..acd6497 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ShowMenuOnRightClickBehavior.java @@ -0,0 +1,188 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; +import java.util.List; + +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; + +import com.horstmann.violet.framework.injection.resources.ResourceBundleInjector; +import com.horstmann.violet.framework.injection.resources.annotation.ResourceBundleBean; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPartBehaviorManager; + +public class ShowMenuOnRightClickBehavior extends AbstractEditorPartBehavior +{ + + public ShowMenuOnRightClickBehavior(IEditorPart editorPart) + { + ResourceBundleInjector.getInjector().inject(this); + this.editorPart = editorPart; + } + + @Override + public void onMouseClicked(MouseEvent event) + { + boolean isButton3Clicked = (event.getButton() == MouseEvent.BUTTON3); + if (event.getClickCount() == 1 && isButton3Clicked) + { + getPopupMenu().show(this.editorPart.getSwingComponent(), event.getX(), event.getY()); + } + } + + + private JPopupMenu getPopupMenu() { + if (this.popupMenu == null) { + this.popupMenu = new JPopupMenu(); + this.popupMenu = fillMenu(this.popupMenu); + } + return this.popupMenu; + } + + /** + * Initializes menu + */ + private JPopupMenu fillMenu(JPopupMenu aPopupMenu) + { + undo.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + IEditorPartBehaviorManager behaviorManager = ShowMenuOnRightClickBehavior.this.editorPart.getBehaviorManager(); + List found = behaviorManager.getBehaviors(UndoRedoCompoundBehavior.class); + if (found.size() != 1) { + return; + } + found.get(0).undo(); + } + }); + aPopupMenu.add(undo); + + redo.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + IEditorPartBehaviorManager behaviorManager = ShowMenuOnRightClickBehavior.this.editorPart.getBehaviorManager(); + List found = behaviorManager.getBehaviors(UndoRedoCompoundBehavior.class); + if (found.size() != 1) { + return; + } + found.get(0).redo(); + } + }); + aPopupMenu.add(redo); + + properties.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + IEditorPartBehaviorManager behaviorManager = ShowMenuOnRightClickBehavior.this.editorPart.getBehaviorManager(); + List found = behaviorManager.getBehaviors(EditSelectedBehavior.class); + if (found.size() != 1) { + return; + } + found.get(0).editSelected(); + } + }); + aPopupMenu.add(properties); + + cut.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + IEditorPartBehaviorManager behaviorManager = ShowMenuOnRightClickBehavior.this.editorPart.getBehaviorManager(); + List found = behaviorManager.getBehaviors(CutCopyPasteBehavior.class); + if (found.size() != 1) { + return; + } + found.get(0).cut(); + } + }); + aPopupMenu.add(cut); + + copy.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + IEditorPartBehaviorManager behaviorManager = ShowMenuOnRightClickBehavior.this.editorPart.getBehaviorManager(); + List found = behaviorManager.getBehaviors(CutCopyPasteBehavior.class); + if (found.size() != 1) { + return; + } + found.get(0).copy(); + } + }); + aPopupMenu.add(copy); + + paste.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + IEditorPartBehaviorManager behaviorManager = ShowMenuOnRightClickBehavior.this.editorPart.getBehaviorManager(); + List found = behaviorManager.getBehaviors(CutCopyPasteBehavior.class); + if (found.size() != 1) { + return; + } + found.get(0).paste(); + } + }); + aPopupMenu.add(paste); + + delete.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + ShowMenuOnRightClickBehavior.this.editorPart.removeSelected(); + } + }); + aPopupMenu.add(delete); + + selectAll.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent event) + { + IEditorPartBehaviorManager behaviorManager = ShowMenuOnRightClickBehavior.this.editorPart.getBehaviorManager(); + List found = behaviorManager.getBehaviors(SelectAllBehavior.class); + if (found.size() != 1) { + return; + } + found.get(0).selectAllGraphElements(); + } + }); + aPopupMenu.add(selectAll); + + return aPopupMenu; + } + + private JPopupMenu popupMenu; + + @ResourceBundleBean(key = "edit.undo") + private JMenuItem undo; + + @ResourceBundleBean(key = "edit.redo") + private JMenuItem redo; + + @ResourceBundleBean(key = "edit.properties") + private JMenuItem properties; + + @ResourceBundleBean(key = "edit.cut") + private JMenuItem cut; + + @ResourceBundleBean(key = "edit.copy") + private JMenuItem copy; + + @ResourceBundleBean(key = "edit.paste") + private JMenuItem paste; + + @ResourceBundleBean(key = "edit.delete") + private JMenuItem delete; + + @ResourceBundleBean(key = "edit.select_all") + private JMenuItem selectAll; + + private IEditorPart editorPart; + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ShowMenuOnRightClickBehavior.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ShowMenuOnRightClickBehavior.properties new file mode 100644 index 0000000..eced802 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ShowMenuOnRightClickBehavior.properties @@ -0,0 +1,31 @@ +edit.undo.text=Undo +edit.undo.mnemonic=U +edit.undo.accelerator=ctrl Z +edit.undo.icon=/icons/16x16/undo.png +edit.redo.text=Redo +edit.redo.mnemonic=R +edit.redo.accelerator=ctrl Y +edit.redo.icon=/icons/16x16/redo.png +edit.properties.text=Properties +edit.properties.mnemonic=O +edit.properties.accelerator=ctrl ENTER +edit.properties.icon=/icons/16x16/properties.png +edit.delete.text=Delete +edit.delete.mnemonic=D +edit.delete.accelerator=DELETE +edit.delete.icon=/icons/16x16/delete.png +edit.cut.text=Cut +edit.cut.mnemonic=T +edit.cut.accelerator=ctrl X +edit.cut.icon=/icons/16x16/cut.png +edit.copy.text=Copy +edit.copy.mnemonic=C +edit.copy.accelerator=ctrl C +edit.copy.icon=/icons/16x16/copy.png +edit.paste.text=Paste +edit.paste.mnemonic=P +edit.paste.accelerator=ctrl V +edit.paste.icon=/icons/16x16/paste.png +edit.select_all.text=Select All +edit.select_all.mnemonic=A +edit.select_all.accelerator=ctrl A \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ShowMenuOnRightClickBehavior_fr.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ShowMenuOnRightClickBehavior_fr.properties new file mode 100644 index 0000000..16fdd0a --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ShowMenuOnRightClickBehavior_fr.properties @@ -0,0 +1,31 @@ +edit.undo.text=Annuler +edit.undo.mnemonic=A +edit.undo.accelerator=ctrl Z +edit.undo.icon=/icons/16x16/undo.png +edit.redo.text=R\u00e9tablir +edit.redo.mnemonic=R +edit.redo.accelerator=ctrl Y +edit.redo.icon=/icons/16x16/redo.png +edit.properties.text=Propri\u00e9t\u00e9s +edit.properties.mnemonic=P +edit.properties.accelerator=ctrl ENTER +edit.properties.icon=/icons/16x16/properties.png +edit.delete.text=Supprimer +edit.delete.mnemonic=S +edit.delete.accelerator=DELETE +edit.delete.icon=/icons/16x16/delete.png +edit.cut.text=Couper +edit.cut.mnemonic=O +edit.cut.accelerator=ctrl X +edit.cut.icon=/icons/16x16/cut.png +edit.copy.text=Copier +edit.copy.mnemonic=I +edit.copy.accelerator=ctrl C +edit.copy.icon=/icons/16x16/copy.png +edit.paste.text=Coller +edit.paste.mnemonic=L +edit.paste.accelerator=ctrl V +edit.paste.icon=/icons/16x16/paste.png +edit.select_all.text=S\u00e9l\u00e9ctionner tout +edit.select_all.mnemonic=A +edit.select_all.accelerator=ctrl A \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SwingRepaintingBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SwingRepaintingBehavior.java new file mode 100644 index 0000000..30e0835 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/SwingRepaintingBehavior.java @@ -0,0 +1,173 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.Graphics2D; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.geom.Point2D; +import java.beans.PropertyChangeEvent; + +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.sidebar.graphtools.GraphTool; + +public class SwingRepaintingBehavior implements IEditorPartBehavior +{ + + private IEditorPart editorPart; + + + public SwingRepaintingBehavior(IEditorPart editorPart) + { + this.editorPart = editorPart; + } + + @Override + public void onToolSelected(GraphTool selectedTool) + { + this.editorPart.getSwingComponent().doLayout(); + this.editorPart.getSwingComponent().repaint(); + } + + @Override + public void onMouseReleased(MouseEvent event) + { + this.editorPart.getSwingComponent().doLayout(); + this.editorPart.getSwingComponent().repaint(); + } + + @Override + public void onMouseClicked(MouseEvent event) + { + this.editorPart.getSwingComponent().doLayout(); + this.editorPart.getSwingComponent().repaint(); + } + + @Override + public void onMousePressed(MouseEvent event) + { + this.editorPart.getSwingComponent().doLayout(); + this.editorPart.getSwingComponent().repaint(); + } + + @Override + public void onMouseDragged(MouseEvent event) + { + this.editorPart.getSwingComponent().doLayout(); + this.editorPart.getSwingComponent().repaint(); + } + + @Override + public void onMouseMoved(MouseEvent event) + { + // Nothing to do + } + + @Override + public void onMouseWheelMoved(MouseWheelEvent event) + { + // Nothing to do + } + + @Override + public void beforeRemovingSelectedElements() + { + // Nothing to do + } + + @Override + public void beforeEditingNode(INode node) + { + // nothing to do + } + + @Override + public void beforeEditingEdge(IEdge edge) + { + // nothing to do + } + + @Override + public void beforeAddingNodeAtPoint(INode node, Point2D location) + { + // nothing to do + } + + @Override + public void beforeAddingEdgeAtPoints(IEdge edge, Point2D startPoint, Point2D endPoint) + { + // nothing to do + } + + @Override + public void afterRemovingSelectedElements() + { + this.editorPart.getSwingComponent().doLayout(); + this.editorPart.getSwingComponent().repaint(); + } + + @Override + public void afterEditingNode(INode node) + { + this.editorPart.getSwingComponent().doLayout(); + this.editorPart.getSwingComponent().repaint(); + } + + @Override + public void afterEditingEdge(IEdge edge) + { + this.editorPart.getSwingComponent().doLayout(); + this.editorPart.getSwingComponent().repaint(); + } + + @Override + public void afterAddingNodeAtPoint(INode node, Point2D location) + { + this.editorPart.getSwingComponent().doLayout(); + this.editorPart.getSwingComponent().repaint(); + } + + @Override + public void afterAddingEdgeAtPoints(IEdge edge, Point2D startPoint, Point2D endPoint) + { + this.editorPart.getSwingComponent().doLayout(); + this.editorPart.getSwingComponent().repaint(); + } + + @Override + public void onPaint(Graphics2D g2) + { + // nothing to do + + } + + @Override + public void onEdgeSelected(IEdge edge) + { + this.editorPart.getSwingComponent().doLayout(); + this.editorPart.getSwingComponent().repaint(); + } + + @Override + public void onNodeSelected(INode node) + { + this.editorPart.getSwingComponent().doLayout(); + this.editorPart.getSwingComponent().repaint(); + } + + @Override + public void whileEditingEdge(IEdge edge, PropertyChangeEvent event) + { + this.editorPart.getSwingComponent().doLayout(); + this.editorPart.getSwingComponent().repaint(); + } + + @Override + public void whileEditingNode(INode node, PropertyChangeEvent event) + { + this.editorPart.getSwingComponent().doLayout(); + this.editorPart.getSwingComponent().repaint(); + } + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoCompoundBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoCompoundBehavior.java new file mode 100644 index 0000000..a3dc622 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoCompoundBehavior.java @@ -0,0 +1,242 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.beans.PropertyChangeEvent; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoManager; + +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; + +/** + * This behavior for undo/redo actions is composed of sub-behaviors + * + * @author Alexandre de Pellegrin + * + */ +public class UndoRedoCompoundBehavior extends AbstractEditorPartBehavior +{ + + /** + * The concerned workspace + */ + private IEditorPart editorPart; + + /** + * Current composed undoable edit + */ + private CompoundEdit currentCapturedEdit; + + /** + * Undo/redo manager + */ + private UndoManager undoManager = new UndoManager(); + + /** + * List of individual undo/redo behaviors + */ + private List behaviors = new ArrayList(); + + + /** + * Default constructor + * @param editorPart + */ + public UndoRedoCompoundBehavior(IEditorPart editorPart) + { + this.editorPart = editorPart; + behaviors.add(new UndoRedoOnAddBehavior(editorPart, this)); + behaviors.add(new UndoRedoOnDragBehavior(editorPart, this)); + behaviors.add(new UndoRedoOnEditBehavior(this)); + behaviors.add(new UndoRedoOnRemoveBehavior(editorPart, this)); + } + + + @Override + public void onMousePressed(MouseEvent event) + { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.onMousePressed(event); + } + } + + + + @Override + public void onMouseDragged(MouseEvent event) + { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.onMouseDragged(event); + } + } + + + + @Override + public void onMouseReleased(MouseEvent event) + { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.onMouseReleased(event); + } + } + + + @Override + public void beforeRemovingSelectedElements() + { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.beforeRemovingSelectedElements(); + } + } + + @Override + public void afterRemovingSelectedElements() { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.afterRemovingSelectedElements(); + } + } + + + @Override + public void beforeAddingNodeAtPoint(INode node, Point2D location) + { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.beforeAddingNodeAtPoint(node, location); + } + } + + @Override + public void afterAddingNodeAtPoint(final INode node, final Point2D location) + { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.afterAddingNodeAtPoint(node, location); + } + } + + @Override + public void beforeAddingEdgeAtPoints(IEdge edge, Point2D startPoint, Point2D endPoint) + { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.beforeAddingEdgeAtPoints(edge, startPoint, endPoint); + } + } + + @Override + public void afterAddingEdgeAtPoints(final IEdge edge, final Point2D startPoint, final Point2D endPoint) + { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.afterAddingEdgeAtPoints(edge, startPoint, endPoint); + } + } + + @Override + public void beforeEditingNode(INode node) + { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.beforeEditingNode(node); + } + } + + @Override + public void whileEditingNode(INode node, PropertyChangeEvent event) + { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.whileEditingNode(node, event); + } + } + + @Override + public void afterEditingNode(INode node) + { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.afterEditingNode(node); + } + } + + @Override + public void beforeEditingEdge(IEdge edge) + { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.beforeEditingEdge(edge); + } + } + + @Override + public void whileEditingEdge(IEdge edge, final PropertyChangeEvent event) + { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.whileEditingEdge(edge, event); + } + } + + @Override + public void afterEditingEdge(IEdge edge) + { + for (IEditorPartBehavior aBehavior : this.behaviors) { + aBehavior.afterEditingEdge(edge); + } + } + + + /** + * Restores previous graph action from the history cursor location + */ + public void undo() + { + if (undoManager.canUndo()) + { + undoManager.undo(); + editorPart.getSwingComponent().repaint(); + } + } + + /** + * Restores next graph action from the history cursor location + */ + public void redo() + { + if (undoManager.canRedo()) + { + undoManager.redo(); + editorPart.getSwingComponent().repaint(); + } + } + + /** + * Starts capturing actions on graph + */ + protected void startHistoryCapture() + { + if (this.currentCapturedEdit == null) + { + this.currentCapturedEdit = new CompoundEdit(); + } + } + + + /** + * @return current composed undoable edit + */ + protected CompoundEdit getCurrentCapturedEdit() + { + return this.currentCapturedEdit; + } + + /** + * Stops capturing actions on graph and adds an entry to history + */ + protected void stopHistoryCapture() + { + if (this.currentCapturedEdit == null) return; + this.currentCapturedEdit.end(); + this.undoManager.addEdit(this.currentCapturedEdit); + this.currentCapturedEdit = null; + } + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnAddBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnAddBehavior.java new file mode 100644 index 0000000..455f708 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnAddBehavior.java @@ -0,0 +1,226 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoableEdit; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; + +/** + * Undo/Redo behavior triggered when nodes and edges are added + * + * @author Alexandre de Pellegrin + * + */ +public class UndoRedoOnAddBehavior extends AbstractEditorPartBehavior +{ + + /** + * The concerned workspace + */ + private IEditorPart editorPart; + + /** + * The global undo/redo behavior which contains all individual undo/redo behaviors + */ + private UndoRedoCompoundBehavior compoundBehavior; + + /** + * Keeps all the nodes attached to the graph before the add action + */ + private List nodesOnGraphBeforeAdd = new ArrayList(); + + /** + * Keeps all the edges attached to the graph before the add action + */ + private List edgesOnGraphBeforeAdd = new ArrayList(); + + + + /** + * Default constructor + * @param editorPart + * @param compoundBehavior + */ + public UndoRedoOnAddBehavior(IEditorPart editorPart, UndoRedoCompoundBehavior compoundBehavior) + { + this.editorPart = editorPart; + this.compoundBehavior = compoundBehavior; + } + + + + + @Override + public void beforeAddingNodeAtPoint(INode node, Point2D location) + { + this.nodesOnGraphBeforeAdd.clear(); + this.edgesOnGraphBeforeAdd.clear(); + this.nodesOnGraphBeforeAdd.addAll(this.editorPart.getGraph().getAllNodes()); + this.edgesOnGraphBeforeAdd.addAll(this.editorPart.getGraph().getAllEdges()); + } + + @Override + public void afterAddingNodeAtPoint(final INode node, final Point2D location) + { + List nodesOnGraphAfterAction = new ArrayList(this.editorPart.getGraph().getAllNodes()); + List edgesOnGraphAfterAction = new ArrayList(this.editorPart.getGraph().getAllEdges()); + + List nodesReallyAdded = new ArrayList(); + nodesReallyAdded.addAll(nodesOnGraphAfterAction); + nodesReallyAdded.removeAll(this.nodesOnGraphBeforeAdd); + + List edgesReallyAdded = new ArrayList(); + edgesReallyAdded.addAll(edgesOnGraphAfterAction); + edgesReallyAdded.removeAll(this.edgesOnGraphBeforeAdd); + + this.compoundBehavior.startHistoryCapture(); + CompoundEdit capturedEdit = this.compoundBehavior.getCurrentCapturedEdit(); + + for (final INode aSelectedNode : nodesReallyAdded) + { + + + UndoableEdit edit = new AbstractUndoableEdit() + { + @Override + public void undo() throws CannotUndoException + { + IGraph graph = editorPart.getGraph(); + graph.removeNode(aSelectedNode); + super.undo(); + } + + @Override + public void redo() throws CannotRedoException + { + super.redo(); + IGraph graph = editorPart.getGraph(); + graph.addNode(aSelectedNode, aSelectedNode.getLocationOnGraph()); + } + }; + capturedEdit.addEdit(edit); + } + + for (final IEdge aSelectedEdge : edgesReallyAdded) + { + UndoableEdit edit = new AbstractUndoableEdit() + { + @Override + public void undo() throws CannotUndoException + { + IGraph graph = editorPart.getGraph(); + graph.removeEdge(aSelectedEdge); + super.undo(); + } + + @Override + public void redo() throws CannotRedoException + { + super.redo(); + IGraph graph = editorPart.getGraph(); + graph.connect(aSelectedEdge, aSelectedEdge.getStart(), aSelectedEdge.getStartLocation(), aSelectedEdge.getEnd(), aSelectedEdge.getEndLocation()); + } + }; + capturedEdit.addEdit(edit); + } + + + + this.compoundBehavior.stopHistoryCapture(); + this.nodesOnGraphBeforeAdd.clear(); + this.edgesOnGraphBeforeAdd.clear(); + } + + @Override + public void beforeAddingEdgeAtPoints(IEdge edge, Point2D startPoint, Point2D endPoint) + { + this.nodesOnGraphBeforeAdd.clear(); + this.edgesOnGraphBeforeAdd.clear(); + this.nodesOnGraphBeforeAdd.addAll(this.editorPart.getGraph().getAllNodes()); + this.edgesOnGraphBeforeAdd.addAll(this.editorPart.getGraph().getAllEdges()); + } + + @Override + public void afterAddingEdgeAtPoints(final IEdge edge, final Point2D startPoint, final Point2D endPoint) + { + List nodesOnGraphAfterAction = new ArrayList(this.editorPart.getGraph().getAllNodes()); + List edgesOnGraphAfterAction = new ArrayList(this.editorPart.getGraph().getAllEdges()); + + List nodesReallyAdded = new ArrayList(); + nodesReallyAdded.addAll(nodesOnGraphAfterAction); + nodesReallyAdded.removeAll(this.nodesOnGraphBeforeAdd); + + List edgesReallyAdded = new ArrayList(); + edgesReallyAdded.addAll(edgesOnGraphAfterAction); + edgesReallyAdded.removeAll(this.edgesOnGraphBeforeAdd); + + this.compoundBehavior.startHistoryCapture(); + CompoundEdit capturedEdit = this.compoundBehavior.getCurrentCapturedEdit(); + + for (final INode aSelectedNode : nodesReallyAdded) + { + + + UndoableEdit edit = new AbstractUndoableEdit() + { + @Override + public void undo() throws CannotUndoException + { + IGraph graph = editorPart.getGraph(); + graph.removeNode(aSelectedNode); + super.undo(); + } + + @Override + public void redo() throws CannotRedoException + { + super.redo(); + IGraph graph = editorPart.getGraph(); + graph.addNode(aSelectedNode, aSelectedNode.getLocationOnGraph()); + } + }; + capturedEdit.addEdit(edit); + } + + for (final IEdge aSelectedEdge : edgesReallyAdded) + { + UndoableEdit edit = new AbstractUndoableEdit() + { + @Override + public void undo() throws CannotUndoException + { + IGraph graph = editorPart.getGraph(); + graph.removeEdge(aSelectedEdge); + super.undo(); + } + + @Override + public void redo() throws CannotRedoException + { + super.redo(); + IGraph graph = editorPart.getGraph(); + graph.connect(aSelectedEdge, aSelectedEdge.getStart(), aSelectedEdge.getStartLocation(), aSelectedEdge.getEnd(), aSelectedEdge.getEndLocation()); + } + }; + capturedEdit.addEdit(edit); + } + + + + this.compoundBehavior.stopHistoryCapture(); + this.nodesOnGraphBeforeAdd.clear(); + this.edgesOnGraphBeforeAdd.clear(); + } + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnDragBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnDragBehavior.java new file mode 100644 index 0000000..938ae96 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnDragBehavior.java @@ -0,0 +1,166 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.event.MouseEvent; +import java.awt.geom.Point2D; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoableEdit; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPartSelectionHandler; + +/** + * Undo/Redo behavior triggered when nodes and edges are dragged + * + * @author Alexandre de Pellegrin + * + */ +public class UndoRedoOnDragBehavior extends AbstractEditorPartBehavior +{ + + /** + * The concerned workspace + */ + private IEditorPart editorPart; + + /** + * The global undo/redo behavior which contains all individual undo/redo behaviors + */ + private UndoRedoCompoundBehavior compoundBehavior; + + /** + * To retreive selected elements + */ + private IEditorPartSelectionHandler selectionHandler; + + /** + * Keeps node locations before dragging event + */ + private Map nodesLocationsBeforeDrag = new HashMap(); + + /** + * Used on node's drag'n drop + */ + private boolean isDragInProgress = false; + + /** + * Default constructor + * @param editorPart + * @param compoundBehavior + */ + public UndoRedoOnDragBehavior(IEditorPart editorPart, UndoRedoCompoundBehavior compoundBehavior) + { + this.editorPart = editorPart; + this.selectionHandler = editorPart.getSelectionHandler(); + this.compoundBehavior = compoundBehavior; + } + + @Override + public void onMousePressed(MouseEvent event) + { + double zoom = editorPart.getZoomFactor(); + final Point2D mousePoint = new Point2D.Double(event.getX() / zoom, event.getY() / zoom); + this.isDragInProgress = false; + if (isMouseOnNode(mousePoint)) + { + saveNodesLocationsBeforeDrag(); + } + } + + @Override + public void onMouseDragged(MouseEvent event) + { + this.isDragInProgress = true; + } + + @Override + public void onMouseReleased(MouseEvent event) + { + if (!this.isDragInProgress) + { + return; + } + List selectedNodes = this.selectionHandler.getSelectedNodes(); + List editList = new ArrayList(); + for (final INode aSelectedNode : selectedNodes) + { + if (!this.nodesLocationsBeforeDrag.containsKey(aSelectedNode)) + { + continue; + } + Point2D lastNodeLocation = this.nodesLocationsBeforeDrag.get(aSelectedNode); + Point2D currentNodeLocation = aSelectedNode.getLocation(); + if (currentNodeLocation.equals(lastNodeLocation)) + { + continue; + } + final double dx = currentNodeLocation.getX() - lastNodeLocation.getX(); + final double dy = currentNodeLocation.getY() - lastNodeLocation.getY(); + UndoableEdit edit = new AbstractUndoableEdit() + { + @Override + public void undo() throws CannotUndoException + { + aSelectedNode.translate(-dx, -dy); + super.undo(); + } + + @Override + public void redo() throws CannotRedoException + { + super.redo(); + aSelectedNode.translate(dx, dy); + } + }; + editList.add(edit); + } + if (editList.size() > 0) + { + this.compoundBehavior.startHistoryCapture(); + CompoundEdit capturedEdit = this.compoundBehavior.getCurrentCapturedEdit(); + for (UndoableEdit edit : editList) + { + capturedEdit.addEdit(edit); + } + this.compoundBehavior.stopHistoryCapture(); + } + this.nodesLocationsBeforeDrag.clear(); + this.isDragInProgress = false; + } + + /** + * Saves nodes locations + */ + private void saveNodesLocationsBeforeDrag() + { + this.nodesLocationsBeforeDrag.clear(); + Collection selectedNodes = this.editorPart.getGraph().getAllNodes(); + for (INode aSelectedNode : selectedNodes) + { + Point2D location = aSelectedNode.getLocation(); + this.nodesLocationsBeforeDrag.put(aSelectedNode, location); + } + } + + private boolean isMouseOnNode(Point2D mouseLocation) + { + IGraph graph = this.editorPart.getGraph(); + INode node = graph.findNode(mouseLocation); + if (node == null) + { + return false; + } + return true; + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnEditBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnEditBehavior.java new file mode 100644 index 0000000..314ac96 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnEditBehavior.java @@ -0,0 +1,112 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.beans.PropertyChangeEvent; + +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoableEdit; + +import com.horstmann.violet.framework.util.PropertyUtils; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; + + +/** + * Undo/Redo behavior triggered when nodes and edges are edited + * + * @author Alexandre de Pellegrin + * + */ +public class UndoRedoOnEditBehavior extends AbstractEditorPartBehavior +{ + + /** + * The global undo/redo behavior which contains all individual undo/redo behaviors + */ + private UndoRedoCompoundBehavior compoundBehavior; + + /** + * Default constructor + * + * @param editorPart + */ + public UndoRedoOnEditBehavior(UndoRedoCompoundBehavior compoundBehavior) + { + this.compoundBehavior = compoundBehavior; + } + + @Override + public void beforeEditingNode(INode node) + { + this.compoundBehavior.startHistoryCapture(); + } + + @Override + public void whileEditingNode(INode node, PropertyChangeEvent event) + { + capturePropertyChanges(event); + } + + @Override + public void afterEditingNode(INode node) + { + this.compoundBehavior.stopHistoryCapture(); + } + + @Override + public void beforeEditingEdge(IEdge edge) + { + this.compoundBehavior.startHistoryCapture(); + } + + @Override + public void whileEditingEdge(IEdge edge, final PropertyChangeEvent event) + { + capturePropertyChanges(event); + } + + @Override + public void afterEditingEdge(IEdge edge) + { + this.compoundBehavior.stopHistoryCapture(); + } + + private void capturePropertyChanges(final PropertyChangeEvent event) + { + CompoundEdit capturedEdit = this.compoundBehavior.getCurrentCapturedEdit(); + if (capturedEdit == null) return; + Object newValue = event.getNewValue(); + Object oldValue = event.getOldValue(); + if (oldValue == null && newValue == null) return; +// boolean isOldValueRecognized = oldValue != null +// && (String.class.isInstance(oldValue) || MultiLineString.class.isInstance(oldValue)); +// boolean isNewValueRecognized = oldValue != null +// && (String.class.isInstance(newValue) || MultiLineString.class.isInstance(newValue)); +// if (!isOldValueRecognized && !isNewValueRecognized) return; + UndoableEdit edit = new AbstractUndoableEdit() + { + @Override + public void undo() throws CannotUndoException + { + PropertyChangeEvent invertedEvent = new PropertyChangeEvent(event.getSource(), event.getPropertyName(), + event.getNewValue(), event.getOldValue()); + changeNodeOrEdgeProperty(invertedEvent); + } + + @Override + public void redo() throws CannotRedoException + { + changeNodeOrEdgeProperty(event); + } + + private void changeNodeOrEdgeProperty(PropertyChangeEvent e) + { + PropertyUtils.setProperty(e.getSource(), e.getPropertyName(), e.getNewValue()); + } + }; + capturedEdit.addEdit(edit); + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnRemoveBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnRemoveBehavior.java new file mode 100644 index 0000000..05cb53c --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/UndoRedoOnRemoveBehavior.java @@ -0,0 +1,199 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.util.ArrayList; +import java.util.List; + +import javax.swing.undo.AbstractUndoableEdit; +import javax.swing.undo.CannotRedoException; +import javax.swing.undo.CannotUndoException; +import javax.swing.undo.CompoundEdit; +import javax.swing.undo.UndoableEdit; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.editorpart.IEditorPart; + +/** + * Undo/Redo behavior triggered when nodes and edges are removed + * + * @author Alexandre de Pellegrin + * + */ +public class UndoRedoOnRemoveBehavior extends AbstractEditorPartBehavior +{ + + /** + * The concerned workspace + */ + private IEditorPart editorPart; + + /** + * The global undo/redo behavior which contains all individual undo/redo behaviors + */ + private UndoRedoCompoundBehavior compoundBehavior; + + /** + * Keeps all the nodes attached to the graph before the remove action + */ + private List nodesOnGraphBeforeRemove = new ArrayList(); + + /** + * Keeps all the edges attached to the graph before the remove action + */ + private List edgesOnGraphBeforeRemove = new ArrayList(); + + /** + * Default constructor + * + * @param editorPart + * @param compoundBehavior + */ + public UndoRedoOnRemoveBehavior(IEditorPart editorPart, UndoRedoCompoundBehavior compoundBehavior) + { + this.editorPart = editorPart; + this.compoundBehavior = compoundBehavior; + } + + @Override + public void beforeRemovingSelectedElements() + { + this.nodesOnGraphBeforeRemove.clear(); + this.edgesOnGraphBeforeRemove.clear(); + this.nodesOnGraphBeforeRemove.addAll(this.editorPart.getGraph().getAllNodes()); + this.edgesOnGraphBeforeRemove.addAll(this.editorPart.getGraph().getAllEdges()); + } + + @Override + public void afterRemovingSelectedElements() + { + List nodesOnGraphAfterAction = new ArrayList(this.editorPart.getGraph().getAllNodes()); + List edgesOnGraphAfterAction = new ArrayList(this.editorPart.getGraph().getAllEdges()); + + List nodesReallyRemoved = new ArrayList(); + nodesReallyRemoved.addAll(this.nodesOnGraphBeforeRemove); + nodesReallyRemoved.removeAll(nodesOnGraphAfterAction); + + List edgesReallyRemoved = new ArrayList(); + edgesReallyRemoved.addAll(this.edgesOnGraphBeforeRemove); + edgesReallyRemoved.removeAll(edgesOnGraphAfterAction); + + this.compoundBehavior.startHistoryCapture(); + CompoundEdit capturedEdit = this.compoundBehavior.getCurrentCapturedEdit(); + + for (final IEdge aSelectedEdge : edgesReallyRemoved) + { + UndoableEdit edit = new AbstractUndoableEdit() + { + @Override + public void undo() throws CannotUndoException + { + IGraph graph = editorPart.getGraph(); + graph.connect(aSelectedEdge, aSelectedEdge.getStart(), aSelectedEdge.getStartLocation(), + aSelectedEdge.getEnd(), aSelectedEdge.getEndLocation()); + super.undo(); + } + + @Override + public void redo() throws CannotRedoException + { + super.redo(); + IGraph graph = editorPart.getGraph(); + graph.removeEdge(aSelectedEdge); + } + }; + capturedEdit.addEdit(edit); + } + + List filteredNodes = removeChildren(nodesReallyRemoved); + for (final INode aSelectedNode : filteredNodes) + { + + UndoableEdit edit = new AbstractUndoableEdit() + { + @Override + public void undo() throws CannotUndoException + { + IGraph graph = editorPart.getGraph(); + graph.addNode(aSelectedNode, aSelectedNode.getLocationOnGraph()); + super.undo(); + } + + @Override + public void redo() throws CannotRedoException + { + super.redo(); + IGraph graph = editorPart.getGraph(); + graph.removeNode(aSelectedNode); + } + }; + capturedEdit.addEdit(edit); + } + + this.compoundBehavior.stopHistoryCapture(); + this.nodesOnGraphBeforeRemove.clear(); + this.edgesOnGraphBeforeRemove.clear(); + } + + /** + * Checks if ancestorNode is a parent node of child node + * + * @param childNode + * @param ancestorNode + * @return b + */ + private boolean isAncestorRelationship(INode childNode, INode ancestorNode) + { + INode parent = childNode.getParent(); + if (parent == null) + { + return false; + } + List fifo = new ArrayList(); + fifo.add(parent); + while (!fifo.isEmpty()) + { + INode aParentNode = fifo.get(0); + fifo.remove(0); + if (aParentNode.equals(ancestorNode)) + { + return true; + } + INode aGranParent = aParentNode.getParent(); + if (aGranParent != null) + { + fifo.add(aGranParent); + } + } + return false; + } + + /** + * Takes a list of nodes and removes from this list all nodes which have ancestors node in this list.
+ * + * @param nodes the list to filter + * @return the filtered list + */ + private List removeChildren(List nodes) + { + List result = new ArrayList(); + for (INode aNode : nodes) + { + boolean isOrphelin = true; + for (INode aParent : nodes) + { + boolean isAncestorRelationship = isAncestorRelationship(aNode, aParent); + if (isAncestorRelationship) + { + isOrphelin = false; + } + } + if (isOrphelin) + { + result.add(aNode); + } + } + return result; + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ZoomByWheelBehavior.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ZoomByWheelBehavior.java new file mode 100644 index 0000000..262469f --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/editorpart/behavior/ZoomByWheelBehavior.java @@ -0,0 +1,37 @@ +package com.horstmann.violet.workspace.editorpart.behavior; + +import java.awt.event.InputEvent; +import java.awt.event.MouseWheelEvent; + +import com.horstmann.violet.workspace.editorpart.IEditorPart; + +public class ZoomByWheelBehavior extends AbstractEditorPartBehavior +{ + + private IEditorPart editorPart; + + public ZoomByWheelBehavior(IEditorPart editorPart) + { + this.editorPart = editorPart; + } + + @Override + public void onMouseWheelMoved(MouseWheelEvent event) + { + boolean isCtrl = (event.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0; + if (!isCtrl) + { + return; + } + int scroll = event.getUnitsToScroll(); + if (scroll < 0) + { + this.editorPart.changeZoom(1); + } + if (scroll > 0) + { + this.editorPart.changeZoom(-1); + } + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/ISideBar.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/ISideBar.java new file mode 100644 index 0000000..2f04a45 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/ISideBar.java @@ -0,0 +1,58 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.workspace.sidebar; + +import java.awt.Component; + +import com.horstmann.violet.workspace.sidebar.graphtools.IGraphToolsBar; + +/** + * Side bar interface definition + * + * @author Alexandre de Pellegrin + * + */ +public interface ISideBar +{ + + /** + * Registers a new swing element to the side bar. This element will be automatically embedded in a collapsible panel which has a + * title + * + * @param element new element to be added + * @param title title of the collapsible panel embedding the component + */ + public void addElement(ISideBarElement element, String title); + + + /** + * @return the graph tools bar + */ + public IGraphToolsBar getGraphToolsBar(); + + + /** + * @return the AWT component representing this side bar + */ + public Component getAWTComponent(); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/ISideBarElement.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/ISideBarElement.java new file mode 100644 index 0000000..7426d03 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/ISideBarElement.java @@ -0,0 +1,29 @@ +package com.horstmann.violet.workspace.sidebar; + +import java.awt.Component; + +import com.horstmann.violet.workspace.IWorkspace; + +/** + * An element displayed on a side bar. Usually, this is a JPanel + * + * @author Alexandre de Pellegrin + * + */ +public interface ISideBarElement +{ + + /** + * Method invoked when this element is added to a sidebar + * + * @param workspace + */ + public void install(IWorkspace workspace); + + + /** + * @return the AWT component representing this side bar + */ + public Component getAWTComponent(); + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/ScrollPaneNavigatorPanel.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/ScrollPaneNavigatorPanel.java new file mode 100644 index 0000000..ed58bbc --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/ScrollPaneNavigatorPanel.java @@ -0,0 +1,232 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.workspace.sidebar; + +import java.awt.AWTException; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Cursor; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.Robot; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; +import java.awt.event.MouseEvent; +import java.awt.geom.Area; +import java.awt.image.BufferedImage; + +import javax.swing.BorderFactory; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; +import javax.swing.event.MouseInputAdapter; +import javax.swing.event.MouseInputListener; + +public class ScrollPaneNavigatorPanel extends JPanel +{ + private static final double MAX_SIZE = 200; + private JScrollPane theScrollPane; + private JComponent theComponent; + private BufferedImage theImage; + private Rectangle theStartRectangle; + private Rectangle theRectangle; + private Point theStartPoint; + private double theScale; + + public ScrollPaneNavigatorPanel(JScrollPane aScrollPane) + { + setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5)); + theScrollPane = aScrollPane; + theComponent = (JComponent) theScrollPane.getViewport().getView(); + theImage = null; + theStartRectangle = null; + theRectangle = null; + theStartPoint = null; + theScale = 0.0; + setCursor(Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR)); + MouseInputListener mil = new MouseInputAdapter() + { + public void mousePressed(MouseEvent e) + { + theStartPoint = e.getPoint(); + } + + public void mouseReleased(MouseEvent e) + { + if (theStartPoint != null) + { + Point newPoint = e.getPoint(); + int deltaX = (int) ((newPoint.x - theStartPoint.x) / theScale); + int deltaY = (int) ((newPoint.y - theStartPoint.y) / theScale); + scroll(deltaX, deltaY); + } + theStartPoint = null; + theStartRectangle = theRectangle; + } + + public void mouseDragged(MouseEvent e) + { + if (theStartPoint == null) return; + Point newPoint = e.getPoint(); + moveRectangle(newPoint.x - theStartPoint.x, newPoint.y - theStartPoint.y); + } + }; + addMouseListener(mil); + addMouseMotionListener(mil); + // thePopupMenu = new JPopupMenu(); + // thePopupMenu.setLayout(new BorderLayout()); + // thePopupMenu.add(this, BorderLayout.CENTER); + // thePopupMenu.setBorder(BorderFactory.createLineBorder(Color.BLACK)); + setLayout(new BorderLayout()); + + theScrollPane.addComponentListener(new ComponentAdapter() + { + public void componentResized(ComponentEvent arg0) + { + display(); + } + }); + } + + protected void paintComponent(Graphics g1D) + { + if (theImage == null || theRectangle == null) return; + Graphics2D g = (Graphics2D) g1D; + Insets insets = getInsets(); + int xOffset = insets.left; + int yOffset = insets.top; + int availableWidth = getWidth() - insets.left - insets.right; + int availableHeight = getHeight() - insets.top - insets.bottom; + g.drawImage(theImage, xOffset, yOffset, null); + Color tmpColor = g.getColor(); + Area area = new Area(new Rectangle(xOffset, yOffset, availableWidth, availableHeight)); + area.subtract(new Area(theRectangle)); + g.setColor(new Color(255, 255, 255, 128)); + g.fill(area); + g.setColor(Color.BLACK); + g.draw(theRectangle); + g.setColor(tmpColor); + } + + public Dimension getPreferredSize() + { + if (theImage == null || theRectangle == null) return new Dimension(); + Insets insets = getInsets(); + return new Dimension(theImage.getWidth(null) + insets.left + insets.right, theImage.getHeight(null) + insets.top + + insets.bottom); + } + + private void display() + { + double compWidth = theComponent.getWidth(); + double compHeight = theComponent.getHeight(); + double scaleX = MAX_SIZE / compWidth; + double scaleY = MAX_SIZE / compHeight; + theScale = Math.min(scaleX, scaleY); + + theImage = new BufferedImage((int) (theComponent.getWidth() * theScale), (int) (theComponent.getHeight() * theScale), + BufferedImage.TYPE_BYTE_BINARY); + + Graphics2D g = theImage.createGraphics(); + g.scale(theScale, theScale); + theComponent.paint(g); + + theStartRectangle = theComponent.getVisibleRect(); + Insets insets = getInsets(); + theStartRectangle.x = (int) (theScale * theStartRectangle.x + insets.left); + theStartRectangle.y = (int) (theScale * theStartRectangle.y + insets.right); + theStartRectangle.width *= theScale; + theStartRectangle.height *= theScale; + theRectangle = theStartRectangle; + + // Dimension pref = thePopupMenu.getPreferredSize(); + // + // thePopupMenu.show(theButton, + // (theButton.getWidth() - pref.width) / 2, + // (theButton.getHeight() - pref.height) / 2); + repaint(); + + try + { + Robot robot = new Robot(); + Point centerPoint = new Point(theRectangle.x + theRectangle.width / 2, theRectangle.y + theRectangle.height / 2); + SwingUtilities.convertPointToScreen(centerPoint, this); + robot.mouseMove(centerPoint.x, centerPoint.y); + } + catch (AWTException e) + { + e.printStackTrace(); + } + } + + private void moveRectangle(int aDeltaX, int aDeltaY) + { + if (theStartRectangle == null) return; + Insets insets = getInsets(); + Rectangle newRect = new Rectangle(theStartRectangle); + newRect.x += aDeltaX; + newRect.y += aDeltaY; + newRect.x = Math.min(Math.max(newRect.x, insets.left), getWidth() - insets.right - newRect.width); + newRect.y = Math.min(Math.max(newRect.y, insets.right), getHeight() - insets.bottom - newRect.height); + Rectangle clip = new Rectangle(); + Rectangle.union(theRectangle, newRect, clip); + clip.grow(2, 2); + theRectangle = newRect; + paintImmediately(clip); + } + + private void scroll(int aDeltaX, int aDeltaY) + { + JComponent component = (JComponent) theScrollPane.getViewport().getView(); + Rectangle rect = component.getVisibleRect(); + rect.x += aDeltaX; + rect.y += aDeltaY; + component.scrollRectToVisible(rect); + // thePopupMenu.setVisible(false); + } + + // public static void main(String[] args) { + // EventQueue.invokeLater(new Runnable() { + // public void run() { + // try { + // JFrame frame = new JFrame(ScrollPaneBidule.class.getName()); + // frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + // URL imageURL = new URL("http://www.aboutstonehenge.info/images/education/stonehenge-wallpaper-1.jpg"); + // JLabel label = new JLabel(new ImageIcon(ImageIO.read(imageURL))); + // JScrollPane scrollPane = new JScrollPane(label); + // new ScrollPaneBidule(scrollPane); + // frame.setContentPane(scrollPane); + // frame.pack(); + // frame.setVisible(true); + // } catch (IOException e) { + // e.printStackTrace(); + // } + // } + // }); + // } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBar.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBar.java new file mode 100644 index 0000000..46d77be --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBar.java @@ -0,0 +1,113 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.workspace.sidebar; + +import java.awt.Component; +import java.util.HashMap; +import java.util.Map; + +import javax.swing.JPanel; + +import com.horstmann.violet.workspace.IWorkspace; +import com.horstmann.violet.workspace.sidebar.editortools.EditorToolsPanel; +import com.horstmann.violet.workspace.sidebar.graphtools.GraphToolsBar; +import com.horstmann.violet.workspace.sidebar.graphtools.IGraphToolsBar; +import com.horstmann.violet.workspace.sidebar.optionaltools.OptionalToolsPanel; + +public class SideBar extends JPanel implements ISideBar +{ + + public SideBar(IWorkspace diagramPanel) + { + this.diagramPanel = diagramPanel; + setupUI(); + } + + private void setupUI() { + setUI(new SideBarUI(this)); + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.sidebar.ISideBar#addElement(com.horstmann.violet.framework.display.clipboard.sidebar.ISideBarElement, + * java.lang.String) + */ + public void addElement(ISideBarElement element, String title) + { + element.install(this.diagramPanel); + this.externalContributionElements.put(element, title); + } + + public IGraphToolsBar getGraphToolsBar() + { + if (this.graphToolsBar == null) + { + this.graphToolsBar = new GraphToolsBar(); + this.graphToolsBar.install(this.diagramPanel); + } + return this.graphToolsBar; + } + + protected ISideBarElement getEditorToolsBar() + { + if (this.editorToolsBar == null) + { + this.editorToolsBar = new EditorToolsPanel(); + this.editorToolsBar.install(this.diagramPanel); + } + return this.editorToolsBar; + } + + protected ISideBarElement getOptionalToolsBar() + { + if (this.optionalToolsBar == null) + { + this.optionalToolsBar = new OptionalToolsPanel(); + this.optionalToolsBar.install(this.diagramPanel); + } + return this.optionalToolsBar; + } + + protected Map getExternalContributionElements() + { + return this.externalContributionElements; + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.sidebar.ISideBar#getAWTComponent() + */ + public Component getAWTComponent() + { + return this; + } + + private IWorkspace diagramPanel; + private IGraphToolsBar graphToolsBar; + private ISideBarElement editorToolsBar; + private ISideBarElement optionalToolsBar; + private Map externalContributionElements = new HashMap(); + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBar.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBar.properties new file mode 100644 index 0000000..7150c8c --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBar.properties @@ -0,0 +1,29 @@ +title.standardbuttons.text=Standard buttons +title.diagramtools.text=Diagram tools +title.extendedfunctions.text=Extended functions +zoomin.tooltip=Zoom in +zoomin.icon=/icons/22x22/zoomin.png +zoomout.tooltip=Zoom out +zoomout.icon=/icons/22x22/zoomout.png +undo.tooltip=Undo +undo.icon=/icons/22x22/undo.png +redo.tooltip=Redo +redo.icon=/icons/22x22/redo.png +delete.tooltip=Delete +delete.icon=/icons/22x22/delete.png +help.tooltip=Help contents +help.icon=/icons/22x22/help.png +export_to_clipboard.tooltip=Export to clipboard +export_to_clipboard.icon=/icons/22x22/exporttoclipboard.png +share_document.tooltip=Share document +share_document.icon=/icons/22x22/sharedocument.png +print.tooltip=Print +print.icon=/icons/22x22/print.png +cut.tooltip=Cut +cut.icon=/icons/22x22/cut.png +copy.tooltip=Copy +copy.icon=/icons/22x22/copy.png +paste.tooltip=Paste +paste.icon=/icons/22x22/paste.png + + diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBarUI.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBarUI.java new file mode 100644 index 0000000..ce4acbb --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBarUI.java @@ -0,0 +1,84 @@ +package com.horstmann.violet.workspace.sidebar; + +import java.awt.BorderLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Font; + +import javax.swing.BoxLayout; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JRootPane; +import javax.swing.SwingUtilities; +import javax.swing.border.MatteBorder; +import javax.swing.plaf.PanelUI; + +import com.horstmann.violet.framework.injection.resources.ResourceBundleInjector; +import com.horstmann.violet.framework.injection.resources.annotation.ResourceBundleBean; +import com.horstmann.violet.framework.theme.ThemeManager; +import com.l2fprod.common.swing.JTaskPane; +import com.l2fprod.common.swing.JTaskPaneGroup; + +@ResourceBundleBean(resourceReference=SideBar.class) +public class SideBarUI extends PanelUI +{ + + public SideBarUI(SideBar sideBar) + { + this.sideBar = sideBar; + ResourceBundleInjector.getInjector().inject(this); + } + + @Override + public void installUI(JComponent c) + { + c.removeAll(); + c.setLayout(new BoxLayout(c, BoxLayout.Y_AXIS)); + this.taskPane = new JTaskPane(); + addElementToTaskPane(this.sideBar.getEditorToolsBar().getAWTComponent(), standardButtonsTitle); + addElementToTaskPane(this.sideBar.getGraphToolsBar().getAWTComponent(), diagramToolsTitle); + addElementToTaskPane(this.sideBar.getOptionalToolsBar().getAWTComponent(), extendedFunctionsTitle); + for (ISideBarElement anExternalElement : this.sideBar.getExternalContributionElements().keySet()) { + String externalElementTitle = this.sideBar.getExternalContributionElements().get(anExternalElement); + addElementToTaskPane(anExternalElement.getAWTComponent(), externalElementTitle); + } + c.add(taskPane); + c.setBorder(new MatteBorder(0, 1, 0, 0, ThemeManager.getInstance().getTheme().getSidebarBorderColor())); + fixWidth(); + this.sideBar.doLayout(); + JRootPane rootPane = SwingUtilities.getRootPane(this.sideBar); + if (rootPane != null) { + rootPane.repaint(); + } + } + + private void fixWidth() + { + JLabel sizer = new JLabel(); + sizer.setPreferredSize(new Dimension(215, 1)); + this.taskPane.add(sizer); + } + + private void addElementToTaskPane(final Component c, String title) + { + JTaskPaneGroup group = new JTaskPaneGroup(); + group.setFont(group.getFont().deriveFont(Font.PLAIN)); + group.setTitle(title); + group.setLayout(new BorderLayout()); + group.add(c, BorderLayout.CENTER); + this.taskPane.add(group); + } + + private SideBar sideBar; + private JTaskPane taskPane; + + @ResourceBundleBean(key = "title.standardbuttons.text") + private String standardButtonsTitle; + + @ResourceBundleBean(key = "title.diagramtools.text") + private String diagramToolsTitle; + + @ResourceBundleBean(key = "title.extendedfunctions.text") + private String extendedFunctionsTitle; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBar_fr.properties b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBar_fr.properties new file mode 100644 index 0000000..83be4aa --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/SideBar_fr.properties @@ -0,0 +1,27 @@ +title.standardbuttons.text=Boutons standards +title.diagramtools.text=Outils du diagramme +title.extendedfunctions.text=Fonctions \u00E9tendues +zoomin.tooltip=Zoom avant +zoomin.icon=/icons/22x22/zoomin.png +zoomout.tooltip=Zoom arri\u00E8re +zoomout.icon=/icons/22x22/zoomout.png +undo.tooltip=Annuler +undo.icon=/icons/22x22/undo.png +redo.tooltip=R\u00E9tablir +redo.icon=/icons/22x22/redo.png +delete.tooltip=Supprimer +delete.icon=/icons/22x22/delete.png +help.tooltip=Aide +help.icon=/icons/22x22/help.png +export_to_clipboard.tooltip=Exporter vers le presse papier +export_to_clipboard.icon=/icons/22x22/exporttoclipboard.png +print.tooltip=Imprimer +print.icon=/icons/22x22/print.png +cut.tooltip=Couper +cut.icon=/icons/22x22/cut.png +copy.tooltip=Copier +copy.icon=/icons/22x22/copy.png +paste.tooltip=Coller +paste.icon=/icons/22x22/paste.png + + diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/editortools/EditorToolsPanel.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/editortools/EditorToolsPanel.java new file mode 100644 index 0000000..70a8dfc --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/editortools/EditorToolsPanel.java @@ -0,0 +1,270 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.workspace.sidebar.editortools; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.List; + +import javax.swing.JButton; +import javax.swing.JPanel; + +import com.horstmann.violet.framework.injection.resources.ResourceBundleInjector; +import com.horstmann.violet.framework.injection.resources.annotation.ResourceBundleBean; +import com.horstmann.violet.workspace.IWorkspace; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.editorpart.IEditorPartBehaviorManager; +import com.horstmann.violet.workspace.editorpart.behavior.CutCopyPasteBehavior; +import com.horstmann.violet.workspace.editorpart.behavior.UndoRedoCompoundBehavior; +import com.horstmann.violet.workspace.sidebar.ISideBarElement; +import com.horstmann.violet.workspace.sidebar.SideBar; + +@ResourceBundleBean(resourceReference = SideBar.class) +public class EditorToolsPanel extends JPanel implements ISideBarElement +{ + + public EditorToolsPanel() + { + super(); + ResourceBundleInjector.getInjector().inject(this); + this.setUI(new EditorToolsPanelUI(this)); + this.bZoomIn.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + workspace.getEditorPart().changeZoom(1); + } + }); + this.bZoomOut.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + workspace.getEditorPart().changeZoom(-1); + } + }); + this.bUndo.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + UndoRedoCompoundBehavior undoRedoBehavior = getUndoRedoBehavior(); + if (undoRedoBehavior != null) { + undoRedoBehavior.undo(); + } + } + }); + this.bRedo.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + UndoRedoCompoundBehavior undoRedoBehavior = getUndoRedoBehavior(); + if (undoRedoBehavior != null) { + undoRedoBehavior.redo(); + } + } + }); + this.bDelete.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + workspace.getEditorPart().removeSelected(); + } + }); + this.bCut.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + CutCopyPasteBehavior cutCopyPasteBehavior = getCutCopyPasteBehavior(); + if (cutCopyPasteBehavior != null) { + cutCopyPasteBehavior.cut(); + } + } + }); + this.bCopy.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + CutCopyPasteBehavior cutCopyPasteBehavior = getCutCopyPasteBehavior(); + if (cutCopyPasteBehavior != null) { + cutCopyPasteBehavior.copy(); + } + } + }); + this.bPaste.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + CutCopyPasteBehavior cutCopyPasteBehavior = getCutCopyPasteBehavior(); + if (cutCopyPasteBehavior != null) { + cutCopyPasteBehavior.paste(); + } + } + }); + } + + /** + * Looks for UndoRedoBehavior on the current editor part + * + * @return the first UndoRedoBehavior object found or null + */ + private UndoRedoCompoundBehavior getUndoRedoBehavior() { + IEditorPart activeEditorPart = workspace.getEditorPart(); + IEditorPartBehaviorManager behaviorManager = activeEditorPart.getBehaviorManager(); + List found = behaviorManager.getBehaviors(UndoRedoCompoundBehavior.class); + if (found.size() != 1) { + return null; + } + return found.get(0); + } + + /** + * Looks for CutCopyPasteBehavior on the current editor part + * + * @return the first CutCopyPasteBehavior object found or null + */ + private CutCopyPasteBehavior getCutCopyPasteBehavior() { + IEditorPart activeEditorPart = workspace.getEditorPart(); + IEditorPartBehaviorManager behaviorManager = activeEditorPart.getBehaviorManager(); + List found = behaviorManager.getBehaviors(CutCopyPasteBehavior.class); + if (found.size() != 1) { + return null; + } + return found.get(0); + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.sidebar.ISideBarElement#getTitle() + */ + public String getTitle() + { + return this.title; + } + + + /* (non-Javadoc) + * @see com.horstmann.violet.product.workspace.sidebar.ISideBarElement#install(com.horstmann.violet.product.workspace.IWorkspace) + */ + public void install(IWorkspace workspace) + { + this.workspace = workspace; + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.sidebar.ISideBarElement#getAWTComponent() + */ + public Component getAWTComponent() + { + return this; + } + + /** + * @return zoom in button + */ + JButton getZoomInButton() + { + return this.bZoomIn; + } + + /** + * @return zoom out button + */ + JButton getZoomOutButton() + { + return this.bZoomOut; + } + + /** + * @return undo button + */ + JButton getUndoButton() + { + return this.bUndo; + } + + /** + * @return redo button + */ + JButton getRedoButton() + { + return this.bRedo; + } + + /** + * @return delete button + */ + JButton getDeleteButton() + { + return this.bDelete; + } + + /** + * @return cut button + */ + JButton getCutButton() + { + return this.bCut; + } + + /** + * @return copy button + */ + JButton getCopyButton() + { + return this.bCopy; + } + + /** + * @return paste button + */ + JButton getPasteButton() + { + return this.bPaste; + } + + + /** current workspace */ + private IWorkspace workspace; + + @ResourceBundleBean(key = "zoomin") + private JButton bZoomIn; + @ResourceBundleBean(key = "zoomout") + private JButton bZoomOut; + @ResourceBundleBean(key = "undo") + private JButton bUndo; + @ResourceBundleBean(key = "redo") + private JButton bRedo; + @ResourceBundleBean(key = "delete") + private JButton bDelete; + @ResourceBundleBean(key = "cut") + private JButton bCut; + @ResourceBundleBean(key = "copy") + private JButton bCopy; + @ResourceBundleBean(key = "paste") + private JButton bPaste; + @ResourceBundleBean(key = "title.standardbuttons.text") + private String title; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/editortools/EditorToolsPanelUI.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/editortools/EditorToolsPanelUI.java new file mode 100644 index 0000000..237b2c9 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/editortools/EditorToolsPanelUI.java @@ -0,0 +1,150 @@ +package com.horstmann.violet.workspace.sidebar.editortools; + +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.PanelUI; + +import com.horstmann.violet.framework.swingextension.IconButtonUI; +import com.horstmann.violet.framework.theme.ThemeManager; + +/** + * UI for displaying a large EditorToolsPanel + * + * @author Alexandre de Pellegrin + * + */ +public class EditorToolsPanelUI extends PanelUI +{ + + /** + * Default constructor + * + * @param editorToolsPanel + */ + public EditorToolsPanelUI(EditorToolsPanel editorToolsPanel) + { + this.editorToolsPanel = editorToolsPanel; + } + + @Override + public void installUI(JComponent c) + { + c.removeAll(); + c.setBackground(ThemeManager.getInstance().getTheme().getSidebarElementBackgroundColor()); + + this.editorToolsPanel.getZoomInButton().setUI(new IconButtonUI(FULLSIZE_SCALING_FACTOR)); + this.editorToolsPanel.getZoomOutButton().setUI(new IconButtonUI(FULLSIZE_SCALING_FACTOR)); + this.editorToolsPanel.getDeleteButton().setUI(new IconButtonUI(FULLSIZE_SCALING_FACTOR)); + this.editorToolsPanel.getUndoButton().setUI(new IconButtonUI(FULLSIZE_SCALING_FACTOR)); + this.editorToolsPanel.getRedoButton().setUI(new IconButtonUI(FULLSIZE_SCALING_FACTOR)); + this.editorToolsPanel.getCutButton().setUI(new IconButtonUI(FULLSIZE_SCALING_FACTOR)); + this.editorToolsPanel.getCopyButton().setUI(new IconButtonUI(FULLSIZE_SCALING_FACTOR)); + this.editorToolsPanel.getPasteButton().setUI(new IconButtonUI(FULLSIZE_SCALING_FACTOR)); + + c.setLayout(new FlowLayout(FlowLayout.CENTER)); + c.add(getToolsPanel()); + } + + /** + * @return the main panel + */ + private JPanel getToolsPanel() + { + if (this.toolsPanel == null) + { + this.toolsPanel = new JPanel(); + this.toolsPanel.setOpaque(false); + this.toolsPanel.setBorder(new EmptyBorder(0, 0, 0, 0)); + this.toolsPanel.add(this.editorToolsPanel.getUndoButton()); + this.toolsPanel.add(this.editorToolsPanel.getZoomInButton()); + this.toolsPanel.add(this.editorToolsPanel.getZoomOutButton()); + this.toolsPanel.add(this.editorToolsPanel.getDeleteButton()); + this.toolsPanel.add(this.editorToolsPanel.getRedoButton()); + this.toolsPanel.add(this.editorToolsPanel.getCutButton()); + this.toolsPanel.add(this.editorToolsPanel.getCopyButton()); + this.toolsPanel.add(this.editorToolsPanel.getPasteButton()); + + GridBagLayout layout = new GridBagLayout(); + this.toolsPanel.setLayout(layout); + + GridBagConstraints c1 = new GridBagConstraints(); + c1.anchor = GridBagConstraints.CENTER; + c1.insets = new Insets(0, 0, 5, 15); + c1.gridx = 0; + c1.gridy = 0; + layout.setConstraints(this.editorToolsPanel.getUndoButton(), c1); + + GridBagConstraints c2 = new GridBagConstraints(); + c2.anchor = GridBagConstraints.CENTER; + c2.insets = new Insets(0, 0, 5, 15); + c2.gridx = 1; + c2.gridy = 0; + layout.setConstraints(this.editorToolsPanel.getZoomInButton(), c2); + + GridBagConstraints c3 = new GridBagConstraints(); + c3.anchor = GridBagConstraints.CENTER; + c3.insets = new Insets(0, 0, 5, 15); + c3.gridx = 2; + c3.gridy = 0; + layout.setConstraints(this.editorToolsPanel.getZoomOutButton(), c3); + + GridBagConstraints c4 = new GridBagConstraints(); + c4.anchor = GridBagConstraints.CENTER; + c4.insets = new Insets(0, 0, 5, 15); + c4.gridx = 3; + c4.gridy = 0; + layout.setConstraints(this.editorToolsPanel.getDeleteButton(), c4); + + GridBagConstraints c5 = new GridBagConstraints(); + c5.anchor = GridBagConstraints.CENTER; + c5.insets = new Insets(0, 0, 5, 0); + c5.gridx = 4; + c5.gridy = 0; + layout.setConstraints(this.editorToolsPanel.getRedoButton(), c5); + + GridBagConstraints c6 = new GridBagConstraints(); + c6.anchor = GridBagConstraints.CENTER; + c6.insets = new Insets(0, 0, 0, 15); + c6.gridx = 1; + c6.gridy = 1; + layout.setConstraints(this.editorToolsPanel.getCutButton(), c6); + + GridBagConstraints c7 = new GridBagConstraints(); + c7.insets = new Insets(0, 0, 0, 15); + c7.weightx = 1; + c7.gridx = 2; + c7.gridy = 1; + layout.setConstraints(this.editorToolsPanel.getCopyButton(), c7); + + GridBagConstraints c8 = new GridBagConstraints(); + c8.insets = new Insets(0, 0, 0, 15); + c8.weightx = 1; + c8.gridx = 3; + c8.gridy = 1; + layout.setConstraints(this.editorToolsPanel.getPasteButton(), c8); + } + return this.toolsPanel; + } + + /** + * Full size icon scaling factor + */ + private static final double FULLSIZE_SCALING_FACTOR = 1; + + /** + * Panel containing tools + */ + private JPanel toolsPanel; + + /** + * Main panel + */ + private EditorToolsPanel editorToolsPanel; + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphTool.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphTool.java new file mode 100644 index 0000000..d657b22 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphTool.java @@ -0,0 +1,215 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.workspace.sidebar.graphtools; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; +import java.awt.geom.Point2D; +import java.awt.geom.Rectangle2D; +import java.util.Locale; +import java.util.ResourceBundle; + +import javax.swing.Icon; + +import com.horstmann.violet.framework.injection.resources.ResourceBundleConstant; +import com.horstmann.violet.framework.util.GrabberUtils; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.product.diagram.common.PointNode; + +public class GraphTool +{ + + /** + * Constructs an edge type tool + * + * @param e + * @param label + */ + public GraphTool(final IEdge e, String label) + { + this.nodeOrEdge = e; + this.label = label; + this.icon = new Icon() + { + public int getIconHeight() + { + return ICON_SIZE; + } + + public int getIconWidth() + { + return ICON_SIZE; + } + + public void paintIcon(Component c, Graphics g, int x, int y) + { + Graphics2D g2 = (Graphics2D) g; + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + PointNode p = new PointNode(); + p.translate(OFFSET, OFFSET); + PointNode q = new PointNode(); + q.translate(ICON_SIZE - OFFSET, ICON_SIZE - OFFSET); + e.setStart(p); + e.setStartLocation(new Point2D.Double(0, 0)); + e.setEnd(q); + e.setEndlocation(new Point2D.Double(0, 0)); + + Rectangle2D bounds = new Rectangle2D.Double(); + bounds.add(p.getBounds()); + bounds.add(q.getBounds()); + bounds.add(e.getBounds()); + + double width = bounds.getWidth(); + double height = bounds.getHeight(); + double scaleX = (ICON_SIZE - OFFSET) / width; + double scaleY = (ICON_SIZE - OFFSET) / height; + double scale = Math.min(scaleX, scaleY); + + AffineTransform oldTransform = g2.getTransform(); + g2.translate(x, y); + g2.scale(scale, scale); + g2.translate(Math.max((height - width) / 2, 0), Math.max((width - height) / 2, 0)); + + g2.setColor(Color.black); + e.draw(g2); + g2.setTransform(oldTransform); + } + }; + + } + + /** + * Constructs a node type tool + * + * @param n + * @param label + */ + public GraphTool(final INode n, String label) + { + this.nodeOrEdge = n; + this.label = label; + this.icon = new Icon() + { + public int getIconHeight() + { + return ICON_SIZE; + } + + public int getIconWidth() + { + return ICON_SIZE; + } + + public void paintIcon(Component c, Graphics g, int x, int y) + { + // Use a buffer image to be more precise in rendering, especially for join/fork node + double width = n.getBounds().getWidth(); + double height = n.getBounds().getHeight(); + // BufferedImage image = new BufferedImage(ICON_SIZE, ICON_SIZE, BufferedImage.TYPE_INT_ARGB); + Graphics2D g2 = (Graphics2D) g; + // Graphics2D g2 = (Graphics2D) image.getGraphics(); + g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + double scaleX = (ICON_SIZE - OFFSET) / width; + double scaleY = (ICON_SIZE - OFFSET) / height; + double scale = Math.min(scaleX, scaleY); + + AffineTransform oldTransform = g2.getTransform(); + g2.translate(x + OFFSET / 2, y + OFFSET / 2); + g2.scale(scale, scale); + g2.translate(Math.max((height - width) / 2, 0), Math.max((width - height) / 2, 0)); + g2.setColor(Color.black); + n.draw(g2); + g2.setTransform(oldTransform); + + // g2b.drawImage(image, x, y, null); + } + }; + } + + /** + * Special constructor for selection tool + */ + public GraphTool() + { + this.icon = new Icon() + { + public int getIconHeight() + { + return ICON_SIZE; + } + + public int getIconWidth() + { + return ICON_SIZE; + } + + public void paintIcon(Component c, Graphics g, int x, int y) + { + Graphics2D g2 = (Graphics2D) g; + GrabberUtils.drawGrabber(g2, x + OFFSET, y + OFFSET); + GrabberUtils.drawGrabber(g2, x + OFFSET, y + ICON_SIZE - OFFSET); + GrabberUtils.drawGrabber(g2, x + ICON_SIZE - OFFSET, y + OFFSET); + GrabberUtils.drawGrabber(g2, x + ICON_SIZE - OFFSET, y + ICON_SIZE - OFFSET); + } + }; + ResourceBundle rs = ResourceBundle.getBundle(ResourceBundleConstant.OTHER_STRINGS, Locale.getDefault()); + this.label = rs.getString("grabber.tooltip"); + this.nodeOrEdge = null; + } + + /** + * @return tool's icon + */ + public Icon getIcon() + { + return icon; + } + + /** + * @return tool's label + */ + public String getLabel() + { + return label; + } + + /** + * @return node or edge associated with this tool + */ + public Object getNodeOrEdge() + { + return nodeOrEdge; + } + + private Object nodeOrEdge; + private Icon icon; + private String label; + private static final int ICON_SIZE = 20; + private static final int OFFSET = 4; + public static final GraphTool SELECTION_TOOL = new GraphTool(); +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBar.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBar.java new file mode 100644 index 0000000..22bc357 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBar.java @@ -0,0 +1,279 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.workspace.sidebar.graphtools; + +import java.awt.Component; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import com.horstmann.violet.product.diagram.abstracts.IGraph; +import com.horstmann.violet.product.diagram.abstracts.edge.IEdge; +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.IWorkspace; +import com.horstmann.violet.workspace.editorpart.IEditorPart; +import com.horstmann.violet.workspace.sidebar.ISideBarElement; + +/** + * A tool bar that contains node and edge prototype icons. Exactly one icon is selected at any time. + */ +public class GraphToolsBar implements IGraphToolsBar, ISideBarElement +{ + + + + public void install(IWorkspace workspace) + { + IEditorPart editorPart = workspace.getEditorPart(); + IGraph graph = editorPart.getGraph(); + this.nodeTools = getStandardNodeTools(graph); + this.edgeTools = getStandardEdgeTools(graph); + this.panel = new GraphToolsBarPanel(this); + this.panel.setUI(new GraphToolsBarPanelUI()); + reset(); + editorPart.getSelectionHandler().setSelectedTool(getSelectedTool()); + } + + public void selectNextTool() + { + int nextPos = 0; + GraphTool selectedTool = getSelectedTool(); + int posForNodes = this.nodeTools.indexOf(selectedTool); + if (posForNodes >= 0) + { + nextPos = posForNodes + 1; + if (nextPos < this.nodeTools.size()) + { + setSelectedTool(this.nodeTools.get(nextPos)); + } + if (nextPos >= this.nodeTools.size() && this.edgeTools.size() > 0) + { + setSelectedTool(this.edgeTools.get(0)); + } + return; + } + int posForEdges = this.edgeTools.indexOf(selectedTool); + if (posForEdges >= 0) + { + nextPos = posForEdges + 1; + if (nextPos < this.edgeTools.size()) + { + setSelectedTool(this.edgeTools.get(nextPos)); + } + return; + } + } + + public void selectPreviousTool() + { + int previousPos = 0; + GraphTool selectedButton = getSelectedTool(); + int posForNodes = this.nodeTools.indexOf(selectedButton); + if (posForNodes >= 0) + { + previousPos = posForNodes - 1; + if (previousPos >= 0) + { + setSelectedTool(this.nodeTools.get(previousPos)); + } + return; + } + int posForEdges = this.edgeTools.indexOf(selectedButton); + if (posForEdges >= 0) + { + previousPos = posForEdges - 1; + if (previousPos >= 0) + { + setSelectedTool(this.edgeTools.get(previousPos)); + } + if (previousPos < 0 && this.nodeTools.size() > 0) + { + setSelectedTool(this.nodeTools.get(this.nodeTools.size() - 1)); + } + return; + } + } + + public Component getAWTComponent() + { + return this.panel; + } + + /** + * @return current graph node tools + */ + protected List getNodeTools() { + return this.nodeTools; + } + + /** + * @return current graph edge tools + */ + protected List getEdgeTools() { + return this.edgeTools; + } + + + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.sidebar.ISideToolPanel#addCustomTool(com.horstmann.violet.product.diagram.abstracts.Node, + * java.lang.String) + */ + public void addTool(INode nodePrototype, String title) + { + GraphTool newTool = new GraphTool(nodePrototype, title); + nodeTools.add(newTool); + // FIXME : rebuild UI + + } + + /** + * Returns standard node tools associated to a graph + * + * @param graph + * @return tools collection + */ + private List getStandardNodeTools(IGraph graph) + { + List nodeTypes = graph.getNodePrototypes(); + List tools = new ArrayList(); + GraphTool firstTool = GraphTool.SELECTION_TOOL; + tools.add(firstTool); + if (nodeTypes.size() == 0) + { + return tools; + } + for (int i = 0; i < nodeTypes.size(); i++) + { + GraphTool aTool = new GraphTool(nodeTypes.get(i), nodeTypes.get(i).getToolTip()); + tools.add(aTool); + } + return tools; + } + + /** + * Returns standard edge tools associated to a graph + * + * @param graph + * @return tools collection + */ + private List getStandardEdgeTools(IGraph graph) + { + List edgeTypes = graph.getEdgePrototypes(); + List tools = new ArrayList(); + if (edgeTypes.size() == 0) + { + return tools; + } + for (int i = 0; i < edgeTypes.size(); i++) + { + GraphTool aTool = new GraphTool(edgeTypes.get(i), edgeTypes.get(i).getToolTip()); + tools.add(aTool); + } + return tools; + } + + + + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.sidebar.ISideToolPanel#getSelectedTool() + */ + public GraphTool getSelectedTool() + { + return this.selectedTool; + } + + + protected void setSelectedTool(GraphTool t) { + boolean isNewSelection = !t.equals(this.selectedTool); + this.selectedTool = t; + if (isNewSelection) { + fireToolChangeEvent(t); + } + } + + + /** + * Remenbers selected tool and informs all listeners about this change + * + * @param nodeOrEdge + */ + private void fireToolChangeEvent(GraphTool tool) + { + Iterator it = this.listeners.iterator(); + while (it.hasNext()) + { + IGraphToolsBarListener listener = it.next(); + listener.toolSelectionChanged(tool); + } + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.sidebar.ISideToolPanel#reset() + */ + public void reset() + { + if (this.nodeTools.size() > 0) + { + setSelectedTool(this.nodeTools.get(0)); + } + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.sidebar.ISideToolPanel#addListener(com.horstmann.violet.framework.display.clipboard.sidebar.SideToolPanel.Listener) + */ + public void addListener(IGraphToolsBarListener listener) + { + this.listeners.add(listener); + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.sidebar.ISideToolPanel#removeListener(com.horstmann.violet.framework.display.clipboard.sidebar.SideToolPanel.Listener) + */ + public void removeListener(IGraphToolsBarListener listener) + { + this.listeners.remove(listener); + } + + private List listeners = new ArrayList(); + private List nodeTools; + private List edgeTools; + private GraphTool selectionTool = GraphTool.SELECTION_TOOL; + private GraphTool selectedTool; + private GraphToolsBarPanel panel; + + + + + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBarButton.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBarButton.java new file mode 100644 index 0000000..bf2d1b4 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBarButton.java @@ -0,0 +1,46 @@ +package com.horstmann.violet.workspace.sidebar.graphtools; + +import com.horstmann.violet.framework.swingextension.CustomToggleButton; +import com.horstmann.violet.framework.theme.ThemeManager; + +/** + * Button embedding a graph tool + * + * @author Alexandre de Pellegrin + * + */ +public class GraphToolsBarButton extends CustomToggleButton +{ + + /** + * Default constructor + * + * @param aTool to be displayed as a button + */ + public GraphToolsBarButton(GraphTool aTool) + { + super(aTool.getLabel(), aTool.getIcon(), ThemeManager.getInstance().getTheme().getToggleButtonSelectedColor(), ThemeManager + .getInstance().getTheme().getToggleButtonSelectedBorderColor(), ThemeManager.getInstance().getTheme() + .getToggleButtonUnselectedColor()); + this.tool = aTool; + } + + + /** + * @return embedded tool + */ + public GraphTool getTool() + { + return tool; + } + + + /** + * Embedded graph tool + */ + private GraphTool tool; + + + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBarPanel.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBarPanel.java new file mode 100644 index 0000000..51da996 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBarPanel.java @@ -0,0 +1,302 @@ +package com.horstmann.violet.workspace.sidebar.graphtools; + +import java.awt.GridLayout; +import java.awt.event.InputEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.util.ArrayList; +import java.util.List; + +import javax.swing.JPanel; + +public class GraphToolsBarPanel extends JPanel +{ + + + + + public GraphToolsBarPanel(GraphToolsBar umlToolsPanel) + { + super(); + this.graphToolsPanel = umlToolsPanel; + this.nodeButtons = getToggleButtons(this.graphToolsPanel.getNodeTools()); + this.edgeButtons = getToggleButtons(this.graphToolsPanel.getEdgeTools()); + this.graphToolsPanel.addListener(getGraphToolsPanelListener()); + } + + + + + + + private IGraphToolsBarListener getGraphToolsPanelListener() { + if (this.listener == null) { + this.listener = new IGraphToolsBarListener() { + public void toolSelectionChanged(GraphTool selectedTool) + { + for (GraphToolsBarButton aButton : nodeButtons) { + if (aButton.getTool().equals(selectedTool)) { + setSelectedButton(aButton); + return; + } + } + for (GraphToolsBarButton aButton : edgeButtons) { + if (aButton.getTool().equals(selectedTool)) { + setSelectedButton(aButton); + return; + } + } + } + }; + } + return this.listener; + } + + /** + * @param diagram tools + * @return buttons representing tools + */ + private List getToggleButtons(List tools) + { + List buttons = new ArrayList(); + for (GraphTool aTool : tools) + { + final GraphToolsBarButton button = new GraphToolsBarButton(aTool); + buttons.add(button); + } + return buttons; + } + + /** + * @return curretly selected button + */ + private GraphToolsBarButton getSelectedButton() + { + for (GraphToolsBarButton button : this.nodeButtons) + { + if (button.isSelected()) + { + return button; + } + } + for (GraphToolsBarButton button : this.edgeButtons) + { + if (button.isSelected()) + { + return button; + } + } + + return this.nodeButtons.get(0); + } + + /** + * @return all node buttons + */ + public List getNodeButtons() { + return this.nodeButtons; + } + + + + + + + /** + * @return all edge buttons + */ + public List getEdgeButtons() { + return this.edgeButtons; + } + + + + + + + public void selectNextButton() + { + int nextPos = 0; + GraphToolsBarButton selectedButton = getSelectedButton(); + int posForNodes = this.nodeButtons.indexOf(selectedButton); + if (posForNodes >= 0) + { + nextPos = posForNodes + 1; + if (nextPos < this.nodeButtons.size()) + { + setSelectedButton(this.nodeButtons.get(nextPos)); + } + if (nextPos >= this.nodeButtons.size() && this.edgeButtons.size() > 0) + { + setSelectedButton(this.edgeButtons.get(0)); + } + return; + } + int posForEdges = this.edgeButtons.indexOf(selectedButton); + if (posForEdges >= 0) + { + nextPos = posForEdges + 1; + if (nextPos < this.edgeButtons.size()) + { + setSelectedButton(this.edgeButtons.get(nextPos)); + } + return; + } + } + + + + + + + public void selectPreviousButton() + { + int previousPos = 0; + GraphToolsBarButton selectedButton = getSelectedButton(); + int posForNodes = this.nodeButtons.indexOf(selectedButton); + if (posForNodes >= 0) + { + previousPos = posForNodes - 1; + if (previousPos >= 0) + { + setSelectedButton(this.nodeButtons.get(previousPos)); + } + return; + } + int posForEdges = this.edgeButtons.indexOf(selectedButton); + if (posForEdges >= 0) + { + previousPos = posForEdges - 1; + if (previousPos >= 0) + { + setSelectedButton(this.edgeButtons.get(previousPos)); + } + if (previousPos < 0 && this.nodeButtons.size() > 0) + { + setSelectedButton(this.nodeButtons.get(this.nodeButtons.size() - 1)); + } + return; + } + } + + + + + + + /** + * Performs button select + * + * @param selectedButton to be considered as selected + */ + private void setSelectedButton(GraphToolsBarButton selectedButton) + { + for (GraphToolsBarButton button : this.nodeButtons) + { + if (button != selectedButton) + { + button.setSelected(false); + } + if (button == selectedButton) + { + button.setSelected(true); + int pos = this.nodeButtons.indexOf(button); + this.graphToolsPanel.setSelectedTool(this.graphToolsPanel.getNodeTools().get(pos)); + } + } + for (GraphToolsBarButton button : this.edgeButtons) + { + if (button != selectedButton) + { + button.setSelected(false); + } + if (button == selectedButton) + { + button.setSelected(true); + int pos = this.edgeButtons.indexOf(button); + this.graphToolsPanel.setSelectedTool(this.graphToolsPanel.getEdgeTools().get(pos)); + } + } + } + + + + + + + /** + * @return panel containing node buttons + */ + public JPanel getNodeButtonsPanel() { + if (this.nodeButtonsPanel == null) { + this.nodeButtonsPanel = getButtonPanel(this.nodeButtons); + } + return this.nodeButtonsPanel; + } + + /** + * @return panel containing edge buttons + */ + public JPanel getEdgeButtonsPanel() { + if (this.edgeButtonsPanel == null) { + this.edgeButtonsPanel = getButtonPanel(this.edgeButtons); + } + return this.edgeButtonsPanel; + } + + /** + * Creates a panel that contains custom toggle buttons. Also sets mouse listeners. + * + * @param buttons to be added to this panel + * @return JPanel + */ + private JPanel getButtonPanel(List buttons) + { + JPanel buttonPanel = new JPanel(); + for (final GraphToolsBarButton button : buttons) + { + button.addMouseListener(new MouseAdapter() + { + public void mouseClicked(MouseEvent arg0) + { + setSelectedButton(button); + } + }); + buttonPanel.add(button); + } + + buttonPanel.setLayout(new GridLayout(0, 1)); +// buttonPanel.addMouseWheelListener(new MouseWheelListener() +// { +// +// public void mouseWheelMoved(MouseWheelEvent e) +// { +// boolean isCtrl = (e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0; +// if (isCtrl) { +// return; +// } +// int scroll = e.getUnitsToScroll(); +// if (scroll > 0) +// { +// selectNextButton(); +// } +// if (scroll < 0) +// { +// selectPreviousButton(); +// } +// } +// +// }); + return buttonPanel; + } + + + private IGraphToolsBarListener listener; + private GraphToolsBar graphToolsPanel; + private List nodeButtons; + private List edgeButtons; + private JPanel nodeButtonsPanel; + private JPanel edgeButtonsPanel; +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBarPanelUI.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBarPanelUI.java new file mode 100644 index 0000000..ed39087 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/GraphToolsBarPanelUI.java @@ -0,0 +1,33 @@ +package com.horstmann.violet.workspace.sidebar.graphtools; + +import java.awt.BorderLayout; + +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.plaf.PanelUI; + +import com.horstmann.violet.framework.theme.ThemeManager; + +public class GraphToolsBarPanelUI extends PanelUI +{ + + @Override + public void installUI(JComponent c) + { + GraphToolsBarPanel panel = (GraphToolsBarPanel) c; + panel.removeAll(); + panel.setBackground(ThemeManager.getInstance().getTheme().getSidebarElementBackgroundColor()); + for (GraphToolsBarButton button : panel.getNodeButtons()) { + button.setTextVisible(true); + } + for (GraphToolsBarButton button : panel.getEdgeButtons()) { + button.setTextVisible(true); + } + JPanel nodeButtonsPanel = panel.getNodeButtonsPanel(); + JPanel edgeButtonsPanel = panel.getEdgeButtonsPanel(); + panel.setLayout(new BorderLayout()); + panel.add(nodeButtonsPanel, BorderLayout.NORTH); + panel.add(edgeButtonsPanel, BorderLayout.SOUTH); + } + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/IGraphToolsBar.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/IGraphToolsBar.java new file mode 100644 index 0000000..2ae157f --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/IGraphToolsBar.java @@ -0,0 +1,83 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.workspace.sidebar.graphtools; + +import com.horstmann.violet.product.diagram.abstracts.node.INode; +import com.horstmann.violet.workspace.sidebar.ISideBarElement; + +/** + * Side tool panel interface + * + * @author Alexandre de Pellegrin + * + */ +public interface IGraphToolsBar extends ISideBarElement +{ + + /** + * Registers a new node tool to this panel.
+ *
+ * Example :
+ * ImageIcon imageIcon = new ImageIcon(this.getClass().getResource("/icons/72x72/welcome_create.png"));
+ * final ImageNode imageNode = new ImageNode(imageIcon.getImage());
+ * addCustomTool(imageNode, "test");
+ * + * @param nodePrototype + * @param title + */ + public void addTool(INode nodePrototype, String title); + + /** + * Select next tool + */ + public void selectNextTool(); + + /** + * Select previous tool + */ + public void selectPreviousTool(); + + /** + * Resets tool selection by selecting the first of the list + */ + public void reset(); + + /** + * Declares a new listener + * + * @param listener + */ + public void addListener(IGraphToolsBarListener listener); + + /** + * Removes a declared listener + * + * @param listener + */ + public void removeListener(IGraphToolsBarListener listener); + + /** + * @return currently selected tool + */ + public GraphTool getSelectedTool(); + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/IGraphToolsBarListener.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/IGraphToolsBarListener.java new file mode 100644 index 0000000..12f9991 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/graphtools/IGraphToolsBarListener.java @@ -0,0 +1,19 @@ +package com.horstmann.violet.workspace.sidebar.graphtools; + +/** + * Listener to be implmenented and registered by each class that needs to know toolbar actions + * + * @author Alexandre de Pellegrin + * + */ +public interface IGraphToolsBarListener +{ + + /** + * Invoked when a tool is selected + * + * @param selectedNodeOrEdge the selected tool + */ + void toolSelectionChanged(GraphTool selectedTool); + +} \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/optionaltools/OptionalToolsPanel.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/optionaltools/OptionalToolsPanel.java new file mode 100644 index 0000000..a2a07f1 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/optionaltools/OptionalToolsPanel.java @@ -0,0 +1,126 @@ +/* + Violet - A program for editing UML diagrams. + + Copyright (C) 2007 Cay S. Horstmann (http://horstmann.com) + Alexandre de Pellegrin (http://alexdp.free.fr); + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +package com.horstmann.violet.workspace.sidebar.optionaltools; + +import java.awt.Component; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.JButton; +import javax.swing.JPanel; + +import com.horstmann.violet.framework.injection.resources.ResourceBundleInjector; +import com.horstmann.violet.framework.injection.resources.annotation.ResourceBundleBean; +import com.horstmann.violet.workspace.IWorkspace; +import com.horstmann.violet.workspace.sidebar.ISideBarElement; +import com.horstmann.violet.workspace.sidebar.SideBar; + +@ResourceBundleBean(resourceReference=SideBar.class) +public class OptionalToolsPanel extends JPanel implements ISideBarElement +{ + + /** + * Default contructor + */ + public OptionalToolsPanel() + { + ResourceBundleInjector.getInjector().inject(this); + this.setUI(new OptionalToolsPanelUI(this)); + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.sidebar.ISideBarElement#install(com.horstmann.violet.framework.display.clipboard.IDiagramPanel) + */ + public void install(IWorkspace diagramPanel) + { + this.diagramPanel = diagramPanel; + } + + /* + * (non-Javadoc) + * + * @see com.horstmann.violet.framework.display.clipboard.sidebar.ISideBarElement#getAWTComponent() + */ + public Component getAWTComponent() + { + return this; + } + + + protected JButton getExportToClipboardButton() + { + bExportToClipboard.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + diagramPanel.getGraphFile().exportToClipboard(); + } + }); + return bExportToClipboard; + } + + protected JButton getPrintButton() + { + bPrint.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + diagramPanel.getGraphFile().exportToPrinter(); + } + }); + return bPrint; + } + + protected JButton getHelpButton() + { + bHelp.addActionListener(new ActionListener() + { + public void actionPerformed(ActionEvent e) + { + //TODO : open online support page + } + }); + return bHelp; + } + + /** + * Current diagram panel + */ + private IWorkspace diagramPanel; + + @ResourceBundleBean(key="share_document") + private JButton bShareDocument; + + @ResourceBundleBean(key="export_to_clipboard") + private JButton bExportToClipboard; + + @ResourceBundleBean(key="print") + private JButton bPrint; + + @ResourceBundleBean(key="help") + private JButton bHelp; + + + +} diff --git a/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/optionaltools/OptionalToolsPanelUI.java b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/optionaltools/OptionalToolsPanelUI.java new file mode 100644 index 0000000..85ac763 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/java/com/horstmann/violet/workspace/sidebar/optionaltools/OptionalToolsPanelUI.java @@ -0,0 +1,92 @@ +package com.horstmann.violet.workspace.sidebar.optionaltools; + +import java.awt.FlowLayout; +import java.awt.GridLayout; + +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.EmptyBorder; +import javax.swing.plaf.PanelUI; + +import com.horstmann.violet.framework.swingextension.IconButtonUI; +import com.horstmann.violet.framework.theme.ThemeManager; + +/** + * Large UI for OptionalToolsPanel + * + * @author Alexandre de Pellegrin + * + */ +public class OptionalToolsPanelUI extends PanelUI +{ + + /** + * Default constructor + * + * @param optionalToolsPanel + */ + public OptionalToolsPanelUI(OptionalToolsPanel optionalToolsPanel) + { + this.optionalToolsPanel = optionalToolsPanel; + } + + @Override + public void installUI(JComponent c) + { + c.removeAll(); + + this.optionalToolsPanel.setBackground(ThemeManager.getInstance().getTheme().getSidebarElementBackgroundColor()); + + JButton bHelp = this.optionalToolsPanel.getHelpButton(); + // addButton(bHelp); + + JButton bPrint = this.optionalToolsPanel.getPrintButton(); + addButton(bPrint); + + JButton bExportToClipboard = this.optionalToolsPanel.getExportToClipboardButton(); + addButton(bExportToClipboard); + + this.optionalToolsPanel.setLayout(new FlowLayout(FlowLayout.CENTER, 0, 0)); + this.optionalToolsPanel.add(getPanel()); + + } + + /** + * Adds a button to the main panel + * + * @param aButton + */ + private void addButton(JButton aButton) + { + aButton.setUI(new IconButtonUI()); + getPanel().add(aButton); + } + + /** + * @return the main panel + */ + private JPanel getPanel() + { + if (this.panel == null) + { + this.panel = new JPanel(); + this.panel.setOpaque(false); + this.panel.setBorder(new EmptyBorder(0, 5, 0, 0)); + GridLayout layout = new GridLayout(0, 5, 15, 10); + this.panel.setLayout(layout); + } + return this.panel; + } + + /** + * Component(s panel + */ + private JPanel panel; + + /** + * Panel we want to construct view + */ + private OptionalToolsPanel optionalToolsPanel; + +} diff --git a/VioletFramework/VioletFramework/src/main/resources/properties/FileStrings.properties b/VioletFramework/VioletFramework/src/main/resources/properties/FileStrings.properties new file mode 100644 index 0000000..3312b9f --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/properties/FileStrings.properties @@ -0,0 +1,11 @@ +files.global.name=All Violet Files +files.global.extension=.violet +files.image.name=Image Files +files.image.extension=.png|.jpg|.jpeg +files.xmi.name=XMI Files +files.xmi.extension=.xmi +files.xmi.step1.xsl=/xsl/violet2xmi.xsl +files.xmi.step2.xsl=/xsl/xmi2xmi.xsl +dialog.overwrite.title=Warning +dialog.overwrite.ok=This file already exists.\nDo you want to overwrite it? +dialog.overwrite.icon=/icons/64x64/save.png \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/resources/properties/FileStrings_fr.properties b/VioletFramework/VioletFramework/src/main/resources/properties/FileStrings_fr.properties new file mode 100644 index 0000000..a2e21a4 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/properties/FileStrings_fr.properties @@ -0,0 +1,11 @@ +files.global.name=Fichiers Violet +files.global.extension=.violet +files.image.name=Fichiers image +files.image.extension=.png|.jpg|.jpeg +files.xmi.name=Fichiers XMI +files.xmi.extension=.xmi +files.xmi.step1.xsl=/xsl/violet2xmi.xsl +files.xmi.step2.xsl=/xsl/xmi2xmi.xsl +dialog.overwrite.title=Attention +dialog.overwrite.ok=Ce fichier existe d\u00e9j\u00e0.\nVoulez-vous l'\u00e9craser? +dialog.overwrite.icon=/icons/64x64/save.png \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/resources/properties/NodeAndEdgeStrings.properties b/VioletFramework/VioletFramework/src/main/resources/properties/NodeAndEdgeStrings.properties new file mode 100644 index 0000000..540dc69 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/properties/NodeAndEdgeStrings.properties @@ -0,0 +1,19 @@ +name=Name +color=Color +text=Text +startarrowhead=Start arrow head +startlabel=Start label +middlelabel=Middle label +endlabel=End label +endarrowhead=End arrow head +bentstyle=Bent style +linestyle=Line style +diagramLink=Linked diagram +attributes=Attributes +methods=Methods +contents=Contents +condition=Condition +width=Width +signal=Signal +value=Value + diff --git a/VioletFramework/VioletFramework/src/main/resources/properties/NodeAndEdgeStrings_fr.properties b/VioletFramework/VioletFramework/src/main/resources/properties/NodeAndEdgeStrings_fr.properties new file mode 100644 index 0000000..753f8d3 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/properties/NodeAndEdgeStrings_fr.properties @@ -0,0 +1,19 @@ +name=Nom +color=Couleur +text=Texte +startarrowhead=Extr\u00E9mit\u00E9 de d\u00E9part +startlabel=Label de d\u00E9part +middlelabel=Label central +endlabel=Label de fin +endarrowhead=Extr\u00E9mit\u00E9 de fin +bentstyle=Forme +linestyle=Trac\u00E9 +diagramlink=Diagramme li\u00E9 +attributes=Attributs +methods=M\u00E9thodes +contents=Contenus +condition=Condition +width=Largeur +signal=Signal +value=Valeur + diff --git a/VioletFramework/VioletFramework/src/main/resources/properties/OtherStrings.properties b/VioletFramework/VioletFramework/src/main/resources/properties/OtherStrings.properties new file mode 100644 index 0000000..2445e7f --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/properties/OtherStrings.properties @@ -0,0 +1,13 @@ +dialog.properties.title=Properties +dialog.properties.empty_bean_message=No property to edit. +dialog.error_version.title=Error +dialog.error_version.text=You need Java version {0} +dialog.generic.error.title=Error +dialog.generic.error.icon=/icons/64x64/error.png +dialog.generic.warning.title=Warning +dialog.generic.warning.icon=/icons/64x64/warning.png +dialog.generic.information.title=Information +dialog.generic.information.icon=/icons/64x64/information.png +grabber.tooltip=Select +file.link.open.text=Open diagram +file.link.text=Linked to diff --git a/VioletFramework/VioletFramework/src/main/resources/properties/OtherStrings_fr.properties b/VioletFramework/VioletFramework/src/main/resources/properties/OtherStrings_fr.properties new file mode 100644 index 0000000..936c027 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/properties/OtherStrings_fr.properties @@ -0,0 +1,13 @@ +dialog.properties.title=Propri\u00E9t\u00E9s +dialog.properties.empty_bean_message=Aucune propri\u00E9t\u00E9 \u00E0 \u00E9diter. +dialog.error_version.title=Erreur +dialog.error_version.text=Vous devez avoir Java version {0} +dialog.generic.error.title=Erreur +dialog.generic.error.icon=/icons/64x64/error.png +dialog.generic.warning.title=Attention +dialog.generic.warning.icon=/icons/64x64/warning.png +dialog.generic.information.title=Information +dialog.generic.information.icon=/icons/64x64/information.png +grabber.tooltip=S\u00E9lectionner +file.link.open.text=Ouvrir le diagramme +file.link.text=Li\u00E9 \u00E0 diff --git a/VioletFramework/VioletFramework/src/main/resources/properties/SyntaxDiagramStrings.properties b/VioletFramework/VioletFramework/src/main/resources/properties/SyntaxDiagramStrings.properties new file mode 100644 index 0000000..a88262e --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/properties/SyntaxDiagramStrings.properties @@ -0,0 +1,6 @@ +node1.tooltip=Start +node2.tooltip=Terminal +node3.tooltip=Nonterminal +node4.tooltip=Junction +node5.tooltip=Stop +edge1.tooltip=Railroad track \ No newline at end of file diff --git a/VioletFramework/VioletFramework/src/main/resources/violet.jnlp.template b/VioletFramework/VioletFramework/src/main/resources/violet.jnlp.template new file mode 100644 index 0000000..0a5a62d --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/violet.jnlp.template @@ -0,0 +1,24 @@ + + + + + Violet UML Editor + Alexandre de Pellegrin / Cay S. Horstmann + Violet UML Editor + + + + + + + + + + + + + + diff --git a/VioletFramework/VioletFramework/src/main/resources/xsl/violet2xmi.xsl b/VioletFramework/VioletFramework/src/main/resources/xsl/violet2xmi.xsl new file mode 100644 index 0000000..92de754 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/xsl/violet2xmi.xsl @@ -0,0 +1,944 @@ + + + + + + + + + + + + + + Violet Plugin + 0.1 revised on $Date: 2008/08/26 22:08:19 $ + + + + + + + ModelName + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + false + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + public + + + + + + + + + - + + + + + - + + + + + + + + + + + | + | + + + + + + + + + + + + + + + | + | + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + false + instance + changeable + instance + + + + --multiplicity + + + --multiplicityrange + + + + + + + ---multiplicity + + + + + + + + + --initvalue + + + + + + + + + + + + + + + + private + + + + protected + + + + public + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + int + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + false + instance + false + sequential + false + false + false + + + + return: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + void + + + + + + + + void + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + -- + + false + + + --- + + + + + + + ----1 + + + + + + + + + + + + + + + + | + | + | + + + + + + + + + + + + + + + + + none + + + + + + none + + + + + + none + + + + + + none + + + + + + none + none + true + true + + + + + + none + none + false + true + + + + + + aggregate + none + true + true + + + + + + composite + none + true + true + + + + + + none + aggregate + true + true + + + + + + none + composite + true + true + + + + + + aggregate + none + false + true + + + + + + composite + none + false + true + + + + + + none + aggregate + false + true + + + + + + none + composite + false + true + + + + + + none + none + true + true + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + false + false + false + + + -1 + + public + + + + + + + + + + -1A + + + -1B + + + + + + + + + + + + + + + + + + + -2A + + + -2B + 0 + -1 + + + + + + + + + -2A + + + -2B + 1 + 1 + + + + + + + + + -2A + + + -2B + 1 + 1 + + + + + + + + + + + + + + -2 + + public + + + + + + + + + + -2A + + + -2B + + + + + + + + + + + + + + + + + + + -2A + + + -2B + 0 + -1 + + + + + + + + + -2A + + + -2B + 1 + 1 + + + + + + + + + -2A + + + -2B + 1 + 1 + + + + + + + + + + + + + + + + + + + + -1 + + + + + + + + + + + + + + + + diff --git a/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2htmldoc.xsl b/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2htmldoc.xsl new file mode 100644 index 0000000..1616ff0 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2htmldoc.xsl @@ -0,0 +1,781 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Class_.html + Class_ + + + Documentation generate by Violet + + + + + + Class + + + Interface + + +

+

+ + + + + + +
Attribute Summary


+ + + + + + +
Method Summary

+ + ( + + + + + + , + + + ) +

+ + + + + + + +
Generalization Summary
+ + + + + + + Generalization.html + description + + +

+ + + + + + + +
Abstraction Summary
+ + + + + + + Abstraction.html + description + + +

+ + + + + + + + +
Association Summary
+ + + + + + + Association.html + description + + +

+ + + + + + + +
Dependency Summary
+ + + + + + + Dependency.html + description + + +

+
+
Attribute Detail
+ +

+ + +

+ + + +
+ +
+
Method Detail
+ +

+ + Parameter
+ + + + +
+
+
+
Documentation HTML generate by Violet
+ +
+
+ + + + + + type:
+
+ + initial value:
+
+ + :
+
+
+
+ + + + + + + + + + + + + Generalization.html + index + + + Documentation generate by Violet + + + +

Generalization

+ + + + + +
Child
+ + + + Class_.html + description + + +

+ + + + + +
Parent
+ + + + Class_.html + description + + +
+
Documentation HTML generate by Violet
+ +
+
+ + + + + + + + + + + + + Abstraction.html + index + + + Documentation generate by Violet + + + +

Abstraction

+ + + + + +
Client
+ + + + Class_.html + description + + +

+ + + + + +
Supplier
+ + + + Class_.html + description + + +
+
Documentation HTML generate by Violet
+ +
+
+ + + + + + + + + + + + + + Dependency.html + index + + + Documentation generate by Violet + + + +

Dependency

+ + + + + +
Client
+ + + + Class_.html + description + + +

+ + + + + +
Supplier
+ + + + Class_.html + description + + +
+
Documentation HTML generate by Violet
+ +
+
+ + + + + + + + + + + + + + Association.html + index + + + Documentation generate by Violet + + + +

Association

+ + + + + + + + +
Association with
+ + + + Class_.html + description + + + + lower: + +
+ upper: + +
+ +

+
Documentation HTML generate by Violet
+ +
+
+ + + + + + + + + index.html + index + + + Documentation generate by Violet + + + + + + + + + + toc.html + toc + + + Documentation generate by Violet + + + +

Summary


+ Documentation HTML generate by Violet + + + +
Name...
Version...
+
Documentation HTML generate by Violet
+ +
+ + menu.html + menu + + + Documentation generate by Violet + + + + + + Menu +
+ + + +
+ + menu.css + menu + body { + margin: 0; + padding: 0; + background: white; + font: 80% verdana, arial, sans-serif; + } + dl, dt, dd, ul, li { + margin: 0; + padding: 0; + list-style-type: none; + } + #menu { + position: absolute; + top: 0; + left: 0; + } + dl#menu { + width: 15em; + } + dl#menu dt { + cursor: pointer; + margin: 2px 0;; + height: 20px; + line-height: 20px; + text-align: center; + font-weight: bold; + border: 1px solid gray; + background: yellow; + } + dl#menu dd { + border: 1px solid gray; + } + dl#menu li { + text-align: center; + background: #fff; + } + dl#menu li a, dl#menu dt a { + color: #000; + text-decoration: none; + display: block; + border: 0 none; + height: 100%; + } + + dl#menu li a:hover, dl#menu dt a:hover { + background: #eee; + } + + #mentions { + font-family: verdana, arial, sans-serif; + position: absolute; + bottom : 200px; + left : 10px; + color: #000; + background-color: #ddd; + } + #mentions a {text-decoration: none; + color: #222; + } + #mentions a:hover{text-decoration: underline; + } + + + menu.js + menu + window.onload=montre; + function montre(id) { + var d = document.getElementById(id); + for (var i = 1; i<=10; i++) { + if (document.getElementById('smenu'+i)) {document.getElementById('smenu'+i).style.display='none';} + } + if (d) {d.style.display='block';} + } + + + toc.css + toc.css + A { + color: #003399; + } + A:active { + color: #003399; + } + A:visited { + color: #888888; + }TABLE.summary { + border-style:solid; + border-width:1px; + border-color:black; + border-collapse: collapse; + border-spacing:0; + empty-cells: hide;width: 90%; + } + TD {border-style:solid; + border-width:1px; + border-color:black; + border-collapse: collapse; + border-spacing:0; + padding: 4pt; + } + TD.TableCol1 { + font-size: 70%; + color: #888888; + } + H1 { + font-size: 120%; + color: blue; + } + H2 { + font-size: 120%; + color: orange; + } + TD.TableCol1 { + width: 30%; + } + TR.TableRowHeading { + background-color:yellow; + } + H6 { + font-size: 60%; + color: grey; + text-align: center; + } + +
+ + + + + + + + + + + + _To_ + + + + + + + + + + + + + + + + + + _To_ + + + + + + + + + + + + + + + + + + _To_ + + + + + + + + + + + + + + + + _To_ + + + + + + + +
diff --git a/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2java.xsl b/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2java.xsl new file mode 100644 index 0000000..a8840cf --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2java.xsl @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .java + + + + + /** + * generate by Violet Uml + * + + + + + + + * More info here + */ + + + + + + + + + + + { + + + + + + + + + + + + + + + + + + + + } + + + + + + + + class + + + + + + + + + + + + extends + + + + + + + , + + + + + + + + + + + + + + + + + + implements + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + = + + + ; + + + + + + + + + + + + + + + + + + + ( + + + + , + + + ){ } + + + + + + + + + + + + + + + + + + + private + + + + + + [] + + + + s + + + + diff --git a/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2mpd.xsl b/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2mpd.xsl new file mode 100644 index 0000000..004df82 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2mpd.xsl @@ -0,0 +1,670 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + com.horstmann.violet.ClassNode + + + + + + + + + + # + + : + + + = + + + + + + + + attribute + + + + attribute + + + + + + + + # + + : + + + = + + + + + + + + + + + + + + + getPK( + + + : + + + = + + + + , + + + ) + + + + + method + + + + method + + + + + + + + + + + + + + + + + + + + + + + + + + - + # + + ( + + + + , + + + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + BLACK_DIAMOND + + + + + + + DIAMOND + + + + + + BLACK_DIAMOND + + + + + + DIAMOND + + + + + + V + + + + + + V + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + _ + + : + + + + + + getFK( + + , + + : + + , + + _ + + : + + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + _ + + : + + + + + + + getFK( + + , + + : + + , + + _ + + : + + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + _ + + : + + + + + + + getFK( + + , + + : + + , + + _ + + : + + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + _ + + + + -1 + com.horstmann.violet.ClassNode + + + + + + + + _ + + : + + + + + + + + _ + + : + + + + + + + + + + getFK( + + , + + : + + , + + _ + + : + + ) + + getFK( + + , + + : + + , + + _ + + : + + ) + + + + + + + + + + + + + + + + + + + + + + + + + + + -1 + + + + + + + + + + + + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + : + + + = + + + + + + + + + getPK( + + + : + + + = + + + + , + + + ) + + + + + + + diff --git a/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2python.xsl b/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2python.xsl new file mode 100644 index 0000000..7df0bc0 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2python.xsl @@ -0,0 +1,288 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .py + + + + + #!/usr/bin/env python + + # generate by Violet Uml + # + + + + + + + # More info here + + + + + + + + + __version__='$Revision: 1.1 $' + __author__='' + __date__='' + + + + + + + + + + + + + ( + + + + , + + + + ) + + : + def __init__(self): + """ + Add description + @since 1.0 + @author + """ + + + + + + + + + + + + + + + + + + + + + + + + + + + from + + + + + import + + + + + from + + + + + import + + + + + + + + + + class + + + + + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + self. + + __ + + + _ + + + = + + + + + + + + + + + + None + + + + + def + + __ + + + _ + + + (self + + , + + + + + , + + + ) + """ + Add description + @since 1.0 + @author + """ + pass + + + + + + = + + + + = None + + + + + + + + + + + + + + self._ + + + + + + = [] + + + = None + + + + + diff --git a/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2ruby.xsl b/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2ruby.xsl new file mode 100644 index 0000000..11fb019 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2ruby.xsl @@ -0,0 +1,237 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + .rb + + + + + #!/usr/bin/env ruby + + # generate by Violet Uml + # class + + + + # More info here + + class + + + + + + + < + + + + , + + + + + + + attr(: + + ,true) + + + attr(: + + ,false) + + + + + + + + def initialize + + ( + + + + + , + + + + + ) + + + # todo + + @ + + = + + + + + + + + + + end + + + + + + + end + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + + , + + + + + + + + + + + + + + + + + + + + + + + + + + + = + + + + + + + + + def + + + ( + + + + + , + + + + ) + + + # todo + end + + + + + + = + + + + + + + + + + + + + + + @ + + + + + = + + + + + + diff --git a/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2sql.xsl b/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2sql.xsl new file mode 100644 index 0000000..2fa5d09 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2sql.xsl @@ -0,0 +1,517 @@ + + + + + + + + + + + + + + export.sql + export.sql + -- + -- generate by Violet + -- + + + + + + + + + + + + + + + + + + + + + -- table + + + + + + + + + + create table + + ( + + + + + DEFAULT + + + + DEFAULT nextval(' + + _ + + ') + + + , + + + + ); + + + + + + + + + + + + + + + + + + + + + + ALTER TABLE + + ADD CONSTRAINT PK_ + + PRIMARY KEY( + + ); + + + + + ALTER TABLE + + ADD CONSTRAINT FK_ + + FOREIGN KEY ( + + ) REFERENCES + + ( + + ); + + + + + + + + + + CREATE INDEX + + + _IDX ON + + ( + + ); + + + + + + + + + + CREATE UNIQUE INDEX + + + _IDX ON + + ( + + ); + + + + + + + + + + + + + + + + + + + + + + + + + CREATE SEQUENCE + + _ + + INCREMENT 1 MINVALUE 1 MAXVALUE 9223372036854775807 CACHE 1; + + + + + + + + + + + + + + ALTER TABLE + + ADD CONSTRAINT PK_ + + PRIMARY KEY( + + ); + + + + + + + + + + + + + CREATE UNIQUE INDEX + + + _IDX ON + + ( + + ); + + + + + + + + + + + + + + + + + + + + + + + + + + + ALTER TABLE + + ADD COLUMN + + _ + + + + ; + + ALTER TABLE + + ADD CONSTRAINT FK_ + + FOREIGN KEY ( + + _ + + ) REFERENCES + + ( + + ) MATCH FULL; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ALTER TABLE + + ADD COLUMN + + _ + + + + ; + + ALTER TABLE + + ADD CONSTRAINT FK_ + + FOREIGN KEY ( + + _ + + ) REFERENCES + + ( + + ) MATCH FULL; + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ALTER TABLE + + ADD COLUMN + + _ + + + + ; + + ALTER TABLE + + ADD CONSTRAINT FK_ + + FOREIGN KEY ( + + _ + + ) REFERENCES + + ( + + ) MATCH FULL; + + + + + + + + + + _ + + + + + create table + + ( + + + _ + + + + + , + + + , + + + _ + + + + + , + + + + ); + + ALTER TABLE + + ADD CONSTRAINT FK_ + + FOREIGN KEY ( + + _ + + ) REFERENCES + + ( + + ) MATCH FULL; + + + ALTER TABLE + + ADD CONSTRAINT FK_ + + FOREIGN KEY ( + + _ + + ) REFERENCES + + ( + + ) MATCH FULL; + + CREATE UNIQUE INDEX + + _IDX ON + + ( + + + _ + + + , + + + , + + + _ + + + , + + + ); + + + + + + + + + + + + + + + + + + ALTER TABLE + + ADD COLUMN + + + + ; + + + + + + + + + ALTER TABLE + + ADD CONSTRAINT PK_ + + PRIMARY KEY( + + ) + + + + + diff --git a/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2xmi.xsl b/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2xmi.xsl new file mode 100644 index 0000000..ef51324 --- /dev/null +++ b/VioletFramework/VioletFramework/src/main/resources/xsl/xmi2xmi.xsl @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + false + false + false + false + + + + + + + + + + + + + + + + + false + false + false + false + Class + + + + + + + + + + + + + + +