Beta2 relase v1.0-beta2
authorNiki Roo <niki@nikiroo.be>
Sun, 6 Mar 2016 17:54:51 +0000 (18:54 +0100)
committerNiki Roo <niki@nikiroo.be>
Sun, 6 Mar 2016 17:54:51 +0000 (18:54 +0100)
Should not crash on unknown colour, 'edit' now in 'view' mode, not
contact list anymore, can now edit/delete/add types, can set the group
of a data, can delete a data and a contact, can add a new contact (UI
still WIP)

README.md
src/be/nikiroo/jvcard/Card.java
src/be/nikiroo/jvcard/Contact.java
src/be/nikiroo/jvcard/Data.java
src/be/nikiroo/jvcard/resources/colors.properties
src/be/nikiroo/jvcard/tui/Main.java
src/be/nikiroo/jvcard/tui/UiColors.java
src/be/nikiroo/jvcard/tui/panes/ContactDetails.java
src/be/nikiroo/jvcard/tui/panes/ContactDetailsRaw.java
src/be/nikiroo/jvcard/tui/panes/ContactList.java
src/be/nikiroo/jvcard/tui/panes/MainContentList.java

index 1d888ecb5f7c24e26c734a102e0ad1888a773684..fa5b5632541240234c62a0f6b39609b9bdfef1ff 100644 (file)
--- a/README.md
+++ b/README.md
@@ -7,7 +7,7 @@ Small TUI (text mode) VCard manager (also supports abook files)
 - it can create a Swing terminal or use a real terminal (try "--help") using Lanterna 3 (which can be found here on GitHub, too)
 - it will list all the contacts of the file you select
 - it will show more detailed informations about a selected contact, including an ASCII art representation of their photo if any
-- it can delete a contact
+- it can create/delete a contact
 - it can be used to edit your data (currently in RAW format, field by field)
 - it can save back to file
 - English and French versions available (will look for the host language, can be forced with "--lang en")
@@ -15,6 +15,6 @@ Small TUI (text mode) VCard manager (also supports abook files)
 ## TODO
 
 - ".properties" files to easily change the colours
-- correct EDIT support
 - customisation of VIEW_CONTACT
 - lot of other things
+- correct UI for new contact/new data/edit data-types
index 4c4e5d174e419bb47c9037e2f4477a08438a3b73..53ae972a5803d38482333acae7e7fe565e45e34d 100644 (file)
@@ -86,6 +86,31 @@ public class Card {
        public Contact get(int index) {
                return contacts.get(index);
        }
+       
+       /**
+        * Add a new {@link Contact} in this {@link Card}.
+        * 
+        * @param contact
+        *            the new contact
+        */
+       public void add(Contact contact) {
+               contact.setParent(this);
+               contact.setDirty();
+               contacts.add(contact);
+       }
+
+       /**
+        * Remove the given {@link Contact} from its this {@link Card} if it is in.
+        * 
+        * @return TRUE in case of success
+        */
+       public boolean remove(Contact contact) {
+               if (contacts.remove(contact)) {
+                       setDirty();
+               }
+
+               return false;
+       }
 
        /**
         * Save the {@link Card} to the given {@link File} with the given
@@ -196,16 +221,6 @@ public class Card {
                }
        }
 
-       /**
-        * Return the full list of {@link Contact}s. Please use responsibly (this is
-        * the original list).
-        * 
-        * @return the list of {@link Contact}s
-        */
-       List<Contact> getContactsList() {
-               return contacts;
-       }
-
        /**
         * Notify that this element has unsaved changes.
         */
index 328993947311772e5b62af6920ea178adef6b02c..c5579fe8c5e1cf4a71eeec149d83c7d23be79ff4 100644 (file)
@@ -34,15 +34,17 @@ public class Contact {
 
                boolean fn = false;
                boolean n = false;
-               for (Data data : content) {
-                       if (data.getName().equals("N")) {
-                               n = true;
-                       } else if (data.getName().equals("FN")) {
-                               fn = true;
-                       }
+               if (content != null) {
+                       for (Data data : content) {
+                               if (data.getName().equals("N")) {
+                                       n = true;
+                               } else if (data.getName().equals("FN")) {
+                                       fn = true;
+                               }
 
-                       if (!data.getName().equals("VERSION")) {
-                               datas.add(data);
+                               if (!data.getName().equals("VERSION")) {
+                                       datas.add(data);
+                               }
                        }
                }
 
@@ -81,6 +83,32 @@ public class Contact {
                return datas.get(index);
        }
 
+       /**
+        * Add a new {@link Data} in this {@link Contact}.
+        * 
+        * @param data
+        *            the new data
+        */
+       public void add(Data data) {
+               data.setParent(this);
+               data.setDirty();
+               datas.add(data);
+       }
+
+       /**
+        * Remove the given {@link Data} from its this {@link Contact} if it is in.
+        * 
+        * @return TRUE in case of success
+        */
+       public boolean remove(Data data) {
+               if (datas.remove(data)) {
+                       setDirty();
+                       return true;
+               }
+
+               return false;
+       }
+
        /**
         * Return the preferred Data field with the given name, or NULL if none.
         * 
@@ -460,14 +488,7 @@ public class Contact {
         */
        public boolean delete() {
                if (parent != null) {
-                       List<Contact> list = parent.getContactsList();
-                       for (int i = 0; i < list.size(); i++) {
-                               if (list.get(i) == this) {
-                                       list.remove(i);
-                                       parent.setDirty();
-                                       return true;
-                               }
-                       }
+                       return parent.remove(this);
                }
 
                return false;
index f2bb4080f6d770776e1824e0cf7cfd7495c20433..377c41e29edbbbc8d97d73d2d27c79390752944f 100644 (file)
@@ -85,6 +85,49 @@ public class Data {
                return types.get(index);
        }
 
+       /**
+        * Add a new {@link TypeInfo} in this {@link Data}.
+        * 
+        * @param type
+        *            the new type
+        */
+       public void add(TypeInfo type) {
+               type.setParent(this);
+               type.setDirty();
+               types.add(type);
+       }
+
+       /**
+        * Remove the given {@link TypeInfo} from its this {@link Data} if it is in.
+        * 
+        * @return TRUE in case of success
+        */
+       public boolean remove(TypeInfo type) {
+               if (types.remove(type)) {
+                       setDirty();
+               }
+
+               return false;
+       }
+
+       /**
+        * Change the {@link TypeInfo}s of this {@link Data}.
+        * 
+        * @param types
+        *            the new types
+        */
+       @Deprecated
+       public void setTypes(List<TypeInfo> types) {
+               // TODO: check if this method is required
+               this.types.clear();
+               for (TypeInfo type : types) {
+                       this.types.add(type);
+                       type.setParent(this);
+               }
+
+               setDirty();
+       }
+
        /**
         * Return the name of this {@link Data}
         * 
@@ -126,6 +169,20 @@ public class Data {
                return group;
        }
 
+       /**
+        * Change the group of this {@link Data}
+        * 
+        * @param group
+        *            the new group
+        */
+       public void setGroup(String group) {
+               if ((group == null && this.group != null)
+                               || (group != null && !group.equals(this.group))) {
+                       this.group = group;
+                       setDirty();
+               }
+       }
+
        /**
         * Return the bkey number of this {@link Data} or -1 if it is not binary.
         * 
@@ -164,6 +221,19 @@ public class Data {
                return b64 >= 0;
        }
 
+       /**
+        * Delete this {@link Contact} from its parent {@link Card} if any.
+        * 
+        * @return TRUE in case of success
+        */
+       public boolean delete() {
+               if (parent != null) {
+                       return parent.remove(this);
+               }
+
+               return false;
+       }
+
        /**
         * Check if this {@link Data} has unsaved changes.
         * 
index 3fe44b70ab07780092544d6bdace8d8f8a809900..092247877c90b4dcbecf1ae18ae8d969163aa14f 100644 (file)
@@ -44,3 +44,8 @@ VIEW_CONTACT_NORMAL_FG = WHITE
 VIEW_CONTACT_NORMAL_BG = BLACK
 VIEW_CONTACT_NOTES_TITLE_FG = BLACK
 VIEW_CONTACT_NOTES_TITLE_BG = WHITE
+CONTACT_LINE_DIRTY_SELECTED_FG = BLACK
+CONTACT_LINE_DIRTY_SELECTED_BG = WHITE
+CONTACT_LINE_DIRTY_FG = BLACK
+CONTACT_LINE_DIRTY_BG = WHITE
+
index 4c3f7623b96828ad1fd9573efadc9735f1c55cec..db6e522c72ed61cc3b0a317e0fc21c97b2c255b8 100644 (file)
@@ -24,7 +24,7 @@ import com.googlecode.lanterna.input.KeyStroke;
  */
 public class Main {
        public static final String APPLICATION_TITLE = "jVcard";
-       public static final String APPLICATION_VERSION = "1.0-beta2-dev";
+       public static final String APPLICATION_VERSION = "1.0-beta2";
 
        static private Trans transService;
 
index a646dbead669ed9dafaf3f549b0eee783eb7f0e5..5c5d01eebcf32e3f1611a3daece2f14ae30f3d18 100644 (file)
@@ -2,6 +2,7 @@ package be.nikiroo.jvcard.tui;
 
 import java.util.HashMap;
 import java.util.Map;
+import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 
 import be.nikiroo.jvcard.resources.Bundles;
@@ -146,7 +147,12 @@ public class UiColors {
         */
        private TextColor getBackgroundColor(Element el) {
                if (!colorMap.containsKey(el.name() + "_BG")) {
-                       String value = bundle.getString(el.name() + "_BG");
+                       String value = null;
+                       try {
+                               value = bundle.getString(el.name() + "_BG");
+                       } catch (MissingResourceException mre) {
+                               value = null;
+                       }
                        colorMap.put(el.name() + "_BG",
                                        convertToColor(value, TextColor.ANSI.BLACK));
                }
@@ -164,7 +170,12 @@ public class UiColors {
         */
        private TextColor getForegroundColor(Element el) {
                if (!colorMap.containsKey(el.name() + "_FG")) {
-                       String value = bundle.getString(el.name() + "_FG");
+                       String value = null;
+                       try {
+                               value = bundle.getString(el.name() + "_FG");
+                       } catch (MissingResourceException mre) {
+                               value = null;
+                       }
                        colorMap.put(el.name() + "_FG",
                                        convertToColor(value, TextColor.ANSI.WHITE));
                }
index 08df4034165c761931a0fedebcf5eebf241d0058..2a69e41db2c64462d55a24449980c6be2672fab9 100644 (file)
@@ -166,6 +166,14 @@ public class ContactDetails extends MainContent {
                                return false;
                        }
                });
+               // TODO: add "normal" edit and remove this one into RAW edit
+               actions.add(new KeyAction(Mode.CONTACT_DETAILS_RAW, 'e',
+                               Trans.StringId.KEY_ACTION_EDIT_CONTACT) {
+                       @Override
+                       public Object getObject() {
+                               return contact;
+                       }
+               });
 
                return actions;
        }
index ec1a13640beffc4c001a4341a2dfdbe61e94f405..a81cec8dbc393e85b8473da2bbb0818b03915545 100644 (file)
@@ -18,14 +18,14 @@ import com.googlecode.lanterna.input.KeyType;
 
 public class ContactDetailsRaw extends MainContentList {
        private Contact contact;
-       private int mode;
+       private boolean extMode;
 
        public ContactDetailsRaw(Contact contact) {
                this.contact = contact;
-               this.mode = 0;
+               this.extMode = false;
 
                for (int i = 0; i < contact.size(); i++) {
-                       addItem("[detail line]");
+                       addItem("x");
                }
        }
 
@@ -44,7 +44,7 @@ public class ContactDetailsRaw extends MainContentList {
                                Trans.StringId.DUMMY) {
                        @Override
                        public Object getObject() {
-                               return contact.get(getSelectedIndex());
+                               return getSelectedData();
                        }
 
                        @Override
@@ -79,14 +79,153 @@ public class ContactDetailsRaw extends MainContentList {
                                return "Cannot modify value";
                        }
                });
+               actions.add(new KeyAction(Mode.ASK_USER_KEY, 'd', Trans.StringId.DUMMY) {
+                       @Override
+                       public Object getObject() {
+                               return getSelectedData();
+                       }
+
+                       @Override
+                       public String getQuestion() {
+                               // TODO i18n
+                               return "Delete data? [Y/N]";
+                       }
+
+                       @Override
+                       public String callback(String answer) {
+                               if (answer.equalsIgnoreCase("y")) {
+                                       Data data = getData();
+                                       if (data != null && data.delete()) {
+                                               removeItem("x");
+                                               return null;
+                                       }
+
+                                       // TODO i18n
+                                       return "Cannot delete data";
+                               }
+
+                               return null;
+                       }
+               });
+               // TODO: ui
+               actions.add(new KeyAction(Mode.ASK_USER, 'a', Trans.StringId.DUMMY) {
+                       @Override
+                       public Object getObject() {
+                               return contact;
+                       }
+
+                       @Override
+                       public String getQuestion() {
+                               // TODO i18n
+                               return "new data (xx = yy): ";
+                       }
+
+                       @Override
+                       public String callback(String answer) {
+                               if (answer.length() > 0 && answer.contains("=")) {
+                                       String[] tab = answer.split("=");
+                                       Data data = new Data(null, tab[0].trim(), tab[1].trim(),
+                                                       null);
+                                       getContact().add(data);
+                                       addItem("x");
+                               }
+
+                               return null;
+                       }
+               });
+               // TODO: use a real UI for this, not a simple text box (a list or
+               // something, maybe a whole new pane?)
+               actions.add(new KeyAction(Mode.ASK_USER, 't', Trans.StringId.DUMMY) {
+                       private String previous;
+
+                       @Override
+                       public Object getObject() {
+                               return getSelectedData();
+                       }
+
+                       @Override
+                       public String getQuestion() {
+                               Data data = getData();
+                               if (data != null) {
+                                       return data.getName();
+                               }
+
+                               return null;
+                       }
+
+                       @Override
+                       public String getDefaultAnswer() {
+                               Data data = getData();
+                               if (data != null) {
+                                       previous = typesToString(data, null).toString();
+                                       return previous;
+                               }
+
+                               return null;
+                       }
+
+                       @Override
+                       public String callback(String answer) {
+                               Data data = getData();
+                               if (data != null) {
+                                       if (!answer.equals(previous)) {
+                                               data.setTypes(stringToTypes(answer));
+                                       }
+                                       return null;
+                               }
+
+                               // TODO: i18n
+                               return "Cannot modify value";
+                       }
+               });
+               actions.add(new KeyAction(Mode.ASK_USER, 'g', Trans.StringId.DUMMY) {
+                       private String previous;
+
+                       @Override
+                       public Object getObject() {
+                               return getSelectedData();
+                       }
+
+                       @Override
+                       public String getQuestion() {
+                               Data data = getData();
+                               if (data != null) {
+                                       return data.getName();
+                               }
+
+                               return null;
+                       }
+
+                       @Override
+                       public String getDefaultAnswer() {
+                               Data data = getData();
+                               if (data != null) {
+                                       previous = data.getGroup();
+                                       return previous;
+                               }
+
+                               return null;
+                       }
+
+                       @Override
+                       public String callback(String answer) {
+                               Data data = getData();
+                               if (data != null) {
+                                       if (!answer.equals(previous)) {
+                                               data.setGroup(answer);
+                                       }
+                                       return null;
+                               }
+
+                               // TODO: i18n
+                               return "Cannot modify group";
+                       }
+               });
                actions.add(new KeyAction(Mode.NONE, KeyType.Tab,
                                Trans.StringId.KEY_ACTION_SWITCH_FORMAT) {
                        @Override
                        public boolean onAction() {
-                               mode++;
-                               if (mode > 1)
-                                       mode = 0;
-
+                               extMode = !extMode;
                                return false;
                        }
                });
@@ -118,6 +257,18 @@ public class ContactDetailsRaw extends MainContentList {
                        boolean focused) {
                // TODO: from ini file?
                int SIZE_COL_1 = 15;
+               int SIZE_COL_2_OPT = 10;
+
+               if (!extMode)
+                       SIZE_COL_2_OPT = 0;
+
+               List<TextPart> parts = new LinkedList<TextPart>();
+               Data data = null;
+               if (index > -1 && index < contact.size())
+                       data = contact.get(index);
+
+               if (data == null)
+                       return parts;
 
                Element el = (focused && selected) ? Element.CONTACT_LINE_SELECTED
                                : Element.CONTACT_LINE;
@@ -126,9 +277,6 @@ public class ContactDetailsRaw extends MainContentList {
                Element elDirty = (focused && selected) ? Element.CONTACT_LINE_DIRTY_SELECTED
                                : Element.CONTACT_LINE_DIRTY;
 
-               Data data = contact.get(index);
-
-               List<TextPart> parts = new LinkedList<TextPart>();
                if (data.isDirty()) {
                        parts.add(new TextPart(" ", el));
                        parts.add(new TextPart("*", elDirty));
@@ -137,27 +285,22 @@ public class ContactDetailsRaw extends MainContentList {
                }
                String name = " " + data.getName() + " ";
                String value = null;
+               String group = null;
 
                StringBuilder valueBuilder = new StringBuilder(" ");
-               switch (mode) {
-               case 0:
+               if (!extMode) {
                        valueBuilder.append(data.getValue());
                        if (data.getGroup() != null && data.getGroup().length() > 0) {
                                valueBuilder.append("(");
                                valueBuilder.append(data.getGroup());
                                valueBuilder.append(")");
                        }
-                       break;
-               case 1:
-                       for (int indexType = 0; indexType < data.size(); indexType++) {
-                               TypeInfo type = data.get(indexType);
-                               if (valueBuilder.length() > 1)
-                                       valueBuilder.append(", ");
-                               valueBuilder.append(type.getName());
-                               valueBuilder.append(": ");
-                               valueBuilder.append(type.getValue());
-                       }
-                       break;
+               } else {
+                       group = data.getGroup();
+                       if (group == null)
+                               group = "";
+
+                       typesToString(data, valueBuilder);
                }
                valueBuilder.append(" ");
 
@@ -167,14 +310,99 @@ public class ContactDetailsRaw extends MainContentList {
                value = StringUtils.sanitize(value, UiColors.getInstance().isUnicode());
 
                name = StringUtils.padString(name, SIZE_COL_1);
+               group = StringUtils.padString(group, SIZE_COL_2_OPT);
                value = StringUtils.padString(value, width - SIZE_COL_1
-                               - getSeparator().length() - 2);
+                               - SIZE_COL_2_OPT - (extMode ? 2 : 1) * getSeparator().length()
+                               - 2);
 
                parts.add(new TextPart(name, el));
                parts.add(new TextPart(getSeparator(), elSep));
                parts.add(new TextPart(value, el));
+               if (extMode) {
+                       parts.add(new TextPart(getSeparator(), elSep));
+                       parts.add(new TextPart(group, el));
+               }
 
                return parts;
-       };
+       }
+
+       /**
+        * Return the currently selected {@link Data}.
+        * 
+        * @return the currently selected {@link Data}
+        */
+       private Data getSelectedData() {
+               int index = getSelectedIndex();
+               if (index > -1 && index < this.contact.size())
+                       return contact.get(index);
+               return null;
+       }
+
+       /**
+        * Serialise the {@link TypeInfo}s in the given {@link Data}.
+        * 
+        * @param data
+        *            the {@link Data} from which to take the {@link TypeInfo}s
+        * @param builder
+        *            an optional {@link StringBuilder} to append the serialized
+        *            version to
+        * 
+        * @return the given {@link StringBuilder} or a new one if the given one is
+        *         NULL
+        */
+       static private StringBuilder typesToString(Data data, StringBuilder builder) {
+               if (builder == null)
+                       builder = new StringBuilder();
+
+               for (int indexType = 0; indexType < data.size(); indexType++) {
+                       TypeInfo type = data.get(indexType);
+                       if (builder.length() > 1)
+                               builder.append(", ");
+                       builder.append(type.getName().replaceAll(",", "\\,"));
+                       builder.append(": ");
+                       builder.append(type.getValue().replaceAll(":", "\\:"));
+               }
+
+               return builder;
+       }
 
+       /**
+        * Unserialise a list of {@link TypeInfo}s.
+        * 
+        * @param value
+        *            the serialised value
+        * 
+        * @return the {@link TypeInfo} in their object form
+        */
+       static private List<TypeInfo> stringToTypes(String value) {
+               List<TypeInfo> infos = new LinkedList<TypeInfo>();
+               if (value == null || value.length() == 0)
+                       return infos;
+
+               char previous = '\0';
+               char car = '\0';
+               int done = 0;
+               for (int index = 0; index < value.length(); index++) {
+                       car = value.charAt(index);
+                       if (index == value.length() - 1) {
+                               index++;
+                               previous = '\0';
+                               car = ',';
+                       }
+
+                       if (previous != '\\' && car == ',') {
+                               String[] tab = value.substring(done, index).split("\\:");
+                               infos.add(new TypeInfo( //
+                                               tab[0].replaceAll("\\,", ",").replaceAll("\\:", ":")
+                                                               .trim(), //
+                                               tab[1].replaceAll("\\,", ",").replaceAll("\\:", ":")
+                                                               .trim()));
+                               done = index + 1;
+                       }
+
+                       previous = car;
+               }
+
+               return infos;
+       }
 }
index 6287a69997caf3bcee592ca394b738c83aff70f0..3cec44e0dafcd989e8ac472c562ec023e7ba8b93 100644 (file)
@@ -6,12 +6,13 @@ import java.util.List;
 
 import be.nikiroo.jvcard.Card;
 import be.nikiroo.jvcard.Contact;
+import be.nikiroo.jvcard.Data;
 import be.nikiroo.jvcard.i18n.Trans;
 import be.nikiroo.jvcard.resources.Bundles;
 import be.nikiroo.jvcard.tui.KeyAction;
-import be.nikiroo.jvcard.tui.UiColors;
 import be.nikiroo.jvcard.tui.KeyAction.DataType;
 import be.nikiroo.jvcard.tui.KeyAction.Mode;
+import be.nikiroo.jvcard.tui.UiColors;
 import be.nikiroo.jvcard.tui.UiColors.Element;
 
 import com.googlecode.lanterna.input.KeyType;
@@ -58,7 +59,7 @@ public class ContactList extends MainContentList {
                                if (filter == null
                                                || c.toString(format).toLowerCase()
                                                                .contains(filter.toLowerCase())) {
-                                       addItem("[contact line]");
+                                       addItem("x");
                                        contacts.add(c);
                                }
                        }
@@ -91,12 +92,29 @@ public class ContactList extends MainContentList {
        public List<KeyAction> getKeyBindings() {
                List<KeyAction> actions = new LinkedList<KeyAction>();
 
-               // TODO add
-               actions.add(new KeyAction(Mode.CONTACT_DETAILS_RAW, 'e',
-                               Trans.StringId.KEY_ACTION_EDIT_CONTACT) {
+               // TODO ui
+               actions.add(new KeyAction(Mode.ASK_USER, 'a', Trans.StringId.DUMMY) {
                        @Override
                        public Object getObject() {
-                               return getSelectedContact();
+                               return card;
+                       }
+
+                       @Override
+                       public String getQuestion() {
+                               // TODO i18n
+                               return "new contact name: ";
+                       }
+
+                       @Override
+                       public String callback(String answer) {
+                               if (answer.length() > 0) {
+                                       List<Data> datas = new LinkedList<Data>();
+                                       datas.add(new Data(null, "FN", answer, null));
+                                       getCard().add(new Contact(datas));
+                                       addItem("x");
+                               }
+
+                               return null;
                        }
                });
                actions.add(new KeyAction(Mode.ASK_USER_KEY, 'd',
@@ -117,6 +135,7 @@ public class ContactList extends MainContentList {
                                if (answer.equalsIgnoreCase("y")) {
                                        Contact contact = getSelectedContact();
                                        if (contact != null && contact.delete()) {
+                                               removeItem("x");
                                                return null;
                                        }
 
index e1358eff66f9659ada469ade30dc0a0f97d886d8..4b6d8ad13b2ad40d77d1f0f02caf90b645638210 100644 (file)
@@ -125,6 +125,39 @@ abstract public class MainContentList extends MainContent implements Runnable {
                lines.addItem(line, this);
        }
 
+       /**
+        * Delete the given item.
+        * 
+        * Remark: it will only delete the first found instance if multiple
+        * instances of this item are present.
+        * 
+        * @param line
+        *            the line to delete
+        * 
+        * @return TRUE if the item was deleted
+        */
+       public boolean removeItem(String line) {
+               boolean deleted = false;
+
+               List<Runnable> copy = lines.getItems();
+               for (int index = 0; index < copy.size(); index++) {
+                       if (copy.get(index).toString().equals(line)) {
+                               deleted = true;
+                               copy.remove(index);
+                               break;
+                       }
+               }
+
+               int index = getSelectedIndex();
+               clearItems();
+               for (Runnable run : copy) {
+                       addItem(run.toString());
+               }
+               setSelectedIndex(index);
+               
+               return deleted;
+       }
+
        /**
         * Clear all the items in this {@link MainContentList}
         */