swingx-master/swingx-core/src/test/java/org/jdesktop/swingx/FindVisualCheck.java
/*
 * $Id$
 *
 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 */

package org.jdesktop.swingx;

import java.awt.Color;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.io.IOException;
import java.net.URL;
import java.util.logging.Logger;
import java.util.regex.Pattern;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;

import org.jdesktop.swingx.action.AbstractActionExt;
import org.jdesktop.swingx.decorator.AbstractHighlighter;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.CompoundHighlighter;
import org.jdesktop.swingx.decorator.HighlightPredicate;
import org.jdesktop.swingx.decorator.SearchPredicate;
import org.jdesktop.swingx.renderer.DefaultTreeRenderer;
import org.jdesktop.swingx.renderer.IconValues;
import org.jdesktop.swingx.renderer.StringValues;
import org.jdesktop.swingx.search.SearchFactory;
import org.jdesktop.swingx.search.TableSearchable;
import org.jdesktop.swingx.search.FindTest.TestListModel;
import org.jdesktop.swingx.search.FindTest.TestTableModel;
import org.jdesktop.swingx.treetable.FileSystemModel;
import org.jdesktop.test.AncientSwingTeam;

public class FindVisualCheck extends InteractiveTestCase {
    @SuppressWarnings("unused")
    private static final Logger LOG = Logger.getLogger(FindVisualCheck.class
            .getName());
    public static void main(String args[]) {
      setSystemLF(true);
//      Locale.setDefault(new Locale("es"));
      FindVisualCheck test = new FindVisualCheck();
      try {
        test.runInteractiveTests();
//          test.runInteractiveTests("interactive.*Compare.*");
//          test.runInteractiveTests("interactive.*Close.*");
      } catch (Exception e) {
          System.err.println("exception when executing interactive tests:");
          e.printStackTrace();
      }
  }
    @Override
    protected void setUp() {
        editorURL = FindVisualCheck.class.getResource("resources/test.txt");
    }
    

    private URL editorURL;

    public FindVisualCheck() {
        super("Find Action Test");
    }

    public void interactiveTableSearchColumn() {
        JXTable table = new JXTable();
        table.setSearchable(new SearchColumnTableSearchable(table, 0));
        table.setModel(new TestTableModel());
        SearchFactory.getInstance().setUseFindBar(true);
        showWithScrollingInFrame(table, "search restricted to first column");
        
    }
    
    /**
     * Custom searchable which supports per-column searching in a JXTable.
     */
    public static class SearchColumnTableSearchable extends TableSearchable {

        public final static int SEARCH_ALL_COLUMNS = -1; 
        int searchColumn;
        
        /**
         * @param table
         */
        public SearchColumnTableSearchable(JXTable table) {
            this(table, SEARCH_ALL_COLUMNS);
        }
        /**
         * @param table
         * @param searchAllColumns
         */
        public SearchColumnTableSearchable(JXTable table, int searchColumn) {
            super(table);
            // TODO : add getters and setters for searchColumn
            this.searchColumn = searchColumn;
        }
        
        /**
         * {@inheritDoc} <p>
         *  
         *  Overridden to support per-column searching.
         */
        @Override
        protected void findMatchAndUpdateState(Pattern pattern, int startRow,
                boolean backwards) {
            if (SEARCH_ALL_COLUMNS == searchColumn) {
                super.findMatchAndUpdateState(pattern, startRow, backwards);
            } else {
                SearchResult result = findMatchInSearchColumn(pattern, startRow, backwards);
                updateState(result);
            }
        }
        /**
         * @param pattern <code>Pattern</code> that we will try to locate
         * @param startRow position in the document in the appropriate
         *        coordinates from which we will start search or -1 to start
         *        from the beginning
         * @param backwards <code>true</code> if we should perform search
         *        towards the beginning
         */
        private SearchResult findMatchInSearchColumn(Pattern pattern,
                int startRow, boolean backwards) {
            SearchResult searchResult = null;
            if (backwards) {
                for (int index = startRow; index >= 0 && searchResult == null; index--) {
                    searchResult = findMatchAt(pattern, index, searchColumn);
                }
            } else {
                for (int index = startRow; index < getSize()
                        && searchResult == null; index++) {
                    searchResult = findMatchAt(pattern, index, searchColumn);
                }
            }
            return searchResult;

        }

        /**
         * {@inheritDoc} <p>
         */
        @Override
        protected int moveStartPosition(int startIndex, boolean backwards) {
            if (backwards) {
                return super.moveStartPosition(startIndex, backwards);
            } else {
                lastSearchResult.resetFoundColumn();
                if (backwards) {
                    startIndex--;
                } else {
                    startIndex++;
                }
                return startIndex;

            }
        }
     
        
        
    }
    /**
     * Requirement: mark all matches. Working in incrememtal find mode only
     */
    public void interactiveTableMarkAllMatches() {
        JXTable table = new JXTable();
        table.setSearchable(new MarkAllTableSearchable(table));
        table.setModel(new TestTableModel());
//        SearchFactory.getInstance().setUseFindBar(true);
        showWithScrollingInFrame(table, "mark all matches - must enable incremental find mode");
    }
    
    /**
     * Searchable which highlights all matches.
     */
    public static class MarkAllTableSearchable extends TableSearchable {
        
        private ColorHighlighter cell;
        private ColorHighlighter base;

        /**
         * @param table
         */
        public MarkAllTableSearchable(JXTable table) {
            super(table);
        }

        
        @Override
        protected AbstractHighlighter getConfiguredMatchHighlighter() {
            AbstractHighlighter hl = super.getConfiguredMatchHighlighter();
            if (hasMatch()) {
                cell.setHighlightPredicate(super.createMatchPredicate());
            }
            return hl;
        }


        @Override
        protected HighlightPredicate createMatchPredicate() {
            return hasMatch() ? new SearchPredicate(lastSearchResult.getPattern()) : HighlightPredicate.NEVER;
        }

        @Override
        protected AbstractHighlighter createMatchHighlighter() {
            base = new ColorHighlighter(Color.YELLOW.brighter(), null, 
                    Color.YELLOW.darker(), null);
            cell = new ColorHighlighter(Color.YELLOW.darker(), null);
            CompoundHighlighter match = new CompoundHighlighter(base, cell);
            return match;
        }

    }

    
   /**
    * Requirement: hook into close. 
    * 
    * Solution: put the custom action in the panel's action map with the 
    * key JXDialog.CLOSE_ACTION_COMMAND.
    * 
    * Nasty problem is that the custom action overwrites the default, 
    * so the dialog is not closed. Nasty solution is to go up the until the
    * JXDialog is found and manually invoke its close.
    */
    public void interactiveCustomClose() {
        final SearchFactory custom = new SearchFactory() {

            @Override
            public JXFindPanel createFindPanel() {
                final JXFindPanel panel = super.createFindPanel();
                Action customClose = new AbstractAction() {

                    public void actionPerformed(ActionEvent e) {
                        LOG.info("my action");
                        // this is nasty ... 
                        Window window = SwingUtilities.getWindowAncestor(panel);
                        if (window instanceof JXDialog) {
                            ((JXDialog) window).doClose();
                        }
                    }
                    
                };
                panel.getActionMap().put(JXDialog.CLOSE_ACTION_COMMAND, customClose);
                return panel;
            }
            
        };
        final JXTable table = new JXTable(new AncientSwingTeam());
        Action customFind = new AbstractAction() {

            public void actionPerformed(ActionEvent e) {
                custom.showFindDialog(table, table.getSearchable());
                
            }
            
        };
        table.getActionMap().put("find", customFind);
        showWithScrollingInFrame(table, "augment close action of dialog");
        
    }
    /**
     * Issue #720, 692-swingx: findDialog on tree selection as match-marker lost
     * 
     * Scenario (#692): open find via button
     * - press button to open find
     * - run a search with match, selects a node
     * - close findDialog, clears selection
     * 
     * Scenario (#702): open find via ctrl-f
     * - focus tree
     * - ctrl-f to open findDialog
     * - run a search with match, selects a node
     * - close findDialog
     * - tab to button, clears selection
     */
    public void interactiveFindDialogSelectionTree() {
        final JXTree table = new JXTree();
        JComponent comp = Box.createVerticalBox();
        comp.add(new JScrollPane(table));
        Action action = new AbstractActionExt("open find dialog") {

            public void actionPerformed(ActionEvent e) {
                SearchFactory.getInstance().showFindDialog(table, table.getSearchable());
                
            }
            
        };
        comp.add(new JButton(action));
        JXFrame frame = wrapInFrame(comp, "Tree FindDialog: selection lost");
        frame.setVisible(true);
    }
    
    /**
     * Issue #718-swingx: finddialog not updated on LF switch.
     * 
     * Hmm .. shouldn't a lf-switcher update all windows? Like the
     * setPlafAction in InteractiveTestCase does (since today <g>).
     * 
     * Yeah, but the dialog had been disposed and the findPanel unparented 
     * if focus is moved somewhere "outside" of the target. Needed to add something
     * focusable to reproduce here: 
     * - open find in table
     * - click button
     * - toggle LF
     * - open find in table: the panel is not changed to new LF
     */
    public void interactiveFindDialogUpdateLF() {
        JXTable table = new JXTable(new AncientSwingTeam());
        table.setColumnControlVisible(true);
        JComponent comp = Box.createVerticalBox();
        comp.add(new JScrollPane(table));
        comp.add(new JButton("something to focus"));
        JXFrame frame = wrapInFrame(comp, "FindDialog on toggleLF", true);
        frame.setVisible(true);
    }
    
    
    public void interactiveShowTree() {
        JXTree tree = new JXTree(new FileSystemModel());
        tree.setCellRenderer(new DefaultTreeRenderer(IconValues.FILE_ICON, StringValues.FILE_NAME));
        tree.setRowHeight(30);
        tree.setLargeModel(true);
        showComponent(tree, "Search in XTree");
    }
    
    public void interactiveShowList() {
        showComponent(new JXList(new TestListModel()), "Search in XList");
    }
    
    public void interactiveShowTable() {
        showComponent(new JXTable(new TestTableModel()), "Search in XTable");
    }



    public void interactiveCompareFindStrategy() {
        final JXTable first = new JXTable(new TestTableModel());
        first.setColumnControlVisible(true);
        final JXTreeTable second = new JXTreeTable(new FileSystemModel());
        JXFrame frame = wrapWithScrollingInFrame(first, second, "Batch/Incremental Search");
        addSearchModeToggle(frame);
        addMessage(frame, "Press ctrl-F to open search widget");
        frame.setVisible(true);
    }

    public void interactiveShowSplitPane() {
       showComponent(createEditor(), new JXTable(new TestTableModel()), "Targetable Search");
    }
    
    public void interactiveShowEditor() {
        showComponent(createEditor(), "Search in XEditorPane");
    }

    /**
     * @return
     * @throws IOException
     */
    private JXEditorPane createEditor()  {
        try {
            return new JXEditorPane(editorURL);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return null;
    }


    public void showComponent(JComponent component, JComponent second, String title) {
        
        JXFrame frame;
        if (second != null) {
          frame = wrapWithScrollingInFrame(component, second, title);
        } else {
            frame= wrapWithScrollingInFrame(component, title);
        }
        
        addMessage(frame, "Press ctrl-F to open search widget");
        frame.setSize(600, 400);
        frame.setVisible(true);
        
    }
    public void showComponent(JComponent component, String title) {
        showComponent(component, null, title);
    }

    
    

    /**
     * Do nothing, keep testRunner happy.
     */
    public void testDummy() {
        
    }
}
