001/* 002 * Copyright 2006 - 2013 003 * Stefan Balev <stefan.balev@graphstream-project.org> 004 * Julien Baudry <julien.baudry@graphstream-project.org> 005 * Antoine Dutot <antoine.dutot@graphstream-project.org> 006 * Yoann Pigné <yoann.pigne@graphstream-project.org> 007 * Guilhelm Savin <guilhelm.savin@graphstream-project.org> 008 * 009 * This file is part of GraphStream <http://graphstream-project.org>. 010 * 011 * GraphStream is a library whose purpose is to handle static or dynamic 012 * graph, create them from scratch, file or any source and display them. 013 * 014 * This program is free software distributed under the terms of two licenses, the 015 * CeCILL-C license that fits European law, and the GNU Lesser General Public 016 * License. You can use, modify and/ or redistribute the software under the terms 017 * of the CeCILL-C license as circulated by CEA, CNRS and INRIA at the following 018 * URL <http://www.cecill.info> or under the terms of the GNU LGPL as published by 019 * the Free Software Foundation, either version 3 of the License, or (at your 020 * option) any later version. 021 * 022 * This program is distributed in the hope that it will be useful, but WITHOUT ANY 023 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 024 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 025 * 026 * You should have received a copy of the GNU Lesser General Public License 027 * along with this program. If not, see <http://www.gnu.org/licenses/>. 028 * 029 * The fact that you are presently reading this means that you have had 030 * knowledge of the CeCILL-C and LGPL licenses and that you accept their terms. 031 */ 032package org.graphstream.ui.graphicGraph.stylesheet; 033 034import java.awt.Color; 035import java.util.ArrayList; 036import java.util.HashMap; 037import java.util.Iterator; 038 039/** 040 * A style is a whole set of settings for a graphic element. 041 * 042 * <p> 043 * Styles inherit each others. By default a style is all set to invalid values 044 * meaning "unset". This means that the value is to be taken from the parent. 045 * The getters are able to resolve this process by themselves and therefore must 046 * be used instead of a direct access to fields. 047 * </p> 048 */ 049public class Style extends StyleConstants { 050 // Attributes 051 052 /** 053 * The vertical part of the cascade. 054 */ 055 protected Rule parent = null; 056 057 /** 058 * The values of each style property. 059 */ 060 protected HashMap<String, Object> values = null; 061 062 /** 063 * The set of special styles that must override this style when some event 064 * occurs. 065 */ 066 protected HashMap<String, Rule> alternates = null; 067 068 // Constructors 069 070 /** 071 * New style with all settings to a special value meaning "unset". In this 072 * modeField, all the settings are inherited from the parent (when set). 073 */ 074 public Style() { 075 this(null); 076 } 077 078 /** 079 * New style with all settings to a special value meaning "unset". In this 080 * modeField, all the settings are inherited from the parent. 081 * 082 * @param parent 083 * The parent style. 084 */ 085 public Style(Rule parent) { 086 this.parent = parent; 087 this.values = new HashMap<String, Object>(); 088 } 089 090 // Access 091 092 /** 093 * The parent style. 094 * 095 * @return a style from which some settings are inherited. 096 */ 097 public Rule getParent() { 098 return parent; 099 } 100 101 /** 102 * Get the value of a given property. 103 * 104 * This code is the same for all "getX" methods so we explain it once here. 105 * This is the implementation of style inheritance. 106 * 107 * First if some event is actually occurring, the alternative styles are 108 * searched first. If these events have unset values for the property, their 109 * parent are then searched. 110 * 111 * If the value for the property is not found in the alternative styles, 112 * alternative styles parents, or if there is no event occurring actually, 113 * this style is checked. 114 * 115 * If its value is unset, the parents of this style are checked. 116 * 117 * Classes are not checked here, they are processed in the 118 * {@link org.graphstream.ui.graphicGraph.StyleGroup} class. 119 * 120 * @param property 121 * The style property the value is searched for. 122 */ 123 public Object getValue(String property, String... events) { 124 if (events != null && events.length > 0)// && alternates != null ) 125 { 126 Object o = null; 127 int i = events.length - 1; 128 129 do { 130 o = getValueForEvent(property, events[i]); 131 i--; 132 } while (o == null && i >= 0); 133 134 if (o != null) 135 return o; 136 } 137 138 Object value = values.get(property); 139 140 if (value == null) { 141 if (parent != null) 142 return parent.style.getValue(property, events); 143 } 144 145 return value; 146 } 147 148 protected Object getValueForEvent(String property, String event) { 149 if (alternates != null) { 150 Rule rule = alternates.get(event); 151 152 if (rule != null) { 153 Object o = rule.getStyle().values.get(property); 154 155 if (o != null) 156 return o; 157 } 158 } else if (parent != null) { 159 return parent.style.getValueForEvent(property, event); 160 } 161 162 return null; 163 } 164 165 /** 166 * True if the given field exists in this style only (not the parents). 167 * 168 * @param field 169 * The field to test. 170 * @return True if this style has a value for the given field. 171 */ 172 public boolean hasValue(String field, String... events) { 173 if (events != null && events.length > 0 && alternates != null) { 174 for (String event : events) { 175 Rule rule = alternates.get(event); 176 177 if (rule != null) { 178 return rule.getStyle().hasValue(field); 179 } 180 } 181 } 182 183 return (values.get(field) != null); 184 } 185 186 // Individual style properties. 187 188 /** 189 * How to fill the content of an element. 190 */ 191 public FillMode getFillMode() { 192 return (FillMode) getValue("fill-mode"); 193 } 194 195 /** 196 * Which color(s) to use for fill modes that use it. 197 */ 198 public Colors getFillColors() { 199 return (Colors) getValue("fill-color"); 200 } 201 202 public int getFillColorCount() { 203 Colors colors = (Colors) getValue("fill-color"); 204 205 if (colors != null) 206 return colors.size(); 207 208 return 0; 209 } 210 211 public Color getFillColor(int i) { 212 Colors colors = (Colors) getValue("fill-color"); 213 214 if (colors != null) 215 return colors.get(i); 216 217 return null; 218 } 219 220 /** 221 * Which image to use when filling the element contents with it. 222 */ 223 public String getFillImage() { 224 return (String) getValue("fill-image"); 225 } 226 227 /** 228 * How to draw the element contour. 229 */ 230 public StrokeMode getStrokeMode() { 231 return (StrokeMode) getValue("stroke-mode"); 232 } 233 234 /** 235 * How to color the element contour. 236 */ 237 public Colors getStrokeColor() { 238 return (Colors) getValue("stroke-color"); 239 } 240 241 public int getStrokeColorCount() { 242 Colors colors = (Colors) getValue("stroke-color"); 243 244 if (colors != null) 245 return colors.size(); 246 247 return 0; 248 } 249 250 public Color getStrokeColor(int i) { 251 Colors colors = (Colors) getValue("stroke-color"); 252 253 if (colors != null) 254 return colors.get(i); 255 256 return null; 257 } 258 259 /** 260 * Width of the element contour. 261 */ 262 public Value getStrokeWidth() { 263 return (Value) getValue("stroke-width"); 264 } 265 266 /** 267 * How to draw the shadow of the element. 268 */ 269 public ShadowMode getShadowMode() { 270 return (ShadowMode) getValue("shadow-mode"); 271 } 272 273 /** 274 * Color(s) of the element shadow. 275 */ 276 public Colors getShadowColors() { 277 return (Colors) getValue("shadow-color"); 278 } 279 280 public int getShadowColorCount() { 281 Colors colors = (Colors) getValue("shadow-color"); 282 283 if (colors != null) 284 return colors.size(); 285 286 return 0; 287 } 288 289 public Color getShadowColor(int i) { 290 Colors colors = (Colors) getValue("shadow-color"); 291 292 if (colors != null) 293 return colors.get(i); 294 295 return null; 296 } 297 298 /** 299 * Width of the element shadow. 300 */ 301 public Value getShadowWidth() { 302 return (Value) getValue("shadow-width"); 303 } 304 305 /** 306 * Offset of the element shadow centre according to the element centre. 307 */ 308 public Values getShadowOffset() { 309 return (Values) getValue("shadow-offset"); 310 } 311 312 /** 313 * Additional space to add inside the element between its contour and its 314 * contents. 315 */ 316 public Values getPadding() { 317 return (Values) getValue("padding"); 318 } 319 320 /** 321 * How to draw the text of the element. 322 */ 323 public TextMode getTextMode() { 324 return (TextMode) getValue("text-mode"); 325 } 326 327 /** 328 * How and when to show the text of the element. 329 */ 330 public TextVisibilityMode getTextVisibilityMode() { 331 return (TextVisibilityMode) getValue("text-visibility-mode"); 332 } 333 334 /** 335 * Visibility values if the text visibility changes. 336 */ 337 public Values getTextVisibility() { 338 return (Values) getValue("text-visibility"); 339 } 340 341 /** 342 * The text color(s). 343 */ 344 public Colors getTextColor() { 345 return (Colors) getValue("text-color"); 346 } 347 348 public int getTextColorCount() { 349 Colors colors = (Colors) getValue("text-color"); 350 351 if (colors != null) 352 return colors.size(); 353 354 return 0; 355 } 356 357 public Color getTextColor(int i) { 358 Colors colors = (Colors) getValue("text-color"); 359 360 if (colors != null) 361 return colors.get(i); 362 363 return null; 364 } 365 366 /** 367 * The text font style variation. 368 */ 369 public TextStyle getTextStyle() { 370 return (TextStyle) getValue("text-style"); 371 } 372 373 /** 374 * The text font. 375 */ 376 public String getTextFont() { 377 return (String) getValue("text-font"); 378 } 379 380 /** 381 * The text size in points. 382 */ 383 public Value getTextSize() { 384 return (Value) getValue("text-size"); 385 } 386 387 /** 388 * How to draw the icon around the text (or instead of the text). 389 */ 390 public IconMode getIconMode() { 391 return (IconMode) getValue("icon-mode"); 392 } 393 394 /** 395 * The icon image to use. 396 */ 397 public String getIcon() { 398 return (String) getValue("icon"); 399 } 400 401 /** 402 * How and when to show the element. 403 */ 404 public VisibilityMode getVisibilityMode() { 405 return (VisibilityMode) getValue("visibility-mode"); 406 } 407 408 /** 409 * The element visibility if it is variable. 410 */ 411 public Values getVisibility() { 412 return (Values) getValue("visibility"); 413 } 414 415 /** 416 * How to size the element. 417 */ 418 public SizeMode getSizeMode() { 419 return (SizeMode) getValue("size-mode"); 420 } 421 422 /** 423 * The element dimensions. 424 */ 425 public Values getSize() { 426 return (Values) getValue("size"); 427 } 428 429 /** 430 * The element polygonal shape. 431 */ 432 public Values getShapePoints() { 433 return (Values) getValue("shape-points"); 434 } 435 436 /** 437 * How to align the text according to the element centre. 438 */ 439 public TextAlignment getTextAlignment() { 440 return (TextAlignment) getValue("text-alignment"); 441 } 442 443 public TextBackgroundMode getTextBackgroundMode() { 444 return (TextBackgroundMode) getValue("text-background-mode"); 445 } 446 447 public Colors getTextBackgroundColor() { 448 return (Colors) getValue("text-background-color"); 449 } 450 451 public Color getTextBackgroundColor(int i) { 452 Colors colors = (Colors) getValue("text-background-color"); 453 454 if (colors != null) 455 return colors.get(i); 456 457 return null; 458 } 459 460 /** 461 * Offset of the text from its computed position. 462 */ 463 public Values getTextOffset() { 464 return (Values) getValue("text-offset"); 465 } 466 467 /** 468 * Padding of the text inside its background, if any. 469 */ 470 public Values getTextPadding() { 471 return (Values) getValue("text-padding"); 472 } 473 474 /** 475 * The element shape. 476 */ 477 public Shape getShape() { 478 return (Shape) getValue("shape"); 479 } 480 481 /** 482 * The element JComponent type if available. 483 */ 484 public JComponents getJComponent() { 485 return (JComponents) getValue("jcomponent"); 486 } 487 488 /** 489 * How to orient a sprite according to its attachement. 490 */ 491 public SpriteOrientation getSpriteOrientation() { 492 return (SpriteOrientation) getValue("sprite-orientation"); 493 } 494 495 /** 496 * The shape of edges arrows. 497 */ 498 public ArrowShape getArrowShape() { 499 return (ArrowShape) getValue("arrow-shape"); 500 } 501 502 /** 503 * Image to use for the arrow. 504 */ 505 public String getArrowImage() { 506 return (String) getValue("arrow-image"); 507 } 508 509 /** 510 * Edge arrow dimensions. 511 */ 512 public Values getArrowSize() { 513 return (Values) getValue("arrow-size"); 514 } 515 516 /** 517 * Colour of all non-graph, non-edge, non-node, non-sprite things. 518 */ 519 public Colors getCanvasColor() { 520 return (Colors) getValue("canvas-color"); 521 } 522 523 public int getCanvasColorCount() { 524 Colors colors = (Colors) getValue("canvas-color"); 525 526 if (colors != null) 527 return colors.size(); 528 529 return 0; 530 } 531 532 public Color getCanvasColor(int i) { 533 Colors colors = (Colors) getValue("canvas-color"); 534 535 if (colors != null) 536 return colors.get(i); 537 538 return null; 539 } 540 541 public Integer getZIndex() { 542 return (Integer) getValue("z-index"); 543 } 544 545 // Commands 546 547 /** 548 * Set the default values for each setting. 549 */ 550 public void setDefaults() { 551 Colors fillColor = new Colors(); 552 Colors strokeColor = new Colors(); 553 Colors shadowColor = new Colors(); 554 Colors textColor = new Colors(); 555 Colors canvasColor = new Colors(); 556 Colors textBgColor = new Colors(); 557 558 fillColor.add(Color.BLACK); 559 strokeColor.add(Color.BLACK); 560 shadowColor.add(Color.GRAY); 561 textColor.add(Color.BLACK); 562 canvasColor.add(Color.WHITE); 563 textBgColor.add(Color.WHITE); 564 565 values.put("z-index", new Integer(0)); 566 567 values.put("fill-mode", FillMode.PLAIN); 568 values.put("fill-color", fillColor); 569 values.put("fill-image", null); 570 571 values.put("stroke-mode", StrokeMode.NONE); 572 values.put("stroke-color", strokeColor); 573 values.put("stroke-width", new Value(Units.PX, 1)); 574 575 values.put("shadow-mode", ShadowMode.NONE); 576 values.put("shadow-color", shadowColor); 577 values.put("shadow-width", new Value(Units.PX, 3)); 578 values.put("shadow-offset", new Values(Units.PX, 3, 3)); 579 580 values.put("padding", new Values(Units.PX, 0, 0, 0)); 581 582 values.put("text-mode", TextMode.NORMAL); 583 values.put("text-visibility-mode", TextVisibilityMode.NORMAL); 584 values.put("text-visibility", null); 585 values.put("text-color", textColor); 586 values.put("text-style", TextStyle.NORMAL); 587 values.put("text-font", "default"); 588 values.put("text-size", new Value(Units.PX, 10)); 589 values.put("text-alignment", TextAlignment.CENTER); 590 values.put("text-background-mode", TextBackgroundMode.NONE); 591 values.put("text-background-color", textBgColor); 592 values.put("text-offset", new Values(Units.PX, 0, 0)); 593 values.put("text-padding", new Values(Units.PX, 0, 0)); 594 595 values.put("icon-mode", IconMode.NONE); 596 values.put("icon", null); 597 598 values.put("visibility-mode", VisibilityMode.NORMAL); 599 values.put("visibility", null); 600 601 values.put("size-mode", SizeMode.NORMAL); 602 values.put("size", new Values(Units.PX, 10, 10, 10)); 603 604 values.put("shape", Shape.CIRCLE); 605 values.put("shape-points", null); 606 values.put("jcomponent", null); 607 608 values.put("sprite-orientation", SpriteOrientation.NONE); 609 610 values.put("arrow-shape", ArrowShape.ARROW); 611 values.put("arrow-size", new Values(Units.PX, 8, 4)); 612 values.put("arrow-image", null); 613 614 values.put("canvas-color", canvasColor); 615 616 } 617 618 /** 619 * Copy all the settings of the other style that are set, excepted the 620 * parent. Only the settings that have a value (different from "unset") are 621 * copied. The parent field is never copied. 622 * 623 * @param other 624 * Another style. 625 */ 626 public void augment(Style other) { 627 if (other != this) { 628 augmentField("z-index", other); 629 augmentField("fill-mode", other); 630 augmentField("fill-color", other); 631 augmentField("fill-image", other); 632 633 augmentField("stroke-mode", other); 634 augmentField("stroke-color", other); 635 augmentField("stroke-width", other); 636 637 augmentField("shadow-mode", other); 638 augmentField("shadow-color", other); 639 augmentField("shadow-width", other); 640 augmentField("shadow-offset", other); 641 642 augmentField("padding", other); 643 644 augmentField("text-mode", other); 645 augmentField("text-visibility-mode", other); 646 augmentField("text-visibility", other); 647 augmentField("text-color", other); 648 augmentField("text-style", other); 649 augmentField("text-font", other); 650 augmentField("text-size", other); 651 augmentField("text-alignment", other); 652 augmentField("text-background-mode", other); 653 augmentField("text-background-color", other); 654 augmentField("text-offset", other); 655 augmentField("text-padding", other); 656 657 augmentField("icon-mode", other); 658 augmentField("icon", other); 659 660 augmentField("visibility-mode", other); 661 augmentField("visibility", other); 662 663 augmentField("size-mode", other); 664 augmentField("size", other); 665 666 augmentField("shape", other); 667 augmentField("shape-points", other); 668 augmentField("jcomponent", other); 669 670 augmentField("sprite-orientation", other); 671 672 augmentField("arrow-shape", other); 673 augmentField("arrow-size", other); 674 augmentField("arrow-image", other); 675 676 augmentField("canvas-color", other); 677 } 678 } 679 680 protected void augmentField(String field, Style other) { 681 Object value = other.values.get(field); 682 683 if (value != null) { 684 if (value instanceof Value) 685 setValue(field, new Value((Value) value)); 686 else if (value instanceof Values) 687 setValue(field, new Values((Values) value)); 688 else if (value instanceof Colors) 689 setValue(field, new Colors((Colors) value)); 690 else 691 setValue(field, value); 692 } 693 } 694 695 /** 696 * Set or change the parent of the style. 697 * 698 * @param parent 699 * The new parent. 700 */ 701 public void reparent(Rule parent) { 702 this.parent = parent; 703 } 704 705 /** 706 * Add an alternative style for specific events. 707 * 708 * @param event 709 * The event that triggers the alternate style. 710 * @param alternateStyle 711 * The alternative style. 712 */ 713 public void addAlternateStyle(String event, Rule alternateStyle) { 714 if (alternates == null) 715 alternates = new HashMap<String, Rule>(); 716 717 alternates.put(event, alternateStyle); 718 } 719 720 // Commands -- Setters 721 722 public void setValue(String field, Object value) { 723 values.put(field, value); 724 } 725 726 // Utility 727 728 @Override 729 public String toString() { 730 return toString(-1); 731 } 732 733 public String toString(int level) { 734 StringBuilder builder = new StringBuilder(); 735 String prefix = ""; 736 String sprefix = " "; 737 738 if (level > 0) { 739 for (int i = 0; i < level; i++) 740 prefix += " "; 741 } 742 743 // builder.append( String.format( "%s%s%n", prefix, super.toString() ) 744 // ); 745 746 if (parent != null) { 747 Rule p = parent; 748 749 while (!(p == null)) { 750 builder.append(String.format(" -> %s", p.selector.toString())); 751 p = p.getStyle().getParent(); 752 } 753 754 } 755 756 if (alternates != null && alternates.size() > 0) { 757 builder.append(String.format(" /")); 758 for (Rule rule : alternates.values()) { 759 builder.append(' '); 760 builder.append(rule.selector.toString()); 761 } 762 } 763 764 builder.append(String.format("%n")); 765 766 Iterator<String> i = values.keySet().iterator(); 767 768 while (i.hasNext()) { 769 String key = i.next(); 770 Object o = values.get(key); 771 772 if (o instanceof ArrayList<?>) { 773 ArrayList<?> array = (ArrayList<?>) o; 774 775 if (array.size() > 0) { 776 builder.append(String.format("%s%s%s%s: ", prefix, sprefix, 777 sprefix, key)); 778 779 for (Object p : array) 780 builder.append(String.format("%s ", p.toString())); 781 782 builder.append(String.format("%n")); 783 } else { 784 builder.append(String.format("%s%s%s%s: <empty>%n", prefix, 785 sprefix, sprefix, key)); 786 } 787 } else { 788 builder.append(String.format("%s%s%s%s: %s%n", prefix, sprefix, 789 sprefix, key, o != null ? o.toString() : "<null>")); 790 } 791 } 792 793 /* 794 * if( level >= 0 ) { if( parent != null ) { String rec = 795 * parent.style.toString( level + 1 ); 796 * 797 * builder.append( rec ); } } 798 */ 799 String res = builder.toString(); 800 801 if (res.length() == 0) 802 return String.format("%s%s<empty>%n", prefix, prefix); 803 804 return res; 805 } 806}