/***************************************************************** * 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 * * http://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; import org.apache.cayenne.modeler.action.ExitAction; import org.apache.cayenne.modeler.action.OpenProjectAction; import org.apache.cayenne.modeler.dialog.validator.ValidatorDialog; import org.apache.cayenne.modeler.editor.EditorView; import org.apache.cayenne.modeler.init.platform.PlatformInitializer; import org.apache.cayenne.modeler.pref.ComponentGeometry; import org.apache.cayenne.modeler.pref.FSPath; import org.apache.cayenne.modeler.util.CayenneController; import org.apache.cayenne.modeler.util.FileFilters; import org.apache.cayenne.modeler.util.state.ProjectStateUtil; import org.apache.cayenne.project.Project; import org.apache.cayenne.project.validation.ProjectValidator; import org.apache.cayenne.validation.ValidationFailure; import org.apache.cayenne.validation.ValidationResult; import javax.swing.WindowConstants; import javax.swing.filechooser.FileFilter; import java.awt.Component; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetAdapter; import java.awt.dnd.DropTargetDropEvent; import java.awt.event.ActionEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.prefs.BackingStoreException; import java.util.prefs.Preferences; /** * Controller of the main application frame. */ public class CayenneModelerController extends CayenneController { private static final ProjectStateUtil PROJECT_STATE_UTIL = new ProjectStateUtil(); private ProjectController projectController; protected CayenneModelerFrame frame; private EditorView editorView; public CayenneModelerController(){} public CayenneModelerController(Application application) { super(application); this.frame = new CayenneModelerFrame(application.getActionManager()); application.getInjector().getInstance(PlatformInitializer.class).setupMenus(frame); this.projectController = new ProjectController(this); } @Override public Component getView() { return frame; } public ProjectController getProjectController() { return projectController; } public FSPath getLastEOModelDirectory() { // find start directory in preferences FSPath path = (FSPath) application .getCayenneProjectPreferences() .getProjectDetailObject(FSPath.class, getViewPreferences().node("lastEOMDir")); if (path.getPath() == null) { path.setPath(getLastDirectory().getPath()); } return path; } protected void initBindings() { frame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { PROJECT_STATE_UTIL.saveLastState(projectController); getApplication().getActionManager().getAction(ExitAction.class).exit(); } }); // Register a hook to save the window position when quit via the app menu. // This is in Mac OSX only. if (System.getProperty("os.name").startsWith("Mac OS")) { Runnable runner = new Runnable() { @Override public void run() { PROJECT_STATE_UTIL.saveLastState(projectController); } }; Runtime.getRuntime().addShutdownHook(new Thread(runner, "Window Prefs Hook")); } new DropTarget(frame, new DropTargetAdapter() { @Override public void drop(DropTargetDropEvent dtde) { dtde.acceptDrop(dtde.getDropAction()); Transferable transferable = dtde.getTransferable(); dtde.dropComplete(processDropAction(transferable)); } }); ComponentGeometry geometry = new ComponentGeometry(frame.getClass(), null); geometry.bind(frame, 1200, 720, 0); } @SuppressWarnings("unchecked") private boolean processDropAction(Transferable transferable) { List<File> fileList; try { fileList = (List<File>) transferable.getTransferData(DataFlavor.javaFileListFlavor); } catch (Exception e) { return false; } if (fileList != null) { File transferFile = fileList.get(0); if (transferFile.isFile()) { FileFilter filter = FileFilters.getApplicationFilter(); if (filter.accept(transferFile)) { ActionEvent e = new ActionEvent(transferFile, ActionEvent.ACTION_PERFORMED, "OpenProject"); Application.getInstance().getActionManager().getAction(OpenProjectAction.class).actionPerformed(e); return true; } } } return false; } public void startupAction() { initBindings(); frame.setVisible(true); } public void projectModifiedAction() { String title = (projectController.getProject().getConfigurationResource() == null) ? "[New]" : projectController.getProject().getConfigurationResource().getURL().getPath(); frame.setTitle("* - " + ModelerConstants.TITLE + " - " + title); } public void projectSavedAction() { projectController.setDirty(false); projectController.updateProjectControllerPreferences(); updateStatus("Project saved..."); frame.setTitle(ModelerConstants.TITLE + " - " + projectController.getProject().getConfigurationResource().getURL().getPath()); } /** * Action method invoked on project closing. */ public void projectClosedAction() { PROJECT_STATE_UTIL.saveLastState(projectController); // --- update view frame.setView(null); // repaint is needed, since sometimes there is a // trace from menu left on the screen frame.repaint(); frame.setTitle(ModelerConstants.TITLE); projectController.setProject(null); projectController.reset(); application.getActionManager().projectClosed(); updateStatus("Project Closed..."); } /** * Handles project opening control. Updates main frame, then delegates control to * child controllers. */ public void projectOpenedAction(Project project) { projectController.setProject(project); editorView = new EditorView(projectController); frame.setView(editorView); projectController.projectOpened(); application.getActionManager().projectOpened(); // do status update AFTER the project is actually opened... if (project.getConfigurationResource() == null) { updateStatus("New project created..."); frame.setTitle(ModelerConstants.TITLE + "- [New]"); } else { updateStatus("Project opened..."); frame.setTitle(ModelerConstants.TITLE + " - " + project.getConfigurationResource().getURL().getPath()); } // update preferences if (project.getConfigurationResource() != null) { getLastDirectory().setDirectory(new File(project.getConfigurationResource().getURL().getPath())); frame.fireRecentFileListChanged(); } PROJECT_STATE_UTIL.fireLastState(projectController); // for validation purposes combine load failures with post-load validation (not // sure if that'll cause duplicate messages?). List<ValidationFailure> allFailures = new ArrayList<>(); Collection<ValidationFailure> loadFailures = project.getConfigurationTree().getLoadFailures(); if (!loadFailures.isEmpty()) { // mark project as unsaved project.setModified(true); projectController.setDirty(true); allFailures.addAll(loadFailures); } ProjectValidator projectValidator = getApplication().getInjector().getInstance(ProjectValidator.class); ValidationResult validationResult = projectValidator.validate(project.getRootNode()); allFailures.addAll(validationResult.getFailures()); if (!allFailures.isEmpty()) { ValidatorDialog.showDialog(frame, validationResult.getFailures()); } } public EditorView getEditorView() { return editorView; } /** Adds path to the list of last opened projects in preferences. */ public void addToLastProjListAction(File file) { Preferences prefLastProjFiles = ModelerPreferences.getLastProjFilesPref(); List<File> arr = ModelerPreferences.getLastProjFiles(); // Add proj path to the preferences // Prevent duplicate entries. if (arr.contains(file)) { arr.remove(file); } arr.add(0, file); while (arr.size() > ModelerPreferences.LAST_PROJ_FILES_SIZE) { arr.remove(arr.size() - 1); } try { prefLastProjFiles.clear(); } catch (BackingStoreException ignored) { // ignore exception } int size = arr.size(); for (int i = 0; i < size; i++) { prefLastProjFiles.put(String.valueOf(i), arr.get(i).getAbsolutePath()); } } /** * Performs status bar update with a message. Message will dissappear in 6 seconds. */ public void updateStatus(String message) { frame.getStatus().setText(message); // start message cleanup thread that would remove the message after X seconds if (message != null && message.trim().length() > 0) { Thread cleanup = new ExpireThread(message, 6); cleanup.start(); } } class ExpireThread extends Thread { int seconds; protected String message; ExpireThread(String message, int seconds) { this.seconds = seconds; this.message = message; } @Override public void run() { try { sleep(seconds * 1000); } catch (InterruptedException e) { // ignore exception } if (message.equals(frame.getStatus().getText())) { updateStatus(null); } } } public void changePathInLastProjListAction(File oldFile, File newFile) { Preferences frefLastProjFiles = ModelerPreferences.getLastProjFilesPref(); List<File> arr = ModelerPreferences.getLastProjFiles(); // Add proj path to the preferences arr.remove(oldFile); arr.remove(newFile); arr.add(0, newFile); while (arr.size() > ModelerPreferences.LAST_PROJ_FILES_SIZE) { arr.remove(arr.size() - 1); } try { frefLastProjFiles.clear(); } catch (BackingStoreException e) { // ignore exception } int size = arr.size(); for (int i = 0; i < size; i++) { frefLastProjFiles.put(String.valueOf(i), arr.get(i).getAbsolutePath()); } getLastDirectory().setDirectory(newFile); frame.fireRecentFileListChanged(); } }