content: /**
   * Copyright 2020 Alexander Herzog
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package ui.parameterseries;
  
  import java.awt.BorderLayout;
  import java.awt.Component;
  import java.awt.event.ActionEvent;
  import java.awt.event.ActionListener;
  import java.awt.event.InputEvent;
  import java.awt.event.KeyAdapter;
  import java.awt.event.KeyEvent;
  import java.awt.event.MouseAdapter;
  import java.awt.event.MouseEvent;
  import java.io.Serializable;
  import java.util.function.IntConsumer;
  
  import javax.swing.DefaultListCellRenderer;
  import javax.swing.DefaultListModel;
  import javax.swing.Icon;
  import javax.swing.JButton;
  import javax.swing.JLabel;
  import javax.swing.JList;
  import javax.swing.JMenuItem;
  import javax.swing.JPanel;
  import javax.swing.JPopupMenu;
  import javax.swing.JScrollPane;
  import javax.swing.JToolBar;
  import javax.swing.KeyStroke;
  import javax.swing.SwingConstants;
  import javax.swing.SwingUtilities;
  
  import language.Language;
  import simulator.editmodel.EditModel;
  import systemtools.BaseDialog;
  import ui.images.Images;
  
  /**
   * Diese Klasse stellt einige Basisfunktionen zur Darstellung von Dialogen
   * zur Auflistung von Eingabeparametern und von Ausgabegren bereit.
   * @author Alexander Herzog
   * @see ParameterCompareSetupValueInputListDialog
   * @see ParameterCompareSetupValueOutputListDialog
   */
  public abstract class ParameterCompareSetupValueBaseListDialog extends BaseDialog {
  	/**
  	 * Serialisierungs-ID der Klasse
  	 * @see Serializable
  	 */
  	private static final long serialVersionUID = -8145433432850373442L;
  
  	/**
  	 * Editor-Modell, welches die Basis fr die Parameterstudie darstellt
  	 */
  	protected final EditModel model;
  
  	/**
  	 * Runnable, das aufgerufen werden kann, wenn die Hilfe zu der Programmfunktion angezeigt werden soll.
  	 */
  	protected final Runnable help;
  
  	/**
  	 * Toolbar des Dialogs
  	 */
  	protected final JToolBar toolbar;
  
  	/** Schaltflche "Hinzufgen" */
  	private final JButton buttonAdd;
  	/** Schaltflche "Bearbeiten" */
  	private final JButton buttonEdit;
  	/** Schaltflche "Lschen" */
  	private final JButton buttonDelete;
  	/** Schaltflche "Nach oben verschieben" */
  	private final JButton buttonMoveUp;
  	/** Schaltflche "Nach unten verschieben" */
  	private final JButton buttonMoveDown;
  	/** Listendarstellung der Eintrge */
  	private final JList<JLabel> list;
  
  	/**
  	 * Konstruktor der Klasse.<br>
  	 * @param owner	bergeordnetes Element
  	 * @param title	Fenstertitel
  	 * @param model	Editor-Modell, welches die Basis fr die Parameterstudie darstellt
  	 * @param help	Hilfe-Runnable
  	 * @see ParameterCompareSetupValueBaseListDialog#initToolbar(String, String, String, String, String, String, String, String, String, String)
  	 * @see ParameterCompareSetupValueBaseListDialog#start()
  	 */
  	public ParameterCompareSetupValueBaseListDialog(final Component owner, final String title, final EditModel model, final Runnable help) {
  		super(owner,title);
  
  		this.model=model;
  		this.help=help;
  
  		addUserButtons();
  		final JPanel content=createGUI(help);
  		content.setLayout(new BorderLayout());
  
  		content.add(new JScrollPane(list=new JList<>(new DefaultListModel<>())),BorderLayout.CENTER);
  
  		toolbar=new JToolBar(SwingConstants.HORIZONTAL);
  		toolbar.setFloatable(false);
  		content.add(toolbar,BorderLayout.NORTH);
  		toolbar.add(buttonAdd=getButton(Images.EDIT_ADD.getIcon(),modifiers->commandAddPopup()));
  		toolbar.add(buttonEdit=getButton(Images.GENERAL_SETUP.getIcon(),modifiers->{if (list.getSelectedIndex()>=0) commandEdit(list.getSelectedIndex());}));
  		toolbar.add(buttonDelete=getButton(Images.EDIT_DELETE.getIcon(),modifiers->{if (list.getSelectedIndex()>=0) commandDelete(list.getSelectedIndex(),(modifiers & ActionEvent.SHIFT_MASK)!=0);}));
  		toolbar.addSeparator();
  		toolbar.add(buttonMoveUp=getButton(Images.ARROW_UP.getIcon(),modifiers->commandMoveUp()));
  		toolbar.add(buttonMoveDown=getButton(Images.ARROW_DOWN.getIcon(),modifiers->commandMoveDown()));
  
  		list.setCellRenderer(new DefaultListCellRenderer() {
  			/**
  			 * Serialisierungs-ID der Klasse
  			 * @see Serializable
  			 */
  			private static final long serialVersionUID = -7787863612588403516L;
  			@Override
  			public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
  				JLabel l=(JLabel)super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
  				l.setText(((JLabel)value).getText());
  				l.setIcon(((JLabel)value).getIcon());
  				return l;
  			}
  		});
  		list.addMouseListener(new MouseAdapter() {
  			@Override
  			public void mousePressed(MouseEvent e) {
  				if (e.getClickCount()==2 && SwingUtilities.isLeftMouseButton(e)) {
  					if (list.getSelectedIndex()>=0) commandEdit(list.getSelectedIndex());
  					e.consume();
  					return;
  				}
  				if (SwingUtilities.isRightMouseButton(e)) {
  					showContextMenu(e);
  					e.consume();
  					return;
  				}
  			}
  		});
  		list.addKeyListener(new KeyAdapter() {
  			@Override
  			public void keyPressed(KeyEvent e) {
  				if (e.getKeyCode()==KeyEvent.VK_INSERT && !e.isControlDown() && !e.isShiftDown()) {commandAdd(0); e.consume(); return;}
  				if (e.getKeyCode()==KeyEvent.VK_ENTER && !e.isControlDown() && !e.isShiftDown()) {if (list.getSelectedIndex()<0) return; commandEdit(list.getSelectedIndex()); e.consume(); return;}
  				if (e.getKeyCode()==KeyEvent.VK_DELETE && !e.isControlDown()) {if (list.getSelectedIndex()<0) return; commandDelete(list.getSelectedIndex(),e.isShiftDown()); e.consume(); return;}
  				if (e.getKeyCode()==KeyEvent.VK_UP && e.isControlDown() && !e.isShiftDown()) {commandMoveUp(); e.consume(); return;}
  				if (e.getKeyCode()==KeyEvent.VK_DOWN && e.isControlDown() && !e.isShiftDown()) {commandMoveDown(); e.consume(); return;}
  			}
  		});
  		list.addListSelectionListener(e->updateToolbar());
  	}
  
  	/**
  	 * In dieser Methode kann {@link BaseDialog#addUserButton} aufgerufen werden.
  	 */
  	protected void addUserButtons() {}
  
  	/**
  	 * Initialisiert den Toolbar.<br>
  	 * Sollte im Konstruktor aufgerufen werden.
  	 * @param add	Titel der "Hinzufgen"-Schaltflche
  	 * @param addHint	Tooltip der "Hinzufgen"-Schaltflche
  	 * @param edit	Titel der "Bearbeiten"-Schaltflche
  	 * @param editHint	Tooltip der "Bearbeiten"-Schaltflche
  	 * @param delete	Titel der "Entfernen"-Schaltflche
  	 * @param deleteHint	Tooltip der "Entfernen"-Schaltflche
  	 * @param moveUp	Titel der "Nach oben"-Schaltflche
  	 * @param moveUpHint	Tooltip der "Nach oben"-Schaltflche
  	 * @param moveDown	Titel der "Nach unten"-Schaltflche
  	 * @param moveDownHint	Tooltip der "Nach unten"-Schaltflche
  	 */
  	protected final void initToolbar(final String add, final String addHint, final String edit, final String editHint, final String delete, final String deleteHint, final String moveUp, final String moveUpHint, final String moveDown, final String moveDownHint) {
  		buttonAdd.setText(add);
  		buttonAdd.setToolTipText(addHint);
  		buttonEdit.setText(edit);
  		buttonEdit.setToolTipText(editHint);
  		buttonDelete.setText(delete);
  		buttonDelete.setToolTipText(deleteHint);
  		buttonMoveUp.setText(moveUp);
  		buttonMoveUp.setToolTipText(moveUpHint);
  		buttonMoveDown.setText(moveDown);
  		buttonMoveDown.setToolTipText(moveDownHint);
  	}
  
  	/**
  	 * Richtet die Dialoggre ein und macht den Dialog sichtbar.<br>
  	 * Sollte im Konstruktor aufgerufen werden.
  	 */
  	protected final void start() {
  		updateList(0);
  
  		setMinSizeRespectingScreensize(650,500);
  		setSizeRespectingScreensize(650,500);
  		setResizable(true);
  		setLocationRelativeTo(owner);
  		setVisible(true);
  	}
  
  	/**
  	 * Erstellt eine neue Schaltflche
  	 * @param icon	Icon der Schaltflche
  	 * @param command	Auszufhrender Befehl, wenn die Schaltflche angeklickt wird
  	 * @return	Neue Schaltflche
  	 */
  	private JButton getButton(final Icon icon, final IntConsumer command) {
  		final JButton button=new JButton("");
  		button.addActionListener(e->command.accept(e.getModifiers()));
  		if (icon!=null) button.setIcon(icon);
  		return button;
  	}
  
  	/**
  	 * Aktualisiert die Darstellung der aktiven und deaktivieren
  	 * Schaltflchen, wenn ein Eintrag in der Liste gewhlt wurde.
  	 */
  	private void updateToolbar() {
  		buttonEdit.setEnabled(list.getSelectedIndex()>=0);
  		buttonDelete.setEnabled(list.getSelectedIndex()>=0);
  		buttonMoveUp.setEnabled(list.getSelectedIndex()>=1);
  		buttonMoveDown.setEnabled(list.getSelectedIndex()>=0 && list.getSelectedIndex()<list.getModel().getSize()-1);
  	}
  
  	/**
  	 * Liefert die Eintrge des Listenmodells
  	 * @return	Eintrge des Listenmodells
  	 */
  	protected abstract DefaultListModel<JLabel> getListModel();
  
  	/**
  	 * Aktualisiert die Liste
  	 * @param deltaSelectedIndex	Gibt an, um wie viele Eintrge die Selektion (sofern vorhanden) nach oben oder unten verschoben werden soll
  	 */
  	protected final void updateList(final int deltaSelectedIndex) {
  		final int selectedIndex=list.getSelectedIndex();
  
  		final DefaultListModel<JLabel> listModel=getListModel();
  		list.setModel(listModel);
  
  		if (selectedIndex>=0 && listModel.getSize()>0) list.setSelectedIndex(Math.max(0,Math.min(listModel.getSize()-1,selectedIndex+deltaSelectedIndex)));
  
  		updateToolbar();
  	}
  
  	/**
  	 * Fgt einen einzelnen Eintrag zu dem "Hinzufgen"-Popupmen hinzu und verknpft diesen mit dem {@link ParameterCompareSetupValueBaseListDialog#commandAdd(int)}-Listener
  	 * @param popupMenu	Popupmen zu dem der Eintrag hinzugefgt werden soll
  	 * @param title	Name des Eintrags
  	 * @param hint	Tooltip fr den Eintrag
  	 * @param icon	Icon fr den Eintrag
  	 * @param nr	Nummer, die dem Listener bergeben werden soll
  	 * @see ParameterCompareSetupValueBaseListDialog#commandAdd(int)
  	 * @see ParameterCompareSetupValueBaseListDialog#addAddModesToMenu(JButton, JPopupMenu)
  	 */
  	protected final void addAddButton(final JPopupMenu popupMenu, final String title, final String hint, final Icon icon, final int nr) {
  		JMenuItem item=new JMenuItem(title);
  		item.setToolTipText(hint);
  		if (icon!=null) item.setIcon(icon);
  		item.addActionListener(e->commandAdd(nr));
  		popupMenu.add(item);
  	}
  
  	/**
  	 * Bietet die Mglichkeit, normale Eintrge zu dem "Hinzufgen"-Popupmen hinzuzufgen (d.h. vor dem Vorlagen-Bereich)
  	 * @param anchor	Element, an dem das Popupmen ausgerichtet werden soll
  	 * @param popupMenu	Popupmen zu dem die Eintrge hinzugefgt werden knnen
  	 * @see ParameterCompareSetupValueBaseListDialog#addTemplatesToMenu(JButton, JPopupMenu)
  	 * @see ParameterCompareSetupValueBaseListDialog#addAddButton(JPopupMenu, String, String, Icon, int)
  	 */
  	protected void addAddModesToMenu(final JButton anchor, final JPopupMenu popupMenu) {
  		addAddButton(popupMenu,Language.tr("ParameterCompare.Settings.List.AddByXML"),Language.tr("ParameterCompare.Settings.List.AddByXML.Hint"),Images.PARAMETERSERIES_SELECT_XML.getIcon(),0);
  	}
  
  	/**
  	 * Zeigt ein Popupmen, das beim Anklicken der Hinzufgen-Schaltflche
  	 * aktiviert wird, an.
  	 * @see #buttonAdd
  	 */
  	private void commandAddPopup() {
  		final JPopupMenu popupMenu=new JPopupMenu();
  
  		addAddModesToMenu(buttonAdd,popupMenu);
  		popupMenu.addSeparator();
  		addTemplatesToMenu(buttonAdd,popupMenu);
  		if (popupMenu.getComponent(popupMenu.getComponentCount()-1) instanceof JPopupMenu.Separator) popupMenu.remove(popupMenu.getComponentCount()-1);
  
  		popupMenu.show(buttonAdd,0,buttonAdd.getHeight());
  	}
  
  	/**
  	 * Bietet die Mglichkeit, Vorlagen-Eintrge zu dem "Hinzufgen"-Popupmen hinzuzufgen
  	 * @param anchor	Element, an dem das Popupmen ausgerichtet werden soll
  	 * @param popupMenu	Popupmen zu dem die Eintrge hinzugefgt werden knnen
  	 * @see ParameterCompareSetupValueBaseListDialog#addAddModesToMenu(JButton, JPopupMenu)
  	 */
  	protected abstract void addTemplatesToMenu(final JButton anchor, final JPopupMenu popupMenu);
  
  	/**
  	 * Wird aufgerufen, wenn der Nutzer einen Eintrag hinzufgen will
  	 * @param nr	Nummer, die vom Listener bergeben wurde
  	 */
  	protected abstract void commandAdd(final int nr);
  
  	/**
  	 * Wird aufgerufen, wenn der Nutzer einen Eintrag bearbeiten will
  	 * @param index	Index des zu bearbeitenden Eintrags
  	 */
  	protected abstract void commandEdit(final int index);
  
  	/**
  	 * Wird aufgerufen, wenn der Nutzer einen Eintrag lschen will
  	 * @param index	Index des zu lschenden Eintrags
  	 * @param shiftDown	Ist Umschalt gedrckt?
  	 */
  	protected abstract void commandDelete(final int index, final boolean shiftDown);
  
  	/**
  	 * Wird aufgerufen, wenn zwei Eintrge in der Liste vertauscht werden sollen
  	 * @param index1	Erster Tausch-Index
  	 * @param index2	Zweiter Tausch-Index
  	 */
  	protected abstract void commandSwap(final int index1, final int index2);
  
  	/**
  	 * Befehl: Listeneintrag nach oben verschieben
  	 */
  	private void commandMoveUp() {
  		final int index=list.getSelectedIndex();
  		if (index<1) return;
  		commandSwap(index,index-1);
  		updateList(-1);
  	}
  
  	/**
  	 * Befehl: Listeneintrag nach unten verschieben
  	 */
  	private void commandMoveDown() {
  		final int index=list.getSelectedIndex();
  		if (index<0 || index>=list.getModel().getSize()-1) return;
  		commandSwap(index,index+1);
  		updateList(1);
  	}
  
  	/**
  	 * Erstellt auf Basis einer Schaltflche einen Menpunkt
  	 * @param button	Ausgangsschaltflche
  	 * @param keyStroke	Anzuzeigender Hotkey (darf <code>null</code> sein)
  	 * @return	Neuer Menpunkt
  	 * @see #showContextMenu(MouseEvent)
  	 */
  	private JMenuItem buttonToMenu(final JButton button, final KeyStroke keyStroke) {
  		final JMenuItem item=new JMenuItem(button.getText(),button.getIcon());
  		item.setToolTipText(button.getToolTipText());
  		if (keyStroke!=null) item.setAccelerator(keyStroke);
  		for (ActionListener listener : button.getActionListeners()) item.addActionListener(listener);
  		item.setEnabled(button.isEnabled());
  		return item;
  	}
  
  	/**
  	 * Zeigt das Kontextmen zu einem Listeneintrag an.
  	 * @param event	Auslsendes Mausereignis
  	 */
  	private void showContextMenu(final MouseEvent event) {
  		final JPopupMenu menu=new JPopupMenu();
  
  		menu.add(buttonToMenu(buttonAdd,KeyStroke.getKeyStroke(KeyEvent.VK_INSERT,0)));
  		menu.add(buttonToMenu(buttonEdit,KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0)));
  		menu.add(buttonToMenu(buttonDelete,KeyStroke.getKeyStroke(KeyEvent.VK_DELETE,0)));
  		menu.addSeparator();
  		menu.add(buttonToMenu(buttonMoveUp,KeyStroke.getKeyStroke(KeyEvent.VK_UP,InputEvent.CTRL_DOWN_MASK)));
  		menu.add(buttonToMenu(buttonMoveDown,KeyStroke.getKeyStroke(KeyEvent.VK_DOWN,InputEvent.CTRL_DOWN_MASK)));
  
  		menu.show(list,event.getX(),event.getY());
  	}
  }