Mozilla/mozilla/editor/libeditor/text/nsPlaintextDataTransfer.cpp
dougt%netscape.com fed2927b2c Create a new xpcom obsolete library (and component library).
Moved nsFileSpec and related classes into this obsolete library.
  Moved nsRegistry and related libreg functionality into the obsolete library.

Updated many callers using the obsolete nsFile spec to use nsIFile and Necko to do file IO.

Combined the following DLLs (source -> dest)
  uriloader -> docshell
  shistory -> docshell
  jsurl -> jsdom
  gkview -> gklayout

Moved nsAdapterEnumerator out of xpcom/ds and into mailnews, since they're the only consumer

Modifed the xpt_link tool so that you can specify a “only include” cid list that can mask CID’s that you are not interested in.

Added build options:
Prevent the building of xpinstall (--disable-xpinstall)
Prevent the building js component loader (--disable-jsloader)
A build option to only build a single profile (--enable-single-profile)
A build flag to only built the required xpfe components (--disable-xpfe-components).

Removal or hiding of unused functions and classes including nsEscape*, nsDequeIterator, nsRecyclingAllocatorImpl, nsDiscriminatedUnion, nsOpaqueKey, nsCRT::strlen, NS_NewCommandLineService

Bug 194240, r/sr = darin, alec.


git-svn-id: svn://10.0.0.236/trunk@139480 18797224-902f-48f8-a5cc-f745e15eee43
2003-03-15 01:04:32 +00:00

589 lines
19 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsICaret.h"
#include "nsPlaintextEditor.h"
#include "nsTextEditUtils.h"
#include "nsIDOMText.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMDocument.h"
#include "nsIDOMAttr.h"
#include "nsIDocument.h"
#include "nsIDOMEventReceiver.h"
#include "nsIDOMNSEvent.h"
#include "nsIDOMKeyEvent.h"
#include "nsIDOMKeyListener.h"
#include "nsIDOMMouseListener.h"
#include "nsIDOMMouseEvent.h"
#include "nsISelection.h"
#include "nsISelectionPrivate.h"
#include "nsIDOMHTMLAnchorElement.h"
#include "nsIDOMHTMLImageElement.h"
#include "nsISelectionController.h"
#include "nsIFrameSelection.h" // For TABLESELECTION_ defines
#include "nsIIndependentSelection.h" //domselections answer to frameselection
#include "nsICSSLoader.h"
#include "nsICSSStyleSheet.h"
#include "nsIHTMLContentContainer.h"
#include "nsIStyleSet.h"
#include "nsIDocumentObserver.h"
#include "nsIDocumentStateListener.h"
#include "nsIEnumerator.h"
#include "nsIContent.h"
#include "nsIContentIterator.h"
#include "nsEditorCID.h"
#include "nsLayoutCID.h"
#include "nsIDOMRange.h"
#include "nsIDOMNSRange.h"
#include "nsISupportsArray.h"
#include "nsVoidArray.h"
#include "nsIURL.h"
#include "nsIComponentManager.h"
#include "nsIServiceManager.h"
#include "nsWidgetsCID.h"
#include "nsIDocumentEncoder.h"
#include "nsIDOMDocumentFragment.h"
#include "nsIPresShell.h"
#include "nsIPresContext.h"
#include "nsIImage.h"
#include "nsAOLCiter.h"
#include "nsInternetCiter.h"
#include "nsXPCOM.h"
#include "nsISupportsPrimitives.h"
// netwerk
#include "nsIURI.h"
#include "nsNetUtil.h"
// Drag & Drop, Clipboard
#include "nsWidgetsCID.h"
#include "nsIClipboard.h"
#include "nsITransferable.h"
#include "nsIDragService.h"
#include "nsIDOMNSUIEvent.h"
// Misc
#include "nsEditorUtils.h"
#include "nsIPref.h"
const PRUnichar nbsp = 160;
// Drag & Drop, Clipboard Support
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
static NS_DEFINE_CID(kCTransferableCID, NS_TRANSFERABLE_CID);
static NS_DEFINE_CID(kCHTMLFormatConverterCID, NS_HTMLFORMATCONVERTER_CID);
// private clipboard data flavors for html copy/paste
#define kHTMLContext "text/_moz_htmlcontext"
#define kHTMLInfo "text/_moz_htmlinfo"
#if defined(NS_DEBUG) && defined(DEBUG_buster)
static PRBool gNoisy = PR_FALSE;
#else
static const PRBool gNoisy = PR_FALSE;
#endif
NS_IMETHODIMP nsPlaintextEditor::PrepareTransferable(nsITransferable **transferable)
{
// Create generic Transferable for getting the data
nsresult rv = nsComponentManager::CreateInstance(kCTransferableCID, nsnull,
NS_GET_IID(nsITransferable),
(void**)transferable);
if (NS_FAILED(rv))
return rv;
// Get the nsITransferable interface for getting the data from the clipboard
if (transferable) (*transferable)->AddDataFlavor(kUnicodeMime);
return NS_OK;
}
NS_IMETHODIMP nsPlaintextEditor::InsertTextFromTransferable(nsITransferable *transferable)
{
nsresult rv = NS_OK;
char* bestFlavor = nsnull;
nsCOMPtr<nsISupports> genericDataObj;
PRUint32 len = 0;
if ( NS_SUCCEEDED(transferable->GetAnyTransferData(&bestFlavor, getter_AddRefs(genericDataObj), &len)) )
{
nsAutoTxnsConserveSelection dontSpazMySelection(this);
nsAutoString flavor, stuffToPaste;
flavor.AssignWithConversion( bestFlavor ); // just so we can use flavor.Equals()
if (flavor.Equals(NS_LITERAL_STRING(kUnicodeMime)))
{
nsCOMPtr<nsISupportsString> textDataObj ( do_QueryInterface(genericDataObj) );
if (textDataObj && len > 0)
{
nsAutoString text;
textDataObj->GetData ( text );
NS_ASSERTION(text.Length() <= (len/2), "Invalid length!");
stuffToPaste.Assign ( text.get(), len / 2 );
nsAutoEditBatch beginBatching(this);
rv = InsertText(stuffToPaste);
}
}
}
nsCRT::free(bestFlavor);
// Try to scroll the selection into view if the paste/drop succeeded
if (NS_SUCCEEDED(rv))
ScrollSelectionIntoView(PR_FALSE);
return rv;
}
NS_IMETHODIMP nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent)
{
ForceCompositionEnd();
nsresult rv;
nsCOMPtr<nsIDragService> dragService =
do_GetService("@mozilla.org/widget/dragservice;1", &rv);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDragSession> dragSession;
dragService->GetCurrentSession(getter_AddRefs(dragSession));
if (!dragSession) return NS_OK;
// Get the nsITransferable interface for getting the data from the drop
nsCOMPtr<nsITransferable> trans;
rv = PrepareTransferable(getter_AddRefs(trans));
if (NS_FAILED(rv)) return rv;
if (!trans) return NS_OK; // NS_ERROR_FAILURE; SHOULD WE FAIL?
PRUint32 numItems = 0;
rv = dragSession->GetNumDropItems(&numItems);
if (NS_FAILED(rv)) return rv;
// Combine any deletion and drop insertion into one transaction
nsAutoEditBatch beginBatching(this);
PRUint32 i;
PRBool doPlaceCaret = PR_TRUE;
for (i = 0; i < numItems; ++i)
{
rv = dragSession->GetData(trans, i);
if (NS_FAILED(rv)) return rv;
if (!trans) return NS_OK; // NS_ERROR_FAILURE; Should we fail?
if ( doPlaceCaret )
{
// check if the user pressed the key to force a copy rather than a move
// if we run into problems here, we'll just assume the user doesn't want a copy
PRBool userWantsCopy = PR_FALSE;
nsCOMPtr<nsIDOMNSUIEvent> nsuiEvent (do_QueryInterface(aDropEvent));
if (!nsuiEvent) return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aDropEvent) );
if (mouseEvent)
#ifdef XP_MAC
mouseEvent->GetAltKey(&userWantsCopy);
#else
mouseEvent->GetCtrlKey(&userWantsCopy);
#endif
// Source doc is null if source is *not* the current editor document
nsCOMPtr<nsIDOMDocument> srcdomdoc;
rv = dragSession->GetSourceDocument(getter_AddRefs(srcdomdoc));
if (NS_FAILED(rv)) return rv;
// Current doc is destination
nsCOMPtr<nsIDOMDocument>destdomdoc;
rv = GetDocument(getter_AddRefs(destdomdoc));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsISelection> selection;
rv = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(rv)) return rv;
if (!selection) return NS_ERROR_FAILURE;
PRBool isCollapsed;
rv = selection->GetIsCollapsed(&isCollapsed);
if (NS_FAILED(rv)) return rv;
// Parent and offset under the mouse cursor
nsCOMPtr<nsIDOMNode> newSelectionParent;
PRInt32 newSelectionOffset = 0;
rv = nsuiEvent->GetRangeParent(getter_AddRefs(newSelectionParent));
if (NS_FAILED(rv)) return rv;
if (!newSelectionParent) return NS_ERROR_FAILURE;
rv = nsuiEvent->GetRangeOffset(&newSelectionOffset);
if (NS_FAILED(rv)) return rv;
// We never have to delete if selection is already collapsed
PRBool deleteSelection = PR_FALSE;
PRBool cursorIsInSelection = PR_FALSE;
// Check if mouse is in the selection
if (!isCollapsed)
{
PRInt32 rangeCount;
rv = selection->GetRangeCount(&rangeCount);
if (NS_FAILED(rv))
return rv;
for (PRInt32 j = 0; j < rangeCount; j++)
{
nsCOMPtr<nsIDOMRange> range;
rv = selection->GetRangeAt(j, getter_AddRefs(range));
if (NS_FAILED(rv) || !range)
continue;//dont bail yet, iterate through them all
nsCOMPtr<nsIDOMNSRange> nsrange(do_QueryInterface(range));
if (NS_FAILED(rv) || !nsrange)
continue;//dont bail yet, iterate through them all
rv = nsrange->IsPointInRange(newSelectionParent, newSelectionOffset, &cursorIsInSelection);
if(cursorIsInSelection)
break;
}
if (cursorIsInSelection)
{
// Dragging within same doc can't drop on itself -- leave!
// (We shouldn't get here - drag event shouldn't have started if over selection)
if (srcdomdoc == destdomdoc)
return NS_OK;
// Dragging from another window onto a selection
// XXX Decision made to NOT do this,
// note that 4.x does replace if dropped on
//deleteSelection = PR_TRUE;
}
else
{
// We are NOT over the selection
if (srcdomdoc == destdomdoc)
{
// Within the same doc: delete if user doesn't want to copy
deleteSelection = !userWantsCopy;
}
else
{
// Different source doc: Don't delete
deleteSelection = PR_FALSE;
}
}
}
if (deleteSelection)
{
// Use an auto tracker so that our drop point is correctly
// positioned after the delete.
nsAutoTrackDOMPoint tracker(mRangeUpdater, &newSelectionParent, &newSelectionOffset);
rv = DeleteSelection(eNone);
if (NS_FAILED(rv)) return rv;
}
// If we deleted the selection because we dropped from another doc,
// then we don't have to relocate the caret (insert at the deletion point)
if (!(deleteSelection && srcdomdoc != destdomdoc))
{
// Move the selection to the point under the mouse cursor
selection->Collapse(newSelectionParent, newSelectionOffset);
}
// We have to figure out whether to delete and relocate caret only once
doPlaceCaret = PR_FALSE;
}
rv = InsertTextFromTransferable(trans);
}
return rv;
}
NS_IMETHODIMP nsPlaintextEditor::CanDrag(nsIDOMEvent *aDragEvent, PRBool *aCanDrag)
{
if (!aCanDrag)
return NS_ERROR_NULL_POINTER;
/* we really should be checking the XY coordinates of the mouseevent and ensure that
* that particular point is actually within the selection (not just that there is a selection)
*/
*aCanDrag = PR_FALSE;
// KLUDGE to work around bug 50703
// After double click and object property editing,
// we get a spurious drag event
if (mIgnoreSpuriousDragEvent)
{
mIgnoreSpuriousDragEvent = PR_FALSE;
return NS_OK;
}
nsCOMPtr<nsISelection> selection;
nsresult res = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(res)) return res;
PRBool isCollapsed;
res = selection->GetIsCollapsed(&isCollapsed);
if (NS_FAILED(res)) return res;
// if we are collapsed, we have no selection so nothing to drag
if ( isCollapsed )
return NS_OK;
nsCOMPtr<nsIDOMEventTarget> eventTarget;
nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aDragEvent));
if (nsevent) {
res = nsevent->GetOriginalTarget(getter_AddRefs(eventTarget));
if (NS_FAILED(res)) {
return res;
}
}
if ( eventTarget )
{
nsCOMPtr<nsIDOMNode> eventTargetDomNode = do_QueryInterface(eventTarget);
if ( eventTargetDomNode )
{
PRBool isTargetedCorrectly = PR_FALSE;
res = selection->ContainsNode(eventTargetDomNode, PR_FALSE, &isTargetedCorrectly);
if (NS_FAILED(res)) return res;
*aCanDrag = isTargetedCorrectly;
}
}
return NS_OK;
}
NS_IMETHODIMP nsPlaintextEditor::DoDrag(nsIDOMEvent *aDragEvent)
{
nsresult rv;
nsCOMPtr<nsIDOMEventTarget> eventTarget;
rv = aDragEvent->GetTarget(getter_AddRefs(eventTarget));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDOMNode> domnode = do_QueryInterface(eventTarget);
/* get the selection to be dragged */
nsCOMPtr<nsISelection> selection;
rv = GetSelection(getter_AddRefs(selection));
if (NS_FAILED(rv)) return rv;
/* create an array of transferables */
nsCOMPtr<nsISupportsArray> transferableArray;
NS_NewISupportsArray(getter_AddRefs(transferableArray));
if (transferableArray == nsnull)
return NS_ERROR_OUT_OF_MEMORY;
/* get the drag service */
nsCOMPtr<nsIDragService> dragService =
do_GetService("@mozilla.org/widget/dragservice;1", &rv);
if (NS_FAILED(rv)) return rv;
/* create html flavor transferable */
nsCOMPtr<nsITransferable> trans = do_CreateInstance(kCTransferableCID);
NS_ENSURE_TRUE(trans, NS_ERROR_FAILURE);
nsCOMPtr<nsIDOMDocument> domdoc;
rv = GetDocument(getter_AddRefs(domdoc));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
if (doc)
{
// find out if we're a plaintext control or not
PRUint32 editorFlags = 0;
rv = GetFlags(&editorFlags);
if (NS_FAILED(rv)) return rv;
PRBool bIsPlainTextControl = ((editorFlags & eEditorPlaintextMask) != 0);
// get correct mimeType and document encoder flags set
nsAutoString mimeType;
PRUint32 docEncoderFlags = 0;
if (bIsPlainTextControl)
{
docEncoderFlags |= nsIDocumentEncoder::OutputBodyOnly | nsIDocumentEncoder::OutputPreformatted;
mimeType = NS_LITERAL_STRING(kUnicodeMime);
}
else
mimeType = NS_LITERAL_STRING(kHTMLMime);
// set up docEncoder
nsCOMPtr<nsIDocumentEncoder> docEncoder = do_CreateInstance(NS_HTMLCOPY_ENCODER_CONTRACTID);
NS_ENSURE_TRUE(docEncoder, NS_ERROR_FAILURE);
rv = docEncoder->Init(doc, mimeType, docEncoderFlags);
if (NS_FAILED(rv)) return rv;
rv = docEncoder->SetSelection(selection);
if (NS_FAILED(rv)) return rv;
// grab a string
nsAutoString buffer;
rv = docEncoder->EncodeToString(buffer);
if (NS_FAILED(rv)) return rv;
// if we have an empty string, we're done; otherwise continue
if ( !buffer.IsEmpty() )
{
nsCOMPtr<nsISupportsString> dataWrapper = do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
NS_ENSURE_TRUE(dataWrapper, NS_ERROR_FAILURE);
rv = dataWrapper->SetData( buffer );
if (NS_FAILED(rv)) return rv;
if (bIsPlainTextControl)
{
// Add the unicode flavor to the transferable
rv = trans->AddDataFlavor(kUnicodeMime);
if (NS_FAILED(rv)) return rv;
}
else
{
rv = trans->AddDataFlavor(kHTMLMime);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIFormatConverter> htmlConverter = do_CreateInstance(kCHTMLFormatConverterCID);
NS_ENSURE_TRUE(htmlConverter, NS_ERROR_FAILURE);
rv = trans->SetConverter(htmlConverter);
if (NS_FAILED(rv)) return rv;
}
// QI the data object an |nsISupports| so that when the transferable holds
// onto it, it will addref the correct interface.
nsCOMPtr<nsISupports> nsisupportsDataWrapper ( do_QueryInterface(dataWrapper) );
rv = trans->SetTransferData(bIsPlainTextControl ? kUnicodeMime : kHTMLMime,
nsisupportsDataWrapper, buffer.Length() * 2);
if (NS_FAILED(rv)) return rv;
/* add the transferable to the array */
rv = transferableArray->AppendElement(trans);
if (NS_FAILED(rv)) return rv;
/* invoke drag */
unsigned int flags;
// in some cases we'll want to cut rather than copy... hmmmmm...
flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE;
rv = dragService->InvokeDragSession( domnode, transferableArray, nsnull, flags);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(aDragEvent));
if (nsevent) {
nsevent->PreventBubble();
}
}
}
return rv;
}
NS_IMETHODIMP nsPlaintextEditor::Paste(PRInt32 aSelectionType)
{
ForceCompositionEnd();
// Get Clipboard Service
nsresult rv;
nsCOMPtr<nsIClipboard> clipboard( do_GetService( kCClipboardCID, &rv ) );
if ( NS_FAILED(rv) )
return rv;
// Get the nsITransferable interface for getting the data from the clipboard
nsCOMPtr<nsITransferable> trans;
rv = PrepareTransferable(getter_AddRefs(trans));
if (NS_SUCCEEDED(rv) && trans)
{
// Get the Data from the clipboard
if (NS_SUCCEEDED(clipboard->GetData(trans, aSelectionType)) && IsModifiable())
{
rv = InsertTextFromTransferable(trans);
}
}
return rv;
}
NS_IMETHODIMP nsPlaintextEditor::CanPaste(PRInt32 aSelectionType, PRBool *aCanPaste)
{
if (!aCanPaste)
return NS_ERROR_NULL_POINTER;
*aCanPaste = PR_FALSE;
// can't paste if readonly
if (!IsModifiable())
return NS_OK;
nsresult rv;
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
if (NS_FAILED(rv)) return rv;
// the flavors that we can deal with
const char* const textEditorFlavors[] = { kUnicodeMime, nsnull };
nsCOMPtr<nsISupportsArray> flavorsList;
rv = nsComponentManager::CreateInstance(NS_SUPPORTSARRAY_CONTRACTID, nsnull,
NS_GET_IID(nsISupportsArray), getter_AddRefs(flavorsList));
if (NS_FAILED(rv)) return rv;
PRUint32 editorFlags;
GetFlags(&editorFlags);
// add the flavors for text editors
for (const char* const* flavor = textEditorFlavors; *flavor; flavor++)
{
nsCOMPtr<nsISupportsCString> flavorString;
nsComponentManager::CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID, nsnull,
NS_GET_IID(nsISupportsCString), getter_AddRefs(flavorString));
if (flavorString)
{
flavorString->SetData(nsDependentCString(*flavor));
flavorsList->AppendElement(flavorString);
}
}
PRBool haveFlavors;
rv = clipboard->HasDataMatchingFlavors(flavorsList, aSelectionType, &haveFlavors);
if (NS_FAILED(rv)) return rv;
*aCanPaste = haveFlavors;
return NS_OK;
}