//Titel:            MVC Pattern mit Testbeispiel für MDI-Fenster
//Version:          2.0, 06.11.2010
//Copyright:        HS-Harz, FB AI
//Autor:            Dipl.-Inf.(TU) Dipl.-Ing.(FH) Michael Wilhelm
//Beschreibung:     Beispiel eines MVC mit Listener, Singelton Pattern
//                  Modell und Controller zusammen


// Beispiel baut ein MVC auf
// ein JTextField wird mit einem Listener abgeleitet
// Modell und Controller sind in einer Klasse
// verwendet Singleton-Pattern fuer das Modell CName

// setLocationRelativeTo(null); 


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;


// InternalFrame
import javax.swing.event.*;

// MVC
import java.util.Observable;
import java.util.Observer;



public class MVC5 extends JFrame {

  // Container für die Clientfenster
  private JDesktopPane _desktopPane = new JDesktopPane();


    // Menü Variablen
  private JMenuBar menuBar1;
  private JMenu MainFile;
  private JMenuItem MnNew;
  private JMenuItem MnOpen;
  private JMenuItem MnClose;

  private JMenu MainEdit;
  private JMenuItem MnCut;
  private JMenuItem MnCopy;
  private JMenuItem MnInsert;


   // 3. Schritt  die Modell+Controller Variable im JFrame deklarieren

   // Konstruktor
  public MVC5() {
    this.setSize(new Dimension(800, 600));
    setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    setGUI();
  }

  public void setGUI() {
    this.getContentPane().setLayout( new BorderLayout() );
    this.setContentPane(_desktopPane);

    setMenus();
    // 4. Schritt  die Modell+Controller Variable erzeugen
    Modell _modell = Modell.getInstance();
    _modell.setName("Paul");
  }   // setPaul



    // setzt die Menüs
  private void setMenus() {
    menuBar1 = new JMenuBar();

      // DATEI
    MainFile = new JMenu("Datei");
    MnNew = new JMenuItem("Neu");
    MnClose = new JMenuItem("Schließen");

    MnNew.addActionListener(new ActionListener()  {
      public void actionPerformed(ActionEvent e) {
        MnNew_click();
      }
    });
    MnNew.setAccelerator( KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.CTRL_MASK));
    MainFile.add(MnNew);

    MainFile.addSeparator();

    // --------------------------
    MnClose.addActionListener(new ActionListener()  {
      public void actionPerformed(ActionEvent e) {
        MnClose_click();
      }
    });
    MainFile.add(MnClose);


    menuBar1.add(MainFile);

    // ----------------
    this.setJMenuBar(menuBar1);

  } // setMenus


  // Menü Aktions Methoden

  void MnNew_click() {
    // 7. Schritt  Viewer in der Methode "New_click" erzeugen    
    JFInternalFrame1 frame = new JFInternalFrame1(null);  //e Uebergabe _modell
    JInternalFrame[] ff = _desktopPane.getAllFrames();
    int i = ff.length;
    int w = ff.length*260;
    int h = ff.length*110;
    frame.setBounds( 10, 10, 350, 100);
    frame.setLocation(  (i%2)*360+20, 60+130*(i/2)  );

    _desktopPane.add(frame);
  }


  void MnClose_click() {
    System.exit(0);
  }


  static public void main(String[] args) {
    MVC5 frame = new MVC5();
    frame.setVisible(true);
  }


}  // MVC5




// 1. Schritt ableiten von Observable, damit Controller und Modell
// hier mit Desgn Pattern SingleTon
class Modell  extends java.util.Observable {
  private static Modell instance = new Modell();

  String sName= "Freya";
  boolean _bUpdateModus=false;

  private void Modell() {
  }

  public static  Modell getInstance() {
    return instance;
  }

   // verhindert eine fortwaehrendes Update
  public void setUpdateModus(boolean bUpdateModus) {
    _bUpdateModus = bUpdateModus;
  }
  public boolean getUpdateModus() {
    return _bUpdateModus;
  }

  public String getName() {
    return sName;
  }

  public void setName(String Name) {
    this.sName = Name;
  }

	// wird vom Internalframe aufgerufen
    // 2. Schritt: Im Controller eine Methode für die Viewer zum Updaten schreiben
  public void DataChangedFromViewer(int sourceViewerTag, String s) {
    System.out.println("s in CName: "+s);
    if ( ! getUpdateModus() ) {
      setUpdateModus(true);
      this.setName(s);
      super.setChanged();  //  wichtig fuer notify
      super.notifyObservers( new Integer(sourceViewerTag) );
    }
    else {
      System.out.println("s in CName: getUpdateModus() = true");
    }
  }

  /*
  public void notifyObersers(Object b) {
    setChanged();
    super.notifyObservers( this );
  }
  */

} // Modell



// ---------------------------------
// 5. Schritt ein Interface dient dazu, eine universelle Loesung zu schaffen
interface ITextField_Change {

  public void sendChangeMessage();

}
       

// 6. Schritt  im InternalFrame  Implements einbauen und die Methode Update
class JFInternalFrame1 extends JInternalFrame  implements Observer, ITextField_Change {
  private static int tagnr = 0;
  private String _sFileName;
  private String _Caption;

  private int _tag;

  private JLabel label1 = new JLabel("Student");
  //private JTextField edit1 = new JTextField("XXXXX");
  private JTextFieldDocListener edit = new JTextFieldDocListener(this,"XXXXX");

  private Modell _modell;

  
  public JFInternalFrame1(String sFileName) {
    _tag = ++tagnr;
    _sFileName = sFileName;
    _modell = Modell.getInstance();
    if (sFileName==null)
      _Caption = "noname";
    else
      _Caption = sFileName;
    setGUI();
    this.setClosable(true);
    this.setIconifiable(true);
    this.setMaximizable(true);
    this.setResizable(true);
    setVisible(true);
  }

  void setGUI() {
   // 6. Schritt  im InternalFrame  Implements einbauen und die Methode Update
    _modell.addObserver(this);  // registrierung

    edit.setFont( new Font("Arial", Font.BOLD,28));
    label1.setFont( new Font("Arial", Font.BOLD,28));

    System.out.println("Name: "+_modell.getName() );
    System.out.println("setUpdateModus(true)");
    _modell.setUpdateModus(true);  // hier wird aktualisiert, sperre
    System.out.println("vor edit.setText");
    edit.setText(    _modell.getName()     );
    System.out.println("nach edit.setText");
    _modell.setUpdateModus(false);  // hier wird aktualisiert, Sperre aufheben
    System.out.println("setUpdateModus(false)");

      // zuweisen des Textlisteners für Aenderungen

    this.getContentPane().setLayout( new GridLayout(1,2) );
    this.setTitle(_Caption);

    this.getContentPane().add(label1);
    this.getContentPane().add(edit);

  }  // setGUI

   // 6. Schritt  im InternalFrame  Implements einbauen und die Methode Update
  // aufgerufen vom Controller
    // Weiterleitung abschalten
    // in arg steht die int sourceViewerTag
    // hier muss eine rekusive Schleife verhindert werden
    // ohne die Abfrage gibt es eine Exception !
  public void update(Observable o, Object arg) {
    int sourceViewerTag = ((Integer) arg).intValue();
    System.out.println("sourceViewerTag: "+sourceViewerTag+"   _tag: "+_tag);
    if (sourceViewerTag != _tag) {
       System.out.println("Update");
       if ( _modell.getUpdateModus() ) {
         System.out.println("JIF Update: getUpdateModus: "+_modell.getName());
         edit.setText( _modell.getName() );
       }
    }
    else {
       System.out.println("bin ich selber sourceViewerTag");
    }
  }  // update


 // 7. Schritt  Methode edit_change (Enter) implementieren, Aufruf des Controllers
    // wird vom JTextFieldDocListener aufgerufen
  public void sendChangeMessage() {
    if ( _modell.getUpdateModus() ) {
      System.out.println(" JFInternalFrame1 sendChangeMessage, _modell.getUpdateModus = true");
    }
    else {
      System.out.println(" JFInternalFrame1 sendChangeMessage, _modell.getUpdateModus= false");
      _modell.DataChangedFromViewer( _tag, edit.getText() );
      _modell.setUpdateModus(false);
    }
  } // sendChangeMessage


}  // JFInternalFrame1


class JTextFieldDocListener extends JTextField   {
   private ITextField_Change _parent;
   public  boolean bModify=false;

   public JTextFieldDocListener(ITextField_Change parent) {
     super();
     init(parent);
   }


   public JTextFieldDocListener(ITextField_Change parent, String sText) {
     super(sText);
     init(parent);
   }

   private void init(ITextField_Change parent) {
     _parent = parent;
       // ableiten der Update-Methoden des Listeners
     this.getDocument().addDocumentListener(new DocumentListener(){
         public void changedUpdate(DocumentEvent ev) {
             System.out.println("changedUpdate");
             _parent.sendChangeMessage();
         }
         
         public void insertUpdate(DocumentEvent ev) {
             System.out.println("changed insert");
             _parent.sendChangeMessage();
         }
         
         public void removeUpdate(DocumentEvent ev) {
             System.out.println("changed remove");
             _parent.sendChangeMessage();
         }
     });     
    } // init
}  // JTextFieldDocListener

