Bug 1319052 - Add F2 key binding to rename directories and mail lists. r=darktrojan
authorAbdallahAfify <AbdallahAfify>
Thu, 23 Apr 2020 23:25:38 +0000
changeset 38035 6a5577377e1dcce5f524bfe68a5024aa68a5dd0e
parent 38034 76857b526d5e0daeee70122d6f46e312738763f8
child 38036 e8fdfd14d5567ea7fc380a84693022ca84a15493
push id2595
push userclokep@gmail.com
push dateMon, 04 May 2020 19:02:04 +0000
treeherdercomm-beta@f53913797371 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdarktrojan
bugs1319052
Bug 1319052 - Add F2 key binding to rename directories and mail lists. r=darktrojan Differential Revision: https://phabricator.services.mozilla.com/D71957
mail/components/addrbook/content/abCommon.js
mail/components/addrbook/content/abTrees.js
mail/components/addrbook/content/addressbook.xhtml
mail/components/addrbook/test/browser/browser_mailing_lists.js
mailnews/addrbook/content/abAddressBookNameDialog.js
mailnews/addrbook/jsaddrbook/AddrBookManager.jsm
mailnews/addrbook/public/nsIAbManager.idl
--- a/mail/components/addrbook/content/abCommon.js
+++ b/mail/components/addrbook/content/abCommon.js
@@ -54,16 +54,17 @@ var DirPaneController = {
       case "cmd_delete":
       case "button_delete":
       case "cmd_properties":
       case "cmd_abToggleStartupDir":
       case "cmd_printcard":
       case "cmd_printcardpreview":
       case "cmd_newlist":
       case "cmd_newCard":
+      case "cmd_rename":
         return true;
       default:
         return false;
     }
   },
 
   isCommandEnabled(command) {
     switch (command) {
@@ -153,16 +154,28 @@ var DirPaneController = {
               e.setAttribute(attr, e.getAttribute(name));
             }
           }
         });
         return enabled;
       }
       case "cmd_abToggleStartupDir":
         return !!getSelectedDirectoryURI();
+      case "cmd_rename":
+        let selectedDirectoryURI = getSelectedDirectoryURI();
+        // Prevent the renaming of Personal Address Book, Collected Addressses and All Address Books directories
+        if (
+          !selectedDirectoryURI ||
+          selectedDirectoryURI == kAllDirectoryRoot + "?" ||
+          selectedDirectoryURI == kPersonalAddressbookURI ||
+          selectedDirectoryURI == kCollectedAddressbookURI
+        ) {
+          return false;
+        }
+        return true;
       case "cmd_newlist":
       case "cmd_newCard":
         return true;
       default:
         return false;
     }
   },
 
@@ -186,16 +199,19 @@ var DirPaneController = {
         abToggleSelectedDirStartup();
         break;
       case "cmd_newlist":
         AbNewList();
         break;
       case "cmd_newCard":
         AbNewCard();
         break;
+      case "cmd_rename":
+        AbRenameDirectory();
+        break;
     }
   },
 
   onEvent(event) {
     // on blur events set the menu item texts back to the normal values
     if (event == "blur") {
       goSetMenuValue("cmd_delete", "valueDefault");
     }
@@ -753,16 +769,23 @@ function ChangeDirectoryByURI(uri = kPer
     ResultsPaneSelectionChanged();
   }
 }
 
 function AbNewList() {
   goNewListDialog(getSelectedDirectoryURI());
 }
 
+function AbRenameDirectory() {
+  gDirTree.startEditing(
+    gDirTree.view.selection.currentIndex,
+    gDirTree.columns.getFirstColumn()
+  );
+}
+
 function goNewListDialog(selectedAB) {
   window.openDialog(
     "chrome://messenger/content/addressbook/abMailListDialog.xhtml",
     "",
     "chrome,modal,resizable=no,centerscreen",
     { selectedAB }
   );
 }
--- a/mail/components/addrbook/content/abTrees.js
+++ b/mail/components/addrbook/content/abTrees.js
@@ -206,16 +206,84 @@ directoryTreeView.prototype = {
   // Override jsTreeView's isContainer, since we want to be able
   // to react to drag-drop events for all items in the directory
   // tree.
   isContainer(aIndex) {
     return true;
   },
 
   /**
+   * Returns true if the selected directory is inline-editable.
+   * @param {int} aRow - the row index of the selected dir/mail list.
+   * @param {int} aCol - not used a directories are represented as rows only.
+   */
+  isEditable(aRow, aCol) {
+    let selectedDirectory = this.getDirectoryAtIndex(aRow);
+
+    // Prevent the renaming of Personal Address Book, Collected Addressses
+    // and All Address Books directories.
+    if (
+      !selectedDirectory ||
+      selectedDirectory.URI == kAllDirectoryRoot + "?" ||
+      selectedDirectory.URI == kPersonalAddressbookURI ||
+      selectedDirectory.URI == kCollectedAddressbookURI
+    ) {
+      return false;
+    }
+    return true;
+  },
+
+  /**
+   * Saves the new name  dir/mail list.
+   * @param {int} aRow - the row index of the selected  dir/mail list.
+   * @param {int} aCol - not used a directories are represented as rows only.
+   * @param {string} aValue - the new name of dir/mail list to be saved.
+   */
+  setCellText(aRow, aCol, aValue) {
+    let selectedDirectory = this.getDirectoryAtIndex(aRow);
+    let newName = aValue.trim();
+
+    // Check if the new name is empty.
+    if (newName.length == 0) {
+      return;
+    }
+
+    // Mailists requires to call the backend to update its name.
+    if (selectedDirectory.isMailList) {
+      // Check if the new name contains 2 spaces.
+      if (newName.match("  ")) {
+        return;
+      }
+
+      // Check if the new name contains the following special characters.
+      for (let char of ',;"<>') {
+        if (newName.includes(char)) {
+          return;
+        }
+      }
+
+      // Do not allow an already existing name.
+      if (MailServices.ab.mailListNameExists(newName)) {
+        return;
+      }
+
+      selectedDirectory.dirName = newName;
+      selectedDirectory.editMailListToDatabase(null);
+    } else {
+      // Do not allow an already existing name.
+      if (MailServices.ab.directoryNameExists(newName)) {
+        return;
+      }
+
+      /* Name is unique, set value */
+      selectedDirectory.dirName = newName;
+    }
+  },
+
+  /**
    * NOTE: This function will result in indeterminate rows being selected.
    *       Callers should take care to re-select a desired row after calling
    *       this function.
    */
   _rebuild() {
     var oldCount = this._rowMap.length;
     this._rowMap = [];
 
--- a/mail/components/addrbook/content/addressbook.xhtml
+++ b/mail/components/addrbook/content/addressbook.xhtml
@@ -112,16 +112,17 @@
     <!-- Mac Window menu -->
     <command id="minimizeWindow" label="&minimizeWindow.label;" oncommand="window.minimize();"/>
     <command id="zoomWindow" label="&zoomWindow.label;" oncommand="zoomWindow();"/>
     <command id="Tasks:Mail" oncommand="toMessengerWindow();"/>
 #endif
     <command id="cmd_CustomizeABToolbar"
              oncommand="CustomizeMailToolbar('ab-toolbox', 'CustomizeABToolbar')"/>
     <command id="cmd_chatWithCard" oncommand="goDoCommand('cmd_chatWithCard')" disabled="true" />
+    <command id="cmd_rename" oncommand="goDoCommand('cmd_rename');" />
 </commandset>
 
 <keyset id="tasksKeys">
   <!-- File Menu -->
 #ifdef XP_MACOSX
   <key id="key_newMessage" key="&newMessageCmd.key;" modifiers="accel,shift" command="cmd_newMessage"/>
 #else
   <key id="key_newMessage" key="&newMessageCmd.key;" modifiers="accel" command="cmd_newMessage"/>
@@ -159,16 +160,17 @@
 #else
   <key id="key_searchAddresses" key="&searchAddressesCmd.key;" oncommand="onAdvancedAbSearch();" modifiers="accel, shift"/>
 #endif
   <key id="key_properties" command="cmd_properties" key="&propertiesCmd.key;" modifiers="accel"/>
 
   <!-- Tab/F6 Keys -->
   <key keycode="VK_TAB" oncommand="SwitchPaneFocus(event);" modifiers="control,shift"/>
   <key keycode="VK_TAB" oncommand="SwitchPaneFocus(event);" modifiers="control"/>
+  <key keycode="VK_F2" command="cmd_rename"/>
   <key keycode="VK_F6" oncommand="SwitchPaneFocus(event);" modifiers="control,shift"/>
   <key keycode="VK_F6" oncommand="SwitchPaneFocus(event);" modifiers="control"/>
   <key keycode="VK_F6" oncommand="SwitchPaneFocus(event);" modifiers="shift"/>
   <key keycode="VK_F6" oncommand="SwitchPaneFocus(event);"/>
 
   <key key="&quickSearchCmd.key;" oncommand="QuickSearchFocus();" modifiers="accel"/>
   <key id="key_mail"  key="&messengerCmd.commandkey;" oncommand="toMessengerWindow();" modifiers="accel"/>
 #ifdef XP_MACOSX
@@ -695,20 +697,21 @@
     <vbox id="dirTreeBox" persist="width collapsed">
       <!-- FIX ME - remove document.commandDispatcher.updateCommands() when tree selection calls this automatically -->
       <tree id="dirTree" class="abDirectory plain" seltype="single" minwidth="150" flex="1" persist="width"
             hidecolumnpicker="true"
             context="dirTreeContext"
             onselect="DirPaneSelectionChange(); document.commandDispatcher.updateCommands('addrbook-select');"
             ondblclick="DirPaneDoubleClick(event);"
             onclick="DirPaneClick(event);"
-            onblur="goOnEvent(this, 'blur')">
+            onblur="goOnEvent(this, 'blur')"
+            editable="true">
 
         <treecols>
-          <treecol id="DirCol" flex="1" primary="true" hideheader="true"
+          <treecol id="DirCol" flex="1" primary="true" hideheader="true" editable="true"
                    crop="center" persist="width" ignoreincolumnpicker="true"/>
         </treecols>
         <treechildren/>
       </tree>
     </vbox>
 
     <splitter id="dirTree-splitter" collapse="before" persist="state"/>
 
--- a/mail/components/addrbook/test/browser/browser_mailing_lists.js
+++ b/mail/components/addrbook/test/browser/browser_mailing_lists.js
@@ -280,18 +280,19 @@ add_task(async () => {
   );
 
   is(
     global.dirTree.view.getCellText(2, global.dirTree.columns[0]),
     inputs.abName,
     `address book ("${inputs.abName}") is displayed in the address book list`
   );
 
-  // Double-click on the address book name to reveal the mailing list.
-  global.dirTreeClick(2, 2);
+  // Expand the tree to reveal the mailing list.
+  global.dirTreeClick(2, 1);
+  EventUtils.sendKey("RETURN", global.abWindow);
 
   is(
     global.dirTree.view.getCellText(3, global.dirTree.columns[0]),
     inputs.mlName,
     `mailing list ("${inputs.mlName}") is displayed in the address book list`
   );
 
   // Open the mailing list dialog, the callback above interacts with it.
--- a/mailnews/addrbook/content/abAddressBookNameDialog.js
+++ b/mailnews/addrbook/content/abAddressBookNameDialog.js
@@ -57,40 +57,35 @@ function abNameOnLoad() {
   } else {
     document.addEventListener("dialogaccept", abNameOKButton);
     gNameInput.focus();
     abNameDoOkEnabling();
   }
 }
 
 function abNameOKButton(event) {
-  var newName = gNameInput.value.trim();
+  let newDirName = gNameInput.value.trim();
 
   // Do not allow an already existing name.
-  for (let ab of MailServices.ab.directories) {
-    if (
-      ab.dirName.toLowerCase() == newName.toLowerCase() &&
-      (!gDirectory || ab.URI != gDirectory.URI)
-    ) {
-      const kAlertTitle = document
-        .getElementById("bundle_addressBook")
-        .getString("duplicateNameTitle");
-      const kAlertText = document
-        .getElementById("bundle_addressBook")
-        .getFormattedString("duplicateNameText", [ab.dirName]);
-      Services.prompt.alert(window, kAlertTitle, kAlertText);
-      event.preventDefault();
-      return;
-    }
+  if (MailServices.ab.directoryNameExists(newDirName)) {
+    const kAlertTitle = document
+      .getElementById("bundle_addressBook")
+      .getString("duplicateNameTitle");
+    const kAlertText = document
+      .getElementById("bundle_addressBook")
+      .getFormattedString("duplicateNameText", [ab.dirName]);
+    Services.prompt.alert(window, kAlertTitle, kAlertText);
+    event.preventDefault();
+    return;
   }
 
   // Either create a new directory or update an existing one depending on what
   // we were given when we started.
   if (gDirectory) {
-    gDirectory.dirName = newName;
+    gDirectory.dirName = newDirName;
   } else {
-    MailServices.ab.newAddressBook(newName, "", kJSDirectory);
+    MailServices.ab.newAddressBook(newDirName, "", kJSDirectory);
   }
 }
 
 function abNameDoOkEnabling() {
   gOkButton.disabled = gNameInput.value.trim() == "";
 }
--- a/mailnews/addrbook/jsaddrbook/AddrBookManager.jsm
+++ b/mailnews/addrbook/jsaddrbook/AddrBookManager.jsm
@@ -440,12 +440,27 @@ AddrBookManager.prototype = {
     ensureInitialized();
     for (let dir of store.values()) {
       if (dir.hasMailListWithName(name)) {
         return true;
       }
     }
     return false;
   },
+
+  /**
+   * Finds out if the directory name already exists.
+   * @param {string} name - The name of a directory to check for.
+   */
+  directoryNameExists(name) {
+    ensureInitialized();
+    for (let dir of store.values()) {
+      if (dir.dirName.toLowerCase() === name.toLowerCase()) {
+        return true;
+      }
+    }
+    return false;
+  },
+
   generateUUID(directoryId, localId) {
     return `${directoryId}#${localId}`;
   },
 };
--- a/mailnews/addrbook/public/nsIAbManager.idl
+++ b/mailnews/addrbook/public/nsIAbManager.idl
@@ -136,16 +136,25 @@ interface nsIAbManager : nsISupports
    *
    * @param  aName      The name of the list to try and find.
    *
    * @return            True if the name exists.
    */
   boolean mailListNameExists(in wstring name);
 
   /**
+   * Finds out if the directory name already exists.
+   *
+   * @param  aName      The name of a directory to check for.
+   *
+   * @return            True if a directory called name already exists.
+   */
+  boolean directoryNameExists(in wstring name);
+
+  /**
    * Generates a UUID from a (directory ID, local ID) tuple.
    *
    * Use of this method is preferred in such cases, since it is designed to work
    * with other methods of this interface.
    *
    * @param directoryId The directory ID.
    * @param localId     The per-directory ID.
    * @return            A string to use for the UUID.