[Schmitzm-commits] r2205 - in trunk/schmitzm-core/src/main: java java/de/schmitzm/lang java/de/schmitzm/swing java/de/schmitzm/swing/menu java/javax java/javax/swing resources/de/schmitzm/swing/resource/locales

scm-commit at wald.intevation.org scm-commit at wald.intevation.org
Thu Jan 31 13:27:01 CET 2013


Author: mojays
Date: 2013-01-31 13:27:01 +0100 (Thu, 31 Jan 2013)
New Revision: 2205

Added:
   trunk/schmitzm-core/src/main/java/de/schmitzm/swing/ListMaintainanceDialog.java
   trunk/schmitzm-core/src/main/java/de/schmitzm/swing/ListMaintainancePanel.java
   trunk/schmitzm-core/src/main/java/de/schmitzm/swing/menu/FavoritesMenu.java
   trunk/schmitzm-core/src/main/java/javax/
   trunk/schmitzm-core/src/main/java/javax/swing/
   trunk/schmitzm-core/src/main/java/javax/swing/ReferencedListModel.java
Modified:
   trunk/schmitzm-core/src/main/java/de/schmitzm/lang/ApplicationProps.java
   trunk/schmitzm-core/src/main/resources/de/schmitzm/swing/resource/locales/SwingResourceBundle.properties
   trunk/schmitzm-core/src/main/resources/de/schmitzm/swing/resource/locales/SwingResourceBundle_de.properties
Log:
ApplicationProps: encryption/decryption swap to methods, new method setStringList(.)
new ListMaintainancePanel/Dialog: abstract components to edit/order a List<?>
new FavoritesMenu: generic JMenu to show varying number of favorite items
new ReferencedListModel: ListModel which data directly refers to an origin list

Modified: trunk/schmitzm-core/src/main/java/de/schmitzm/lang/ApplicationProps.java
===================================================================
--- trunk/schmitzm-core/src/main/java/de/schmitzm/lang/ApplicationProps.java	2013-01-31 08:18:32 UTC (rev 2204)
+++ trunk/schmitzm-core/src/main/java/de/schmitzm/lang/ApplicationProps.java	2013-01-31 12:27:01 UTC (rev 2205)
@@ -286,6 +286,19 @@
   }
 
   /**
+   * Decrypts a string value.
+   */
+  public String decrypt(String encryptedValue) {
+    if ( encryptCipher == null || decryptCipher == null )
+      throw new UnsupportedOperationException("getStringDecrypted(.) can not be used before a cipher is set.");
+    if ( encryptedValue == null )
+      return null;
+    byte[] encryptedBytes = CryptUtil.convertWritableStringToEncryptedBytes(encryptedValue);
+    String decryptedValue = CryptUtil.decryptString(encryptedBytes, getDecrytionCipher());
+    return decryptedValue;
+  }
+  
+  /**
    * Returns a property value which was stored encrypted in the map.
    * @param key properties key
    * @param defaultValueDecrypted default value (already decrypted!)
@@ -299,9 +312,7 @@
     if ( encryptedValue == null )
       return defaultValueDecrypted.length > 0 ? defaultValueDecrypted[0] : null;
     try {
-      byte[] encryptedBytes = CryptUtil.convertWritableStringToEncryptedBytes(encryptedValue);
-      String decryptedValue = CryptUtil.decryptString(encryptedBytes, getDecrytionCipher());
-      return decryptedValue;
+      return decrypt(encryptedValue);
     } catch (Exception err) {
       LOGGER.error("Error decrypting value '"+encryptedValue+"' for key '"+key+"' (NULL value returned): "+err.getMessage());
       LOGGER.debug(err.getMessage(),err);
@@ -329,6 +340,24 @@
   }
   
   /**
+   * Encodes a list as semicolon-separated list
+   * and stores it in map
+   */
+  public <T> void setStringList(KEYS key, List<T> list) {
+    String listStr = "";
+    
+    if ( list != null ) {
+      for (T v : list) {
+        String valueStr = v != null ? v.toString() : "";
+        if (!StringUtils.isBlank(listStr))
+          listStr += ";";
+        listStr += valueStr.trim();
+      }
+    }
+    set(key, listStr);
+  }
+
+  /**
    * Encodes a map as semicolon-separated list of "key:value" pairs
    * and stores it in map
    */
@@ -348,22 +377,12 @@
     set(key, mapStr);
   }
 
-  /**
-   * Set a value in the underlying {@link Properties}. The value
-   * is encrypted by the encryption {@link Cipher}.
-   * @see #setCipher(Cipher, Cipher)
-   * @see #setCipher(byte[])
-   * @see #setCipher(String)
-   */
-  public void setEncrypted(KEYS key, Object clearValue) {
+  public String encrypt(Object clearValue) {
     if ( encryptCipher == null || decryptCipher == null )
       throw new UnsupportedOperationException("setEncrypted(.) can not be used before a cipher is set.");
-    // NULL can not be encrypted
-    if ( clearValue == null ) {
-      LOGGER.warn("NULL value for key '"+key+"' is stored not-encrypt!");
-      set(key,clearValue);
-      return;
-    }
+    if ( clearValue == null )
+      return null;
+    
     // convert Byte[] to byte[]
     if ( clearValue instanceof Byte[] ) {
       Byte[] b = (Byte[])clearValue;
@@ -383,9 +402,29 @@
     //          properties.put(key.toString(), encryptBytes) forces
     //          a crash on store() because cast to String fails!
     // Solution: store bytes concatenated
-    set(key, CryptUtil.convertEncryptedBytesToWritableString(encryptBytes)); 
+    return CryptUtil.convertEncryptedBytesToWritableString(encryptBytes);  
+    
   }
   
+  /**
+   * Set a value in the underlying {@link Properties}. The value
+   * is encrypted by the encryption {@link Cipher}.
+   * @see #setCipher(Cipher, Cipher)
+   * @see #setCipher(byte[])
+   * @see #setCipher(String)
+   */
+  public void setEncrypted(KEYS key, Object clearValue) {
+//    if ( encryptCipher == null || decryptCipher == null )
+//      throw new UnsupportedOperationException("setEncrypted(.) can not be used before a cipher is set.");
+    // NULL can not be encrypted
+    if ( clearValue == null ) {
+      LOGGER.warn("NULL value for key '"+key+"' is stored not-encrypt!");
+      set(key,clearValue);
+      return;
+    }
+    set(key, encrypt(clearValue)); 
+  }
+  
   /////////////////////////////////////////////////////
   ///////////////  Property storage  //////////////////
   /////////////////////////////////////////////////////

Added: trunk/schmitzm-core/src/main/java/de/schmitzm/swing/ListMaintainanceDialog.java
===================================================================
--- trunk/schmitzm-core/src/main/java/de/schmitzm/swing/ListMaintainanceDialog.java	                        (rev 0)
+++ trunk/schmitzm-core/src/main/java/de/schmitzm/swing/ListMaintainanceDialog.java	2013-01-31 12:27:01 UTC (rev 2205)
@@ -0,0 +1,113 @@
+/**
+ * Copyright (c) 2009 Martin O. J. Schmitz.
+ * 
+ * This file is part of the SCHMITZM library - a collection of utility 
+ * classes based on Java 1.6, focusing (not only) on Java Swing 
+ * and the Geotools library.
+ * 
+ * The SCHMITZM project is hosted at:
+ * http://wald.intevation.org/projects/schmitzm/
+ * 
+ * This program 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 3
+ * 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 Lesser General Public License (license.txt)
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * or try this link: http://www.gnu.org/licenses/lgpl.html
+ * 
+ * Contributors:
+ *     Martin O. J. Schmitz - initial API and implementation
+ *     Stefan A. Tzeggai - additional utility classes
+ */
+package de.schmitzm.swing;
+
+import java.awt.BorderLayout;
+import java.awt.Dialog;
+import java.awt.event.ActionEvent;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+
+/**
+ * Dialog to maintain list data.
+ * @author Martin O.J. Schmitz
+ *
+ */
+public abstract class ListMaintainanceDialog<T> extends OkCancelDialog {
+  
+  protected ListMaintainancePanel<T> listPanel;
+  protected boolean orderable;
+
+  /**
+   * Creates a new dialog.
+   * @param owner parent frame
+   * @param title dialog title
+   * @param orderable indicates whether the element order should be maintained by
+   *                  the panel
+   */
+  public ListMaintainanceDialog(JFrame owner, String title, boolean orderable) {
+    super(owner, title, true, false);
+    this.orderable = orderable;
+    init();
+  }
+
+  /**
+   * Creates a new dialog.
+   * @param owner parent frame
+   * @param title dialog title
+   * @param orderable indicates whether the element order should be maintained by
+   *                  the panel
+   */
+  public ListMaintainanceDialog(Dialog owner, String title, boolean orderable) {
+    super(owner, title, true, false);
+    this.orderable = orderable;
+    init();
+  }
+  
+  @Override
+  protected void init() {
+    super.init();
+
+    this.listPanel = new ListMaintainancePanel<T>(orderable) {
+      @Override
+      protected T createNewElement() {
+        return ListMaintainanceDialog.this.createNewElement();
+      }
+
+      @Override
+      protected T editElement(T element) {
+        return ListMaintainanceDialog.this.editElement(element);
+      }
+    };
+
+    getContentPane().add( listPanel, BorderLayout.CENTER );
+    pack();
+    SwingUtil.setRelativeFramePosition(this, getOwner(), 0.5, 0.5);
+  }
+  
+  /**
+   * Creates a new element. Called by {@link ListMaintainancePanel#actionPerformed(ActionEvent)}.
+   */
+  protected abstract T createNewElement();
+    
+  /**
+   * Edits an element. Called by {@link ListMaintainancePanel#actionPerformed(ActionEvent)}.
+   */
+  protected abstract T editElement(T element);
+
+  /**
+   * Returns the list panel. E.g. to edit the data from scratch.
+   */
+  public ListMaintainancePanel<T> getListPanel() {
+    return listPanel;
+  }
+
+}

Added: trunk/schmitzm-core/src/main/java/de/schmitzm/swing/ListMaintainancePanel.java
===================================================================
--- trunk/schmitzm-core/src/main/java/de/schmitzm/swing/ListMaintainancePanel.java	                        (rev 0)
+++ trunk/schmitzm-core/src/main/java/de/schmitzm/swing/ListMaintainancePanel.java	2013-01-31 12:27:01 UTC (rev 2205)
@@ -0,0 +1,379 @@
+package de.schmitzm.swing;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseListener;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import javax.swing.Action;
+import javax.swing.DefaultListModel;
+import javax.swing.JButton;
+import javax.swing.JList;
+import javax.swing.JScrollPane;
+import javax.swing.ReferencedListModel;
+import javax.swing.event.ListDataListener;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+
+import net.miginfocom.swing.MigLayout;
+
+/**
+ * Panel showing a {@link JList} which allows to modify the list.
+ * @author Martin O.J. Schmitz
+ */
+public abstract class ListMaintainancePanel<T> extends JPanel implements ActionListener, ListSelectionListener {
+  protected JList<T> list;
+
+  protected Action addAction;
+  protected Action removeAction;
+  protected Action editAction;
+  protected Action moveUpAction;
+  protected Action moveDownAction;
+
+  /**
+   * Creates a new list with actions to add, edit and remove elements.
+   * @param data initial data
+   * @param orderable    indicates whether the element order should be maintained by
+   *                     the panel
+   */
+  public ListMaintainancePanel(boolean orderable) {
+    this(true, true, true, orderable, null);
+  }
+
+  /**
+   * Creates a new list.
+   * @param data initial data
+   * @param addAction    indicates whether the elements can be added
+   * @param removeAction indicates whether the elements can be removed
+   * @param editAction   indicates whether the elements can be edited/changed
+   * @param orderable    indicates whether the element order should be maintained by
+   *                     the panel
+   */
+  public ListMaintainancePanel(boolean addAction, boolean removeAction, boolean editAction, boolean orderable) {
+    this(addAction, removeAction, editAction, orderable,null);
+  }
+
+  /**
+   * Creates a new list.
+   * @param data list data (if {@code null} an new empty {@link ArrayList} is created)
+   * @param orderable indicates whether the element order should be maintained by
+   *                  the panel
+   */
+  public ListMaintainancePanel(final boolean addAction, final boolean removeAction, final boolean editAction, final boolean orderable, List<T> data) {
+    super( new BorderLayout() );
+
+    list = new JList<T>( new ListModel<T>() );
+    list.addListSelectionListener(this);
+    list.addMouseListener( new MouseAdapter() {
+      @Override
+      public void mouseClicked(MouseEvent e) {
+        if ( e == null || !editAction )
+          return;
+        if ( e.getButton() == MouseEvent.BUTTON1 &&
+             e.getClickCount() == 2 )
+          performEditAction();
+      }
+    });
+
+    // Order actions
+    JPanel orderActionsPanel = new JPanel(new MigLayout("wrap 1","[]","[top][bottom]"));
+    if ( orderable ) {
+      this.moveUpAction   = SwingUtil.createAction(SwingUtil.R("ListMaintainancePanel.action.moveUp"), this, "MOVE_UP", SwingUtil.R("ListMaintainancePanel.action.moveUp.desc"), SwingUtil.createImageIconFromResourcePath(SwingUtil.class, "resource/icons/small/arrow_up.png", null));
+      this.moveDownAction = SwingUtil.createAction(SwingUtil.R("ListMaintainancePanel.action.moveDown"), this, "MOVE_DOWN", SwingUtil.R("ListMaintainancePanel.action.moveDown.desc"), SwingUtil.createImageIconFromResourcePath(SwingUtil.class, "resource/icons/small/arrow_down.png", null));
+      orderActionsPanel.add(new JButton(this.moveUpAction));
+      orderActionsPanel.add(new JButton(this.moveDownAction));
+    }
+    // Edit actions
+    JPanel editActionsPanel = new JPanel(new MigLayout("wrap 1","[]","[top]"));
+    if ( addAction ) {
+      this.addAction      = SwingUtil.createAction(SwingUtil.R("ListMaintainancePanel.action.add"), this, "ADD", SwingUtil.R("ListMaintainancePanel.action.add.desc"), SwingUtil.createImageIconFromResourcePath(SwingUtil.class, "resource/icons/small/add.png", null));
+      editActionsPanel.add(new JButton(this.addAction));
+    }
+    if  ( editAction ) {
+      this.editAction     = SwingUtil.createAction(SwingUtil.R("ListMaintainancePanel.action.edit"), this, "EDIT", SwingUtil.R("ListMaintainancePanel.action.edit.desc"), SwingUtil.createImageIconFromResourcePath(SwingUtil.class, "resource/icons/small/properties.png", null));
+      editActionsPanel.add(new JButton(this.editAction));
+    }
+    if  ( removeAction ) {
+      this.removeAction   = SwingUtil.createAction(SwingUtil.R("ListMaintainancePanel.action.remove"), this, "REMOVE", SwingUtil.R("ListMaintainancePanel.action.remove.desc"), SwingUtil.createImageIconFromResourcePath(SwingUtil.class, "resource/icons/small/cancel2.png", null));
+      editActionsPanel.add(new JButton(this.removeAction));
+    }
+    add(new JScrollPane(list), BorderLayout.CENTER);
+    if ( orderActionsPanel.getComponentCount() > 0 )
+      add(orderActionsPanel, BorderLayout.WEST);
+    if ( editActionsPanel.getComponentCount() > 0 )
+      add(editActionsPanel, BorderLayout.EAST);
+    
+    for (Component c : orderActionsPanel.getComponents())
+      if ( c instanceof JButton ) {
+        ((JButton)c).setHideActionText(true);
+        SwingUtil.setMaximumWidth(c,20);
+      }
+    for (Component c : editActionsPanel.getComponents())
+      if ( c instanceof JButton ) {
+        ((JButton)c).setHideActionText(true);
+        SwingUtil.setMaximumWidth(c,30);
+      }
+    
+    setData(data);
+  }
+  
+  /**
+   * Returns the {@link JList}.
+   */
+  public JList<T> getList() {
+    return list;
+  }
+  
+  /**
+   * Returns the data. Always returns a new {@link List} instance. Changes
+   * on this object does not effect the GUI!
+   */
+  public List<T> getData() {
+    return ((ListModel<T>)list.getModel()).getDataSource();
+  }
+
+  /**
+   * Sets the data of the list. 
+   */
+  public void setData(List<T> data) {
+    ((ListModel<T>)list.getModel()).setDataSource(data);
+  }
+
+
+  /**
+   * Reacts on selection changes in lists with call of {@link #updateActions()}.
+   */
+  @Override
+  public void valueChanged(ListSelectionEvent e) {
+    updateActions();
+  }
+  
+  /**
+   * Updates the enabled state of actions according to list selections.
+   */
+  protected void updateActions() {
+    boolean listSelected = list.getSelectedIndices().length > 0;
+    if ( editAction != null )
+      editAction.setEnabled( listSelected );
+    if ( removeAction != null )
+      removeAction.setEnabled( listSelected );
+    if ( moveUpAction != null )
+      moveUpAction.setEnabled( listSelected );
+    if ( moveDownAction != null )
+      moveDownAction.setEnabled( listSelected );
+  }
+  
+  /**
+   * Adds a {@link ListDataListener} to the {@linkplain #list list}
+   * which is informed on each list change.
+   */
+  public void addListDataListener(ListDataListener l) {
+    list.getModel().addListDataListener(l);
+  }
+  
+  /**
+   * Removes a {@link ListDataListener} from the {@linkplain #listlist}.
+   */
+  public void removeListDataListener(ListDataListener l) {
+    list.getModel().removeListDataListener(l);
+  }
+
+  /**
+   * Removes all list entries from list. 
+   */
+  public void clearList() {
+    // remove all entries from field sequence
+    ((ListModel<T>)list.getModel()).removeAllElements();
+  }
+
+  /**
+   * Adds an element to the list. 
+   */
+  public void addElement(int idx, T element) {
+    if ( idx >= 0 )
+      ((ListModel<T>)list.getModel()).insertElementAt(element,idx);
+    else
+      ((ListModel<T>)list.getModel()).addElement(element);
+  }
+
+  /**
+   * Adds an element to the end of the list. 
+   */
+  public void addElement(T element) {
+    addElement(-1,element);
+  }
+  
+  /**
+   * Removes an element from the list. 
+   */
+  public void removeElement(T element) {
+    ((ListModel<T>)list.getModel()).removeElement(element);
+  }
+  
+  /**
+   * Creates a new element. Called by {@link #actionPerformed(ActionEvent)}.
+   */
+  protected abstract T createNewElement();
+  
+  /**
+   * Edits an element. Called by {@link #actionPerformed(ActionEvent)}.
+   */
+  protected abstract T editElement(T element);
+
+  /**
+   * Performs the button actions.
+   */
+  @Override
+  public void actionPerformed(ActionEvent e) {
+    String actionID = e.getActionCommand();
+    
+    if ( "ADD".equals(actionID) ) {
+      T elem = createNewElement();
+      if ( elem != null )
+        addElement(elem);
+    }
+    if ( "EDIT".equals(actionID) ) {
+      performEditAction();
+    }
+    if ( "REMOVE".equals(actionID) ) {
+      for ( T elem : list.getSelectedValuesList() )
+        removeElement(elem);
+    }
+    if ( "MOVE_UP".equals(actionID) ) {
+      List<T> selectedValues = list.getSelectedValuesList();
+      int[] selectedIdx = list.getSelectedIndices();
+      int firstIdx = selectedIdx[0];
+      int lastIdx  = selectedIdx[selectedIdx.length-1];
+      if ( firstIdx > 0 ) {
+        ((ListModel<T>)list.getModel()).removeRange(firstIdx, lastIdx);
+        ((ListModel<T>)list.getModel()).addElements(firstIdx-1, selectedValues);
+        list.addSelectionInterval(firstIdx-1, lastIdx-1);
+      }
+    }
+    if ( "MOVE_DOWN".equals(actionID) ) {
+      List<T> selectedValues = list.getSelectedValuesList();
+      int[] selectedIdx = list.getSelectedIndices();
+      int firstIdx = selectedIdx[0];
+      int lastIdx  = selectedIdx[selectedIdx.length-1];
+      if ( lastIdx < ((ListModel<T>)list.getModel()).getSize()-1 ) {
+        ((ListModel<T>)list.getModel()).removeRange(firstIdx, lastIdx);
+        ((ListModel<T>)list.getModel()).addElements(firstIdx+1, selectedValues);
+        list.addSelectionInterval(firstIdx+1, lastIdx+1);
+      }
+    }
+    
+  }  
+  
+  /**
+   * Invokes the edit action for the selected list item. Does nothing if
+   * edit action is not enabled or no list item is selected. 
+   */
+  protected void performEditAction() {
+    if ( editAction == null )
+      return;
+    T elem = list.getSelectedValue();
+    if ( elem == null )
+      return;
+    // Edit action:
+    int elemIdx = list.getSelectedIndex();
+    elem = editElement(elem);
+    // set element
+    // --> not really necessary, but method fires update event!
+    if ( elem != null )
+      ((ListModel<T>)getList().getModel()).setElementAt(elem, elemIdx);
+  }
+  
+  /**
+   * Data model for the {@link ListMaintainancePanel}.
+   * @author Martin O.J. Schmitz
+   */
+  protected class ListModel<T> extends ReferencedListModel<T> {
+    /**
+     * Creates empty list model.
+     */
+    public ListModel() {
+      this(null);
+    }
+
+
+    /**
+     * Creates list model and initializes the values.
+     */
+    public ListModel(List<T> list) {
+      super(list);
+    }
+    
+    
+    /**
+     * Removes elements from list.
+     */
+    public void removeElements(T[] elements) {
+      for (T f : elements)
+        removeElement(f);
+    }
+
+    /**
+     * Removes elements from list.
+     */
+    public void removeElements(Collection<T> elements) {
+      for (T f : elements)
+        removeElement(f);
+    }
+
+    /**
+     * Adds elements to bottom of the list.
+     */
+    public void addElements(T[] elements) {
+      addElements(getSize(), elements);
+    }
+    
+    /**
+     * Inserts elements to bottom at specific list position.
+     */
+    public void addElements(int idx, T[] elements) {
+      if ( elements == null )
+        return;
+      for (T f : elements)
+        insertElementAt(f, idx++);
+    }
+
+    /**
+     * Adds elements to bottom of the list.
+     */
+    public void addElements(Collection<T> elements) {
+      addElements(0, elements);
+    }
+    
+    /**
+     * Inserts elements to bottom at specific list position.
+     */
+    public void addElements(int idx, Collection<T> elements) {
+      if ( elements == null )
+        return;
+      for (T f : elements)
+        insertElementAt(f, idx++);
+    }
+
+    /**
+     * Initializes the list elements from given list.
+     */
+    public void setElements(T[] fields) {
+      removeAllElements();
+      addElements(fields);
+    }
+
+    /**
+     * Initializes the list elements from given list.
+     */
+    public void setElements(Collection<T> fields) {
+      removeAllElements();
+      addElements(fields);
+    }
+  }
+}
\ No newline at end of file

Added: trunk/schmitzm-core/src/main/java/de/schmitzm/swing/menu/FavoritesMenu.java
===================================================================
--- trunk/schmitzm-core/src/main/java/de/schmitzm/swing/menu/FavoritesMenu.java	                        (rev 0)
+++ trunk/schmitzm-core/src/main/java/de/schmitzm/swing/menu/FavoritesMenu.java	2013-01-31 12:27:01 UTC (rev 2205)
@@ -0,0 +1,214 @@
+/**
+ * Copyright (c) 2009 Martin O. J. Schmitz.
+ * 
+ * This file is part of the SCHMITZM library - a collection of utility 
+ * classes based on Java 1.6, focusing (not only) on Java Swing 
+ * and the Geotools library.
+ * 
+ * The SCHMITZM project is hosted at:
+ * http://wald.intevation.org/projects/schmitzm/
+ * 
+ * This program 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 3
+ * 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 Lesser General Public License (license.txt)
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * or try this link: http://www.gnu.org/licenses/lgpl.html
+ * 
+ * Contributors:
+ *     Martin O. J. Schmitz - initial API and implementation
+ *     Stefan A. Tzeggai - additional utility classes
+ */
+package de.schmitzm.swing.menu;
+
+import java.awt.Font;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.swing.AbstractAction;
+import javax.swing.Action;
+import javax.swing.JMenu;
+import javax.swing.JMenuItem;
+
+import de.schmitzm.swing.ButtonGroup;
+import de.schmitzm.swing.SwingUtil;
+
+/**
+ * {@link JMenu} to show a varying number of "Favorite" items. Additionally the
+ * menu contains an
+ * @author Martin O.J. Schmitz
+ *
+ */
+public class FavoritesMenu<F> extends JMenu {
+  /** {@link Action} command for "Maintain favorites" action (if no explicit
+   *  action is specified in constructor)  */
+  public static final String ACTION_CMD_MAINTAIN = "FAVORITES_MAINTAIN";
+  /** {@link Action} command for "Add favorite" action (if no explicit
+   *  action is specified in constructor)  */
+  public static final String ACTION_CMD_ADD = "FAVORITES_ADD";
+  /** {@link Action} command for "Remove favorite" action (this constant only
+   *  defines a prefix. In {@link ActionEvent} the prefix will be followed by
+   *  the favorite ID)  */
+  public static final String ACTION_CMD_PREFIX_REMOVE = "FAVORITES_REMOVE_";
+  /** {@link Action} command for "Apply favorite" action (this constant only
+   *  defines a prefix. In {@link ActionEvent} the prefix will be followed by
+   *  the favorite ID)  */
+  public static final String ACTION_CMD_PREFIX_APPLY = "FAVORITES_APPLY_";
+
+  /** Map key which is used to store the favorite object in the {@link Action} */
+  public static final String ACTION_KEY_FAVORITE = "FAVORITE";
+  
+  private static final JMenuItem NO_FAVS_MENU_ITEM = new JMenuItem(SwingUtil.R("FavoritesMenu.empty"));
+  static {
+    NO_FAVS_MENU_ITEM.setEnabled(false);
+    NO_FAVS_MENU_ITEM.setFont( NO_FAVS_MENU_ITEM.getFont().deriveFont(Font.ITALIC) );
+  }
+  
+  protected ActionListener actionListener = null;
+  protected Action addFavoriteAction = null;
+  protected Action maintainFavoritesAction = null;
+  
+  protected Map<? extends Object, F> favorites = null;
+  protected Map<Object, Action> favoriteActionsByFavId = new HashMap<Object, Action>();
+  protected Map<F, Action> favoriteActionsByFav = new HashMap<F, Action>();
+  
+  private ButtonGroup favsButtonGroup;
+
+  /**
+   * Creates a new menu.
+   * @param name menu title
+   * @param favorites map which contains the favorites (if this map's content changes
+   *                  {@link #initMenu(Map)} should be called to update the menu structure!)
+   * @param actionListener listener which is called for menu actions
+   */
+  public FavoritesMenu(String name, Map<Object,F> favorites, ActionListener actionListener) {
+    this(name,favorites,actionListener,null,null);
+  }
+  
+  
+  /**
+   * Creates a new menu.
+   * @param name menu title
+   * @param favorites map which contains the favorites (if this map's content changes
+   *                  {@link #initMenu(Map)} should be called to update the menu structure!)
+   * @param actionListener listener which is called for menu actions
+   * @param addFavoriteAction action used for "Add favorite" (if {@code null} a default action
+   *                          will be created, which also invokes the {@code actionListener}) 
+   * @param maintainFavoritesAction action used for "Maintain favorites" (if {@code null} a default action
+   *                                will be created, which also invokes the {@code actionListener}) 
+   */
+  public FavoritesMenu(String name, Map<?,F> favorites, ActionListener actionListener, Action addFavoriteAction, Action maintainFavoritesAction) {
+    super(name);
+    if ( addFavoriteAction == null )
+      addFavoriteAction = SwingUtil.createAction(SwingUtil.R("FavoritesMenu.add"), actionListener, ACTION_CMD_ADD, SwingUtil.R("FavoritesMenu.add.desc"), null);
+    if ( maintainFavoritesAction == null )
+      addFavoriteAction = SwingUtil.createAction(SwingUtil.R("FavoritesMenu.maintain"), actionListener, ACTION_CMD_MAINTAIN, SwingUtil.R("FavoritesMenu.maintain.desc"), null);
+
+    this.actionListener = actionListener;
+    this.addFavoriteAction = addFavoriteAction;
+    this.maintainFavoritesAction = maintainFavoritesAction;
+    
+    initMenu(favorites);
+  }
+  
+  /**
+   * Initializes the menu structure according to the current favorites map.
+   */
+  public void initMenu() {
+    initMenu( favorites );
+  }
+  
+  /**
+   * Initializes the menu structure.
+   * @param favorites favorites to show in menu
+   */
+  public void initMenu(Map<? extends Object,F> favorites) {
+    if ( favorites == null )
+      favorites = new HashMap<Object,F>();
+    
+    // remember selected favorite before clearing the maps and lists
+    F selectedFav = getSelectedFavorite();
+    
+    removeAll();
+    this.favorites = favorites;
+    this.favoriteActionsByFav.clear();
+    this.favoriteActionsByFavId.clear();
+    
+    add(addFavoriteAction);
+    addSeparator();
+    if ( favorites.size() > 0 ) {
+      favsButtonGroup = new ButtonGroup();
+      for (Object favID : favorites.keySet() ) {
+        F fav = favorites.get(favID);
+        // Create action to apply favorite
+        Action favApplyAction = SwingUtil.createAction(favID.toString(),
+                                                       actionListener,
+                                                       ACTION_CMD_PREFIX_APPLY+favID,
+                                                       SwingUtil.R("FavoritesMenu.apply.desc"),
+                                                       null);
+        favApplyAction.putValue(ACTION_KEY_FAVORITE, fav);
+        favoriteActionsByFavId.put(favID, favApplyAction);
+        favoriteActionsByFav.put(fav, favApplyAction);
+//        // Create actions to remove favorite
+//        Action favRemoveAction = SwingUtil.createAction("Remove favorite",
+//                                                        actionListener,
+//                                                        ACTION_CMD_PREFIX_REMOVE+favID,
+//                                                        "Remove favorites from list.",
+//                                                        null);
+        // Create menu item for favorite
+        JMenuItem favMenuItem = SwingUtil.createRadioMenuItem(favApplyAction, favsButtonGroup, fav == selectedFav);
+        add( favMenuItem );
+      }
+    } else {
+      add(NO_FAVS_MENU_ITEM);
+    }    
+    addSeparator();
+    add(maintainFavoritesAction);
+  }
+  
+  /**
+   * Returns the favorite object currently selected in the menu.
+   */
+  public F getSelectedFavorite() {
+    if ( favsButtonGroup == null ||
+         favsButtonGroup.getSelectedButton() == null ||
+         favsButtonGroup.getSelectedButton().getAction() == null )
+      return null;
+    Action favAction = favsButtonGroup.getSelectedButton().getAction();
+    return (F)favAction.getValue(ACTION_KEY_FAVORITE);
+//    for (F fav : favorites.values()) {
+//      Action favAction = favoriteActionsByFav.get(fav);
+//      if ( Boolean.TRUE.equals(favAction.getValue(AbstractAction.SELECTED_KEY)) )
+//        return fav;
+//    }
+//    return null;
+  }
+
+  /**
+   * Sets the favorite to be selected/applied in the menu.
+   */
+  public void setSelectedFavorite(F fav) {
+    Action favAction = favoriteActionsByFav.get(fav);
+    if ( favAction != null )
+      favAction.putValue(AbstractAction.SELECTED_KEY, true);
+  }
+
+  /**
+   * Sets the favorite to be selected/applied in the menu.
+   */
+  public void setSelectedFavoriteByID(Object favId) {
+    Action favAction = favoriteActionsByFavId.get(favId);
+    if ( favAction != null )
+      favAction.putValue(AbstractAction.SELECTED_KEY, true);
+  }
+}

Added: trunk/schmitzm-core/src/main/java/javax/swing/ReferencedListModel.java
===================================================================
--- trunk/schmitzm-core/src/main/java/javax/swing/ReferencedListModel.java	                        (rev 0)
+++ trunk/schmitzm-core/src/main/java/javax/swing/ReferencedListModel.java	2013-01-31 12:27:01 UTC (rev 2205)
@@ -0,0 +1,386 @@
+/**
+ * Copyright (c) 2009 Martin O. J. Schmitz.
+ * 
+ * This file is part of the SCHMITZM library - a collection of utility 
+ * classes based on Java 1.6, focusing (not only) on Java Swing 
+ * and the Geotools library.
+ * 
+ * The SCHMITZM project is hosted at:
+ * http://wald.intevation.org/projects/schmitzm/
+ * 
+ * This program 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 3
+ * 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 Lesser General Public License (license.txt)
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ * or try this link: http://www.gnu.org/licenses/lgpl.html
+ * 
+ * Contributors:
+ *     Martin O. J. Schmitz - initial API and implementation
+ *     Stefan A. Tzeggai - additional utility classes
+ */
+package javax.swing;
+
+import java.util.List;
+import java.util.Vector;
+
+/**
+ * This {@link ListModel} is a reduced version of {@link javax.swing.DefaultListModel}
+ * which uses a {@link List} as that the {@link #delegate} field (which contains the data).
+ * Furthermore the field is {@code protected} so that sub-classes can override the implementation.<br>
+ * <br>
+ * In contrast to {@link javax.swing.DefaultListModel} this model does not hold a copy
+ * of the list data, but changes an underlying {@link List} directly (to avoid unnecessary copy
+ * processes). However "external" inserts and deletes on the list data should be avoided because
+ * no corresponding events are fired!   
+ * @author Martin O.J. Schmitz
+ *
+ */
+public class ReferencedListModel<E> extends AbstractListModel<E>
+{
+    /** Hold the list data. */
+    protected List<E> delegate;
+
+    
+    /**
+     * Creates a new model with a new empty {@link Vector} as data source.
+     */
+    public ReferencedListModel() {
+      this(null);
+    }
+
+    /**
+     * Creates a new model.
+     */
+    public ReferencedListModel(List<E> data) {
+      super();
+      if ( data == null )
+        data = new Vector<E>();
+      this.delegate = data;
+    }
+    
+    /**
+     * Resets the data source.
+     */
+    public void setDataSource(List<E> data) {
+      // first remove all elements (and fire event)
+      removeAllElements();
+      // redefine the data
+      if ( data == null )
+        data = new Vector<E>();
+      this.delegate = data;
+
+      int index = delegate.size()-1;
+      if (index >= 0) {
+          fireIntervalAdded(this, 0, index);
+      }
+    }
+
+    /**
+     * Returns the data source. Modifications of this list will be effect the
+     * model data, but not immediately the GUI because no events are fired!
+     */
+    public List<E> getDataSource() {
+      return delegate;
+    }
+    
+    ////////////////////////////////////////////////////////////////////////
+    // The following methods are copied from javax.swing.DefaultListModel //
+    // and only lightly modified to respect the List type of delegate (   //
+    // instead of Vector)                                                 //
+    ////////////////////////////////////////////////////////////////////////
+
+    /**
+     * Returns the number of components in this list.
+     * <p>
+     * This method is identical to <code>size</code>, which implements the
+     * <code>List</code> interface defined in the 1.2 Collections framework.
+     * This method exists in conjunction with <code>setSize</code> so that
+     * <code>size</code> is identifiable as a JavaBean property.
+     *
+     * @return  the number of components in this list
+     * @see #size()
+     */
+    public int getSize() {
+        return delegate.size();
+    }
+
+    /**
+     * Returns the component at the specified index.
+     * <blockquote>
+     * <b>Note:</b> Although this method is not deprecated, the preferred
+     *    method to use is <code>get(int)</code>, which implements the
+     *    <code>List</code> interface defined in the 1.2 Collections framework.
+     * </blockquote>
+     * @param      index   an index into this list
+     * @return     the component at the specified index
+     * @exception  ArrayIndexOutOfBoundsException  if the <code>index</code>
+     *             is negative or greater than the current size of this
+     *             list
+     * @see #get(int)
+     */
+    public E getElementAt(int index) {
+        return delegate.get(index);
+    }
+
+    /**
+     * Returns the number of components in this list.
+     *
+     * @return  the number of components in this list
+     * @see Vector#size()
+     */
+    public int size() {
+        return delegate.size();
+    }
+
+    /**
+     * Tests whether this list has any components.
+     *
+     * @return  <code>true</code> if and only if this list has
+     *          no components, that is, its size is zero;
+     *          <code>false</code> otherwise
+     * @see Vector#isEmpty()
+     */
+    public boolean isEmpty() {
+        return delegate.isEmpty();
+    }
+
+    /**
+     * Tests whether the specified object is a component in this list.
+     *
+     * @param   elem   an object
+     * @return  <code>true</code> if the specified object
+     *          is the same as a component in this list
+     * @see Vector#contains(Object)
+     */
+    public boolean contains(Object elem) {
+        return delegate.contains(elem);
+    }
+
+    /**
+     * Searches for the first occurrence of <code>elem</code>.
+     *
+     * @param   elem   an object
+     * @return  the index of the first occurrence of the argument in this
+     *          list; returns <code>-1</code> if the object is not found
+     * @see Vector#indexOf(Object)
+     */
+    public int indexOf(Object elem) {
+        return delegate.indexOf(elem);
+    }
+
+    /**
+     * Returns the index of the last occurrence of <code>elem</code>.
+     *
+     * @param   elem   the desired component
+     * @return  the index of the last occurrence of <code>elem</code>
+     *          in the list; returns <code>-1</code> if the object is not found
+     * @see Vector#lastIndexOf(Object)
+     */
+    public int lastIndexOf(Object elem) {
+        return delegate.lastIndexOf(elem);
+    }
+
+    /**
+     * Returns the component at the specified index.
+     * Throws an <code>ArrayIndexOutOfBoundsException</code> if the index
+     * is negative or not less than the size of the list.
+     * <blockquote>
+     * <b>Note:</b> Although this method is not deprecated, the preferred
+     *    method to use is <code>get(int)</code>, which implements the
+     *    <code>List</code> interface defined in the 1.2 Collections framework.
+     * </blockquote>
+     *
+     * @param      index   an index into this list
+     * @return     the component at the specified index
+     * @see #get(int)
+     * @see Vector#elementAt(int)
+     */
+    public E elementAt(int index) {
+        return delegate.get(index);
+    }
+
+    /**
+     * Returns the first component of this list.
+     * Throws a <code>NoSuchElementException</code> if this
+     * vector has no components.
+     * @return     the first component of this list
+     * @see Vector#firstElement()
+     */
+    public E firstElement() {
+        return delegate.get(0);
+    }
+
+    /**
+     * Returns the last component of the list.
+     * Throws a <code>NoSuchElementException</code> if this vector
+     * has no components.
+     *
+     * @return  the last component of the list
+     * @see Vector#lastElement()
+     */
+    public E lastElement() {
+        return delegate.get( getSize()-1 );
+    }
+
+    /**
+     * Sets the component at the specified <code>index</code> of this
+     * list to be the specified element. The previous component at that
+     * position is discarded.
+     * <p>
+     * Throws an <code>ArrayIndexOutOfBoundsException</code> if the index
+     * is invalid.
+     * <blockquote>
+     * <b>Note:</b> Although this method is not deprecated, the preferred
+     *    method to use is <code>set(int,Object)</code>, which implements the
+     *    <code>List</code> interface defined in the 1.2 Collections framework.
+     * </blockquote>
+     *
+     * @param      element what the component is to be set to
+     * @param      index   the specified index
+     * @see #set(int,Object)
+     * @see Vector#setElementAt(Object,int)
+     */
+    public void setElementAt(E element, int index) {
+        delegate.set(index,element);
+        fireContentsChanged(this, index, index);
+    }
+
+    /**
+     * Deletes the component at the specified index.
+     * <p>
+     * Throws an <code>ArrayIndexOutOfBoundsException</code> if the index
+     * is invalid.
+     * <blockquote>
+     * <b>Note:</b> Although this method is not deprecated, the preferred
+     *    method to use is <code>remove(int)</code>, which implements the
+     *    <code>List</code> interface defined in the 1.2 Collections framework.
+     * </blockquote>
+     *
+     * @param      index   the index of the object to remove
+     * @see #remove(int)
+     * @see Vector#removeElementAt(int)
+     */
+    public void removeElementAt(int index) {
+        delegate.remove(index);
+        fireIntervalRemoved(this, index, index);
+    }
+
+    /**
+     * Inserts the specified element as a component in this list at the
+     * specified <code>index</code>.
+     * <p>
+     * Throws an <code>ArrayIndexOutOfBoundsException</code> if the index
+     * is invalid.
+     * <blockquote>
+     * <b>Note:</b> Although this method is not deprecated, the preferred
+     *    method to use is <code>add(int,Object)</code>, which implements the
+     *    <code>List</code> interface defined in the 1.2 Collections framework.
+     * </blockquote>
+     *
+     * @param      element the component to insert
+     * @param      index   where to insert the new component
+     * @exception  ArrayIndexOutOfBoundsException  if the index was invalid
+     * @see #add(int,Object)
+     * @see Vector#insertElementAt(Object,int)
+     */
+    public void insertElementAt(E element, int index) {
+        delegate.add(index,element);
+        fireIntervalAdded(this, index, index);
+    }
+
+    /**
+     * Adds the specified component to the end of this list.
+     *
+     * @param   element   the component to be added
+     * @see Vector#addElement(Object)
+     */
+    public void addElement(E element) {
+        int index = delegate.size();
+        delegate.add(element);
+        fireIntervalAdded(this, index, index);
+    }
+
+    /**
+     * Removes the first (lowest-indexed) occurrence of the argument
+     * from this list.
+     *
+     * @param   obj   the component to be removed
+     * @return  <code>true</code> if the argument was a component of this
+     *          list; <code>false</code> otherwise
+     * @see Vector#removeElement(Object)
+     */
+    public boolean removeElement(Object obj) {
+        int index = indexOf(obj);
+        boolean rv = delegate.remove(obj);
+        if (index >= 0) {
+            fireIntervalRemoved(this, index, index);
+        }
+        return rv;
+    }
+
+
+    /**
+     * Removes all components from this list and sets its size to zero.
+     * <blockquote>
+     * <b>Note:</b> Although this method is not deprecated, the preferred
+     *    method to use is <code>clear</code>, which implements the
+     *    <code>List</code> interface defined in the 1.2 Collections framework.
+     * </blockquote>
+     *
+     * @see #clear()
+     * @see Vector#removeAllElements()
+     */
+    public void removeAllElements() {
+        int index1 = delegate.size()-1;
+        delegate.clear();
+        if (index1 >= 0) {
+            fireIntervalRemoved(this, 0, index1);
+        }
+    }
+
+
+    /**
+     * Deletes the components at the specified range of indexes.
+     * The removal is inclusive, so specifying a range of (1,5)
+     * removes the component at index 1 and the component at index 5,
+     * as well as all components in between.
+     * <p>
+     * Throws an <code>ArrayIndexOutOfBoundsException</code>
+     * if the index was invalid.
+     * Throws an <code>IllegalArgumentException</code> if
+     * <code>fromIndex > toIndex</code>.
+     *
+     * @param      fromIndex the index of the lower end of the range
+     * @param      toIndex   the index of the upper end of the range
+     * @see        #remove(int)
+     */
+    public void removeRange(int fromIndex, int toIndex) {
+        if (fromIndex > toIndex) {
+            throw new IllegalArgumentException("fromIndex must be <= toIndex");
+        }
+        for(int i = toIndex; i >= fromIndex; i--) {
+            delegate.remove(i);
+        }
+        fireIntervalRemoved(this, fromIndex, toIndex);
+    }
+
+    /**
+     * Returns a string that displays and identifies this
+     * object's properties.
+     *
+     * @return a String representation of this object
+     */
+   public String toString() {
+        return delegate.toString();
+    }
+}
+

Modified: trunk/schmitzm-core/src/main/resources/de/schmitzm/swing/resource/locales/SwingResourceBundle.properties
===================================================================
--- trunk/schmitzm-core/src/main/resources/de/schmitzm/swing/resource/locales/SwingResourceBundle.properties	2013-01-31 08:18:32 UTC (rev 2204)
+++ trunk/schmitzm-core/src/main/resources/de/schmitzm/swing/resource/locales/SwingResourceBundle.properties	2013-01-31 12:27:01 UTC (rev 2205)
@@ -319,4 +319,22 @@
 POP3Settings.auth.pw=Password
 
 DirectoryChooser.title=Choose directory
-DirectoryChooser.DirectoryRoot=Computer
\ No newline at end of file
+DirectoryChooser.DirectoryRoot=Computer
+
+FavoritesMenu.add=Add favorite
+FavoritesMenu.add.desc=Add a favorite to the favorites list
+FavoritesMenu.maintain=Maintain favorites
+FavoritesMenu.maintain.desc=Maintain the list of favorites
+FavoritesMenu.empty=No favorites defined yet
+FavoritesMenu.apply.desc=Apply favorite
+
+ListMaintainancePanel.action.add=<<<
+ListMaintainancePanel.action.add.desc=Add new list element
+ListMaintainancePanel.action.remove=>>>
+ListMaintainancePanel.action.remove.desc=Remove selected list elements
+ListMaintainancePanel.action.edit=Edit
+ListMaintainancePanel.action.edit.desc=Edit selected list element
+ListMaintainancePanel.action.moveUp=Up
+ListMaintainancePanel.action.moveUp.desc=Move selected elements up in list
+ListMaintainancePanel.action.moveDown=Dn
+ListMaintainancePanel.action.moveDown.desc=Move selected elements down in list

Modified: trunk/schmitzm-core/src/main/resources/de/schmitzm/swing/resource/locales/SwingResourceBundle_de.properties
===================================================================
--- trunk/schmitzm-core/src/main/resources/de/schmitzm/swing/resource/locales/SwingResourceBundle_de.properties	2013-01-31 08:18:32 UTC (rev 2204)
+++ trunk/schmitzm-core/src/main/resources/de/schmitzm/swing/resource/locales/SwingResourceBundle_de.properties	2013-01-31 12:27:01 UTC (rev 2205)
@@ -292,4 +292,22 @@
 POP3Settings.auth.pw=Passwort
 
 DirectoryChooser.title=Choose directory
-DirectoryChooser.DirectoryRoot=Computer
\ No newline at end of file
+DirectoryChooser.DirectoryRoot=Computer
+
+FavoritesMenu.add=Favorit hinzufügen
+FavoritesMenu.add.desc=Fügt einen Favorit der Liste hinzu
+FavoritesMenu.maintain=Favoriten verwalten
+FavoritesMenu.maintain.desc=Verwalten der Favoriten-Liste
+FavoritesMenu.empty=Keine Favoriten definiert
+FavoritesMenu.apply.desc=Favorit anwenden
+
+ListMaintainancePanel.action.add=<<<
+ListMaintainancePanel.action.add.desc=Neues Listen-Element hinzufügen
+ListMaintainancePanel.action.remove=>>>
+ListMaintainancePanel.action.remove.desc=Ausgewählte Listen-Elemente entfernen
+ListMaintainancePanel.action.edit=Bearb.
+ListMaintainancePanel.action.edit.desc=Ausgwähltes Listen-Element bearbeiten
+ListMaintainancePanel.action.moveUp=Hoch
+ListMaintainancePanel.action.moveUp.desc=Ausgewählte Elemente in der Liste nach oben/vorne verschieben
+ListMaintainancePanel.action.moveDown=Runter
+ListMaintainancePanel.action.moveDown.desc=Ausgewählte Elemente in der Liste nach unten/hinten verschieben



More information about the Schmitzm-commits mailing list