//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 Observer mit Enter-Taste fuer den Datentransfer


// Beispiel für das Observer Design Pattern
// Das onChange-Event wird ueber die Klasse JTextFieldDocListener implementiert


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


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

// 1. Import 
import java.util.Observable;
import java.util.Observer;

import java.util.ArrayList;


public class Observer2 extends JFrame {
  private int anz=0;

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


   // 4. Schritt  die Modell Variable im JFrame deklarieren
  private Modell modell;   // Modell 

   // Konstruktor
  public Observer2() {
    this.setSize(new Dimension(200, 100));
    setLocation(200,70);
    setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
    setGUI();
  }

  public void setGUI() {
    this.getContentPane().setLayout( new BorderLayout() );
    setMenus();
    // 5. Schritt  die Modell Variable erzeugen
    modell = new Modell();
    modell.setName("Paul");
  }



    // 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);

    for (int i = 0; i < menuBar1.getComponentCount(); i++) {
      JMenu main = (JMenu) menuBar1.getComponent(i);
      main.setFont(new Font("Arial", Font.BOLD, 18));
      for (int j = 0; j < main.getItemCount(); j++) {
        Component c = main.getItem(j);
        if (c instanceof JMenuItem) {
          JMenuItem mn = (JMenuItem) c;
          mn.setFont(new Font("Arial", Font.BOLD, 18));
        }
      }
    }

  } // setMenus


  // Menü Aktions Methoden

  void MnNew_click() {
    // 7. Schritt  Viewer in der Methgode "New_click" erzeugen    
   JObserverFrame frame = new JObserverFrame(
          Integer.toString(anz), modell);
    frame.setLocation(  (anz%2)*390+10, 180+110*(anz/2)  );
    frame.setSize(330,80);
    ++anz;
  }

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



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


//   |
//  +--java.util.Observable

// 2. Schritt ableiten von Observable, damit Modell und Obervable
class Modell  extends java.util.Observable {
  String name= "Zappa";
  boolean bUpdateModus=false;

  public String getName() {
    return name;
  }

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


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

    // Diese Methode wird von einem Viewern aufgerufen
    //
    // 3. 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");
    }
  }

} // Modell


// 6. Schritt  im JFrame  Implements einbauen
class JObserverFrame extends JFrame implements Observer, ActionListener{
  private static int tagnr = 0;
  private int tag;
  private String sFileName;

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

  // 7. Schritt  Modell deklarieren
  private Modell modell;
  private String caption;


  // 8. Schritt  Konstruktor
  public JObserverFrame( String caption, Observable datenModell) {
    this.caption = caption;   
    setTitle(caption);
    this.tag=++tagnr;

    // 9. Schritt  globale Variable setzen
    modell = (Modell) datenModell;  //
    // 10. Schritt  im JFrame  registrierung
    modell.addObserver(this);  
    setGUI();
    setVisible(true);  // neu ab JDK 5.0
  }

  public void setGUI() {
    label1.setFont( new Font("Arial", Font.BOLD,28));

    // 11. Schritt  aus dem Modell die Daten holen
    modell.setUpdateModus(true);  // hier wird aktualisiert, sperre
    edit.setText(  modell.getName()     );
    modell.setUpdateModus(false);  // hier wird aktualisiert, sperre
    edit.setFont( new Font("Arial", Font.BOLD,28));

    this.setResizable(true);
    this.getContentPane().setLayout( new GridLayout(1,2) );

    this.getContentPane().add(label1);
    this.getContentPane().add(edit);
  }  // setGUI
  

  // 12. Schritt  Schnittstellen implementieren: 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

   // 14. Schritt  Methode edit_change (Enter) implementieren, Aufruf des Controllers
   // wird vom JTextFieldDocListener aufgerufen
   
  @Override
  public void actionPerformed(ActionEvent e){
    
        modell.DataChangedFromViewer( tag, edit.getText() );        
        modell.setUpdateModus(false);
/*
if ( modell.getUpdateModus() ) {
      System.out.println(" actionPerformedaction:  modell.getUpdateModus() ist an");
    }
    else {
      if (e.getSource()==edit) {
        modell.DataChangedFromViewer( tag, edit.getText() );        
        modell.setUpdateModus(false);
      }
    }*/
  }


}  // JObserverFrame


class JTextFieldDocListener extends JTextField   {
  ArrayList<ActionListener> listeners = new ArrayList<ActionListener>();

  public synchronized void addEventListener(ActionListener listener)  {
    listeners.add(listener);
  }
  public synchronized void removeEventListener(ActionListener listener)   {
    listeners.remove(listener);
  }
 
  JTextFieldDocListener thiss;
  
   public JTextFieldDocListener(ActionListener listener) {
     super();
     init(listener);
   }


   public JTextFieldDocListener(ActionListener listener, String sText) {
     super(sText);
     init(listener);
   }

   private void init(ActionListener listener) {
     addEventListener(listener);
     thiss = this;

     this.getDocument().addDocumentListener(new DocumentListener(){
        @Override
         public void changedUpdate(DocumentEvent ev) {
             System.out.println("changedUpdate");
             /*
              ActionEvent(Object source, int id, String command)
                        Constructs an ActionEvent object.
              ActionEvent(Object source, int id, String command, int modifiers)
                        Constructs an ActionEvent object with modifier keys.
              ActionEvent(Object source, int id, String command, long when, int modifiers)
                        Constructs an ActionEvent object with the specified modifier keys and timestamp.
             */
            
             for (ActionListener listener : listeners) {
               ActionEvent e = new ActionEvent( thiss,0,"update",0,0);
               listener.actionPerformed(e);
             }
         }
         
      @Override
         public void insertUpdate(DocumentEvent ev) {
             //System.out.println("changed insert");
             ActionEvent e = new ActionEvent(thiss,0,"insert",0,0);
             for (ActionListener listener : listeners) {
               listener.actionPerformed(e);
             }
         }
         
      @Override
         public void removeUpdate(DocumentEvent ev) {
             //System.out.println("changed remove");
             ActionEvent e = new ActionEvent(thiss,0,"remove",0,0);
             for (ActionListener listener : listeners) {
               listener.actionPerformed(e);
             }
         }
     });     
    } // init
}  // JTextFieldDocListener