1 /*
   2  * Copyright (c) 2012, 2015, Oracle and/or its affiliates.
   3  * All rights reserved. Use is subject to license terms.
   4  *
   5  * This file is available and licensed under the following license:
   6  *
   7  * Redistribution and use in source and binary forms, with or without
   8  * modification, are permitted provided that the following conditions
   9  * are met:
  10  *
  11  *  - Redistributions of source code must retain the above copyright
  12  *    notice, this list of conditions and the following disclaimer.
  13  *  - Redistributions in binary form must reproduce the above copyright
  14  *    notice, this list of conditions and the following disclaimer in
  15  *    the documentation and/or other materials provided with the distribution.
  16  *  - Neither the name of Oracle Corporation nor the names of its
  17  *    contributors may be used to endorse or promote products derived
  18  *    from this software without specific prior written permission.
  19  *
  20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  31  */
  32 package com.oracle.javafx.scenebuilder.kit.editor;
  33 
  34 import com.oracle.javafx.scenebuilder.kit.util.Deprecation;
  35 import java.io.File;
  36 import java.io.IOException;
  37 import java.util.ArrayList;
  38 import java.util.List;
  39 import java.util.Locale;
  40 import javafx.scene.input.MouseEvent;
  41 
  42 /**
  43  * This class contains static methods that depends on the platform.
  44  *
  45  * @treatAsPrivate
  46  */
  47 public class EditorPlatform {
  48 
  49     private static final String osName = System.getProperty("os.name").toLowerCase(Locale.ROOT); //NOI18N
  50 
  51     /**
  52      * True if current platform is running Linux.
  53      */
  54     public static final boolean IS_LINUX = osName.contains("linux"); //NOI18N
  55 
  56     /**
  57      * True if current platform is running Mac OS X.
  58      */
  59     public static final boolean IS_MAC = osName.contains("mac"); //NOI18N
  60 
  61     /**
  62      * True if current platform is running Windows.
  63      */
  64     public static final boolean IS_WINDOWS = osName.contains("windows"); //NOI18N
  65     
  66     /**
  67      * This URL is where you go when the user takes Scene Builder Help action (shortcut F1)
  68      */
  69     public static final String DOCUMENTATION_URL = "https://docs.oracle.com/javafx/index.html"; //NOI18N
  70 
  71     /**
  72      * Javadoc home (for Inspector and CSS Analyzer properties)
  73      */
  74     public final static String JAVADOC_HOME = "https://docs.oracle.com/javase/8/javafx/api/"; //NOI18N
  75     
  76 
  77     /**
  78      * Themes supported by Scene Builder Kit.
  79      */
  80     public enum Theme {
  81 
  82         MODENA,
  83         MODENA_TOUCH,
  84         MODENA_HIGH_CONTRAST_BLACK_ON_WHITE,
  85         MODENA_HIGH_CONTRAST_WHITE_ON_BLACK,
  86         MODENA_HIGH_CONTRAST_YELLOW_ON_BLACK,
  87         MODENA_TOUCH_HIGH_CONTRAST_BLACK_ON_WHITE,
  88         MODENA_TOUCH_HIGH_CONTRAST_WHITE_ON_BLACK,
  89         MODENA_TOUCH_HIGH_CONTRAST_YELLOW_ON_BLACK,
  90         CASPIAN,
  91         CASPIAN_HIGH_CONTRAST,
  92         CASPIAN_EMBEDDED,
  93         CASPIAN_EMBEDDED_HIGH_CONTRAST,
  94         CASPIAN_EMBEDDED_QVGA,
  95         CASPIAN_EMBEDDED_QVGA_HIGH_CONTRAST
  96     }
  97 
  98     /**
  99      * Returns the url string for locating the specified stylesheet.
 100      * SB uses a set of CSS files aggregating several @import statements (see DTL-6799).
 101      *
 102      * @param theme theme for which string should be computed
 103      * @return string for locating the specified stylesheet.
 104      */
 105     public static String getThemeStylesheetURL(Theme theme) {
 106         final String result;
 107 
 108         switch (theme) {
 109             default:
 110                 result = null;
 111                 break;
 112             case MODENA:
 113                 result = Deprecation.MODENA_STYLESHEET;
 114                 break;
 115             case MODENA_TOUCH:
 116                 result = Deprecation.MODENA_TOUCH_STYLESHEET;
 117                 break;
 118             case MODENA_HIGH_CONTRAST_BLACK_ON_WHITE:
 119                 result = Deprecation.MODENA_HIGHCONTRAST_BLACKONWHITE_STYLESHEET;
 120                 break;
 121             case MODENA_HIGH_CONTRAST_WHITE_ON_BLACK:
 122                 result = Deprecation.MODENA_HIGHCONTRAST_WHITEONBLACK_STYLESHEET;
 123                 break;
 124             case MODENA_HIGH_CONTRAST_YELLOW_ON_BLACK:
 125                 result = Deprecation.MODENA_HIGHCONTRAST_YELLOWONBLACK_STYLESHEET;
 126                 break;
 127             case MODENA_TOUCH_HIGH_CONTRAST_BLACK_ON_WHITE:
 128                 result = Deprecation.MODENA_TOUCH_HIGHCONTRAST_BLACKONWHITE_STYLESHEET;
 129                 break;
 130             case MODENA_TOUCH_HIGH_CONTRAST_WHITE_ON_BLACK:
 131                 result = Deprecation.MODENA_TOUCH_HIGHCONTRAST_WHITEONBLACK_STYLESHEET;
 132                 break;
 133             case MODENA_TOUCH_HIGH_CONTRAST_YELLOW_ON_BLACK:
 134                 result = Deprecation.MODENA_TOUCH_HIGHCONTRAST_YELLOWONBLACK_STYLESHEET;
 135                 break;
 136             case CASPIAN:
 137                 result = Deprecation.CASPIAN_STYLESHEET;
 138                 break;
 139             case CASPIAN_HIGH_CONTRAST:
 140                 result = Deprecation.CASPIAN_HIGHCONTRAST_STYLESHEET;
 141                 break;
 142             case CASPIAN_EMBEDDED:
 143                 result = Deprecation.CASPIAN_EMBEDDED_STYLESHEET;
 144                 break;
 145             case CASPIAN_EMBEDDED_HIGH_CONTRAST:
 146                 result = Deprecation.CASPIAN_EMBEDDED_HIGHCONTRAST_STYLESHEET;
 147                 break;
 148             case CASPIAN_EMBEDDED_QVGA:
 149                 result = Deprecation.CASPIAN_EMBEDDED_QVGA_STYLESHEET;
 150                 break;
 151             case CASPIAN_EMBEDDED_QVGA_HIGH_CONTRAST:
 152                 result = Deprecation.CASPIAN_EMBEDDED_QVGA_HIGHCONTRAST_STYLESHEET;
 153                 break;
 154         }
 155         
 156         if (!theme.equals(Theme.MODENA)) {
 157             assert result != null : "Missing logic for " + theme;
 158         }
 159 
 160         return result;
 161     }
 162     
 163     public static String getPlatformThemeStylesheetURL() {
 164         // Return USER_AGENT css, which is Modena for fx 8.0
 165         return Deprecation.MODENA_STYLESHEET;
 166     }
 167     
 168     public static boolean isModena(Theme theme) {
 169         return theme.toString().startsWith("MODENA");
 170     }
 171     
 172     public static boolean isModenaBlackonwhite(Theme theme) {
 173         return isModena(theme)
 174                 && theme.toString().contains("BLACK_ON_WHITE");
 175     }
 176     
 177     public static boolean isModenaWhiteonblack(Theme theme) {
 178         return isModena(theme)
 179                 && theme.toString().contains("WHITE_ON_BLACK");
 180     }
 181     
 182     public static boolean isModenaYellowonblack(Theme theme) {
 183         return isModena(theme)
 184                 && theme.toString().contains("YELLOW_ON_BLACK");
 185     }
 186     
 187     public static boolean isModenaHighContrast(Theme theme) {
 188         return isModena(theme)
 189                 && theme.toString().contains("HIGH_CONTRAST");
 190     }
 191     
 192     public static boolean isModenaTouch(Theme theme) {
 193         return isModena(theme)
 194                 && theme.toString().contains("TOUCH");
 195     }
 196     
 197     public static boolean isModenaTouchHighContrast(Theme theme) {
 198         return isModena(theme)
 199                 && theme.toString().contains("HIGH_CONTRAST")
 200                 && theme.toString().contains("TOUCH");
 201     }
 202     
 203     public static boolean isCaspian(Theme theme) {
 204         return theme.toString().startsWith("CASPIAN");
 205     }
 206 
 207     /**
 208      * Requests the underlying platform to open a given file. On Linux, it runs
 209      * 'xdg-open'. On Mac, it runs 'open'. On Windows, it runs 'cmd /c start'.
 210      *
 211      * @param path path for the file to be opened
 212      * @throws IOException if an error occurs
 213      */
 214     public static void open(String path) throws IOException {
 215         List<String> args = new ArrayList<>();
 216         if (EditorPlatform.IS_MAC) {
 217             args.add("open"); //NOI18N
 218             args.add(path);
 219         } else if (EditorPlatform.IS_WINDOWS) {
 220             args.add("cmd"); //NOI18N
 221             args.add("/c"); //NOI18N
 222             args.add("start"); //NOI18N
 223 
 224             if (path.contains(" ")) { //NOI18N
 225                 args.add("\"html\""); //NOI18N
 226             }
 227 
 228             args.add(path);
 229         } else if (EditorPlatform.IS_LINUX) {
 230             // xdg-open does fine on Ubuntu, which is a Debian.
 231             // I've no idea how it does with other Linux flavors.
 232             args.add("xdg-open"); //NOI18N
 233             args.add(path);
 234         }
 235 
 236         if (!args.isEmpty()) {
 237             executeDaemon(args, null);
 238         }
 239     }
 240 
 241     /**
 242      * Requests the underlying platform to "reveal" the specified folder. On
 243      * Linux, it runs 'nautilus'. On Mac, it runs 'open'. On Windows, it runs
 244      * 'explorer /select'.
 245      *
 246      * @param filePath path for the folder to be revealed
 247      * @throws IOException if an error occurs
 248      */
 249     public static void revealInFileBrowser(File filePath) throws IOException {
 250         List<String> args = new ArrayList<>();
 251         String path = filePath.toURI().toURL().toExternalForm();
 252         if (EditorPlatform.IS_MAC) {
 253             args.add("open"); //NOI18N
 254             args.add("-R"); //NOI18N
 255             args.add(path);
 256         } else if (EditorPlatform.IS_WINDOWS) {
 257             args.add("explorer"); //NOI18N
 258             args.add("/select," + path); //NOI18N
 259         } else if (EditorPlatform.IS_LINUX) {
 260             // nautilus does fine on Ubuntu, which is a Debian.
 261             // I've no idea how it does with other Linux flavors.
 262             args.add("nautilus"); //NOI18N
 263             // The nautilus that comes with Ubuntu up to 11.04 included doesn't
 264             // take a file path as parameter (you get an error popup), you must
 265             // provide a dir path.
 266             // Starting with Ubuntu 11.10 (the first based on kernel 3.x) a
 267             // file path is well managed.
 268             int osVersionNumerical = Integer.parseInt(System.getProperty("os.version").substring(0, 1)); //NOI18N
 269             if (osVersionNumerical < 3) {
 270                 // Case Ubuntu 10.04 to 11.04: What you provide to nautilus is
 271                 // the name of the directory containing the file you want to see
 272                 // listed. See DTL-5384.
 273                 path = filePath.getAbsoluteFile().getParent();
 274                 if (path == null) {
 275                     path = "."; //NOI18N
 276                 }
 277             }
 278             args.add(path);
 279         } else {
 280             // Not Supported
 281         }
 282 
 283         if (!args.isEmpty()) {
 284             executeDaemon(args, null);
 285         }
 286     }
 287 
 288     /**
 289      * Returns true if the modifier key for continuous selection is down.
 290      *
 291      * @param e mouse event to check (never null)
 292      * @return true if the modifier key for continuous selection is down.
 293      */
 294     public static boolean isContinuousSelectKeyDown(MouseEvent e) {
 295         return e.isShiftDown();
 296     }
 297 
 298     /**
 299      * Returns true if the modifier key for non-continuous selection is down.
 300      *
 301      * @param e mouse event to check (never null).
 302      * @return true if the modifier key for non-continuous selection is down.
 303      */
 304     public static boolean isNonContinousSelectKeyDown(MouseEvent e) {
 305         return IS_MAC ? e.isMetaDown(): e.isControlDown();
 306     }
 307 
 308     /**
 309      * Returns true if the jvm is running with assertions enabled.
 310      *
 311      * @return true if the jvm is running with assertions enabled.
 312      */
 313     public static boolean isAssertionEnabled() {
 314         return EditorPlatform.class.desiredAssertionStatus();
 315     }
 316 
 317     /*
 318      * Private
 319      */
 320     private static void executeDaemon(List<String> cmd, File wDir) throws IOException {
 321         try {
 322             ProcessBuilder builder = new ProcessBuilder(cmd);
 323             builder = builder.directory(wDir);
 324             builder.start();
 325         } catch (RuntimeException ex) {
 326             throw new IOException(ex);
 327         }
 328     }
 329 
 330 }