_onDrop()

in mail/base/content/about3Pane.js [3275:3483]


  _onDrop(event) {
    this._timedExpand();
    this._clearDropTarget();
    this._autoExpandedRows.length = 0;
    if (event.dataTransfer.dropEffect == "none") {
      // Somehow this is possible. It should not be possible.
      return;
    }

    const row = event.target.closest("li");
    if (!row) {
      return;
    }

    const targetFolder = MailServices.folderLookup.getFolderForURL(row.uri);

    const types = Array.from(event.dataTransfer.mozTypesAt(0));
    if (types.includes("text/x-moz-message")) {
      const array = [];
      let sourceFolder;
      for (let i = 0; i < event.dataTransfer.mozItemCount; i++) {
        const msgHdr = top.messenger.msgHdrFromURI(
          event.dataTransfer.mozGetDataAt("text/x-moz-message", i)
        );
        if (!i) {
          sourceFolder = msgHdr.folder;
        }
        array.push(msgHdr);
      }
      let isMove = event.dataTransfer.dropEffect == "move";
      const isNews = sourceFolder.flags & Ci.nsMsgFolderFlags.Newsgroup;
      if (!sourceFolder.canDeleteMessages || isNews) {
        isMove = false;
      }

      Services.prefs.setStringPref(
        "mail.last_msg_movecopy_target_uri",
        targetFolder.URI
      );
      Services.prefs.setBoolPref("mail.last_msg_movecopy_was_move", isMove);
      // ### ugh, so this won't work with cross-folder views. We would
      // really need to partition the messages by folder.
      if (isMove) {
        dbViewWrapperListener.threadPaneCommandUpdater.updateNextMessageAfterDelete();
      }
      MailServices.copy.copyMessages(
        sourceFolder,
        array,
        targetFolder,
        isMove,
        null,
        top.msgWindow,
        true
      );
    } else if (types.includes("text/x-moz-folder")) {
      const rows = [];
      let isMove = event.dataTransfer.dropEffect == "move";
      if (event.dataTransfer.mozItemCount == 1) {
        // Only one folder was dragged and dropped.
        // If the dropped Y-coordinate is near the center of the targetFolder,
        // simply move it into the targetFolder. Otherwise, reorder the dropped
        // folder above or below the targetFolder.

        const sourceFolder = event.dataTransfer
          .mozGetDataAt("text/x-moz-folder", 0)
          .QueryInterface(Ci.nsIMsgFolder);

        let destinationFolder = targetFolder;

        let isReordering = false;
        let insertAfter = false;
        // Only allow moving a folder in "all" mode, otherwise it would be
        // impossible to reorder folders unambiguously.
        if (
          isMove &&
          targetFolder.parent &&
          sourceFolder.server == targetFolder.server &&
          !targetFolder.isServer &&
          row.modeName == "all"
        ) {
          const { center, quarterOfHeight } = this._calculateElementHeight(row);
          const upperElementEnd = event.clientY < center - quarterOfHeight;
          const lowerElementEndWithoutChildren =
            event.clientY > center + quarterOfHeight &&
            (!row.classList.contains("children") ||
              row.classList.contains("collapsed"));
          isReordering = upperElementEnd || lowerElementEndWithoutChildren;
          insertAfter = lowerElementEndWithoutChildren;
          if (isReordering) {
            // To insert the sourceFolder before or after the targetFolder,
            // we have to transfer sourceFolder to the parent of targetFolder
            // as a sibling of targetFolder. If it is the same as the current
            // parent, there is no need to perform the transferFolder, so let
            // destinationFolder be null.
            destinationFolder =
              targetFolder.parent != sourceFolder.parent
                ? targetFolder.parent
                : null;
          }
        }

        if (destinationFolder) {
          // Move sourceFolder to a different parent.

          // Reset the sort order of sourceFolder before moving it.
          sourceFolder.userSortOrder = Ci.nsIMsgFolder.NO_SORT_VALUE;
          // Start the move. This is done in an asynchronous process, so order
          // them in the listener that will be called when the move is complete.
          isMove = folderPaneContextMenu.transferFolder(
            isMove,
            sourceFolder,
            destinationFolder,
            isReordering
              ? new ReorderFolderListener(
                  sourceFolder,
                  targetFolder,
                  insertAfter
                )
              : null
          );

          // Save in prefs the destination folder URI and if this was a move
          // or copy.
          // This is to fill in the next folder or message context menu item
          // "Move|Copy to <DestinationFolderName> Again".
          Services.prefs.setStringPref(
            "mail.last_msg_movecopy_target_uri",
            destinationFolder.URI
          );
        } else if (isReordering) {
          // Reorder within current siblings.
          this.insertFolder(sourceFolder, targetFolder, insertAfter);
          if (folderTree.selection.has(folderTree.rows.indexOf(row))) {
            rows.push(this.getRowForFolder(sourceFolder.URI, row.modeName));
          }
        }
        Services.prefs.setBoolPref("mail.last_msg_movecopy_was_move", isMove);
      } else {
        // FIXME! Bug 1896531.
        console.warn(
          "Bug 1896531. Copy and move for multiselection is only partially supported and it might fail."
        );

        for (let i = 0; i < event.dataTransfer.mozItemCount; i++) {
          const sourceFolder = event.dataTransfer
            .mozGetDataAt("text/x-moz-folder", i)
            .QueryInterface(Ci.nsIMsgFolder);

          isMove = folderPaneContextMenu.transferFolder(
            isMove,
            sourceFolder,
            targetFolder
          );
          rows.push(this.getRowForFolder(sourceFolder.URI, row.modeName));
        }
        // Save in prefs the target folder URI and if this was a move or copy.
        // This is to fill in the next folder or message context menu item
        // "Move|Copy to <TargetFolderName> Again".
        Services.prefs.setStringPref(
          "mail.last_msg_movecopy_target_uri",
          targetFolder.URI
        );
        Services.prefs.setBoolPref("mail.last_msg_movecopy_was_move", isMove);
      }
      this.swapFolderSelection(rows);
    } else if (types.includes("application/x-moz-file")) {
      for (let i = 0; i < event.dataTransfer.mozItemCount; i++) {
        const extFile = event.dataTransfer
          .mozGetDataAt("application/x-moz-file", i)
          .QueryInterface(Ci.nsIFile);
        if (extFile.isFile() && /\.eml$/i.test(extFile.leafName)) {
          MailServices.copy.copyFileMessage(
            extFile,
            targetFolder,
            null,
            false,
            1,
            "",
            null,
            top.msgWindow
          );
        }
      }
    } else if (types.includes("text/x-moz-newsfolder")) {
      const rows = [];
      for (let i = 0; i < event.dataTransfer.mozItemCount; i++) {
        const folder = event.dataTransfer
          .mozGetDataAt("text/x-moz-newsfolder", i)
          .QueryInterface(Ci.nsIMsgFolder);

        const newsRoot = targetFolder.rootFolder.QueryInterface(
          Ci.nsIMsgNewsFolder
        );
        newsRoot.reorderGroup(folder, targetFolder);
        rows.push(this.getRowForFolder(folder, row.modeName));
      }
      this.swapFolderSelection(rows);
    } else if (
      types.includes("text/x-moz-url-data") ||
      types.includes("text/x-moz-url")
    ) {
      // This is a potential rss feed. A link image as well as link text url
      // should be handled; try to extract a url from non moz apps as well.
      const feedURI = FeedUtils.getFeedUriFromDataTransfer(event.dataTransfer);
      FeedUtils.subscribeToFeed(feedURI.spec, targetFolder);
    }

    event.preventDefault();
  },