Bug 978592 - offer the flat folder views of Unread and Favorites again that got removed in bug 533775. ui-r=pending, r=mkmelin, a=Standard8 (and for CLOSED TREE)
authoraceman <acelists@atlas.sk>
Tue, 29 Apr 2014 14:02:59 +0100
changeset 19964 c23b118f9dafbb29e90d78b7ee24f350c7c87a04
parent 19963 a184d0a8e93c696a0d8097daf8059366eabe7b09
child 19965 6d6550f3e91a5cd10c55837017e3a56dd3945835
push id1151
push usermbanner@mozilla.com
push dateMon, 09 Jun 2014 22:14:36 +0000
treeherdercomm-beta@ce127428ad7d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspending, mkmelin, Standard8
bugs978592, 533775
Bug 978592 - offer the flat folder views of Unread and Favorites again that got removed in bug 533775. ui-r=pending, r=mkmelin, a=Standard8 (and for CLOSED TREE)
mail/base/content/folderPane.js
mail/base/content/mailWindowOverlay.js
mail/base/content/mailWindowOverlay.xul
mail/locales/en-US/chrome/messenger/messenger.dtd
mail/locales/en-US/chrome/messenger/messenger.properties
--- a/mail/base/content/folderPane.js
+++ b/mail/base/content/folderPane.js
@@ -89,16 +89,31 @@ let IFolderTreeMode = {
    * example, combined views like Smart Folders add any new inbox as a child of
    * the smart inbox.
    *
    * @param aParent The parent of the folder that was added.
    * @param aFolder The folder that was added.
    */
   onFolderAdded: function IFolderTreeMode_onFolderAdded(aParent, aFolder) {
     gFolderTreeView.addFolder(aParent, aFolder);
+  },
+
+  /**
+   * Notified when a folder int property is changed.
+   *
+   * Returns true if the event was processed inside the function and no further
+   * default handling should be done in the caller. Otherwise false.
+   *
+   * @param aItem      The folder with a change.
+   * @param aProperty  The changed property string.
+   * @param aOld       The old value of the property.
+   * @param aNew       The new value of the property.
+   */
+  handleChangedIntProperty: function(aItem, aProperty, aOld, aNew) {
+    return false;
   }
 };
 
 /**
  * This is our controller for the folder-tree. It includes our nsITreeView
  * implementation, as well as other control functions.
  */
 let gFolderTreeView = {
@@ -164,16 +179,17 @@ let gFolderTreeView = {
           Components.utils.reportError(
             document.getElementById("bundle_messenger")
                     .getFormattedString("failedToReadFile", [aJSONFile, x]));
         }
       }
     }
 
     // Load our data
+    this._updateCompactState(this.mode);
     this._rebuild();
     // And actually draw the tree
     aTree.view = this;
 
     // Add this listener so that we can update the tree when things change
     MailServices.mailSession.AddFolderListener(this, Ci.nsIFolderListener.all);
   },
 
@@ -280,46 +296,130 @@ let gFolderTreeView = {
   getFolderAtCoords: function ftv_getFolderAtCoords(aX, aY) {
     let row = gFolderTreeView._treeElement.treeBoxObject.getRowAt(aX, aY);
     if (row in gFolderTreeView._rowMap)
       return gFolderTreeView._rowMap[row]._folder;
     return null;
   },
 
   /**
+   * Toggles the compact view of the current more.
+   *
+   * @param aCompact  Boolean telling whether compact view should be enabled.
+   */
+  toggleCompact: function(aCompact) {
+    let targetMode = this.fullMode(this.baseMode(), aCompact);
+    this._updateCompactState(targetMode);
+    this.mode = targetMode;
+  },
+
+  /**
+   * Update state of checkboxes according to currently selected mode.
+   * Synchronize the state of our 2 "compact" menuitems and decide if they
+   * should be disabled.
+   *
+   * @param aMode  The current folder mode.
+   */
+  _updateCompactState: function(aMode) {
+    let checked = aMode.endsWith("_compact");
+    let menuitem = document.getElementById("menu_compactFolderView");
+    let appmenuitem = document.getElementById("appmenu_compactFolderView");
+    if (checked) {
+      menuitem.setAttribute("checked", "true");
+      appmenuitem.setAttribute("checked", "true");
+    } else {
+      menuitem.removeAttribute("checked");
+      appmenuitem.removeAttribute("checked");
+    }
+    let baseMode = this.baseMode(aMode);
+    let compactToggleable = (baseMode in this._modes) &&
+                            (this.fullMode(baseMode, true) in this._modes);
+    menuitem.disabled = !compactToggleable;
+    appmenuitem.disabled = !compactToggleable;
+  },
+
+  /**
    * A string representation for the current display-mode.  Each value here must
    * correspond to an entry in _modes
    */
   _mode: null,
   get mode() {
     if (!this._mode) {
       this._mode = this._treeElement.getAttribute("mode");
       // this can happen when an extension is removed
       if (!(this._mode in this._modes))
         this._mode = kDefaultMode;
     }
     return this._mode;
   },
   set mode(aMode) {
-    this._mode = aMode;
+    // If a mode name ending with "_compact" is chosen, obey that mode.
+    // However, if a name without "_compact" is chosen, check the state of the
+    // "Compact" checkbox in the UI whether the user actually wanted the compact version.
+    let userMode = aMode;
+    if (!userMode.endsWith("_compact")) {
+      userMode = this.fullMode(aMode,
+        document.getElementById("menu_compactFolderView").hasAttribute("checked"));
+    }
+    // Some combinations of user selection and "Compact view" checkbox are not supported.
+    // In that case fall back to a version of this mode that exists.
+    if (userMode in this._modes) {
+      this._mode = userMode;
+    } else {
+      let baseMode = this.baseMode(aMode);
+      if (baseMode in this._modes)
+        this._mode = baseMode;
+      else
+        this._mode = this.fullMode(baseMode, true);
+    }
+    this._updateCompactState(this._mode);
 
     let string;
     if (this._mode in this._modeDisplayNames)
       string = this._modeDisplayNames[this._mode];
     else {
-      let key = "folderPaneModeHeader_" + aMode;
+      let key = "folderPaneModeHeader_" + this._mode;
       string = document.getElementById("bundle_messenger").getString(key);
     }
     document.getElementById('folderpane-title').value = string;
 
-    this._treeElement.setAttribute("mode", aMode);
+    this._treeElement.setAttribute("mode", this._mode);
     this._rebuild();
   },
 
   /**
+   * Name of the mode without the _compact suffix, used e.g. in the menulists.
+   *
+   * @param aMode  If set, construct the base name from this mode name instead
+   *               of the currently active one.
+   */
+  baseMode: function(aMode) {
+    if (!aMode)
+      aMode = this.mode;
+
+    return aMode.replace(/_compact$/, "");
+  },
+
+  /**
+   * Name of the mode including the _compact suffix if appropriate.
+   *
+   * @param aMode  If set, construct the base name from this mode name instead
+   *               of the currently active one.
+   * @param aCOmpact  Bool value whether to force adding the suffix or not.
+   */
+  fullMode: function(aMode, aCompact) {
+    if (!aMode)
+      aMode = this.mode;
+    if (aCompact == undefined)
+      aCompact = aMode.endsWith("_compact");
+
+    return this.baseMode(aMode) + (aCompact ? "_compact" : "");
+  },
+
+  /**
    * Selects a given nsIMsgFolder in the tree.  This function will also ensure
    * that the folder is actually being displayed (that is, that none of its
    * ancestors are collapsed.
    *
    * @param aFolder  the nsIMsgFolder to select
    * @param [aForceSelect] Whether we should switch to the default mode to
    *      select the folder in case we didn't find the folder in the current
    *      view. Defaults to false.
@@ -1104,26 +1204,32 @@ let gFolderTreeView = {
 
   //
   // WARNING: Everything below this point is considered private.  Touch at your
   //          own risk.
 
   /**
    * This is an array of all possible modes for the folder tree. You should not
    * modify this directly, but rather use registerFolderTreeMode.
+   *
+   * Internally each mode is defined separatelly. But in the UI we currently expose
+   * only the "base" name (see baseMode()) of the mode plus a "Compact view" option
+   * The internal name of the mode to use is then constructed from the base name
+   * and "_compact" suffix if compact view is selected. See bug 978592.
+   *
    */
-  _modeNames: ["all", "unread", "favorite", "recent", "smart"],
+  _modeNames: ["all", "unread", "unread_compact", "favorite", "favorite_compact", "recent_compact", "smart"],
   _modeDisplayNames: {},
 
   /**
    * This is a javascript map of which folders we had open, so that we can
    * persist their state over-time.  It is designed to be used as a JSON object.
    */
   _persistOpenMap: {},
-  _notPersistedModes: ["unread", "favorite", "recent"],
+  _notPersistedModes: ["unread", "unread_compact", "favorite", "favorite_compact", "recent_compact"],
 
   /**
    * Iterate over the persistent list and open the items (folders) stored in it.
    */
   _restoreOpenStates: function ftv__persistOpenStates() {
     let mode = this.mode;
     // Remove any saved state of modes where open state should not be persisted.
     // This is mostly for migration from older profiles that may have the info stored.
@@ -1205,17 +1311,17 @@ let gFolderTreeView = {
    * settings
    */
   _rebuild: function ftv__rebuild() {
     let newRowMap;
     try {
       newRowMap = this._modes[this.mode].generateMap(this);
     } catch(ex) {
       Services.console.logStringMessage("generator " + this.mode + " failed with exception: " + ex);
-      this.mode = "all";
+      this.mode = kDefaultMode;
       newRowMap = this._modes[this.mode].generateMap(this);
     }
     let selectedFolders = this.getSelectedFolders();
     if (this.selection)
       this.selection.clearSelection();
     // There's a chance the call to the map generator altered this._rowMap, so
     // evaluate oldCount after calling it rather than before
     let oldCount = this._rowMap ? this._rowMap.length : null;
@@ -1274,18 +1380,20 @@ let gFolderTreeView = {
 
         return [new ftvItem(acct.incomingServer.rootFolder)
                 for each (acct in accounts)];
       }
     },
 
     /**
      * The unread mode returns all folders that are not root-folders and that
-     * have unread items.  Also always keep the currently selected folder
+     * have unread items. Also always keep the currently selected folder
      * so it doesn't disappear under the user.
+     * It also includes parent folders of the Unread folders so the hierarchy
+     * shown.
      */
     unread: {
       __proto__: IFolderTreeMode,
 
       generateMap: function ftv_unread_generateMap(ftv) {
         let filterUnread = function filterUnread(aFolder) {
           let currentFolder = gFolderTreeView.getSelectedFolders()[0];
           const outFolderFlagMask = nsMsgFolderFlags.SentMail |
@@ -1304,22 +1412,81 @@ let gFolderTreeView = {
         for (let acct of accounts) {
           let rootFolder = acct.incomingServer.rootFolder;
           // Add rootFolders of accounts that contain at least one Favorite folder.
           if (rootFolder.getNumUnread(true) > 0)
             unreadRootFolders.push(new ftvItem(rootFolder, filterUnread));
         }
 
         return unreadRootFolders;
+      },
+
+      handleChangedIntProperty: function(aItem, aProperty, aOld, aNew) {
+        // We want to rebuild only if we have a newly unread folder
+        // and we didn't already have the folder.
+        if (aProperty == "TotalUnreadMessages" && aOld == 0 && aNew > 0 &&
+            this.getIndexOfFolder(aItem) == null) {
+          gFolderTreeView._rebuild();
+          return true;
+        }
+        return false;
+      }
+    },
+
+    /**
+     * A variant of the 'unread' mode above. This does not include the parent folders
+     * and the unread folders are shown in a flat list with no hierarchy.
+     */
+    unread_compact: {
+      __proto__: IFolderTreeMode,
+
+      generateMap: function(ftv) {
+        let map = [];
+        let currentFolder = gFolderTreeView.getSelectedFolders()[0];
+        const outFolderFlagMask = nsMsgFolderFlags.SentMail |
+          nsMsgFolderFlags.Drafts | nsMsgFolderFlags.Queue |
+          nsMsgFolderFlags.Templates;
+        for (let folder of ftv._enumerateFolders) {
+          if (!folder.isSpecialFolder(outFolderFlagMask, true) &&
+              (!folder.isServer && folder.getNumUnread(false) > 0) ||
+              (folder == currentFolder))
+            map.push(new ftvItem(folder));
+        }
+
+        // There are no children in this view!
+        for (let folder of map) {
+          folder.__defineGetter__("children", function() []);
+          folder.addServerName = true;
+        }
+        sortFolderItems(map);
+        return map;
+      },
+
+      getParentOfFolder: function(aFolder) {
+        // This is a flat view, so no folders have parents.
+        return null;
+      },
+
+      handleChangedIntProperty: function(aItem, aProperty, aOld, aNew) {
+        // We want to rebuild only if we have a newly unread folder
+        // and we didn't already have the folder.
+        if (aProperty == "TotalUnreadMessages" && aOld == 0 && aNew > 0 &&
+            this.getIndexOfFolder(aItem) == null) {
+          gFolderTreeView._rebuild();
+          return true;
+        }
+        return false;
       }
     },
 
     /**
      * The favorites mode returns all folders whose flags are set to include
      * the favorite flag.
+     * It also includes parent folders of the Unread folders so the hierarchy
+     * shown.
      */
     favorite: {
       __proto__: IFolderTreeMode,
 
       generateMap: function ftv_favorite_generateMap(ftv) {
         let accounts = gFolderTreeView._sortedAccounts();
         // Force each root folder to do its local subfolder discovery.
         MailUtils.discoverFolders();
@@ -1331,23 +1498,89 @@ let gFolderTreeView = {
         for (let acct of accounts) {
           let rootFolder = acct.incomingServer.rootFolder;
           // Add rootFolders of accounts that contain at least one Favorite folder.
           if (filterFavorite(rootFolder))
             favRootFolders.push(new ftvItem(rootFolder, filterFavorite));
         }
 
         return favRootFolders;
+      },
+
+      handleChangedIntProperty: function(aItem, aProperty, aOld, aNew) {
+        // We want to rebuild if the favorite status of a folder changed.
+        if (aProperty == "FolderFlag" &&
+            ((aOld & Components.interfaces.nsMsgFolderFlags.Favorite) !=
+            (aNew & Components.interfaces.nsMsgFolderFlags.Favorite))) {
+          gFolderTreeView._rebuild();
+          return true;
+        }
+        return false;
       }
     },
 
-    recent: {
+    /**
+     * A variant of the 'favorite' mode above. This does not include the parent folders
+     * and the unread folders are shown in a compact list with no hierarchy.
+     */
+    favorite_compact: {
       __proto__: IFolderTreeMode,
 
-      generateMap: function ftv_recent_generateMap(ftv) {
+      generateMap: function(ftv) {
+        let faves = [];
+        for (let folder of ftv._enumerateFolders) {
+          if (folder.getFlag(nsMsgFolderFlags.Favorite))
+            faves.push(new ftvItem(folder));
+        }
+
+        // We want to display the account name alongside folders that have
+        // duplicated folder names.
+        let uniqueNames = new Set(); // set of folder names seen at least once
+        let dupeNames = new Set(); // set of folders seen at least twice
+        for (let item of faves) {
+          let name = item._folder.abbreviatedName.toLocaleLowerCase();
+          if (uniqueNames.has(name)) {
+            if (!dupeNames.has(name))
+              dupeNames.add(name);
+          } else {
+            uniqueNames.add(name);
+          }
+        }
+
+        // There are no children in this view!
+        for (let item of faves) {
+          let name = item._folder.abbreviatedName.toLocaleLowerCase();
+          item.__defineGetter__("children", function() []);
+          item.addServerName = dupeNames.has(name);
+        }
+        sortFolderItems(faves);
+        return faves;
+      },
+
+      getParentOfFolder: function(aFolder) {
+        // This is a flat view, so no folders have parents.
+        return null;
+      },
+
+      handleChangedIntProperty: function(aItem, aProperty, aOld, aNew) {
+        // We want to rebuild if the favorite status of a folder changed.
+        if (aProperty == "FolderFlag" &&
+            ((aOld & Components.interfaces.nsMsgFolderFlags.Favorite) !=
+            (aNew & Components.interfaces.nsMsgFolderFlags.Favorite))) {
+          gFolderTreeView._rebuild();
+          return true;
+        }
+        return false;
+      }
+    },
+
+    recent_compact: {
+      __proto__: IFolderTreeMode,
+
+      generateMap: function(ftv) {
         const MAXRECENT = 15;
 
         // Get 15 (MAXRECENT) most recently accessed folders.
         let recentFolders = getMostRecentFolders(ftv._enumerateFolders,
                                                  MAXRECENT,
                                                  "MRUTime",
                                                  null);
 
@@ -1370,17 +1603,17 @@ let gFolderTreeView = {
         for (let folder of items) {
           folder.__defineGetter__("children", function() []);
           folder.addServerName = true;
         }
 
         return items;
       },
 
-      getParentOfFolder: function ftv_unread_getParentOfFolder(aFolder) {
+      getParentOfFolder: function(aFolder) {
         // This is a flat view, so no folders have parents.
         return null;
       }
     },
 
     /**
      * The smart folder mode combines special folders of a particular type
      * across accounts into a single cross-folder saved search.
@@ -1829,29 +2062,19 @@ let gFolderTreeView = {
     }
     this._rowMap.splice(index, kidCount);
     this._tree.rowCountChanged(index, -1 * kidCount);
     this._tree.invalidateRow(index);
   },
 
   OnItemPropertyChanged: function(aItem, aProperty, aOld, aNew) {},
   OnItemIntPropertyChanged: function(aItem, aProperty, aOld, aNew) {
-    // We want to rebuild only if we're in unread mode, and we have a
-    // newly unread folder, and we didn't already have the folder.
-    // Or if we are in favorite view and the favorite status of a folder changed.
-    if ((this._mode == "unread" &&
-         aProperty == "TotalUnreadMessages" && aOld == 0 &&
-         this.getIndexOfFolder(aItem) == null) ||
-        (this._mode == "favorite" && aProperty == "FolderFlag" &&
-         (aOld & Components.interfaces.nsMsgFolderFlags.Favorite) !=
-         (aNew & Components.interfaces.nsMsgFolderFlags.Favorite)))
-    {
-      this._rebuild();
+    // First try mode specific handling of the changed property.
+    if (this._modes[this.mode].handleChangedIntProperty(aItem, aProperty, aOld, aNew))
       return;
-    }
 
     if (aItem instanceof Components.interfaces.nsIMsgFolder) {
       let index = this.getIndexOfFolder(aItem);
       let folder = aItem;
       let folderTreeMode = this._modes[this._mode];
       // look for first visible ancestor
       while (index == null) {
         folder = folderTreeMode.getParentOfFolder(folder);
--- a/mail/base/content/mailWindowOverlay.js
+++ b/mail/base/content/mailWindowOverlay.js
@@ -263,17 +263,17 @@ function InitViewLayoutStyleMenu(event)
     layoutStyleMenuitem.setAttribute("checked", "true");
 }
 
 /**
  * Initialize (check) appropriate folder mode under the View | Folder menu.
  */
 function InitViewFolderViewsMenu(event)
 {
-  let selected = event.target.querySelector("[value=" + gFolderTreeView.mode + "]");
+  let selected = event.target.querySelector("[value=" + gFolderTreeView.baseMode() + "]");
   if (selected) {
     selected.setAttribute("checked", "true");
   }
 }
 
 function setSortByMenuItemCheckState(id, value)
 {
   var menuitem = document.getElementById(id);
--- a/mail/base/content/mailWindowOverlay.xul
+++ b/mail/base/content/mailWindowOverlay.xul
@@ -1214,16 +1214,21 @@
                       name="viewmessages"
                       oncommand="gFolderTreeView.mode = this.value;"/>
             <menuitem id="appmenu_recentFolders"
                       value="recent"
                       label="&recentFolders.label;"
                       type="radio"
                       name="viewmessages"
                       oncommand="gFolderTreeView.mode = this.value;"/>
+            <menuseparator id="appmenu_compactViewSeparator"/>
+            <menuitem id="appmenu_compactFolderView"
+                      label="&compactVersion.label;"
+                      type="checkbox"
+                      oncommand="gFolderTreeView.toggleCompact(this.hasAttribute('checked'));"/>
             <menuseparator id="appmenu_favoritePropertiesSeparator"/>
             <menuitem id="appmenu_favoriteFolder"
                       type="checkbox"
                       label="&menuFavoriteFolder.label;"
                       check="false"
                       oncommand="ToggleFavoriteFolderFlag();"/>
             <menuitem id="appmenu_properties"
                       label="&folderPropsCmd2.label;"
@@ -2370,16 +2375,22 @@
                       accesskey="&favoriteFolders.accesskey;"
                       type="radio" name="viewmessages"
                       oncommand="gFolderTreeView.mode = this.value;"/>
             <menuitem id="menu_recentFolders" value="recent"
                       label="&recentFolders.label;"
                       accesskey="&recentFolders.accesskey;"
                       type="radio" name="viewmessages"
                       oncommand="gFolderTreeView.mode = this.value;"/>
+            <menuseparator id="menu_compactViewSeparator"/>
+            <menuitem id="menu_compactFolderView"
+                      label="&compactVersion.label;"
+                      accesskey="&compactVersion.accesskey;"
+                      type="checkbox"
+                      oncommand="gFolderTreeView.toggleCompact(this.hasAttribute('checked'));"/>
           </menupopup>
         </menu>
         <menuseparator id="viewSortMenuSeparator"/>
         <menu id="viewSortMenu" accesskey="&sortMenu.accesskey;" label="&sortMenu.label;">
           <menupopup id="menu_viewSortPopup" onpopupshowing="InitViewSortByMenu()">
             <menuitem id="sortByDateMenuitem" type="radio" name="sortby" label="&sortByDateCmd.label;" accesskey="&sortByDateCmd.accesskey;" oncommand="MsgSortThreadPane('byDate')"/>
             <menuitem id="sortByReceivedMenuitem" type="radio" name="sortby" label="&sortByReceivedCmd.label;" accesskey="&sortByReceivedCmd.accesskey;" oncommand="MsgSortThreadPane('byReceived')"/>
             <menuitem id="sortByFlagMenuitem" type="radio" name="sortby" label="&sortByStarCmd.label;" accesskey="&sortByStarCmd.accesskey;" oncommand="MsgSortThreadPane('byFlagged')"/>
--- a/mail/locales/en-US/chrome/messenger/messenger.dtd
+++ b/mail/locales/en-US/chrome/messenger/messenger.dtd
@@ -155,16 +155,18 @@
 <!ENTITY allFolders.label "All">
 <!ENTITY allFolders.accesskey "A">
 <!ENTITY unreadFolders.label "Unread">
 <!ENTITY unreadFolders.accesskey "U">
 <!ENTITY favoriteFolders.label "Favorite">
 <!ENTITY favoriteFolders.accesskey "F">
 <!ENTITY recentFolders.label "Recent">
 <!ENTITY recentFolders.accesskey "R">
+<!ENTITY compactVersion.label "Compact View">
+<!ENTITY compactVersion.accesskey "C">
 
 <!-- Sort Menu -->
 <!ENTITY sortMenu.label "Sort by">
 <!ENTITY sortMenu.accesskey "S">
 <!ENTITY sortByDateCmd.label "Date">
 <!ENTITY sortByDateCmd.accesskey "e">
 <!ENTITY sortByReceivedCmd.label "Received">
 <!ENTITY sortByReceivedCmd.accesskey "v">
--- a/mail/locales/en-US/chrome/messenger/messenger.properties
+++ b/mail/locales/en-US/chrome/messenger/messenger.properties
@@ -502,18 +502,20 @@ updatesItem_resumeFallback=Resume Downloading Update…
 updatesItem_resume.accesskey=D
 updatesItem_pending=Apply Downloaded Update Now…
 updatesItem_pendingFallback=Apply Downloaded Update Now…
 updatesItem_pending.accesskey=D
 
 # Folder Pane Header Title Strings
 folderPaneModeHeader_all=All Folders
 folderPaneModeHeader_unread=Unread Folders
+folderPaneModeHeader_unread_compact=Unread Folders - Compact View
 folderPaneModeHeader_favorite=Favorite Folders
-folderPaneModeHeader_recent=Recent Folders
+folderPaneModeHeader_favorite_compact=Favorite Folders - Compact View
+folderPaneModeHeader_recent_compact=Recent Folders - Compact View
 folderPaneModeHeader_smart=Unified Folders
 unifiedAccountName=Unified Folders
 
 # Copy / Move to Folder Again
 #LOCALIZATION NOTE %1$S is the name of the folder we will move to. moveToFolderAgainAccessKey
 # should have the same value as copyToFolderAgainAccessKey as they are the same menu item in the UI
 # moveToFolderAgainAccessKey should also be a letter that occurs before %1$S
 moveToFolderAgain=Move to "%1$S" Again