From 2c78a4c028db6c3894971ce6c9f21315b3059dfd Mon Sep 17 00:00:00 2001 From: Dominique Marcadet <Dominique.Marcadet@centralesupelec.fr> Date: Tue, 25 Jun 2019 10:53:54 +0200 Subject: [PATCH] Validator HMI put back to a running state --- .../.classpath | 7 + .../.project | 28 ++ .../.settings/org.eclipse.jdt.core.prefs | 11 + .../META-INF/MANIFEST.MF | 11 + .../build.properties | 19 + .../pom.xml | 73 +++ .../RiseClipseValidatorSCLApplication.java | 93 ++++ .../validator/ui/component/OCLFilePane.java | 118 +++++ .../validator/ui/component/ResultFrame.java | 54 ++ .../validator/ui/component/ResultPane.java | 256 ++++++++++ .../validator/ui/component/SCLFilePane.java | 109 ++++ .../ui/component/SclFileCheckBox.java | 43 ++ .../validator/ui/component/SclFileList.java | 109 ++++ .../ui/component/TextLineNumber.java | 467 ++++++++++++++++++ pom.xml | 1 + 15 files changed, 1399 insertions(+) create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/.classpath create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/.project create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/.settings/org.eclipse.jdt.core.prefs create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/META-INF/MANIFEST.MF create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/build.properties create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/pom.xml create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/application/RiseClipseValidatorSCLApplication.java create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/OCLFilePane.java create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/ResultFrame.java create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/ResultPane.java create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/SCLFilePane.java create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/SclFileCheckBox.java create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/SclFileList.java create mode 100644 fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/TextLineNumber.java diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/.classpath b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/.classpath new file mode 100644 index 0000000..22f3064 --- /dev/null +++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/.classpath @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/> + <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/.project b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/.project new file mode 100644 index 0000000..f41fad3 --- /dev/null +++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/.project @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.ManifestBuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.pde.SchemaBuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.eclipse.pde.PluginNature</nature> + </natures> +</projectDescription> diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/.settings/org.eclipse.jdt.core.prefs b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..3a21537 --- /dev/null +++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/META-INF/MANIFEST.MF b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/META-INF/MANIFEST.MF new file mode 100644 index 0000000..4c65ec1 --- /dev/null +++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/META-INF/MANIFEST.MF @@ -0,0 +1,11 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: RiseClipse IEC61850 SCL Tools UI +Bundle-Vendor: CentraleSupélec & EDF +Bundle-SymbolicName: fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui +Bundle-Version: 1.0.0.qualifier +Export-Package: fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui.application, + fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui.component +Require-Bundle: fr.centralesupelec.edf.riseclipse.main, + fr.centralesupelec.edf.riseclipse.iec61850.scl.validator +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/build.properties b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/build.properties new file mode 100644 index 0000000..f4cfb4e --- /dev/null +++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/build.properties @@ -0,0 +1,19 @@ +# Copyright (c) 2018 CentraleSup�lec & EDF. +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Eclipse Public License v1.0 +# which accompanies this distribution, and is available at +# http://www.eclipse.org/legal/epl-v10.html +# +# This file is part of the RiseClipse tool +# +# Contributors: +# Computer Science Department, CentraleSup�lec +# EDF R&D +# Contacts: +# dominique.marcadet@centralesupelec.fr +# aurelie.dehouck-neveu@edf.fr +# Web site: +# http://wdi.supelec.fr/software/RiseClipse/ +source.. = src/ +bin.includes = META-INF/,\ + . diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/pom.xml b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/pom.xml new file mode 100644 index 0000000..d237a67 --- /dev/null +++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/pom.xml @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <groupId>fr.centralesupelec.edf.riseclipse</groupId> + <artifactId>fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui</artifactId> + <version>1.1.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <parent> + <groupId>fr.centralesupelec.edf.riseclipse</groupId> + <artifactId>RiseClipseValidator_SCL2003</artifactId> + <version>1.1.0-SNAPSHOT</version> + </parent> + + <properties> + <toolName>RiseClipseSCLValidatorApplication</toolName> + </properties> + + <build> + <sourceDirectory>src</sourceDirectory> + <plugins> + <!-- Create a jar file with dependencies included --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>3.0.0</version> + <configuration> + <!-- set the name of the resulting runnable jar --> + <finalName>${toolName}-${project.version}</finalName> + <appendAssemblyId>false</appendAssemblyId> + </configuration> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <transformers> + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> + <mainClass>fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui.application.RiseClipseSCLValidatorApplication</mainClass> + </transformer> + <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer"> + <resource>plugin.properties</resource> + </transformer> + </transformers> + <filters> + <filter> + <artifact>*:*</artifact> + <excludes> + <exclude>META-INF/*.SF</exclude> + <exclude>META-INF/*.DSA</exclude> + <exclude>META-INF/*.RSA</exclude> + </excludes> + </filter> + </filters> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>fr.centralesupelec.edf.riseclipse</groupId> + <artifactId>fr.centralesupelec.edf.riseclipse.iec61850.scl.tools</artifactId> + <version>1.1.0-SNAPSHOT</version> + </dependency> + </dependencies> + +</project> diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/application/RiseClipseValidatorSCLApplication.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/application/RiseClipseValidatorSCLApplication.java new file mode 100644 index 0000000..b19b74f --- /dev/null +++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/application/RiseClipseValidatorSCLApplication.java @@ -0,0 +1,93 @@ +/** + * Copyright (c) 2018 CentraleSupélec & EDF. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This file is part of the RiseClipse tool + * + * Contributors: + * Computer Science Department, CentraleSupélec + * EDF R&D + * Contacts: + * dominique.marcadet@centralesupelec.fr + * aurelie.dehouck-neveu@edf.fr + * Web site: + * http://wdi.supelec.fr/software/RiseClipse/ + */ +package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui.application; + +import java.awt.EventQueue; +import java.io.File; +import java.util.ArrayList; + +import javax.swing.JFrame; +import javax.swing.JTabbedPane; + +import fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui.component.OCLFilePane; +import fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui.component.SCLFilePane; + +import javax.swing.JScrollPane; +import javax.swing.JPanel; + +public class RiseClipseValidatorSCLApplication { + + private JFrame frame; + private OCLFilePane oclTree; + + /** + * Launch the application. + */ + public static void main( String[] args ) { + EventQueue.invokeLater( new Runnable() { + public void run() { + try { + RiseClipseValidatorSCLApplication window = new RiseClipseValidatorSCLApplication(); + window.frame.setVisible( true ); + } + catch( Exception e ) { + e.printStackTrace(); + } + } + } ); + } + + /** + * Create the application. + */ + public RiseClipseValidatorSCLApplication() { + initialize(); + } + + /** + * Initialize the contents of the frame. + */ + private void initialize() { + frame = new JFrame(); + frame.setTitle( "RiseClipseValidatorSCLApplication" ); + frame.setBounds( 100, 100, 800, 600 ); + frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); + + JTabbedPane tabbedPane = new JTabbedPane( JTabbedPane.TOP ); + frame.getContentPane().add( tabbedPane ); + + JPanel sclPanel = new SCLFilePane( this ); + tabbedPane.addTab( "SCL Files", null, sclPanel, null ); + + JScrollPane oclPane = new JScrollPane(); + tabbedPane.addTab( "OCL Files", null, oclPane, null ); + + File fileRoot = new File( System.getProperty( "user.dir" ) + "/OCL" ); + oclTree = new OCLFilePane( fileRoot ); + oclPane.setViewportView( oclTree ); + + } + + public ArrayList< File > getOclFiles() { + ArrayList< File > oclFiles = new ArrayList<>(); + oclTree.getOclFiles( oclFiles ); + return oclFiles; + } + +} diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/OCLFilePane.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/OCLFilePane.java new file mode 100644 index 0000000..ca0c74d --- /dev/null +++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/OCLFilePane.java @@ -0,0 +1,118 @@ +/** + * Copyright (c) 2018 CentraleSupélec & EDF. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This file is part of the RiseClipse tool + * + * Contributors: + * Computer Science Department, CentraleSupélec + * EDF R&D + * Contacts: + * dominique.marcadet@centralesupelec.fr + * aurelie.dehouck-neveu@edf.fr + * Web site: + * http://wdi.supelec.fr/software/RiseClipse/ + */ +package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui.component; + +import java.awt.Component; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.util.ArrayList; + +import javax.swing.JCheckBox; +import javax.swing.JTree; +import javax.swing.tree.DefaultMutableTreeNode; +import javax.swing.tree.DefaultTreeModel; +import javax.swing.tree.TreeCellRenderer; +import javax.swing.tree.TreePath; + +@SuppressWarnings( "serial" ) +public class OCLFilePane extends JTree { + + private DefaultMutableTreeNode root; + + public OCLFilePane( File fileRoot ) { + root = new DefaultMutableTreeNode( new SclFileCheckBox( fileRoot ) ); + setModel( new DefaultTreeModel( root ) ); + setShowsRootHandles( true ); + + createChildren( fileRoot, root ); + + setCellRenderer( new SclFileCellRenderer() ); + + addMouseListener( new MouseAdapter() { + public void mousePressed( MouseEvent e ) { + int selRow = getRowForLocation( e.getX(), e.getY() ); + + if( selRow != -1 ) { + TreePath selPath = getPathForLocation( e.getX(), e.getY() ); + DefaultMutableTreeNode node = (( DefaultMutableTreeNode ) selPath.getLastPathComponent() ); + SclFileCheckBox checkbox = ( SclFileCheckBox ) node.getUserObject(); + propagateInTree( node, ! checkbox.getCheckBox().isSelected() ); + repaint(); + } + } + + private void propagateInTree( DefaultMutableTreeNode node, boolean selected ) { + SclFileCheckBox checkbox = ( SclFileCheckBox ) node.getUserObject(); + checkbox.getCheckBox().setSelected( selected ); + for( int i = 0; i < node.getChildCount(); ++i ) { + propagateInTree( ( DefaultMutableTreeNode ) node.getChildAt( i ), selected ); + } + } + } ); + + } + + private void createChildren( File fileRoot, DefaultMutableTreeNode node ) { + File[] files = fileRoot.listFiles(); + if( files == null ) return; + + for( File file : files ) { + DefaultMutableTreeNode childNode = new DefaultMutableTreeNode( new SclFileCheckBox( file ) ); + node.add( childNode ); + if( file.isDirectory() ) { + createChildren( file, childNode ); + } + } + } + + protected class SclFileCellRenderer implements TreeCellRenderer { + + @Override + public Component getTreeCellRendererComponent( JTree tree, Object value, boolean selected, boolean expanded, + boolean leaf, int row, boolean hasFocus ) { + DefaultMutableTreeNode node = ( DefaultMutableTreeNode ) value; + SclFileCheckBox file = ( SclFileCheckBox ) node.getUserObject(); + JCheckBox checkbox = file.getCheckBox(); + checkbox.setEnabled( isEnabled() ); + checkbox.setFont( getFont() ); + checkbox.setFocusPainted( false ); + checkbox.setBorderPainted( true ); + return checkbox; + } + } + + public void getOclFiles( ArrayList< File > oclFiles ) { + getOclFiles( root, oclFiles ); + } + + private void getOclFiles( DefaultMutableTreeNode node, ArrayList< File > oclFiles ) { + SclFileCheckBox checkbox = ( SclFileCheckBox ) node.getUserObject(); + if( checkbox.getFile().isFile() ) { + if( checkbox.getCheckBox().isSelected() ) { + oclFiles.add( checkbox.getFile() ); + } + } + else { + for( int i = 0; i < node.getChildCount(); ++i ) { + getOclFiles( ( DefaultMutableTreeNode ) node.getChildAt( i ), oclFiles ); + } + } + } +} diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/ResultFrame.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/ResultFrame.java new file mode 100644 index 0000000..e17f920 --- /dev/null +++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/ResultFrame.java @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2018 CentraleSupélec & EDF. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This file is part of the RiseClipse tool + * + * Contributors: + * Computer Science Department, CentraleSupélec + * EDF R&D + * Contacts: + * dominique.marcadet@centralesupelec.fr + * aurelie.dehouck-neveu@edf.fr + * Web site: + * http://wdi.supelec.fr/software/RiseClipse/ + */ +package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui.component; + +import javax.swing.JFrame; +import javax.swing.JTabbedPane; + +import fr.centralesupelec.edf.riseclipse.util.IRiseClipseConsole; + +@SuppressWarnings( "serial" ) +public class ResultFrame extends JFrame { + + private JTabbedPane tabbedPane; + + public ResultFrame() { + setTitle( "RiseClipseValidatorSCL results" ); + setBounds( 200, 200, 800, 600 ); + + tabbedPane = new JTabbedPane( JTabbedPane.TOP ); + getContentPane().add( tabbedPane ); + + setVisible( true ); + } + + public IRiseClipseConsole getMainConsole() { + ResultPane console = new ResultPane( null, false ); + tabbedPane.addTab( "RiseClipseValidatorSCL", null, console, null ); + return console; + } + + public IRiseClipseConsole getConsoleFor( String filename ) { + ResultPane result = new ResultPane( filename, true ); + String name = filename.substring( filename.lastIndexOf( '/' ) + 1 ); + tabbedPane.addTab( name, null, result, null ); + return result; + } + +} diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/ResultPane.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/ResultPane.java new file mode 100644 index 0000000..86eb6ef --- /dev/null +++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/ResultPane.java @@ -0,0 +1,256 @@ +/** + * Copyright (c) 2018 CentraleSupélec & EDF. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This file is part of the RiseClipse tool + * + * Contributors: + * Computer Science Department, CentraleSupélec + * EDF R&D + * Contacts: + * dominique.marcadet@centralesupelec.fr + * aurelie.dehouck-neveu@edf.fr + * Web site: + * http://wdi.supelec.fr/software/RiseClipse/ + */ +package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui.component; + +import java.awt.BorderLayout; +import java.awt.Graphics; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; + +import fr.centralesupelec.edf.riseclipse.util.AbstractRiseClipseConsole; +import fr.centralesupelec.edf.riseclipse.util.IRiseClipseConsole; + +@SuppressWarnings( "serial" ) +public class ResultPane extends JPanel implements IRiseClipseConsole, ActionListener { + + private final static String newline = "\n"; + + private ArrayList< Integer > levels; + private ArrayList< String > messages; + private JCheckBox cbVerbose; + private JCheckBox cbInfo; + private JCheckBox cbWarning; + private JCheckBox cbError; + private JTextArea text; + + private JButton btnOpenFile; + + private JButton btnSaveResults; + + private String filename; + + public ResultPane( String filename, boolean withButtons ) { + this.filename = filename; + + levels = new ArrayList<>(); + messages = new ArrayList<>(); + + setLayout( new BorderLayout( 0, 0 )); + + JPanel cbPanel = new JPanel(); + add( cbPanel, BorderLayout.NORTH ); + + cbVerbose = new JCheckBox( "Verbose" ); + cbVerbose.setSelected( false ); + cbVerbose.addActionListener( this ); + cbPanel.add( cbVerbose ); + + cbInfo = new JCheckBox( "Info" ); + cbInfo.setSelected( true ); + cbInfo.addActionListener( this ); + cbPanel.add( cbInfo ); + + cbWarning = new JCheckBox( "Warning" ); + cbWarning.setSelected( true ); + cbWarning.addActionListener( this ); + cbPanel.add( cbWarning ); + + cbError = new JCheckBox( "Error" ); + cbError.setSelected( true ); + cbError.addActionListener( this ); + cbPanel.add( cbError ); + + text = new JTextArea( 0, 0 ); + text.setEditable( false ); + + JScrollPane scrollPane = new JScrollPane( text ); + add( scrollPane, BorderLayout.CENTER ); + + JPanel btnPanel = new JPanel(); + add( btnPanel, BorderLayout.SOUTH ); + + if( withButtons ) { + btnOpenFile = new JButton( "Open file" ); + btnOpenFile.addActionListener( this ); + btnPanel.add( btnOpenFile ); + + btnSaveResults = new JButton( "Save results" ); + btnSaveResults.addActionListener( this ); + btnPanel.add( btnSaveResults ); + } + } + + @Override + public void paint( Graphics g ) { + text.setText( allMesages() ); + + super.paint( g ); + } + + private String allMesages() { + StringBuffer buf = new StringBuffer(); + + for( int i = 0; i < messages.size(); ++i ) { + boolean display = cbVerbose.isSelected(); + String level = "VERBOSE"; + switch( levels.get( i )) { + case IRiseClipseConsole.VERBOSE_LEVEL: + break; + case IRiseClipseConsole.INFO_LEVEL: + display = cbInfo.isSelected(); + level = "INFO"; + break; + case IRiseClipseConsole.WARNING_LEVEL: + display = cbWarning.isSelected(); + level = "WARNING"; + break; + case IRiseClipseConsole.ERROR_LEVEL: + display = cbError.isSelected(); + level = "ERROR"; + break; + } + if( display ) { + String m = messages.get( i ); + if(( m.length() > 0 ) && ( m.charAt( 0 ) == '\t' )) { + buf.append( level + ":" + m + newline ); + } + else { + buf.append( level + ":\t" + m + newline ); + } + } + } + + return buf.toString(); + } + + @Override + public int setLevel( int level ) { + // We keep all messages + return IRiseClipseConsole.VERBOSE_LEVEL; + } + + @Override + public void verbose( Object o ) { + levels.add( IRiseClipseConsole.VERBOSE_LEVEL ); + messages.add( o.toString() ); + } + + @Override + public void info( Object o ) { + levels.add( IRiseClipseConsole.INFO_LEVEL ); + messages.add( o.toString() ); + } + + @Override + public void warning( Object o ) { + levels.add( IRiseClipseConsole.WARNING_LEVEL ); + messages.add( o.toString() ); + } + + @Override + public void error( Object o ) { + levels.add( IRiseClipseConsole.ERROR_LEVEL ); + messages.add( o.toString() ); + } + + @Override + public void fatal( Object o ) { + levels.add( IRiseClipseConsole.FATAL_LEVEL ); + messages.add( o.toString() ); + } + + @Override + public void actionPerformed( ActionEvent e ) { + Object source = e.getSource(); + + if(( source == cbVerbose ) || ( source == cbInfo ) || ( source == cbWarning ) || ( source == cbError )) { + // The state of the checkbox is directly tested, so just repaint + repaint(); + return; + } + + if( source == btnOpenFile ) { + + JFrame frame = new JFrame( filename ); + frame.setBounds( 300, 300, 900, 700 ); + + JTextArea textArea = new JTextArea(); + try { + BufferedReader br = new BufferedReader( new FileReader( filename )); + textArea.read( br, null ); + br.close(); + } + catch( IOException ex ) { + AbstractRiseClipseConsole.getConsole().error( ex.getMessage() ); + return; + } + + JScrollPane scrollPane = new JScrollPane( textArea ); + TextLineNumber tln = new TextLineNumber( textArea ); + scrollPane.setRowHeaderView( tln ); + frame.getContentPane().add( scrollPane ); + + frame.setVisible( true ); + + return; + } + + if( source == btnSaveResults ) { + JFileChooser fileChooser = new JFileChooser(); + if( fileChooser.showSaveDialog( this ) == JFileChooser.APPROVE_OPTION ) { + File file = fileChooser.getSelectedFile(); + try { + BufferedWriter writer = new BufferedWriter( new FileWriter( file )); + writer.write( allMesages() ); + writer.close(); + } + catch( IOException ex ) { + AbstractRiseClipseConsole.getConsole().error( ex.getMessage() ); + } + } + + } + } + + @Override + public void displayIdenticalMessages() { + // Not taken into account for the moment + } + + @Override + public void doNotDisplayIdenticalMessages() { + // Not taken into account for the moment + } + +} diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/SCLFilePane.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/SCLFilePane.java new file mode 100644 index 0000000..6c03ee3 --- /dev/null +++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/SCLFilePane.java @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2018 CentraleSupélec & EDF. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This file is part of the RiseClipse tool + * + * Contributors: + * Computer Science Department, CentraleSupélec + * EDF R&D + * Contacts: + * dominique.marcadet@centralesupelec.fr + * aurelie.dehouck-neveu@edf.fr + * Web site: + * http://wdi.supelec.fr/software/RiseClipse/ + */ +package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui.component; + +import java.awt.BorderLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import javax.swing.JButton; +import javax.swing.JFileChooser; +import javax.swing.JPanel; +import javax.swing.JScrollPane; + +import fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.RiseClipseValidatorSCL; +import fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui.application.RiseClipseValidatorSCLApplication; +import fr.centralesupelec.edf.riseclipse.util.AbstractRiseClipseConsole; +import fr.centralesupelec.edf.riseclipse.util.IRiseClipseConsole; + +@SuppressWarnings( "serial" ) +public class SCLFilePane extends JPanel implements ActionListener { + + private JButton btnAddSclFile; + private JButton btnValidate; + private SclFileList sclFilesList; + private RiseClipseValidatorSCLApplication application; + + public SCLFilePane( RiseClipseValidatorSCLApplication application ) { + this.application = application; + + setLayout( new BorderLayout( 0, 0 )); + + JPanel btnPanel = new JPanel(); + add( btnPanel, BorderLayout.SOUTH ); + + btnAddSclFile = new JButton( "Add SCL file" ); + btnAddSclFile.addActionListener( this ); + btnPanel.add( btnAddSclFile ); + + btnValidate = new JButton( "Validate" ); + btnValidate.addActionListener( this ); + btnPanel.add( btnValidate ); + + JScrollPane sclFilesPane = new JScrollPane(); + add( sclFilesPane, BorderLayout.CENTER ); + + sclFilesList = new SclFileList(); + sclFilesPane.setViewportView( sclFilesList ); + } + + @Override + public void actionPerformed( ActionEvent e ) { + Object source = e.getSource(); + + if( source == btnAddSclFile ) { + JFileChooser fileChooser = new JFileChooser(); + if( fileChooser.showOpenDialog( this ) == JFileChooser.APPROVE_OPTION ) { + sclFilesList.add( fileChooser.getSelectedFile() ); + } + return; + } + + if( source == btnValidate ) { + ArrayList< File > oclFiles = application.getOclFiles(); + List< String > oclFileNames = + oclFiles + .stream() + .map( f -> f.getAbsolutePath() ) + .collect( Collectors.toList() ); + + ArrayList< String > sclFiles = sclFilesList.getSclFiles(); + + ResultFrame result = new ResultFrame(); + + IRiseClipseConsole console = result.getMainConsole(); + AbstractRiseClipseConsole.changeConsole( console ); + RiseClipseValidatorSCL.displayLegal( ); + RiseClipseValidatorSCL.prepare( oclFileNames, null, false ); + result.repaint(); + for( int i = 0; i < sclFiles.size(); ++i ) { + console = result.getConsoleFor( sclFiles.get( i )); + AbstractRiseClipseConsole.changeConsole( console ); + RiseClipseValidatorSCL.run( true, sclFiles.get( i )); + result.repaint(); + } + + return; + } + } +} diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/SclFileCheckBox.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/SclFileCheckBox.java new file mode 100644 index 0000000..dc3884d --- /dev/null +++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/SclFileCheckBox.java @@ -0,0 +1,43 @@ +/** + * Copyright (c) 2018 CentraleSupélec & EDF. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This file is part of the RiseClipse tool + * + * Contributors: + * Computer Science Department, CentraleSupélec + * EDF R&D + * Contacts: + * dominique.marcadet@centralesupelec.fr + * aurelie.dehouck-neveu@edf.fr + * Web site: + * http://wdi.supelec.fr/software/RiseClipse/ + */ +package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui.component; + +import java.io.File; + +import javax.swing.JCheckBox; + +public class SclFileCheckBox { + + private File file; + private JCheckBox checkBox; + + public SclFileCheckBox( File file ) { + this.file = file; + this.checkBox = new JCheckBox( file.getName() ); + this.checkBox.setSelected( true ); + } + + public File getFile() { + return file; + } + + public JCheckBox getCheckBox() { + return checkBox; + } +} diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/SclFileList.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/SclFileList.java new file mode 100644 index 0000000..f19f414 --- /dev/null +++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/SclFileList.java @@ -0,0 +1,109 @@ +/** + * Copyright (c) 2018 CentraleSupélec & EDF. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * This file is part of the RiseClipse tool + * + * Contributors: + * Computer Science Department, CentraleSupélec + * EDF R&D + * Contacts: + * dominique.marcadet@centralesupelec.fr + * aurelie.dehouck-neveu@edf.fr + * Web site: + * http://wdi.supelec.fr/software/RiseClipse/ + */ +package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui.component; + +import java.awt.Component; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.io.File; +import java.util.ArrayList; + +import javax.swing.DefaultListModel; +import javax.swing.JCheckBox; +import javax.swing.JList; +import javax.swing.ListCellRenderer; +import javax.swing.ListSelectionModel; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; + +/* + * Adapted from http://www.devx.com/tips/Tip/5342 + */ +@SuppressWarnings( "serial" ) +public class SclFileList extends JList< SclFileCheckBox > { + + protected static Border noFocusBorder = new EmptyBorder( 1, 1, 1, 1 ); + + private DefaultListModel< SclFileCheckBox > model; + + public SclFileList() { + model = new DefaultListModel< SclFileCheckBox >(); + setModel( model ); + + setCellRenderer( new SclFileCellRenderer() ); + + addMouseListener( new MouseAdapter() { + public void mousePressed( MouseEvent e ) { + int index = locationToIndex( e.getPoint() ); + + if( index != -1 ) { + JCheckBox checkbox = getModel().getElementAt( index ).getCheckBox(); + checkbox.setSelected( !checkbox.isSelected() ); + repaint(); + } + } + } ); + + setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); + } + + public void add( File file ) { + for( int i = 0; i < model.size(); ++i ) { + if( model.getElementAt( i ).getFile().getAbsolutePath().equals( file.getAbsolutePath() )) { + return; + } + } + + SclFileCheckBox check = new SclFileCheckBox( file ); + model.addElement( check ); + } + + protected class SclFileCellRenderer implements ListCellRenderer< SclFileCheckBox > { + + @Override + public Component getListCellRendererComponent( JList< ? extends SclFileCheckBox > list, SclFileCheckBox file, int index, + boolean isSelected, boolean cellHasFocus ) { + JCheckBox checkbox = file.getCheckBox(); + checkbox.setBackground( isSelected ? getSelectionBackground() : getBackground() ); + checkbox.setForeground( isSelected ? getSelectionForeground() : getForeground() ); + checkbox.setEnabled( isEnabled() ); + checkbox.setFont( getFont() ); + checkbox.setFocusPainted( false ); + checkbox.setBorderPainted( true ); + checkbox.setBorder( isSelected ? UIManager.getBorder( "List.focusCellHighlightBorder" ) : noFocusBorder ); + return checkbox; + } + } + + public ArrayList< String > getSclFiles() { + ArrayList< String > sclFiles = new ArrayList< String >(); + + for( int i = 0; i < model.size(); ++i ) { + if( model.getElementAt( i ).getCheckBox().isSelected() ) { + sclFiles.add( model.getElementAt( i ).getFile().getAbsolutePath() ); + } + } + + return sclFiles; + } + +} + + diff --git a/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/TextLineNumber.java b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/TextLineNumber.java new file mode 100644 index 0000000..54c75ff --- /dev/null +++ b/fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui/src/fr/centralesupelec/edf/riseclipse/iec61850/scl/validator/ui/component/TextLineNumber.java @@ -0,0 +1,467 @@ +/* + * Code from: https://tips4java.wordpress.com/2009/05/23/text-component-line-number/ + * Posted by Rob Camick on May 23, 2009 + * + * There is no explicit license, but the web site says: + * We assume no responsibility for the code. You are free to use and/or modify and/or distribute any or + * all code posted on the Java Tips Weblog without restriction. A credit in the code comments would be nice, + * but not in any way mandatory.We assume no responsibility for the code. You are free to use and/or modify + * and/or distribute any or all code posted on the Java Tips Weblog without restriction. A credit in the code + * comments would be nice, but not in any way mandatory. + */ + +/* + * + * This file is part of the RiseClipse tool + * + * Web site: + * http://wdi.supelec.fr/software/RiseClipse/ + */ +package fr.centralesupelec.edf.riseclipse.iec61850.scl.validator.ui.component; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics; +import java.awt.Insets; +import java.awt.Point; +import java.awt.Rectangle; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.HashMap; + +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.border.MatteBorder; +import javax.swing.event.CaretEvent; +import javax.swing.event.CaretListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.Element; +import javax.swing.text.JTextComponent; +import javax.swing.text.StyleConstants; +import javax.swing.text.Utilities; + + +/** + * This class will display line numbers for a related text component. The text + * component must use the same line height for each line. TextLineNumber + * supports wrapped lines and will highlight the line number of the current + * line in the text component. + * + * This class was designed to be used as a component added to the row header + * of a JScrollPane. + */ +@SuppressWarnings( "serial" ) +public class TextLineNumber extends JPanel implements CaretListener, DocumentListener, PropertyChangeListener { + public final static float LEFT = 0.0f; + public final static float CENTER = 0.5f; + public final static float RIGHT = 1.0f; + + private final static Border OUTER = new MatteBorder( 0, 0, 0, 2, Color.GRAY ); + + private final static int HEIGHT = Integer.MAX_VALUE - 1000000; + + // Text component this TextTextLineNumber component is in sync with + + private JTextComponent component; + + // Properties that can be changed + + private boolean updateFont; + private int borderGap; + private Color currentLineForeground; + private float digitAlignment; + private int minimumDisplayDigits; + + // Keep history information to reduce the number of times the component + // needs to be repainted + + private int lastDigits; + private int lastHeight; + private int lastLine; + + private HashMap< String, FontMetrics > fonts; + + /** + * Create a line number component for a text component. This minimum + * display width will be based on 3 digits. + * + * @param component the related text component + */ + public TextLineNumber( JTextComponent component ) { + this( component, 3 ); + } + + /** + * Create a line number component for a text component. + * + * @param component the related text component + * @param minimumDisplayDigits the number of digits used to calculate + * the minimum width of the component + */ + public TextLineNumber( JTextComponent component, int minimumDisplayDigits ) { + this.component = component; + + setFont( component.getFont() ); + + setBorderGap( 5 ); + setCurrentLineForeground( Color.RED ); + setDigitAlignment( RIGHT ); + setMinimumDisplayDigits( minimumDisplayDigits ); + + component.getDocument().addDocumentListener( this ); + component.addCaretListener( this ); + component.addPropertyChangeListener( "font", this ); + } + + /** + * Gets the update font property + * + * @return the update font property + */ + public boolean getUpdateFont() { + return updateFont; + } + + /** + * Set the update font property. Indicates whether this Font should be + * updated automatically when the Font of the related text component + * is changed. + * + * @param updateFont when true update the Font and repaint the line + * numbers, otherwise just repaint the line numbers. + */ + public void setUpdateFont( boolean updateFont ) { + this.updateFont = updateFont; + } + + /** + * Gets the border gap + * + * @return the border gap in pixels + */ + public int getBorderGap() { + return borderGap; + } + + /** + * The border gap is used in calculating the left and right insets of the + * border. Default value is 5. + * + * @param borderGap the gap in pixels + */ + public void setBorderGap( int borderGap ) { + this.borderGap = borderGap; + Border inner = new EmptyBorder( 0, borderGap, 0, borderGap ); + setBorder( new CompoundBorder( OUTER, inner ) ); + lastDigits = 0; + setPreferredWidth(); + } + + /** + * Gets the current line rendering Color + * + * @return the Color used to render the current line number + */ + public Color getCurrentLineForeground() { + return currentLineForeground == null ? getForeground() : currentLineForeground; + } + + /** + * The Color used to render the current line digits. Default is Coolor.RED. + * + * @param currentLineForeground the Color used to render the current line + */ + public void setCurrentLineForeground( Color currentLineForeground ) { + this.currentLineForeground = currentLineForeground; + } + + /** + * Gets the digit alignment + * + * @return the alignment of the painted digits + */ + public float getDigitAlignment() { + return digitAlignment; + } + + /** + * Specify the horizontal alignment of the digits within the component. + * Common values would be: + * <ul> + * <li>TextLineNumber.LEFT + * <li>TextLineNumber.CENTER + * <li>TextLineNumber.RIGHT (default) + * </ul> + * @param currentLineForeground the Color used to render the current line + */ + public void setDigitAlignment( float digitAlignment ) { + this.digitAlignment = digitAlignment > 1.0f ? 1.0f : digitAlignment < 0.0f ? -1.0f : digitAlignment; + } + + /** + * Gets the minimum display digits + * + * @return the minimum display digits + */ + public int getMinimumDisplayDigits() { + return minimumDisplayDigits; + } + + /** + * Specify the mimimum number of digits used to calculate the preferred + * width of the component. Default is 3. + * + * @param minimumDisplayDigits the number digits used in the preferred + * width calculation + */ + public void setMinimumDisplayDigits( int minimumDisplayDigits ) { + this.minimumDisplayDigits = minimumDisplayDigits; + setPreferredWidth(); + } + + /** + * Calculate the width needed to display the maximum line number + */ + private void setPreferredWidth() { + Element root = component.getDocument().getDefaultRootElement(); + int lines = root.getElementCount(); + int digits = Math.max( String.valueOf( lines ).length(), minimumDisplayDigits ); + + // Update sizes when number of digits in the line number changes + + if( lastDigits != digits ) { + lastDigits = digits; + FontMetrics fontMetrics = getFontMetrics( getFont() ); + int width = fontMetrics.charWidth( '0' ) * digits; + Insets insets = getInsets(); + int preferredWidth = insets.left + insets.right + width; + + Dimension d = getPreferredSize(); + d.setSize( preferredWidth, HEIGHT ); + setPreferredSize( d ); + setSize( d ); + } + } + + /** + * Draw the line numbers + */ + @Override + public void paintComponent( Graphics g ) { + super.paintComponent( g ); + + // Determine the width of the space available to draw the line number + + FontMetrics fontMetrics = component.getFontMetrics( component.getFont() ); + Insets insets = getInsets(); + int availableWidth = getSize().width - insets.left - insets.right; + + // Determine the rows to draw within the clipped bounds. + + Rectangle clip = g.getClipBounds(); + int rowStartOffset = component.viewToModel( new Point( 0, clip.y ) ); + int endOffset = component.viewToModel( new Point( 0, clip.y + clip.height ) ); + + while( rowStartOffset <= endOffset ) { + try { + if( isCurrentLine( rowStartOffset ) ) + g.setColor( getCurrentLineForeground() ); + else + g.setColor( getForeground() ); + + // Get the line number as a string and then determine the + // "X" and "Y" offsets for drawing the string. + + String lineNumber = getTextLineNumber( rowStartOffset ); + int stringWidth = fontMetrics.stringWidth( lineNumber ); + int x = getOffsetX( availableWidth, stringWidth ) + insets.left; + int y = getOffsetY( rowStartOffset, fontMetrics ); + g.drawString( lineNumber, x, y ); + + // Move to the next row + + rowStartOffset = Utilities.getRowEnd( component, rowStartOffset ) + 1; + } + catch( Exception e ) { + break; + } + } + } + + /* + * We need to know if the caret is currently positioned on the line we + * are about to paint so the line number can be highlighted. + */ + private boolean isCurrentLine( int rowStartOffset ) { + int caretPosition = component.getCaretPosition(); + Element root = component.getDocument().getDefaultRootElement(); + + if( root.getElementIndex( rowStartOffset ) == root.getElementIndex( caretPosition ) ) + return true; + else + return false; + } + + /* + * Get the line number to be drawn. The empty string will be returned + * when a line of text has wrapped. + */ + protected String getTextLineNumber( int rowStartOffset ) { + Element root = component.getDocument().getDefaultRootElement(); + int index = root.getElementIndex( rowStartOffset ); + Element line = root.getElement( index ); + + if( line.getStartOffset() == rowStartOffset ) + return String.valueOf( index + 1 ); + else + return ""; + } + + /* + * Determine the X offset to properly align the line number when drawn + */ + private int getOffsetX( int availableWidth, int stringWidth ) { + return ( int ) ( ( availableWidth - stringWidth ) * digitAlignment ); + } + + /* + * Determine the Y offset for the current row + */ + private int getOffsetY( int rowStartOffset, FontMetrics fontMetrics ) throws BadLocationException { + // Get the bounding rectangle of the row + + Rectangle r = component.modelToView( rowStartOffset ); + int lineHeight = fontMetrics.getHeight(); + int y = r.y + r.height; + int descent = 0; + + // The text needs to be positioned above the bottom of the bounding + // rectangle based on the descent of the font(s) contained on the row. + + if( r.height == lineHeight ) // default font is being used + { + descent = fontMetrics.getDescent(); + } + else // We need to check all the attributes for font changes + { + if( fonts == null ) fonts = new HashMap< String, FontMetrics >(); + + Element root = component.getDocument().getDefaultRootElement(); + int index = root.getElementIndex( rowStartOffset ); + Element line = root.getElement( index ); + + for( int i = 0; i < line.getElementCount(); i++ ) { + Element child = line.getElement( i ); + AttributeSet as = child.getAttributes(); + String fontFamily = ( String ) as.getAttribute( StyleConstants.FontFamily ); + Integer fontSize = ( Integer ) as.getAttribute( StyleConstants.FontSize ); + String key = fontFamily + fontSize; + + FontMetrics fm = fonts.get( key ); + + if( fm == null ) { + Font font = new Font( fontFamily, Font.PLAIN, fontSize ); + fm = component.getFontMetrics( font ); + fonts.put( key, fm ); + } + + descent = Math.max( descent, fm.getDescent() ); + } + } + + return y - descent; + } + + // + // Implement CaretListener interface + // + @Override + public void caretUpdate( CaretEvent e ) { + // Get the line the caret is positioned on + + int caretPosition = component.getCaretPosition(); + Element root = component.getDocument().getDefaultRootElement(); + int currentLine = root.getElementIndex( caretPosition ); + + // Need to repaint so the correct line number can be highlighted + + if( lastLine != currentLine ) { + repaint(); + lastLine = currentLine; + } + } + + // + // Implement DocumentListener interface + // + @Override + public void changedUpdate( DocumentEvent e ) { + documentChanged(); + } + + @Override + public void insertUpdate( DocumentEvent e ) { + documentChanged(); + } + + @Override + public void removeUpdate( DocumentEvent e ) { + documentChanged(); + } + + /* + * A document change may affect the number of displayed lines of text. + * Therefore the lines numbers will also change. + */ + private void documentChanged() { + // View of the component has not been updated at the time + // the DocumentEvent is fired + + SwingUtilities.invokeLater( new Runnable() { + @Override + public void run() { + try { + int endPos = component.getDocument().getLength(); + Rectangle rect = component.modelToView( endPos ); + + if( rect != null && rect.y != lastHeight ) { + setPreferredWidth(); + repaint(); + lastHeight = rect.y; + } + } + catch( BadLocationException ex ) { + /* nothing to do */ } + } + } ); + } + + // + // Implement PropertyChangeListener interface + // + @Override + public void propertyChange( PropertyChangeEvent evt ) { + if( evt.getNewValue() instanceof Font ) { + if( updateFont ) { + Font newFont = ( Font ) evt.getNewValue(); + setFont( newFont ); + lastDigits = 0; + setPreferredWidth(); + } + else { + repaint(); + } + } + } + +} + + diff --git a/pom.xml b/pom.xml index 1b9e8d3..4bf8e2d 100644 --- a/pom.xml +++ b/pom.xml @@ -147,6 +147,7 @@ <modules> <module>fr.centralesupelec.edf.riseclipse.iec61850.scl.tools</module> + <module>fr.centralesupelec.edf.riseclipse.iec61850.scl.tools.ui</module> </modules> </project> -- GitLab