swingx-master/swingx-core/src/test/java/org/jdesktop/swingx/plaf/basic/BasicMonthViewVisualCheck.java
/*
 * $Id$
 *
 * Copyright 2007 Sun Microsystems, Inc., 4150 Network Circle,
 * Santa Clara, California 95054, U.S.A. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */
package org.jdesktop.swingx.plaf.basic;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Locale;
import java.util.logging.Logger;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.Box;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.plaf.ComponentUI;

import org.jdesktop.swingx.InteractiveTestCase;
import org.jdesktop.swingx.JXDatePicker;
import org.jdesktop.swingx.JXFrame;
import org.jdesktop.swingx.JXMonthView;
import org.jdesktop.swingx.SwingXUtilities;
import org.jdesktop.swingx.action.AbstractActionExt;
import org.jdesktop.swingx.calendar.DateSelectionModel.SelectionMode;
import org.jdesktop.swingx.plaf.basic.BasicMonthViewUI.RenderingHandler;
import org.jdesktop.swingx.renderer.FormatStringValue;
import org.jdesktop.swingx.renderer.StringValue;

/**
 * TODO add type doc
 * 
 * @author Jeanette Winzenburg
 */
public class BasicMonthViewVisualCheck extends InteractiveTestCase {

    @SuppressWarnings("unused")
    private static final Logger LOG = Logger
            .getLogger(BasicMonthViewVisualCheck.class.getName());
    
    public static void main(String[] args) {
        BasicMonthViewVisualCheck test = new BasicMonthViewVisualCheck();
        try {
//            test.runInteractiveTests();
            test.runInteractiveTests(".*Rendering.*");
//            test.runInteractiveTests(".*Debug.*");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static class MyMonthViewUI extends BasicMonthViewUI {

        @SuppressWarnings({"UnusedDeclaration"})
        public static ComponentUI createUI(JComponent c) {
            return new MyMonthViewUI();
        }

        @Override
        protected CalendarRenderingHandler createRenderingHandler() {
            return new MyCalendarRenderingHandler();
        }
        
        
    }
    
    /**
     * Issue #1062-swingx: core formats don't use arabic digits. Opened rendering 
     */
    public static class MyCalendarRenderingHandler extends RenderingHandler {

        @Override
        protected StringValue createDayStringValue(Locale locale) {
            if (locale == null) {
                locale = Locale.getDefault();
            }
            SimpleDateFormat dateFormat = new SimpleDateFormat("d", locale);
            LOG.info("got here? " + locale.getCountry() + "-" + locale.getLanguage());
            adjustFormatSymbols(locale, (DecimalFormat) dateFormat.getNumberFormat());
            FormatStringValue sv = new FormatStringValue(dateFormat) {

                @Override
                public String getString(Object value) {
                    if (value instanceof Calendar) {
                        ((DateFormat) getFormat()).setTimeZone(((Calendar) value).getTimeZone());
                        value = ((Calendar) value).getTime();
                    }
                    return super.getString(value);
                }

            };
            return sv;
        }

        /**
         * @param locale
         * @param df
         */
        private void adjustFormatSymbols(Locale locale, DecimalFormat df) {
            if ("ar".equals(locale.getLanguage())) {
                DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
                // set the beginning of the range to Arabic digits
                dfs.setZeroDigit('\u0660');
                df.setDecimalFormatSymbols(dfs);
            }
        }

        @Override
        protected StringValue createWeekOfYearStringValue(Locale locale) {
            if (locale == null) {
                locale = Locale.getDefault();
            }
            DecimalFormat format = (DecimalFormat) NumberFormat.getInstance(locale);
            adjustFormatSymbols(locale, format);
            StringValue wsv = new FormatStringValue(format) {

                @Override
                public String getString(Object value) {
                    if (value instanceof Calendar) {
                        value = ((Calendar) value).get(Calendar.WEEK_OF_YEAR);
                    }
                    return super.getString(value);
                }

            };
            return wsv;
        }

        
        
    }
    /**
     * Issue #750-swingx: use rendering to side-step antialiase probs.
     * 
     * Debugging ...
     */
    public void interactiveRenderingOn() {
        new JXMonthView();
        // KEEP this is global state - uncomment for debug painting completely
//        // use spinning navigation header if zoomable
//        UIManager.put(CalendarHeaderHandler.uiControllerID, "org.jdesktop.swingx.plaf.basic.SpinningCalendarHeaderHandler");
//        // configure header: arrows around month text
//        UIManager.put(SpinningCalendarHeaderHandler.ARROWS_SURROUND_MONTH, Boolean.TRUE);
//        // configuter header: allow focus in spinner text (== editable)
//        UIManager.put(SpinningCalendarHeaderHandler.FOCUSABLE_SPINNER_TEXT, Boolean.TRUE);
//        // custom ui delegate for arabic digits in formats
//        UIManager.put(JXMonthView.uiClassID, "org.jdesktop.swingx.plaf.basic.BasicMonthViewUIVisualCheck$MyMonthViewUI");
//        // force picker to use a zoomable monthView by default
//        UIManager.put("JXDatePicker.forceZoomable", Boolean.TRUE);
        // KEEP this is global state - uncomment for debug painting completely
        UIManager.put("JXMonthView.trailingDayForeground", Color.YELLOW);
        UIManager.put("JXMonthView.leadingDayForeground", Color.ORANGE);
        UIManager.put("JXMonthView.weekOfTheYearForeground", Color.GREEN);
        UIManager.put("JXMonthView.unselectableDayForeground", Color.MAGENTA);
        String frameTitle = "Debug painting: rendering on";
        showDebugMonthView(frameTitle);
    }

    /**
     * @param frameTitle
     * @param disableRendering
     */
    private void showDebugMonthView(String frameTitle) {
        final JXMonthView monthView = new JXMonthView();
        LOG.info("todayBack: " + monthView.getTodayBackground());
//        monthView.setZoomable(true);
        monthView.setDayForeground(Calendar.SUNDAY, Color.BLUE);
        monthView.setDaysOfTheWeekForeground(Color.RED);
        monthView.setFlaggedDayForeground(Color.CYAN);
        monthView.setSelectionBackground(Color.GRAY);
        monthView.setSelectionForeground(Color.GREEN);
        monthView.setTodayBackground(Color.PINK);
        monthView.setTraversable(true);
        monthView.setShowingWeekNumber(true);
        monthView.setShowingLeadingDays(true);
        monthView.setShowingTrailingDays(true);
        monthView.setSelectionMode(SelectionMode.SINGLE_INTERVAL_SELECTION);
        monthView.setPreferredColumnCount(2);
        monthView.setPreferredRowCount(2);
        final JXFrame frame = wrapInFrame(monthView, frameTitle);
        addComponentOrientationToggle(frame);
        Action toggleTraversable = new AbstractAction("toggle zoomable") {

            public void actionPerformed(ActionEvent e) {
                monthView.setZoomable(!monthView.isZoomable());
                
            }
            
        };
        addAction(frame, toggleTraversable);
        addEnabledToggle(frame, monthView);
        final JXDatePicker picker = new JXDatePicker();
        picker.getMonthView().setShowingWeekNumber(monthView.isShowingWeekNumber());
        picker.getMonthView().setZoomable(true);
        Action toggleShowingWeekNumbers = new AbstractAction("toggle weekNumbers") {
            
            public void actionPerformed(ActionEvent e) {
                monthView.setShowingWeekNumber(!monthView.isShowingWeekNumber());
                picker.getMonthView().setShowingWeekNumber(monthView.isShowingWeekNumber());
            }
            
        };
        addAction(frame, toggleShowingWeekNumbers);
        picker.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                if (e.getActionCommand().equals(JXDatePicker.CANCEL_KEY)) return;
                if (picker.getDate() == null) return;
                monthView.setFlaggedDates(picker.getDate());
            }
            
        });
        final JXDatePicker unselectable = new JXDatePicker();
        unselectable.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                if (e.getActionCommand().equals(JXDatePicker.CANCEL_KEY)) return;
                if (unselectable.getDate() == null) return;
                monthView.setUnselectableDates(unselectable.getDate());
            }
            
        });
        final JComboBox zoneSelector = new JComboBox(Locale.getAvailableLocales());
        // Synchronize the monthView's and selector's zones.
        zoneSelector.setSelectedItem(monthView.getLocale());

        // Set the monthView's time zone based on the selected time zone.
        zoneSelector.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent event) {
                Locale zone = (Locale) zoneSelector.getSelectedItem();
                SwingXUtilities.setComponentTreeLocale(frame, zone);
//                monthView.setLocale(zone);
//                monthView.revalidate();
            }
        });


        JComponent pickers = Box.createHorizontalBox();
        pickers.add(new JLabel("Flagged: "));
        pickers.add(picker);
        pickers.add(new JLabel("Unselectable: "));
        pickers.add(unselectable);
        pickers.add(new JLabel("Locale: "));
        pickers.add(zoneSelector);
        frame.add(pickers, BorderLayout.SOUTH);
        show(frame);
    }


    /**
     * Issue #736-swingx: monthView cannot cope with minimalDaysInFirstWeek.
     * 
     * Debugging ...
     */
    public void interactiveDayAt() {
        final JXMonthView monthView = new JXMonthView();
        monthView.setTraversable(true);
        monthView.setShowingWeekNumber(true);
        monthView.setShowingLeadingDays(true);
        monthView.setShowingTrailingDays(true);
        monthView.setSelectionMode(SelectionMode.SINGLE_INTERVAL_SELECTION);
        monthView.setPreferredColumnCount(2);
        monthView.setPreferredRowCount(2);
        final BasicMonthViewUI ui = ((BasicMonthViewUI) monthView.getUI());
        monthView.addMouseListener(new MouseAdapter() {

            @Override
            public void mouseReleased(MouseEvent e) {
//                LOG.info("calendar grid" + ui.calendarGrid);
//                LOG.info("dayAt " + e.getPoint() + ": "
//                        + "\n" + monthView.getDayAtLocation(e.getX(), e.getY()));
//                Calendar monthAtLocation = ui.getMonthAtLocation(e.getX(), e.getY());
//                LOG.info("month start " + 
//                        (monthAtLocation != null ? monthAtLocation.getTime() : null));
                
//                Point p = ui.getMonthGridPositionAtLocation(e.getX(), e.getY());
//                LOG.info("month bounds from logical " + 
//                        p + " \n " +
//                        ui.getMonthBounds(p.y, p.x));
//                LOG.info("month bounds at location" + 
//                        ui.getMonthBoundsAtLocation(e.getX(), e.getY()));
                LOG.info("day grid position " + 
                        ui.getDayGridPositionAtLocation(e.getX(), e.getY()) 
                      + "\nday bounds " + 
                        ui.getDayBoundsAtLocation(e.getX(), e.getY()));
            }
            
        });
        Action action = new AbstractActionExt("toggle minimal") {

            public void actionPerformed(ActionEvent e) {
                int minimal = monthView.getSelectionModel().getMinimalDaysInFirstWeek();
                monthView.getSelectionModel().setMinimalDaysInFirstWeek(minimal > 1 ? 1 : 4);
            }
            
        };
        final JXFrame frame = wrapInFrame(monthView, "test mapping: printed on mouse release");
        addAction(frame, action);
        addComponentOrientationToggle(frame);
        frame.pack();
        frame.setVisible(true);
    }

    /**
     * Issue #736-swingx: monthView cannot cope with minimalDaysInFirstWeek.
     * 
     * Here: look at impact of forcing the minimalDays to a value different
     * from the calendar. Days must be displayed in starting from the 
     * first row under the days-of-week.
     * 
     * Not yet completely fixed: for very late firstDayOfWeek, the Jan is incompletely
     * painted for mininalDays > 1. Rare enough to ignore for now?
     */
    public void interactiveMinimalDaysInFirstWeek() {
        final JXMonthView monthView = new JXMonthView();
        monthView.setTraversable(true);
        monthView.setShowingWeekNumber(true);
        monthView.setShowingLeadingDays(true);
        monthView.setShowingTrailingDays(true);
        monthView.setSelectionMode(SelectionMode.SINGLE_INTERVAL_SELECTION);
        Action action = new AbstractActionExt("toggle minimal") {

            public void actionPerformed(ActionEvent e) {
                int minimal = monthView.getSelectionModel().getMinimalDaysInFirstWeek();
                monthView.getSelectionModel().setMinimalDaysInFirstWeek(minimal > 1 ? 1 : 4);
            }
            
        };
        final JXFrame frame = wrapInFrame(monthView, "click unselectable fires ActionEvent");
        addAction(frame, action);
        addComponentOrientationToggle(frame);
        final JComboBox dayOfWeekComboBox = new JComboBox(new String[]{"Sunday", "Monday", "Tuesday",
                "Wednesday", "Thursday", "Friday", "Saturday"});
        dayOfWeekComboBox.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                int selected = dayOfWeekComboBox.getSelectedIndex();
                monthView.setFirstDayOfWeek(selected + Calendar.SUNDAY);
                
            }
            
        });
        dayOfWeekComboBox.setSelectedIndex(monthView.getFirstDayOfWeek() - Calendar.SUNDAY);
        addStatusComponent(frame, dayOfWeekComboBox);
        frame.pack();
        frame.setVisible(true);
    }


}
