/**
   * Constructor.
   *
   * @param eventSource source of the mouse and key events which will be translated into scripting
   *     language commands. Such a typical source is e.g. the VNC viewer panel.
   */
  public RecordingModule(
      MainFrame frame, Component eventSource, ScriptManager scriptManager, UserConfiguration cfg) {
    this.cfg = cfg;
    this.scriptManager = scriptManager;
    readOnly = cfg.getBoolean("rfb.readOnly").booleanValue();

    fb = (DesktopViewer) eventSource;
    fb.removeMouseListener(fb);
    eventSource.addMouseListener(this);
    fb.addMouseListener(fb);
    eventSource.addMouseMotionListener(this);
    eventSource.addMouseWheelListener(this);
    eventSource.addKeyListener(this);

    client = scriptManager.getClient();
    if (client != null) {
      client.addServerListener(this);
    }

    //        scriptManager.addMouseInputListener(this);
    //        scriptManager.addKeyListener(this);

    // Number of archived events
    //        events.setSize(EVENT_VECTOR_SIZE);

    // Populate the reversed keycode->keyname Map
    Map t = Utils.getKeyCodeTable();
    Iterator e = t.keySet().iterator();
    Object o;
    while (e.hasNext()) {
      o = e.next();
      keyCodes.put(t.get(o), o);
    }
    cfg.addConfigurationListener(this);
    scriptManager.addScriptListener(this);
    configurationChanged(null);
  }
  public void setEnabled(boolean enabled) {
    if (!enabled && this.enabled && lastElement != null) {
      // Insert a timeout to the last generated command
      Editor ed = editorPnl.getEditor();
      if (lastElement.getDocument().equals(ed.getDocument())) {
        Element e = ed.getCurrentElement();
        int offset = ed.getCaretPosition() - e.getStartOffset();
        ed.setCaretPosition(lastElement.getStartOffset());
        String text = DocumentUtils.getElementText(lastElement);
        int pos = text.indexOf(" ");
        String cmd = pos >= 0 ? text.substring(0, pos) : text;
        String time = convertTime(System.currentTimeMillis() - lastInsertTime);
        CommandHandler h =
            (CommandHandler) scriptManager.getCommandHandlers().get(cmd.toUpperCase());

        if (h != null
            && h.getContextAttributes() != null
            && h.getContextAttributes().containsKey("wait")) {
          text += " wait=" + time;
        } else {
          text += "\nWait " + time;
        }
        insertLine(text, true, false, false);
      }
    }
    this.enabled = enabled;
    fb.setRecordingMode(enabled);
    if (enabled) {
      firstRecord = true;
      lastElement = null;
      lastInsertTime = -1;
      lastMouseMoveEvent = null;
      events.clear();
      synchronized (rfbEvents) {
        rfbEvents.clear();
      }
    }
  }
  public synchronized Element insertLine(
      String command,
      boolean replaceLast,
      boolean insertPrecedingWait,
      boolean removePrecedingWait) {
    //        System.out.println("insertLine(\"" + command + "\")");
    EditorPnl editor = getEditorPnl();
    if (editor != null) {
      try {
        Document doc = editor.getEditor().getDocument();
        int caretPos = editor.getEditor().getCaretPosition();
        //                System.out.println("  -->Initial caret position: " + caretPos);
        int elemIndex = doc.getDefaultRootElement().getElementIndex(caretPos);
        boolean insertNewLine = true;

        Element ce = doc.getDefaultRootElement().getElement(elemIndex);
        String txt = DocumentUtils.getElementText(ce).trim();
        if (!replaceLast && caretPos == 0 && elemIndex == 0 && !"".equals(txt)) {
          doc.insertString(0, "\n", null);
          txt = "";
          insertNewLine = false;
        }

        // The following code finds out if we are in an empty line or not.
        // If the current line contains some text, a new line is inserted
        // and the caret is moved there.
        if (!txt.equals("")) {
          Element next = doc.getDefaultRootElement().getElement(elemIndex + 1);
          if (next == null || !DocumentUtils.getElementText(next).trim().equals("")) {
            //                        DocumentUtils.analyzeEditorDocument(editor.getEditor(),
            // false);
            //                        System.out.println("Inserting an empty line to offset " +
            // ce.getEndOffset());
            doc.insertString(ce.getEndOffset() - 1, "\n", null);
            //                        DocumentUtils.analyzeEditorDocument(editor.getEditor(),
            // false);
            next =
                doc.getDefaultRootElement()
                    .getElement(doc.getDefaultRootElement().getElementIndex(ce.getEndOffset() + 1));
          }
          //                    caretPos = next.getEndOffset()-1;
          caretPos = ce.getEndOffset();
          //                    System.out.println("Setting caret position to "+caretPos);
          //                    System.out.println("  -->1. Setting caret position to: " +
          // caretPos);
          editor.getEditor().setCaretPosition(caretPos);
        }

        Element e = DocumentUtils.getCommandElementPriorTo(doc, caretPos);
        //                System.out.println(" --> Element prior: " +
        // DocumentUtils.getElementText(e));

        // First look if we should insert a Wait command
        if (!replaceLast
            && (insertPrecedingWait || removePrecedingWait)
            && (!firstRecord && !removePrecedingWait)
            && e != null) {
          String prevCmd = DocumentUtils.getElementText(e);
          int pos = prevCmd.indexOf(" ");
          String cmd = pos >= 0 ? prevCmd.substring(0, pos) : prevCmd;
          String time = convertTime(System.currentTimeMillis() - lastInsertTime);
          CommandHandler h =
              (CommandHandler) scriptManager.getCommandHandlers().get(cmd.toUpperCase());

          if (!"waitfor".equalsIgnoreCase(cmd) && !"screenshot".equalsIgnoreCase(cmd)) {

            if (h != null
                && h.getContextAttributes() != null
                && h.getContextAttributes().containsKey("wait")) {
              doc.remove(e.getStartOffset(), prevCmd.length());
              String replaceStr = insertPrecedingWait ? " wait=" + time : "";
              String s =
                  prevCmd
                          .replaceAll("\\n", "")
                          .replaceAll("\\swait=[\"]*[0-9]*[sSmMhHdD]*[\"]*", "")
                      + replaceStr;
              doc.insertString(e.getStartOffset(), s, null);
              caretPos = e.getEndOffset();
              //                            System.out.println("  -->2. Setting caret position to: "
              // + caretPos);
            } else {
              String waitCmd = "Wait " + time + '\n';
              doc.insertString(caretPos, waitCmd, null);
              caretPos += waitCmd.length();
              //                            System.out.println("  -->3. Setting caret position to: "
              // + caretPos);
            }
          }
        }

        firstRecord = false;

        if (replaceLast && e != null) {
          int length = e.getEndOffset() - e.getStartOffset();
          doc.remove(e.getStartOffset(), length);
          caretPos = e.getStartOffset();
        }
        command = insertNewLine && !replaceLast ? command + "\n" : command;
        doc.insertString(caretPos, command, null);
        editor.getEditor().setCaretPosition(caretPos + 1);

        //                System.out.println(" --> Inserted, caretPos = " +
        // editor.getEditor().getCaretPosition());

        //                DocumentUtils.analyzeEditorDocument(editor.getEditor(), true);
        lastElement =
            doc.getDefaultRootElement()
                .getElement(doc.getDefaultRootElement().getElementIndex(caretPos));
        lastInsertTime = System.currentTimeMillis();
        filterRfbEvents();

      } catch (Exception ex) {
        ex.printStackTrace();
      }
    }
    return lastElement;
  }