org.nuiton.widget.editor.Editor Maven / Gradle / Ivy
Show all versions of nuiton-widgets Show documentation
/* *##% Graphical Widget
* Copyright (C) 2004 - 2008 CodeLutin
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* . ##%*/
/* *
* Editor.java
*
* Created: 6 août 2006 10:42:11
*
* @author poussin
* @version $Revision: 233 $
*
* Last update: $Date: 2009-12-02 18:00:27 +0100 (mer., 02 déc. 2009) $
* by : $Author: echatellier $
*/
package org.nuiton.widget.editor;
import static org.nuiton.i18n.I18n._;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Event;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.Action;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.event.CaretListener;
import javax.swing.event.DocumentListener;
import javax.swing.text.TextAction;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* L'idee de cette editor, est qu'il ne fait rien lui meme, mais delegue a un
* autre editeur enregistré. L'enregistrement ce fait par le nom de la classe
* sous forme de String, ce qui permet d'enregistrer des editeurs qui ne peuvent
* pas etre chargé car il manque des jars. Par exemple NetBeansEditor qui
* demande beaucoup de jar, peu ne pas etre chargé convenablement, dans ce cas
* un autre editeur sera choisi.
*
* L'ordre d'enregistrement est important. Le premier editor enregistré sera le
* premier editeur essayé.
*
* Si l'on souhaite creer un nouvel editeur, il faut implanter
* {@link EditorInterface} et etendre au moins {@link Component}
*
* Si aucun editeur n'est trouvé alors {@link DefaultEditor} est utilisé
*
* Editeur permet d'utiliser Ctrl-s pour sauver le fichier courant quel que soit
* l'editeur.
*
* @author poussin
*/
public class Editor extends JPanel implements EditorInterface {
/** serialVersionUID */
private static final long serialVersionUID = 5820456710194699050L;
/** to use log facility, just put in your code: log.info(\"...\"); */
static private Log log = LogFactory.getLog(Editor.class);
static protected EditorInterface DEFAULT_EDITOR = new DefaultEditor();
static {
registered = new ArrayList();
// we try to automatic register some Editor
addEditor(SDocEditor.class.getName());
addEditor(JEditEditor.class.getName());
addEditor(RSyntaxEditor.class.getName());
}
/** tous les Editors enregistré */
static protected List registered;
/**
* register new editor
*
* @return the new registered Editor, or null, if new editor can't be
* instanciant.
*/
static EditorInterface addEditor(String editorClassName) {
try {
Class> editorClass = Class.forName(editorClassName);
EditorInterface result = (EditorInterface) editorClass
.newInstance();
registered.add(result);
return result;
} catch (NoClassDefFoundError eee) {
if (log.isDebugEnabled()) {
log.debug("Can't find your editor class: " + editorClassName,
eee);
}
if (log.isInfoEnabled()) {
log.info("Can't find your editor class: " + editorClassName);
}
} catch (ClassNotFoundException eee) {
if (log.isDebugEnabled()) {
log.debug("Can't find your editor class: " + editorClassName,
eee);
}
if (log.isInfoEnabled()) {
log.info("Can't find your editor class: " + editorClassName);
}
} catch (ClassCastException eee) {
if (log.isDebugEnabled()) {
log.debug("Your editor class is not Editor Child: "
+ editorClassName, eee);
}
if (log.isInfoEnabled()) {
log.info("Your editor class is not Editor Child: "
+ editorClassName);
}
} catch (InstantiationException eee) {
if (log.isDebugEnabled()) {
log.debug("Can't instanciant your Editor class: "
+ editorClassName, eee);
}
if (log.isInfoEnabled()) {
log.info("Can't instanciant your Editor class: "
+ editorClassName);
}
} catch (IllegalAccessException eee) {
if (log.isDebugEnabled()) {
log.debug("Can't access your Editor class: " + editorClassName,
eee);
}
if (log.isInfoEnabled()) {
log.info("Can't access your Editor class: " + editorClassName);
}
} catch (Exception eee) {
if (log.isDebugEnabled()) {
log.debug("Error during instanciation of your Editor: "
+ editorClassName, eee);
}
if (log.isInfoEnabled()) {
log.info("Error during instanciation of your Editor: "
+ editorClassName, eee);
}
}
return null;
}
static protected EditorInterface nullEditor = new NullEditor();
/** editor already instanciate for this editor */
protected Map usedEditor = new HashMap();
/** currentEditor is currently opened editor in this editor */
protected EditorInterface currentEditor = nullEditor;
/** current opened file */
protected File openedFile = null;
/** All document listener registered on this editor */
protected Set documentListeners = new HashSet();
/** All caret listener registered on this editor */
protected Set caretListeners = new HashSet();
/**
* If the edited file is modifier, before switch to other or close it, ask
* the user if he want save the modification
*/
protected boolean askIfNotSaved = true;
/** force usage of default editor */
protected boolean forceDefault = false;
public Editor() {
setLayout(new BorderLayout());
addKeyBinding();
close();
}
protected void addKeyBinding() {
// Add Ctrl-s -> save
Action saveAction = new SaveAction(this);
KeyStroke key = KeyStroke.getKeyStroke(KeyEvent.VK_S, Event.CTRL_MASK);
this.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(
key, "save");
this.getActionMap().put("save", saveAction);
}
/**
* @return the askIfNotSaved
*/
public boolean isAskIfNotSaved() {
return this.askIfNotSaved;
}
/**
* @param askIfNotSaved the askIfNotSaved to set
*/
public void setAskIfNotSaved(boolean askIfNotSaved) {
this.askIfNotSaved = askIfNotSaved;
}
/**
* @return the forceDefault
*/
public boolean isForceDefault() {
return this.forceDefault;
}
/**
* @param forceDefault the forceDefault to set
*/
public void setForceDefault(boolean forceDefault) {
this.forceDefault = forceDefault;
}
/**
* try to find better editor for this file
*
* @param file
* @return the better editor, or Default Editor
*/
public EditorInterface getEditor(File file) {
// if no editor found, DEFAULT_EDITOR will be used
EditorInterface editor = DEFAULT_EDITOR;
if (!isForceDefault()) {
for (EditorInterface e : Editor.registered) {
if (e.accept(file)) {
editor = e;
}
}
}
EditorInterface newEditor = usedEditor.get(editor);
try {
if (newEditor == null) {
// create new instance for this editor
newEditor = editor.getClass().newInstance();
usedEditor.put(editor, newEditor);
}
} catch (InstantiationException eee) {
if (log.isDebugEnabled()) {
log.debug("Can't instanciant your Editor class: "
+ editor.getClass().getName(), eee);
}
if (log.isInfoEnabled()) {
log.info("Can't instanciant your Editor class: "
+ editor.getClass().getName());
}
} catch (IllegalAccessException eee) {
if (log.isDebugEnabled()) {
log.debug("Can't access your Editor class: "
+ editor.getClass().getName(), eee);
}
if (log.isInfoEnabled()) {
log.info("Can't access your Editor class: "
+ editor.getClass().getName());
}
}
return newEditor;
}
public void setCurrentEditor(EditorInterface editor) {
// remove old editor
if (this.currentEditor != null) {
remove((Component) this.currentEditor);
}
// remove all listener on old editor
for (DocumentListener l : documentListeners) {
this.currentEditor.removeDocumentListener(l);
}
for (CaretListener l : caretListeners) {
this.currentEditor.removeCaretListener(l);
}
this.currentEditor = editor;
// and add all listener on new editor
for (DocumentListener l : documentListeners) {
this.currentEditor.addDocumentListener(l);
}
for (CaretListener l : caretListeners) {
this.currentEditor.addCaretListener(l);
}
// put new editor as child
add((Component) editor, BorderLayout.CENTER);
}
/**
* @return the currentEditor
*/
public EditorInterface getCurrentEditor() {
return this.currentEditor;
}
/**
* @return the openedFile
*/
public File getOpenedFile() {
return this.openedFile;
}
/**
* @param openedFile the openedFile to set
*/
public void setOpenedFile(File openedFile) {
this.openedFile = openedFile;
}
/**
* Closs current file
*
* @return the current editor
*/
public boolean close() {
if (askAndSaveOrCancel()) {
setOpenedFile(null);
setCurrentEditor(nullEditor);
return true;
}
return false;
}
/**
* ask the user to save the current opened file if necessary (current file
* is modified)
*
* @return false if user awnser Cancel, true otherwize.
*/
protected boolean askAndSaveOrCancel() {
boolean result = true;
if (isAskIfNotSaved() && getCurrentEditor().isModified()) {
int val = JOptionPane.showConfirmDialog(this,
_("nuitonwidgets.editor.saveorcancel"));
switch (val) {
case JOptionPane.YES_OPTION:
save();
result = true;
break;
case JOptionPane.NO_OPTION:
result = true;
break;
case JOptionPane.CANCEL_OPTION:
result = false;
break;
}
}
return result;
}
/**
* Save current opened file
*
* @return true if all is ok
*/
public boolean save() {
File file = getOpenedFile();
boolean result = getCurrentEditor().saveAs(file);
return result;
}
/**
* if return true, this editor support this file type. Default implantation
* return true
*
* @param file
* @return if return true, this editor support this file type.
*/
public boolean accept(File file) {
return true;
}
/**
* indicate if current opened file has been modified
*
* @return true if currend file is modified
*/
public boolean isModified() {
boolean result = getCurrentEditor().isModified();
return result;
}
/**
* Replace the current edited file by file passed in argument. When you
* overide this method, you must call {@link #setOpenedFile(File)}
*
* @param file the file to open
* @return true if file has been opened
*/
public boolean open(File file) {
boolean result = false;
if (askAndSaveOrCancel()) {
EditorInterface editor = getEditor(file);
result = editor.open(file);
if (result) {
setOpenedFile(file);
setCurrentEditor(editor);
} else {
close();
}
}
return result;
}
/**
* Replace the current edited file by file passed in argument
*
* @param file the file to open
* @return true if file has been saved and reopen with new name
*/
public boolean saveAs(File file) {
boolean result = getCurrentEditor().saveAs(file);
if (result) {
result = open(file);
}
return result;
}
/*
* (non-Javadoc)
*
* @see org.nuiton.widget.editor.EditorInterface#getText()
*/
public String getText() {
String result = getCurrentEditor().getText();
return result;
}
/*
* (non-Javadoc)
*
* @see org.nuiton.widget.editor.EditorInterface#getText()
*/
public void setText(String text) {
getCurrentEditor().setText(text);
}
/*
* @see org.nuiton.widget.editor.EditorInterface#copy()
*/
@Override
public void copy() {
currentEditor.copy();
}
/*
* @see org.nuiton.widget.editor.EditorInterface#cut()
*/
@Override
public void cut() {
currentEditor.cut();
}
/*
* @see org.nuiton.widget.editor.EditorInterface#paste()
*/
@Override
public void paste() {
currentEditor.paste();
}
@Override
public void setEnabled(boolean b) {
super.setEnabled(b);
currentEditor.setEnabled(b);
}
/*
* @see org.nuiton.widget.editor.EditorInterface#addDocumentListener(javax.swing.event.DocumentListener)
*/
@Override
public void addDocumentListener(DocumentListener listener) {
documentListeners.add(listener);
getCurrentEditor().addDocumentListener(listener);
}
/*
* @see org.nuiton.widget.editor.EditorInterface#removeDocumentListener(javax.swing.event.DocumentListener)
*/
@Override
public void removeDocumentListener(DocumentListener listener) {
documentListeners.remove(listener);
getCurrentEditor().removeDocumentListener(listener);
}
/*
* @see org.nuiton.widget.editor.EditorInterface#addCaretListener(javax.swing.event.CaretListener)
*/
@Override
public void addCaretListener(CaretListener listener) {
caretListeners.add(listener);
getCurrentEditor().addCaretListener(listener);
}
/*
* @see org.nuiton.widget.editor.EditorInterface#removeCaretListener(javax.swing.event.CaretListener)
*/
@Override
public void removeCaretListener(CaretListener listener) {
caretListeners.remove(listener);
getCurrentEditor().removeCaretListener(listener);
}
static class SaveAction extends TextAction {
/** serialVersionUID */
private static final long serialVersionUID = 4694356772539222176L;
/** to use log facility, just put in your code: log.info(\"...\"); */
static private Log log = LogFactory.getLog(SaveAction.class);
protected Editor editor;
/* Create this object with the appropriate identifier. */
SaveAction(Editor editor) {
super("save");
this.editor = editor;
}
/**
* The operation to perform when this action is triggered.
*
* @param e the action event
*/
public void actionPerformed(ActionEvent e) {
log.info("try to save file");
if (!editor.save()) {
log.warn("Unable to save");
} else {
log.info("saved ok");
}
}
}
}