import java.awt.*;
import java.awt.event.*;
import java.io.*;
//import java.util.*;
import java.awt.datatransfer.*;

import javax.swing.text.*;
import javax.swing.undo.*;
import javax.swing.event.*;
import javax.swing.*;

class MinEdit0 extends JPanel {
	private JFrame frame;
  private JTextComponent editor;
  private JMenuBar menubar;
  private JToolBar toolbar;
  private StatusBar status;
	private File currentFile = null;
	private JFileChooser selecteurFichier;
  protected UndoHandler undoHandler;
  protected EditMenuManager editMenuManager;
	WindowListener appCloser;

  MinEdit0() {
  	setBorder(BorderFactory.createEtchedBorder());
	  setLayout(new BorderLayout());
		menubar = createMenubar();
		toolbar = createToolbar();
		status = createStatusBar();
  	editor = createEditor();
    editor.setFont(new Font("monospaced", Font.PLAIN, 12));
		editor.getDocument().addUndoableEditListener(undoHandler);
		editor.getDocument().addDocumentListener(status);
		undoHandler = new UndoHandler();
		editMenuManager = new EditMenuManager();
		editor.addCaretListener(editMenuManager);
		add(toolbar, BorderLayout.NORTH);
	  add(new JScrollPane(editor), BorderLayout.CENTER);
	  add(status, BorderLayout.SOUTH);
		appCloser = new AppCloser();
    selecteurFichier = new JFileChooser();
		selecteurFichier.setCurrentDirectory(new File("."));
	}

  public static void main(String[] args) {
		JWindow temporaire = new JWindow();
		temporaire.getContentPane().add(new JLabel(
      "MinEdit en cours de chargement...", SwingConstants.CENTER));
		temporaire.setBounds(200,200,200,100);
		temporaire.setVisible(true);

		JFrame frame = new JFrame();                
		MinEdit0 n = new MinEdit0();
		frame.setTitle("Untitled");
		frame.setBackground(Color.lightGray);
		frame.setJMenuBar(n.getMenubar());
		frame.setContentPane(n);
		frame.addWindowListener(n.appCloser);
		frame.setSize(600, 600);
		n.setFrame(frame);
		temporaire.setVisible(false);
		temporaire.dispose();

		frame.show();
	}

  class AppCloser extends WindowAdapter {
    public void windowClosing(WindowEvent e) {
			if (status.isModified()) {
		    int reponse = JOptionPane.showConfirmDialog(null, 
			    "Voulez vous sauver le texte courant\n"+
          " avant de quitter ?", 
          "Sauver avant de quitter ?", 
          JOptionPane.YES_NO_OPTION);
		    if (reponse == JOptionPane.YES_OPTION) 
					saveAction.actionPerformed(null);
	    }   
			System.exit(0);
		}
	}
		
  public void setFrame(JFrame frame) { 
    this.frame = frame; 
	}

  protected JTextComponent createEditor() {
	  return new JTextArea();
	}
  protected JTextComponent getEditor() {
	  return editor;
	}

  protected JMenuBar getMenubar() {
	  return menubar;
	}

  protected StatusBar createStatusBar() {
    return new StatusBar();
	}
  protected StatusBar  getStatusBar() {
    return status;
	}

  private JToolBar createToolbar() {
    JButton b;
	  JToolBar tb = new JToolBar();
    b = tb.add(newAction);
    b.setText(null);      
    b.setToolTipText("nouveau"); 
    b = tb.add(openAction);
    b.setText(null);      
    b.setToolTipText("ouvrir..."); 
    b = tb.add(saveAction);
    b.setText(null);      
    b.setToolTipText("sauver"); 
		tb.add(Box.createHorizontalStrut(10));
    b = tb.add(copyAction);
    b.setText(null);      
    b.setToolTipText("copier"); 
    b = tb.add(cutAction);
    b.setText(null);
    b.setToolTipText("couper");
    b = tb.add(pasteAction); 
    b.setText(null);
    b.setToolTipText("coller");
		tb.add(Box.createHorizontalStrut(10));
    b = tb.add(undoAction);
    b.setText(null);      
    b.setToolTipText("annuler"); 
    b = tb.add(redoAction);
    b.setText(null);      
    b.setToolTipText("rétablir"); 

		tb.add(Box.createHorizontalGlue());
		return tb;
	}

  protected JMenuBar createMenubar() {
	  JMenuBar mb = new JMenuBar();
	  JMenu menu;
		JMenuItem item;
		menu = new JMenu("Fichier");
		item = menu.add(newAction);
		item.setIcon(null); item.setMnemonic('N');
		item = menu.add(openAction);
		item.setIcon(null); item.setMnemonic('O');
		item = menu.add(saveAction);
		item.setIcon(null); item.setMnemonic('S');
		item = menu.add(saveAsAction);
		item.setIcon(null); item.setMnemonic('A');
		menu.addSeparator();
		item = menu.add(exitAction);
		mb.add(menu);
		menu = new JMenu("Edit");
		menu.add(cutAction).setIcon(null);
		menu.add(copyAction).setIcon(null);
		menu.add(pasteAction).setIcon(null);
		menu.add(selectAllAction);
		menu.addSeparator();
		menu.add(undoAction);
		menu.add(redoAction);
		mb.add(menu);
		return mb;
	}


  class UndoHandler extends UndoManager {
    public void undoableEditHappened(UndoableEditEvent e) {
	    super.addEdit(e.getEdit());
	    update();
		}
		public void update() {
      undoAction.setEnabled(canUndo());
      redoAction.setEnabled(canRedo());
		}
	}

	class StatusBar extends JPanel implements DocumentListener {
		boolean modStatus = false; // true = modified;
		static final String saved = "--:--";
		static final String modified = "-:**";
		JLabel modLabel = new JLabel(saved);
    public StatusBar() {
	    setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
			setFont(new Font("monospaced", Font.BOLD, 12));
			add(modLabel);
			add(Box.createHorizontalGlue());
		}
		public void changedUpdate(DocumentEvent ev) { 
			setModified();
		}
		public void insertUpdate(DocumentEvent ev) {
			setModified();
		}
		public void removeUpdate(DocumentEvent ev) { 
			setModified();
		}
		public void setSaved() { 
 			modStatus = false;  modLabel.setText(saved); 
			getEditor().getDocument().addDocumentListener(this);
			saveAction.setEnabled(false);
		}
		public void setModified() { 
			modStatus = true; modLabel.setText(modified);
			getEditor().getDocument().removeDocumentListener(this);
			saveAction.setEnabled(true);
		}
		public boolean isModified() { return modStatus; }
}

// --- action implementations -----------------------------------

  private Action undoAction = new UndoAction();
  private Action redoAction = new RedoAction();
	private Action newAction = new NewAction();
	private Action openAction = new OpenAction();
	private Action saveAction = new SaveAction();
	private Action saveAsAction = new SaveAsAction();
	private Action exitAction = new ExitAction();
	private Action cutAction = new CutAction();
	private Action copyAction = new CopyAction();
	private Action pasteAction = new PasteAction();
	private Action selectAllAction = new SelectAllAction();

  class UndoAction extends AbstractAction {
	  public UndoAction() {
	    super("Undo", new ImageIcon("gifs/undo.gif"));
	    setEnabled(false);
		}
    public void actionPerformed(ActionEvent e) {
	    try { undoHandler.undo(); }
			catch (CannotUndoException ex) {}
	    undoHandler.update();
	  }
	}

  class RedoAction extends AbstractAction {
	  public RedoAction() {
	    super("Redo", new ImageIcon("gifs/redo.gif"));
	    setEnabled(false);
		}
		public void actionPerformed(ActionEvent e) {
			try { undoHandler.redo(); }
 			catch (CannotRedoException ex) {}
			undoHandler.update();
		}
	}
    
  class NewAction extends AbstractAction {
		NewAction() {
			 super("Nouveau", new ImageIcon("gifs/new.gif"));
		}
		public void actionPerformed(ActionEvent e) {
	    Document oldDoc = getEditor().getDocument();
			if (!isConfirmed(
		    "Voulez vous sauver le texte courant\n"+
        " avant de créer un nouveau document ?", 
        "Sauver avant nouveau ?")) return;
			oldDoc.removeUndoableEditListener(undoHandler);
			oldDoc.removeDocumentListener(status);
			Document newDoc = new PlainDocument();
			getEditor().setDocument(newDoc);
			newDoc.addUndoableEditListener(undoHandler);
			status.setSaved();
			currentFile = null;
			frame.setTitle("Untitled");
	    revalidate();
		}
	}
	boolean isConfirmed(String question, String titre) {
		if (!status.isModified()) return true;
		int reponse = JOptionPane.showConfirmDialog(null, 
			question, titre, JOptionPane.YES_NO_CANCEL_OPTION);
		switch(reponse) {
		  case JOptionPane.YES_OPTION: {
				saveAction.actionPerformed(null);
				return !status.isModified();
			}
		  case JOptionPane.NO_OPTION: return true;
		  case JOptionPane.CANCEL_OPTION: return false;
		}
		return false;
	}
  class OpenAction extends AbstractAction {
    OpenAction() {
			super("Ouvrir...", new ImageIcon("gifs/open.gif"));
		}
    public void actionPerformed(ActionEvent e) {
			if (!isConfirmed(
		    "Voulez vous sauver le texte courant\n"+
        " avant d'ouvrir un autre fichier ?", 
        "Sauver avant d'ouvrir ?")) return;
			int answer = selecteurFichier.showOpenDialog(frame);
			if (answer != JFileChooser.APPROVE_OPTION)
				return;
	    currentFile = selecteurFichier.getSelectedFile();
			try {
				FileReader in = new FileReader(currentFile);
				getEditor().read(in, null);
				in.close();
			} 
			catch (IOException ex) { ex.printStackTrace(); } 
			status.setSaved();
			frame.setTitle(currentFile.getName());
		}
 	}
	class SaveAsAction extends AbstractAction {
    SaveAsAction() {
			super("Sauver sous...", new ImageIcon("gifs/save.gif"));
		}

    public void actionPerformed(ActionEvent e) {
			int answer = selecteurFichier.showSaveDialog(frame);
			if (answer != JFileChooser.APPROVE_OPTION)
				return;
	    currentFile = selecteurFichier.getSelectedFile();
			frame.setTitle(currentFile.getName());
			doWrite(currentFile);
      selecteurFichier.rescanCurrentDirectory();
		}             
 	}
		
  void doWrite(File f) {			
		try {
			FileWriter out = new FileWriter(f);
			getEditor().write(out);
			out.close();
			status.setSaved();
		} 
		catch (IOException ex) { ex.printStackTrace(); }
	}
	class SaveAction extends AbstractAction {
    SaveAction() {
			super("Sauver", new ImageIcon("gifs/save.gif"));
		}
    public void actionPerformed(ActionEvent e) {
			if (currentFile != null)
				doWrite(currentFile);
			else saveAsAction.actionPerformed(e);
		}
	}

  class CutAction extends AbstractAction {
		CutAction() {
       super("Cut", new ImageIcon("gifs/cut.gif"));
		}
		public void actionPerformed(ActionEvent e) {
	    getEditor().cut();
			editMenuManager.doCut();
		}
	}
  class CopyAction extends AbstractAction {
		CopyAction() {
			 super("Copy", new ImageIcon( "gifs/copy.gif" ));
			}
		public void actionPerformed(ActionEvent e) {
	    getEditor().copy();
			editMenuManager.doCopy();
		}
	}
  class PasteAction extends AbstractAction {
		PasteAction() {
	     super("Paste", new ImageIcon("gifs/paste.gif"));
		}
		public void actionPerformed(ActionEvent e) {
	    getEditor().paste();
			editMenuManager.doPaste();
		}
	}

  class SelectAllAction extends AbstractAction {
		SelectAllAction() {
	     super("Select all");
		}
		public void actionPerformed(ActionEvent e) {
	    getEditor().selectAll();
			//editMenuManager.doPaste();
		}
	}

   class ExitAction extends AbstractAction {
    ExitAction() {
	    super("Quitter");
		}
		public void actionPerformed(ActionEvent e) {
			if (!isConfirmed(
		    "Voulez vous sauver le texte courant\n"+
        " avant de quitter ?", 
        "Sauver avant de quitter ?")) return;
	    System.exit(0);
		}
	}

class EditMenuManager implements CaretListener {
	int state;
  static final int EMPTY = 0, CUTCOPY = 1, PASTE = 2, FULL = 3;
  public EditMenuManager() {
		doInitial();
	}    
	boolean isEmptyClipBoard() {
		Clipboard clip = Toolkit.getDefaultToolkit().getSystemClipboard();
		Transferable contents = clip.getContents(null);
    if (contents == null) return false;
    return !contents.isDataFlavorSupported(DataFlavor.stringFlavor);
	}
	void doInitial() {
		if (isEmptyClipBoard()) 
			state = EMPTY;  
		else
			state = PASTE; 
		updateEnables(state);
	}    
  void doCopy() {
		if (state == CUTCOPY) {
			state = FULL;
			updateEnables(state);
		}
	}
	void doCut() {
		state = PASTE;
		updateEnables(state);
	}
	void doPaste() {
		if (state == FULL) {
      state = PASTE;
			updateEnables(state);
		}
	}
	void doSelected() {
		if (state == EMPTY) state = CUTCOPY;
		else if (state == PASTE) state = FULL;
		updateEnables(state);
	}
	void doDeselected() {
		if (state == CUTCOPY) state = EMPTY;
		else if (state == FULL) state = PASTE; 	
		updateEnables(state);

	}
	public void caretUpdate(CaretEvent e) {
		int now = e.getDot();
		int before = e.getMark();
		boolean nowSelected = now != before;
		if (nowSelected) 
			doSelected();
		else 
      doDeselected();
	}

	public void updateEnables(int state) {
		switch (state) {
		  case EMPTY :
				cutAction.setEnabled(false);
				copyAction.setEnabled(false);
				pasteAction.setEnabled(false);
				break;
		  case CUTCOPY:
				cutAction.setEnabled(true);
				copyAction.setEnabled(true);
				pasteAction.setEnabled(false);
				break;
		  case PASTE: 
				cutAction.setEnabled(false);
				copyAction.setEnabled(false);
				pasteAction.setEnabled(true);
				break;
		  case FULL:
				cutAction.setEnabled(true);
				copyAction.setEnabled(true);
				pasteAction.setEnabled(true);
		}
	}
}

}

