001package com.hfg.bio.phylogeny; 002 003 004import java.awt.*; 005 006import com.hfg.exception.InvalidValueException; 007import com.hfg.exception.ProgrammingException; 008import com.hfg.graphics.units.GfxSize; 009import com.hfg.graphics.units.GfxUnits; 010import com.hfg.graphics.units.Pixels; 011import com.hfg.setting.BooleanSetting; 012import com.hfg.setting.ComplexSetting; 013import com.hfg.setting.FloatSetting; 014import com.hfg.setting.IntSetting; 015import com.hfg.setting.Settings; 016import com.hfg.setting.StringSetting; 017import com.hfg.util.StringBuilderPlus; 018import com.hfg.util.StringUtil; 019 020//------------------------------------------------------------------------------ 021/** 022 * Phylogenetic tree display options for use with NewickTree. 023 * 024 * @author J. Alex Taylor, hairyfatguy.com 025 */ 026//------------------------------------------------------------------------------ 027// com.hfg XML/HTML Coding Library 028// 029// This library is free software; you can redistribute it and/or 030// modify it under the terms of the GNU Lesser General Public 031// License as published by the Free Software Foundation; either 032// version 2.1 of the License, or (at your option) any later version. 033// 034// This library is distributed in the hope that it will be useful, 035// but WITHOUT ANY WARRANTY; without even the implied warranty of 036// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 037// Lesser General Public License for more details. 038// 039// You should have received a copy of the GNU Lesser General Public 040// License along with this library; if not, write to the Free Software 041// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 042// 043// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 044// jataylor@hairyfatguy.com 045//------------------------------------------------------------------------------ 046 047public class TreeDisplaySettings extends Settings 048{ 049 050 private static final String CLADOGRAM_STYLE = "CladogramStyle"; 051 private static final String TREE_WIDTH = "TreeWidth"; 052 private static final String ASSIGN_GROUPS_TO_SINGLETONS = "AssignGroupsToSingletons"; 053 private static final String ALIGN_LEAF_NODE_LABELS = "AlignLeaftNodeLabels"; 054 private static final String SHOW_SCALE = "ShowScale"; 055 private static final String ENABLE_DYNAMIC_GROUPING = "EnableDynamicGrouping"; 056 private static final String BRANCH_LENGTH_TRAVERSAL_LIMIT = "BranchLengthTraversalLimit"; 057 private static final String SHOW_BRANCH_LENGTH_TRAVERSAL_LIMIT = "ShowBranchLengthTraversalLimit"; 058 private static final String BLTL_ONCHANGE_CALLBACK = "BLTL_OnChangeCallback"; 059 private static final String BLTL_SELECTION_COMPLETE_CALLBACK = "BLTL_SelectionCompleteCallback"; 060 private static final String FONT = "Font"; 061 private static final String LEAF_LABEL_LEFT_PADDING = "LeafLabelLeftPadding"; 062 private static final String SCALE_TICK_HEIGHT = "ScaleTickHeight"; 063 private static final String SCALE_PADDING_TOP = "ScalePaddingTop"; 064 065 private static CladogramStyle sDefaultCladogramStyle = CladogramStyle.Rectangular; 066 private static int sDefaultTreeWidth = 500; 067 private static boolean sDefaultAssignGroupsToSingletons = false; 068 private static boolean sDefaultAlignLeaftNodeLabels = true; 069 private static boolean sDefaultShowScale = true; 070 private static boolean sDefaultShowBranchLengthTraversalLimit = false; 071 private static boolean sDefaultEnableDynamicGrouping = false; 072 private static Font sDefaultFont = Font.decode("Arial-PLAIN-10"); 073 private static int sDefaultScaleTickHeight = 10; 074 private static GfxSize sDefaultLeafLabelLeftPadding = new Pixels(10); 075 private static GfxSize sDefaultScalePaddingTop = new Pixels(40); 076 077 //************************************************************************** 078 // CONSTRUCTORS 079 //************************************************************************** 080 081 //--------------------------------------------------------------------------- 082 public TreeDisplaySettings() 083 { 084 085 } 086 087 //--------------------------------------------------------------------------- 088 @Override 089 protected void init() 090 { 091 super.init(); 092 093 add(new ComplexSetting(CLADOGRAM_STYLE, sDefaultCladogramStyle)); 094 add(new IntSetting(TREE_WIDTH, sDefaultTreeWidth)); 095 add(new BooleanSetting(ASSIGN_GROUPS_TO_SINGLETONS, sDefaultAssignGroupsToSingletons)); 096 add(new BooleanSetting(ALIGN_LEAF_NODE_LABELS, sDefaultAlignLeaftNodeLabels)); 097 add(new BooleanSetting(SHOW_SCALE, sDefaultShowScale)); 098 add(new BooleanSetting(ENABLE_DYNAMIC_GROUPING, sDefaultEnableDynamicGrouping)); 099 add(new FloatSetting(BRANCH_LENGTH_TRAVERSAL_LIMIT)); 100 add(new BooleanSetting(SHOW_BRANCH_LENGTH_TRAVERSAL_LIMIT, sDefaultShowBranchLengthTraversalLimit)); 101 add(new StringSetting(BLTL_ONCHANGE_CALLBACK)); 102 add(new StringSetting(BLTL_SELECTION_COMPLETE_CALLBACK)); 103 add(new StringSetting(FONT)); 104 add(new IntSetting(LEAF_LABEL_LEFT_PADDING)); 105 add(new IntSetting(SCALE_TICK_HEIGHT, sDefaultScaleTickHeight)); 106 add(new IntSetting(SCALE_PADDING_TOP)); 107 108 setFont(sDefaultFont); 109 setLeafLabelLeftPadding(sDefaultLeafLabelLeftPadding); 110 setScalePaddingTop(sDefaultScalePaddingTop); 111 } 112 113 //########################################################################### 114 // PUBLIC METHODS 115 //########################################################################### 116 117 //--------------------------------------------------------------------------- 118 public TreeDisplaySettings setCladogramStyle(CladogramStyle inValue) 119 { 120 get(CLADOGRAM_STYLE).setValue(inValue); 121 return this; 122 } 123 124 //--------------------------------------------------------------------------- 125 public CladogramStyle getCladogramStyle() 126 { 127 return (CladogramStyle) get(CLADOGRAM_STYLE).getValue(); 128 } 129 130 131 //--------------------------------------------------------------------------- 132 public TreeDisplaySettings setTreeWidth(int inValue) 133 { 134 get(TREE_WIDTH).setValue(inValue); 135 return this; 136 } 137 138 //--------------------------------------------------------------------------- 139 public Integer getTreeWidth() 140 { 141 return (Integer) get(TREE_WIDTH).getValue(); 142 } 143 144 145 //--------------------------------------------------------------------------- 146 public TreeDisplaySettings setAssignGroupsToSingletons(boolean inValue) 147 { 148 get(ASSIGN_GROUPS_TO_SINGLETONS).setValue(inValue); 149 return this; 150 } 151 152 //--------------------------------------------------------------------------- 153 public Boolean getAssignGroupsToSingletons() 154 { 155 return (Boolean) get(ASSIGN_GROUPS_TO_SINGLETONS).getValue(); 156 } 157 158 159 //--------------------------------------------------------------------------- 160 public TreeDisplaySettings setAlignLeaftNodeLabels(boolean inValue) 161 { 162 get(ALIGN_LEAF_NODE_LABELS).setValue(inValue); 163 return this; 164 } 165 166 //--------------------------------------------------------------------------- 167 public Boolean getAlignLeaftNodeLabels() 168 { 169 return (Boolean) get(ALIGN_LEAF_NODE_LABELS).getValue(); 170 } 171 172 173 //--------------------------------------------------------------------------- 174 public TreeDisplaySettings setShowScale(boolean inValue) 175 { 176 get(SHOW_SCALE).setValue(inValue); 177 return this; 178 } 179 180 //--------------------------------------------------------------------------- 181 public Boolean getShowScale() 182 { 183 return (Boolean) get(SHOW_SCALE).getValue(); 184 } 185 186 187 //--------------------------------------------------------------------------- 188 /** 189 When outputting the tree in SVG, this option allows for dynamically adjusting 190 the branch length traversal limit to group closely related leaf nodes. 191 * @param inValue whether or not to enable the dynamic grouping option 192 * @return this Settings object to allow method chaining 193 */ 194 public TreeDisplaySettings setEnableDynamicGrouping(boolean inValue) 195 { 196 get(ENABLE_DYNAMIC_GROUPING).setValue(inValue); 197 return this; 198 } 199 200 //--------------------------------------------------------------------------- 201 public Boolean getEnableDynamicGrouping() 202 { 203 return (Boolean) get(ENABLE_DYNAMIC_GROUPING).getValue(); 204 } 205 206 207 //--------------------------------------------------------------------------- 208 public TreeDisplaySettings setBranchLengthTraversalLimit(Float inValue) 209 { 210 get(BRANCH_LENGTH_TRAVERSAL_LIMIT).setValue(inValue); 211 return this; 212 } 213 214 //--------------------------------------------------------------------------- 215 public Float getBranchLengthTraversalLimit() 216 { 217 return (Float) get(BRANCH_LENGTH_TRAVERSAL_LIMIT).getValue(); 218 } 219 220 //-------------------------------------------------------------------------- 221 public void boundsCheckBranchLengthTraversalLimit(PhylogeneticTree inTree) 222 { 223 Float branchLengthTraversalLimit = getBranchLengthTraversalLimit(); 224 if (branchLengthTraversalLimit != null) 225 { 226 // Bounds check 227 if (branchLengthTraversalLimit < 0) 228 { 229 branchLengthTraversalLimit = 0f; 230 } 231 else if (branchLengthTraversalLimit > 2 * inTree.getRootedTreeDistance()) 232 { 233 branchLengthTraversalLimit = 2 * inTree.getRootedTreeDistance(); 234 } 235 236 setBranchLengthTraversalLimit(branchLengthTraversalLimit); 237 } 238 } 239 240 241 //--------------------------------------------------------------------------- 242 public TreeDisplaySettings setShowBranchLengthTraversalLimit(boolean inValue) 243 { 244 get(SHOW_BRANCH_LENGTH_TRAVERSAL_LIMIT).setValue(inValue); 245 return this; 246 } 247 248 //--------------------------------------------------------------------------- 249 public Boolean getShowBranchLengthTraversalLimit() 250 { 251 return (Boolean) get(SHOW_BRANCH_LENGTH_TRAVERSAL_LIMIT).getValue(); 252 } 253 254 255 //--------------------------------------------------------------------------- 256 /** 257 While dynamically adjusting the branch length traversal limit to group closely 258 related leaf nodes, this callback, if defined, is invoked so the changing BLTL 259 value can be displayed. 260 * @param inValue the javascript method to invoke (with the new BLTL value, and 261 * the drag handle object as arguments) 262 * @return this Settings object to allow method chaining 263 */ 264 public TreeDisplaySettings setBLTL_OnChangeCallback(String inValue) 265 { 266 get(BLTL_ONCHANGE_CALLBACK).setValue(inValue); 267 return this; 268 } 269 270 //--------------------------------------------------------------------------- 271 public String getBLTL_OnChangeCallback() 272 { 273 return (String) get(BLTL_ONCHANGE_CALLBACK).getValue(); 274 } 275 276 277 //--------------------------------------------------------------------------- 278 /** 279 When the dynamic adjustment of the branch length traversal limit to group closely 280 related leaf nodes is completed (end of drag event), this callback, if defined, is invoked. 281 * @param inValue the javascript method to invoke (with the new BLTL value, and 282 * the drag handle object as arguments) 283 * @return this Settings object to allow method chaining 284 */ 285 public TreeDisplaySettings setBLTL_SelectionCompleteCallback(String inValue) 286 { 287 get(BLTL_SELECTION_COMPLETE_CALLBACK).setValue(inValue); 288 return this; 289 } 290 291 //--------------------------------------------------------------------------- 292 public String getBLTL_SelectionCompleteCallback() 293 { 294 return (String) get(BLTL_SELECTION_COMPLETE_CALLBACK).getValue(); 295 } 296 297 298 //--------------------------------------------------------------------------- 299 public TreeDisplaySettings setFont(Font inValue) 300 { 301 CharSequence value = null; 302 303 if (inValue != null) 304 { 305 // TODO: This logic should be put in some utility function like FontUtil.encode() 306 String style; 307 switch (inValue.getStyle()) 308 { 309 case Font.PLAIN: 310 style = "PLAIN"; 311 break; 312 case Font.BOLD: 313 style = "BOLD"; 314 break; 315 case Font.ITALIC: 316 style = "ITALIC"; 317 break; 318 case 3: 319 style = "BOLDITALIC"; 320 break; 321 default: 322 throw new ProgrammingException("Unexpected Font style: " + inValue.getStyle() + "!"); 323 } 324 325 StringBuilderPlus stringValue = new StringBuilderPlus().setDelimiter("-"); 326 327 stringValue.append(inValue.getFontName()) 328 .delimitedAppend(style) 329 .delimitedAppend(inValue.getSize()); 330 value = stringValue; 331 } 332 333 get(FONT).setValue(value != null ? value.toString() : null); 334 return this; 335 } 336 337 //--------------------------------------------------------------------------- 338 public Font getFont() 339 { 340 Font value = null; 341 342 String stringValue = (String) get(FONT).getValue(); 343 if (StringUtil.isSet(stringValue)) 344 { 345 value = Font.decode(stringValue); 346 } 347 348 return value; 349 } 350 351 352 //--------------------------------------------------------------------------- 353 public TreeDisplaySettings setLeafLabelLeftPadding(GfxSize inValue) 354 { 355 if (null == inValue) 356 { 357 throw new InvalidValueException("The leaf label left padding cannot be set to null!"); 358 } 359 360 get(LEAF_LABEL_LEFT_PADDING).setValue(inValue != null ? inValue.toInt(GfxUnits.pixels) : null); 361 return this; 362 } 363 364 //--------------------------------------------------------------------------- 365 public GfxSize getLeafLabelLeftPadding() 366 { 367 GfxSize padding = null; 368 369 Integer pixelValue = (Integer) get(LEAF_LABEL_LEFT_PADDING).getValue(); 370 if (pixelValue != null) 371 { 372 padding = GfxSize.allocate(pixelValue, GfxUnits.pixels); 373 } 374 375 return padding; 376 } 377 378 379 //--------------------------------------------------------------------------- 380 public TreeDisplaySettings setScaleTickHeight(int inValue) 381 { 382 get(SCALE_TICK_HEIGHT).setValue(inValue); 383 return this; 384 } 385 386 //--------------------------------------------------------------------------- 387 public Integer getScaleTickHeight() 388 { 389 return (Integer) get(SCALE_TICK_HEIGHT).getValue(); 390 } 391 392 393 //--------------------------------------------------------------------------- 394 public TreeDisplaySettings setScalePaddingTop(GfxSize inValue) 395 { 396 if (null == inValue) 397 { 398 throw new InvalidValueException("The scale padding top cannot be set to null!"); 399 } 400 401 get(SCALE_PADDING_TOP).setValue(inValue != null ? inValue.toInt(GfxUnits.pixels) : null); 402 return this; 403 } 404 405 //--------------------------------------------------------------------------- 406 public GfxSize getScalePaddingTop() 407 { 408 GfxSize padding = null; 409 410 Integer pixelValue = (Integer) get(SCALE_PADDING_TOP).getValue(); 411 if (pixelValue != null) 412 { 413 padding = GfxSize.allocate(pixelValue, GfxUnits.pixels); 414 } 415 416 return padding; 417 } 418 419}