Back to index

lightning-sunbird  0.9+nobinonly
nsEditorEventListeners.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Pierre Phaneuf <pp@ludusdesign.com>
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 #include "nsEditorEventListeners.h"
00039 #include "nsEditor.h"
00040 
00041 #include "nsIDOMEvent.h"
00042 #include "nsIDOMNSEvent.h"
00043 #include "nsIDOMDocument.h"
00044 #include "nsIDocument.h"
00045 #include "nsIPresShell.h"
00046 #include "nsISelection.h"
00047 #include "nsISelectionController.h"
00048 #include "nsIDOMKeyEvent.h"
00049 #include "nsIDOMMouseEvent.h"
00050 #include "nsIDOMNSUIEvent.h"
00051 #include "nsIPrivateTextEvent.h"
00052 #include "nsIPrivateCompositionEvent.h"
00053 #include "nsIEditorMailSupport.h"
00054 #include "nsIPrefBranch.h"
00055 #include "nsIPrefService.h"
00056 #include "nsILookAndFeel.h"
00057 #include "nsPresContext.h"
00058 #ifdef USE_HACK_REPAINT
00059 // for repainting hack only
00060 #include "nsIView.h"
00061 #include "nsIViewManager.h"
00062 // end repainting hack only
00063 #endif
00064 
00065 // Drag & Drop, Clipboard
00066 #include "nsIServiceManager.h"
00067 #include "nsIClipboard.h"
00068 #include "nsIDragService.h"
00069 #include "nsIDragSession.h"
00070 #include "nsIContent.h"
00071 #include "nsISupportsPrimitives.h"
00072 #include "nsIDOMNSRange.h"
00073 #include "nsEditorUtils.h"
00074 #include "nsIDOMEventTarget.h"
00075 #include "nsIEventStateManager.h"
00076 
00077 //#define DEBUG_IME
00078 
00079 /*
00080  * nsTextEditorKeyListener implementation
00081  */
00082 
00083 NS_IMPL_ISUPPORTS2(nsTextEditorKeyListener, nsIDOMEventListener, nsIDOMKeyListener)
00084 
00085 
00086 nsTextEditorKeyListener::nsTextEditorKeyListener()
00087 {
00088 }
00089 
00090 
00091 
00092 nsTextEditorKeyListener::~nsTextEditorKeyListener() 
00093 {
00094 }
00095 
00096 
00097 nsresult
00098 nsTextEditorKeyListener::HandleEvent(nsIDOMEvent* aEvent)
00099 {
00100   return NS_OK;
00101 }
00102 
00103 // individual key handlers return NS_OK to indicate NOT consumed
00104 // by default, an error is returned indicating event is consumed
00105 // joki is fixing this interface.
00106 nsresult
00107 nsTextEditorKeyListener::KeyDown(nsIDOMEvent* aKeyEvent)
00108 {
00109   return NS_OK;
00110 }
00111 
00112 
00113 nsresult
00114 nsTextEditorKeyListener::KeyUp(nsIDOMEvent* aKeyEvent)
00115 {
00116   return NS_OK;
00117 }
00118 
00119 
00120 nsresult
00121 nsTextEditorKeyListener::KeyPress(nsIDOMEvent* aKeyEvent)
00122 {
00123   // DOM event handling happens in two passes, the client pass and the system
00124   // pass.  We do all of our processing in the system pass, to allow client
00125   // handlers the opportunity to cancel events and prevent typing in the editor.
00126   // If the client pass cancelled the event, defaultPrevented will be true
00127   // below.
00128 
00129   nsCOMPtr<nsIDOMNSUIEvent> nsUIEvent = do_QueryInterface(aKeyEvent);
00130   if(nsUIEvent) 
00131   {
00132     PRBool defaultPrevented;
00133     nsUIEvent->GetPreventDefault(&defaultPrevented);
00134     if(defaultPrevented)
00135       return NS_OK;
00136   }
00137 
00138   nsCOMPtr<nsIDOMKeyEvent>keyEvent = do_QueryInterface(aKeyEvent);
00139   if (!keyEvent) 
00140   {
00141     //non-key event passed to keypress.  bad things.
00142     return NS_OK;
00143   }
00144 
00145   // we should check a flag here to see if we should be using built-in key bindings
00146   // mEditor->GetFlags(&flags);
00147   // if (flags & ...)
00148 
00149   PRUint32 keyCode;
00150   keyEvent->GetKeyCode(&keyCode);
00151 
00152   // if we are readonly or disabled, then do nothing.
00153   PRUint32 flags;
00154   if (NS_SUCCEEDED(mEditor->GetFlags(&flags)))
00155   {
00156     if (flags & nsIPlaintextEditor::eEditorReadonlyMask || 
00157         flags & nsIPlaintextEditor::eEditorDisabledMask) 
00158       return NS_OK;
00159   }
00160   else
00161     return NS_ERROR_FAILURE;  // Editor unable to handle this.
00162 
00163   nsCOMPtr<nsIPlaintextEditor> textEditor (do_QueryInterface(mEditor));
00164   if (!textEditor) return NS_ERROR_NO_INTERFACE;
00165 
00166   // if there is no charCode, then it's a key that doesn't map to a character,
00167   // so look for special keys using keyCode.
00168   if (0 != keyCode)
00169   {
00170     PRBool isAnyModifierKeyButShift;
00171     nsresult rv;
00172     rv = keyEvent->GetAltKey(&isAnyModifierKeyButShift);
00173     if (NS_FAILED(rv)) return rv;
00174     
00175     if (!isAnyModifierKeyButShift)
00176     {
00177       rv = keyEvent->GetMetaKey(&isAnyModifierKeyButShift);
00178       if (NS_FAILED(rv)) return rv;
00179       
00180       if (!isAnyModifierKeyButShift)
00181       {
00182         rv = keyEvent->GetCtrlKey(&isAnyModifierKeyButShift);
00183         if (NS_FAILED(rv)) return rv;
00184       }
00185     }
00186 
00187     switch (keyCode)
00188     {
00189       case nsIDOMKeyEvent::DOM_VK_META:
00190       case nsIDOMKeyEvent::DOM_VK_SHIFT:
00191       case nsIDOMKeyEvent::DOM_VK_CONTROL:
00192       case nsIDOMKeyEvent::DOM_VK_ALT:
00193         aKeyEvent->PreventDefault(); // consumed
00194         return NS_OK;
00195         break;
00196 
00197       case nsIDOMKeyEvent::DOM_VK_BACK_SPACE: 
00198         if (isAnyModifierKeyButShift)
00199           return NS_OK;
00200 
00201         mEditor->DeleteSelection(nsIEditor::ePrevious);
00202         aKeyEvent->PreventDefault(); // consumed
00203         return NS_OK;
00204         break;
00205  
00206       case nsIDOMKeyEvent::DOM_VK_DELETE:
00207         /* on certain platforms (such as windows) the shift key
00208            modifies what delete does (cmd_cut in this case).
00209            bailing here to allow the keybindings to do the cut.*/
00210         PRBool isShiftModifierKey;
00211         rv = keyEvent->GetShiftKey(&isShiftModifierKey);
00212         if (NS_FAILED(rv)) return rv;
00213 
00214         if (isAnyModifierKeyButShift || isShiftModifierKey)
00215            return NS_OK;
00216         mEditor->DeleteSelection(nsIEditor::eNext);
00217         aKeyEvent->PreventDefault(); // consumed
00218         return NS_OK; 
00219         break;
00220  
00221       case nsIDOMKeyEvent::DOM_VK_TAB:
00222         if ((flags & nsIPlaintextEditor::eEditorSingleLineMask) ||
00223             (flags & nsIPlaintextEditor::eEditorPasswordMask)   ||
00224             (flags & nsIPlaintextEditor::eEditorWidgetMask))
00225           return NS_OK; // let it be used for focus switching
00226 
00227         if (isAnyModifierKeyButShift)
00228           return NS_OK;
00229 
00230         // else we insert the tab straight through
00231         textEditor->HandleKeyPress(keyEvent);
00232         // let HandleKeyPress consume the event
00233         return NS_OK; 
00234 
00235       case nsIDOMKeyEvent::DOM_VK_RETURN:
00236       case nsIDOMKeyEvent::DOM_VK_ENTER:
00237         if (isAnyModifierKeyButShift)
00238           return NS_OK;
00239 
00240         if (!(flags & nsIPlaintextEditor::eEditorSingleLineMask))
00241         {
00242           textEditor->HandleKeyPress(keyEvent);
00243           aKeyEvent->PreventDefault(); // consumed
00244         }
00245         return NS_OK;
00246     }
00247   }
00248 
00249   textEditor->HandleKeyPress(keyEvent);
00250   return NS_OK; // we don't PreventDefault() here or keybindings like control-x won't work 
00251 }
00252 
00253 
00254 /*
00255  * nsTextEditorMouseListener implementation
00256  */
00257 
00258 NS_IMPL_ISUPPORTS2(nsTextEditorMouseListener, nsIDOMEventListener, nsIDOMMouseListener)
00259 
00260 
00261 nsTextEditorMouseListener::nsTextEditorMouseListener() 
00262 {
00263 }
00264 
00265 
00266 
00267 nsTextEditorMouseListener::~nsTextEditorMouseListener() 
00268 {
00269 }
00270 
00271 
00272 nsresult
00273 nsTextEditorMouseListener::HandleEvent(nsIDOMEvent* aEvent)
00274 {
00275   return NS_OK;
00276 }
00277 
00278 
00279 
00280 nsresult
00281 nsTextEditorMouseListener::MouseClick(nsIDOMEvent* aMouseEvent)
00282 {
00283   nsCOMPtr<nsIDOMMouseEvent> mouseEvent ( do_QueryInterface(aMouseEvent) );
00284   nsCOMPtr<nsIDOMNSEvent> nsEvent ( do_QueryInterface(aMouseEvent) );
00285   PRBool isTrusted = PR_FALSE;
00286   if (!mouseEvent || !nsEvent ||
00287       NS_FAILED(nsEvent->GetIsTrusted(&isTrusted)) || !isTrusted) {
00288     //non-ui, or non-trusted evet passed in.  bad things.
00289     return NS_OK;
00290   }
00291 
00292   nsCOMPtr<nsIEditor> editor = do_QueryInterface(mEditor);
00293   if (!editor) { return NS_OK; }
00294 
00295   // If we got a mouse down inside the editing area, we should force the 
00296   // IME to commit before we change the cursor position
00297   nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(mEditor);
00298   if (imeEditor)
00299     imeEditor->ForceCompositionEnd();
00300 
00301   PRUint16 button = (PRUint16)-1;
00302   mouseEvent->GetButton(&button);
00303   // middle-mouse click (paste);
00304   if (button == 1)
00305   {
00306     nsresult rv;
00307     nsCOMPtr<nsIPrefBranch> prefBranch =
00308       do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
00309     if (NS_SUCCEEDED(rv) && prefBranch)
00310     {
00311       PRBool doMiddleMousePaste = PR_FALSE;;
00312       rv = prefBranch->GetBoolPref("middlemouse.paste", &doMiddleMousePaste);
00313       if (NS_SUCCEEDED(rv) && doMiddleMousePaste)
00314       {
00315         // Set the selection to the point under the mouse cursor:
00316         nsCOMPtr<nsIDOMNSUIEvent> nsuiEvent (do_QueryInterface(aMouseEvent));
00317         if (!nsuiEvent)
00318           return NS_ERROR_NULL_POINTER;
00319         nsCOMPtr<nsIDOMNode> parent;
00320         if (NS_FAILED(nsuiEvent->GetRangeParent(getter_AddRefs(parent))))
00321           return NS_ERROR_NULL_POINTER;
00322         PRInt32 offset = 0;
00323         if (NS_FAILED(nsuiEvent->GetRangeOffset(&offset)))
00324           return NS_ERROR_NULL_POINTER;
00325 
00326         nsCOMPtr<nsISelection> selection;
00327         if (NS_SUCCEEDED(editor->GetSelection(getter_AddRefs(selection))))
00328           (void)selection->Collapse(parent, offset);
00329 
00330         // If the ctrl key is pressed, we'll do paste as quotation.
00331         // Would've used the alt key, but the kde wmgr treats alt-middle specially. 
00332         PRBool ctrlKey = PR_FALSE;
00333         mouseEvent->GetCtrlKey(&ctrlKey);
00334 
00335         nsCOMPtr<nsIEditorMailSupport> mailEditor;
00336         if (ctrlKey)
00337           mailEditor = do_QueryInterface(mEditor);
00338 
00339         PRInt32 clipboard;
00340 
00341 #if defined(XP_OS2) || defined(XP_WIN32)
00342         clipboard = nsIClipboard::kGlobalClipboard;
00343 #else
00344         clipboard = nsIClipboard::kSelectionClipboard;
00345 #endif
00346 
00347         if (mailEditor)
00348           mailEditor->PasteAsQuotation(clipboard);
00349         else
00350           editor->Paste(clipboard);
00351 
00352         // Prevent the event from propagating up to be possibly handled
00353         // again by the containing window:
00354         mouseEvent->StopPropagation();
00355         mouseEvent->PreventDefault();
00356 
00357         // We processed the event, whether drop/paste succeeded or not
00358         return NS_OK;
00359       }
00360     }
00361   }
00362   return NS_OK;
00363 }
00364 
00365 nsresult
00366 nsTextEditorMouseListener::MouseDown(nsIDOMEvent* aMouseEvent)
00367 {
00368   return NS_OK;
00369 }
00370 
00371 nsresult
00372 nsTextEditorMouseListener::MouseUp(nsIDOMEvent* aMouseEvent)
00373 {
00374   return NS_OK;
00375 }
00376 
00377 
00378 nsresult
00379 nsTextEditorMouseListener::MouseDblClick(nsIDOMEvent* aMouseEvent)
00380 {
00381   return NS_OK;
00382 }
00383 
00384 
00385 
00386 nsresult
00387 nsTextEditorMouseListener::MouseOver(nsIDOMEvent* aMouseEvent)
00388 {
00389   return NS_OK;
00390 }
00391 
00392 
00393 
00394 nsresult
00395 nsTextEditorMouseListener::MouseOut(nsIDOMEvent* aMouseEvent)
00396 {
00397   return NS_OK;
00398 }
00399 
00400 
00401 /*
00402  * nsTextEditorTextListener implementation
00403  */
00404 
00405 NS_IMPL_ISUPPORTS2(nsTextEditorTextListener, nsIDOMEventListener, nsIDOMTextListener)
00406 
00407 
00408 nsTextEditorTextListener::nsTextEditorTextListener()
00409 :   mCommitText(PR_FALSE),
00410    mInTransaction(PR_FALSE)
00411 {
00412 }
00413 
00414 
00415 nsTextEditorTextListener::~nsTextEditorTextListener() 
00416 {
00417 }
00418 
00419 nsresult
00420 nsTextEditorTextListener::HandleEvent(nsIDOMEvent* aEvent)
00421 {
00422 #ifdef DEBUG_IME
00423    printf("nsTextEditorTextListener::HandleEvent\n");
00424 #endif
00425   return NS_OK;
00426 }
00427 
00428 
00429 
00430 nsresult
00431 nsTextEditorTextListener::HandleText(nsIDOMEvent* aTextEvent)
00432 {
00433 #ifdef DEBUG_IME
00434    printf("nsTextEditorTextListener::HandleText\n");
00435 #endif
00436    nsCOMPtr<nsIPrivateTextEvent> textEvent = do_QueryInterface(aTextEvent);
00437    if (!textEvent) {
00438       //non-ui event passed in.  bad things.
00439       return NS_OK;
00440    }
00441 
00442    nsAutoString            composedText;
00443    nsresult                result;
00444    nsIPrivateTextRangeList *textRangeList;
00445    nsTextEventReply        *textEventReply;
00446 
00447    textEvent->GetText(composedText);
00448    textEvent->GetInputRange(&textRangeList);
00449    textEvent->GetEventReply(&textEventReply);
00450    textRangeList->AddRef();
00451    nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(mEditor, &result);
00452    if (imeEditor) {
00453      PRUint32 flags;
00454      // if we are readonly or disabled, then do nothing.
00455      if (NS_SUCCEEDED(mEditor->GetFlags(&flags))) {
00456        if (flags & nsIPlaintextEditor::eEditorReadonlyMask || 
00457            flags & nsIPlaintextEditor::eEditorDisabledMask) {
00458 #if DEBUG_IME
00459          printf("nsTextEditorTextListener::HandleText,  Readonly or Disabled\n");
00460 #endif
00461          return NS_OK;
00462        }
00463      }
00464      result = imeEditor->SetCompositionString(composedText,textRangeList,textEventReply);
00465    }
00466    return result;
00467 }
00468 
00469 /*
00470  * nsTextEditorDragListener implementation
00471  */
00472 
00473 nsTextEditorDragListener::nsTextEditorDragListener() 
00474 : mEditor(nsnull)
00475 , mPresShell(nsnull)
00476 , mCaretDrawn(PR_FALSE)
00477 {
00478 }
00479 
00480 nsTextEditorDragListener::~nsTextEditorDragListener() 
00481 {
00482 }
00483 
00484 NS_IMPL_ISUPPORTS2(nsTextEditorDragListener, nsIDOMEventListener, nsIDOMDragListener)
00485 
00486 nsresult
00487 nsTextEditorDragListener::HandleEvent(nsIDOMEvent* aEvent)
00488 {
00489   return NS_OK;
00490 }
00491 
00492 
00493 nsresult
00494 nsTextEditorDragListener::DragGesture(nsIDOMEvent* aDragEvent)
00495 {
00496   if ( !mEditor )
00497     return NS_ERROR_NULL_POINTER;
00498   
00499   // ...figure out if a drag should be started...
00500   PRBool canDrag;
00501   nsresult rv = mEditor->CanDrag(aDragEvent, &canDrag);
00502   if ( NS_SUCCEEDED(rv) && canDrag )
00503     rv = mEditor->DoDrag(aDragEvent);
00504 
00505   return rv;
00506 }
00507 
00508 
00509 nsresult
00510 nsTextEditorDragListener::DragEnter(nsIDOMEvent* aDragEvent)
00511 {
00512   if (!mCaret)
00513   {
00514     nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
00515     if (presShell)
00516     {
00517       mCaret = do_CreateInstance("@mozilla.org/layout/caret;1");
00518       if (mCaret)
00519       {
00520         mCaret->Init(presShell);
00521         mCaret->SetCaretReadOnly(PR_TRUE);
00522       }
00523       mCaretDrawn = PR_FALSE;
00524     }
00525   }
00526   
00527   return DragOver(aDragEvent);
00528 }
00529 
00530 
00531 nsresult
00532 nsTextEditorDragListener::DragOver(nsIDOMEvent* aDragEvent)
00533 {
00534   // XXX cache this between drag events?
00535   nsresult rv;
00536   nsCOMPtr<nsIDragService> dragService = do_GetService("@mozilla.org/widget/dragservice;1", &rv);
00537   if (!dragService) return rv;
00538 
00539   // does the drag have flavors we can accept?
00540   nsCOMPtr<nsIDragSession> dragSession;
00541   dragService->GetCurrentSession(getter_AddRefs(dragSession));
00542   if (!dragSession) return NS_ERROR_FAILURE;
00543 
00544   PRBool canDrop = CanDrop(aDragEvent);
00545   if (canDrop)
00546   {
00547     nsCOMPtr<nsIDOMDocument> domdoc;
00548     mEditor->GetDocument(getter_AddRefs(domdoc));
00549     canDrop = nsEditorHookUtils::DoAllowDropHook(domdoc, aDragEvent, dragSession);
00550   }
00551 
00552   dragSession->SetCanDrop(canDrop);
00553 
00554   // We need to consume the event to prevent the browser's
00555   // default drag listeners from being fired. (Bug 199133)
00556 
00557   aDragEvent->PreventDefault(); // consumed
00558     
00559   if (canDrop)
00560   {
00561     if (mCaret)
00562     {
00563       nsCOMPtr<nsIDOMNSUIEvent> nsuiEvent (do_QueryInterface(aDragEvent));
00564       if (nsuiEvent)
00565       {
00566         nsCOMPtr<nsIDOMNode> parent;
00567         rv = nsuiEvent->GetRangeParent(getter_AddRefs(parent));
00568         if (NS_FAILED(rv)) return rv;
00569         if (!parent) return NS_ERROR_FAILURE;
00570 
00571         PRInt32 offset = 0;
00572         rv = nsuiEvent->GetRangeOffset(&offset);
00573         if (NS_FAILED(rv)) return rv;
00574 
00575         // to avoid flicker, we could track the node and offset to see if we moved
00576         if (mCaretDrawn)
00577           mCaret->EraseCaret();
00578         
00579         //mCaret->SetCaretVisible(PR_TRUE);   // make sure it's visible
00580         mCaret->DrawAtPosition(parent, offset);
00581         mCaretDrawn = PR_TRUE;
00582       }
00583     }
00584   }
00585   else
00586   {
00587     if (mCaret && mCaretDrawn)
00588     {
00589       mCaret->EraseCaret();
00590       mCaretDrawn = PR_FALSE;
00591     } 
00592   }
00593 
00594   return NS_OK;
00595 }
00596 
00597 
00598 nsresult
00599 nsTextEditorDragListener::DragExit(nsIDOMEvent* aDragEvent)
00600 {
00601   if (mCaret && mCaretDrawn)
00602   {
00603     mCaret->EraseCaret();
00604     mCaretDrawn = PR_FALSE;
00605   }
00606 
00607   return NS_OK;
00608 }
00609 
00610 
00611 
00612 nsresult
00613 nsTextEditorDragListener::DragDrop(nsIDOMEvent* aMouseEvent)
00614 {
00615   if (mCaret)
00616   {
00617     if (mCaretDrawn)
00618     {
00619       mCaret->EraseCaret();
00620       mCaretDrawn = PR_FALSE;
00621     }
00622     mCaret->SetCaretVisible(PR_FALSE);    // hide it, so that it turns off its timer
00623     mCaret = nsnull;      // release it
00624   }
00625 
00626   if (!mEditor)
00627     return NS_ERROR_FAILURE;
00628 
00629   PRBool canDrop = CanDrop(aMouseEvent);
00630   if (!canDrop)
00631   {
00632     // was it because we're read-only?
00633 
00634     PRUint32 flags;
00635     if (NS_SUCCEEDED(mEditor->GetFlags(&flags))
00636         && ((flags & nsIPlaintextEditor::eEditorDisabledMask) ||
00637             (flags & nsIPlaintextEditor::eEditorReadonlyMask)) )
00638     {
00639       // it was decided to "eat" the event as this is the "least surprise"
00640       // since someone else handling it might be unintentional and the 
00641       // user could probably re-drag to be not over the disabled/readonly 
00642       // editfields if that is what is desired.
00643       return aMouseEvent->StopPropagation();
00644     }
00645     return NS_OK;
00646   }
00647 
00648   aMouseEvent->StopPropagation();
00649   aMouseEvent->PreventDefault();
00650   return mEditor->InsertFromDrop(aMouseEvent);
00651 }
00652 
00653 PRBool
00654 nsTextEditorDragListener::CanDrop(nsIDOMEvent* aEvent)
00655 {
00656   // if the target doc is read-only, we can't drop
00657   PRUint32 flags;
00658   if (NS_FAILED(mEditor->GetFlags(&flags)))
00659     return PR_FALSE;
00660 
00661   if ((flags & nsIPlaintextEditor::eEditorDisabledMask) || 
00662       (flags & nsIPlaintextEditor::eEditorReadonlyMask)) {
00663     return PR_FALSE;
00664   }
00665 
00666   // XXX cache this between drag events?
00667   nsresult rv;
00668   nsCOMPtr<nsIDragService> dragService = do_GetService("@mozilla.org/widget/dragservice;1", &rv);
00669 
00670   // does the drag have flavors we can accept?
00671   nsCOMPtr<nsIDragSession> dragSession;
00672   if (dragService)
00673     dragService->GetCurrentSession(getter_AddRefs(dragSession));
00674   if (!dragSession) return PR_FALSE;
00675 
00676   PRBool flavorSupported = PR_FALSE;
00677   dragSession->IsDataFlavorSupported(kUnicodeMime, &flavorSupported);
00678 
00679   // if we aren't plaintext editing, we can accept more flavors
00680   if (!flavorSupported 
00681      && (flags & nsIPlaintextEditor::eEditorPlaintextMask) == 0)
00682   {
00683     dragSession->IsDataFlavorSupported(kHTMLMime, &flavorSupported);
00684     if (!flavorSupported)
00685       dragSession->IsDataFlavorSupported(kFileMime, &flavorSupported);
00686 #if 0
00687     if (!flavorSupported)
00688       dragSession->IsDataFlavorSupported(kJPEGImageMime, &flavorSupported);
00689 #endif
00690   }
00691 
00692   if (!flavorSupported)
00693     return PR_FALSE;     
00694 
00695   nsCOMPtr<nsIDOMDocument> domdoc;
00696   rv = mEditor->GetDocument(getter_AddRefs(domdoc));
00697   if (NS_FAILED(rv)) return PR_FALSE;
00698 
00699   nsCOMPtr<nsIDOMDocument> sourceDoc;
00700   rv = dragSession->GetSourceDocument(getter_AddRefs(sourceDoc));
00701   if (NS_FAILED(rv)) return PR_FALSE;
00702   if (domdoc == sourceDoc)      // source and dest are the same document; disallow drops within the selection
00703   {
00704     nsCOMPtr<nsISelection> selection;
00705     rv = mEditor->GetSelection(getter_AddRefs(selection));
00706     if (NS_FAILED(rv) || !selection)
00707       return PR_FALSE;
00708     
00709     PRBool isCollapsed;
00710     rv = selection->GetIsCollapsed(&isCollapsed);
00711     if (NS_FAILED(rv)) return PR_FALSE;
00712   
00713     // Don't bother if collapsed - can always drop
00714     if (!isCollapsed)
00715     {
00716       nsCOMPtr<nsIDOMNSUIEvent> nsuiEvent (do_QueryInterface(aEvent));
00717       if (!nsuiEvent) return PR_FALSE;
00718 
00719       nsCOMPtr<nsIDOMNode> parent;
00720       rv = nsuiEvent->GetRangeParent(getter_AddRefs(parent));
00721       if (NS_FAILED(rv) || !parent) return PR_FALSE;
00722 
00723       PRInt32 offset = 0;
00724       rv = nsuiEvent->GetRangeOffset(&offset);
00725       if (NS_FAILED(rv)) return PR_FALSE;
00726 
00727       PRInt32 rangeCount;
00728       rv = selection->GetRangeCount(&rangeCount);
00729       if (NS_FAILED(rv)) return PR_FALSE;
00730 
00731       for (PRInt32 i = 0; i < rangeCount; i++)
00732       {
00733         nsCOMPtr<nsIDOMRange> range;
00734         rv = selection->GetRangeAt(i, getter_AddRefs(range));
00735         nsCOMPtr<nsIDOMNSRange> nsrange(do_QueryInterface(range));
00736         if (NS_FAILED(rv) || !nsrange) 
00737           continue; //don't bail yet, iterate through them all
00738 
00739         PRBool inRange = PR_TRUE;
00740         (void)nsrange->IsPointInRange(parent, offset, &inRange);
00741         if (inRange)
00742           return PR_FALSE;  //okay, now you can bail, we are over the orginal selection
00743       }
00744     }
00745   }
00746   
00747   return PR_TRUE;
00748 }
00749 
00750 
00751 nsTextEditorCompositionListener::nsTextEditorCompositionListener()
00752 {
00753 }
00754 
00755 nsTextEditorCompositionListener::~nsTextEditorCompositionListener() 
00756 {
00757 }
00758 
00759 NS_IMPL_ISUPPORTS2(nsTextEditorCompositionListener, nsIDOMEventListener, nsIDOMCompositionListener)
00760 
00761 nsresult
00762 nsTextEditorCompositionListener::HandleEvent(nsIDOMEvent* aEvent)
00763 {
00764 #ifdef DEBUG_IME
00765    printf("nsTextEditorCompositionListener::HandleEvent\n");
00766 #endif
00767   return NS_OK;
00768 }
00769 
00770 void nsTextEditorCompositionListener::SetEditor(nsIEditor *aEditor)
00771 {
00772   nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(aEditor);
00773   if (!imeEditor) return;      // should return an error here!
00774   
00775   // note that we don't hold an extra reference here.
00776   mEditor = imeEditor;
00777 }
00778 
00779 nsresult
00780 nsTextEditorCompositionListener::HandleStartComposition(nsIDOMEvent* aCompositionEvent)
00781 {
00782 #ifdef DEBUG_IME
00783    printf("nsTextEditorCompositionListener::HandleStartComposition\n");
00784 #endif
00785   nsCOMPtr<nsIPrivateCompositionEvent> pCompositionEvent = do_QueryInterface(aCompositionEvent);
00786   if (!pCompositionEvent) return NS_ERROR_FAILURE;
00787   
00788   nsTextEventReply* eventReply;
00789   nsresult rv = pCompositionEvent->GetCompositionReply(&eventReply);
00790   if (NS_FAILED(rv)) return rv;
00791 
00792   return mEditor->BeginComposition(eventReply);
00793 }
00794 nsresult
00795 nsTextEditorCompositionListener::HandleQueryComposition(nsIDOMEvent* aCompositionEvent)
00796 {
00797 #ifdef DEBUG_IME
00798    printf("nsTextEditorCompositionListener::HandleQueryComposition\n");
00799 #endif
00800   nsCOMPtr<nsIPrivateCompositionEvent> pCompositionEvent = do_QueryInterface(aCompositionEvent);
00801   if (!pCompositionEvent) return NS_ERROR_FAILURE;
00802   
00803   nsTextEventReply* eventReply;
00804   nsresult rv = pCompositionEvent->GetCompositionReply(&eventReply);
00805   if (NS_FAILED(rv)) return rv;
00806 
00807   return mEditor->QueryComposition(eventReply);
00808 }
00809 
00810 nsresult
00811 nsTextEditorCompositionListener::HandleEndComposition(nsIDOMEvent* aCompositionEvent)
00812 {
00813 #ifdef DEBUG_IME
00814    printf("nsTextEditorCompositionListener::HandleEndComposition\n");
00815 #endif
00816    return mEditor->EndComposition();
00817 }
00818 
00819 
00820 nsresult
00821 nsTextEditorCompositionListener::HandleQueryReconversion(nsIDOMEvent* aReconversionEvent)
00822 {
00823 #ifdef DEBUG_IME
00824   printf("nsTextEditorCompositionListener::HandleQueryReconversion\n");
00825 #endif
00826   nsCOMPtr<nsIPrivateCompositionEvent> pCompositionEvent = do_QueryInterface(aReconversionEvent);
00827   if (!pCompositionEvent)
00828     return NS_ERROR_FAILURE;
00829 
00830   nsReconversionEventReply* eventReply;
00831   nsresult rv = pCompositionEvent->GetReconversionReply(&eventReply);
00832   if (NS_FAILED(rv))
00833     return rv;
00834 
00835   return mEditor->GetReconversionString(eventReply);
00836 }
00837 
00838 nsresult
00839 nsTextEditorCompositionListener::HandleQueryCaretRect(nsIDOMEvent* aQueryCaretRectEvent)
00840 {
00841 #ifdef DEBUG_IME
00842   printf("nsTextEditorCompositionListener::HandleQueryCaretRect\n");
00843 #endif
00844   nsCOMPtr<nsIPrivateCompositionEvent> pCompositionEvent = do_QueryInterface(aQueryCaretRectEvent);
00845   if (!pCompositionEvent)
00846     return NS_ERROR_FAILURE;
00847 
00848   nsQueryCaretRectEventReply* eventReply;
00849   nsresult rv = pCompositionEvent->GetQueryCaretRectReply(&eventReply);
00850   if (NS_FAILED(rv))
00851     return rv;
00852 
00853   return mEditor->GetQueryCaretRect(eventReply);
00854 }
00855 
00856 /*
00857  * Factory functions
00858  */
00859 
00860 
00861 
00862 nsresult 
00863 NS_NewEditorKeyListener(nsIDOMEventListener ** aInstancePtrResult, 
00864                         nsIEditor *aEditor)
00865 {
00866   nsTextEditorKeyListener* it = new nsTextEditorKeyListener();
00867   if (nsnull == it) {
00868     return NS_ERROR_OUT_OF_MEMORY;
00869   }
00870 
00871   it->SetEditor(aEditor);
00872 
00873   return it->QueryInterface(NS_GET_IID(nsIDOMEventListener), (void **) aInstancePtrResult);   
00874 }
00875 
00876 
00877 
00878 nsresult
00879 NS_NewEditorMouseListener(nsIDOMEventListener ** aInstancePtrResult, 
00880                           nsIEditor *aEditor)
00881 {
00882   nsTextEditorMouseListener* it = new nsTextEditorMouseListener();
00883   if (nsnull == it) {
00884     return NS_ERROR_OUT_OF_MEMORY;
00885   }
00886 
00887   it->SetEditor(aEditor);
00888 
00889   return it->QueryInterface(NS_GET_IID(nsIDOMEventListener), (void **) aInstancePtrResult);   
00890 }
00891 
00892 
00893 nsresult
00894 NS_NewEditorTextListener(nsIDOMEventListener** aInstancePtrResult, nsIEditor* aEditor)
00895 {
00896    nsTextEditorTextListener*   it = new nsTextEditorTextListener();
00897    if (nsnull==it) {
00898       return NS_ERROR_OUT_OF_MEMORY;
00899    }
00900 
00901    it->SetEditor(aEditor);
00902 
00903    return it->QueryInterface(NS_GET_IID(nsIDOMEventListener), (void **) aInstancePtrResult);
00904 }
00905 
00906 
00907 
00908 nsresult
00909 NS_NewEditorDragListener(nsIDOMEventListener ** aInstancePtrResult, nsIPresShell* aPresShell,
00910                           nsIEditor *aEditor)
00911 {
00912   nsTextEditorDragListener* it = new nsTextEditorDragListener();
00913   if (nsnull == it) {
00914     return NS_ERROR_OUT_OF_MEMORY;
00915   }
00916 
00917   it->SetEditor(aEditor);
00918   it->SetPresShell(aPresShell);
00919 
00920   return it->QueryInterface(NS_GET_IID(nsIDOMEventListener), (void **) aInstancePtrResult);   
00921 }
00922 
00923 nsresult
00924 NS_NewEditorCompositionListener(nsIDOMEventListener** aInstancePtrResult, nsIEditor* aEditor)
00925 {
00926    nsTextEditorCompositionListener*   it = new nsTextEditorCompositionListener();
00927    if (nsnull==it) {
00928       return NS_ERROR_OUT_OF_MEMORY;
00929    }
00930    it->SetEditor(aEditor);
00931   return it->QueryInterface(NS_GET_IID(nsIDOMEventListener), (void **) aInstancePtrResult);
00932 }
00933 
00934 nsresult 
00935 NS_NewEditorFocusListener(nsIDOMEventListener ** aInstancePtrResult, 
00936                           nsIEditor *aEditor)
00937 {
00938   nsTextEditorFocusListener* it = new nsTextEditorFocusListener();
00939   if (nsnull == it) {
00940     return NS_ERROR_OUT_OF_MEMORY;
00941   }
00942   it->SetEditor(aEditor);
00943   return it->QueryInterface(NS_GET_IID(nsIDOMEventListener), (void **) aInstancePtrResult);
00944 }
00945 
00946 
00947 
00948 /*
00949  * nsTextEditorFocusListener implementation
00950  */
00951 
00952 NS_IMPL_ISUPPORTS2(nsTextEditorFocusListener, nsIDOMEventListener, nsIDOMFocusListener)
00953 
00954 
00955 nsTextEditorFocusListener::nsTextEditorFocusListener() 
00956 {
00957 }
00958 
00959 nsTextEditorFocusListener::~nsTextEditorFocusListener() 
00960 {
00961 }
00962 
00963 nsresult
00964 nsTextEditorFocusListener::HandleEvent(nsIDOMEvent* aEvent)
00965 {
00966   return NS_OK;
00967 }
00968 
00969 static PRBool
00970 IsTargetFocused(nsIDOMEventTarget* aTarget)
00971 {
00972   // The event target could be either a content node or a document.
00973   nsCOMPtr<nsIDocument> doc;
00974   nsCOMPtr<nsIContent> content = do_QueryInterface(aTarget);
00975   if (content)
00976     doc = content->GetDocument();
00977   else
00978     doc = do_QueryInterface(aTarget);
00979 
00980   if (!doc)
00981     return PR_FALSE;
00982 
00983   nsIPresShell *shell = doc->GetShellAt(0);
00984   if (!shell)
00985     return PR_FALSE;
00986 
00987   nsPresContext *presContext = shell->GetPresContext();
00988   if (!presContext)
00989     return PR_FALSE;
00990 
00991   nsCOMPtr<nsIContent> focusedContent;
00992   presContext->EventStateManager()->
00993     GetFocusedContent(getter_AddRefs(focusedContent));
00994 
00995   // focusedContent will be null in the case where the document has focus,
00996   // and so will content.
00997 
00998   return (focusedContent == content);
00999 }
01000 
01001 nsresult
01002 nsTextEditorFocusListener::Focus(nsIDOMEvent* aEvent)
01003 {
01004   NS_ENSURE_ARG(aEvent);
01005   // It's possible for us to receive a focus when we're really not focused.
01006   // This happens, for example, when an onfocus handler that's hooked up
01007   // before this listener focuses something else.  In that case, all of the
01008   // onblur handlers will be fired synchronously, then the remaining focus
01009   // handlers will be fired from the original event.  So, check to see that
01010   // we're really focused.  (Note that the analogous situation does not
01011   // happen for blurs, due to the ordering in
01012   // nsEventStateManager::SendFocuBlur().
01013 
01014   nsCOMPtr<nsIDOMEventTarget> target;
01015   aEvent->GetTarget(getter_AddRefs(target));
01016   if (!IsTargetFocused(target))
01017     return NS_OK;
01018 
01019   // turn on selection and caret
01020   if (mEditor)
01021   {
01022     aEvent->StopPropagation();
01023 
01024     PRUint32 flags;
01025     mEditor->GetFlags(&flags);
01026     if (! (flags & nsIPlaintextEditor::eEditorDisabledMask))
01027     { // only enable caret and selection if the editor is not disabled
01028       nsCOMPtr<nsIEditor>editor = do_QueryInterface(mEditor);
01029       if (editor)
01030       {
01031         nsCOMPtr<nsISelectionController>selCon;
01032         editor->GetSelectionController(getter_AddRefs(selCon));
01033         if (selCon)
01034         {
01035           if (! (flags & nsIPlaintextEditor::eEditorReadonlyMask)) {
01036             selCon->SetCaretEnabled(PR_TRUE);
01037           }
01038 
01039           selCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
01040 #ifdef USE_HACK_REPAINT
01041   // begin hack repaint
01042           nsIViewManager* viewmgr = ps->GetViewManager();
01043           if (viewmgr) {
01044             nsIView* view;
01045             viewmgr->GetRootView(view);         // views are not refCounted
01046             if (view) {
01047               viewmgr->UpdateView(view,NS_VMREFRESH_IMMEDIATE);
01048             }
01049           }
01050   // end hack repaint
01051 #else
01052           selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
01053 #endif
01054         }
01055       }
01056     }
01057 
01058     nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(mEditor);
01059     if (imeEditor)
01060       imeEditor->NotifyIMEOnFocus();
01061   }
01062   return NS_OK;
01063 }
01064 
01065 nsresult
01066 nsTextEditorFocusListener::Blur(nsIDOMEvent* aEvent)
01067 {
01068   NS_ENSURE_ARG(aEvent);
01069   // turn off selection and caret
01070   if (mEditor)
01071   {
01072     aEvent->StopPropagation();
01073 
01074     // when imeEditor exists, call ForceCompositionEnd() to tell
01075     // the input focus is leaving first
01076     nsCOMPtr<nsIEditorIMESupport> imeEditor = do_QueryInterface(mEditor);
01077     if (imeEditor) {
01078       imeEditor->ForceCompositionEnd();
01079       imeEditor->NotifyIMEOnBlur();
01080     }
01081 
01082     nsCOMPtr<nsIEditor>editor = do_QueryInterface(mEditor);
01083     if (editor)
01084     {
01085       nsCOMPtr<nsISelectionController>selCon;
01086       editor->GetSelectionController(getter_AddRefs(selCon));
01087       if (selCon)
01088       {
01089         selCon->SetCaretEnabled(PR_FALSE);
01090 
01091         PRUint32 flags;
01092         mEditor->GetFlags(&flags);
01093         if((flags & nsIPlaintextEditor::eEditorWidgetMask)  ||
01094           (flags & nsIPlaintextEditor::eEditorPasswordMask) ||
01095           (flags & nsIPlaintextEditor::eEditorReadonlyMask) ||
01096           (flags & nsIPlaintextEditor::eEditorDisabledMask) ||
01097           (flags & nsIPlaintextEditor::eEditorFilterInputMask))
01098         {
01099           selCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);//hide but do NOT turn off
01100         }
01101         else
01102         {
01103           selCon->SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
01104         }
01105 
01106 #ifdef USE_HACK_REPAINT
01107 // begin hack repaint
01108         nsIViewManager* viewmgr = ps->GetViewManager();
01109         if (viewmgr) 
01110         {
01111           nsIView* view;
01112           viewmgr->GetRootView(view);         // views are not refCounted
01113           if (view) {
01114             viewmgr->UpdateView(view,NS_VMREFRESH_IMMEDIATE);
01115           }
01116         }
01117 // end hack repaint
01118 #else
01119         selCon->RepaintSelection(nsISelectionController::SELECTION_NORMAL);
01120 #endif
01121       }
01122     }
01123   }
01124   return NS_OK;
01125 }
01126