| <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> |
| <html> |
| |
| <head> |
| <meta content="text/html; charset=utf-8" http-equiv="Content-Type"> |
| <title>Using styles</title> |
| <link href="../book.css" rel="Stylesheet" type="text/css"> |
| <link href="../code.css" rel="Stylesheet" type="text/css"> |
| </head> |
| |
| <body> |
| |
| <h1>Using Styles</h1> |
| <p>Sometimes graphical tools give the user the possibility to freely change the |
| graphical attributes to paint each shape (color, line-width, …). But more often |
| all shapes of the same kind shall be painted in the same way.</p> |
| <p>For example in a Ecore editor it makes sense, that all EClasses look identical. |
| The user might still have the possibility to change the color of a EClass, but then |
| the color of all other EClasses should be changed, too.</p> |
| <p>The graphics framework supports this by providing "styles". A style is a container |
| for graphical attributes (color, line-width, …), which can be associated by a graphics |
| algorithm. A style can have a parent-style, from which a style can "inherit" graphical |
| attributes (similar to cascading style sheets).</p> |
| <p>Concretely the value of a graphical attribute is determined by finding the first |
| value which is not null in the following order:</p> |
| <ol> |
| <li>The graphics algorithm itself</li> |
| <li>The style assigned to the graphics algorithm, if style exists</li> |
| <li>The parent-style(s) of the style, if parent-style(s) exists</li> |
| </ol> |
| <p>It has several advantages if the same style is associated by many graphics algorithms:</p> |
| <ul> |
| <li>When a graphical attribute value of the style is changed, all graphics algorithms |
| are repainted using this new style</li> |
| <li>A tool could provide a predefined set of styles from which the user can |
| choose (similar to the "themes" available in many programs)</li> |
| <li>Redundant storage of identical information is avoided</li> |
| </ul> |
| <h2>Creating a Style Utility Class</h2> |
| <p>In this example we want to associate the same style to the graphics algorithms |
| of all EClasses. The graphical attributes (color, line-width, …) are then set on |
| the style and no longer on the different graphics algorithm.</p> |
| <p>Additionally we will provide functionality to change the color-value of that |
| style, and as a result all EClasses will be painted in this color.</p> |
| <p>We start by implementing a utility class for the handling of styles.</p> |
| <p>We store the styles in the diagram with a given ID. The utility class has methods |
| to return a specific style by either finding it in the diagram or by creating a |
| new style and assigning it to the diagram.</p> |
| <p>Note, that we have two styles: one main style for all graphics algorithms of |
| the EClass, and one child style for the Text of the EClass. The reason is, that |
| we want to have a different color for the text and for the lines, but in both cases |
| the color is stored a "foreground" color.</p> |
| <p>You can see the complete implementation of the style utility class here:</p> |
| <!-- Begin code ------------------------------------------------------------------------------- --> |
| <p> </p> |
| <div class="literallayout"> |
| <div class="incode"> |
| <p class="code"><span class="keyword">package</span> org.eclipse.graphiti.examples.tutorial;<br> <br> |
| <span class="keyword">public class</span> StyleUtil {<br> <br> |
| <span class="keyword">private static final</span> IColorConstant |
| <span class="string"><em>E_CLASS_TEXT_FOREGROUND</em></span> =<br> |
| <span class="keyword"> new</span> ColorConstant(51, 51, 153);<br> <br> |
| <span class="keyword"> private static final</span> IColorConstant<span class="string"><em> |
| E_CLASS_FOREGROUND</em></span> =<br> |
| <span class="keyword">new</span> ColorConstant(255, 102, 0);<br> <br> |
| <span class="keyword"> private static final</span> IColorConstant |
| <em class="string">E_CLASS_BACKGROUND</em> =<br> |
| <span class="keyword">new</span> ColorConstant(255, 204, 153);<br> <br> |
| <span class="keyword">private static</span> String <span class="string"> |
| <em>DEFAULT_FONT</em></span> = <span class="string">"Arial"</span>;<br> <br> |
| <span class="keyword"> public static</span> Style getStyleForEClass(Diagram |
| diagram) {<br> |
| <span class="keyword">final</span> String styleId = <span class="string"> |
| "E-CLASS"</span>;<br> <br> |
| Style style = <em>findStyle</em>(diagram, styleId);<br> <br> |
| IGaService gaService = Graphiti.getGaService();<br> |
| <span class="keyword">if</span> (style == <span class="keyword">null</span>) |
| { <span class="comment">// style not found - create new style</span><br> |
| style = gaService.createStyle(diagram, styleId);<br> |
| style.setForeground(gaService.manageColor(diagram,<br> |
| <span class="string"><em>E_CLASS_FOREGROUND</em></span>));<br> |
| style.setBackground(gaService.manageColor(diagram,<br> |
| <span class="string"><em>E_CLASS_BACKGROUND</em></span>));<br> |
| style.setLineWidth(2);<br> |
| }<br> <span class="keyword">return</span> |
| style;<br> }<br> <br> |
| <span class="keyword"> public static</span> Style getStyleForEClassText(Diagram |
| diagram) {<br> |
| <span class="keyword">final</span> String styleId = <span class="string"> |
| "E-CLASS-TEXT"</span>;<br> <br> |
| <span class="comment">// this is a child style of the e-class-style</span><br> |
| Style parentStyle = getStyleForEClass(diagram);<br> |
| Style style = <em>findStyle</em>(parentStyle, styleId);<br> <br> |
| <span class="keyword">if</span> (style == <span class="keyword">null</span>) |
| { <span class="comment">// style not found - create new style</span><br> |
| IGaService gaService = Graphiti.getGaService();<br> |
| style = gaService.createStyle(getStyleForEClass(diagram), styleId);<br> |
| <span class="comment">// "overwrites" values from parent style</span><br> |
| style.setForeground(gaService.manageColor(diagram,<br> |
| <span class="string"><em> E_CLASS_TEXT_FOREGROUND</em></span>));<br> |
| style.setFont(gaService.manageFont(diagram, <span class="string"><em>DEFAULT_FONT</em></span>, |
| 8,<br> |
| <span class="keyword">false</span>, <span class="keyword">true</span>));<br> |
| }<br> <span class="keyword">return</span> |
| style;<br> }<br> <br> |
| <span class="comment">// find the style with a given id in the style-container, |
| can return null</span><br> <span class="keyword">private |
| static</span> Style findStyle(StyleContainer styleContainer, String id) |
| {<br> <span class="comment">// |
| find and return style</span><br> |
| Collection<Style> styles = styleContainer.getStyles();<br> |
| <span class="keyword">if</span> (styles != <span class="keyword">null</span>) |
| {<br> |
| <span class="keyword">for</span> (Style style : styles) {<br> |
| <span class="keyword">if</span> (id.equals(style.getId())) {<br> |
| <span class="keyword">return</span> style;<br> |
| }<br> |
| } <br> |
| }<br> <span class="keyword">return |
| null</span>;<br> }<br>}<br></p> |
| </div> |
| </div> |
| <p> </p> |
| <!-- End code ------------------------------------------------------------------------------- --> |
| <p> </p> |
| <p>Additionally we have to associate the styles to the graphics algorithms in the |
| <span class="inlinecode">add</span>method of the <span class="inlinecode">TutorialAddEClassFeature</span>.</p> |
| <p>We do this exactly at those places, where previously the graphical attributes |
| were set directly on the graphics algorithms.</p> |
| <p>You can see the changed <span class="inlinecode">add</span> method in the following |
| code snippet:</p> |
| <!-- Begin code ------------------------------------------------------------------------------- --> |
| <p> </p> |
| <div class="literallayout"> |
| <div class="incode"> |
| <p class="code"><span class="keyword">public</span> PictogramElement add(IAddContext |
| context) {<br> <br> <span class="comment">// ... |
| EXISTING CODING ...<br></span><br> IGaService gaService |
| = Graphiti.getGaService();<br> <br> |
| RoundedRectangle roundedRectangle; <span class="comment">// need to access |
| it later</span><br> {<br> |
| <span class="comment">// create invisible outer rectangle expanded by<br> |
| // the width needed for the anchor</span><br> |
| Rectangle invisibleRectangle =<br> |
| gaService.createInvisibleRectangle(containerShape);<br> |
| gaService.setLocationAndSize(invisibleRectangle,<br> |
| context.getX(), context.getY(), width + <span class="string"><em>INVISIBLE_RECT_RIGHT</em></span>,<br> |
| height);<br> <br> |
| <span class="comment">// create and set visible rectangle inside invisible |
| rectangle</span><br> roundedRectangle |
| =<br> |
| gaService.createRoundedRectangle(invisibleRectangle, 5, 5);<br> |
| roundedRectangle.setStyle(StyleUtil<br> |
| .<em>getStyleForEClass</em>(getDiagram()));<br> |
| gaService.setLocationAndSize(roundedRectangle, 0, 0, width, height); <br> <br> |
| <span class="comment">// create link and wire it</span><br> |
| link(containerShape, addedClass);<br> }<br> <br> |
| <span class="comment">// SHAPE WITH LINE</span><br> {<br> |
| <span class="comment">// create shape for line</span><br> |
| Shape shape = peCreateService.createShape(containerShape, |
| <span class="keyword">false</span>);<br> <br> |
| <span class="comment">// create and set graphics algorithm</span><br> |
| Polyline polyline =<br> |
| gaService.createPolyline(shape, <span class="keyword">new int</span>[] { |
| 0, 20, width, 20 });<br> polyline.setStyle(StyleUtil.<em>getStyleForEClass</em>(getDiagram()));<br> |
| }<br> <br> <span class="comment">// SHAPE WITH TEXT</span><br> |
| {<br> <span class="comment">// |
| create shape for text</span><br> |
| Shape shape = peCreateService.createShape(containerShape, |
| <span class="keyword">false</span>);<br> <br> |
| <span class="comment">// create and set text graphics algorithm</span><br> |
| Text text = gaService.createText(shape, addedClass.getName());<br> |
| text.setStyle(StyleUtil.<em>getStyleForEClassText</em>(getDiagram()));<br> |
| text.setHorizontalAlignment(Orientation.<span class="string"><em>ALIGNMENT_CENTER</em></span>);<br> |
| text.setVerticalAlignment(Orientation.<em class="string">ALIGNMENT_CENTER</em>);<br> |
| gaService.setLocationAndSize(text, 0, 0, width, 20);<br> <br> |
| <span class="comment">// create link and wire it</span><br> |
| link(shape, addedClass);<br> <br> |
| <span class="comment">// provide information to support direct-editing directly<br> |
| // after object creation (must be activated additionally)</span><br> |
| IDirectEditingInfo directEditingInfo =<br> |
| getFeatureProvider().getDirectEditingInfo();<br> |
| <span class="comment">// set container shape for direct editing after object |
| creation</span><br> directEditingInfo.setMainPictogramElement(containerShape);<br> |
| <span class="comment">// set shape and graphics algorithm where the editor |
| for<br> // direct editing shall |
| be opened after object creation</span><br> |
| directEditingInfo.setPictogramElement(shape);<br> |
| directEditingInfo.setGraphicsAlgorithm(text);<br> }<br> <br> |
| <span class="comment">// add a chopbox anchor to the shape<br><br> |
| // ... EXISTING CODING ...</span><br><br> rectangle.setStyle(StyleUtil.<em>getStyleForEClass</em>(getDiagram()));<br> |
| <br> <span class="comment">// ... EXISTING CODING ...</span><br> <br> |
| <span class="keyword">return</span> containerShape;<br>}<br></p> |
| </div> |
| </div> |
| <p> </p> |
| <!-- End code ------------------------------------------------------------------------------- --> |
| <p>To test the just implemented styles we have to create a small |
| <a href="custom-feature.htm">custom feature</a>, which allows changing the foreground |
| color or background color of the style.</p> |
| <p>The implementation can be seen here:</p> |
| <!-- Begin code ------------------------------------------------------------------------------- --> |
| <p> </p> |
| <div class="literallayout"> |
| <div class="incode"> |
| <p class="code"><span class="keyword">package</span> org.eclipse.graphiti.examples.tutorial.features;<br> <br> |
| <span class="keyword">public class</span> TutorialChangeColorEClassFeature |
| <span class="keyword">extends</span> AbstractCustomFeature {<br> <br> |
| <span class="keyword"> private boolean</span> <span class="string"> |
| background</span>;<br> <br> <span class="keyword"> |
| public</span> TutorialChangeColorEClassFeature(IFeatureProvider fp,<br> |
| <span class="keyword">boolean</span> background) {<br> |
| <span class="keyword">super</span>(fp);<br> |
| <span class="keyword">this</span>.<span class="string">background </span> |
| = background;<br> }<br> <br> @Override<br> |
| <span class="keyword">public</span> String getName() {<br> |
| String colorType = <span class="string">background</span> ? |
| <span class="string">"&background"</span> : <span class="string">"&foreground"</span>;<br> |
| <span class="keyword">return</span> <span class="string">"Change "</span> |
| + colorType + <span class="string">" color"</span>;<br> |
| }<br> <br> @Override<br> |
| <span class="keyword">public</span> String getDescription() {<br> |
| String colorType = <span class="string">background</span> ? |
| <span class="string">"background"</span> : <span class="string">"foreground"</span>;<br> |
| <span class="keyword">return</span> <span class="string">"Change the "</span> |
| + colorType + <span class="string">" color"</span>;<br> |
| }<br> <br> @Override<br> |
| <span class="keyword">public boolean</span> canExecute(ICustomContext context) |
| {<br> PictogramElement[] pes = |
| context.getPictogramElements();<br> |
| <span class="keyword">if</span> (pes == <span class="keyword">null</span> |
| || pes.<span class="string">length</span> == 0) { <span class="comment"> |
| // nothing selected</span><br> |
| <span class="keyword">return false</span>;<br> |
| }<br> <br> |
| <span class="comment">// return true, if all elements are EClasses<br> |
| // note, that in execute() the selected elements are not even accessed,<br> |
| // so theoretically it would be possible that canExecute() always<br> |
| // returns true. But for usability reasons it is better to check<br> |
| // if the selected elements are EClasses.</span><br> |
| <span class="keyword">for</span> (PictogramElement pe : pes) {<br> |
| <span class="keyword">final</span> Object bo = getBusinessObjectForPictogramElement(pe);<br> |
| <span class="keyword">if</span> (!(bo <span class="keyword">instanceof</span> |
| EClass)) {<br> |
| <span class="keyword"> return false</span>;<br> |
| }<br> }<br> |
| <span class="keyword">return true</span>;<br> }<br> <br> |
| <span class="keyword">public void</span> execute(ICustomContext context) |
| {<br> Style style = StyleUtil.<em>getStyleForEClass</em>(getDiagram());<br> <br> |
| <span class="comment">// let the user choose the new color</span><br> |
| Color currentColor;<br> |
| <span class="keyword">if</span> (<span class="string">background</span>) |
| {<br> |
| currentColor = style.getBackground();<br> |
| } <span class="keyword">else</span> {<br> |
| currentColor = style.getForeground();<br> |
| }<br> Color newColor = ExampleUtil<em>.editColor</em>(currentColor);<br> |
| <span class="keyword">if</span> (newColor == <span class="keyword">null</span>) |
| { <span class="comment">// user did not choose new color</span><br> |
| <span class="keyword"> return</span>;<br> |
| }<br> <br> |
| <span class="comment">// set new color</span><br> |
| <span class="keyword">if</span> (<span class="string">background</span>) |
| {<br> |
| style.setBackground(newColor);<br> |
| } <span class="keyword">else</span> {<br> |
| style.setForeground(newColor);<br> |
| }<br> }<br>}<br></p> |
| </div> |
| </div> |
| <p> </p> |
| <!-- End code ------------------------------------------------------------------------------- --> |
| <p>Finally the feature provider has to deliver our newly created custom feature |
| (overwrite the method |
| <a href="../../../javadoc/org/eclipse/graphiti/features/IFeatureProvider.html#getCustomFeatures(org.eclipse.graphiti.features.context.ICustomContext)"> |
| getCustomFeatures</a>).</p> |
| <p>This implementation can be seen here:</p> |
| <!-- Begin code ------------------------------------------------------------------------------- --> |
| <p> </p> |
| <div class="literallayout"> |
| <div class="incode"> |
| <p class="code">@Override<br><span class="keyword">public</span> ICustomFeature[] |
| getCustomFeatures(ICustomContext context) {<br> |
| <span class="keyword">return new</span> ICustomFeature[] { |
| <span class="keyword">new</span> TutorialRenameEClassFeature(<span class="keyword">this</span>),<br> |
| <span class="keyword">new</span> TutorialDrillDownEClassFeature(<span class="keyword">this</span>),<br> |
| <span class="keyword">new</span> TutorialAssociateDiagramEClassFeature(<span class="keyword">this</span>),<br> |
| <span class="keyword">new</span> TutorialChangeColorEClassFeature(<span class="keyword">this</span>, |
| <span class="keyword">true</span>),<br> |
| <span class="keyword">new</span> TutorialChangeColorEClassFeature(<span class="keyword">this</span>, |
| <span class="keyword">false</span>) };<br>}<br></p> |
| </div> |
| </div> |
| <p> </p> |
| <!-- End code ------------------------------------------------------------------------------- --> |
| <h2>Test: Change background color of all EClasses</h2> |
| <p>Now start the editor and test this:</p> |
| <ol> |
| <li>create or open a new diagram</li> |
| <li>create several new EClasses</li> |
| <li>open the context menu on one of the EClasses; choose "Change background |
| color"</li> |
| <li>verify that the color of all EClasses is changed accordingly</li> |
| </ol> |
| <p> </p> |
| |
| </body> |
| |
| </html> |