Java – TextPad Demo


/**
 * TPEditor.java
 *
 * Ejemplo de un editor básico para documentos de texto plano utilizando la biblioteca gráfica Swing.
 * Funciona desde Java SE 5.0 en adelante.
 */

package textpademo;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
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.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.UndoableEditEvent;
import javax.swing.event.UndoableEditListener;
import javax.swing.text.BadLocationException;
import javax.swing.undo.UndoManager;

/**
 * Clase principal donde se construye la GUI del editor.
 *
 * @author Dark[byte]
 */
public class TPEditor {

    private JFrame jFrame;            //instancia de JFrame (ventana principal)
    private JMenuBar jMenuBar;        //instancia de JMenuBar (barra de menú)
    private JToolBar jToolBar;        //instancia de JToolBar (barra de herramientas)
    private JTextArea jTextArea;      //instancia de JTextArea (área de edición)
    private JPopupMenu jPopupMenu;    //instancia de JPopupMenu (menú emergente)
    private JPanel statusBar;         //instancia de JPanel (barra de estado)

    private JCheckBoxMenuItem itemLineWrap;         //instancias de algunos items de menú que necesitan ser accesibles
    private JCheckBoxMenuItem itemShowToolBar;
    private JCheckBoxMenuItem itemFixedToolBar;
    private JCheckBoxMenuItem itemShowStatusBar;
    private JMenuItem mbItemUndo;
    private JMenuItem mbItemRedo;
    private JMenuItem mpItemUndo;
    private JMenuItem mpItemRedo;

    private JButton buttonUndo;    //instancias de algunos botones que necesitan ser accesibles
    private JButton buttonRedo;

    private JLabel sbFilePath;    //etiqueta que muestra la ubicación del archivo actual
    private JLabel sbFileSize;    //etiqueta que muestra el tamaño del archivo actual
    private JLabel sbCaretPos;    //etiqueta que muestra la posición del cursor en el área de edición

    private boolean hasChanged = false;    //el estado del documento actual, no modificado por defecto
    private File currentFile = null;       //el archivo actual, ninguno por defecto

    private final EventHandler eventHandler;          //instancia de EventHandler (la clase que maneja eventos)
    private final ActionPerformer actionPerformer;    //instancia de ActionPerformer (la clase que ejecuta acciones)
    private final UndoManager undoManager;            //instancia de UndoManager (administrador de edición)

    /**
     * Punto de entrada del programa.
     *
     * Instanciamos esta clase para construir la GUI y hacerla visible.
     *
     * @param args argumentos de la línea de comandos.
     */
    public static void main(String[] args) {
        //construye la GUI en el EDT (Event Dispatch Thread)
        javax.swing.SwingUtilities.invokeLater(new Runnable() {

            @Override
            public void run() {
                new TPEditor().jFrame.setVisible(true);    //hace visible la GUI creada por la clase TPEditor
            }
        });
    }

    /**
     * Constructor de la clase.
     *
     * Se construye la GUI del editor, y se instancian clases importantes.
     */
    public TPEditor() {
        try {    //LookAndFeel nativo
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception ex) {
            System.err.println(ex);
        }

        //construye un JFrame con título
        jFrame = new JFrame("TextPad Demo - Sin Título");
        jFrame.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);

        //asigna un manejador de eventos para el cierre del JFrame
        jFrame.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent we) {
                actionPerformer.actionExit();    //invoca el método actionExit()
            }
        });

        eventHandler = new EventHandler();              //construye una instancia de EventHandler
        actionPerformer = new ActionPerformer(this);    //construye una instancia de ActionPerformer
        undoManager = new UndoManager();                //construye una instancia de UndoManager
        undoManager.setLimit(50);                       //le asigna un límite al buffer de ediciones

        buildTextArea();     //construye el área de edición, es importante que esta sea la primera parte en construirse
        buildMenuBar();      //construye la barra de menú
        buildToolBar();      //construye la barra de herramientas
        buildStatusBar();    //construye la barra de estado
        buildPopupMenu();    //construye el menú emergente

        jFrame.setJMenuBar(jMenuBar);                              //designa la barra de menú del JFrame
        Container c = jFrame.getContentPane();                     //obtiene el contendor principal
        c.add(jToolBar, BorderLayout.NORTH);                       //añade la barra de herramientas, orientación NORTE del contendor
        c.add(new JScrollPane(jTextArea), BorderLayout.CENTER);    //añade el area de edición en el CENTRO
        c.add(statusBar, BorderLayout.SOUTH);                      //añade la barra de estado, orientación SUR

        //configura el JFrame con un tamaño inicial proporcionado con respecto a la pantalla
        Dimension pantalla = Toolkit.getDefaultToolkit().getScreenSize();
        jFrame.setSize(pantalla.width / 2, pantalla.height / 2);

        //centra el JFrame en pantalla
        jFrame.setLocationRelativeTo(null);
    }

    /**
     * Construye el área de edición.
     */
    private void buildTextArea() {
        jTextArea = new JTextArea();    //construye un JTextArea

        //se configura por defecto para que se ajusten las líneas al tamaño del área de texto ...
        jTextArea.setLineWrap(true);
        //... y que se respete la integridad de las palaras en el ajuste
        jTextArea.setWrapStyleWord(true);

        //asigna el manejador de eventos para el cursor
        jTextArea.addCaretListener(eventHandler);
        //asigna el manejador de eventos para el ratón
        jTextArea.addMouseListener(eventHandler);
        //asigna el manejador de eventos para registrar los cambios sobre el documento
        jTextArea.getDocument().addUndoableEditListener(eventHandler);

        //remueve las posibles combinaciones de teclas asociadas por defecto con el JTextArea
        jTextArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.CTRL_MASK), "none");    //remueve CTRL + X ("Cortar")
        jTextArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK), "none");    //remueve CTRL + C ("Copiar")
        jTextArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_V, ActionEvent.CTRL_MASK), "none");    //remueve CTRL + V ("Pegar")
    }

    /**
     * Construye la barra de menú.
     */
    private void buildMenuBar() {
        jMenuBar = new JMenuBar();    //construye un JMenuBar

        //construye el menú "Archivo", a continuación se construyen los items para este menú
        JMenu menuFile = new JMenu("Archivo");

        //construye el item "Nuevo"
        JMenuItem itemNew = new JMenuItem("Nuevo");
        //le asigna una conbinación de teclas
        itemNew.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, KeyEvent.CTRL_MASK));
        //le asigna un nombre de comando
        itemNew.setActionCommand("cmd_new");

        JMenuItem itemOpen = new JMenuItem("Abrir");
        itemOpen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_MASK));
        itemOpen.setActionCommand("cmd_open");

        JMenuItem itemSave = new JMenuItem("Guardar");
        itemSave.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_MASK));
        itemSave.setActionCommand("cmd_save");

        JMenuItem itemSaveAs = new JMenuItem("Guardar como...");
        itemSaveAs.setActionCommand("cmd_saveas");
        itemSaveAs.addActionListener(eventHandler);

        JMenuItem itemPrint = new JMenuItem("Imprimir");
        itemPrint.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, KeyEvent.CTRL_MASK));
        itemPrint.setActionCommand("cmd_print");

        JMenuItem itemExit = new JMenuItem("Salir");
        itemExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, KeyEvent.CTRL_MASK));
        itemExit.setActionCommand("cmd_exit");

        menuFile.add(itemNew);    //se añaden los items al menú "Archivo"
        menuFile.add(itemOpen);
        menuFile.add(itemSave);
        menuFile.add(itemSaveAs);
        menuFile.addSeparator();
        menuFile.add(itemPrint);
        menuFile.addSeparator();
        menuFile.add(itemExit);
        //----------------------------------------------

        //construye el menú "Editar", a continuación se construyen los items para este menú
        JMenu menuEdit = new JMenu("Editar");

        mbItemUndo = new JMenuItem("Deshacer");
        mbItemUndo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Z, ActionEvent.CTRL_MASK));
        mbItemUndo.setEnabled(false);
        mbItemUndo.setActionCommand("cmd_undo");

        mbItemRedo = new JMenuItem("Rehacer");
        mbItemRedo.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Y, ActionEvent.CTRL_MASK));
        mbItemRedo.setEnabled(false);
        mbItemRedo.setActionCommand("cmd_redo");

        JMenuItem itemCut = new JMenuItem("Cortar");
        itemCut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.CTRL_MASK));
        itemCut.setActionCommand("cmd_cut");

        JMenuItem itemCopy = new JMenuItem("Copiar");
        itemCopy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK));
        itemCopy.setActionCommand("cmd_copy");

        JMenuItem itemPaste = new JMenuItem("Pegar");
        itemPaste.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, ActionEvent.CTRL_MASK));
        itemPaste.setActionCommand("cmd_paste");

        JMenuItem itemSearch = new JMenuItem("Buscar");
        itemSearch.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, ActionEvent.CTRL_MASK));
        itemSearch.setActionCommand("cmd_search");

        JMenuItem itemSearchNext = new JMenuItem("Buscar siguiente");
        itemSearchNext.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0));
        itemSearchNext.setActionCommand("cmd_searchnext");

        JMenuItem itemGotoLine = new JMenuItem("Ir a la línea...");
        itemGotoLine.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T, ActionEvent.CTRL_MASK));
        itemGotoLine.setActionCommand("cmd_gotoline");

        JMenuItem itemSelectAll = new JMenuItem("Seleccionar todo");
        itemSelectAll.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_E, ActionEvent.CTRL_MASK));
        itemSelectAll.setActionCommand("cmd_selectall");

        menuEdit.add(mbItemUndo);    //se añaden los items al menú "Editar"
        menuEdit.add(mbItemRedo);
        menuEdit.addSeparator();     //añade separadores entre algunos items
        menuEdit.add(itemCut);
        menuEdit.add(itemCopy);
        menuEdit.add(itemPaste);
        menuEdit.addSeparator();
        menuEdit.add(itemSearch);
        menuEdit.add(itemSearchNext);
        menuEdit.add(itemGotoLine);
        menuEdit.addSeparator();
        menuEdit.add(itemSelectAll);
        //----------------------------------------------

        //construye el menú "Opciones", a continuación se construyen los items para este menú
        JMenu menuTools = new JMenu("Opciones");

        itemLineWrap = new JCheckBoxMenuItem("Ajuste de línea");
        itemLineWrap.setSelected(true);
        itemLineWrap.setActionCommand("cmd_linewrap");

        itemShowToolBar = new JCheckBoxMenuItem("Ver barra de herramientas");
        itemShowToolBar.setSelected(true);
        itemShowToolBar.setActionCommand("cmd_showtoolbar");

        itemFixedToolBar = new JCheckBoxMenuItem("Fijar barra de herramientas");
        itemFixedToolBar.setSelected(true);
        itemFixedToolBar.setActionCommand("cmd_fixedtoolbar");

        itemShowStatusBar = new JCheckBoxMenuItem("Ver barra de estado");
        itemShowStatusBar.setSelected(true);
        itemShowStatusBar.setActionCommand("cmd_showstatusbar");

        JMenuItem itemFont = new JMenuItem("Fuente de letra");
        itemFont.setActionCommand("cmd_font");

        JMenuItem itemFontColor = new JMenuItem("Color de letra");
        itemFontColor.setActionCommand("cmd_fontcolor");

        JMenuItem itemBackgroundColor = new JMenuItem("Color de fondo");
        itemBackgroundColor.setActionCommand("cmd_backgroundcolor");

        menuTools.add(itemLineWrap);    //se añaden los items al menú "Opciones"
        menuTools.add(itemShowToolBar);
        menuTools.add(itemFixedToolBar);
        menuTools.add(itemShowStatusBar);
        menuTools.addSeparator();
        menuTools.add(itemFont);
        menuTools.add(itemFontColor);
        menuTools.add(itemBackgroundColor);

        //construye el menú "Ayuda", a continuación se construye el único item para este menú
        JMenu menuHelp = new JMenu("Ayuda");

        JMenuItem itemAbout = new JMenuItem("Acerca de");
        itemAbout.setActionCommand("cmd_about");

        menuHelp.add(itemAbout);     //se añade el único item al menú "Ayuda"
        //----------------------------------------------

        jMenuBar.add(menuFile);    //se añaden los menúes construidos a la barra de menú
        jMenuBar.add(Box.createHorizontalStrut(5));    //añade espacios entre cada menú
        jMenuBar.add(menuEdit);
        jMenuBar.add(Box.createHorizontalStrut(5));
        jMenuBar.add(menuTools);
        jMenuBar.add(Box.createHorizontalStrut(5));
        jMenuBar.add(menuHelp);

        /** itera sobre todos los componentes de la barra de menú, se les asigna el mismo
        manejador de eventos a todos excepto a los separadores */
        for (Component c1 : jMenuBar.getComponents()) {
            //si el componente es un menú
            if (c1.getClass().equals(javax.swing.JMenu.class)) {
                //itera sobre los componentes del menú
                for (Component c2 : ((JMenu) c1).getMenuComponents()) {
                    //si el componente no es un separador
                    if (!c2.getClass().equals(javax.swing.JPopupMenu.Separator.class)) {
                        ((JMenuItem) c2).addActionListener(eventHandler);
                    }
                }
            }
        }
    }

    /**
     * Construye la barra de herramientas.
     */
    private void buildToolBar() {
        jToolBar = new JToolBar();       //construye un JToolBar
        jToolBar.setFloatable(false);    //se configura por defecto como barra fija

        //construye el botón "Nuevo"
        JButton buttonNew = new JButton();
        //le asigna una etiqueta flotante
        buttonNew.setToolTipText("Nuevo");
        //le asigna una imagen ubicada en los recursos del proyecto
        buttonNew.setIcon(new ImageIcon(getClass().getResource("/res/tp_new.png")));
        //le asigna un nombre de comando
        buttonNew.setActionCommand("cmd_new");

        JButton buttonOpen = new JButton();
        buttonOpen.setToolTipText("Abrir");
        buttonOpen.setIcon(new ImageIcon(getClass().getResource("/res/tp_open.png")));
        buttonOpen.setActionCommand("cmd_open");

        JButton buttonSave = new JButton();
        buttonSave.setToolTipText("Guardar");
        buttonSave.setIcon(new ImageIcon(getClass().getResource("/res/tp_save.png")));
        buttonSave.setActionCommand("cmd_save");

        JButton buttonSaveAs = new JButton();
        buttonSaveAs.setToolTipText("Guardar como...");
        buttonSaveAs.setIcon(new ImageIcon(getClass().getResource("/res/tp_saveas.png")));
        buttonSaveAs.setActionCommand("cmd_saveas");

        JButton buttonPrint = new JButton();
        buttonPrint.setToolTipText("Imprimir");
        buttonPrint.setIcon(new ImageIcon(getClass().getResource("/res/tp_print.png")));
        buttonPrint.setActionCommand("cmd_print");

        buttonUndo = new JButton();
        buttonUndo.setEnabled(false);
        buttonUndo.setToolTipText("Deshacer");
        buttonUndo.setIcon(new ImageIcon(getClass().getResource("/res/tp_undo.png")));
        buttonUndo.setActionCommand("cmd_undo");

        buttonRedo = new JButton();
        buttonRedo.setEnabled(false);
        buttonRedo.setToolTipText("Rehacer");
        buttonRedo.setIcon(new ImageIcon(getClass().getResource("/res/tp_redo.png")));
        buttonRedo.setActionCommand("cmd_redo");

        JButton buttonCut = new JButton();
        buttonCut.setToolTipText("Cortar");
        buttonCut.setIcon(new ImageIcon(getClass().getResource("/res/tp_cut.png")));
        buttonCut.setActionCommand("cmd_cut");

        JButton buttonCopy = new JButton();
        buttonCopy.setToolTipText("Copiar");
        buttonCopy.setIcon(new ImageIcon(getClass().getResource("/res/tp_copy.png")));
        buttonCopy.setActionCommand("cmd_copy");

        JButton buttonPaste = new JButton();
        buttonPaste.setToolTipText("Pegar");
        buttonPaste.setIcon(new ImageIcon(getClass().getResource("/res/tp_paste.png")));
        buttonPaste.setActionCommand("cmd_paste");

        jToolBar.add(buttonNew);    //se añaden los botones construidos a la barra de herramientas
        jToolBar.add(buttonOpen);
        jToolBar.add(buttonSave);
        jToolBar.add(buttonSaveAs);
        jToolBar.addSeparator();    //añade separadores entre algunos botones
        jToolBar.add(buttonPrint);
        jToolBar.addSeparator();
        jToolBar.add(buttonUndo);
        jToolBar.add(buttonRedo);
        jToolBar.addSeparator();
        jToolBar.add(buttonCut);
        jToolBar.add(buttonCopy);
        jToolBar.add(buttonPaste);

        /** itera sobre todos los componentes de la barra de herramientas, se les asigna el
        mismo margen y el mismo manejador de eventos unicamente a los botones */
        for (Component c : jToolBar.getComponents()) {
            //si el componente es un botón
            if (c.getClass().equals(javax.swing.JButton.class)) {
                JButton jb = (JButton) c;
                jb.setMargin(new Insets(0, 0, 0, 0));
                jb.addActionListener(eventHandler);
            }
        }
    }

    /**
     * Construye la barra de estado.
     */
    private void buildStatusBar() {
        statusBar = new JPanel();    //construye un JPanel
        //se configura con un BoxLayout
        statusBar.setLayout(new BoxLayout(statusBar, BoxLayout.LINE_AXIS));
        //le añade un borde compuesto
        statusBar.setBorder(BorderFactory.createCompoundBorder(
                BorderFactory.createLoweredBevelBorder(),
                BorderFactory.createEmptyBorder(5, 5, 5, 5)));

        //construye la etiqueta para mostrar la ubicación del archivo actual
        sbFilePath = new JLabel("...");
        //construye la etiqueta para mostrar el tamaño del archivo actual
        sbFileSize = new JLabel("");
        //construye la etiqueta para mostrar la posición del cursor en el documento actual
        sbCaretPos = new JLabel("...");

        /** se añaden las etiquetas construidas al JPanel, el resultado es un panel
        similar a una barra de estado */
        statusBar.add(sbFilePath);
        statusBar.add(Box.createRigidArea(new Dimension(10, 0)));
        statusBar.add(sbFileSize);
        statusBar.add(Box.createRigidArea(new Dimension(10, 0)));
        statusBar.add(Box.createHorizontalGlue());
        statusBar.add(sbCaretPos);
    }

    /**
     * Construye el menú emergente.
     */
    private void buildPopupMenu() {
        jPopupMenu = new JPopupMenu();    //construye un JPopupMenu

        //construye el item "Deshacer"
        mpItemUndo = new JMenuItem("Deshacer");
        //se configura desactivado por defecto
        mpItemUndo.setEnabled(false);
        //le asigna un nombre de comando
        mpItemUndo.setActionCommand("cmd_undo");

        mpItemRedo = new JMenuItem("Rehacer");
        mpItemRedo.setEnabled(false);
        mpItemRedo.setActionCommand("cmd_redo");

        JMenuItem itemCut = new JMenuItem("Cortar");
        itemCut.setActionCommand("cmd_cut");

        JMenuItem itemCopy = new JMenuItem("Copiar");
        itemCopy.setActionCommand("cmd_copy");

        JMenuItem itemPaste = new JMenuItem("Pegar");
        itemPaste.setActionCommand("cmd_paste");

        JMenuItem itemGoto = new JMenuItem("Ir a...");
        itemGoto.setActionCommand("cmd_gotoline");

        JMenuItem itemSearch = new JMenuItem("Buscar");
        itemSearch.setActionCommand("cmd_search");

        JMenuItem itemSearchNext = new JMenuItem("Buscar siguiente");
        itemSearchNext.setActionCommand("cmd_searchnext");

        JMenuItem itemSelectAll = new JMenuItem("Seleccionar todo");
        itemSelectAll.setActionCommand("cmd_selectall");

        jPopupMenu.add(mpItemUndo);    //se añaden los items al menú emergente
        jPopupMenu.add(mpItemRedo);
        jPopupMenu.addSeparator();     //añade separadores entre algunos items
        jPopupMenu.add(itemCut);
        jPopupMenu.add(itemCopy);
        jPopupMenu.add(itemPaste);
        jPopupMenu.addSeparator();
        jPopupMenu.add(itemGoto);
        jPopupMenu.add(itemSearch);
        jPopupMenu.add(itemSearchNext);
        jPopupMenu.addSeparator();
        jPopupMenu.add(itemSelectAll);

        /** itera sobre todos los componentes del menú emergente, se les asigna el mismo
        manejador de eventos a todos excepto a los separadores */
        for (Component c : jPopupMenu.getComponents()) {
            //si el componente es un item
            if (c.getClass().equals(javax.swing.JMenuItem.class)) {
                ((JMenuItem) c).addActionListener(eventHandler);
            }
        }
    }

    /**
     * Hace visible el menú emergente.
     *
     * @param me evento del ratón
     */
    private void showPopupMenu(MouseEvent me) {
        if (me.isPopupTrigger() == true) {    //si el evento es el desencadenador de menú emergente
            //hace visible el menú emergente en las coordenadas actuales del ratón
            jPopupMenu.show(me.getComponent(), me.getX(), me.getY());
        }
    }

    /**
     * Actualiza el estado de las opciones "Deshacer" y "Rehacer".
     */
    void updateControls() {
        //averigua si se pueden deshacer los cambios en el documento actual
        boolean canUndo = undoManager.canUndo();
        //averigua si se pueden rehacer los cambios en el documento actual
        boolean canRedo = undoManager.canRedo();

        //activa o desactiva las opciones en la barra de menú
        mbItemUndo.setEnabled(canUndo);
        mbItemRedo.setEnabled(canRedo);

        //activa o desactiva las opciones en la barra de herramientas
        buttonUndo.setEnabled(canUndo);
        buttonRedo.setEnabled(canRedo);

        //activa o desactiva las opciones en el menú emergente
        mpItemUndo.setEnabled(canUndo);
        mpItemRedo.setEnabled(canRedo);
    }

    /**
     * Retorna la instancia de EventHandler, la clase interna que maneja eventos.
     *
     * @return el manejador de eventos.
     */
    EventHandler getEventHandler() {
        return eventHandler;
    }

    /**
     * Retorna la instancia de UndoManager, la cual administra las ediciones sobre
     * el documento en el área de texto.
     *
     * @return el administrador de edición.
     */
    UndoManager getUndoManager() {
        return undoManager;
    }

    /**
     * Retorna el estado del documento actual.
     *
     * @return true si ah sido modificado, false en caso contrario
     */
    boolean documentHasChanged() {
        return hasChanged;
    }

    /**
     * Establece el estado del documento actual.
     *
     * @param hasChanged true si ah sido modificado, false en caso contrario
     */
    void setDocumentChanged(boolean hasChanged) {
        this.hasChanged = hasChanged;
    }

    /**
     * Retorna la instancia de JTextArea, el área de edición.
     *
     * @return retorna el área de edición.
     */
    JTextArea getJTextArea() {
        return jTextArea;
    }

    /**
     * Retorna la instancia de JFrame, la ventana principal del editor.
     *
     * @return la ventana principal del editor.
     */
    JFrame getJFrame() {
        return jFrame;
    }

    /**
     * Retorna la instancia de File, el archivo actual.
     *
     * @return el archivo actual
     */
    File getCurrentFile() {
        return currentFile;
    }

    /**
     * Establece el archivo actual.
     *
     * @param currentFile el archivo actual
     */
    void setCurrentFile(File currentFile) {
        this.currentFile = currentFile;
    }

    /**
     * Retorna la instancia de la etiqueta sbFilePath, donde se muestra la ubicación
     * del archivo actual.
     *
     * @return la instancia de la etiqueta sbFilePath
     */
    JLabel getJLabelFilePath() {
        return sbFilePath;
    }

    /**
     * Retorna la instancia de la etiqueta sbFileSize, donde se muestra el tamaño
     * del archivo actual
     *
     * @return la instancia de la etiqueta sbFileSize
     */
    JLabel getJLabelFileSize() {
        return sbFileSize;
    }

    /**
     * Clase interna que extiende e implementa las clases e interfaces necesarias para
     * atender y manejar los eventos sobre la GUI principal del editor.
     */
    class EventHandler extends MouseAdapter implements ActionListener,
                                                       CaretListener,
                                                       UndoableEditListener {

        /**
         * Atiende y maneja los eventos de acción.
         *
         * @param ae evento de acción
         */
        @Override
        public void actionPerformed(ActionEvent ae) {
            String ac = ae.getActionCommand();    //se obtiene el nombre del comando ejecutado

            if (ac.equals("cmd_new") == true) {    //opción seleccionada: "Nuevo"
                actionPerformer.actionNew();
            } else if (ac.equals("cmd_open") == true) {    //opción seleccionada: "Abrir"
                actionPerformer.actionOpen();
            } else if (ac.equals("cmd_save") == true) {    //opción seleccionada: "Guardar"
                actionPerformer.actionSave();
            } else if (ac.equals("cmd_saveas") == true) {    //opción seleccionada: "Guardar como"
                actionPerformer.actionSaveAs();
            } else if (ac.equals("cmd_print") == true) {    //opción seleccionada: "Imprimir"
                actionPerformer.actionPrint();
            } else if (ac.equals("cmd_exit") == true) {    //opción seleccionada: "Salir"
                actionPerformer.actionExit();
            } else if (ac.equals("cmd_undo") == true) {    //opción seleccionada: "Deshacer"
                actionPerformer.actionUndo();
            } else if (ac.equals("cmd_redo") == true) {    //opción seleccionada: "Rehacer"
                actionPerformer.actionRedo();
            } else if (ac.equals("cmd_cut") == true) {    //opción seleccionada: "Cortar"
                //corta el texto seleccionado en el documento
                jTextArea.cut();
            } else if (ac.equals("cmd_copy") == true) {    //opción seleccionada: "Copiar"
                //copia el texto seleccionado en el documento
                jTextArea.copy();
            } else if (ac.equals("cmd_paste") == true) {    //opción seleccionada: "Pegar"
                //pega en el documento el texto del portapapeles
                jTextArea.paste();
            } else if (ac.equals("cmd_gotoline") == true) {    //opción seleccionada: "Ir a la línea..."
                actionPerformer.actionGoToLine();
            } else if (ac.equals("cmd_search") == true) {    //opción seleccionada: "Buscar"
                actionPerformer.actionSearch();
            } else if (ac.equals("cmd_searchnext") == true) {    //opción seleccionada: "Buscar siguiente"
                actionPerformer.actionSearchNext();
            } else if (ac.equals("cmd_selectall") == true) {    //opción seleccionada: "Seleccionar todo"
                jTextArea.selectAll();
            } else if (ac.equals("cmd_linewrap") == true) {    //opción seleccionada: "Ajuste de línea"
                //si esta propiedad esta activada se desactiva, o lo inverso
                jTextArea.setLineWrap(!jTextArea.getLineWrap());
                jTextArea.setWrapStyleWord(!jTextArea.getWrapStyleWord());
            } else if (ac.equals("cmd_showtoolbar") == true) {    //opción seleccionada: "Ver barra de herramientas"
                //si la barra de herramientas esta visible se oculta, o lo inverso
                jToolBar.setVisible(!jToolBar.isVisible());
            } else if (ac.equals("cmd_fixedtoolbar") == true) {    //opción seleccionada: "Fijar barra de herramientas"
                //si esta propiedad esta activada se desactiva, o lo inverso
                jToolBar.setFloatable(!jToolBar.isFloatable());
            } else if (ac.equals("cmd_showstatusbar") == true) {    //opción seleccionada: "Ver barra de estado"
                //si la barra de estado esta visible se oculta, o lo inverso
                statusBar.setVisible(!statusBar.isVisible());
            } else if (ac.equals("cmd_font") == true) {    //opción seleccionada: "Fuente de letra"
                actionPerformer.actionSelectFont();
            } else if (ac.equals("cmd_fontcolor") == true) {    //opción seleccionada: "Color de letra"
                actionPerformer.actionSelectFontColor();
            } else if (ac.equals("cmd_backgroundcolor") == true) {    //opción seleccionada: "Color de fondo"
                actionPerformer.actionSelectBackgroundColor();
            } else if (ac.equals("cmd_about") == true) {    //opción seleccionada: "Acerca de"
                //presenta un dialogo modal con alguna informacion
                JOptionPane.showMessageDialog(jFrame,
                                              "TextPad Demo por Dark[byte]",
                                              "Acerca de",
                                              JOptionPane.INFORMATION_MESSAGE);
            }
        }

        /**
         * Atiende y maneja los eventos del cursor.
         *
         * @param ce evento del cursor
         */
        @Override
        public void caretUpdate(CaretEvent e) {
            final int caretPos;  //valor de la posición del cursor sin inicializar
            int y = 1;           //valor de la línea inicialmente en 1
            int x = 1;           //valor de la columna inicialmente en 1

            try {
                //obtiene la posición del cursor con respecto al inicio del JTextArea (área de edición)
                caretPos = jTextArea.getCaretPosition();
                //sabiendo lo anterior se obtiene el valor de la línea actual (se cuenta desde 0)
                y = jTextArea.getLineOfOffset(caretPos);

                /** a la posición del cursor se le resta la posición del inicio de la línea para
                determinar el valor de la columna actual */
                x = caretPos - jTextArea.getLineStartOffset(y);

                //al valor de la línea actual se le suma 1 porque estas comienzan contándose desde 0
                y += 1;
            } catch (BadLocationException ex) {    //en caso de que ocurra una excepción
                System.err.println(ex);
            }

            /** muestra la información recolectada en la etiqueta sbCaretPos de la
            barra de estado, también se incluye el número total de lineas */
            sbCaretPos.setText("Líneas: " + jTextArea.getLineCount() + " - Y: " + y + " - X: " + x);
        }

        /**
         * Atiende y maneja los eventos sobre el documento en el área de edición.
         *
         * @param uee evento de edición
         */
        @Override
        public void undoableEditHappened(UndoableEditEvent uee) {
            /** el cambio realizado en el área de edición se guarda en el buffer
            del administrador de edición */
            undoManager.addEdit(uee.getEdit());
            updateControls();    //actualiza el estado de las opciones "Deshacer" y "Rehacer"

            hasChanged = true;
        }

        /**
         * Atiende y maneja los eventos sobre el ratón cuando este es presionado.
         *
         * @param me evento del ratón
         */
        @Override
        public void mousePressed(MouseEvent me) {
            showPopupMenu(me);
        }

        /**
         * Atiende y maneja los eventos sobre el ratón cuando este es liberado.
         *
         * @param me evento del ratón
         */
        @Override
        public void mouseReleased(MouseEvent me) {
            showPopupMenu(me);
        }
    }
}
/**
 * ActionPerformer.java
 */

package textpademo;

import java.awt.Color;
import java.awt.Font;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.regex.Pattern;
import javax.swing.JColorChooser;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.filechooser.FileFilter;
import javax.swing.text.BadLocationException;
import javax.swing.undo.CannotRedoException;
import javax.swing.undo.CannotUndoException;

/**
 * Clase que ejecuta las operaciones solicitadas.
 * 
 * @author Dark[byte]
 */
public class ActionPerformer {

    private final TPEditor tpEditor;    //instancia de TPEditor (la clase principal)
    private String lastSearch = "";     //la última búsqueda de texto realizada, por defecto no contiene nada

    /**
     * Constructor de la clase.
     * 
     * @param tpEditor clase principal
     */
    public ActionPerformer(TPEditor tpEditor) {
        this.tpEditor = tpEditor;    //guarda la instancia de la clase TPEditor
    }

    /**
     * Opción seleccionada: "Nuevo".
     * 
     * Reemplaza el documento actual por uno nuevo vacío.
     */
    public void actionNew() {
        if (tpEditor.documentHasChanged() == true) {    //si el documento esta marcado como modificado
            //le ofrece al usuario guardar los cambios
            int option = JOptionPane.showConfirmDialog(tpEditor.getJFrame(), "¿Desea guardar los cambios?");

            switch (option) {
                case JOptionPane.YES_OPTION:       //si elige que si
                    actionSave();                  //guarda el archivo
                    break;
                case JOptionPane.CANCEL_OPTION:    //si elige cancelar
                    return;                        //cancela esta operación
                //en otro caso se continúa con la operación y no se guarda el documento actual
            }
        }

        tpEditor.getJFrame().setTitle("TextPad Demo - Sin Título");    //nuevo título de la ventana

        //limpia el contenido del area de edición
        tpEditor.getJTextArea().setText("");
        //limpia el contenido de las etiquetas en la barra de estado
        tpEditor.getJLabelFilePath().setText("");
        tpEditor.getJLabelFileSize().setText("");

        tpEditor.getUndoManager().die();    //limpia el buffer del administrador de edición
        tpEditor.updateControls();          //actualiza el estado de las opciones "Deshacer" y "Rehacer"

        //el archivo asociado al documento actual se establece como null
        tpEditor.setCurrentFile(null);
        //marca el estado del documento como no modificado
        tpEditor.setDocumentChanged(false);
    }

    /**
     * Opción seleccionada: "Abrir".
     * 
     * Le permite al usuario elegir un archivo para cargar en el área de edición.
     */
    public void actionOpen() {
        if (tpEditor.documentHasChanged() == true) {    //si el documento esta marcado como modificado
            //le ofrece al usuario guardar los cambios
            int option = JOptionPane.showConfirmDialog(tpEditor.getJFrame(), "¿Desea guardar los cambios?");

            switch (option) {
                case JOptionPane.YES_OPTION:     //si elige que si
                    actionSave();               //guarda el archivo
                    break;
                case JOptionPane.CANCEL_OPTION:  //si elige cancelar
                    return;                      //cancela esta operación
                //en otro caso se continúa con la operación y no se guarda el documento actual
            }
        }

        JFileChooser fc = getJFileChooser();    //obtiene un JFileChooser

        //presenta un dialogo modal para que el usuario seleccione un archivo
        int state = fc.showOpenDialog(tpEditor.getJFrame());

        if (state == JFileChooser.APPROVE_OPTION) {    //si elige abrir el archivo
            File f = fc.getSelectedFile();    //obtiene el archivo seleccionado

            try {
                //abre un flujo de datos desde el archivo seleccionado
                BufferedReader br = new BufferedReader(new FileReader(f));
                //lee desde el flujo de datos hacia el area de edición
                tpEditor.getJTextArea().read(br, null);
                br.close();    //cierra el flujo

                tpEditor.getJTextArea().getDocument().addUndoableEditListener(tpEditor.getEventHandler());

                tpEditor.getUndoManager().die();    //se limpia el buffer del administrador de edición
                tpEditor.updateControls();          //se actualiza el estado de las opciones "Deshacer" y "Rehacer"

                //nuevo título de la ventana con el nombre del archivo cargado
                tpEditor.getJFrame().setTitle("TextPad Demo - " + f.getName());

                //muestra la ubicación del archivo actual
                tpEditor.getJLabelFilePath().setText(shortPathName(f.getAbsolutePath()));
                //muestra el tamaño del archivo actual
                tpEditor.getJLabelFileSize().setText(roundFileSize(f.length()));

                //establece el archivo cargado como el archivo actual
                tpEditor.setCurrentFile(f);
                //marca el estado del documento como no modificado
                tpEditor.setDocumentChanged(false);
            } catch (IOException ex) {    //en caso de que ocurra una excepción
                //presenta un dialogo modal con alguna información de la excepción
                JOptionPane.showMessageDialog(tpEditor.getJFrame(),
                                              ex.getMessage(),
                                              ex.toString(),
                                              JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    /**
     * Opción seleccionada: "Guardar".
     * 
     * Guarda el documento actual en el archivo asociado actualmente.
     */
    public void actionSave() {
        if (tpEditor.getCurrentFile() == null) {    //si no hay un archivo asociado al documento actual
            actionSaveAs();    //invoca el método actionSaveAs()
        } else if (tpEditor.documentHasChanged() == true) {    //si el documento esta marcado como modificado
            try {
                //abre un flujo de datos hacia el archivo asociado al documento actual
                BufferedWriter bw = new BufferedWriter(new FileWriter(tpEditor.getCurrentFile()));
                //escribe desde el flujo de datos hacia el archivo
                tpEditor.getJTextArea().write(bw);
                bw.close();    //cierra el flujo

                //marca el estado del documento como no modificado
                tpEditor.setDocumentChanged(false);
            } catch (IOException ex) {    //en caso de que ocurra una excepción
                //presenta un dialogo modal con alguna información de la excepción
                JOptionPane.showMessageDialog(tpEditor.getJFrame(),
                                              ex.getMessage(),
                                              ex.toString(),
                                              JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    /**
     * Opción seleccionada: "Guardar como".
     * 
     * Le permite al usuario elegir la ubicación donde se guardará el documento actual.
     */
    public void actionSaveAs() {
        JFileChooser fc = getJFileChooser();    //obtiene un JFileChooser

        //presenta un dialogo modal para que el usuario seleccione un archivo
        int state = fc.showSaveDialog(tpEditor.getJFrame());
        if (state == JFileChooser.APPROVE_OPTION) {    //si elige guardar en el archivo
            File f = fc.getSelectedFile();    //obtiene el archivo seleccionado

            try {
                //abre un flujo de datos hacia el archivo asociado seleccionado
                BufferedWriter bw = new BufferedWriter(new FileWriter(f));
                //escribe desde el flujo de datos hacia el archivo
                tpEditor.getJTextArea().write(bw);
                bw.close();    //cierra el flujo

                //nuevo título de la ventana con el nombre del archivo guardado
                tpEditor.getJFrame().setTitle("TextPad Demo - " + f.getName());

                //muestra la ubicación del archivo guardado
                tpEditor.getJLabelFilePath().setText(shortPathName(f.getAbsolutePath()));
                //muestra el tamaño del archivo guardado
                tpEditor.getJLabelFileSize().setText(roundFileSize(f.length()));

                //establece el archivo guardado como el archivo actual
                tpEditor.setCurrentFile(f);
                //marca el estado del documento como no modificado
                tpEditor.setDocumentChanged(false);
            } catch (IOException ex) {    //en caso de que ocurra una excepción
                //presenta un dialogo modal con alguna información de la excepción
                JOptionPane.showMessageDialog(tpEditor.getJFrame(),
                                              ex.getMessage(),
                                              ex.toString(),
                                              JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    /**
     * Opción seleccionada: "Imprimir".
     * 
     * Imprime el documento actual.
     */
    public void actionPrint() {
        boolean result = false;    //resultado de la impresión, por defecto es false

        //si el documento actual no esta vacío
        if (tpEditor.getJTextArea().getText().trim().equals("") == false) {
            //invoca nuestra la clase PrintAction para presentar el dialogo de impresión
            result = PrintAction.print(tpEditor.getJTextArea(), tpEditor.getJFrame());
        }
    }

    /**
     * Opción seleccionada: "Salir".
     * 
     * Finaliza el programa.
     */
    public void actionExit() {
        if (tpEditor.documentHasChanged() == true) {    //si el documento esta marcado como modificado
            //le ofrece al usuario guardar los cambios
            int option = JOptionPane.showConfirmDialog(tpEditor.getJFrame(), "¿Desea guardar los cambios?");

            switch (option) {
                case JOptionPane.YES_OPTION:     //si elige que si
                    actionSave();                //guarda el archivo
                    break;
                case JOptionPane.CANCEL_OPTION:  //si elige cancelar
                    return;                      //cancela esta operación
                //en otro caso se continúa con la operación y no se guarda el documento actual
            }
        }


        System.exit(0);    //finaliza el programa con el código 0 (sin errores)
    }

    /**
     * Opción seleccionada: "Deshacer".
     * 
     * Deshace el último cambio realizado en el documento actual.
     */
    public void actionUndo() {
        try {
            //deshace el último cambio realizado sobre el documento en el área de edición
            tpEditor.getUndoManager().undo();
        } catch (CannotUndoException ex) {    //en caso de que ocurra una excepción
            System.err.println(ex);
        }

        //actualiza el estado de las opciones "Deshacer" y "Rehacer"
        tpEditor.updateControls();
    }

    /**
     * Opción seleccionada: "Rehacer".
     * 
     * Rehace el último cambio realizado en el documento actual.
     */
    public void actionRedo() {
        try {
            //rehace el último cambio realizado sobre el documento en el área de edición
            tpEditor.getUndoManager().redo();
        } catch (CannotRedoException ex) {    //en caso de que ocurra una excepción
            System.err.println(ex);
        }

        //actualiza el estado de las opciones "Deshacer" y "Rehacer"
        tpEditor.updateControls();
    }

    /**
     * Opción seleccionada: "Buscar".
     * 
     * Busca un texto especificado por el usuario en el documento actual. El texto queda 
     * guardado para búsquedas siguientes.
     */
    public void actionSearch() {
        //solicita al usuario que introduzca el texto a buscar
        String text = JOptionPane.showInputDialog(
                tpEditor.getJFrame(),
                "Texto:",
                "TextPad Demo - Buscar",
                JOptionPane.QUESTION_MESSAGE);

        if (text != null) {    //si se introdujo texto (puede ser una cadena vacía)
            String textAreaContent = tpEditor.getJTextArea().getText();    //obtiene todo el contenido del área de edición
            int pos = textAreaContent.indexOf(text);    //obtiene la posición de la primera ocurrencia del texto

            if (pos > -1) {    //si la posición es mayor a -1 significa que la búsqueda fue positiva
                //selecciona el texto en el área de edición para resaltarlo
                tpEditor.getJTextArea().select(pos, pos + text.length());
            }

            //establece el texto buscado como el texto de la última búsqueda realizada
            lastSearch = text;
        }
    }

    /**
     * Opción seleccionada: "Buscar siguiente".
     * 
     * Busca el texto de la última búsqueda en el documento actual.
     */
    public void actionSearchNext() {
        if (lastSearch.length() > 0) {    //si la última búsqueda contiene texto
            String textAreaContent = tpEditor.getJTextArea().getText();    //se obtiene todo el contenido del área de edición
            int pos = tpEditor.getJTextArea().getCaretPosition();    //se obtiene la posición del cursor sobre el área de edición
            //buscando a partir desde la posición del cursor, se obtiene la posición de la primera ocurrencia del texto
            pos = textAreaContent.indexOf(lastSearch, pos);

            if (pos > -1) {    //si la posición es mayor a -1 significa que la búsqueda fue positiva
                //selecciona el texto en el área de edición para resaltarlo
                tpEditor.getJTextArea().select(pos, pos + lastSearch.length());
            }
        } else {    //si la última búsqueda no contiene nada
            actionSearch();    //invoca el método actionSearch()
        }
    }

    /**
     * Opción seleccionada: "Ir a la línea...".
     * 
     * Posiciona el cursor en el inicio de una línea especificada por el usuario.
     */
    public void actionGoToLine() {
        //solicita al usuario que introduzca el número de línea
        String line = JOptionPane.showInputDialog(
                tpEditor.getJFrame(),
                "Número:",
                "TextPad Demo - Ir a la línea...",
                JOptionPane.QUESTION_MESSAGE);

        if (line != null && line.length() > 0) {    //si se introdujo un dato
            try {
                int pos = Integer.parseInt(line);    //el dato introducido se convierte en entero

                //si el número de línea esta dentro de los límites del área de texto
                if (pos >= 0 && pos <= tpEditor.getJTextArea().getLineCount()) {
                    //posiciona el cursor en el inicio de la línea
                    tpEditor.getJTextArea().setCaretPosition(tpEditor.getJTextArea().getLineStartOffset(pos));
                }
            } catch (NumberFormatException ex) {    //en caso de que ocurran excepciones
                System.err.println(ex);
            } catch (BadLocationException ex) {
                System.err.println(ex);
            }
        }
    }

    /**
     * Opción seleccionada: "Fuente de letra".
     * 
     * Le permite al usuario elegir la fuente para la letra en el área de edición.
     */
    public void actionSelectFont() {
        //presenta el dialogo de selección de fuentes
        Font font = JFontChooser.showDialog(tpEditor.getJFrame(),
                                            "TextPad Demo - Fuente de letra:",
                                            tpEditor.getJTextArea().getFont());
        if (font != null) {    //si un fuente fue seleccionado
            //se establece como fuente del area de edición
            tpEditor.getJTextArea().setFont(font);
        }
    }

    /**
     * Opción seleccionada: "Color de letra".
     * 
     * Le permite al usuario elegir el color para la letra en el área de edición.
     */
    public void actionSelectFontColor() {
        //presenta el dialogo de selección de colores
        Color color = JColorChooser.showDialog(tpEditor.getJFrame(),
                                               "TextPad Demo - Color de letra:",
                                               tpEditor.getJTextArea().getForeground());
        if (color != null) {    //si un color fue seleccionado
            //se establece como color del fuente y cursor
            tpEditor.getJTextArea().setForeground(color);
            tpEditor.getJTextArea().setCaretColor(color);
        }
    }

    /**
     * Opción seleccionada: "Color de fondo".
     * 
     * Le permite al usuario elegir el color para el fondo del área de edición.
     */
    public void actionSelectBackgroundColor() {
        //presenta el dialogo de selección de colores
        Color color = JColorChooser.showDialog(tpEditor.getJFrame(),
                                               "TextPad Demo - Color de fondo:",
                                               tpEditor.getJTextArea().getForeground());
        if (color != null) {    //si un color fue seleccionado
            //se establece como color de fondo
            tpEditor.getJTextArea().setBackground(color);
        }
    }

    /**
     * Retorna la instancia de un JFileChooser, con el cual se muestra un dialogo que permite
     * seleccionar un archivo.
     * 
     * @return un dialogo para seleccionar un archivo.
     */
    private static JFileChooser getJFileChooser() {
        JFileChooser fc = new JFileChooser();                     //construye un JFileChooser
        fc.setDialogTitle("TextPad Demo - Elige un archivo:");    //se le establece un título
        fc.setMultiSelectionEnabled(false);                       //desactiva la multi-selección
        fc.setFileFilter(textFileFilter);                         //aplica un filtro de extensiones
        return fc;    //retorna el JFileChooser
    }

    /**
     * Clase anónima interna que extiende la clase javax.swing.filechooser.FileFilter para 
     * establecer un filtro de archivos en el JFileChooser.
     */
    private static FileFilter textFileFilter = new FileFilter() {

        @Override
        public boolean accept(File f) {
            //acepta directorios y archivos de extensión .txt
            return f.isDirectory() || f.getName().toLowerCase().endsWith("txt");
        }

        @Override
        public String getDescription() {
            //la descripción del tipo de archivo aceptado
            return "Text Files";
        }
    };

    /**
     * Retorna la ruta de la ubicación de un archivo en forma reducida.
     * 
     * @param longpath la ruta de un archivo
     * @return la ruta reducida del archivo
     */
    private static String shortPathName(String longPath) {
        //construye un arreglo de cadenas, donde cada una es un nombre de directorio
        String[] tokens = longPath.split(Pattern.quote(File.separator));

        //construye un StringBuilder donde se añadirá el resultado
        StringBuilder shortpath = new StringBuilder();

        //itera sobre el arreglo de cadenas
        for (int i = 0 ; i < tokens.length ; i++) {
            if (i == tokens.length - 1) {              //si la cadena actual es la última, es el nombre del archivo
                shortpath.append(tokens[i]);    //añade al resultado sin separador
                break;                          //termina el bucle
            } else if (tokens[i].length() >= 10) {     //si la cadena actual tiene 10 o más caracteres
                //se toman los primeros 3 caracteres y se añade al resultado con un separador
                shortpath.append(tokens[i].substring(0, 3)).append("...").append(File.separator);
            } else {                                   //si la cadena actual tiene menos de 10 caracteres
                //añade al resultado con un separador
                shortpath.append(tokens[i]).append(File.separator);
            }
        }

        return shortpath.toString();    //retorna la cadena resultante
    }

    /**
     * Redondea la longitud de un archivo en KiloBytes si es necesario.
     * 
     * @param length longitud de un archivo
     * @return el tamaño redondeado  
     */
    private static String roundFileSize(long length) {
        //retorna el tamaño del archivo redondeado
        return (length < 1024) ? length + " bytes" : (length / 1024) + " Kbytes";
    }
}
/**
 * PrintAction.java
 */

package textpademo;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import javax.print.attribute.HashPrintRequestAttributeSet;
import javax.print.attribute.PrintRequestAttributeSet;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.WindowConstants;

/**
 * Clase que implementa la interface java.awt.print.Printable para imprimir el documento
 * presente en el área de edición.
 * 
 * @author Dark[byte]
 */
public class PrintAction implements Printable {

    private final JTextArea jTextArea;    //área de edición donde se encuentra el documento actual
    private JDialog dialog;               //dialogo de estado, muestra el estado de la impresión
    private int[] pageBreaks;             //arreglo de quiebres de página
    private String[] textLines;           //arreglo de líneas de texto
    private int currentPage = -1;         //página actual impresa, por defecto inicilizada en -1
    private boolean result = false;       //resultado de la impresión, por defecto es negativo

    /**
     * Constructor de la clase.
     * 
     * @param jComponent componente cuyo contenido se imprimirá
     */
    public PrintAction(JComponent jComponent) {
        this.jTextArea = (JTextArea) jComponent;    //guarda la instancia del área de edición
    }

    /**
     * Método estático que construye e inicializa la clase PrintAction para imprimir
     * el documento presente en el área de edición.
     * 
     * @param jComponent componente cuyo contenido se imprimirá
     * @param owner la ventana padre
     * @return true si la impresión fue exitosa, false en caso contrario
     */
    public static boolean print(JComponent jComponent, Frame owner) {
        PrintAction pa = new PrintAction(jComponent);    //construye una instancia de PrintAction
        return pa.printDialog(owner);                    //inicia la impresión y retorna un valor booleano
    }

    /**
     * Le permite al usuario configurar algunos aspectos de la impresión antes de comenzar. Durante
     * la impresión muestra una ventana de dialogo con información de la misma.
     * 
     * @param owner la ventana padre
     * @return true si la impresión fue exitosa, false en caso contrario
     */
    public boolean printDialog(Frame owner) {
        //construye un trabajo de impresión
        final PrinterJob pj = PrinterJob.getPrinterJob();
        //construye un conjunto de atributos para la impresión
        final PrintRequestAttributeSet pras = new HashPrintRequestAttributeSet();
        //establece a la clase PrintAction como responsable de renderizar las páginas del documento
        pj.setPrintable(this);

        boolean option = pj.printDialog(pras);    //presenta un dialogo de impresión

        if (option == true) {    //si el usuario acepta
            //construye el dialogo modal de estado sobre la ventana padre
            dialog = new PrintingMessageBox(owner, pj);

            //crea un nuevo hilo para que se ocupe de la impresión
            new Thread(new Runnable() {

                @Override
                public void run() {
                    try {
                        pj.print();                        //inicia la impresión
                        PrintAction.this.result = true;    //resultado positivo
                    } catch (PrinterException ex) {        //en caso de que ocurra una excepción
                        System.err.println(ex);
                    }

                    dialog.setVisible(false);    //oculta el dialogo de estado
                }
            }).start();    //inicia el hilo de impresión

            dialog.setVisible(true);    //hace visible el dialogo de estado
        }

        return PrintAction.this.result;    //retorna el resultado de la impresión
    }

    /**
     * Se renderiza cada página solicitada por el sistema de impresión.
     * 
     * @param g objeto de gráficos
     * @param pf el formato de la página
     * @param pageIndex el índice de la página a imprimir
     * @return PAGE_EXISTS si la página se tiene que imprimir, NO_SUCH_PAGE si la página no es valida
     */
    @Override
    public int print(Graphics g, PageFormat pf, int pageIndex) {
        Graphics2D g2d = (Graphics2D) g;                      //conversión de gráficos simples a gráficos 2D
        g2d.setFont(new Font("Serif", Font.PLAIN, 10));       //establece un fuente para todo el texto
        int lineHeight = g2d.getFontMetrics().getHeight();    //obtiene la altura del fuente

        if (pageBreaks == null) {    //si los quiebres de página no fueron calculados
            //construye un arreglo con las líneas de texto presentes en el área de edición
            textLines = jTextArea.getText().split("\n");
            //calcula el número de líneas que caben en cada página
            int linesPerPage = (int) (pf.getImageableHeight() / lineHeight);
            //calcula el número de quiebres de página necesarios para imprimir todo el documento
            int numBreaks = (textLines.length - 1) / linesPerPage;
            //construye un arreglo con los quiebres de página 
            pageBreaks = new int[numBreaks];
            for (int i = 0 ; i < numBreaks ; i++) {
                //se calcula la posición para cada quiebre de página
                pageBreaks[i] = (i + 1) * linesPerPage;
            }
        }

        //si el índice de página solicitado es menor o igual que la cantidad de quiebres total
        if (pageIndex <= pageBreaks.length) {
            /** establece una igualdad entre el origen del espacio gráfico (x:0,y:0) y el origen 
            del área imprimible definido por el formato de página */
            g2d.translate(pf.getImageableX(), pf.getImageableY());

            int y = 0;    //coordenada "y", inicializada en 0 (principio de página)
            //obtiene la primera línea para la página actual
            int startLine = (pageIndex == 0) ? 0 : pageBreaks[pageIndex - 1];
            //obtiene la última línea para la página actual
            int endLine = (pageIndex == pageBreaks.length) ? textLines.length : pageBreaks[pageIndex];

            //itera sobre las líneas que forman parte de la página actual
            for (int line = startLine ; line < endLine ; line++) {
                y += lineHeight;                          //aumenta la coordenada "y" para cada línea
                g2d.drawString(textLines[line], 0, y);    //imprime la linea en las coordenadas actuales
            }

            updateStatus(pageIndex);    //actualiza el estado de impresión

            return PAGE_EXISTS;     //la página solicitada será impresa
        } else {
            return NO_SUCH_PAGE;    //la pagina solicitada no es valida
        }
    }

    /**
     * Actualiza la información en el dialogo de estado de la impresión.
     * 
     * @param pageIndex índice de la página impresa
     */
    private void updateStatus(int pageIndex) {
        if (pageIndex != currentPage) {
            currentPage++;    //incrementa la página actual    

            //acceso seguro al EDT (Event Dispatch Thread) para actualizar la GUI
            javax.swing.SwingUtilities.invokeLater(new Runnable() {

                @Override
                public void run() {
                    //actualiza la información de la etiqueta lbStatusMsg
                    ((PrintingMessageBox) dialog).setStatusMsg("Imprimiendo página " + (currentPage + 1) + " ...");
                }
            });
        }
    }

    /**
     * Clase interna que extiende javax.swing.JDialog para presentar una ventana modal de dialogo
     * que muestra el estado de la impresión y un botón para cancelar la operación.
     */
    private class PrintingMessageBox extends JDialog {

        private JLabel lbStatusMsg;    //etiqueta que muestra el estado de impresión

        /**
         * Constructor de esta clase.
         * 
         * Construye una ventana modal de dialogo sobre una ventana padre.
         * 
         * @param owner la ventana padre
         * @param pj trabajo de impresión
         */
        public PrintingMessageBox(Frame owner, final PrinterJob pj) {
            /** invoca el constructor de la superclase para establecer la ventana padre, el título 
            de la ventana, y que será una ventana modal */
            super(owner, "Impresión", true);
            setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);

            //construye y configura la etiqueta que muestra el estado
            lbStatusMsg = new JLabel("Iniciando ...");
            lbStatusMsg.setPreferredSize(new Dimension(200, 30));
            lbStatusMsg.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));

            //construye el botón de cancelar
            JButton buttonCancel = new JButton("Cancelar");
            JPanel jp = new JPanel();
            jp.add(buttonCancel);

            //asigna un manejador de eventos para el botón de cancelar
            buttonCancel.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    pj.cancel();          //cancela el trabajo de impresión
                    setVisible(false);    //oculta esta ventana
                }
            });

            getContentPane().add(lbStatusMsg, BorderLayout.CENTER);    //añade la etiqueta en el CENTRO
            getContentPane().add(jp, BorderLayout.SOUTH);              //añade el botón, orientación SUR

            //asigna un manejador de eventos para cuando la ventana pierde la visibilidad
            this.addComponentListener(new ComponentAdapter() {

                @Override
                public void componentHidden(ComponentEvent e) {
                    Window w = (Window) e.getComponent();    //convierte el componente afectado en una ventana
                    w.dispose();                             //destruye la ventana
                }
            });

            setResizable(false);             //no se permite redimensionar la ventana
            pack();                          //se le da el tamaño preferido
            setLocationRelativeTo(owner);    //la ventana se centra sobre el editor de texto
        }

        /**
         * Establece el texto de estado que se muestra en el dialogo.
         * 
         * @param statusMsg texto de estado
         */
        public void setStatusMsg(String statusMsg) {
            lbStatusMsg.setText(statusMsg);    //establece el texto de la etiqueta lbStatusMsg
        }
    }
}
/**
 * JFontChooser.java
 */

package textpademo;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Frame;
import java.awt.GraphicsEnvironment;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.Arrays;
import java.util.Comparator;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.text.Position;

/**
 * Clase que extiende javax.swing.JComponent para crear un componente Swing donde el usuario
 * pueda seleccionar un fuente.
 * 
 * @author Dark[byte]
 */
public class JFontChooser extends JComponent {

    private final Font initialFont;    //fuente inicial
    private Font font;                 //fuente seleccionado por el usuario

    /**
     * Constructor de esta clase.
     * 
     * @param initialFont el fuente inicial
     */
    public JFontChooser(Font initialFont) {
        this.initialFont = initialFont;    //guarda el fuente inicial
    }

    /**
     * Método estático que construye e inicializa la clase JFontChooser para presentar al 
     * usuario el dialogo de selección de fuente.
     * 
     * @param owner la ventana padre
     * @param title el título de la ventana
     * @param initialFont el fuente inicial
     * 
     * @return el fuente seleccionado
     */
    public static Font showDialog(Frame owner, String title, Font initialFont) {
        JFontChooser fontChooser = new JFontChooser(initialFont);    //construye una instancia de JFontChooser

        //construye el dialogo de selección de fuente sobre la ventana padre
        JDialog dialog = new FontChooserDialog(owner, title, fontChooser);
        dialog.setVisible(true);    //hace visible el dialogo

        return fontChooser.getSelectedFont();    //retorna el fuente seleccionado
    }

    /**
     * Retorna el fuente inicial.
     * 
     * @return el fuente inicial
     */
    public Font getInitialFont() {
        return initialFont;
    }

    /**
     * Retorna el fuente seleccionado.
     * 
     * @return el fuente seleccionado
     */
    public Font getSelectedFont() {
        return font;
    }

    /**
     * Establece el fuente seleccionado.
     * 
     * @param font el fuente seleccionado
     */
    public void setSelectedFont(Font font) {
        this.font = font;
    }
}

class FontChooserDialog extends JDialog {

    private JTextField textFieldNames;     //campo de texto para el nombre del fuente
    private JTextField textFieldStyles;    //campo de texto para el estilo del fuente
    private JTextField textFieldSizes;     //campo de texto para el tamaño del fuente
    private JList listFontNames;     //lista para nombres de fuente
    private JList listFontStyles;    //lista para estilos de fuente
    private JList listFontSizes;     //lista para tamaños de fuente
    private JLabel textExample;    //etiqueta que muestra un ejemplo del fuente seleccionado
    //arreglo con nombres de fuente disponibles
    private static final String[] FONT_NAMES = GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
    //arreglo con estilos de fuente
    private static final String[] FONT_STYLES = {
        "Normal", "Bold", "Italic", "Bold Italic"
    };
    //arreglo con tamaños de fuente
    private static final String[] FONT_SIZES = {
        "8", "9", "10", "11", "12", "13", "14", "16", "18", "20", "24", "28", "32", "48", "72"
    };

    /**
     * Constructor de la clase.
     * 
     * Construye una ventana modal de dialogo sobre una ventana padre. El usuario 
     * puede seleccionar un fuente.
     * 
     * @param owner la ventana padre
     * @param title
     * @param jFontChooser 
     */
    public FontChooserDialog(Frame owner, String title, final JFontChooser jFontChooser) {
        /** invoca el constructor de la superclase para establecer la ventana padre, el título 
        de la ventana, y que será una ventana modal */
        super(owner, title, true);

        final EventHandler eventHandler = new EventHandler();    //construye una instancia de EventHandler
        JScrollPane jScrollPane;

        JPanel cp = (JPanel) getContentPane();                        //obtiene el panel de contenido principal
        cp.setLayout(new GridBagLayout());                            //establece un GridBagLayout
        cp.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));    //establece un borde de espacio

        //construye un conjunto de limitaciones para los componentes del GridBagLayout
        GridBagConstraints gbc = new GridBagConstraints();

        JLabel label1 = new JLabel("Name:");    //construye la etiqueta "Name:"
        gbc.insets = new Insets(0, 5, 0, 5);
        gbc.anchor = GridBagConstraints.FIRST_LINE_START;
        gbc.weightx = 0.5;
        gbc.gridx = 1;
        gbc.gridy = 1;
        gbc.gridwidth = 1;
        gbc.gridheight = 1;
        cp.add(label1, gbc);    //añade la etiqueta, coordenadas X:1 - Y:1

        JLabel label2 = new JLabel("Style:");    //construye la etiqueta "Style:"
        gbc.gridx = 2;
        gbc.gridy = 1;
        gbc.gridwidth = 1;
        gbc.gridheight = 1;
        cp.add(label2, gbc);    //añade la etiqueta, coordenadas X:2 - Y:1

        JLabel label3 = new JLabel("Size:");    //construye la etiqueta "Size:"
        gbc.gridx = 3;
        gbc.gridy = 1;
        gbc.gridwidth = 1;
        gbc.gridheight = 1;
        cp.add(label3, gbc);    //añade la etiqueta, coordenadas X:3 - Y:1

        textFieldNames = new JTextField("");    //construye el campo de texto para el nombre del fuente
        int fixedWidth = textFieldNames.getPreferredSize().width;
        Dimension fixedSize = new Dimension(fixedWidth, 20);
        textFieldNames.setMinimumSize(fixedSize);
        textFieldNames.setMaximumSize(fixedSize);
        textFieldNames.setPreferredSize(fixedSize);
        textFieldNames.addKeyListener(eventHandler);    //asigna el manejador de eventos para el teclado
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.gridx = 1;
        gbc.gridy = 2;
        gbc.gridwidth = 1;
        gbc.gridheight = 1;
        cp.add(textFieldNames, gbc);    //añade el campo de texto, coordenadas X:1 - Y:2

        textFieldStyles = new JTextField("");    //construye el campo de texto para el estilo del fuente
        fixedWidth = textFieldStyles.getPreferredSize().width;
        fixedSize = new Dimension(fixedWidth, 20);
        textFieldStyles.setMinimumSize(fixedSize);
        textFieldStyles.setMaximumSize(fixedSize);
        textFieldStyles.setPreferredSize(fixedSize);
        textFieldStyles.addKeyListener(eventHandler);    //asigna el manejador de eventos para el teclado
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.gridx = 2;
        gbc.gridy = 2;
        gbc.gridwidth = 1;
        gbc.gridheight = 1;
        cp.add(textFieldStyles, gbc);    //añade el campo de texto, coordenadas X:2 - Y:2

        textFieldSizes = new JTextField("");    //construye el campo de texto para el tamaño del fuente
        fixedWidth = textFieldSizes.getPreferredSize().width;
        fixedSize = new Dimension(fixedWidth, 20);
        textFieldSizes.setMinimumSize(fixedSize);
        textFieldSizes.setMaximumSize(fixedSize);
        textFieldSizes.setPreferredSize(fixedSize);
        textFieldSizes.addKeyListener(eventHandler);    //asigna el manejador de eventos para el teclado
        gbc.fill = GridBagConstraints.HORIZONTAL;
        gbc.gridx = 3;
        gbc.gridy = 2;
        gbc.gridwidth = 1;
        gbc.gridheight = 1;
        cp.add(textFieldSizes, gbc);    //añade el campo de texto, coordenadas X:3 - Y:2

        listFontNames = new JList(FONT_NAMES);    //construye la lista para nombres de fuente
        jScrollPane = new JScrollPane(listFontNames);
        String fontName = jFontChooser.getInitialFont().getName();
        listFontNames.setSelectedValue(fontName, true);    //selecciona el nombre de fuente inicial
        textFieldNames.setText(fontName);
        listFontNames.addListSelectionListener(eventHandler);
        gbc.gridx = 1;
        gbc.gridy = 3;
        gbc.gridwidth = 1;
        gbc.gridheight = 1;
        cp.add(jScrollPane, gbc);    //añade la lista, coordenadas X:1 - Y:3

        listFontStyles = new JList(FONT_STYLES);    //construye la lista para estilos de fuente
        jScrollPane = new JScrollPane(listFontStyles);
        int fontSyle = jFontChooser.getInitialFont().getStyle();
        listFontStyles.setSelectedIndex(fontSyle);    //selecciona el estilo de fuente inicial
        textFieldStyles.setText(listFontStyles.getSelectedValue().toString());
        listFontStyles.addListSelectionListener(eventHandler);
        gbc.gridx = 2;
        gbc.gridy = 3;
        gbc.gridwidth = 1;
        gbc.gridheight = 1;
        cp.add(jScrollPane, gbc);    //añade la lista, coordenadas X:2 - Y:3

        listFontSizes = new JList(FONT_SIZES);    //construye la lista para tamaños de fuente
        jScrollPane = new JScrollPane(listFontSizes);
        String fontSize = String.valueOf(jFontChooser.getInitialFont().getSize());
        listFontSizes.setSelectedValue(fontSize, true);    //selecciona el tamaño de fuente inicial
        textFieldSizes.setText(fontSize);
        listFontSizes.addListSelectionListener(eventHandler);
        gbc.gridx = 3;
        gbc.gridy = 3;
        gbc.gridwidth = 1;
        gbc.gridheight = 1;
        cp.add(jScrollPane, gbc);    //añade la lista, coordenadas X:3 - Y:3

        textExample = new JLabel("AaBaCcDdEeFfGgHhJj");        //construye la etiqueta para mostrar un ejemplo del fuente seleccionado 
        textExample.setHorizontalAlignment(JLabel.CENTER);
        textExample.setFont(jFontChooser.getInitialFont());    //establece el fuente inicial
        textExample.setOpaque(true);
        textExample.setBackground(Color.WHITE);
        textExample.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("Texto de ejemplo"),
                                                                 BorderFactory.createEmptyBorder(10, 10, 10, 10)));
        int fixedHeight = textExample.getPreferredSize().height;
        fixedSize = new Dimension(100, fixedHeight);
        textExample.setMinimumSize(fixedSize);
        textExample.setMaximumSize(fixedSize);
        textExample.setPreferredSize(fixedSize);
        gbc.gridx = 1;
        gbc.gridy = 4;
        gbc.gridwidth = 3;
        gbc.gridheight = 1;
        cp.add(textExample, gbc);    //añade la etiqueta, coordenadas X:1 - Y:4

        JPanel jp = new JPanel();                        //construye un panel para los botones
        JButton buttonOk = new JButton("Ok");            //construye el botón de aceptar
        JButton buttonCancel = new JButton("Cancel");    //construye el botón de cancelar
        jp.add(buttonOk);                                //añade los botones al panel
        jp.add(buttonCancel);
        gbc.anchor = GridBagConstraints.CENTER;
        gbc.gridx = 1;
        gbc.gridy = 5;
        cp.add(jp, gbc);    //añade el panel, coordenadas X:1 - Y:5

        //asigna un manejador de eventos para el botón de cancelar
        buttonOk.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                jFontChooser.setSelectedFont(getSelectedFont());
                //fontChooser.font = getSelectedFont();
                setVisible(false);
            }
        });

        //asigna un manejador de eventos para el botón de cancelar
        buttonCancel.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                setVisible(false);    //oculta esta ventana
            }
        });

        //asigna un manejador de eventos para el cierre del JDialog
        this.addWindowListener(new WindowAdapter() {

            @Override
            public void windowClosing(WindowEvent e) {
                Window w = e.getWindow();    //convierte el componente afectado en una ventana
                w.setVisible(false);         //oculta esta ventana
            }
        });

        //asigna un manejador de eventos para cuando la ventana pierde la visibilidad
        this.addComponentListener(new ComponentAdapter() {

            @Override
            public void componentHidden(ComponentEvent e) {
                Window w = (Window) e.getComponent();    //convierte el componente afectado en una ventana
                w.dispose();                             //destruye la ventana
            }
        });

        setResizable(false);              //no se permite redimensionar la ventana
        pack();                           //se le da el tamaño preferido
        setLocationRelativeTo(owner);     //la ventana se centra sobre el editor de texto
    }

    /**
     * Retorna el fuente seleccionado por el usuario. Es el producto resultante de la elección de 
     * un tipo, un estilo y un tamaño
     * 
     * @return el fuente seleccionado.
     */
    public Font getSelectedFont() {
        try {
            //retorna el fuente seleccionado en el dialogo
            return new Font(String.valueOf(listFontNames.getSelectedValue()), listFontStyles.getSelectedIndex(),
                            Integer.parseInt(String.valueOf(listFontSizes.getSelectedValue())));
        } catch (NumberFormatException nfe) {    //en caso de que ocurra una excepción
            System.err.println(nfe);
        }
        return null;    //retorna null
    }

    /**
     * Clase interna que extiende e implementa las clases e interfaces necesarias para 
     * atender y manejar los eventos sobre la ventana de selección de fuente.
     */
    class EventHandler extends KeyAdapter implements Comparator<String>,
                                                     ListSelectionListener {

        /**
         * Atiende y maneja los eventos de teclado cuando se libera una tecla.
         * 
         * @param ke evento de tecla
         */
        @Override
        public void keyReleased(KeyEvent ke) {
            //obtiene el origen del evento, y lo convierte en un campo de texto
            final JTextField eventTField = (JTextField) ke.getSource();
            final String text = eventTField.getText();    //obtiene el contenido del campo de texto


            //se averigua en que campo de texto se ah ejecutado el evento
            if (eventTField == textFieldNames) {    //si el campo de texto es textFieldNames
                //obtiene el índice del proximo elemento en la lista que coincida con el contenido del campo de texto
                int index = listFontNames.getNextMatch(text, 0, Position.Bias.Forward);

                if (index > -1) {    //si el índice es mayor que -1
                    /** realiza una búsqueda binaria sobre el arreglo de nombres de fuente, se utiliza
                    esta clase como comparador implementando la interface Comparator */
                    if (Arrays.binarySearch(FONT_NAMES, text, this) > -1) //si el resultado es mayor que -1
                    {
                        listFontNames.setSelectedIndex(index);    //selecciona el índice
                    }

                    listFontNames.ensureIndexIsVisible(index);    //hace el índice visible en la lista
                }
            } else if (eventTField == textFieldStyles) {    //si el campo de texto es textFieldStyles
                //obtiene el índice del proximo elemento en la lista que coincida con el contenido del campo de texto
                int index = listFontStyles.getNextMatch(text, 0, Position.Bias.Forward);

                if (index > -1) {    //si el índice es mayor que -1
                    //itera sobre los elementos en el arreglo de estilos de fuente
                    for (int i = 0 ; i < FONT_STYLES.length ; i++) {
                        //si el contenido del campo de texto es igual al elemento actual
                        if (text.equalsIgnoreCase(FONT_STYLES[i]) == true) {
                            listFontStyles.setSelectedIndex(index);    //selecciona el índice
                        }
                    }

                    listFontStyles.ensureIndexIsVisible(index);    //hace el índice visible en la lista
                }
            } else if (eventTField == textFieldSizes) {    //si el campo de texto es textFieldSizes
                //obtiene el índice del proximo elemento en la lista que coincida con el contenido del campo de texto
                int index = listFontSizes.getNextMatch(text, 0, Position.Bias.Forward);

                if (index > -1) {    //si el índice es mayor que -1
                    //itera sobre los elementos en el arreglo de tamaños de fuente
                    for (int i = 0 ; i < FONT_SIZES.length ; i++) {
                        //si el contenido del campo de texto es igual al elemento actual
                        if (text.equalsIgnoreCase(FONT_SIZES[i]) == true) {
                            listFontSizes.setSelectedIndex(index);    //selecciona el índice
                        }
                    }

                    listFontSizes.ensureIndexIsVisible(index);    //hace el índice visible en la lista
                }
            }

            //establece el fuente seleccionado en la etiqueta de muestra
            textExample.setFont(getSelectedFont());
        }

        /**
         * Comparación lexicográfica de dos cadenas de texto ignorando mayúsculas.
         * 
         * @param string1 una cadena de texto
         * @param string2 otra cadena de texto
         * @return true si son dos cadenas iguales, false en caso contrario 
         */
        @Override
        public int compare(String string1, String string2) {
            //compara dos cadenas de texto ignorando mayúsculas
            return string1.compareToIgnoreCase(string2);
        }

        /**
         * Atiende y maneja los eventos de selección en las listas de la ventana.
         * 
         * @param lse evento de selección en una lista
         */
        @Override
        public void valueChanged(ListSelectionEvent lse) {
            //averigua en que lista se ah ejecutado el evento
            if (lse.getSource() == listFontNames) {    //si la lista es listFontNames
                //establece el contenido del campo de texto textFieldNames
                textFieldNames.setText(String.valueOf(listFontNames.getSelectedValue()));
            } else if (lse.getSource() == listFontStyles) {    //si la lista es listFontStyles
                //establece el contenido del campo de texto textFieldStyles
                textFieldStyles.setText(String.valueOf(listFontStyles.getSelectedValue()));
            } else if (lse.getSource() == listFontSizes) {    //si la lista es listFontSizes
                //establece el contenido del campo de texto textFieldSizes
                textFieldSizes.setText(String.valueOf(listFontSizes.getSelectedValue()));
            }

            //establece el fuente seleccionado en la etiqueta de muestra
            textExample.setFont(getSelectedFont());
        }
    }
}

Estas son las imágenes .PNG 32×32 que se utilizan como recursos en el proyecto:


, , , ,

  1. #1 por MaXr el septiembre 27, 2012 - 3:29 pm

    Muchas gracias…. Funciona perfecto y los comentarios en el código están excelentes

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: