/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenFlexo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.dgmodule.controller; import java.awt.Color; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.util.Collection; import java.util.Hashtable; import java.util.Map; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import org.openflexo.FlexoCst; import org.openflexo.components.AskParametersDialog; import org.openflexo.dg.ProjectDocGenerator; import org.openflexo.dg.docx.ProjectDocDocxGenerator; import org.openflexo.dg.html.ProjectDocHTMLGenerator; import org.openflexo.dgmodule.DGPreferences; import org.openflexo.dgmodule.controller.action.DGControllerActionInitializer; import org.openflexo.dgmodule.controller.browser.DGBrowser; import org.openflexo.dgmodule.menu.DGMenuBar; import org.openflexo.dgmodule.view.DGBrowserView; import org.openflexo.dgmodule.view.DGFileVersionPopup; import org.openflexo.dgmodule.view.DGMainPane; import org.openflexo.doceditor.controller.DEController; import org.openflexo.doceditor.controller.DESelectionManager; import org.openflexo.doceditor.controller.TOCPerspective; import org.openflexo.foundation.DataModification; import org.openflexo.foundation.FlexoEditor; import org.openflexo.foundation.FlexoModelObject; import org.openflexo.foundation.FlexoObservable; import org.openflexo.foundation.FlexoObserver; import org.openflexo.foundation.GraphicalFlexoObserver; import org.openflexo.foundation.ObjectDeleted; import org.openflexo.foundation.cg.CGFile; import org.openflexo.foundation.cg.CGObject; import org.openflexo.foundation.cg.DGRepository; import org.openflexo.foundation.cg.GenerationRepository; import org.openflexo.foundation.cg.action.AbstractGCAction; import org.openflexo.foundation.gen.GenerationProgressNotification; import org.openflexo.foundation.param.CheckboxParameter; import org.openflexo.foundation.param.RadioButtonListParameter; import org.openflexo.foundation.rm.FlexoCopiedResource; import org.openflexo.foundation.rm.FlexoGeneratedResource; import org.openflexo.foundation.rm.FlexoProject; import org.openflexo.foundation.rm.ResourceUpdateHandler.GeneratedResourceModifiedHook; import org.openflexo.foundation.rm.cg.CGRepositoryFileResource; import org.openflexo.foundation.rm.cg.ContentSource; import org.openflexo.foundation.toc.TOCEntry; import org.openflexo.generator.AbstractProjectGenerator; import org.openflexo.generator.action.AcceptDiskUpdate; import org.openflexo.generator.action.GCAction.ProjectGeneratorFactory; import org.openflexo.generator.exception.GenerationException; import org.openflexo.icon.GeneratorIconLibrary; import org.openflexo.icon.IconLibrary; import org.openflexo.icon.UtilsIconLibrary; import org.openflexo.jedit.JEditTextArea; import org.openflexo.jedit.JEditTextArea.CursorPositionListener; import org.openflexo.localization.FlexoLocalization; import org.openflexo.module.FlexoModule; import org.openflexo.module.GeneratedResourceModifiedChoice; import org.openflexo.toolbox.FileCst; import org.openflexo.toolbox.FileResource; import org.openflexo.view.FlexoMainPane; import org.openflexo.view.menu.FlexoMenuBar; /** * Controller for Generator module * * @author gpolet */ public class DGController extends DEController implements FlexoObserver, ProjectGeneratorFactory { protected static final Logger logger = Logger.getLogger(DGController.class.getPackage().getName()); public DocGeneratorPerspective DOCUMENTATION_GENERATOR_PERSPECTIVE; public TemplatesPerspective TEMPLATES_PERSPECTIVE; public VersionningPerspective VERSIONNING_PERSPECTIVE; @Override public boolean useNewInspectorScheme() { return true; } @Override public boolean useOldInspectorScheme() { return false; } public static final FileResource flexoTemplatesDirectory = new FileResource(FileCst.GENERATOR_TEMPLATES_REL_PATH); protected Map<DGRepository, ProjectDocGenerator> _projectGenerators; protected DGFooter _footer; private DGBrowserView dgBrowserView; /** * Default constructor * * @throws Exception */ public DGController(FlexoModule module) { super(module); } private Map<DGRepository, ProjectDocGenerator> getProjectGeneratorsMap() { if (_projectGenerators == null) { _projectGenerators = new Hashtable<DGRepository, ProjectDocGenerator>(); } return _projectGenerators; } @Override protected void initializePerspectives() { _CGGeneratedResourceModifiedHook = new DGGeneratedResourceModifiedHook(); browser = new DGBrowser(this); dgBrowserView = new DGBrowserView(this, browser); createFooter(); addToPerspectives(DOCUMENTATION_GENERATOR_PERSPECTIVE = new DocGeneratorPerspective(this)); addToPerspectives(TOC_PERSPECTIVE = new TOCPerspective(this)); addToPerspectives(TEMPLATES_PERSPECTIVE = new TemplatesPerspective(this)); addToPerspectives(VERSIONNING_PERSPECTIVE = new VersionningPerspective(this)); } public DGBrowser getBrowser() { return browser; } public DGBrowserView getDgBrowserView() { return dgBrowserView; } @Override public void updateEditor(FlexoEditor from, FlexoEditor to) { super.updateEditor(from, to); if (from != null && from.getProject() != null) { from.getProject().getGeneratedDoc().setFactory(null); } if (to != null && to.getResourceUpdateHandler() != null) { to.getResourceUpdateHandler().setGeneratedResourceModifiedHook(_CGGeneratedResourceModifiedHook); } if (to != null && getEditor().getProject() != null) { to.getProject().getGeneratedDoc().setFactory(this); } browser.setRootObject(to != null && to.getProject() != null ? to.getProject().getGeneratedDoc() : null); } @Override public FlexoModelObject getDefaultObjectToSelect(FlexoProject project) { return project.getGeneratedDoc(); } @Override protected DESelectionManager createSelectionManager() { return new DGSelectionManager(this); } /** * Creates a new instance of MenuBar for the module this controller refers to * * @return */ @Override protected FlexoMenuBar createNewMenuBar() { return new DGMenuBar(this); } @Override protected FlexoMainPane createMainPane() { return new DGMainPane(this); } @Override public DGMainPane getMainPane() { return (DGMainPane) super.getMainPane(); } @Override public DGControllerActionInitializer createControllerActionInitializer() { return new DGControllerActionInitializer(this); } @Override public void update(FlexoObservable observable, DataModification dataModification) { if (dataModification instanceof GenerationProgressNotification) { if (((GenerationProgressNotification) dataModification).getIsFineMessage()) { refreshSecondaryProgressWindow(((GenerationProgressNotification) dataModification).getProgressMessage()); } else { refreshProgressWindow(((GenerationProgressNotification) dataModification).getProgressMessage()); } } else if (observable instanceof DGRepository && dataModification instanceof ObjectDeleted) { getObservedRepositories().remove(observable); observable.deleteObserver(this); } } @Override public AbstractProjectGenerator<? extends GenerationRepository> generatorForRepository(GenerationRepository repository) { if (repository instanceof DGRepository) { return getProjectGenerator((DGRepository) repository); } else { if (logger.isLoggable(Level.SEVERE)) { logger.severe("Cannot create project generator for " + repository); } } return null; } public ProjectDocGenerator getProjectGenerator(DGRepository repository) { if (!repository.isConnected()) { return null; } ProjectDocGenerator returned = getProjectGeneratorsMap().get(repository); if (returned == null) { try { switch (repository.getFormat()) { /*case LATEX: returned = new ProjectDocLatexGenerator(repository.getProject(), repository); break;*/ case HTML: returned = new ProjectDocHTMLGenerator(repository.getProject(), repository); break; case DOCX: returned = new ProjectDocDocxGenerator(repository.getProject(), repository); break; } } catch (GenerationException e) { showError(e.getLocalizedMessage()); return null; } getProjectGeneratorsMap().put(repository, returned); } return returned; } public Collection<ProjectDocGenerator> getProjectGenerators() { return getProjectGeneratorsMap().values(); } /** * Select the view representing supplied object, if this view exists. Try all to really display supplied object, even if required view * is not the current displayed view * * @param object * : the object to focus on */ @Override public void selectAndFocusObject(FlexoModelObject object) { if (object instanceof CGFile) { setCurrentEditedObjectAsModuleView(object); } if (object instanceof TOCEntry) { setCurrentEditedObjectAsModuleView(object); } getSelectionManager().setSelectedObject(object); } public DGRepository getCurrentGeneratedCodeRepository() { FlexoModelObject object = getCurrentDisplayedObjectAsModuleView(); if (object instanceof CGObject) { return (DGRepository) AbstractGCAction.repositoryForObject((CGObject) object); } return null; } GenerationRepository _lastEditedCGRepository; private Vector<DGRepository> observedRepositories; public void refreshFooter() { _footer.refresh(); } private void createFooter() { _footer = new DGFooter(); } public GenerationRepository getLastEditedCGRepository() { return _lastEditedCGRepository; } private final Hashtable<CGFile, Hashtable<ContentSource, DGFileVersionPopup>> storedPopups = new Hashtable<CGFile, Hashtable<ContentSource, DGFileVersionPopup>>(); public DGFileVersionPopup getPopupShowingFileVersion(CGFile aFile, ContentSource contentSource) { if (storedPopups.get(aFile) == null) { Hashtable<ContentSource, DGFileVersionPopup> newHashtable = new Hashtable<ContentSource, DGFileVersionPopup>(); storedPopups.put(aFile, newHashtable); } if (storedPopups.get(aFile).get(contentSource) == null) { DGFileVersionPopup newPopup = new DGFileVersionPopup(aFile, contentSource, this); storedPopups.get(aFile).put(contentSource, newPopup); } return storedPopups.get(aFile).get(contentSource); } public void deletePopupShowingFileVersion(CGFile aFile, ContentSource contentSource) { storedPopups.get(aFile).remove(contentSource); } public DGFooter getFooter() { return _footer; } public class DGFooter extends JPanel implements GraphicalFlexoObserver, FocusListener, CursorPositionListener { private final JLabel statusLabel; private final JPanel statusCountPanel; private final JPanel editorInfoPanel; private final JLabel generationModifiedLabel; private final JLabel diskModifiedLabel; private final JLabel conflictsLabel; private final JLabel needsMemoryGenerationLabel; private final JLabel errorsLabel; private final JLabel cursorPositionLabel; private final JLabel editorStatusLabel; public DGFooter() { super(new GridLayout(1, 3)); statusLabel = new JLabel("012345678901234567890123456789012345678901234567890123456789", SwingConstants.LEFT); statusLabel.setFont(FlexoCst.MEDIUM_FONT); statusLabel.setBorder(BorderFactory.createEmptyBorder(0, 20, 0, 20)); add(statusLabel); statusCountPanel = new JPanel(new FlowLayout()); generationModifiedLabel = new JLabel("0"); generationModifiedLabel.setFont(FlexoCst.MEDIUM_FONT); statusCountPanel.add(generationModifiedLabel); statusCountPanel.add(new JLabel(UtilsIconLibrary.LEFT_MODIFICATION_ICON)); statusCountPanel.add(Box.createRigidArea(new Dimension(3, 16))); diskModifiedLabel = new JLabel("5"); diskModifiedLabel.setFont(FlexoCst.MEDIUM_FONT); statusCountPanel.add(diskModifiedLabel); statusCountPanel.add(new JLabel(UtilsIconLibrary.RIGHT_MODIFICATION_ICON)); statusCountPanel.add(Box.createRigidArea(new Dimension(3, 16))); conflictsLabel = new JLabel("8"); conflictsLabel.setFont(FlexoCst.MEDIUM_FONT); statusCountPanel.add(conflictsLabel); statusCountPanel.add(new JLabel(UtilsIconLibrary.CONFLICT_ICON)); statusCountPanel.add(Box.createRigidArea(new Dimension(3, 16))); statusCountPanel.add(new JLabel(UtilsIconLibrary.SEPARATOR_ICON)); statusCountPanel.add(Box.createRigidArea(new Dimension(3, 16))); needsMemoryGenerationLabel = new JLabel("1"); needsMemoryGenerationLabel.setFont(FlexoCst.MEDIUM_FONT); statusCountPanel.add(needsMemoryGenerationLabel); statusCountPanel.add(new JLabel(GeneratorIconLibrary.GENERATE_CODE_ICON)); statusCountPanel.add(Box.createRigidArea(new Dimension(3, 16))); statusCountPanel.add(new JLabel(UtilsIconLibrary.SEPARATOR_ICON)); statusCountPanel.add(Box.createRigidArea(new Dimension(3, 16))); errorsLabel = new JLabel("0"); errorsLabel.setFont(FlexoCst.MEDIUM_FONT); statusCountPanel.add(errorsLabel); statusCountPanel.add(new JLabel(IconLibrary.UNFIXABLE_ERROR_ICON)); add(statusCountPanel); editorInfoPanel = new JPanel(new FlowLayout()); editorInfoPanel.add(new JLabel(UtilsIconLibrary.SEPARATOR_ICON)); editorInfoPanel.add(Box.createRigidArea(new Dimension(3, 16))); cursorPositionLabel = new JLabel("-:-", SwingConstants.CENTER); cursorPositionLabel.setPreferredSize(new Dimension(50, 16)); cursorPositionLabel.setFont(FlexoCst.MEDIUM_FONT); editorInfoPanel.add(cursorPositionLabel); editorInfoPanel.add(Box.createRigidArea(new Dimension(3, 16))); editorInfoPanel.add(new JLabel(UtilsIconLibrary.SEPARATOR_ICON)); editorInfoPanel.add(Box.createRigidArea(new Dimension(3, 16))); editorStatusLabel = new JLabel(""); editorStatusLabel.setFont(FlexoCst.MEDIUM_FONT); editorInfoPanel.add(editorStatusLabel); add(editorInfoPanel); refreshEditorInfoPanel(); } public void refresh() { DGRepository repositoryToConsider = getCurrentGeneratedCodeRepository(); // logger.info("Refresh footer with "+repositoryToConsider); boolean displayItemStatus; if (repositoryToConsider != null) { if (!getObservedRepositories().contains(repositoryToConsider)) { getObservedRepositories().add(repositoryToConsider); repositoryToConsider.addObserver(this); } String repName = "[" + repositoryToConsider.getName() + "] "; if (!repositoryToConsider.isConnected()) { statusLabel.setText(repName + FlexoLocalization.localizedForKey("repository_disconnected")); statusLabel.setForeground(Color.BLACK); displayItemStatus = false; } else { if (getProjectGeneratorsMap().get(repositoryToConsider) == null) { statusLabel.setText(repName + FlexoLocalization.localizedForKey("doc_generation_not_synchronized")); displayItemStatus = false; } else { statusLabel.setText(repName + FlexoLocalization.localizedForKey("doc_generation_is_synchronized")); displayItemStatus = true; } statusLabel.setForeground(Color.BLACK); } } else { statusLabel.setText(FlexoLocalization.localizedForKey("no_repository_selected")); statusLabel.setForeground(Color.GRAY); displayItemStatus = false; } if (displayItemStatus) { generationModifiedLabel.setForeground(Color.BLACK); generationModifiedLabel.setText("" + repositoryToConsider.getGenerationModifiedCount()); diskModifiedLabel.setForeground(Color.BLACK); diskModifiedLabel.setText("" + repositoryToConsider.getDiskModifiedCount()); conflictsLabel.setForeground(Color.BLACK); conflictsLabel.setText("" + repositoryToConsider.getConflictsCount()); needsMemoryGenerationLabel.setForeground(Color.BLACK); needsMemoryGenerationLabel.setText("" + repositoryToConsider.getNeedsMemoryGenerationCount()); errorsLabel.setForeground(Color.BLACK); errorsLabel.setText("" + repositoryToConsider.getErrorsCount()); } else { generationModifiedLabel.setForeground(Color.GRAY); generationModifiedLabel.setText("-"); diskModifiedLabel.setForeground(Color.GRAY); diskModifiedLabel.setText("-"); conflictsLabel.setForeground(Color.GRAY); conflictsLabel.setText("-"); needsMemoryGenerationLabel.setForeground(Color.GRAY); needsMemoryGenerationLabel.setText("-"); errorsLabel.setForeground(Color.GRAY); errorsLabel.setText("-"); } refreshEditorInfoPanel(); validate(); repaint(); } @Override public void update(final FlexoObservable observable, final DataModification dataModification) { if (!SwingUtilities.isEventDispatchThread()) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { update(observable, dataModification); } }); return; } refresh(); } @Override public void focusGained(FocusEvent e) { if (e.getComponent() instanceof JEditTextArea) { ((JEditTextArea) e.getComponent()).addToCursorPositionListener(this); _activeGenericCodeDisplayer = (JEditTextArea) e.getComponent(); refresh(); } } @Override public void focusLost(FocusEvent e) { if (e.getComponent() instanceof JEditTextArea) { ((JEditTextArea) e.getComponent()).removeFromCursorPositionListener(this); if (_activeGenericCodeDisplayer == e.getComponent()) { _activeGenericCodeDisplayer = null; } refresh(); } } private JEditTextArea _activeGenericCodeDisplayer; @Override public void positionChanged(int newPosX, int newPosY) { refreshEditorInfoPanel(); } private void refreshEditorInfoPanel() { // logger.info("refreshEditorInfoPanel()"); if (_activeGenericCodeDisplayer == null) { cursorPositionLabel.setText("-"); editorStatusLabel.setText(FlexoLocalization.localizedForKey("no_edition")); } else { cursorPositionLabel.setText(_activeGenericCodeDisplayer.getCursorY() + ":" + _activeGenericCodeDisplayer.getCursorX()); editorStatusLabel.setText(_activeGenericCodeDisplayer.isEditable() ? FlexoLocalization.localizedForKey("edition") : FlexoLocalization.localizedForKey("read_only")); } } } private DGGeneratedResourceModifiedHook _CGGeneratedResourceModifiedHook; private DGBrowser browser; public class DGGeneratedResourceModifiedHook implements GeneratedResourceModifiedHook { protected DGGeneratedResourceModifiedHook() { } @Override public void handleGeneratedResourceModified(FlexoGeneratedResource aGeneratedResource) { if (aGeneratedResource instanceof CGRepositoryFileResource && !(aGeneratedResource instanceof FlexoCopiedResource)) { if (logger.isLoggable(Level.INFO)) { logger.info("Resource " + aGeneratedResource + " has been modified on the disk."); } CGRepositoryFileResource generatedResource = (CGRepositoryFileResource) aGeneratedResource; GeneratedResourceModifiedChoice choice = DGPreferences.getGeneratedResourceModifiedChoice(); if (choice == GeneratedResourceModifiedChoice.ASK) { RadioButtonListParameter<String> whatToDo = new RadioButtonListParameter<String>("whatToDo", "what_would_you_like_to_do", GeneratedResourceModifiedChoice.IGNORE.getLocalizedName(), GeneratedResourceModifiedChoice.IGNORE.getLocalizedName(), GeneratedResourceModifiedChoice.ACCEPT.getLocalizedName()); CheckboxParameter rememberMyChoice = new CheckboxParameter("rememberMyChoice", "remember_my_choice", false); AskParametersDialog dialog = AskParametersDialog.createAskParametersDialog(getProject(), null, FlexoLocalization.localizedForKey("resource_edited"), FlexoLocalization.localizedForKey("resource") + " " + generatedResource.getFileName() + " " + FlexoLocalization.localizedForKey("has_been_edited"), whatToDo, rememberMyChoice); if (dialog.getStatus() == AskParametersDialog.VALIDATE) { if (whatToDo.getValue().equals(GeneratedResourceModifiedChoice.IGNORE.getLocalizedName())) { choice = GeneratedResourceModifiedChoice.IGNORE; } else if (whatToDo.getValue().equals(GeneratedResourceModifiedChoice.ACCEPT.getLocalizedName())) { choice = GeneratedResourceModifiedChoice.ACCEPT; } if (rememberMyChoice.getValue()) { DGPreferences.setGeneratedResourceModifiedChoice(choice); } } } if (choice == GeneratedResourceModifiedChoice.ASK) { choice = GeneratedResourceModifiedChoice.IGNORE; } // First of all, notify resource changed generatedResource.notifyResourceChangedOnDisk(); Vector<CGObject> resourceChangedOnDisk = new Vector<CGObject>(); resourceChangedOnDisk.add(generatedResource.getCGFile()); if (choice == GeneratedResourceModifiedChoice.IGNORE) { } else if (choice == GeneratedResourceModifiedChoice.ACCEPT) { AcceptDiskUpdate.actionType.makeNewAction(generatedResource.getCGFile(), resourceChangedOnDisk, getEditor()).doAction(); } } } } public DGGeneratedResourceModifiedHook getCGGeneratedResourceModifiedHook() { return _CGGeneratedResourceModifiedHook; } public void setCGGeneratedResourceModifiedHook(DGGeneratedResourceModifiedHook generatedResourceModifiedHook) { _CGGeneratedResourceModifiedHook = generatedResourceModifiedHook; } private Vector<DGRepository> getObservedRepositories() { if (observedRepositories == null) { observedRepositories = new Vector<DGRepository>(); } return observedRepositories; } }