/*
 * jNPad v0.3 - jNPad's an Simple Text Editor written in Java
 *
 * Copyright (C) 2014-2017  rgs
 *
 * Require JDK 1.6 (or later)
 *
 * This program 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 2 of the License, or (at your option)
 * any later version.
 *
 * This program 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, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 *
 * Info, Questions, Suggestions & Bugs Report to rgsevero@gmail.com
 */

package jnpad;

import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;

import jnpad.action.ActionManager;
import jnpad.action.JNPadActions;
import jnpad.config.Config;
import jnpad.text.Buffer;
import jnpad.ui.EdgeBorder;
import jnpad.ui.JNPadLabel;
import jnpad.ui.JNPadMenuItem;
import jnpad.ui.status.HeapMonitor;
import jnpad.ui.status.IStatusBar;
import jnpad.util.LinePosition;
import jnpad.util.LineSeparator;
import jnpad.util.Utilities;

/**
 * The Class JNPadStatusBar.
 *
 * @version 0.3
 * @since   jNPad v0.1
 */
public class JNPadStatusBar extends JPanel implements IStatusBar {
  JPanel                      pnText                 = new JPanel();
  JPanel                      pnStatus               = new JPanel();

  JNPadMenuItem               miHeapMonitor          = new JNPadMenuItem(GUIUtilities.loadIcon("java-close.png"));                    //$NON-NLS-1$

  private final static String POSITION               = "Pos: ";                               //$NON-NLS-1$
  private final static String SELECTION              = "Sel: ";                               //$NON-NLS-1$
  private final static String OCCURRENCES            = "Oc: ";                                //$NON-NLS-1$

  private static final String MAX_STRING_LINE        = formatLine(9999, 999, 999, 9999, 999);
  private static final String MAX_STRING_POSITION    = formatPosition(999999, 999999);
  private static final String MAX_STRING_CHARACTER   = "M  9999  \\u00FF";                    //$NON-NLS-1$
  private static final String MAX_STRING_SELECTION   = formatSelection(999999);
  private static final String MAX_STRING_OCCURRENCES = formatOccurrences(9999);
  private static final String MAX_STRING_EOL         = "UNIX";                                //$NON-NLS-1$
  private static final String MAX_STRING_ENCODING    = "windows-1252";                        //$NON-NLS-1$
  private static final String MAX_STRING_TEXT_MODE   = formatTextMode(true);

  JLabel                      lbLine                 = new JNPadLabel();
  JLabel                      lbPosition             = new JNPadLabel();
  JLabel                      lbCharacter            = new JNPadLabel();
  JLabel                      lbSelection            = new JNPadLabel();
  JLabel                      lbOccurrences          = new JNPadLabel();
  JLabel                      lbEOL                  = new JNPadLabel();
  JLabel                      lbEncoding             = new JNPadLabel();
  JLabel                      lbTextMode             = new JNPadLabel();
  JLabel                      lbReadOnly             = new JNPadLabel();
  JLabel                      lbCapsLock             = new JNPadLabel();
  JLabel                      lbNumLock              = new JNPadLabel();

  JLabel                      lbStatusBar            = new JNPadLabel();

  private boolean             isHeapMonitorVisible   = Config.HEAP_MONITOR_VISIBLE.getValue();
  private HeapMonitor         heapMonitor;

  private Timer               timer;

  private JNPadFrame          jNPad;

  /** Logger */
  private final static Logger LOGGER                 = Logger.getLogger(JNPadStatusBar.class.getName());
  
  /** UID */
  private static final long   serialVersionUID       = -4781128306022933777L;

  /**
   * Instantiates a new jNPad status bar.
   *
   * @param jNPad the jNPad's frame
   */
  public JNPadStatusBar(JNPadFrame jNPad) {
    super(new BorderLayout());
    try {
      this.jNPad = jNPad;

      jbInit();
    }
    catch (Exception ex) {
      LOGGER.log(Level.WARNING, ex.getMessage(), ex);
    }
  }

  /**
   * Component initialization.
   *
   * @throws Exception the exception
   */
  private void jbInit() throws Exception {
    add(pnText, BorderLayout.NORTH);
    add(pnStatus, BorderLayout.CENTER);

    GUIUtilities.updateSize(lbLine, MAX_STRING_LINE);
    GUIUtilities.updateSize(lbPosition, MAX_STRING_POSITION);
    GUIUtilities.updateSize(lbCharacter, MAX_STRING_CHARACTER);
    GUIUtilities.updateSize(lbSelection, MAX_STRING_SELECTION);
    GUIUtilities.updateSize(lbOccurrences, MAX_STRING_OCCURRENCES);
    GUIUtilities.updateSize(lbEOL, MAX_STRING_EOL);
    GUIUtilities.updateSize(lbEncoding, MAX_STRING_ENCODING);
    GUIUtilities.updateSize(lbTextMode, MAX_STRING_TEXT_MODE);

    lbPosition.setHorizontalAlignment(SwingConstants.CENTER);
    lbCharacter.setHorizontalAlignment(SwingConstants.CENTER);
    lbSelection.setHorizontalAlignment(SwingConstants.CENTER);
    lbOccurrences.setHorizontalAlignment(SwingConstants.CENTER);
    lbEOL.setHorizontalAlignment(SwingConstants.CENTER);
    lbEncoding.setHorizontalAlignment(SwingConstants.CENTER);
    lbTextMode.setHorizontalAlignment(SwingConstants.CENTER);
    lbReadOnly.setHorizontalAlignment(SwingConstants.CENTER);
    lbCapsLock.setHorizontalAlignment(SwingConstants.CENTER);
    lbNumLock.setHorizontalAlignment(SwingConstants.CENTER);

    lbLine.setText(formatLine(1, 1, 1, 1, 1));
    lbLine.setToolTipText(JNPadBundle.getString("JNPadStatusBar.line.tooltip")); //$NON-NLS-1$

    lbPosition.setText(formatPosition(1, 1));
    lbPosition.setToolTipText(JNPadBundle.getString("JNPadStatusBar.position.tooltip")); //$NON-NLS-1$

    lbCharacter.setText(Utilities.EMPTY_STRING);
    lbCharacter.setToolTipText(JNPadBundle.getString("JNPadStatusBar.character.tooltip")); //$NON-NLS-1$
    
    lbSelection.setText(formatSelection(0));
    lbSelection.setToolTipText(JNPadBundle.getString("JNPadStatusBar.selection.tooltip")); //$NON-NLS-1$
    
    lbOccurrences.setText(formatOccurrences(0));
    lbOccurrences.setToolTipText(JNPadBundle.getString("JNPadStatusBar.occurrences.tooltip")); //$NON-NLS-1$
    
    lbEOL.setText("DOS"); //$NON-NLS-1$
    lbEOL.setToolTipText(JNPadBundle.getString("JNPadStatusBar.eol.tooltip")); //$NON-NLS-1$
    
    lbEncoding.setText(Config.FILE_ENCODING.getValue());
    lbEncoding.setToolTipText(JNPadBundle.getString("JNPadStatusBar.encoding.tooltip")); //$NON-NLS-1$
    
    lbTextMode.setText(formatTextMode(false));
    lbTextMode.setToolTipText(JNPadBundle.getString("JNPadStatusBar.textMode.tooltip")); //$NON-NLS-1$

    lbReadOnly.setText(JNPadBundle.getString("JNPadStatusBar.readOnly.text")); //$NON-NLS-1$
    lbReadOnly.setToolTipText(JNPadBundle.getString("JNPadStatusBar.readOnly.tooltip")); //$NON-NLS-1$
    lbReadOnly.setEnabled(false);
    
    try {
      lbCapsLock.setText(JNPadBundle.getString("JNPadStatusBar.capsLock.text")); //$NON-NLS-1$
      lbCapsLock.setEnabled(Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_CAPS_LOCK));      
    }
    catch (UnsupportedOperationException e) {
      lbCapsLock.setText("-"); //$NON-NLS-1$
      lbCapsLock.setEnabled(false);
    }
    lbCapsLock.setToolTipText(JNPadBundle.getString("JNPadStatusBar.capsLock.tooltip")); //$NON-NLS-1$
    
    try {
      lbNumLock.setText(JNPadBundle.getString("JNPadStatusBar.numLock.text")); //$NON-NLS-1$
      lbNumLock.setEnabled(Toolkit.getDefaultToolkit().getLockingKeyState(KeyEvent.VK_NUM_LOCK));      
    }
    catch (UnsupportedOperationException e) {
      lbNumLock.setText("-"); //$NON-NLS-1$
      lbNumLock.setEnabled(false);
    }
    lbNumLock.setToolTipText(JNPadBundle.getString("JNPadStatusBar.numLock.tooltip")); //$NON-NLS-1$

    pnStatus.setLayout(new BorderLayout());
    pnStatus.setBorder(new EdgeBorder(EdgeBorder.EDGE_TOP));
    pnStatus.add(lbStatusBar, BorderLayout.CENTER);
    if (isHeapMonitorVisible) {
      pnStatus.add(heapMonitor = new HeapMonitor(this), BorderLayout.EAST);
    }
    
    pnText.setLayout(new GridBagLayout());
    int gridx = 0, gridy = 0;

    pnText.add(lbLine, new GridBagConstraints(gridx++, gridy, 1, 1, 1.0, 0.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    addSeperator(gridx++, gridy);
    pnText.add(lbPosition, new GridBagConstraints(gridx++, gridy, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    addSeperator(gridx++, gridy);
    pnText.add(lbCharacter, new GridBagConstraints(gridx++, gridy, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    addSeperator(gridx++, gridy);
    pnText.add(lbSelection, new GridBagConstraints(gridx++, gridy, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    addSeperator(gridx++, gridy);
    pnText.add(lbOccurrences, new GridBagConstraints(gridx++, gridy, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    addSeperator(gridx++, gridy);
    pnText.add(lbEOL, new GridBagConstraints(gridx++, gridy, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    addSeperator(gridx++, gridy);
    pnText.add(lbEncoding, new GridBagConstraints(gridx++, gridy, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    addSeperator(gridx++, gridy);
    pnText.add(lbTextMode, new GridBagConstraints(gridx++, gridy, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    addSeperator(gridx++, gridy);
    pnText.add(lbReadOnly, new GridBagConstraints(gridx++, gridy, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    addSeperator(gridx++, gridy);
    pnText.add(lbCapsLock, new GridBagConstraints(gridx++, gridy, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
    addSeperator(gridx++, gridy);
    pnText.add(lbNumLock, new GridBagConstraints(gridx, gridy, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));

    final MouseListener mouseHandler = new MouseAdapter() {
      @Override
      public void mouseClicked(final MouseEvent e) {
        handleMouseClicked(e);
      }
    };

    final MouseListener popupHandler = new MouseAdapter() {
      @Override
      public void mouseClicked(final MouseEvent e) {
        if (e.isPopupTrigger()) {
          showPopupMenu(e);
        }
      }

      @Override
      public void mouseReleased(final MouseEvent e) {
        if (e.isPopupTrigger()) {
          showPopupMenu(e);
        }
      }
    };

    lbStatusBar.addMouseListener(popupHandler);
    lbLine.addMouseListener(mouseHandler);
    lbEOL.addMouseListener(mouseHandler);
    lbEncoding.addMouseListener(popupHandler);
    lbReadOnly.addMouseListener(mouseHandler);

    miHeapMonitor.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent e) {
        showHeapMonitor(!isHeapMonitorVisible);
      }
    });
  }

  /**
   * Adds the seperator.
   *
   * @param x int
   * @param y int
   */
  private void addSeperator(int x, int y) {
    pnText.add(new JSeparator(SwingConstants.VERTICAL), new GridBagConstraints(x, y, 1, 1, 0.0, 0.0, GridBagConstraints.EAST, GridBagConstraints.VERTICAL, new Insets(0, 5, 0, 5), 0, 0));
  }

  //////////////////////////////////////////////////////////////////////////////
  /**
   * The Class ResetTask.
   */
  private class ResetTask extends TimerTask {
    /**
     * Run.
     *
     * @see java.util.TimerTask#run()
     */
    @Override
    public void run() {
      SwingUtilities.invokeLater(new Runnable() {
        @Override public void run() {
          reset();
        }
      });
    }
  }
  //////////////////////////////////////////////////////////////////////////////

  /**
   * Reset.
   */
  private void reset() {
    if (timer != null) {
      timer.cancel();
      timer = null;
    }
    lbStatusBar.setIcon(null);
    clearStatus();
  }

  /**
   * Sets the line position.
   *
   * @param p the new line position
   * @see jnpad.ui.status.ITextStatusBar#setLinePosition(jnpad.util.LinePosition)
   */
  @Override
  public void setLinePosition(LinePosition p) {
    lbLine.setText(p.toString());
  }

  /**
   * Sets the position.
   *
   * @param pos the pos
   * @param len the len
   * @see jnpad.ui.status.ITextStatusBar#setPosition(int, int)
   */
  @Override
  public void setPosition(int pos, int len) {
    lbPosition.setText(formatPosition(pos, len));
  }

  /**
   * Sets the character.
   *
   * @param c the new character
   * @see jnpad.ui.status.ITextStatusBar#setCharacter(char)
   */
  @Override
  public void setCharacter(char c) {
    lbCharacter.setText(formatCharacter(c));
  }

  /**
   * Sets the selection.
   *
   * @param sel the new selection
   * @see jnpad.ui.status.ITextStatusBar#setSelection(int)
   */
  @Override
  public void setSelection(int sel) {
    lbSelection.setText(formatSelection(sel));
  }

  /**
   * Sets the occurrences.
   *
   * @param occ the new occurrences
   * @see jnpad.ui.status.ITextStatusBar#setOccurrences(int)
   */
  @Override
  public void setOccurrences(int occ) {
    lbOccurrences.setText(formatOccurrences(occ));
  }

  /**
   * Sets the eOL.
   *
   * @param ls the new eOL
   * @see jnpad.ui.status.ITextStatusBar#setEOL(jnpad.util.LineSeparator)
   */
  @Override
  public void setEOL(LineSeparator ls) {
    lbEOL.setText(ls.getName());
  }

  /**
   * Sets the encoding.
   *
   * @param encoding the new encoding
   * @see jnpad.ui.status.ITextStatusBar#setEncoding(java.lang.String)
   */
  @Override
  public void setEncoding(String encoding) {
    lbEncoding.setText(encoding);
  }

  /**
   * Checks if is overwrite text mode.
   *
   * @return boolean
   * @see jnpad.ui.status.ITextStatusBar#isOverwriteTextMode()
   */
  @Override
  public boolean isOverwriteTextMode() {
    return "OVR".equals(lbTextMode.getText()); //$NON-NLS-1$
  }
  
  /**
   * Sets the as overwrite text mode.
   *
   * @param b the new as overwrite text mode
   * @see jnpad.ui.status.ITextStatusBar#setAsOverwriteTextMode(boolean)
   */
  @Override
  public void setAsOverwriteTextMode(boolean b) {
    lbTextMode.setText(formatTextMode(b));
  }

  /**
   * Checks if is read only indicator enabled.
   *
   * @return boolean
   * @see jnpad.ui.status.ITextStatusBar#isReadOnlyIndicatorEnabled()
   */
  @Override
  public boolean isReadOnlyIndicatorEnabled() {
    return lbReadOnly.isEnabled();
  }

  /**
   * Sets the read only indicator enabled.
   *
   * @param b the new read only indicator enabled
   * @see jnpad.ui.status.ITextStatusBar#setReadOnlyIndicatorEnabled(boolean)
   */
  @Override
  public void setReadOnlyIndicatorEnabled(boolean b) {
    lbReadOnly.setEnabled(b);
  }

  /**
   * Checks if is caps lock indicator enabled.
   *
   * @return boolean
   * @see jnpad.ui.status.ITextStatusBar#isCapsLockIndicatorEnabled()
   */
  @Override
  public boolean isCapsLockIndicatorEnabled() {
    return lbCapsLock.isEnabled();
  }

  /**
   * Sets the caps lock indicator enabled.
   *
   * @param b boolean
   * @see jnpad.ui.status.ITextStatusBar#setCapsLockIndicatorEnabled(boolean)
   */
  @Override
  public void setCapsLockIndicatorEnabled(boolean b) {
    lbCapsLock.setEnabled(b);
  }

  /**
   * Checks if is num lock indicator enabled.
   *
   * @return boolean
   * @see jnpad.ui.status.ITextStatusBar#isNumLockIndicatorEnabled()
   */
  @Override
  public boolean isNumLockIndicatorEnabled() {
    return lbNumLock.isEnabled();
  }

  /**
   * Sets the num lock indicator enabled.
   *
   * @param b the new num lock indicator enabled
   * @see jnpad.ui.status.ITextStatusBar#setNumLockIndicatorEnabled(boolean)
   */
  @Override
  public void setNumLockIndicatorEnabled(boolean b) {
    lbNumLock.setEnabled(b);
  }

  /**
   * Gets the message.
   *
   * @param message_type StatusMessage
   * @return String
   * @see jnpad.ui.status.ITextStatusBar#getMessage(jnpad.ui.status.ITextStatusBar.StatusMessage)
   */
  @Override
  public String getMessage(StatusMessage message_type) {
    switch (message_type) {
      case STATUS     : return lbStatusBar.getText();
      case LINE       : return lbLine.getText();
      case POSITION   : return lbPosition.getText();
      case CHARACTER  : return lbCharacter.getText();
      case SELECTION  : return lbSelection.getText();
      case OCCURRENCES: return lbOccurrences.getText();
      case EOL        : return lbEOL.getText();
      case ENCODING   : return lbEncoding.getText();
      case TEXT_MODE  : return lbTextMode.getText();
      case READ_ONLY  : return String.valueOf(lbReadOnly.isEnabled());
      case CAPS_LOCK  : return String.valueOf(lbCapsLock.isEnabled());
      case NUM_LOCK   : return String.valueOf(lbNumLock.isEnabled());
      default         : return Utilities.EMPTY_STRING;
    }
  }

  /**
   * Sets the message.
   *
   * @param message_type StatusMessage
   * @param message String
   * @see jnpad.ui.status.ITextStatusBar#setMessage(jnpad.ui.status.ITextStatusBar.StatusMessage,
   * java.lang.String)
   */
  @Override
  public void setMessage(StatusMessage message_type, String message) {
    switch (message_type) {
      case STATUS     : lbStatusBar.setText(message)  ; break;
      case LINE       : lbLine.setText(message)       ; break;
      case POSITION   : lbPosition.setText(message)   ; break;
      case CHARACTER  : lbCharacter.setText(message)  ; break;
      case SELECTION  : lbSelection.setText(message)  ; break;
      case OCCURRENCES: lbOccurrences.setText(message); break;
      case EOL        : lbEOL.setText(message)        ; break;
      case ENCODING   : lbEncoding.setText(message)   ; break;
      case TEXT_MODE  : lbTextMode.setText(message)   ; break;
      case READ_ONLY:
        lbReadOnly.setEnabled(Boolean.valueOf(message));
        break;
      case CAPS_LOCK:
        lbCapsLock.setEnabled(Boolean.valueOf(message));
        break;
      case NUM_LOCK:
        lbNumLock.setEnabled(Boolean.valueOf(message));
        break;
      default: //Keep FindBugs happy
        break;
    }
  }

  // --- formats ---------------------------------------------------------------
  /**
   * Format line.
   *
   * @param line int
   * @param column int
   * @param character int
   * @param lines int
   * @param characters int
   * @return String
   */
  private static String formatLine(int line, int column, int character, int lines, int characters) {
    return LinePosition.toString(line, column, character, lines, characters);
  }

  /**
   * Format position.
   *
   * @param pos int
   * @param len int
   * @return String
   */
  private static String formatPosition(int pos, int len) {
    return new StringBuilder(POSITION).append(pos).append(" / ").append(len). //$NON-NLS-1$
        append(" (").append((int) ((((double) pos) / len) * 100)).append("%)").toString(); //$NON-NLS-1$ //$NON-NLS-2$
  }

  /**
   * Format character.
   *
   * @param c char
   * @return String
   */
  private static String formatCharacter(char c) {
    return new StringBuilder().
               append(c).append("  "). //$NON-NLS-1$
               append((int) c).append("  "). //$NON-NLS-1$
               append(String.format("\\u%04x", (int) c)).toString(); //$NON-NLS-1$
  }

  /**
   * Format selection.
   *
   * @param sel int
   * @return String
   */
  private static String formatSelection(int sel) {
    return SELECTION.concat(String.valueOf(sel));
  }

  /**
   * Format occurrences.
   *
   * @param occ int
   * @return String
   */
  private static String formatOccurrences(int occ) {
    return OCCURRENCES.concat(String.valueOf(occ));
  }

  /**
   * Format text mode.
   *
   * @param b boolean
   * @return String
   */
  private static String formatTextMode(boolean b) {
    return b ? "OVR" : "INS"; //$NON-NLS-1$ //$NON-NLS-2$
  }
  // ---------------------------------------------------------------------------
  
  /**
   * Gets the status.
   *
   * @return String
   * @see jnpad.ui.status.StatusDisplayable#getStatus()
   */
  @Override
  public String getStatus() {
    return lbStatusBar.getText();
  }

  /**
   * Clear status.
   *
   * @see jnpad.ui.status.StatusDisplayable#clearStatus()
   */
  @Override
  public void clearStatus() {
    setStatus(Utilities.SPACE_STRING);
  }

  /**
   * Sets the status.
   *
   * @param text String
   * @see jnpad.ui.status.StatusDisplayable#setStatus(java.lang.String)
   */
  @Override
  public void setStatus(String text) {
    setStatus(StatusType.SIMPLE, text, TIMEOUT_NONE);
  }

  /**
   * Sets the status.
   *
   * @param text String
   * @param timeout_sec int
   * @see jnpad.ui.status.StatusDisplayable#setStatus(java.lang.String, int)
   */
  @Override
  public void setStatus(String text, int timeout_sec) {
    setStatus(StatusType.SIMPLE, text, timeout_sec);
  }

  /**
   * Sets the status.
   *
   * @param type StatusType
   * @param text String
   * @see jnpad.ui.status.StatusDisplayable#setStatus(jnpad.ui.status.StatusDisplayable.StatusType,
   * java.lang.String)
   */
  @Override
  public void setStatus(final StatusType type, final String text) {
    setStatus(type, text, TIMEOUT_NONE);
  }

  /**
   * Sets the status.
   *
   * @param type StatusType
   * @param text String
   * @param timeout_sec int
   * @see jnpad.ui.status.StatusDisplayable#setStatus(jnpad.ui.status.StatusDisplayable.StatusType,
   * java.lang.String, int)
   */
  @Override
  public void setStatus(final StatusType type, final String text, final int timeout_sec) {
    if (text == null) {
      return;
    }

    GUIUtilities.runOrInvokeLater(new Runnable() {
      public void run() {
        if (timer != null) {
          timer.cancel();
          timer = null;
        }

        if (timeout_sec > 0) {
          timer = new Timer();
          timer.schedule(new ResetTask(), timeout_sec * 1000);
        }

        lbStatusBar.setText((text.length() == 0) ? Utilities.SPACE_STRING : text);

        switch (type) {
          case ERROR      : lbStatusBar.setIcon(GUIUtilities.loadIcon("error.png"))      ; break; //$NON-NLS-1$
          case WARNING    : lbStatusBar.setIcon(GUIUtilities.loadIcon("warning.png"))    ; break; //$NON-NLS-1$
          case INFORMATION: lbStatusBar.setIcon(GUIUtilities.loadIcon("information.png")); break; //$NON-NLS-1$
          case SIMPLE     :
          default         : lbStatusBar.setIcon(null)                                  ; break;
        }
      }
    });
  }

  /**
   * Handle mouse clicked.
   *
   * @param e MouseEvent
   */
  void handleMouseClicked(final MouseEvent e) {
    Object obj = e.getSource();
    if (obj == lbLine) {
      ActionManager.INSTANCE.executeAction(JNPadActions.ACTION_NAME_GO_TO_LINE);
    }
    else if (obj == lbEOL) {
      Buffer buffer = jNPad.getActiveBuffer();
      if (buffer != null) {
        LineSeparator lineSeparator = buffer.getLineSeparator();
        LineSeparator newLineSeparator = null;
        if (lineSeparator == LineSeparator.DOS)
          newLineSeparator = LineSeparator.UNIX;
        else if (lineSeparator == LineSeparator.UNIX)
          newLineSeparator = LineSeparator.MAC;
        else if (lineSeparator == LineSeparator.MAC)
          newLineSeparator = LineSeparator.DOS;
        if (newLineSeparator != null) {
          buffer.setLineSeparator(newLineSeparator);
          jNPad.setStatus(JNPadBundle.getString("JNPadStatusBar.eol.status", newLineSeparator.getName()), TIMEOUT_DEFAULT); //$NON-NLS-1$
        }
      }
    }
    else if (obj == lbReadOnly) {
      Buffer buffer = jNPad.getActiveBuffer();
      if (buffer != null) {
        buffer.setReadOnly(!lbReadOnly.isEnabled());
      }
    }
  }

  /**
   * Show popup menu.
   *
   * @param e MouseEvent
   */
  void showPopupMenu(final MouseEvent e) {
    Object obj = e.getSource();
    if (obj == lbStatusBar) {
      JPopupMenu popupMenu = new JPopupMenu();
      if (isHeapMonitorVisible) {
        miHeapMonitor.setText(JNPadBundle.getString("HeapMonitor.hide.text")); //$NON-NLS-1$
        miHeapMonitor.setDescription(JNPadBundle.getString("HeapMonitor.hide.description")); //$NON-NLS-1$
        miHeapMonitor.setIcon(GUIUtilities.loadIcon("java-close.png")); //$NON-NLS-1$
      }
      else {
        miHeapMonitor.setText(JNPadBundle.getString("HeapMonitor.show.text")); //$NON-NLS-1$
        miHeapMonitor.setDescription(JNPadBundle.getString("HeapMonitor.hide.description")); //$NON-NLS-1$
        miHeapMonitor.setIcon(GUIUtilities.loadIcon("java-open.png")); //$NON-NLS-1$
      }
      popupMenu.add(miHeapMonitor);
      SwingUtilities.updateComponentTreeUI(popupMenu);
      popupMenu.pack();
      popupMenu.show(e.getComponent(), e.getX(), e.getY());
    }
    else if (obj == lbEncoding) {
      JPopupMenu popupMenu = jNPad.createEncodingPopupMenu();
      popupMenu.show(e.getComponent(), e.getX(), e.getY() - popupMenu.getPreferredSize().height);
    }
  }
  
  /**
   * Show heap monitor.
   *
   * @param b boolean
   */
  public void showHeapMonitor(boolean b) {
    if (isHeapMonitorVisible != b) {
      if (b) {
        if (heapMonitor == null)
          heapMonitor = new HeapMonitor(this);
        pnStatus.add(heapMonitor, BorderLayout.EAST);
      }
      else {
        if (heapMonitor != null)
          pnStatus.remove(heapMonitor);
      }
      pnStatus.validate();
      pnStatus.repaint();
      isHeapMonitorVisible = b;
    }
  }
  
}
