Package com.mucommander.ui.viewer.text

Source Code of com.mucommander.ui.viewer.text.TextEditorImpl

/*
* This file is part of muCommander, http://www.mucommander.com
* Copyright (C) 2002-2012 Maxence Bernard
*
* muCommander is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* muCommander is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

package com.mucommander.ui.viewer.text;

import java.awt.Font;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;

import javax.swing.JFrame;
import javax.swing.JTextArea;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.DefaultEditorKit;
import javax.swing.text.Document;

import com.mucommander.ui.theme.ColorChangedEvent;
import com.mucommander.ui.theme.FontChangedEvent;
import com.mucommander.ui.theme.Theme;
import com.mucommander.ui.theme.ThemeListener;
import com.mucommander.ui.theme.ThemeManager;

/**
* Text editor implementation used by {@link TextViewer} and {@link TextEditor}.
*
* @author Maxence Bernard, Mariusz Jakubowski, Nicolas Rinaudo, Arik Hadas
*/
class TextEditorImpl implements ThemeListener {

  private String searchString;

  private JFrame frame;

  private JTextArea textArea;

  /** Indicates whether there is a line separator in the original file */
  private boolean lineSeparatorExists;

  ////////////////////
  // Initialization //
  ////////////////////

  public TextEditorImpl(boolean isEditable) {
    // Initialize text area
    initTextArea(isEditable);

    // Listen to theme changes to update the text area if it is visible
    ThemeManager.addCurrentThemeListener(this);
  }

  private void initTextArea(boolean isEditable) {
    textArea = new JTextArea() {
      @Override
      public Insets getInsets() {
        return new Insets(4, 3, 4, 3);
      }
    };

    textArea.setEditable(isEditable);

    // Use theme colors and font
    textArea.setForeground(ThemeManager.getCurrentColor(Theme.EDITOR_FOREGROUND_COLOR));
    textArea.setCaretColor(ThemeManager.getCurrentColor(Theme.EDITOR_FOREGROUND_COLOR));
    textArea.setBackground(ThemeManager.getCurrentColor(Theme.EDITOR_BACKGROUND_COLOR));
    textArea.setSelectedTextColor(ThemeManager.getCurrentColor(Theme.EDITOR_SELECTED_FOREGROUND_COLOR));
    textArea.setSelectionColor(ThemeManager.getCurrentColor(Theme.EDITOR_SELECTED_BACKGROUND_COLOR));
    textArea.setFont(ThemeManager.getCurrentFont(Theme.EDITOR_FONT));

    textArea.setWrapStyleWord(true);

    textArea.addMouseWheelListener(new MouseWheelListener() {

      /**
       * Mouse events bubble up until finding a component with a relative listener.
       * That's why in case we get an event that needs to initiate its default behavior,
       * we just bubble it up to the parent component of the JTextArea. 
       */
      public void mouseWheelMoved(MouseWheelEvent e) {
        boolean isCtrlPressed = (e.getModifiers() & KeyEvent.CTRL_MASK)!=0;
        if (isCtrlPressed) {
          Font currentFont = textArea.getFont();
          int currentFontSize = currentFont.getSize();
          boolean rotationUp = e.getWheelRotation() < 0;
          if ((!rotationUp && currentFontSize > 1) || rotationUp) {
            Font newFont = new Font(currentFont.getName(), currentFont.getStyle(), currentFontSize + (rotationUp ? 1 : -1));
            textArea.setFont(newFont);
          }
        }
        else {
          textArea.getParent().dispatchEvent(e);
        }
      }
    });
  }

  /////////////////
  // Search code //
  /////////////////

  void find() {
    FindDialog findDialog = new FindDialog(frame);

    if(findDialog.wasValidated()) {
      searchString = findDialog.getSearchString().toLowerCase();

      if(!searchString.equals(""))
        doSearch(0, true);
    }

    // Request the focus on the text area which could be lost after the Find dialog was disposed
    textArea.requestFocus();
  }

  void findNext() {
    doSearch(textArea.getSelectionEnd(), true);
  }

  void findPrevious() {
    doSearch(textArea.getSelectionStart() - 1, false);
  }

  private String getTextLC() {
    return textArea.getText().toLowerCase();
  }

  private void doSearch(int startPos, boolean forward) {
    if (searchString == null || searchString.length() == 0)
      return;
    int pos;
    if (forward) {
      pos = getTextLC().indexOf(searchString, startPos);
    } else {
      pos = getTextLC().lastIndexOf(searchString, startPos);
    }
    if (pos >= 0) {
      textArea.select(pos, pos + searchString.length());
    } else {
      // Beep when no match has been found.
      // The beep method is called from a separate thread because this method seems to lock until the beep has
      // been played entirely. If the 'Find next' shortcut is left pressed, a series of beeps will be played when
      // the end of the file is reached, and we don't want those beeps to played one after the other as to:
      // 1/ not lock the event thread
      // 2/ have those beeps to end rather sooner than later
      new Thread() {
        @Override
        public void run() {
          Toolkit.getDefaultToolkit().beep();
        }
      }.start();
    }
  }

  public boolean isWrap() {
    return textArea.getLineWrap();
  }

  ////////////////////////////
  // Package-access methods //
  ////////////////////////////

  void wrap(boolean isWrap) {
    textArea.setLineWrap(isWrap);
    textArea.repaint();
  }

  void copy() {
    textArea.copy();
  }

  void cut() {
    textArea.cut();
  }

  void paste() {
    textArea.paste();
  }

  void selectAll() {
    textArea.selectAll();
  }

  void requestFocus() {
    textArea.requestFocus();
  }

  JTextArea getTextArea() {
    return textArea;
  }

  void addDocumentListener(DocumentListener documentListener) {
    textArea.getDocument().addDocumentListener(documentListener);
  }

  void read(Reader reader) throws IOException {
    // Feed the file's contents to text area
    textArea.read(reader, null);

    // If there are more than one lines, there is a line separator
    lineSeparatorExists = textArea.getLineCount() > 1;

    // Move cursor to the top
    textArea.setCaretPosition(0);
  }

  void write(Writer writer) throws IOException {
    Document document = textArea.getDocument();

    // According to the documentation in DefaultEditorKit, the line separator is set to be as the system property
    // if no other line separator exists in the file, but in practice it is not, so this is a workaround for it
    if (!lineSeparatorExists)
      document.putProperty(DefaultEditorKit.EndOfLineStringProperty, System.getProperty("line.separator"));

    try {
      textArea.getUI().getEditorKit(textArea).write(new BufferedWriter(writer), document, 0, document.getLength());
    }
    catch(BadLocationException e) {
      throw new IOException(e.getMessage());
    }
  }

  //////////////////////////////////
  // ThemeListener implementation //
  //////////////////////////////////

  /**
   * Receives theme color changes notifications.
   */
  public void colorChanged(ColorChangedEvent event) {
    switch(event.getColorId()) {
    case Theme.EDITOR_FOREGROUND_COLOR:
      textArea.setForeground(event.getColor());
      break;

    case Theme.EDITOR_BACKGROUND_COLOR:
      textArea.setBackground(event.getColor());
      break;

    case Theme.EDITOR_SELECTED_FOREGROUND_COLOR:
      textArea.setSelectedTextColor(event.getColor());
      break;

    case Theme.EDITOR_SELECTED_BACKGROUND_COLOR:
      textArea.setSelectionColor(event.getColor());
      break;
    }
  }

  /**
   * Receives theme font changes notifications.
   */
  public void fontChanged(FontChangedEvent event) {
    if(event.getFontId() == Theme.EDITOR_FONT)
      textArea.setFont(event.getFont());
  }
}
TOP

Related Classes of com.mucommander.ui.viewer.text.TextEditorImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.