1 /*
   2  * Copyright (c) 2012, 2014, 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.app.preferences;
  33 
  34 import com.oracle.javafx.scenebuilder.app.SceneBuilderApp.ToolTheme;
  35 import com.oracle.javafx.scenebuilder.app.i18n.I18N;
  36 
  37 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.ALIGNMENT_GUIDES_COLOR;
  38 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.BACKGROUND_IMAGE;
  39 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.CSS_TABLE_COLUMNS_ORDERING_REVERSED;
  40 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.ROOT_CONTAINER_HEIGHT;
  41 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.ROOT_CONTAINER_WIDTH;
  42 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.HIERARCHY_DISPLAY_OPTION;
  43 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.LIBRARY_DISPLAY_OPTION;
  44 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.PARENT_RING_COLOR;
  45 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.RECENT_ITEMS;
  46 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.RECENT_ITEMS_SIZE;
  47 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesController.TOOL_THEME;
  48 
  49 import com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal.BackgroundImage;
  50 import com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal.CSSAnalyzerColumnsOrder;
  51 
  52 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal.DEFAULT_ALIGNMENT_GUIDES_COLOR;
  53 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal.DEFAULT_BACKGROUND_IMAGE;
  54 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal.DEFAULT_HIERARCHY_DISPLAY_OPTION;
  55 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal.DEFAULT_LIBRARY_DISPLAY_OPTION;
  56 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal.DEFAULT_PARENT_RING_COLOR;
  57 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal.DEFAULT_RECENT_ITEMS_SIZE;
  58 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal.DEFAULT_ROOT_CONTAINER_HEIGHT;
  59 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal.DEFAULT_ROOT_CONTAINER_WIDTH;
  60 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal.DEFAULT_TOOL_THEME;
  61 import static com.oracle.javafx.scenebuilder.app.preferences.PreferencesRecordGlobal.recentItemsSizes;
  62 
  63 import com.oracle.javafx.scenebuilder.kit.editor.panel.hierarchy.AbstractHierarchyPanelController.DisplayOption;
  64 import com.oracle.javafx.scenebuilder.kit.editor.panel.inspector.editors.DoubleField;
  65 import com.oracle.javafx.scenebuilder.kit.editor.panel.library.LibraryPanelController.DISPLAY_MODE;
  66 import com.oracle.javafx.scenebuilder.kit.editor.panel.util.AbstractFxmlWindowController;
  67 import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.PaintPicker;
  68 import com.oracle.javafx.scenebuilder.kit.util.control.paintpicker.PaintPicker.Mode;
  69 
  70 import java.util.Arrays;
  71 
  72 import javafx.beans.value.ChangeListener;
  73 import javafx.beans.value.ObservableValue;
  74 import javafx.event.ActionEvent;
  75 import javafx.fxml.FXML;
  76 import javafx.scene.control.ChoiceBox;
  77 import javafx.scene.control.CustomMenuItem;
  78 import javafx.scene.control.MenuButton;
  79 import javafx.scene.paint.Color;
  80 import javafx.scene.paint.Paint;
  81 import javafx.scene.shape.Rectangle;
  82 import javafx.stage.WindowEvent;
  83 
  84 /**
  85  * Preferences window controller.
  86  */
  87 public class PreferencesWindowController extends AbstractFxmlWindowController {
  88 
  89     @FXML
  90     private DoubleField rootContainerHeight;
  91     @FXML
  92     private DoubleField rootContainerWidth;
  93     @FXML
  94     private ChoiceBox<BackgroundImage> backgroundImage;
  95     @FXML
  96     private ChoiceBox<ToolTheme> toolTheme;
  97     @FXML
  98     private ChoiceBox<DISPLAY_MODE> libraryDisplayOption;
  99     @FXML
 100     private ChoiceBox<DisplayOption> hierarchyDisplayOption;
 101     @FXML
 102     private ChoiceBox<CSSAnalyzerColumnsOrder> cssAnalyzerColumnsOrder;
 103     @FXML
 104     private MenuButton alignmentGuidesButton;
 105     @FXML
 106     private MenuButton parentRingButton;
 107     @FXML
 108     private CustomMenuItem alignmentGuidesMenuItem;
 109     @FXML
 110     private CustomMenuItem parentRingMenuItem;
 111     @FXML
 112     private Rectangle alignmentGuidesGraphic;
 113     @FXML
 114     private Rectangle parentRingGraphic;
 115     @FXML
 116     private ChoiceBox<Integer> recentItemsSize;
 117 
 118     private PaintPicker alignmentColorPicker;
 119     private PaintPicker parentRingColorPicker;
 120 
 121     public PreferencesWindowController() {
 122         super(PreferencesWindowController.class.getResource("Preferences.fxml"), //NOI18N
 123                 I18N.getBundle());
 124     }
 125 
 126     /*
 127      * AbstractModalDialog
 128      */
 129     @Override
 130     protected void controllerDidLoadFxml() {
 131         super.controllerDidLoadFxml();
 132 
 133         final PreferencesController preferencesController
 134                 = PreferencesController.getSingleton();
 135         final PreferencesRecordGlobal recordGlobal
 136                 = preferencesController.getRecordGlobal();
 137 
 138         // Root container size
 139         rootContainerHeight.setText(String.valueOf(recordGlobal.getRootContainerHeight()));
 140         rootContainerHeight.setOnAction(t -> {
 141             final String value = rootContainerHeight.getText();
 142             recordGlobal.setRootContainerHeight(Double.valueOf(value));
 143             rootContainerHeight.selectAll();
 144             // Update preferences
 145             recordGlobal.writeToJavaPreferences(ROOT_CONTAINER_HEIGHT);
 146             // Update UI
 147             recordGlobal.refreshRootContainerHeight();
 148         });
 149         rootContainerWidth.setText(String.valueOf(recordGlobal.getRootContainerWidth()));
 150         rootContainerWidth.setOnAction(t -> {
 151             final String value = rootContainerWidth.getText();
 152             recordGlobal.setRootContainerWidth(Double.valueOf(value));
 153             rootContainerWidth.selectAll();
 154             // Update preferences
 155             recordGlobal.writeToJavaPreferences(ROOT_CONTAINER_WIDTH);
 156             // Update UI
 157             recordGlobal.refreshRootContainerWidth();
 158         });
 159 
 160         // Background image
 161         backgroundImage.getItems().setAll(Arrays.asList(BackgroundImage.class.getEnumConstants()));
 162         backgroundImage.setValue(recordGlobal.getBackgroundImage());
 163         backgroundImage.getSelectionModel().selectedItemProperty().addListener(new BackgroundImageListener());
 164 
 165         // PaintPicker delegate shared by alignmentColorPicker and parentRingColorPicker
 166         final PaintPicker.Delegate delegate = new PaintPickerDelegate();
 167 
 168         // Alignment guides color
 169         final Color alignmentColor = recordGlobal.getAlignmentGuidesColor();
 170         alignmentColorPicker = new PaintPicker(delegate, Mode.COLOR);
 171         alignmentGuidesGraphic.setFill(alignmentColor);
 172         alignmentGuidesMenuItem.setContent(alignmentColorPicker);
 173         alignmentColorPicker.setPaintProperty(alignmentColor);
 174         alignmentColorPicker.paintProperty().addListener(
 175                 new AlignmentGuidesColorListener(alignmentGuidesGraphic));
 176 
 177         // Parent ring color
 178         final Color parentRingColor = recordGlobal.getParentRingColor();
 179         parentRingColorPicker = new PaintPicker(delegate, Mode.COLOR);
 180         parentRingGraphic.setFill(parentRingColor);
 181         parentRingMenuItem.setContent(parentRingColorPicker);
 182         parentRingColorPicker.setPaintProperty(parentRingColor);
 183         parentRingColorPicker.paintProperty().addListener(
 184                 new ParentRingColorListener(parentRingGraphic));
 185 
 186         // Tool theme
 187         toolTheme.getItems().setAll(Arrays.asList(ToolTheme.class.getEnumConstants()));
 188         toolTheme.setValue(recordGlobal.getToolTheme());
 189         toolTheme.getSelectionModel().selectedItemProperty().addListener(new ToolThemeListener());
 190 
 191         // Library view option
 192         final DISPLAY_MODE availableDisplayMode[] = new DISPLAY_MODE[]{
 193             DISPLAY_MODE.LIST, DISPLAY_MODE.SECTIONS};
 194         libraryDisplayOption.getItems().setAll(Arrays.asList(availableDisplayMode));
 195         libraryDisplayOption.setValue(recordGlobal.getLibraryDisplayOption());
 196         libraryDisplayOption.getSelectionModel().selectedItemProperty().addListener(new LibraryOptionListener());
 197 
 198         // Hierarchy display option
 199         hierarchyDisplayOption.getItems().setAll(Arrays.asList(DisplayOption.class.getEnumConstants()));
 200         hierarchyDisplayOption.setValue(recordGlobal.getHierarchyDisplayOption());
 201         hierarchyDisplayOption.getSelectionModel().selectedItemProperty().addListener(new DisplayOptionListener());
 202 
 203         // CSS analyzer column order
 204         cssAnalyzerColumnsOrder.getItems().setAll(Arrays.asList(CSSAnalyzerColumnsOrder.class.getEnumConstants()));
 205         cssAnalyzerColumnsOrder.setValue(recordGlobal.getCSSAnalyzerColumnsOrder());
 206         cssAnalyzerColumnsOrder.getSelectionModel().selectedItemProperty().addListener(new ColumnOrderListener());
 207 
 208         // Number of open recent items
 209         recentItemsSize.getItems().setAll(recentItemsSizes);
 210         recentItemsSize.setValue(recordGlobal.getRecentItemsSize());
 211         recentItemsSize.getSelectionModel().selectedItemProperty().addListener(new RecentItemsSizeListener());
 212     }
 213 
 214     /*
 215      * AbstractWindowController
 216      */
 217     @Override
 218     protected void controllerDidCreateStage() {
 219         assert getRoot() != null;
 220         assert getRoot().getScene() != null;
 221         assert getRoot().getScene().getWindow() != null;
 222 
 223         getStage().setTitle(I18N.getString("prefs.title"));
 224         getStage().setResizable(false);
 225     }
 226 
 227     @Override
 228     public void onCloseRequest(WindowEvent event) {
 229         super.closeWindow();
 230     }
 231 
 232     @FXML
 233     void resetToDefaultAction(ActionEvent event) {
 234         final PreferencesController preferencesController
 235                 = PreferencesController.getSingleton();
 236         final PreferencesRecordGlobal recordGlobal
 237                 = preferencesController.getRecordGlobal();
 238 
 239         // Root container size
 240         rootContainerHeight.setText(String.valueOf(DEFAULT_ROOT_CONTAINER_HEIGHT));
 241         rootContainerHeight.getOnAction().handle(new ActionEvent());
 242         rootContainerWidth.setText(String.valueOf(DEFAULT_ROOT_CONTAINER_WIDTH));
 243         rootContainerWidth.getOnAction().handle(new ActionEvent());
 244 
 245         // Background image
 246         backgroundImage.setValue(DEFAULT_BACKGROUND_IMAGE);
 247 
 248         // Alignment guides color
 249         alignmentColorPicker.setPaintProperty(DEFAULT_ALIGNMENT_GUIDES_COLOR);
 250 
 251         // Parent ring color
 252         parentRingColorPicker.setPaintProperty(DEFAULT_PARENT_RING_COLOR);
 253 
 254         // Tool theme
 255         toolTheme.setValue(DEFAULT_TOOL_THEME);
 256 
 257         // Library view option
 258         libraryDisplayOption.setValue(DEFAULT_LIBRARY_DISPLAY_OPTION);
 259 
 260         // Hierarchy display option
 261         hierarchyDisplayOption.setValue(DEFAULT_HIERARCHY_DISPLAY_OPTION);
 262 
 263         // CSS analyzer column order
 264         cssAnalyzerColumnsOrder.setValue(recordGlobal.getDefaultCSSAnalyzerColumnsOrder());
 265 
 266         // Number of open recent items
 267         recentItemsSize.setValue(DEFAULT_RECENT_ITEMS_SIZE);
 268     }
 269 
 270     /**
 271      * *************************************************************************
 272      * Static inner class
 273      * *************************************************************************
 274      */
 275     private static class BackgroundImageListener implements ChangeListener<BackgroundImage> {
 276 
 277         @Override
 278         public void changed(ObservableValue<? extends BackgroundImage> observable,
 279                 BackgroundImage oldValue, BackgroundImage newValue) {
 280             final PreferencesController preferencesController
 281                     = PreferencesController.getSingleton();
 282             final PreferencesRecordGlobal recordGlobal
 283                     = preferencesController.getRecordGlobal();
 284             // Update preferences
 285             recordGlobal.setBackgroundImage(newValue);
 286             recordGlobal.writeToJavaPreferences(BACKGROUND_IMAGE);
 287             // Update UI
 288             recordGlobal.refreshBackgroundImage();
 289         }
 290     }
 291 
 292     private static class ToolThemeListener implements ChangeListener<ToolTheme> {
 293 
 294         @Override
 295         public void changed(ObservableValue<? extends ToolTheme> observable,
 296                 ToolTheme oldValue, ToolTheme newValue) {
 297             final PreferencesController preferencesController
 298                     = PreferencesController.getSingleton();
 299             final PreferencesRecordGlobal recordGlobal
 300                     = preferencesController.getRecordGlobal();
 301             // Update preferences
 302             recordGlobal.setToolTheme(newValue);
 303             recordGlobal.writeToJavaPreferences(TOOL_THEME);
 304             // Update UI
 305             recordGlobal.refreshToolTheme();
 306         }
 307     }
 308 
 309     private static class LibraryOptionListener implements ChangeListener<DISPLAY_MODE> {
 310 
 311         @Override
 312         public void changed(ObservableValue<? extends DISPLAY_MODE> ov, DISPLAY_MODE oldValue, DISPLAY_MODE newValue) {
 313             final PreferencesController preferencesController
 314                     = PreferencesController.getSingleton();
 315             final PreferencesRecordGlobal recordGlobal
 316                     = preferencesController.getRecordGlobal();
 317             // Update preferences
 318             recordGlobal.setLibraryDisplayOption(newValue);
 319             recordGlobal.writeToJavaPreferences(LIBRARY_DISPLAY_OPTION);
 320             // Update UI
 321             recordGlobal.refreshLibraryDisplayOption();
 322         }
 323     }
 324 
 325     private static class DisplayOptionListener implements ChangeListener<DisplayOption> {
 326 
 327         @Override
 328         public void changed(ObservableValue<? extends DisplayOption> observable,
 329                 DisplayOption oldValue, DisplayOption newValue) {
 330             final PreferencesController preferencesController
 331                     = PreferencesController.getSingleton();
 332             final PreferencesRecordGlobal recordGlobal
 333                     = preferencesController.getRecordGlobal();
 334             // Update preferences
 335             recordGlobal.setHierarchyDisplayOption(newValue);
 336             recordGlobal.writeToJavaPreferences(HIERARCHY_DISPLAY_OPTION);
 337             // Update UI
 338             recordGlobal.refreshHierarchyDisplayOption();
 339         }
 340     }
 341 
 342     private static class ColumnOrderListener implements ChangeListener<CSSAnalyzerColumnsOrder> {
 343 
 344         @Override
 345         public void changed(ObservableValue<? extends CSSAnalyzerColumnsOrder> observable,
 346                 CSSAnalyzerColumnsOrder oldValue, CSSAnalyzerColumnsOrder newValue) {
 347             final PreferencesController preferencesController
 348                     = PreferencesController.getSingleton();
 349             final PreferencesRecordGlobal recordGlobal
 350                     = preferencesController.getRecordGlobal();
 351             // Update preferences
 352             recordGlobal.setCSSAnalyzerColumnsOrder(newValue);
 353             recordGlobal.writeToJavaPreferences(CSS_TABLE_COLUMNS_ORDERING_REVERSED);
 354             // Update UI
 355             recordGlobal.refreshCSSAnalyzerColumnsOrder();
 356         }
 357     }
 358 
 359     private static class RecentItemsSizeListener implements ChangeListener<Integer> {
 360 
 361         @Override
 362         public void changed(ObservableValue<? extends Integer> observable,
 363                 Integer oldValue, Integer newValue) {
 364             final PreferencesController preferencesController
 365                     = PreferencesController.getSingleton();
 366             final PreferencesRecordGlobal recordGlobal
 367                     = preferencesController.getRecordGlobal();
 368             // Update preferences
 369             recordGlobal.setRecentItemsSize(newValue);
 370             recordGlobal.writeToJavaPreferences(RECENT_ITEMS_SIZE);
 371             recordGlobal.writeToJavaPreferences(RECENT_ITEMS);
 372         }
 373     }
 374 
 375     private static class AlignmentGuidesColorListener implements ChangeListener<Paint> {
 376 
 377         private final Rectangle graphic;
 378 
 379         public AlignmentGuidesColorListener(Rectangle graphic) {
 380             this.graphic = graphic;
 381         }
 382 
 383         @Override
 384         public void changed(ObservableValue<? extends Paint> ov, Paint oldValue, Paint newValue) {
 385             assert newValue instanceof Color;
 386             final PreferencesController preferencesController
 387                     = PreferencesController.getSingleton();
 388             final PreferencesRecordGlobal recordGlobal
 389                     = preferencesController.getRecordGlobal();
 390             // Update preferences
 391             recordGlobal.setAlignmentGuidesColor((Color) newValue);
 392             recordGlobal.writeToJavaPreferences(ALIGNMENT_GUIDES_COLOR);
 393             // Update UI
 394             recordGlobal.refreshAlignmentGuidesColor();
 395             graphic.setFill(newValue);
 396         }
 397     }
 398 
 399     private static class ParentRingColorListener implements ChangeListener<Paint> {
 400 
 401         private final Rectangle graphic;
 402 
 403         public ParentRingColorListener(Rectangle graphic) {
 404             this.graphic = graphic;
 405         }
 406 
 407         @Override
 408         public void changed(ObservableValue<? extends Paint> ov, Paint oldValue, Paint newValue) {
 409             assert newValue instanceof Color;
 410             final PreferencesController preferencesController
 411                     = PreferencesController.getSingleton();
 412             final PreferencesRecordGlobal recordGlobal
 413                     = preferencesController.getRecordGlobal();
 414             // Update preferences
 415             recordGlobal.setParentRingColor((Color) newValue);
 416             recordGlobal.writeToJavaPreferences(PARENT_RING_COLOR);
 417             // Update UI
 418             recordGlobal.refreshParentRingColor();
 419             graphic.setFill(newValue);
 420         }
 421     }
 422 
 423     private static class PaintPickerDelegate implements PaintPicker.Delegate {
 424 
 425         @Override
 426         public void handleError(String warningKey, Object... arguments) {
 427             // Log a warning in message bar
 428         }
 429     }
 430 }