ActionMap createActionMap() {
    ActionMap map = new ActionMapUIResource();

    Action refreshAction =
        new UIAction(FilePane.ACTION_REFRESH) {
          public void actionPerformed(ActionEvent evt) {
            getFileChooser().rescanCurrentDirectory();
          }
        };

    map.put(FilePane.ACTION_APPROVE_SELECTION, getApproveSelectionAction());
    map.put(FilePane.ACTION_CANCEL, getCancelSelectionAction());
    map.put(FilePane.ACTION_REFRESH, refreshAction);
    map.put(FilePane.ACTION_CHANGE_TO_PARENT_DIRECTORY, getChangeToParentDirectoryAction());
    return map;
  }
Example #2
0
  /**
   * Registers the keystroke of the given action as "command" of the given component.
   *
   * <p>This code is based on the Sulky-tools, found at &lt;http://github.com/huxi/sulky&gt;.
   *
   * @param aComponent the component that should react on the keystroke, cannot be <code>null</code>
   *     ;
   * @param aAction the action of the keystroke, cannot be <code>null</code>;
   * @param aCommandName the name of the command to register the keystore under.
   */
  public static void registerKeystroke(
      final JComponent aComponent, final Action aAction, final String aCommandName) {
    final KeyStroke keyStroke = (KeyStroke) aAction.getValue(Action.ACCELERATOR_KEY);
    if (keyStroke == null) {
      return;
    }

    InputMap inputMap = aComponent.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
    ActionMap actionMap = aComponent.getActionMap();
    inputMap.put(keyStroke, aCommandName);
    actionMap.put(aCommandName, aAction);

    inputMap = aComponent.getInputMap(JComponent.WHEN_FOCUSED);
    Object value = inputMap.get(keyStroke);
    if (value != null) {
      inputMap.put(keyStroke, aCommandName);
    }

    inputMap = aComponent.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
    value = inputMap.get(keyStroke);
    if (value != null) {
      inputMap.put(keyStroke, aCommandName);
    }
  }
Example #3
0
  EditFrame(RopeFrame parent) {
    super(parent);

    // Implement a smarter way to set the initial frame position and size
    setLocation(0, 0);
    setSize(670, 705);

    try {
      jbInit();
    } catch (Exception ex) {
      ex.printStackTrace();
    }

    sourceArea.addCaretListener(this);
    browseButton.addActionListener(this);
    optionsButton.addActionListener(this);
    assembleButton.addActionListener(this);
    saveButton.addActionListener(this);

    messageList.addMouseListener(
        new MouseAdapter() {
          @Override
          public void mouseClicked(MouseEvent event) {
            highlightError(messageList.locationToIndex(event.getPoint()));
          }
        });

    undoMgr = new CompoundUndoManager(sourceArea);

    undoAction = undoMgr.getUndoAction();
    redoAction = undoMgr.getRedoAction();

    undoMgr.updateUndoAction = new UpdateUndoAction();
    undoMgr.updateRedoAction = new UpdateRedoAction();

    document = sourceArea.getDocument();

    ActionMap am = sourceArea.getActionMap();
    InputMap im = sourceArea.getInputMap(JComponent.WHEN_FOCUSED);

    // Remove automatic key bindings because we want them controlled by menu items
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, RopeHelper.modifierMaks), "none");
    im.put(
        KeyStroke.getKeyStroke(KeyEvent.VK_Z, RopeHelper.modifierMaks + InputEvent.SHIFT_MASK),
        "none");
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, RopeHelper.modifierMaks), "none");
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, RopeHelper.modifierMaks), "none");
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, RopeHelper.modifierMaks), "none");
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, RopeHelper.modifierMaks), "none");
    im.put(KeyStroke.getKeyStroke(KeyEvent.VK_L, RopeHelper.modifierMaks), "none");

    // Set custom binding action for tab key
    String action = "tabKeyAction";
    im.put(KeyStroke.getKeyStroke("TAB"), action);
    am.put(
        action,
        new AbstractAction() {
          private static final long serialVersionUID = 1L;

          @Override
          public void actionPerformed(ActionEvent e) {
            try {
              int caretPos = sourceArea.getCaretPosition();
              int lineNum = sourceArea.getLineOfOffset(caretPos);
              int startLine = sourceArea.getLineStartOffset(lineNum);
              int endLine = sourceArea.getLineEndOffset(lineNum);
              int linePos = caretPos - startLine;

              if (linePos >= 39 && linePos < 79) {
                caretPos = startLine + linePos + 10 - ((linePos + 1) % 10);
              } else if (linePos >= 20 && linePos <= 39) {
                caretPos = startLine + 39;
              } else if (linePos >= 15 && linePos <= 19) {
                caretPos = startLine + 20;
              } else if (linePos >= 5 && linePos <= 14) {
                caretPos = startLine + 15;
              } else {
                caretPos = startLine + 5;
              }

              // If the line is shorter than the new position fo the caret add enough spaces...
              if (caretPos > endLine) {
                StringBuilder str = new StringBuilder();
                int size = caretPos - endLine;
                while (size-- >= 0) {
                  str.append(' ');
                }
                document.insertString(endLine - 1, str.toString(), null);
              }

              sourceArea.setCaretPosition(caretPos);
            } catch (BadLocationException ex) {
              ex.printStackTrace();
            }
          }
        });

    // Set custom binding action for return/enter key
    String actionKey = "backspaceKeyAction";
    im.put(KeyStroke.getKeyStroke("BACK_SPACE"), actionKey);
    am.put(
        actionKey,
        new AbstractAction()
        // How can I get the original action?
        {
          private static final long serialVersionUID = 1L;

          @Override
          public void actionPerformed(ActionEvent e) {
            try {
              int caretPos = sourceArea.getCaretPosition();
              int lineNum = sourceArea.getLineOfOffset(caretPos);
              int startLine = sourceArea.getLineStartOffset(lineNum);
              int endLine = sourceArea.getLineEndOffset(lineNum);
              int linePos = caretPos - startLine;

              if (linePos == 15) {
                int endPos = 5;
                int charPos = linePos;
                for (; charPos > endPos; charPos--) {
                  char ch = sourceArea.getText().charAt((startLine + charPos) - 1);
                  if (!Character.isWhitespace(ch)) {
                    break;
                  }
                }

                sourceArea.setCaretPosition(startLine + charPos);
              } else {
                int startSel = sourceArea.getSelectionStart();
                int endSel = sourceArea.getSelectionEnd();
                if (startSel == endSel) {
                  startSel = caretPos - 1;
                  endSel = caretPos;
                }

                StringBuilder sb = new StringBuilder(sourceArea.getText());
                sb.replace(startSel, endSel, "");
                sourceArea.setText(sb.toString());
                sourceArea.setCaretPosition(startSel);
              }
            } catch (BadLocationException ex) {
              ex.printStackTrace();
            }
          }
        });

    // Set custom binding action for return/enter key
    action = "enterKeyAction";
    im.put(KeyStroke.getKeyStroke("ENTER"), action);
    am.put(
        action,
        new AbstractAction() {
          private static final long serialVersionUID = 1L;

          @Override
          public void actionPerformed(ActionEvent e) {
            try {
              int caretPos = sourceArea.getCaretPosition();
              int lineNum = sourceArea.getLineOfOffset(caretPos);
              int startLine = sourceArea.getLineStartOffset(lineNum);
              int linePos = caretPos - startLine;

              if (linePos >= 5) {
                document.insertString(caretPos, "\n     ", null);
              } else {
                document.insertString(caretPos, "\n", null);
              }
            } catch (BadLocationException ex) {
              ex.printStackTrace();
            }
          }
        });

    document.addDocumentListener(
        new DocumentListener() {
          @Override
          public void insertUpdate(DocumentEvent e) {
            setSourceChanged(true);
          }

          @Override
          public void removeUpdate(DocumentEvent e) {
            setSourceChanged(true);
          }

          @Override
          public void changedUpdate(DocumentEvent e) {
            setSourceChanged(true);
          }
        });
  }
public class DistributedTextEditor extends JFrame {

  private static final long serialVersionUID = -1412971829037207445L;

  private static DistributedTextEditor editor;

  private JTextArea area1 = new JTextArea(20, 120);
  private JTextArea area2 = new JTextArea(20, 120);
  private JTextField ipaddress = new JTextField("localhost");
  private JTextField portNumber = new JTextField("4242");

  private EventReplayer er;
  private Thread ert;

  private JFileChooser dialog = new JFileChooser(System.getProperty("user.dir"));

  private String currentFile = "Untitled";
  private boolean changed = false;
  private boolean connected = false;
  private boolean active = false;
  private boolean locked = false;
  private boolean listen = false;
  private DocumentEventCapturer dec;
  private ServerSocket serverSocket;
  private Socket clientSocket;
  private LamportClock lc;
  private int serverport;

  public DistributedTextEditor() {
    area1.setFont(new Font("Monospaced", Font.PLAIN, 12));

    area2.setFont(new Font("Monospaced", Font.PLAIN, 12));
    ((AbstractDocument) area1.getDocument()).setDocumentFilter(dec);
    area2.setEditable(false);

    Container content = getContentPane();
    content.setLayout(new BoxLayout(content, BoxLayout.Y_AXIS));

    JScrollPane scroll1 =
        new JScrollPane(
            area1, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
    content.add(scroll1, BorderLayout.CENTER);

    JScrollPane scroll2 =
        new JScrollPane(
            area2, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
    content.add(scroll2, BorderLayout.CENTER);

    content.add(ipaddress, BorderLayout.CENTER);
    content.add(portNumber, BorderLayout.CENTER);

    JMenuBar JMB = new JMenuBar();
    setJMenuBar(JMB);
    JMenu file = new JMenu("File");
    JMenu edit = new JMenu("Edit");
    JMB.add(file);
    JMB.add(edit);

    file.add(Listen);
    file.add(Connect);
    file.add(Disconnect);
    file.addSeparator();
    file.add(Save);
    file.add(SaveAs);
    file.add(Quit);

    edit.add(Copy);
    edit.add(Paste);
    edit.getItem(0).setText("Copy");
    edit.getItem(1).setText("Paste");

    Save.setEnabled(false);
    SaveAs.setEnabled(false);
    Disconnect.setEnabled(false);

    setDefaultCloseOperation(EXIT_ON_CLOSE);
    pack();
    area1.addKeyListener(k1);
    area1.addMouseListener(m1);
    setTitle("Disconnected");
    setVisible(true);
    area1.insert("Welcome to Hjortehandlerne's distributed text editor. \n", 0);

    this.addWindowListener(w1);
  }

  private WindowListener w1 =
      new WindowListener() {
        /** Kill all active threads */
        @Override
        public void windowClosing(WindowEvent e) {
          disconnect();
        }

        @Override
        public void windowActivated(WindowEvent e) {}

        @Override
        public void windowClosed(WindowEvent e) {}

        @Override
        public void windowDeactivated(WindowEvent e) {}

        @Override
        public void windowDeiconified(WindowEvent e) {}

        @Override
        public void windowIconified(WindowEvent e) {}

        @Override
        public void windowOpened(WindowEvent e) {}
      };

  private KeyListener k1 =
      new KeyAdapter() {
        public void keyPressed(KeyEvent e) {
          changed = true;
          Save.setEnabled(true);
          SaveAs.setEnabled(true);
        }

        /**
         * The keyReleased event ensures that the caret-position is updated for both peers, when the
         * user moves the caret with the arrow-keys.
         */
        public void keyReleased(KeyEvent e) {
          int left = e.VK_LEFT;
          int right = e.VK_RIGHT;
          int up = e.VK_UP;
          int down = e.VK_DOWN;
          int home = e.VK_HOME;
          int end = e.VK_END;
          int A = e.VK_A;
          int kc = e.getKeyCode();

          if (kc == left
              || kc == right
              || kc == up
              || kc == down
              || kc == home
              || kc == end
              || (kc == A && e.isControlDown())) {
            if (dec == null) return;
            lc.increment();
            TextEvent cu = new CaretUpdate(area1.getCaretPosition(), lc.getTimeStamp());
            dec.sendObjectToAllPeers(cu);
            er.getEventHistoryLock().lock();
            er.getEventHistory().add(cu);
            er.getEventHistoryLock().unlock();
          }
        }
      };

  /**
   * This mouselistener ensures that both peers have an updated caret-position for this user, when
   * he moves his caret by a mouseclick.
   */
  private MouseListener m1 =
      new MouseAdapter() {
        public void mouseReleased(MouseEvent e) {
          if (e.getButton() == e.BUTTON1 && connected) {
            if (dec == null) return;
            lc.increment();
            TextEvent cu = new CaretUpdate(area1.getCaretPosition(), lc.getTimeStamp());
            dec.sendObjectToAllPeers(cu);
            er.getEventHistoryLock().lock();
            er.getEventHistory().add(cu);
            er.getEventHistoryLock().unlock();
          }
        }
      };

  /**
   * This action is called when the Listen-button is fired. It creates a serversocket, and awaits a
   * connection. When the first connection is made, a ConnectionData object is sent to the other
   * peer, and this editor creates new data for the incomming peer. After the first connection is
   * made, waitForConnection is called, which waits for more connections.
   */
  Action Listen =
      new AbstractAction("Listen") {
        public void actionPerformed(ActionEvent e) {
          saveOld();
          final InetAddress local;
          active = true;
          try {
            local = InetAddress.getLocalHost();
            Runnable server =
                new Runnable() {
                  public void run() {
                    serverport = Integer.parseInt(portNumber.getText());
                    int myID = getNewId();
                    registerOnPort();
                    editor.setTitleToListen();
                    clientSocket = waitForConnectionFromClient();
                    lc = new LamportClock(myID);
                    area1.setText("");
                    resetArea2();
                    if (clientSocket != null) {
                      listen = true;
                      connected = true;
                      dec = new DocumentEventCapturer(lc, editor);
                      setDocumentFilter(dec);
                      er = new EventReplayer(editor, dec, lc);
                      er.updateCaretPos(myID, 0);
                      ert = new Thread(er);
                      ert.start();

                      try {
                        ObjectOutputStream output =
                            new ObjectOutputStream(clientSocket.getOutputStream());
                        ObjectInputStream input =
                            new ObjectInputStream(clientSocket.getInputStream());
                        JoinNetworkRequest request = (JoinNetworkRequest) input.readObject();
                        int id = getNewId();
                        Peer peer =
                            new Peer(
                                editor,
                                er,
                                id,
                                clientSocket,
                                output,
                                input,
                                lc,
                                clientSocket.getInetAddress().getHostAddress(),
                                request.getPort());
                        ConnectionData cd =
                            new ConnectionData(
                                er.getEventHistory(),
                                er.getAcknowledgements(),
                                er.getCarets(),
                                id,
                                area1.getText(),
                                lc.getTimeStamp(),
                                lc.getID(),
                                dec.getPeers(),
                                serverSocket.getLocalPort());
                        dec.addPeer(peer);
                        er.addCaretPos(id, 0);
                        Thread t = new Thread(peer);
                        t.start();
                        peer.writeObjectToStream(cd);
                      } catch (IOException | ClassNotFoundException e) {
                        e.printStackTrace();
                      }
                    }
                    waitForConnection();
                  }
                };
            Thread serverThread = new Thread(server);
            serverThread.start();
          } catch (UnknownHostException e1) {
            e1.printStackTrace();
          }

          changed = false;
          Disconnect.setEnabled(true);
          Listen.setEnabled(false);
          Connect.setEnabled(false);
          Save.setEnabled(false);
          SaveAs.setEnabled(false);
        }
      };

  /**
   * waitForConnection waits for incomming connections. There are two cases. If we receive a
   * JoinNetworkRequest it means that a new peer tries to get into the network. We lock the entire
   * system, and sends a ConnectionData object to the new peer, from which he can connect to every
   * other peer. We add this new peer to our data.
   *
   * <p>If we receive a NewPeerDataRequest, it means that a new peer has received ConnectionData
   * from another peer in the network, and he is now trying to connect to everyone, including me. We
   * then update our data with the new peer.
   */
  private void waitForConnection() {
    while (active) {
      Socket client = waitForConnectionFromClient();
      if (client != null) {
        try {
          ObjectOutputStream output = new ObjectOutputStream(client.getOutputStream());
          ObjectInputStream input = new ObjectInputStream(client.getInputStream());
          Object o = input.readObject();

          if (o instanceof JoinNetworkRequest) {
            JoinNetworkRequest request = (JoinNetworkRequest) o;
            dec.sendObjectToAllPeers(new LockRequest(lc.getTimeStamp()));
            waitForAllToLock();
            setLocked(true);
            Thread.sleep(500);
            int id = getNewId();
            Peer p =
                new Peer(
                    editor,
                    er,
                    id,
                    client,
                    output,
                    input,
                    lc,
                    client.getInetAddress().getHostAddress(),
                    request.getPort());
            ConnectionData cd =
                new ConnectionData(
                    er.getEventHistory(),
                    er.getAcknowledgements(),
                    er.getCarets(),
                    id,
                    area1.getText(),
                    lc.getTimeStamp(),
                    lc.getID(),
                    dec.getPeers(),
                    serverSocket.getLocalPort());
            p.writeObjectToStream(cd);
            dec.addPeer(p);
            Thread t = new Thread(p);
            t.start();
            er.addCaretPos(id, 0);
          } else if (o instanceof NewPeerDataRequest) {
            NewPeerDataRequest request = (NewPeerDataRequest) o;
            Peer newPeer =
                new Peer(
                    editor,
                    er,
                    request.getId(),
                    client,
                    output,
                    input,
                    lc,
                    client.getInetAddress().getHostAddress(),
                    request.getPort());
            dec.addPeer(newPeer);
            er.addCaretPos(request.getId(), request.getCaretPos());
            newPeer.writeObjectToStream(new NewPeerDataAcknowledgement(lc.getTimeStamp()));
            Thread t = new Thread(newPeer);
            t.start();
          }
        } catch (IOException | ClassNotFoundException | InterruptedException e) {
          e.printStackTrace();
        }
      }
    }
  }

  private void waitForAllToLock() {
    for (Peer p : dec.getPeers()) {
      while (!p.isLocked() && p.isConnected()) {
        try {
          Thread.sleep(100);
        } catch (InterruptedException e) {
        }
      }
      p.setLocked(false);
    }
  }

  /**
   * Will register this server on the port number portNumber. Will not start waiting for
   * connections. For this you should call waitForConnectionFromClient().
   */
  private void registerOnPort() {
    try {
      serverSocket = new ServerSocket(Integer.parseInt(portNumber.getText()));
    } catch (IOException e) {
      serverSocket = null;
      System.err.println("Cannot open server socket on port number" + portNumber.getText());
      System.err.println(e);
      System.exit(-1);
    }
  }

  /** Closes the serversocket */
  public void deregisterOnPort() {
    if (serverSocket != null) {
      try {
        serverSocket.close();
        serverSocket = null;
      } catch (IOException e) {
        System.err.println(e);
      }
    }
  }

  /**
   * Waits for the next client to connect on port number portNumber or takes the next one in line in
   * case a client is already trying to connect. Returns the socket of the connection, null if there
   * were any failures.
   */
  private Socket waitForConnectionFromClient() {
    Socket res = null;
    try {
      res = serverSocket.accept();
    } catch (IOException e) {
    }
    return res;
  }

  /**
   * This action is called when the Connect-button is fired. It creates a ClientSocket with the
   * known peer, and opens a serversocket to listen for new peers. We send a JoinNetworkRequest to
   * the known peer, and receive ConnectionData from him. We then connect to all other peers in the
   * network, and update our data from the received ConnectionData. At last we call
   * waitForConnection() to wait for new connections from new peers, and finally we tell everyone to
   * unlock the system
   */
  Action Connect =
      new AbstractAction("Connect") {
        public void actionPerformed(ActionEvent e) {
          saveOld();
          area1.setText("");
          resetArea2();
          try {
            clientSocket = new Socket(ipaddress.getText(), Integer.parseInt(portNumber.getText()));
            Random r = new Random();
            serverport = 10000 + r.nextInt(8999); // random port :D

            serverSocket = new ServerSocket(serverport);
            active = true;
            editor.setTitleToListen();

            connected = true;

            ObjectOutputStream output = new ObjectOutputStream(clientSocket.getOutputStream());
            ObjectInputStream input = new ObjectInputStream(clientSocket.getInputStream());
            output.writeObject(new JoinNetworkRequest(serverport));

            ConnectionData data = getConnectionData(clientSocket, input);

            lc = new LamportClock(data.getId());
            lc.setMaxTime(data.getTs());
            dec = new DocumentEventCapturer(lc, editor);
            er = new EventReplayer(editor, dec, lc);
            ert = new Thread(er);
            ert.start();

            Peer peer =
                new Peer(
                    editor,
                    er,
                    data.getHostId(),
                    clientSocket,
                    output,
                    input,
                    lc,
                    clientSocket.getInetAddress().getHostAddress(),
                    data.getPort());
            dec.addPeer(peer);
            Thread thread = new Thread(peer);
            thread.start();

            er.setAcknowledgements(data.getAcknowledgements());
            er.setEventHistory(data.getEventHistory());
            er.setCarets(data.getCarets());

            er.addCaretPos(lc.getID(), 0);

            for (PeerWrapper p : data.getPeers()) {
              Socket socket;
              try {
                socket = connectToPeer(p.getIP(), p.getPort());
                ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream());
                ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
                outputStream.writeObject(
                    new NewPeerDataRequest(lc.getID(), serverSocket.getLocalPort(), 0));
                Peer newPeer =
                    new Peer(
                        editor,
                        er,
                        p.getId(),
                        socket,
                        outputStream,
                        inputStream,
                        lc,
                        p.getIP(),
                        p.getPort());
                dec.addPeer(newPeer);
                Thread t = new Thread(newPeer);
                t.start();
              } catch (IOException ex) {
                continue;
              }
            }

            Thread t1 =
                new Thread(
                    new Runnable() {

                      @Override
                      public void run() {
                        waitForConnection();
                      }
                    });
            t1.start();
            area1.setText(data.getTextField());
            area1.setCaretPosition(0);
            setDocumentFilter(dec);

            dec.sendObjectToAllPeers(new UnlockRequest(lc.getTimeStamp()));

            changed = false;
            Connect.setEnabled(false);
            Disconnect.setEnabled(true);
            Listen.setEnabled(false);
            Save.setEnabled(false);
            SaveAs.setEnabled(false);
          } catch (NumberFormatException | IOException e1) {
            setTitle("Unable to connect");
          }
        }

        private Socket connectToPeer(String ip, int port) {
          try {
            Socket peer = new Socket(ip, port);
            return peer;
          } catch (IOException e) {
            e.printStackTrace();
          }
          return null;
        }

        private ConnectionData getConnectionData(Socket clientSocket, ObjectInputStream input) {
          ConnectionData res;
          try {
            Object o = input.readObject();
            res = (ConnectionData) o;
            return res;
          } catch (IOException | ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
          }
          return null;
        }
      };

  /** This action is called when the Disconnect-button is fired. */
  Action Disconnect =
      new AbstractAction("Disconnect") {
        public void actionPerformed(ActionEvent e) {
          disconnect();
        }
      };

  /**
   * The disconnect method will stop the eventreplayer, and send null to the other peers, to stop
   * their reading from their streams. The GUI-menu is updated appropriately.
   */
  public void disconnect() {
    setTitle("Disconnected");
    active = false;
    if (connected == true) {
      er.stopStreamToQueue();
      ert.interrupt();
      setDocumentFilter(null);
      connected = false;
      setLocked(false);
    }
    deregisterOnPort();
    Disconnect.setEnabled(false);
    Connect.setEnabled(true);
    Listen.setEnabled(true);
    Save.setEnabled(true);
    SaveAs.setEnabled(true);
  }

  Action Save =
      new AbstractAction("Save") {
        public void actionPerformed(ActionEvent e) {
          if (!currentFile.equals("Untitled")) saveFile(currentFile);
          else saveFileAs();
        }
      };

  Action SaveAs =
      new AbstractAction("Save as...") {
        public void actionPerformed(ActionEvent e) {
          saveFileAs();
        }
      };

  Action Quit =
      new AbstractAction("Quit") {
        public void actionPerformed(ActionEvent e) {
          saveOld();
          System.exit(0);
        }
      };

  ActionMap m = area1.getActionMap();
  Action Copy = m.get(DefaultEditorKit.copyAction);
  Action Paste = m.get(DefaultEditorKit.pasteAction);

  private void saveFileAs() {
    if (dialog.showSaveDialog(null) == JFileChooser.APPROVE_OPTION)
      saveFile(dialog.getSelectedFile().getAbsolutePath());
  }

  private void saveOld() {
    if (changed) {
      if (JOptionPane.showConfirmDialog(
              this,
              "Would you like to save " + currentFile + " ?",
              "Save",
              JOptionPane.YES_NO_OPTION)
          == JOptionPane.YES_OPTION) saveFile(currentFile);
    }
  }

  private void saveFile(String fileName) {
    try {
      FileWriter w = new FileWriter(fileName);
      area1.write(w);
      w.close();
      currentFile = fileName;
      changed = false;
      Save.setEnabled(false);
    } catch (IOException e) {
    }
  }

  public boolean getActive() {
    return active;
  }

  public DocumentFilter getDocumentFilter() {
    return dec;
  }

  public JTextArea getTextArea() {
    return area1;
  }

  public DocumentEventCapturer getDocumentEventCapturer() {
    return dec;
  }

  public void setDocumentFilter(DocumentFilter filter) {
    ((AbstractDocument) area1.getDocument()).setDocumentFilter(filter);
  }

  public void setErrorMessage(String s) {
    area2.setText("Error: " + s);
  }

  public void setTitleToListen() {
    try {
      String serverString = serverSocket.getInetAddress().getLocalHost().toString();
      String serverIP = serverString.split("/")[1];
      editor.setTitle("Listening on: " + serverIP + ":" + serverport);
    } catch (UnknownHostException e) {
      e.printStackTrace();
    }
  }

  public void resetArea2() {
    area2.setText("");
  }

  public static void main(String[] arg) {
    editor = new DistributedTextEditor();
  }

  public void setLocked(boolean b) {
    locked = b;
    area1.setEnabled(!b);
  }

  public boolean getListen() {
    return listen;
  }

  public void setTextInArea2(String res) {
    area2.setText(res);
  }

  public EventReplayer getEventReplayer() {
    return er;
  }

  public int getNewId() {
    Random r = new Random();
    return r.nextInt(1000000);
  }
}
/**
 * A text editor created for my own use. Credit to
 * http://forum.codecall.net/topic/49721-simple-text-editor/
 *
 * @author Corey Hickson
 * @version 0.1
 */
public class CoreyTextEditor extends JFrame {

  // Variables to help initialize the text editor
  private JTextArea area = new JTextArea(20, 120);
  private JFileChooser dialog = new JFileChooser(System.getProperty("user.dir"));
  private String currentFile = "Untitled";
  private boolean changed = false;

  /** Constructor to create the frame and its components */
  public CoreyTextEditor() {
    // Create a scroll pane
    area.setFont(new Font("Monospaced", Font.PLAIN, 12));
    JScrollPane scroll =
        new JScrollPane(
            area, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
    add(scroll, BorderLayout.CENTER);

    // Adds the system default look and feel

    try {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (ClassNotFoundException
        | InstantiationException
        | UnsupportedLookAndFeelException
        | IllegalAccessException e) {
      e.printStackTrace();
    }

    // Create a menu bar
    JMenuBar JMB = new JMenuBar();
    setJMenuBar(JMB);
    JMenu file = new JMenu("File");
    JMenu edit = new JMenu("Edit");
    JMB.add(file);
    JMB.add(edit);

    // Finishing our menu bar
    file.add(New);
    file.add(Open);
    file.add(Save);
    file.add(SaveAs);
    file.addSeparator();
    file.add(Quit);

    edit.add(Cut);
    edit.add(Copy);
    edit.add(Paste);

    edit.getItem(0).setText("Cut");
    edit.getItem(0).setIcon(new ImageIcon("cut.gif"));
    edit.getItem(1).setText("Copy");
    edit.getItem(1).setIcon(new ImageIcon("copy.gif"));
    edit.getItem(2).setText("Paste");
    edit.getItem(2).setIcon(new ImageIcon("paste.gif"));

    // Time to make a toolbar!
    JToolBar tool = new JToolBar();
    add(tool, BorderLayout.NORTH);
    tool.add(New);
    tool.add(Open);
    tool.add(Save);
    tool.addSeparator();

    JButton cut = tool.add(Cut);
    JButton cop = tool.add(Copy);
    JButton pas = tool.add(Paste);

    cut.setText(null);
    cut.setIcon(new ImageIcon("cut.gif"));
    cop.setText(null);
    cop.setIcon(new ImageIcon("copy.gif"));
    pas.setText(null);
    pas.setIcon(new ImageIcon("paste.gif"));

    Save.setEnabled(false);
    SaveAs.setEnabled(false);

    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    pack();

    /*
     KeyListener to change Save and SaveAs
    */
    KeyListener k1 =
        new KeyAdapter() {
          public void keyPressed(KeyEvent e) {
            changed = true;
            Save.setEnabled(true);
            SaveAs.setEnabled(true);
          }
        };
    area.addKeyListener(k1);
    setTitle(currentFile + " - CoreyTextEditor");
    setVisible(true);
  }

  Action New =
      new AbstractAction("New", new ImageIcon("new.gif")) {
        public void actionPerformed(ActionEvent e) {
          saveOld();
          area.setText("");
          currentFile = "Untitled";
          setTitle(currentFile + " - CoreyTextEditor");
          changed = false;
          Save.setEnabled(false);
          SaveAs.setEnabled(false);
        }
      };

  Action Open =
      new AbstractAction("Open", new ImageIcon("open.gif")) {
        public void actionPerformed(ActionEvent e) {
          saveOld();
          if (dialog.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) {
            readInFile(dialog.getSelectedFile().getAbsolutePath());
          }
          SaveAs.setEnabled(true);
        }
      };

  Action Save =
      new AbstractAction("Save", new ImageIcon("save.gif")) {
        public void actionPerformed(ActionEvent e) {
          if (!currentFile.equals("Untitled")) {
            saveFile(currentFile);
          } else {
            saveFileAs();
          }
        }
      };

  Action SaveAs =
      new AbstractAction("Save as...") {
        public void actionPerformed(ActionEvent e) {
          saveFileAs();
        }
      };

  Action Quit =
      new AbstractAction("Quit") {
        public void actionPerformed(ActionEvent e) {
          saveOld();
          System.exit(0);
        }
      };

  ActionMap m = area.getActionMap();
  Action Cut = m.get(DefaultEditorKit.cutAction);
  Action Copy = m.get(DefaultEditorKit.copyAction);
  Action Paste = m.get(DefaultEditorKit.pasteAction);

  private void saveFileAs() {
    if (dialog.showSaveDialog(null) == JFileChooser.APPROVE_OPTION) {
      saveFile(dialog.getSelectedFile().getAbsolutePath());
    }
  }

  private void saveOld() {
    if (changed) {
      if (JOptionPane.showConfirmDialog(
              this,
              "Would you like to save " + currentFile + " ?",
              "Save",
              JOptionPane.YES_NO_OPTION)
          == JOptionPane.YES_OPTION) {
        saveFile(currentFile);
      }
    }
  }

  private void readInFile(String fileName) {
    try {
      FileReader r = new FileReader(fileName);
      area.read(r, null);
      r.close();
      currentFile = fileName;
      setTitle(currentFile + " - CoreyTextEditor");
      changed = false;
    } catch (IOException e) {
      Toolkit.getDefaultToolkit().beep();
      JOptionPane.showMessageDialog(this, "Editor can't find the file called " + fileName);
    }
  }

  private void saveFile(String fileName) {
    try {
      FileWriter w = new FileWriter(fileName);
      area.write(w);
      w.close();
      currentFile = fileName;
      setTitle(currentFile + " - CoreyTextEditor");
      changed = false;
      Save.setEnabled(false);
    } catch (IOException e) {
      // No handling done here
    }
  }

  public static void main(String[] args) {
    new CoreyTextEditor();
  }
}
  /**
   * Constructor, initialises the editor components.
   *
   * @param initialText The initial text to be displayed in the editor.
   * @param handler The GUI handler for this component.
   */
  public GUITextModelEditor(String initialText, GUIMultiModelHandler handler) {
    this.handler = handler;
    setLayout(new BorderLayout());

    // Setup the editor with it's custom editor kits. To switch between
    // editor kits just use setContentType() for the desired content type.
    editor =
        new JEditorPane() {
          @Override
          public String getToolTipText(MouseEvent event) {
            if (parseError != null) {
              try {
                int offset = this.viewToModel(new Point(event.getX(), event.getY()));

                int startOffset =
                    computeDocumentOffset(parseError.getBeginLine(), parseError.getBeginColumn());
                int endOffset =
                    computeDocumentOffset(parseError.getEndLine(), parseError.getEndColumn()) + 1;

                if (offset >= startOffset && offset <= endOffset) return parseError.getMessage();
              } catch (BadLocationException e) {
              }
            }

            return null;
          }
        };

    editor.setToolTipText("dummy");

    editor.setEditorKitForContentType("text/prism", new PrismEditorKit(handler));
    editor.setEditorKitForContentType("text/pepa", new PepaEditorKit(handler));
    // The default editor kit is the Prism one.
    editor.setContentType("text/prism");
    editor.setBackground(Color.white);
    editor.addMouseListener(editorMouseListener);
    editor.setEditable(true);
    editor.setText(initialText);
    editor.getDocument().addDocumentListener(this);
    editor.addCaretListener(
        new CaretListener() {
          public void caretUpdate(CaretEvent e) {
            GUITextModelEditor.this
                .handler
                .getGUIPlugin()
                .getSelectionChangeHandler()
                .notifyListeners(new GUIEvent(1));
          }
        });
    editor.getDocument().putProperty(PlainDocument.tabSizeAttribute, new Integer(4));

    editor.addMouseListener(this);
    errorHighlightPainter =
        new DefaultHighlighter.DefaultHighlightPainter(new Color(255, 192, 192));
    undoManager = new GUIUndoManager(GUIPrism.getGUI());
    undoManager.setLimit(200);

    // Setup the scrollpane
    editorScrollPane = new JScrollPane(editor);
    add(editorScrollPane, BorderLayout.CENTER);
    gutter = new GUITextModelEditorGutter(editor);

    // Get the 'show line numbers' setting to determine
    // if the line numbers should be shown.
    showLineNumbersSetting =
        handler
            .getGUIPlugin()
            .getPrism()
            .getSettings()
            .getBoolean(PrismSettings.MODEL_SHOW_LINE_NUMBERS);
    if (showLineNumbersSetting) {
      editorScrollPane.setRowHeaderView(gutter);
    }

    // Add a Prism settings listener to catch changes made to the
    // 'show line numbers' setting.
    handler
        .getGUIPlugin()
        .getPrism()
        .getSettings()
        .addSettingsListener(
            new PrismSettingsListener() {
              public void notifySettings(PrismSettings settings) {
                // Check if the setting has changed.
                if (settings.getBoolean(PrismSettings.MODEL_SHOW_LINE_NUMBERS)
                    != showLineNumbersSetting) {
                  showLineNumbersSetting = !showLineNumbersSetting;
                  if (showLineNumbersSetting) {
                    editorScrollPane.setRowHeaderView(gutter);
                  } else {
                    editorScrollPane.setRowHeaderView(null);
                  }
                }
              }
            });

    // initialize the actions for the context menu
    initActions();

    // method to initialize the context menu popup
    initContextMenu();

    InputMap inputMap = editor.getInputMap();
    inputMap.clear();

    inputMap.put(
        KeyStroke.getKeyStroke(KeyEvent.VK_Z, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
        "prism_undo");
    inputMap.put(
        KeyStroke.getKeyStroke(KeyEvent.VK_Z, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
        "prism_undo");
    inputMap.put(
        KeyStroke.getKeyStroke(KeyEvent.VK_Y, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
        "prism_redo");
    inputMap.put(
        KeyStroke.getKeyStroke(KeyEvent.VK_A, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
        "prism_selectall");
    inputMap.put(
        KeyStroke.getKeyStroke(KeyEvent.VK_D, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
        "prism_delete");
    inputMap.put(
        KeyStroke.getKeyStroke(KeyEvent.VK_X, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
        "prism_cut");
    inputMap.put(
        KeyStroke.getKeyStroke(
            KeyEvent.VK_Z,
            Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()
                | java.awt.event.InputEvent.SHIFT_MASK),
        "prism_redo");
    inputMap.put(
        KeyStroke.getKeyStroke(KeyEvent.VK_V, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
        "prism_paste");
    inputMap.put(
        KeyStroke.getKeyStroke(KeyEvent.VK_E, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()),
        "prism_jumperr");

    ActionMap actionMap = editor.getActionMap();
    actionMap.put("prism_undo", GUIPrism.getClipboardPlugin().getUndoAction());
    actionMap.put("prism_redo", GUIPrism.getClipboardPlugin().getRedoAction());
    actionMap.put("prism_selectall", GUIPrism.getClipboardPlugin().getSelectAllAction());
    actionMap.put("prism_cut", GUIPrism.getClipboardPlugin().getCutAction());
    actionMap.put("prism_copy", GUIPrism.getClipboardPlugin().getCopyAction());
    actionMap.put("prism_paste", GUIPrism.getClipboardPlugin().getPasteAction());
    actionMap.put("prism_delete", GUIPrism.getClipboardPlugin().getDeleteAction());
    actionMap.put("prism_jumperr", actionJumpToError);

    // Attempt to programmatically allow all accelerators
    /*ArrayList plugins = ((GUIMultiModel)handler.getGUIPlugin()).getGUI().getPlugins();
    Iterator it = plugins.iterator();

    while (it.hasNext())
    {
    	GUIPlugin plugin = ((GUIPlugin)it.next());
    	System.out.println(plugin.getName());
    	JMenu firstMenu = plugin.getMenu();

    	Stack<MenuElement> menuStack = new Stack<MenuElement>();

    	menuStack.add(firstMenu);

    	while (!menuStack.empty())
    	{
    		MenuElement menu = menuStack.pop();

    		if (menu instanceof JMenuItem)
    		{
    			JMenuItem menuItem = ((JMenuItem)menu);

    			KeyStroke accelerator = menuItem.getAccelerator();
    			Action action = menuItem.getAction();

    			if (action != null && accelerator != null && menuItem.getText() != null)
    			{
    				System.out.println(menuItem.getText() + " " + menuItem.getName());
    				inputMap.put(accelerator, "prism_" + menuItem.getText());
    				actionMap.put("prism_" + menuItem.getText(), action);
    			}
    		}

    		MenuElement[] subelements = menu.getSubElements();

    		if (subelements != null)
    		{
    			for (int i = 0; i < subelements.length; i++)
    				menuStack.push(subelements[i]);
    		}
    	}
    }*/

    editor.getDocument().addUndoableEditListener(undoManager);
    editor
        .getDocument()
        .addUndoableEditListener(
            new UndoableEditListener() {
              public void undoableEditHappened(UndoableEditEvent e) {
                System.out.println("adding undo edit");
              }
            });
  }