NS_IMETHODIMP
nsHTMLEditorEventListener::MouseDown(nsIDOMEvent* aMouseEvent)
{
  NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_AVAILABLE);

  nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
  if (!mouseEvent) {
    //non-ui event passed in.  bad things.
    return NS_OK;
  }

  nsHTMLEditor* htmlEditor = GetHTMLEditor();

  // Detect only "context menu" click
  //XXX This should be easier to do!
  // But eDOMEvents_contextmenu and NS_CONTEXTMENU is not exposed in any event interface :-(
  PRUint16 buttonNumber;
  nsresult res = mouseEvent->GetButton(&buttonNumber);
  NS_ENSURE_SUCCESS(res, res);

  PRBool isContextClick = buttonNumber == 2;

  PRInt32 clickCount;
  res = mouseEvent->GetDetail(&clickCount);
  NS_ENSURE_SUCCESS(res, res);

  nsCOMPtr<nsIDOMEventTarget> target;
  nsCOMPtr<nsIDOMNSEvent> internalEvent = do_QueryInterface(aMouseEvent);
  res = internalEvent->GetExplicitOriginalTarget(getter_AddRefs(target));
  NS_ENSURE_SUCCESS(res, res);
  NS_ENSURE_TRUE(target, NS_ERROR_NULL_POINTER);
  nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);

  if (isContextClick || (buttonNumber == 0 && clickCount == 2))
  {
    nsCOMPtr<nsISelection> selection;
    mEditor->GetSelection(getter_AddRefs(selection));
    NS_ENSURE_TRUE(selection, NS_OK);

    // Get location of mouse within target node
    nsCOMPtr<nsIDOMNSUIEvent> uiEvent = do_QueryInterface(aMouseEvent);
    NS_ENSURE_TRUE(uiEvent, NS_ERROR_FAILURE);

    nsCOMPtr<nsIDOMNode> parent;
    PRInt32 offset = 0;

    res = uiEvent->GetRangeParent(getter_AddRefs(parent));
    NS_ENSURE_SUCCESS(res, res);
    NS_ENSURE_TRUE(parent, NS_ERROR_FAILURE);

    res = uiEvent->GetRangeOffset(&offset);
    NS_ENSURE_SUCCESS(res, res);

    // Detect if mouse point is within current selection for context click
    PRBool nodeIsInSelection = PR_FALSE;
    if (isContextClick)
    {
      PRBool isCollapsed;
      selection->GetIsCollapsed(&isCollapsed);
      if (!isCollapsed)
      {
        PRInt32 rangeCount;
        res = selection->GetRangeCount(&rangeCount);
        NS_ENSURE_SUCCESS(res, res);

        for (PRInt32 i = 0; i < rangeCount; i++)
        {
          nsCOMPtr<nsIDOMRange> range;

          res = selection->GetRangeAt(i, getter_AddRefs(range));
          if (NS_FAILED(res) || !range) 
            continue;//don't bail yet, iterate through them all

          nsCOMPtr<nsIDOMNSRange> nsrange(do_QueryInterface(range));
          if (NS_FAILED(res) || !nsrange) 
            continue;//don't bail yet, iterate through them all

          res = nsrange->IsPointInRange(parent, offset, &nodeIsInSelection);

          // Done when we find a range that we are in
          if (nodeIsInSelection)
            break;
        }
      }
    }
    nsCOMPtr<nsIDOMNode> node = do_QueryInterface(target);
    if (node && !nodeIsInSelection)
    {
      if (!element)
      {
        if (isContextClick)
        {
          // Set the selection to the point under the mouse cursor:
          selection->Collapse(parent, offset);
        }
        else
        {
          // Get enclosing link if in text so we can select the link
          nsCOMPtr<nsIDOMElement> linkElement;
          res = htmlEditor->GetElementOrParentByTagName(NS_LITERAL_STRING("href"), node, getter_AddRefs(linkElement));
          NS_ENSURE_SUCCESS(res, res);
          if (linkElement)
            element = linkElement;
        }
      }
      // Select entire element clicked on if NOT within an existing selection
      //   and not the entire body, or table-related elements
      if (element)
      {
        nsCOMPtr<nsIDOMNode> selectAllNode =
          htmlEditor->FindUserSelectAllNode(element);

        if (selectAllNode)
        {
          nsCOMPtr<nsIDOMElement> newElement = do_QueryInterface(selectAllNode);
          if (newElement)
          {
            node = selectAllNode;
            element = newElement;
          }
        }

        if (isContextClick && !nsHTMLEditUtils::IsImage(node))
        {
          selection->Collapse(parent, offset);
        }
        else
        {
          htmlEditor->SelectElement(element);
        }
      }
    }
    // HACK !!! Context click places the caret but the context menu consumes
    // the event; so we need to check resizing state ourselves
    htmlEditor->CheckSelectionStateForAnonymousButtons(selection);

    // Prevent bubbling if we changed selection or 
    //   for all context clicks
    if (element || isContextClick)
    {
    #ifndef XP_OS2
      mouseEvent->PreventDefault();
    #endif
      return NS_OK;
    }
  }
  else if (!isContextClick && buttonNumber == 0 && clickCount == 1)
  {
    // if the target element is an image, we have to display resizers
    PRInt32 clientX, clientY;
    mouseEvent->GetClientX(&clientX);
    mouseEvent->GetClientY(&clientY);
    htmlEditor->MouseDown(clientX, clientY, element, aMouseEvent);
  }

  return nsEditorEventListener::MouseDown(aMouseEvent);
}
nsresult
nsHTMLEditorMouseListener::MouseDown(nsIDOMEvent* aMouseEvent)
{
  nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
  if (!mouseEvent) {
    //non-ui event passed in.  bad things.
    return NS_OK;
  }

  // Don't do anything special if not an HTML editor
  nsCOMPtr<nsIHTMLEditor> htmlEditor = do_QueryInterface(mEditor);
  if (htmlEditor)
  {
    // Detect only "context menu" click
    //XXX This should be easier to do!
    // But eDOMEvents_contextmenu and NS_CONTEXTMENU is not exposed in any event interface :-(
    PRUint16 buttonNumber;
    nsresult res = mouseEvent->GetButton(&buttonNumber);
    if (NS_FAILED(res)) return res;

    PRBool isContextClick;

#if defined(XP_MAC) || defined(XP_MACOSX)
    // Ctrl+Click for context menu
    res = mouseEvent->GetCtrlKey(&isContextClick);
    if (NS_FAILED(res)) return res;
#else
    // Right mouse button for Windows, UNIX
    isContextClick = buttonNumber == 2;
#endif
    
    PRInt32 clickCount;
    res = mouseEvent->GetDetail(&clickCount);
    if (NS_FAILED(res)) return res;

    nsCOMPtr<nsIDOMEventTarget> target;
    nsCOMPtr<nsIDOMNSEvent> internalEvent = do_QueryInterface(aMouseEvent);
    res = internalEvent->GetExplicitOriginalTarget(getter_AddRefs(target));
    if (NS_FAILED(res)) return res;
    if (!target) return NS_ERROR_NULL_POINTER;
    nsCOMPtr<nsIDOMElement> element = do_QueryInterface(target);

    if (isContextClick || (buttonNumber == 0 && clickCount == 2))
    {
      nsCOMPtr<nsISelection> selection;
      mEditor->GetSelection(getter_AddRefs(selection));
      if (!selection) return NS_OK;

      // Get location of mouse within target node
      nsCOMPtr<nsIDOMNSUIEvent> uiEvent = do_QueryInterface(aMouseEvent);
      if (!uiEvent) return NS_ERROR_FAILURE;

      nsCOMPtr<nsIDOMNode> parent;
      PRInt32 offset = 0;

      res = uiEvent->GetRangeParent(getter_AddRefs(parent));
      if (NS_FAILED(res)) return res;
      if (!parent) return NS_ERROR_FAILURE;

      res = uiEvent->GetRangeOffset(&offset);
      if (NS_FAILED(res)) return res;

      // Detect if mouse point is within current selection for context click
      PRBool nodeIsInSelection = PR_FALSE;
      if (isContextClick)
      {
        PRBool isCollapsed;
        selection->GetIsCollapsed(&isCollapsed);
        if (!isCollapsed)
        {
          PRInt32 rangeCount;
          res = selection->GetRangeCount(&rangeCount);
          if (NS_FAILED(res)) return res;

          for (PRInt32 i = 0; i < rangeCount; i++)
          {
            nsCOMPtr<nsIDOMRange> range;

            res = selection->GetRangeAt(i, getter_AddRefs(range));
            if (NS_FAILED(res) || !range) 
              continue;//dont bail yet, iterate through them all

            nsCOMPtr<nsIDOMNSRange> nsrange(do_QueryInterface(range));
            if (NS_FAILED(res) || !nsrange) 
              continue;//dont bail yet, iterate through them all

            res = nsrange->IsPointInRange(parent, offset, &nodeIsInSelection);

            // Done when we find a range that we are in
            if (nodeIsInSelection)
              break;
          }
        }
      }
      nsCOMPtr<nsIDOMNode> node = do_QueryInterface(target);
      if (node && !nodeIsInSelection)
      {
        if (!element)
        {
          if (isContextClick)
          {
            // Set the selection to the point under the mouse cursor:
            selection->Collapse(parent, offset);
          }
          else
          {
            // Get enclosing link if in text so we can select the link
            nsCOMPtr<nsIDOMElement> linkElement;
            res = htmlEditor->GetElementOrParentByTagName(NS_LITERAL_STRING("href"), node, getter_AddRefs(linkElement));
            if (NS_FAILED(res)) return res;
            if (linkElement)
              element = linkElement;
          }
        }
        // Select entire element clicked on if NOT within an existing selection
        //   and not the entire body, or table-related elements
        if (element)
        {
          nsCOMPtr<nsIDOMNode> selectAllNode = mHTMLEditor->FindUserSelectAllNode(element);

          if (selectAllNode)
          {
            nsCOMPtr<nsIDOMElement> newElement = do_QueryInterface(selectAllNode);
            if (newElement)
            {
              node = selectAllNode;
              element = newElement;
            }
          }

// XXX: should we call nsHTMLEditUtils::IsTableElement here?
// that also checks for thead, tbody, tfoot
          if (nsTextEditUtils::IsBody(node) ||
              nsHTMLEditUtils::IsTableCellOrCaption(node) ||
              nsHTMLEditUtils::IsTableRow(node) ||
              nsHTMLEditUtils::IsTable(node))
          {
            // This will place caret just inside table cell or at start of body
            selection->Collapse(parent, offset);
          }
          else
          {
            htmlEditor->SelectElement(element);
          }
        }
      }
      // HACK !!! Context click places the caret but the context menu consumes
      // the event; so we need to check resizing state ourselves
      htmlEditor->CheckSelectionStateForAnonymousButtons(selection);

      // Prevent bubbling if we changed selection or 
      //   for all context clicks
      if (element || isContextClick)
      {
      #ifndef XP_OS2
        mouseEvent->PreventDefault();
      #endif
        return NS_OK;
      }
    }
    else if (!isContextClick && buttonNumber == 0 && clickCount == 1)
    {
      // if the target element is an image, we have to display resizers
      nsCOMPtr<nsIHTMLObjectResizer> objectResizer = do_QueryInterface(htmlEditor);
      PRInt32 clientX, clientY;
      mouseEvent->GetClientX(&clientX);
      mouseEvent->GetClientY(&clientY);
      objectResizer->MouseDown(clientX, clientY, element);
    }
  }

  return nsTextEditorMouseListener::MouseDown(aMouseEvent);
}