/* Edit.java -- prosty edytor tekstu.  Matt Mahoney, mmahoney@cs.fit.edu

To edit a file, enter the following command:

  java Edit [filename]

where [filename] is optional.  You can then edit or create a file by
typing into the text box.  If the file does not exist, then a new
one will be created.  If no filename is given, then you can begin typing
and then are prompted to enter a file name when saving or exiting.

The program displays a text box for editing and a menu bar as follows:

  ------------------------------------------------
  | Edit - Untitled                       _ [] X |
  |----------------------------------------------|
  | File                                         |
  |----------------------------------------------|
  | New          |                               |
  | Open         |                               |
  | Save  Ctrl+S |                               |
  | Save as      |                               |
  | Print        |    --------------------       |
  | ------------ |    |  Save changes?   |       |
  | Exit  Ctrl+X |    | -------  ------- |       |
  |--------------|    | | Yes |  |  No | |       |
  |                   | -------  ------- |       |
  |                   --------------------       |
  |                                              |
  ------------------------------------------------

Selecting NEW creates a blank, untitled file for editing.  If the
previous file has been changed since last being saved, you are
prompted to save the file.  If you click "Yes" and the file is
untitled, you are prompted for a file name in a "Save as" dialog box.

Selecting OPEN prompts for a file name with an "Open" dialog box.
If the file exists, it is loaded into the text box.  Otherwise the
text box is cleared.  If the contents of the text box have changed,
then you are first prompted to save as with NEW.

Selecting SAVE saves the current file to disk.  If the file is untitled,
you are prompted for a file name, which then appears in the title bar.

Selecting SAVE AS prompts you to enter a file name whether or not
the file is untitled.  The new file name then becomes the new title.

Selecting PRINT prints the file to the printer.

Selecting EXIT or clicking on the [X] (close) box exits the program
and closes the window.  If the file has been changed since the last
save, you are asked whether you wish to save the file as with NEW.
*/

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

// The main program
public class Edit extends Frame
    implements ActionListener, WindowListener, TextListener {

  private TextArea textArea=new TextArea("", 25, 80);  // Edit window
  private String filename="Untitled";  // File name
  private boolean isChanged=false;  // Does the file need saving?

  // Constructor, args are passed from main()
  public Edit(String args[]) {

    // Add menus.  Add self as event handler to menu items.
    MenuBar mb=new MenuBar();
    setMenuBar(mb);
    Menu m=new Menu("File");
    mb.add(m);
    MenuItem mi;
    mi=new MenuItem("New");
    mi.addActionListener(this);
    m.add(mi);
    mi=new MenuItem("Open");
    mi.addActionListener(this);
    m.add(mi);
    mi=new MenuItem("Save", new MenuShortcut(KeyEvent.VK_S));
    mi.addActionListener(this);
    m.add(mi);
    mi=new MenuItem("Save as");
    mi.addActionListener(this);
    m.add(mi);
    mi=new MenuItem("Print");
    mi.addActionListener(this);
    m.add(mi);
    m.insertSeparator(5);  // Insert line
    mi=new MenuItem("Exit", new MenuShortcut(KeyEvent.VK_X));
    mi.addActionListener(this);
    m.add(mi);

    // Add the textArea to the window.  Add self as event handler
    setLayout(new BorderLayout());
    add("Center", textArea);
    textArea.setFont(new Font("Monospaced", Font.PLAIN, 12)); // 12pt courier
    textArea.addTextListener(this);

    // Add self as event handler for window closing
    addWindowListener(this);

    // Set the title and load the file.  If no file name is given, create
    // a blank file titled "Untitled".
    if (args.length>0)
      loadFile(args[0]);  // Read or create file
    else
      loadFile("Untitled");  // Create new untitled file

    // Make the window visible
    pack();  // Set the window size to fit all components
    show();  // Make window visible
  }

  // Start the program
  public static void main(String[] args) {
    new Edit(args);
  }

  // saveChanges() -- If the text has been edited since the last
  // load or save, ask the user (in a Yes-No dialog) whether to save
  // changes.  If yes, then save the file with the current filename.
  // If untitled, prompt for a filename (using a Save-as file dialog).

  private void saveChanges() {
    if (isChanged && new ChoiceDialog(this, new String[] {
        "Save changes to "+filename+"?", "Yes", "No"}).get()==1)  // yes?
      saveFile(false);
  }

  // loadFile(newfile) -- Clear the edit window and load a new file into
  // the editor.  If the previous file needs saving, then save it first.  Set
  // the title to the new file.  If the new file is null, then create a new
  // blank untitled file.  If the new file does not exist, then
  // start with a blank text field and warn the user.

  private void loadFile(String newfile) {
    saveChanges();
    textArea.setText("");
    rename(newfile);

    // Read the file if it exists
    if (!filename.equals("Untitled")) {
      try {
        InputStream in=new FileInputStream(filename);
        int n;
        byte[] a=new byte[4096];
        while ((n=in.read(a))>0)
          textArea.append(new String(a, 0, n));
        in.close();
      }
      catch (FileNotFoundException x) {  // Warn if new file
        new ChoiceDialog(this, new String[] {
          "Creating new file: "+filename, "OK"}).get();
      }
      catch (IOException x) {}
    }
    isChanged=false;
  }

  // Save the file.  Prompt for a filename if untitled or prompt is true
  private void saveFile(boolean prompt) {
    if (filename.equals("Untitled") || prompt) {
      FileDialog d=new FileDialog(this, "", FileDialog.SAVE);
      d.show();
      if (d.getFile()!=null)
        rename(d.getDirectory()+d.getFile());
    }
    // Save the file.  Display any errors in an OK dialog box
    try {
      Writer out=new FileWriter(filename);
      out.write(textArea.getText());
      out.close();
      isChanged=false;
    }
    catch (IOException x) {
      new ChoiceDialog(this, new String[] {
        "Error saving "+filename+": "+x, "OK"});
    }
  }

  // If the file needs saving, prompt user before exiting
  private void exit(Window parent) {
    saveChanges();
    dispose();
    System.exit(0);
  }

  // Change filename and the title bar to newname
  private void rename(String newname) {
    filename=newname;
    setTitle("Edit - "+filename);
  }

  // Event handlers
  public void windowActivated(WindowEvent e)   {}
  public void windowClosed(WindowEvent e)      {}
  public void windowDeactivated(WindowEvent e) {}
  public void windowDeiconified(WindowEvent e) {}
  public void windowIconified(WindowEvent e)   {}
  public void windowOpened(WindowEvent e)      {}

  // User clicks on [X]
  public void windowClosing(WindowEvent e) {
    exit(e.getWindow());
  }

  // Text is edited
  public void textValueChanged(TextEvent e) {
    isChanged=true;
  }

  // Menu item event handlers
  public void actionPerformed(ActionEvent e) {

    // New -- Create untitled file
    if (e.getActionCommand().equals("New"))
      loadFile("Untitled");

    // Open -- prompt for file name and load
    else if (e.getActionCommand().equals("Open")) {
      System.out.println("Open");
      FileDialog d=new FileDialog(this, "", FileDialog.LOAD);
      d.show();
      if (d.getFile()!=null)
        loadFile(d.getDirectory()+d.getFile());
    }

    // Save -- use current file name unless untitled
    else if (e.getActionCommand().equals("Save"))
      saveFile(false);

    // Save as -- prompt for new file name
    else if (e.getActionCommand().equals("Save as"))
      saveFile(true);

    // Print to printer
    else if (e.getActionCommand().equals("Print")) {
      PrintJob pj=Toolkit.getDefaultToolkit().getPrintJob(this, filename,
        new Properties());  // opens printer properties dialog box
      if (pj!=null) {  // user has a printer and didn't cancel?
        final int fontsize=10;  // in pts
        final Font f=new Font("Monospaced", Font.PLAIN, fontsize);
        final int height=pj.getPageDimension().height; // in pts
        final int margin=pj.getPageResolution()/2;  // 1/2 inch margins
        String text=textArea.getText();  // text to be printed
        int ypos=height;  // vertical position from top of page in pts
        Graphics g=null;  // graphics for current page
        while (text!=null && text.length()>0) {
          if (ypos>height-margin) { // start a new page
            if (g!=null)
              g.dispose();  // eject previous page if any
            g=pj.getGraphics();  // start next page
            g.setFont(f);
            g.setColor(Color.black);
            ypos=margin+fontsize;  // go to top of page
          }
          String s="";  // next line of text to print
          int i=text.indexOf('\n');  // remove first line of text, put in s
          if (i<0) {  // last line?
            s=text;
            text=null;
          }
          else if (i>80) {  // break long line
            s=text.substring(0, 80);
            text=text.substring(80);
          }
          else {  // normal line of text
            s=text.substring(0, i);
            text=text.substring(i+1);
          }
          if (s.endsWith("\r"))  // chop off CR in case lines end in CR-LF
            s=s.substring(0, s.length()-1);
          g.drawString(s, margin, ypos);  // print s
          ypos+=fontsize+2;
        }
        if (g!=null)
          g.dispose();  // eject last page
        pj.end();  // send job to printer
      }
    }

    // Exit -- same as click on [X]
    else if (e.getActionCommand().equals("Exit"))
      exit(this);
  }
}

/* A ChoiceDialog is used to display a message and obtain a multiple
choice response.  The constructor is passed the parent frame and
an array of strings.  The first string is the question, and the
remaining strings are the labels of the buttons which the user
may press.  The get() method returns the number of the button
pressed (starting with 1), or 0 if the user closed the box without
pressing a button.  The dialog box is modal: it remains active until
dismissed by the user.  For instance (when called from a Frame):

  new ChoiceDialog(this, new String[] {"Save changes?", "Yes", "No"}).get()

displays a dialog window like this on the center of the screen:

  |----------------------|
  |     Save changes?    |
  |                      |
  |   |-----|  |-----|   |
  |   | Yes |  | No  |   |
  |   |-----|  |-----|   |
  |                      |
  |----------------------|

then waits for the user to press a button.  It returns 1 for "Yes" and
2 for "No".
*/

class ChoiceDialog extends Dialog implements ActionListener {
  private Label label;       // question (args[0])
  private Button[] buttons;  // answer buttons (labelled args[1..n])
  private Panel panel;       // Container for buttons at bottom of dialog box
  private int answer=0;      // Last button clicked (1-n)

  // Constructor.  parent is the window creating the dialog.
  // args[0] is the question, args[1..n] are n possible answers.
  public ChoiceDialog(Frame parent, String[] args) {
    super(parent, "", true);  // no title, modal
    panel=new Panel();
    panel.setLayout(new FlowLayout());
    setLayout(new BorderLayout());
    add("Center", new Label(args[0]));
    add("South", panel);
    label=new Label(args[0]);
    buttons=new Button[args.length-1];
    for (int i=0; i<buttons.length; ++i) {
      panel.add(buttons[i]=new Button(args[i+1]));
      buttons[i].addActionListener(this);
    }
    pack();
  }

  // Prompt user, return button number of response (1 to n)
  public int get() {

    // Center the dialog on the screen
    Dimension screen=Toolkit.getDefaultToolkit().getScreenSize();
    Dimension comp=getSize();
    setLocation((screen.width-comp.width)/2, (screen.height-comp.height)/2);

    // Pop up modal window, wait for user to press a button
    show();
    return answer;  // Set by action event during show()
  }

  // Set answer to the number of the button that was pressed
  public void actionPerformed(ActionEvent e) {
    answer=0;
    for (int i=0; i<buttons.length; ++i)
      if (e.getSource()==buttons[i])
        answer=i+1;
    setVisible(false);
  }
}
