001/* 002 * Copyright 2010-2015 Institut Pasteur. 003 * 004 * This file is part of Icy. 005 * 006 * Icy is free software: you can redistribute it and/or modify 007 * it under the terms of the GNU General Public License as published by 008 * the Free Software Foundation, either version 3 of the License, or 009 * (at your option) any later version. 010 * 011 * Icy is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with Icy. If not, see <http://www.gnu.org/licenses/>. 018 */ 019package icy.gui.util; 020 021import icy.common.listener.weak.WeakListener; 022import icy.image.ImageUtil; 023import icy.preferences.GeneralPreferences; 024import icy.system.IcyExceptionHandler; 025import icy.system.SystemUtil; 026import icy.system.thread.ThreadUtil; 027import icy.util.ClassUtil; 028import icy.util.ReflectionUtil; 029import icy.util.StringUtil; 030 031import java.awt.Color; 032import java.awt.Component; 033import java.awt.Image; 034import java.awt.Window; 035import java.awt.event.ActionEvent; 036import java.awt.event.ActionListener; 037import java.util.ArrayList; 038import java.util.Map; 039 040import javax.swing.JDialog; 041import javax.swing.JFrame; 042import javax.swing.JInternalFrame; 043import javax.swing.PopupFactory; 044import javax.swing.UIManager; 045import javax.swing.UIManager.LookAndFeelInfo; 046import javax.swing.plaf.ColorUIResource; 047import javax.swing.plaf.InternalFrameUI; 048 049import org.pushingpixels.flamingo.api.common.CommandToggleButtonGroup; 050import org.pushingpixels.flamingo.api.common.JCommandToggleMenuButton; 051import org.pushingpixels.flamingo.api.common.popup.JCommandPopupMenu; 052import org.pushingpixels.substance.api.ColorSchemeAssociationKind; 053import org.pushingpixels.substance.api.ComponentState; 054import org.pushingpixels.substance.api.DecorationAreaType; 055import org.pushingpixels.substance.api.SubstanceColorScheme; 056import org.pushingpixels.substance.api.SubstanceLookAndFeel; 057import org.pushingpixels.substance.api.SubstanceSkin; 058import org.pushingpixels.substance.api.colorscheme.LightAquaColorScheme; 059import org.pushingpixels.substance.api.fonts.FontPolicy; 060import org.pushingpixels.substance.api.fonts.FontSet; 061import org.pushingpixels.substance.api.fonts.SubstanceFontUtilities; 062import org.pushingpixels.substance.api.skin.SkinChangeListener; 063import org.pushingpixels.substance.api.skin.SkinInfo; 064import org.pushingpixels.substance.internal.ui.SubstanceInternalFrameUI; 065import org.pushingpixels.substance.internal.utils.SubstanceColorSchemeUtilities; 066import org.pushingpixels.substance.internal.utils.SubstanceInternalFrameTitlePane; 067import org.pushingpixels.substance.internal.utils.SubstanceTitlePane; 068 069import ij.util.Java2; 070 071/** 072 * @author Stephane 073 */ 074public class LookAndFeelUtil 075{ 076 /** 077 * Weak listener wrapper for SkinChangeListener. 078 * 079 * @author Stephane 080 */ 081 public static class WeakSkinChangeListener extends WeakListener<SkinChangeListener> implements SkinChangeListener 082 { 083 public WeakSkinChangeListener(SkinChangeListener listener) 084 { 085 super(listener); 086 } 087 088 @Override 089 public void removeListener(Object source) 090 { 091 removeSkinChangeListener(this); 092 } 093 094 @Override 095 public void skinChanged() 096 { 097 final SkinChangeListener listener = getListener(); 098 099 if (listener != null) 100 listener.skinChanged(); 101 } 102 } 103 104 static int defaultFontSize; 105 static Map<String, SkinInfo> map; 106 private static ArrayList<SkinInfo> skins; 107 108 static int currentFontSize; 109 110 public static void init() 111 { 112 try 113 { 114 // so ImageJ won't change look and feel later 115 ReflectionUtil.getField(Java2.class, "lookAndFeelSet", true).set(null, Boolean.valueOf(true)); 116 } 117 catch (Exception e) 118 { 119 // do it in another way (slower) 120 Java2.setSystemLookAndFeel(); 121 } 122 123 // enable or not EDT checking in substance 124 SystemUtil.setProperty("insubstantial.checkEDT", "false"); 125 SystemUtil.setProperty("insubstantial.logEDT", "true"); 126 127 // funny features of substance 128 129 // AnimationConfigurationManager.getInstance().allowAnimations(AnimationFacet.FOCUS_LOOP_ANIMATION); 130 // AnimationConfigurationManager.getInstance().allowAnimations(AnimationFacet.GHOSTING_BUTTON_PRESS); 131 // AnimationConfigurationManager.getInstance().allowAnimations(AnimationFacet.GHOSTING_ICON_ROLLOVER); 132 // AnimationConfigurationManager.getInstance().allowAnimations(AnimationFacet.ICON_GLOW); 133 134 // UIManager.put(SubstanceLookAndFeel.USE_THEMED_DEFAULT_ICONS, Boolean.TRUE); 135 // UIManager.put(SubstanceLookAndFeel.SHOW_EXTRA_WIDGETS, Boolean.TRUE); 136 // UIManager.put(SubstanceLookAndFeel.WATERMARK_VISIBLE, Boolean.TRUE); 137 138 // SubstanceLookAndFeel.setWidgetVisible(null, true, SubstanceWidgetType.MENU_SEARCH); 139 // SubstanceLookAndFeel.setWidgetVisible(null, false, 140 // SubstanceWidgetType.TITLE_PANE_HEAP_STATUS); 141 142 // enabled LAF decoration instead of native ones 143 JFrame.setDefaultLookAndFeelDecorated(true); 144 JDialog.setDefaultLookAndFeelDecorated(true); 145 146 map = SubstanceLookAndFeel.getAllSkins(); 147 skins = new ArrayList<SkinInfo>(map.values()); 148 149 final LookAndFeelInfo[] lafInfos = new LookAndFeelInfo[skins.size()]; 150 151 // install Substance look and feel 152 for (int i = 0; i < skins.size(); i++) 153 { 154 final SkinInfo skin = skins.get(i); 155 final String className = skin.getClassName(); 156 final String simpleName = ClassUtil.getSimpleClassName(className); 157 final int len = simpleName.length(); 158 // remove the "skin" suffix from simple name 159 final String lafClassName = ClassUtil.getPackageName(className) + ".Substance" 160 + simpleName.substring(0, len - 4) + "LookAndFeel"; 161 162 lafInfos[i] = new LookAndFeelInfo(skin.getDisplayName(), lafClassName); 163 } 164 165 // replace system LAF by substance LAF 166 UIManager.setInstalledLookAndFeels(lafInfos); 167 // set default skin 168 setSkin(GeneralPreferences.getGuiSkin()); 169 170 // get default font size 171 final FontPolicy fontPolicy = SubstanceFontUtilities.getDefaultFontPolicy(); 172 final FontSet fontSet = fontPolicy.getFontSet("Substance", null); 173 174 defaultFontSize = fontSet.getMessageFont().getSize(); 175 currentFontSize = defaultFontSize; 176 177 // set default font size 178 setFontSize(GeneralPreferences.getGuiFontSize()); 179 180 // Define custom PopupFactory : 181 // That fix Ribbon repaint bugs on Medium Weight components for OSX 182 PopupFactory.setSharedInstance(new CustomPopupFactory()); 183 } 184 185 /** 186 * Add skin change listener (automatically add weak listener) 187 */ 188 public static void addSkinChangeListener(SkinChangeListener listener) 189 { 190 SubstanceLookAndFeel.registerSkinChangeListener(listener); 191 } 192 193 /** 194 * Remove skin change listener 195 */ 196 public static void removeSkinChangeListener(SkinChangeListener listener) 197 { 198 SubstanceLookAndFeel.unregisterSkinChangeListener(listener); 199 } 200 201 /** 202 * get current Substance skin 203 */ 204 public static SubstanceSkin getCurrentSkin() 205 { 206 return SubstanceLookAndFeel.getCurrentSkin(); 207 } 208 209 /** 210 * get current Substance skin display name 211 */ 212 public static String getCurrentSkinName() 213 { 214 final SubstanceSkin skin = getCurrentSkin(); 215 216 if (skin != null) 217 return skin.getDisplayName(); 218 219 return null; 220 } 221 222 // /** 223 // * get current Look And Feel 224 // */ 225 // public static LookAndFeel getLookAndFeel() 226 // { 227 // return UIManager.getLookAndFeel(); 228 // } 229 // 230 // /** 231 // * get current Look And Feel name 232 // */ 233 // public static String getLookAndFeelName() 234 // { 235 // final LookAndFeel laf = getLookAndFeel(); 236 // 237 // if (laf instanceof SubstanceLookAndFeel) 238 // return SubstanceLookAndFeel.getCurrentSkin().getDisplayName(); 239 // 240 // return UIManager.getLookAndFeel().getName(); 241 // } 242 243 public static JCommandPopupMenu getLookAndFeelMenu() 244 { 245 final JCommandPopupMenu result = new JCommandPopupMenu(); 246 247 final CommandToggleButtonGroup buttonGroup = new CommandToggleButtonGroup(); 248 final String currentSkinName = getCurrentSkinName(); 249 250 for (SkinInfo skin : skins) 251 { 252 final String skinName = skin.getDisplayName(); 253 254 final JCommandToggleMenuButton button = new JCommandToggleMenuButton(skinName, null); 255 256 button.addActionListener(new ActionListener() 257 { 258 @Override 259 public void actionPerformed(ActionEvent e) 260 { 261 // set LAF 262 LookAndFeelUtil.setSkin(skinName); 263 // and save to preferences 264 GeneralPreferences.setGuiSkin(skinName); 265 } 266 }); 267 268 result.addMenuButton(button); 269 270 // TODO: why this is needed ? ribbon bug ? 271 button.getUI().installUI(button); 272 273 buttonGroup.add(button); 274 buttonGroup.setSelected(button, button.getText().equals(currentSkinName)); 275 } 276 277 return result; 278 } 279 280 /** 281 * get default Look And Feel font size 282 */ 283 public static int getDefaultFontSize() 284 { 285 return defaultFontSize; 286 } 287 288 /** 289 * get default Substance skin 290 */ 291 public static String getDefaultSkin() 292 { 293 // should be Cerulean 294 return skins.get(4).getDisplayName(); 295 } 296 297 // /** 298 // * get default Look And Feel 299 // */ 300 // public static String getDefaultLookAndFeel() 301 // { 302 // return "org.pushingpixels.substance.api.skin.SubstanceCremeCoffeeLookAndFeel"; 303 // // return UIManager.getSystemLookAndFeelClassName(); 304 // } 305 306 /** 307 * Get current LookAndFeel font size 308 */ 309 public static int getFontSize() 310 { 311 return currentFontSize; 312 } 313 314 /** 315 * Set LookAndFeel font size 316 */ 317 public static void setFontSize(final int size) 318 { 319 if (size != currentFontSize) 320 { 321 ThreadUtil.invokeLater(new Runnable() 322 { 323 @Override 324 public void run() 325 { 326 final float scaleFactor = (float) size / (float) defaultFontSize; 327 328 try 329 { 330 // will fail here if look and feel is not a substance one 331 SubstanceLookAndFeel.setFontPolicy(SubstanceFontUtilities.getScaledFontPolicy(scaleFactor)); 332 currentFontSize = size; 333 } 334 catch (Exception e) 335 { 336 System.err.println("LookAndFeelUtil.setFontSize(...) error :"); 337 IcyExceptionHandler.showErrorMessage(e, false); 338 } 339 } 340 }); 341 } 342 } 343 344 /** 345 * Set the specified LookAndFeel skin (skin display name) 346 */ 347 public static void setSkin(final String skinName) 348 { 349 if (!StringUtil.equals(skinName, getCurrentSkinName())) 350 { 351 ThreadUtil.invokeLater(new Runnable() 352 { 353 @Override 354 public void run() 355 { 356 try 357 { 358 SubstanceLookAndFeel.setSkin(map.get(skinName).getClassName()); 359 } 360 catch (Exception e) 361 { 362 System.err.println("LookAndFeelUtil.setSkin(...) error :"); 363 IcyExceptionHandler.showErrorMessage(e, false); 364 } 365 } 366 }); 367 } 368 } 369 370 /** 371 * Return a foreground component color image depending original alpha intensity image 372 */ 373 public static Image getForegroundImageFromAlphaImage(Component c, Image alphaImage) 374 { 375 return paintForegroundImageFromAlphaImage(c, alphaImage, null); 376 } 377 378 /** 379 * Return a background component color image depending original alpha intensity image 380 */ 381 public static Image getBackgroundImageFromAlphaImage(Component c, Image alphaImage) 382 { 383 return paintBackgroundImageFromAlphaImage(c, alphaImage, null); 384 } 385 386 /** 387 * Paint foreground component color in 'out' image<br> 388 * depending original alpha intensity from 'alphaImage' 389 */ 390 public static Image paintForegroundImageFromAlphaImage(Component c, Image alphaImage, Image out) 391 { 392 return ImageUtil.paintColorImageFromAlphaImage(alphaImage, out, getForeground(c)); 393 } 394 395 /** 396 * Paint background component color in 'out' image<br> 397 * depending original alpha intensity from 'alphaImage' 398 */ 399 public static Image paintBackgroundImageFromAlphaImage(Component c, Image alphaImage, Image out) 400 { 401 return ImageUtil.paintColorImageFromAlphaImage(alphaImage, out, getBackground(c)); 402 } 403 404 public static SubstanceColorScheme getActiveColorScheme(DecorationAreaType d) 405 { 406 return getCurrentSkin().getActiveColorScheme(d); 407 } 408 409 /** 410 * @deprecated Use {@link #getActiveColorScheme(DecorationAreaType)} instead. 411 */ 412 @Deprecated 413 public static SubstanceColorScheme getActiveColorSheme(DecorationAreaType d) 414 { 415 return getActiveColorScheme(d); 416 } 417 418 public static SubstanceColorScheme getBackgroundColorScheme(DecorationAreaType d) 419 { 420 final SubstanceSkin skin = getCurrentSkin(); 421 if (skin != null) return skin.getBackgroundColorScheme(d); 422 423 // Arrive only when using designer --> use a random color theme, we don't care 424 return new LightAquaColorScheme(); 425 } 426 427 public static SubstanceColorScheme getDisabledColorScheme(DecorationAreaType d) 428 { 429 final SubstanceSkin skin = getCurrentSkin(); 430 if (skin != null) return skin.getDisabledColorScheme(d); 431 432 // Arrive only when using designer --> use a random color theme, we don't care 433 return new LightAquaColorScheme(); 434 } 435 436 public static SubstanceColorScheme getEnabledColorScheme(DecorationAreaType d) 437 { 438 final SubstanceSkin skin = getCurrentSkin(); 439 if (skin != null) return skin.getEnabledColorScheme(d); 440 441 // Arrive only when using designer --> use a random color theme, we don't care 442 return new LightAquaColorScheme(); 443 } 444 445 public static SubstanceSkin getSkin() 446 { 447 return SubstanceLookAndFeel.getCurrentSkin(); 448 } 449 450 public static SubstanceSkin getSkin(Component c) 451 { 452 return SubstanceLookAndFeel.getCurrentSkin(c); 453 } 454 455 public static DecorationAreaType getDecoration(Component c) 456 { 457 return SubstanceLookAndFeel.getDecorationType(c); 458 } 459 460 public static SubstanceColorScheme getColorScheme(Component c, ComponentState state) 461 { 462 return getSkin().getColorScheme(c, state); 463 } 464 465 public static SubstanceColorScheme getColorScheme(Component c, ColorSchemeAssociationKind kind, ComponentState state) 466 { 467 return getSkin().getColorScheme(c, kind, state); 468 } 469 470 public static SubstanceColorScheme getActiveColorScheme(Component c) 471 { 472 final SubstanceSkin skin = getSkin(c); 473 final DecorationAreaType decoration = getDecoration(c); 474 475 if ((skin != null) && (decoration != null)) 476 return skin.getActiveColorScheme(decoration); 477 478 return null; 479 } 480 481 public static SubstanceColorScheme getActiveColorScheme(Component c, ComponentState state) 482 { 483 return SubstanceColorSchemeUtilities.getActiveColorScheme(c, state); 484 } 485 486 /** 487 * @deprecated Use {@link #getActiveColorScheme(Component)} instead. 488 */ 489 @Deprecated 490 public static SubstanceColorScheme getActiveColorSheme(Component c) 491 { 492 return getActiveColorScheme(c); 493 } 494 495 /** 496 * @deprecated Use {@link #getActiveColorScheme(Component, ComponentState)} instead. 497 */ 498 @Deprecated 499 public static SubstanceColorScheme getActiveColorSheme(Component c, ComponentState state) 500 { 501 return getActiveColorScheme(c, state); 502 } 503 504 public static SubstanceColorScheme getBackgroundColorScheme(Component c) 505 { 506 final SubstanceSkin skin = getSkin(c); 507 final DecorationAreaType decoration = getDecoration(c); 508 509 if ((skin != null) && (decoration != null)) 510 return skin.getBackgroundColorScheme(decoration); 511 512 return null; 513 } 514 515 public static SubstanceColorScheme getDisabledColorScheme(Component c) 516 { 517 final SubstanceSkin skin = getSkin(c); 518 final DecorationAreaType decoration = getDecoration(c); 519 520 if ((skin != null) && (decoration != null)) 521 return skin.getDisabledColorScheme(decoration); 522 523 return null; 524 } 525 526 public static SubstanceColorScheme getEnabledColorScheme(Component c) 527 { 528 final SubstanceSkin skin = getSkin(c); 529 final DecorationAreaType decoration = getDecoration(c); 530 531 if ((skin != null) && (decoration != null)) 532 return skin.getEnabledColorScheme(decoration); 533 534 return null; 535 } 536 537 /** 538 * Return the foreground color for the specified component 539 */ 540 public static Color getForeground(Component c) 541 { 542 SubstanceColorScheme colorScheme; 543 544 if (c == null) 545 colorScheme = getEnabledColorScheme(DecorationAreaType.GENERAL); 546 else if (c.isEnabled()) 547 colorScheme = getEnabledColorScheme(c); 548 else 549 colorScheme = getDisabledColorScheme(c); 550 551 if (colorScheme == null) 552 getEnabledColorScheme(DecorationAreaType.GENERAL); 553 554 if (colorScheme != null) 555 return new ColorUIResource(colorScheme.getForegroundColor()); 556 557 if (c != null) 558 return c.getForeground(); 559 560 return Color.white; 561 } 562 563 /** 564 * Return the selected foreground color for the specified component 565 */ 566 public static Color getSelectedForeground(Component c) 567 { 568 SubstanceColorScheme colorScheme; 569 570 if (c == null) 571 colorScheme = getEnabledColorScheme(DecorationAreaType.GENERAL); 572 else if (c.isEnabled()) 573 colorScheme = getEnabledColorScheme(c); 574 else 575 colorScheme = getDisabledColorScheme(c); 576 577 if (colorScheme == null) 578 getEnabledColorScheme(DecorationAreaType.GENERAL); 579 580 if (colorScheme != null) 581 return new ColorUIResource(colorScheme.getSelectionForegroundColor()); 582 583 if (c != null) 584 return c.getForeground(); 585 586 return Color.gray; 587 } 588 589 /** 590 * Return the background color for the specified component 591 */ 592 public static Color getBackground(Component c) 593 { 594 SubstanceColorScheme colorScheme; 595 596 if (c == null) 597 colorScheme = getEnabledColorScheme(DecorationAreaType.GENERAL); 598 else if (c.isEnabled()) 599 colorScheme = getEnabledColorScheme(c); 600 else 601 colorScheme = getDisabledColorScheme(c); 602 603 if (colorScheme == null) 604 getEnabledColorScheme(DecorationAreaType.GENERAL); 605 606 if (colorScheme != null) 607 return new ColorUIResource(colorScheme.getBackgroundFillColor()); 608 609 if (c != null) 610 return c.getBackground(); 611 612 return Color.lightGray; 613 } 614 615 /** 616 * Return the selected background color for the specified component 617 */ 618 public static Color getSelectedBackground(Component c) 619 { 620 SubstanceColorScheme colorScheme; 621 622 if (c == null) 623 colorScheme = getEnabledColorScheme(DecorationAreaType.GENERAL); 624 else if (c.isEnabled()) 625 colorScheme = getEnabledColorScheme(c); 626 else 627 colorScheme = getDisabledColorScheme(c); 628 629 if (colorScheme == null) 630 getEnabledColorScheme(DecorationAreaType.GENERAL); 631 632 if (colorScheme != null) 633 return new ColorUIResource(colorScheme.getSelectionBackgroundColor()); 634 635 if (c != null) 636 return c.getBackground(); 637 638 return Color.darkGray; 639 } 640 641 public static SubstanceTitlePane getTitlePane(Window window) 642 { 643 return (SubstanceTitlePane) SubstanceLookAndFeel.getTitlePaneComponent(window); 644 } 645 646 public static SubstanceInternalFrameTitlePane getTitlePane(JInternalFrame frame) 647 { 648 final InternalFrameUI ui = frame.getUI(); 649 650 if (ui instanceof SubstanceInternalFrameUI) 651 return ((SubstanceInternalFrameUI) ui).getTitlePane(); 652 653 return null; 654 } 655 656 public static void setTitlePane(JInternalFrame frame, SubstanceInternalFrameTitlePane titlePane) 657 { 658 final InternalFrameUI ui = frame.getUI(); 659 660 if (ui instanceof SubstanceInternalFrameUI) 661 ((SubstanceInternalFrameUI) ui).setNorthPane(titlePane); 662 } 663}