/*
 * jNPad v0.3 - jNPad's an Simple Text Editor written in Java
 *
 * Copyright (C) 2014-2017  rgs
 *
 * Require JDK 1.6 (or later)
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 *
 *
 * Info, Questions, Suggestions & Bugs Report to rgsevero@gmail.com
 */

package jnpad.config;

import java.awt.Toolkit;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.KeyStroke;

import jnpad.JNPadBundle;
import jnpad.util.Utilities;

/**
 * The Class Accelerators.
 *
 * @version 0.3
 * @since   jNPad v0.1
 */
public final class Accelerators {
  private static Properties     shortcuts;

  private static final boolean  USE_COMPOSITE_SHORTCUTS = true;
  private static Boolean        useCompositeShortcuts;

  // --- Accelerators ----------------------------------------------------------
  // File menu.
  /** The Constant NEW. */
  public static final KeyStroke NEW;

  /** The Constant OPEN. */
  public static final KeyStroke OPEN;

  /** The Constant CLOSE. */
  public static final KeyStroke CLOSE;

  /** The Constant CLOSE_FILES. */
  public static final KeyStroke CLOSE_FILES;

  /** The Constant CLOSE_ALL. */
  public static final KeyStroke CLOSE_ALL;

  /** The Constant SAVE. */
  public static final KeyStroke SAVE;

  /** The Constant SAVE_ALL. */
  public static final KeyStroke SAVE_ALL;

  /** The Constant SAVE_AS. */
  public static final KeyStroke SAVE_AS;

  /** The Constant FILE_PROPERTIES. */
  public static final KeyStroke FILE_PROPERTIES;
  
  /** The Constant FILE_BROWSER. */
  public static final KeyStroke FILE_BROWSER;

  /** The Constant PRINT_SET. */
  public static final KeyStroke PRINT_SET;

  /** The Constant PRINT. */
  public static final KeyStroke PRINT;

  /** The Constant RELOAD. */
  public static final KeyStroke RELOAD;

  /** The Constant EXECUTE_ACTION. */
  public static final KeyStroke EXECUTE_ACTION;

  /** The Constant REDUCE_AS_TRAY_ICON. */
  public static final KeyStroke REDUCE_AS_TRAY_ICON;

  /** The Constant RESTART. */
  public static final KeyStroke RESTART;

  /** The Constant EXIT. */
  public static final KeyStroke EXIT;

  
  // Edit menu.
  /** The Constant UNDO. */
  public static final KeyStroke UNDO;

  /** The Constant REDO. */
  public static final KeyStroke REDO;

  /** The Constant CUT. */
  public static final KeyStroke CUT;

  /** The Constant CUT_LINE. */
  public static final KeyStroke CUT_LINE;

  /** The Constant COPY. */
  public static final KeyStroke COPY;

  /** The Constant COPY_LINE. */
  public static final KeyStroke COPY_LINE;

  /** The Constant PASTE. */
  public static final KeyStroke PASTE;

  /** The Constant SELECT_ALL. */
  public static final KeyStroke SELECT_ALL;

  /** The Constant SELECT_PARAGRAPH. */
  public static final KeyStroke SELECT_PARAGRAPH;

  /** The Constant SELECTION_BEGIN_PARAGRAPH. */
  public static final KeyStroke SELECTION_BEGIN_PARAGRAPH;

  /** The Constant SELECTION_END_PARAGRAPH. */
  public static final KeyStroke SELECTION_END_PARAGRAPH;

  /** The Constant SELECT_LINE. */
  public static final KeyStroke SELECT_LINE;

  /** The Constant SELECTION_BEGIN_LINE. */
  public static final KeyStroke SELECTION_BEGIN_LINE;

  /** The Constant SELECTION_END_LINE. */
  public static final KeyStroke SELECTION_END_LINE;

  /** The Constant SELECT_WORD. */
  public static final KeyStroke SELECT_WORD;

  /** The Constant DELETE. */
  public static final KeyStroke DELETE;

  /** The Constant DELETE_PARAGRAPH. */
  public static final KeyStroke DELETE_PARAGRAPH;

  /** The Constant DELETION_BEGIN_PARAGRAPH. */
  public static final KeyStroke DELETION_BEGIN_PARAGRAPH;

  /** The Constant DELETION_END_PARAGRAPH. */
  public static final KeyStroke DELETION_END_PARAGRAPH;

  /** The Constant DELETE_LINE. */
  public static final KeyStroke DELETE_LINE;

  /** The Constant DELETION_BEGIN_LINE. */
  public static final KeyStroke DELETION_BEGIN_LINE;

  /** The Constant DELETION_END_LINE. */
  public static final KeyStroke DELETION_END_LINE;

  /** The Constant DELETE_WORD. */
  public static final KeyStroke DELETE_WORD;

  /** The Constant TABS_TO_SPACES. */
  public static final KeyStroke TABS_TO_SPACES;

  /** The Constant SPACES_TO_TABS. */
  public static final KeyStroke SPACES_TO_TABS;

  /** The Constant BEGIN_LINES. */
  public static final KeyStroke BEGIN_LINES;

  /** The Constant END_LINES. */
  public static final KeyStroke END_LINES;

  /** The Constant DUPLICATE_CURRENT_LINE. */
  public static final KeyStroke DUPLICATE_CURRENT_LINE;

  /** The Constant JOIN_LINES. */
  public static final KeyStroke JOIN_LINES;

  /** The Constant SORT_LINES_ASCENDING. */
  public static final KeyStroke SORT_LINES_ASCENDING;

  /** The Constant SORT_LINES_DESCENDING. */
  public static final KeyStroke SORT_LINES_DESCENDING;

  /** The Constant REMOVE_END_SPACES. */
  public static final KeyStroke REMOVE_END_SPACES;

  /** The Constant REMOVE_EMPTY_LINES. */
  public static final KeyStroke REMOVE_EMPTY_LINES;

  /** The Constant COMPLETE_WORD. */
  public static final KeyStroke COMPLETE_WORD;

  /** The Constant COMPLETE_WORD_ALL. */
  public static final KeyStroke COMPLETE_WORD_ALL;

  /** The Constant WORD_COUNT. */
  public static final KeyStroke WORD_COUNT;

  /** The Constant INSERT_FILE_EXTENSION. */
  public static final KeyStroke INSERT_FILE_EXTENSION;

  /** The Constant INSERT_FILE_FULL_NAME. */
  public static final KeyStroke INSERT_FILE_FULL_NAME;

  /** The Constant INSERT_FILE_NAME. */
  public static final KeyStroke INSERT_FILE_NAME;

  /** The Constant INSERT_FILE_FULL_PATH. */
  public static final KeyStroke INSERT_FILE_FULL_PATH;

  /** The Constant INSERT_FILE_PATH. */
  public static final KeyStroke INSERT_FILE_PATH;

  /** The Constant INSERT_DATE. */
  public static final KeyStroke INSERT_DATE;

  
  // Search menu.
  /** The Constant FIND. */
  public static final KeyStroke FIND;

  /** The Constant FIND_NEXT. */
  public static final KeyStroke FIND_NEXT;

  /** The Constant FIND_PREVIOUS. */
  public static final KeyStroke FIND_PREVIOUS;

  /** The Constant REPLACE. */
  public static final KeyStroke REPLACE;

  /** The Constant REPLACE_NEXT. */
  public static final KeyStroke REPLACE_NEXT;

  /** The Constant INCREMENTAL_SEARCH. */
  public static final KeyStroke INCREMENTAL_SEARCH;

  /** The Constant GO_TO_LINE. */
  public static final KeyStroke GO_TO_LINE;

  /** The Constant HIGHLIGHT_ALL_OCCURRENCES. */
  public static final KeyStroke HIGHLIGHT_ALL_OCCURRENCES;

  /** The Constant CLEAR_ALL_OCCURRENCES. */
  public static final KeyStroke CLEAR_ALL_OCCURRENCES;

  /** The Constant NEXT_OCCURRENCE. */
  public static final KeyStroke NEXT_OCCURRENCE;

  /** The Constant PREVIOUS_OCCURRENCE. */
  public static final KeyStroke PREVIOUS_OCCURRENCE;

  /** The Constant NEXT_BOOKMARK. */
  public static final KeyStroke NEXT_BOOKMARK;

  /** The Constant PREVIOUS_BOOKMARK. */
  public static final KeyStroke PREVIOUS_BOOKMARK;

  /** The Constant TOGGLE_BOOKMARK. */
  public static final KeyStroke TOGGLE_BOOKMARK;

  /** The Constant CLEAR_ALL_BOOKMARKS. */
  public static final KeyStroke CLEAR_ALL_BOOKMARKS;

  
  // View menu.
  /** The Constant TOOLBAR_VISIBLE. */
  public static final KeyStroke TOOLBAR_VISIBLE;

  /** The Constant FINDRESULTS_VISIBLE. */
  public static final KeyStroke FINDRESULTS_VISIBLE;

  /** The Constant STATUSBAR_VISIBLE. */
  public static final KeyStroke STATUSBAR_VISIBLE;

  /** The Constant LINE_WRAP. */
  public static final KeyStroke LINE_WRAP;

  /** The Constant LINE_NUMBERS. */
  public static final KeyStroke LINE_NUMBERS;

  /** The Constant ACTIVE_LINE. */
  public static final KeyStroke ACTIVE_LINE;

  /** The Constant RIGHT_MARGIN_LINE. */
  public static final KeyStroke RIGHT_MARGIN_LINE;

  /** The Constant MARK_STRIP. */
  public static final KeyStroke MARK_STRIP;

  /** The Constant MARK_OCCURRENCES. */
  public static final KeyStroke MARK_OCCURRENCES;

  /** The Constant MARK_BRACKETS. */
  public static final KeyStroke MARK_BRACKETS;
  
  /** The Constant FOCUS_ON_MAIN_VIEW. */
  public static final KeyStroke FOCUS_ON_MAIN_VIEW;

  /** The Constant SELECT_SCOPE_AS_VIEWER. */
  public static final KeyStroke SELECT_SCOPE_AS_VIEWER;
  
  /** The Constant SELECT_SCOPE_AS_VIEW_SET. */
  public static final KeyStroke SELECT_SCOPE_AS_VIEW_SET;
  
  /** The Constant SELECT_SCOPE_AS_VIEW. */
  public static final KeyStroke SELECT_SCOPE_AS_VIEW;
  
  /** The Constant SELECT_SCOPE_AS_PANE. */
  public static final KeyStroke SELECT_SCOPE_AS_PANE;

  /** The Constant FULLSCREEN. */
  public static final KeyStroke FULLSCREEN;

  
  // Format menu.
  /** The Constant RIGHT_INDENT. */
  public static final KeyStroke RIGHT_INDENT;

  /** The Constant LEFT_INDENT. */
  public static final KeyStroke LEFT_INDENT;

  /** The Constant TO_UPPER. */
  public static final KeyStroke TO_UPPER;

  /** The Constant TO_LOWER. */
  public static final KeyStroke TO_LOWER;

  /** The Constant CAPITALIZE. */
  public static final KeyStroke CAPITALIZE;

  /** The Constant INVERT_UPPER_LOWER. */
  public static final KeyStroke INVERT_UPPER_LOWER;

  /** The Constant TO_TITLE. */
  public static final KeyStroke TO_TITLE;

  /** The Constant SET_READ_ONLY. */
  public static final KeyStroke SET_READ_ONLY;

  /** The Constant ALIGN_STRINGS_BY_COMMA. */
  public static final KeyStroke ALIGN_STRINGS_BY_COMMA;

  /** The Constant ALIGN_STRINGS_BY_EQUAL. */
  public static final KeyStroke ALIGN_STRINGS_BY_EQUAL;

  /** The Constant ALIGN_STRINGS_BY_CLIPBOARD. */
  public static final KeyStroke ALIGN_STRINGS_BY_CLIPBOARD;

  /** The Constant INCREASE_FONT. */
  public static final KeyStroke INCREASE_FONT;

  /** The Constant DECREASE_FONT. */
  public static final KeyStroke DECREASE_FONT;

  
  // Configure menu.
  /** The Constant FONTS. */
  public static final KeyStroke FONTS;

  
  // Window menu.
  /** The Constant WINDOW. */
  public static final KeyStroke WINDOW;

  /** The Constant NEXT_BUFFER. */
  public static final KeyStroke NEXT_BUFFER;

  /** The Constant PREVIOUS_BUFFER. */
  public static final KeyStroke PREVIOUS_BUFFER;

  /** The Constant SPLIT_VERTICALLY. */
  public static final KeyStroke SPLIT_VERTICALLY;

  /** The Constant SPLIT_HORIZONTALLY. */
  public static final KeyStroke SPLIT_HORIZONTALLY;

  /** The Constant UNSPLIT. */
  public static final KeyStroke UNSPLIT;

  /** The Constant UNSPLIT_CURRENT. */
  public static final KeyStroke UNSPLIT_CURRENT;

  /** The Constant RESTORE_SPLIT. */
  public static final KeyStroke RESTORE_SPLIT;

  /** The Constant NEXT_EDIT_PANE. */
  public static final KeyStroke NEXT_EDIT_PANE;

  /** The Constant PREVIOUS_EDIT_PANE. */
  public static final KeyStroke PREVIOUS_EDIT_PANE;

  /** The Constant SPLIT_VIEWER_VERTICALLY. */
  public static final KeyStroke SPLIT_VIEWER_VERTICALLY;
  
  /** The Constant SPLIT_VIEWER_HORIZONTALLY. */
  public static final KeyStroke SPLIT_VIEWER_HORIZONTALLY;
  
  /** The Constant UNSPLIT_VIEWER. */
  public static final KeyStroke UNSPLIT_VIEWER;
  
  /** The Constant UNSPLIT_VIEWER_CURRENT. */
  public static final KeyStroke UNSPLIT_VIEWER_CURRENT;
  
  /** The Constant RESTORE_VIEWER_SPLIT. */
  public static final KeyStroke RESTORE_VIEWER_SPLIT;

  /** The Constant NEXT_BUFFER_SET. */
  public static final KeyStroke NEXT_BUFFER_SET;
  
  /** The Constant PREVIOUS_BUFFER_SET. */
  public static final KeyStroke PREVIOUS_BUFFER_SET;
  
  
  // Help menu.
  /** The Constant ACLTR_ABOUT. */
  public static final KeyStroke ABOUT;

  
  // Others
  /** The Constant NEXT_COLORSCHEME. */
  public static final KeyStroke NEXT_COLORSCHEME;

  /** The Constant PREVIOUS_COLORSCHEME. */
  public static final KeyStroke PREVIOUS_COLORSCHEME;

  /** The Constant SET_EXTENDED_STATE_MAXIMIZED. */
  public static final KeyStroke SET_EXTENDED_STATE_MAXIMIZED;

  /** The Constant SET_EXTENDED_STATE_ICONIFIED. */
  public static final KeyStroke SET_EXTENDED_STATE_ICONIFIED;

  /** The Constant SHOW_POPUP_MENU. */
  public static final KeyStroke SHOW_POPUP_MENU;

  /** The Constant SHOW_POPUP_MENU2. */
  public static final KeyStroke SHOW_POPUP_MENU2;

  /** The Constant SET_POSITION_SOUTH_WEST. */
  public static final KeyStroke SET_POSITION_SOUTH_WEST;

  /** The Constant SET_SET_POSITION_SOUTH. */
  public static final KeyStroke SET_POSITION_SOUTH;

  /** The Constant SET_POSITION_SOUTH_EAST. */
  public static final KeyStroke SET_POSITION_SOUTH_EAST;

  /** The Constant SET_POSITION_WEST. */
  public static final KeyStroke SET_POSITION_WEST;

  /** The Constant SET_POSITION_CENTER. */
  public static final KeyStroke SET_POSITION_CENTER;

  /** The Constant SET_POSITION_EAST. */
  public static final KeyStroke SET_POSITION_EAST;

  /** The Constant SET_POSITION_NORTH_WEST. */
  public static final KeyStroke SET_POSITION_NORTH_WEST;

  /** The Constant SET_POSITION_NORTH. */
  public static final KeyStroke SET_POSITION_NORTH;

  /** The Constant SET_POSITION_NORTH_EAST. */
  public static final KeyStroke SET_POSITION_NORTH_EAST;
  // ---------------------------------------------------------------------------

  /** Logger */
  private static final Logger   LOGGER                  = Logger.getLogger(Accelerators.class.getName());

  static {
    shortcuts = new Properties();

    if (Config.isUsingCustomShortcuts()) {
      String path = Config.KEYMAP_DIR + Utilities.DIR_SEPARATOR + Config.JNPAD_KEYMAP.getValue() + ".properties"; //$NON-NLS-1$
      if (!new File(path).exists()) {
        LOGGER.config(path + " does not exist."); //$NON-NLS-1$
        path = Config.KEYMAP_DIR + Utilities.DIR_SEPARATOR + Config.JNPAD_KEYMAP.getDefault() + ".properties"; //$NON-NLS-1$
      }
      loadShortcuts(path);
    }

    final int A   = InputEvent.ALT_DOWN_MASK;
    final int C   = InputEvent.CTRL_DOWN_MASK;
    final int S   = InputEvent.SHIFT_DOWN_MASK;
    final int CA  = InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK;
    final int CS  = InputEvent.CTRL_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK;
    final int CAS = InputEvent.CTRL_DOWN_MASK | InputEvent.ALT_DOWN_MASK | InputEvent.SHIFT_DOWN_MASK;
    final int AS  = InputEvent.ALT_DOWN_MASK  | InputEvent.SHIFT_DOWN_MASK;

	NEW                         = KeyStroke.getKeyStroke('N', C);
	OPEN                        = KeyStroke.getKeyStroke('O', C);
	CLOSE                       = KeyStroke.getKeyStroke(KeyEvent.VK_F4, C);
	CLOSE_FILES                 = KeyStroke.getKeyStroke(KeyEvent.VK_F4, CS);
	CLOSE_ALL                   = KeyStroke.getKeyStroke(KeyEvent.VK_F4, S);
	SAVE                        = KeyStroke.getKeyStroke('S', C);
	SAVE_ALL                    = KeyStroke.getKeyStroke('S', CS);
	SAVE_AS                     = KeyStroke.getKeyStroke('S', CA);
	FILE_PROPERTIES             = KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, C);
	FILE_BROWSER                = KeyStroke.getKeyStroke('V', CS);
	PRINT_SET                   = KeyStroke.getKeyStroke('P', CS);
	PRINT                       = KeyStroke.getKeyStroke('P', C);
	RELOAD                      = KeyStroke.getKeyStroke(KeyEvent.VK_F5, A);
	EXECUTE_ACTION              = KeyStroke.getKeyStroke(KeyEvent.VK_PERIOD, CS);
	REDUCE_AS_TRAY_ICON         = KeyStroke.getKeyStroke(KeyEvent.VK_F6, A);
    RESTART                     = KeyStroke.getKeyStroke(KeyEvent.VK_F3, A);
	EXIT                        = KeyStroke.getKeyStroke(KeyEvent.VK_F4, A);

	UNDO                        = KeyStroke.getKeyStroke('Z', C);
	REDO                        = KeyStroke.getKeyStroke('Z', CS);
	CUT                         = KeyStroke.getKeyStroke('X', C);
	CUT_LINE                    = KeyStroke.getKeyStroke('X', CS);
	COPY                        = KeyStroke.getKeyStroke('C', C);
	COPY_LINE                   = KeyStroke.getKeyStroke('C', CS);
	PASTE                       = KeyStroke.getKeyStroke('V', C);
	SELECT_ALL                  = KeyStroke.getKeyStroke('A', C);
	SELECT_PARAGRAPH            = KeyStroke.getKeyStroke('R', C);
	SELECTION_BEGIN_PARAGRAPH   = KeyStroke.getKeyStroke('R', CS);
	SELECTION_END_PARAGRAPH     = KeyStroke.getKeyStroke('R', CA);
	SELECT_LINE                 = KeyStroke.getKeyStroke('L', C);
	SELECTION_BEGIN_LINE        = KeyStroke.getKeyStroke('L', CS);
	SELECTION_END_LINE          = KeyStroke.getKeyStroke('L', CA);
	SELECT_WORD                 = KeyStroke.getKeyStroke('W', C);
	DELETE                      = KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0);
	DELETE_PARAGRAPH            = KeyStroke.getKeyStroke('K', C);
	DELETION_BEGIN_PARAGRAPH    = KeyStroke.getKeyStroke('K', CS);
	DELETION_END_PARAGRAPH      = KeyStroke.getKeyStroke('K', CA);
	DELETE_LINE                 = KeyStroke.getKeyStroke('D', C);
	DELETION_BEGIN_LINE         = KeyStroke.getKeyStroke('D', CS);
	DELETION_END_LINE           = KeyStroke.getKeyStroke('D', CA);
	DELETE_WORD                 = KeyStroke.getKeyStroke('W', CS);
	TABS_TO_SPACES              = KeyStroke.getKeyStroke('S', CAS);
	SPACES_TO_TABS              = KeyStroke.getKeyStroke('T', CAS);
	BEGIN_LINES                 = KeyStroke.getKeyStroke('B', C);
	END_LINES                   = KeyStroke.getKeyStroke('B', CS);
	DUPLICATE_CURRENT_LINE      = KeyStroke.getKeyStroke('T', C);
	JOIN_LINES                  = KeyStroke.getKeyStroke('I', C);
	SORT_LINES_ASCENDING        = KeyStroke.getKeyStroke('Q', C);
	SORT_LINES_DESCENDING       = KeyStroke.getKeyStroke('Q', CS);
	REMOVE_END_SPACES           = KeyStroke.getKeyStroke('Y', C);
	REMOVE_EMPTY_LINES          = KeyStroke.getKeyStroke('Y', CS);
	COMPLETE_WORD               = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, C);
	COMPLETE_WORD_ALL           = KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, CS);
	WORD_COUNT                  = KeyStroke.getKeyStroke('G', CS);
	INSERT_FILE_EXTENSION       = KeyStroke.getKeyStroke('M', C);
	INSERT_FILE_FULL_NAME       = KeyStroke.getKeyStroke('M', CS);
	INSERT_FILE_NAME            = KeyStroke.getKeyStroke('M', CA);
	INSERT_FILE_FULL_PATH       = KeyStroke.getKeyStroke('J', C);
	INSERT_FILE_PATH            = KeyStroke.getKeyStroke('J', CS);
	INSERT_DATE                 = KeyStroke.getKeyStroke(KeyEvent.VK_F5, 0);

	FIND                        = KeyStroke.getKeyStroke('F', C);
	FIND_NEXT                   = KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0);
	FIND_PREVIOUS               = KeyStroke.getKeyStroke(KeyEvent.VK_F3, S);
	REPLACE                     = KeyStroke.getKeyStroke('E', C);
	REPLACE_NEXT                = KeyStroke.getKeyStroke(KeyEvent.VK_F4, 0);
	INCREMENTAL_SEARCH          = KeyStroke.getKeyStroke('I', CA);
	GO_TO_LINE                  = KeyStroke.getKeyStroke('G', C);
	HIGHLIGHT_ALL_OCCURRENCES   = KeyStroke.getKeyStroke('N', CS);
	CLEAR_ALL_OCCURRENCES       = KeyStroke.getKeyStroke('E', CS);
	NEXT_OCCURRENCE             = KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, CS);
	PREVIOUS_OCCURRENCE         = KeyStroke.getKeyStroke(KeyEvent.VK_UP, CS);
	NEXT_BOOKMARK               = KeyStroke.getKeyStroke(KeyEvent.VK_F2, 0);
	PREVIOUS_BOOKMARK           = KeyStroke.getKeyStroke(KeyEvent.VK_F2, S);
	TOGGLE_BOOKMARK             = KeyStroke.getKeyStroke(KeyEvent.VK_F2, C);
	CLEAR_ALL_BOOKMARKS         = KeyStroke.getKeyStroke(KeyEvent.VK_F2, CS);

	TOOLBAR_VISIBLE             = KeyStroke.getKeyStroke('T', CA);
	FINDRESULTS_VISIBLE         = KeyStroke.getKeyStroke('F', CA);
	STATUSBAR_VISIBLE           = KeyStroke.getKeyStroke('S', CA);
	LINE_WRAP                   = KeyStroke.getKeyStroke(KeyEvent.VK_F5, C);
	LINE_NUMBERS                = KeyStroke.getKeyStroke(KeyEvent.VK_F6, C);
	ACTIVE_LINE                 = KeyStroke.getKeyStroke(KeyEvent.VK_F7, C);
	RIGHT_MARGIN_LINE           = KeyStroke.getKeyStroke(KeyEvent.VK_F8, C);
	MARK_STRIP                  = KeyStroke.getKeyStroke(KeyEvent.VK_F9, C);
	MARK_OCCURRENCES            = KeyStroke.getKeyStroke(KeyEvent.VK_F10, C);
    MARK_BRACKETS               = KeyStroke.getKeyStroke(KeyEvent.VK_F11, C);
    FOCUS_ON_MAIN_VIEW          = KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, AS);
	SELECT_SCOPE_AS_VIEWER      = KeyStroke.getKeyStroke(KeyEvent.VK_0, CS);
	SELECT_SCOPE_AS_VIEW_SET    = KeyStroke.getKeyStroke(KeyEvent.VK_1, CS);
	SELECT_SCOPE_AS_VIEW        = KeyStroke.getKeyStroke(KeyEvent.VK_2, CS);
	SELECT_SCOPE_AS_PANE        = KeyStroke.getKeyStroke(KeyEvent.VK_3, CS);
	FULLSCREEN                  = KeyStroke.getKeyStroke(KeyEvent.VK_F11, 0);

	RIGHT_INDENT                = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0);
	LEFT_INDENT                 = KeyStroke.getKeyStroke(KeyEvent.VK_TAB, S);
	TO_UPPER                    = KeyStroke.getKeyStroke('U', CS);
	TO_LOWER                    = KeyStroke.getKeyStroke('U', C);
	CAPITALIZE                  = KeyStroke.getKeyStroke('A', CS);
	INVERT_UPPER_LOWER          = KeyStroke.getKeyStroke('I', CS);
	TO_TITLE                    = KeyStroke.getKeyStroke('T', CS);
	SET_READ_ONLY               = KeyStroke.getKeyStroke('R', CAS);
	ALIGN_STRINGS_BY_COMMA      = KeyStroke.getKeyStroke(KeyEvent.VK_F9, CS);
	ALIGN_STRINGS_BY_EQUAL      = KeyStroke.getKeyStroke(KeyEvent.VK_F10, CS);
	ALIGN_STRINGS_BY_CLIPBOARD  = KeyStroke.getKeyStroke(KeyEvent.VK_F11, CS);
	INCREASE_FONT               = KeyStroke.getKeyStroke(KeyEvent.VK_ADD, C);
	DECREASE_FONT               = KeyStroke.getKeyStroke(KeyEvent.VK_SUBTRACT, C);

	FONTS                       = KeyStroke.getKeyStroke('F', CS);

	WINDOW                      = KeyStroke.getKeyStroke('W', AS);
	NEXT_BUFFER                 = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, A);
	PREVIOUS_BUFFER             = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, A);
	SPLIT_VERTICALLY            = KeyStroke.getKeyStroke('V', AS);
	SPLIT_HORIZONTALLY          = KeyStroke.getKeyStroke('H', AS);
	UNSPLIT                     = KeyStroke.getKeyStroke('C', AS);
	UNSPLIT_CURRENT             = KeyStroke.getKeyStroke('U', AS);
	RESTORE_SPLIT               = KeyStroke.getKeyStroke('R', AS);
	NEXT_EDIT_PANE              = KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, AS);
	PREVIOUS_EDIT_PANE          = KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, AS);
	SPLIT_VIEWER_VERTICALLY     = KeyStroke.getKeyStroke(KeyEvent.VK_3, C);
	SPLIT_VIEWER_HORIZONTALLY   = KeyStroke.getKeyStroke(KeyEvent.VK_2, C);
	UNSPLIT_VIEWER              = KeyStroke.getKeyStroke(KeyEvent.VK_1, C);
	UNSPLIT_VIEWER_CURRENT      = KeyStroke.getKeyStroke(KeyEvent.VK_0, C);
	RESTORE_VIEWER_SPLIT        = KeyStroke.getKeyStroke(KeyEvent.VK_4, C);
	NEXT_BUFFER_SET             = KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, A);
	PREVIOUS_BUFFER_SET         = KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, A);
	
	ABOUT                       = KeyStroke.getKeyStroke(KeyEvent.VK_F1, S);

	NEXT_COLORSCHEME            = KeyStroke.getKeyStroke(KeyEvent.VK_F6, S);
	PREVIOUS_COLORSCHEME        = KeyStroke.getKeyStroke(KeyEvent.VK_F5, S);
	SET_EXTENDED_STATE_MAXIMIZED= KeyStroke.getKeyStroke(KeyEvent.VK_F12, S);
	SET_EXTENDED_STATE_ICONIFIED= KeyStroke.getKeyStroke(KeyEvent.VK_F12, C);
	SHOW_POPUP_MENU             = KeyStroke.getKeyStroke(KeyEvent.VK_F10, S);
    SHOW_POPUP_MENU2            = KeyStroke.getKeyStroke(KeyEvent.VK_CONTEXT_MENU, 0);
	SET_POSITION_SOUTH_WEST     = KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD1, C);
	SET_POSITION_SOUTH          = KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD2, C);
	SET_POSITION_SOUTH_EAST     = KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD3, C);
	SET_POSITION_WEST           = KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD4, C);
	SET_POSITION_CENTER         = KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD5, C);
	SET_POSITION_EAST           = KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD6, C);
	SET_POSITION_NORTH_WEST     = KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD7, C);
	SET_POSITION_NORTH          = KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD8, C);
	SET_POSITION_NORTH_EAST     = KeyStroke.getKeyStroke(KeyEvent.VK_NUMPAD9, C);
  }

  /** no instances */
  private Accelerators() {
    super();
  }

  /**
   * Load shortcuts.
   *
   * @param filePath the file path
   */
  private static void loadShortcuts(String filePath) {
    BufferedReader in = null;
    try {
      in = new BufferedReader(new InputStreamReader(new FileInputStream(filePath), "UTF-8")); //$NON-NLS-1$
      shortcuts.load(in);
    }
    catch (IOException ex) {
      // no pasa nada
    }
    finally {
      try {
        if (in != null) {
          in.close();
        }
      }
      catch (IOException ex) {
        LOGGER.log(Level.WARNING, ex.getMessage(), ex);
      }
    }
  }

  /**
   * Gets the prop key binding.
   *
   * @param key the key
   * @return the prop key binding
   */
  public static String getPropKeyBinding(String key) {
    if (!Config.isUsingCustomShortcuts() || !isUsingCompositeShortcuts()) {
      return null;
    }
    return shortcuts.getProperty(key);
  }

  /**
   * Converts a string to a keystroke. The string should be of the
   * form <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i>
   * is any combination of A for Alt, C for Control, S for Shift
   * or M for Meta, and <i>shortcut</i> is either a single character,
   * or a keycode name from the <code>KeyEvent</code> class, without
   * the <code>VK_</code> prefix.
   *
   * @param key A string description of the key stroke
   * @param def KeyStroke
   * @return KeyStroke
   */
  public static KeyStroke getPropAccelerator(String key, KeyStroke def) {
    if (!Config.isUsingCustomShortcuts() || isUsingCompositeShortcuts()) {
      return def;
    }
    String propval = shortcuts.getProperty(key);
    if (propval != null) {
      try {
        KeyStroke keyStroke = parseKeyStroke(propval);
        if (keyStroke != null)
          return keyStroke;
      }
      catch (Exception ex) {
        LOGGER.log(Level.WARNING, ex.getMessage(), ex);
      }
    }
    return def;
  }

  /**
   * Converts a string to a keystroke. The string should be of the form
   * <i>modifiers</i>+<i>shortcut</i> where <i>modifiers</i> is any combination
   * of A for Alt, C for Control, S for Shift or M for Meta, and <i>shortcut</i>
   * is either a single character, or a keycode name from the
   * <code>KeyEvent</code> class, without the <code>VK_</code> prefix.
   * 
   * @param keyStroke A string description of the key stroke
   * @return KeyStroke
   */
  public static KeyStroke parseKeyStroke(String keyStroke) {
    if (keyStroke == null) {
      return null;
    }

    int modifiers = 0;
    int index = keyStroke.indexOf('+');

    if (index != -1) {
      for (int i = 0; i < index; i++) {
        switch (Character.toUpperCase(keyStroke.charAt(i))) {
          case 'A':
            modifiers |= InputEvent.ALT_MASK;
            break;
          case 'C':
            modifiers |= Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); //InputEvent.CTRL_MASK;
            break;
          case 'M':
            modifiers |= InputEvent.META_MASK;
            break;
          case 'S':
            modifiers |= InputEvent.SHIFT_MASK;
            break;
          default: // Keep FindBugs happy
            break;
        }
      }
    }

    String key = keyStroke.substring(index + 1);

    if (key.length() == 1) {
      char ch = Character.toUpperCase(key.charAt(0));
      if (modifiers == 0) {
        return KeyStroke.getKeyStroke(ch);
      }
      return KeyStroke.getKeyStroke(ch, modifiers);
    }
    else if (key.length() == 0) {
      LOGGER.log(Level.WARNING, "Invalid key stroke: " + keyStroke); //$NON-NLS-1$
      return null;
    }
    else {
      int ch;
      try {
        ch = KeyEvent.class.getField("VK_".concat(key)).getInt(null); //$NON-NLS-1$
      }
      catch (Exception ex) {
        LOGGER.log(Level.WARNING, "Invalid key stroke: " + keyStroke, ex); //$NON-NLS-1$
        return null;
      }

      return KeyStroke.getKeyStroke(ch, modifiers);
    }
  }

  /**
   * Gets the prop key strokes.
   *
   * @param key the key
   * @param def the def
   * @return the prop key strokes
   */
  public static KeyStroke[] getPropKeyStrokes(String key, KeyStroke def) {
    label0: if (Config.isUsingCustomShortcuts()) {
      String propval = shortcuts.getProperty(key);
      if (propval != null) {
        StringTokenizer st = new StringTokenizer(propval);
        KeyStroke[] result = new KeyStroke[st.countTokens()];
        for (int i = 0; st.hasMoreTokens(); i++) {
          KeyStroke keyStroke = parseKeyStroke(st.nextToken());
          if (keyStroke == null) {
            break label0;
          }
          result[i] = keyStroke;
        }
        return result;
      }
    }
    return new KeyStroke[] { def };
  }
  
  /**
   * Checks if is using composite shortcuts.
   *
   * @return true, if is using composite shortcuts
   */
  public static boolean isUsingCompositeShortcuts() {
    if(!Config.isUsingCustomShortcuts())
      return false;

    if (useCompositeShortcuts == null) {
      String propval = shortcuts.getProperty("shortcuts.composite"); //$NON-NLS-1$
      if (propval != null) {
        useCompositeShortcuts = BooleanProp.parse(propval);
      }
      else {
        useCompositeShortcuts = USE_COMPOSITE_SHORTCUTS;
      }
    }
    return useCompositeShortcuts;
  }

  /**
   * Gets the shortcut label.
   *
   * @param action the action
   * @return the shortcut label
   */
  public static String getShortcutLabel(String action) {
    if (action == null)
      return null;
    
    String shortcut1 = getPropKeyBinding(action + ".shortcut"); //$NON-NLS-1$
    String shortcut2 = getPropKeyBinding(action + ".shortcut2"); //$NON-NLS-1$

    if (Utilities.isEmptyString(shortcut1)) {
      if (Utilities.isEmptyString(shortcut2))
        return null;
      return shortcut2;
    }
    if (Utilities.isEmptyString(shortcut2))
      return shortcut1;
    return JNPadBundle.getString("Accelerators.getShortcutLabel", shortcut1, shortcut2); //$NON-NLS-1$
  }

  /**
   * The main method.
   *
   * @param args the arguments
   * @throws Exception the exception
   */
  public static void main(String[] args) throws Exception {
    checkUniquenessOfKeyStrokes();
  }

  /**
   * Check uniqueness of key strokes.
   *
   * @throws Exception the exception
   */
  public static void checkUniquenessOfKeyStrokes() throws Exception {
    Class<?> clazz = Class.forName("jnpad.config.Accelerators"); //$NON-NLS-1$
    HashSet<String> kss = new HashSet<String>();
    int collisions = 0;

    for (Field fi : clazz.getDeclaredFields()) {
      Object vi = fi.get(null);

      if (vi instanceof KeyStroke) {
        KeyStroke ki = (KeyStroke) vi;

        String sig = String.valueOf(ki.getKeyCode());

        if ( (ki.getModifiers() & InputEvent.SHIFT_DOWN_MASK) == InputEvent.SHIFT_DOWN_MASK) {
          sig += " Shift"; //$NON-NLS-1$
        }
        if ( (ki.getModifiers() & InputEvent.CTRL_DOWN_MASK) == InputEvent.CTRL_DOWN_MASK) {
          sig += " Ctrl"; //$NON-NLS-1$
        }
        if ( (ki.getModifiers() & InputEvent.ALT_DOWN_MASK) == InputEvent.ALT_DOWN_MASK) {
          sig += " Alt"; //$NON-NLS-1$
        }
        if ( (ki.getModifiers() & InputEvent.ALT_GRAPH_DOWN_MASK) == InputEvent.ALT_GRAPH_DOWN_MASK) {
          sig += " AltGr"; //$NON-NLS-1$

        }
        if (kss.contains(sig)) {
          System.err.println("KeyStroke collision " + fi.getName() + " value  <" + ki + "> is already used ! (" + sig + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
          collisions++;
        }
        else {
          kss.add(sig);
        }
      }
    }

    if (collisions == 0) {
      System.out.println("Check done. no accelerators collisions"); //$NON-NLS-1$
    }
    else {
      System.out.println(collisions + " accelerators collisions detected"); //$NON-NLS-1$
    }

  }

}
