content: /*****************************************************************
   *   Licensed to the Apache Software Foundation (ASF) under one
   *  or more contributor license agreements.  See the NOTICE file
   *  distributed with this work for additional information
   *  regarding copyright ownership.  The ASF licenses this file
   *  to you under the Apache License, Version 2.0 (the
   *  "License"); you may not use this file except in compliance
   *  with the License.  You may obtain a copy of the License at
   *
   *    https://www.apache.org/licenses/LICENSE-2.0
   *
   *  Unless required by applicable law or agreed to in writing,
   *  software distributed under the License is distributed on an
   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   *  KIND, either express or implied.  See the License for the
   *  specific language governing permissions and limitations
   *  under the License.
   ****************************************************************/
  
  package org.apache.cayenne.modeler.util;
  
  import java.awt.Component;
  import java.awt.Dimension;
  import java.awt.Point;
  import java.awt.Window;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.event.KeyEvent;
  import java.beans.PropertyChangeListener;
  import java.beans.PropertyChangeSupport;
  import java.util.prefs.Preferences;
  
  import javax.swing.JComponent;
  import javax.swing.JDialog;
  import javax.swing.JFrame;
  import javax.swing.JOptionPane;
  import javax.swing.KeyStroke;
  
  import org.apache.cayenne.modeler.Application;
  import org.apache.cayenne.modeler.pref.FSPath;
  import org.apache.cayenne.swing.BoundComponent;
  import org.apache.cayenne.util.Util;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  /**
   * A superclass of CayenneModeler controllers.
   */
  public abstract class CayenneController implements BoundComponent {
  
      private static Logger logObj = LoggerFactory.getLogger(CayenneController.class);
  
      protected CayenneController parent;
      protected Application application;
      protected PropertyChangeSupport propertyChangeSupport;
      
      public CayenneController(){}
      
      public CayenneController(CayenneController parent) {
          this.application = (parent != null) ? parent.getApplication() : null;
          this.parent = parent;
      }
  
      public CayenneController(Application application) {
          this.application = application;
      }
  
      public Application getApplication() {
          return application;
      }
  
      public CayenneController getParent() {
          return parent;
      }
  
      /**
       * Returns the view associated with this Controller.
       */
      public abstract Component getView();
  
      /**
       * Returns last file system directory visited by user for this component. If there is
       * no such directory set up in the preferences, creates a new object, setting its path
       * to the parent last directory or to the user HOME directory.
       */
      public FSPath getLastDirectory() {
          // find start directory in preferences
          FSPath path = (FSPath) application
                  .getCayenneProjectPreferences()
                  .getProjectDetailObject(
                          FSPath.class,
                          getViewPreferences().node("lastDir"));
  
          if (path.getPath() == null) {
  
              String pathString = (getParent() != null) ? getParent()
                      .getLastDirectory()
                      .getPath() : System.getProperty("user.home");
              path.setPath(pathString);
          }
  
          return path;
      }
  
      /**
       * Returns preference for this component view.
       */
      protected Preferences getViewPreferences() {
  
          if (getApplication().getProject() == null) {
              return getApplication().getPreferencesNode(getView().getClass(), "");
          }
  
          Preferences pref = getApplication().getMainPreferenceForProject();
          String pathToProject = pref.absolutePath();
          String path = pathToProject
                  + "/"
                  + getView().getClass().getName().replace(".", "/");
  
          return pref.node(path);
      }
  
      /**
       * Utility method to provide a visual indication an execution error. This
       * implementation logs an error and pops up a dialog window with error message.
       */
      protected void reportError(String title, Throwable th) {
          th = Util.unwindException(th);
          logObj.info("Error in " + getClass().getName(), th);
          th.printStackTrace();
  
          JOptionPane.showMessageDialog(
                  getView(),
                  th.getMessage(),
                  title,
                  JOptionPane.ERROR_MESSAGE);
      }
  
      /**
       * Centers view on parent window.
       */
      protected void centerView() {
          Window parentWindow = parent.getWindow();
  
          Dimension parentSize = parentWindow.getSize();
          Dimension windowSize = getView().getSize();
          Point parentLocation = new Point(0, 0);
          if (parentWindow.isShowing()) {
              parentLocation = parentWindow.getLocationOnScreen();
          }
  
          int x = parentLocation.x + parentSize.width / 2 - windowSize.width / 2;
          int y = parentLocation.y + parentSize.height / 2 - windowSize.height / 2;
  
          getView().setLocation(x, y);
      }
  
      /**
       * If this view or a parent view is a JDialog, makes it closeable on ESC hit. Dialog
       * "defaultCloseOperation" property is taken into account when processing ESC button
       * click.
       */
      protected void makeCloseableOnEscape() {
  
          Window window = getWindow();
          if (!(window instanceof JDialog)) {
              return;
          }
  
          final JDialog dialog = (JDialog) window;
  
          KeyStroke escReleased = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0, true);
          ActionListener closeAction = new ActionListener() {
  
              public void actionPerformed(ActionEvent e) {
                  if (dialog.isVisible()) {
                      switch (dialog.getDefaultCloseOperation()) {
                          case JDialog.HIDE_ON_CLOSE:
                              dialog.setVisible(false);
                              break;
                          case JDialog.DISPOSE_ON_CLOSE:
                              dialog.setVisible(false);
                              dialog.dispose();
                              break;
                          case JDialog.DO_NOTHING_ON_CLOSE:
                          default:
                              break;
                      }
                  }
              }
          };
          dialog.getRootPane().registerKeyboardAction(
                  closeAction,
                  escReleased,
                  JComponent.WHEN_IN_FOCUSED_WINDOW);
      }
  
      /**
       * Finds a Window in the view hierarchy.
       */
      public Window getWindow() {
          Component view = getView();
          while (view != null) {
              if (view instanceof Window) {
                  return (Window) view;
              }
  
              view = view.getParent();
          }
  
          return null;
      }
  
      /**
       * Finds a JFrame in the view hierarchy.
       */
      public JFrame getFrame() {
          Component view = getView();
          while (view != null) {
              if (view instanceof JFrame) {
                  return (JFrame) view;
              }
  
              view = view.getParent();
          }
  
          return null;
      }
  
      /**
       * Fires property change event. Exists for the benefit of subclasses.
       */
      protected void firePropertyChange(
              String propertyName,
              Object oldValue,
              Object newValue) {
          if (propertyChangeSupport != null) {
              propertyChangeSupport.firePropertyChange(propertyName, oldValue, newValue);
          }
      }
  
      /**
       * Adds a listener for property change events.
       */
      public void addPropertyChangeListener(
              String expression,
              PropertyChangeListener listener) {
  
          if (propertyChangeSupport == null) {
              propertyChangeSupport = new PropertyChangeSupport(this);
          }
  
          propertyChangeSupport.addPropertyChangeListener(expression, listener);
      }
  
      /**
       * Default implementation is a noop. Override to handle parent binding updates.
       */
      public void bindingUpdated(String expression, Object newValue) {
          // do nothing...
      }
  }
  