// RootView.java
// By Ned Etcode
// Copyright 1995, 1996 Netscape Communications Corp.  All rights reserved.

package netscape.application;

import netscape.util.*;

/** View subclass that functions as the top level View of the Applet frame
  * or ExternalWindow. It displays a Color and/or an Image (tiled, centered,
  * or scaled). All other Applet Views and InternalWindows are descendants
  * of the RootView.<p>You will rarely need to work with your
  * Applet's RootView. You will never instantiate a RootView
  * directly.
  */


// ALERT! Need a boolean that says we're in mouse drag mode


public class RootView extends View implements EventProcessor,
    ExtendedTarget {
    Color               _backgroundColor;
    Image               _image;
    FoundationPanel     panel;
    Application         application;

    Timer               _autoscrollTimer;
    ColorChooser        colorChooser;
    FontChooser         fontChooser;
    InternalWindow      _mainWindow, _documentWindow;
    View                _mouseView, _moveView, _focusedView, _windowClipView,
                        _mouseClickView;
    Vector              windows = new Vector();
    long                _lastClickTime;
    int                 _clickCount, _mouseX, _mouseY, _currentCursor,
                        _viewCursor, _overrideCursor = DEFAULT_CURSOR,
                        mouseDownCount, _imageDisplayStyle;
    Vector              dirtyViews = new Vector();
    boolean             _redrawTransWindows = true, recomputeCursor,
                        recomputeMoveView, isVisible, redrawAll = true;
    Vector              componentViews;
    MouseFilter         mouseFilter = new MouseFilter();

    static Vector _commands;

    static {
        _commands = new Vector();
        _commands.addElement(SHOW_FONT_CHOOSER);
        _commands.addElement(SHOW_COLOR_CHOOSER);
        _commands.addElement(NEW_FONT_SELECTION);
    }

    /* constructors */

    /** Constructs a RootView with origin <B>(0, 0)</b> and zero
      * width and height.
      */
    public RootView() {
        this(0, 0, 0, 0);
    }

    /** Constructs a RootView with bounds <B>rect</B>.
      */
    public RootView(Rect rect) {
        this(rect.x, rect.y, rect.width, rect.height);
    }

    /** Constructs a RootView with
      * bounds (<B>x</B>, <B>y</B>, <B>width</B>, <B>height</B>)
      */
    public RootView(int x, int y, int width, int height) {
        super(x, y, width, height);

        _backgroundColor = Color.gray;
        application = Application.application();
    }

    /* window management */

    void addWindowRelativeTo(InternalWindow aWindow, int position,
                             InternalWindow otherWindow) {
        if (aWindow == null) {
            return;
        }

        if (_windowClipView != null) {
            _windowClipView.addSubview(aWindow);
        } else {
            addSubview(aWindow);
        }

        makeWindowVisible(aWindow, position, otherWindow);
    }

    void removeWindow(InternalWindow aWindow) {
        InternalWindow  newMain, theWindow;
        int             index;

        if (aWindow == null) {
            return;
        }

        aWindow.removeFromSuperview();

        index = windows.indexOf(aWindow) - 1;
        if (index < 0) {
            index = windows.indexOf(aWindow);
        }

        windows.removeElement(aWindow);
        if (aWindow == _mainWindow) {
            if (index >= windows.count()) {
                theWindow = null;
            } else {
                theWindow = (InternalWindow)windows.elementAt(index);
            }
            if (_chooseNewMainAndDocumentWindow(theWindow)) {
                if (_mainWindow != null) {
                    _mainWindow.draw();
                }
            }
        }

        redraw(aWindow.bounds);
        redrawTransparentWindows(aWindow.bounds, null);

        createMouseEnterLater();
    }

    /** Returns the Vector containing all InternalWindows currently displayed
      * in the RootView.  Do <i>not</i> modify this Vector.
     */
    public Vector internalWindows() {
        return windows;
    }

    /** Returns the Application's main InternalWindow.  The main InternalWindow
      * represents the InternalWindow the user is currently working with, and
      * displays its title bar differently than all other InternalWindows.  The
      * IFC passes key Events to the View in the main InternalWindow that has
      * requested to receive them.
      * @see InternalWindow
      */
    public InternalWindow mainWindow() {
        return _mainWindow;
    }

    /** Returns the Application's document InternalWindow.  Generally, the main
     * InternalWindow is also the document InternalWindow, but not always.  The
     * document/main distinction comes in handy when you have a modeless dialog
     * that acts upon another InternalWindow.  For example, if you created a
     * Find dialog to let the user locate a word within the current document,
     * your application should provide some indication of which InternalWindow
     * the Find dialog is working with.
     * @see InternalWindow
     * @private
     */
    public InternalWindow documentWindow() {
        if (_documentWindow == null && _mainWindow != null &&
            _mainWindow.canBecomeDocument()) {
            return _mainWindow;
        }

        return _documentWindow;
    }

    boolean _chooseNewMainAndDocumentWindow(InternalWindow newMain) {
        InternalWindow      oldMain, newDocumentWindow, nextWindow;
        int                 i;
        boolean             redraw = false;

        if ((_mainWindow == newMain) ||
            (newMain != null && !newMain.canBecomeMain())) {
            return false;
        }

        oldMain = _mainWindow;
        _mainWindow = newMain;

        if (oldMain != null) {
            oldMain.didResignMain();
        }

        if (_mainWindow != null) {
            _mainWindow.didBecomeMain();
        }

        i = windows.count();
        newDocumentWindow = null;
        while (i-- > 0) {
            nextWindow = (InternalWindow)windows.elementAt(i);
            if (nextWindow.canBecomeDocument()) {
                newDocumentWindow = nextWindow;
                break;
            }
        }

        if (_documentWindow != newDocumentWindow) {
            if (_documentWindow != null) {
                if (_mainWindow != _documentWindow) {
                    _documentWindow.didResignDocument();
                } else {
                    _documentWindow.disableDrawing();
                    _documentWindow.didResignDocument();
                    _documentWindow.reenableDrawing();
                }
            }

            _documentWindow = newDocumentWindow;

            if (_documentWindow != null) {
                if (_mainWindow != _documentWindow) {
                    _documentWindow.didBecomeDocument();
                } else {
                    _documentWindow.disableDrawing();
                    _documentWindow.didBecomeDocument();
                    _documentWindow.reenableDrawing();
                }
            }
        }

        if (_documentWindow == _mainWindow) {
            _documentWindow = null;
        }

        createMouseEnterLater();

        return redraw;
    }

    void makeWindowVisible(InternalWindow aWindow, int position,
                           InternalWindow otherWindow) {
        InternalWindow          nextWindow = null;
        int             originalIndex, windowLayer, i;
        boolean         found, redrawWindow, inserted;

        if (aWindow == null) {
            return;
        }

        originalIndex = windows.indexOf(aWindow);
        windowLayer = aWindow.layer();

      /* make sure relative window is in the same layer */
        if (otherWindow != null) {
            if (otherWindow.layer() > windowLayer) {
                otherWindow = null;
                position = InternalWindow.ABOVE;
            } else if (otherWindow.layer() < windowLayer) {
                otherWindow = null;
                position = InternalWindow.BEHIND;
            }
        }

        windows.removeElement(aWindow);

        if (otherWindow != null) {
            if (position == InternalWindow.ABOVE) {
                found = windows.insertElementAfter(aWindow, otherWindow);
            } else {
                found = windows.insertElementBefore(aWindow, otherWindow);
            }

            if (found) {
                redrawWindow = _chooseNewMainAndDocumentWindow(aWindow);

                if (redrawWindow ||
                    originalIndex != windows.indexOf(aWindow)) {
                    aWindow.draw();
                    updateTransWindows(aWindow);
                }

                return;
            }

          /* otherWindow not in window list, so just ignore */
            otherWindow = null;
        }

      /* make first window in our layer */
        i = windows.count();
        while (i-- > 0) {
            nextWindow = (InternalWindow)windows.elementAt(i);

            if (nextWindow.layer() > windowLayer) {
                continue;
            } else if (nextWindow.layer() <= windowLayer) {
                break;
            }
        }

        if (nextWindow == null) {
            windows.insertElementAt(aWindow, 0);
            inserted = true;
        } else if (nextWindow.layer() > windowLayer) {
            inserted = windows.insertElementBefore(aWindow, nextWindow);
        } else {
            inserted = windows.insertElementAfter(aWindow, nextWindow);
        }
        if (!inserted) {
            windows.insertElementAt(aWindow, 0);
        }

        redrawWindow = _chooseNewMainAndDocumentWindow(aWindow);

        if (redrawWindow ||
            originalIndex != windows.indexOf(aWindow)) {
            aWindow.draw();
            updateTransWindows(aWindow);
        }
    }

    void updateTransWindows(InternalWindow belowWindow) {
        InternalWindow          nextWindow;
        Rect            belowRect;
        int             i, count;

        if (belowWindow == null) {
            return;
        }

        count = windows.indexOf(belowWindow);
        belowRect = belowWindow.bounds;
        for (i = 0; i < count; i++) {
            nextWindow = (InternalWindow)windows.elementAt(i);

            if (!nextWindow.isTransparent()) {
                continue;
            }

            if (nextWindow.bounds.intersects(belowRect) ||
                belowRect.intersects(nextWindow.bounds)) {

                nextWindow.updateDrawingBuffer();
            }
        }
    }


  /* drawing */

    void disableWindowsAbove(InternalWindow aWindow, boolean flag) {
        InternalWindow  nextWindow;
        int     i, count;

        if (aWindow == null) {
            return;
        }

        i = windows.indexOf(aWindow);

        if (i == -1) {
            return;
        }

        count = windows.count();
        for (; i < count; i++) {
            nextWindow = (InternalWindow)windows.elementAt(i);
            if (flag) {
                nextWindow.disableDrawing();
            } else {
                nextWindow.reenableDrawing();
            }
        }
    }

    Vector windowRects(Rect aRect, InternalWindow viewWindow) {
        InternalWindow    nextWindow;
        Vector            rectVector = null;
        int               i, count;

        count = windows.count();
        if (viewWindow != null) {
            i = windows.indexOf(viewWindow) + 1;
        } else {
            i = 0;
        }
        for (; i < count; i++) {
            nextWindow = (InternalWindow)windows.elementAt(i);

            if (nextWindow.bounds.intersects(aRect) ||
                aRect.intersects(nextWindow.bounds)) {
                if (rectVector == null) {
                    rectVector = VectorCache.newVector();
                }
                rectVector.addElement(new Rect(nextWindow.bounds));
            }
        }

        return rectVector;
    }

    void setRedrawTransparentWindows(boolean flag) {
        _redrawTransWindows = flag;
    }

    /** @private
      */
    public void redrawTransparentWindows(Rect clipRect,
                                         InternalWindow aboveWindow) {
        InternalWindow  nextWindow;
        int             i, count;
        Rect            windowRect = null;

        if (!_redrawTransWindows) {
            return;
        }

        count = windows.count();
        if (aboveWindow != null) {
            i = windows.indexOf(aboveWindow) + 1;
        } else {
            i = 0;
        }
        for (; i < count; i++) {
            nextWindow = (InternalWindow)windows.elementAt(i);

            if (!nextWindow.isTransparent()) {
                continue;
            }

            if (nextWindow.bounds.intersects(clipRect) ||
                clipRect.intersects(nextWindow.bounds)) {

                if (windowRect == null)
                    windowRect = Rect.newRect();

                convertRectToView(nextWindow, clipRect, windowRect);

                nextWindow.draw(windowRect);
            }
        }
        if (windowRect != null) {
            Rect.returnRect(windowRect);
        }
    }

     void paint(ApplicationEvent updateEvent) {
        UpdateFilter updateFilter = new UpdateFilter(updateEvent.rect);

        updateFilter.rootView = this;
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
        }
        application.eventLoop().filterEvents(updateFilter);

        if (redrawAll) {
            // We are redrawing the entire RootView because on Windows
            // there is a bug which causes the wrong clipRect to be given.
            Rect clipRect = Rect.newRect(0, 0, bounds.width, bounds.height);

            redraw(clipRect);
            redrawTransparentWindows(clipRect, null);
            Rect.returnRect(clipRect);
        } else {
            redraw(updateFilter._rect);
            redrawTransparentWindows(updateFilter._rect, null);
        }

        /* jla - this was needed to get repaint events to flush properly in
         * navigator on solaris
         */
        AWTCompatibility.awtToolkit().sync();
    }

    void resize(ApplicationEvent resizeEvent) {
        ResizeFilter resizeFilter = new ResizeFilter();

        resizeFilter.lastEvent = resizeEvent;
        application.eventLoop().filterEvents(resizeFilter);

        sizeTo(resizeFilter.lastEvent.rect.width,
               resizeFilter.lastEvent.rect.height);
    }

/* events */

    /** If called after a MOUSE_DOWN or MOUSE_DRAGGED Event, forces all
      * subsequent MOUSE_DRAGGED Events and the MOUSE_UP Event to go to
      * <b>aView</b>.
      */
    public void setMouseView(View aView) {
        _mouseView = aView;
    }

    /** Returns the View currently receiving MOUSE_DRAGGED or MOUSE_UP Events.
      * @see #setMouseView
      */
    public View mouseView() {
        return _mouseView;
    }


    /** Returns the View under point (<b>x</b>, <b>y</b>).
     */
    public View viewForMouse(int x, int y) {
        InternalWindow  nextWindow;
        View    theView = null;
        int     i;

        i = windows.count();
        while (i-- > 0 && theView == null) {
            nextWindow = (InternalWindow)windows.elementAt(i);
            theView = nextWindow.viewForMouse(x - nextWindow.bounds.x,
                                              y - nextWindow.bounds.y);
        }

        if (theView != null)
            return theView;
        else
            return super.viewForMouse(x, y);
    }

    void _mouseDown(MouseEvent event) {
        InternalWindow  mouseWindow;
        View            newMouseView, origMouseView, modalView;
        long            currentTime;

        mouseDownCount++;

        /* if one mouse button is already down, filter out additional downs */
        if (mouseDownCount > 1) {
            return;
        }

        /* who got clicked */
        newMouseView = viewForMouse(event.x, event.y);
        if (newMouseView == null) {
            _mouseView = null;
            return;
        }

        /* have to compare new view to current to determine if there's really
         * a double-click
         */
        // click count is now kept by the awt.  ALERT!
        currentTime = event.timeStamp;
        if (_mouseClickView == newMouseView &&
            (currentTime - _lastClickTime < 250)) {
            _clickCount++;
        } else {
            _clickCount = 1;
        }
        _lastClickTime = currentTime;
        event.setClickCount(_clickCount);

        /* we set this even if we're in a modal loop because subsequent mouse
         * moved events will be meant for the clicked view;  if it isn't the
         * same as the modal view or descendant, we'll just filter it at
         * that point
         */
        _mouseView = _mouseClickView = newMouseView;

        /* if we're in a modal loop, mouse clicks can only go to the modal view
         * or a descendant
         */
        if(viewExcludedFromModalSession(newMouseView))
            return;

        /* set the _mouseView's window's active view */
        if (!(_mouseView instanceof InternalWindow)) {
            mouseWindow = _mouseView.window();
            if (mouseWindow != null &&
                _chooseNewMainAndDocumentWindow(mouseWindow)) {
                mouseWindow.drawTitleBar();
            }
        }

        /* pass the event along */
        event.x -= _mouseView.absoluteX();
        event.y -= _mouseView.absoluteY();

        /* inside of mouse down, the program might set the mouse view to be
         * some other view;  if mouseDown() returns false and we don't check
         * for this, the new mouse view will not receive more mouse events
         */
        origMouseView = _mouseView;
        if (!_mouseView.mouseDown(event) && (origMouseView == _mouseView)) {
            _mouseView = null;
        }
    }

    /** Returns <b>true</b> if the RootView is visible. */
    public boolean isVisible() {
        return isVisible;
    }

    void setVisible(boolean flag) {
        if (isVisible != flag) {
            isVisible = flag;
            if (isVisible) {
                ancestorWasAddedToViewHierarchy(this);
                if (_focusedView != null) {
                    _focusedView.resumeFocus();
                }
            } else {
                ancestorWillRemoveFromViewHierarchy(this);
                if (_focusedView != null) {
                    _focusedView.pauseFocus();
                }
            }
        }
    }

    void _mouseDrag(MouseEvent event) {
        InternalWindow  newMenu;
        View            newView, modalView;
        Autoscroller    autoscroller;
        int             xOffset, yOffset;
        boolean         pointIsVisible;

        /* modal loop? */
        if(viewExcludedFromModalSession(_mouseView))
            return;

        if (_mouseView != null) {
            pointIsVisible =
                _mouseView.containsPointInVisibleRect(event.x, event.y);

            if (_mouseView.wantsAutoscrollEvents() && !pointIsVisible) {
                if (_autoscrollTimer == null) {
                    autoscroller = new Autoscroller();

                    _autoscrollTimer = new Timer(autoscroller, "autoscroll",
                                                 100);
                    _autoscrollTimer.start();
                    _autoscrollTimer.setData(_mouseView);
                    autoscroller.setEvent(
                                _mouseView.convertEventToView(null, event));
                } else {
                    autoscroller = (Autoscroller)_autoscrollTimer.target();
                    autoscroller.setEvent(
                                _mouseView.convertEventToView(null, event));
                }
            } else {
                if (_autoscrollTimer != null) {
                    _autoscrollTimer.stop();
                    _autoscrollTimer = null;
                }
                event.setClickCount(_clickCount);
                _mouseView.mouseDragged(event);
            }
        } else if (_autoscrollTimer != null) {
            _autoscrollTimer.stop();
            _autoscrollTimer = null;
        }
    }

    void _mouseUp(MouseEvent event) {
        View modalView;

        mouseDownCount--;

        /* if mouse still down (multiple mouse buttons) filter out the up */
        if (mouseDownCount > 0) {
            return;
        }

        /* modal loop? */
        if(viewExcludedFromModalSession(_mouseView))
            return;

        if (_mouseView != null) {
            event.setClickCount(_clickCount);
            _mouseView.mouseUp(event);

            if (_autoscrollTimer != null) {
                _autoscrollTimer.stop();
                _autoscrollTimer = null;
            }
        }
    }

    void createMouseEnter() {
        MouseEvent        fakeMouseEvent;

        fakeMouseEvent = new MouseEvent(System.currentTimeMillis(),
                                        MouseEvent.MOUSE_ENTERED, _mouseX,
                                        _mouseY, 0);
        _mouseEnter(fakeMouseEvent);
    }

    void createMouseEnterLater() {
        if (_mouseView == null) {
            recomputeMoveView = true;
        }
    }

    void _mouseEnter(MouseEvent event) {
        _mouseMove(event);
    }

    void _mouseMove(MouseEvent event) {
        View            newMoveView, oldMoveView;
        View            modalView;

        newMoveView = viewForMouse(event.x, event.y);

        /** When we have a modal view, we should send mouse moved events
         *  only to view that are descendant from the modal view
         */
        if(viewExcludedFromModalSession(newMoveView))
            return;

        if (newMoveView == _moveView) {
            if (_moveView == null) {
                return;
            }
            _moveView.mouseMoved(convertEventToView(_moveView, event));
        } else {
            oldMoveView = _moveView;
            _moveView = newMoveView;
            if (oldMoveView != null) {
                oldMoveView.mouseExited(
                    convertEventToView(oldMoveView, event));
            }

            if (_moveView != null) {
                _moveView.mouseEntered(convertEventToView(_moveView, event));
            }
        }

        updateCursorLater();
    }

    void _mouseExit(MouseEvent event) {
        if (_moveView != null) {
            _moveView.mouseExited(convertEventToView(_moveView, event));
            _moveView = null;

            updateCursorLater();
        }
    }



/* cursors */


    void flushCursor() {
        int        cursorToSet;

        if (_overrideCursor != DEFAULT_CURSOR) {
            cursorToSet = _overrideCursor;
        } else {
            cursorToSet = _viewCursor;
        }

        if (cursorToSet != _currentCursor) {
            panel.setCursor(cursorToSet);
            _currentCursor = cursorToSet;
        }
    }

    void computeCursor() {
        View        underView;
        Point        mousePoint;

        underView = viewForMouse(_mouseX, _mouseY);
        if (underView != null) {
            mousePoint = Point.newPoint();
            convertToView(underView, _mouseX, _mouseY, mousePoint);

            _viewCursor = underView.cursorForPoint(mousePoint.x, mousePoint.y);

            Point.returnPoint(mousePoint);
        } else {
            _viewCursor = ARROW_CURSOR;
        }

        flushCursor();
    }

    /** Returns the current cursor.
      * @see View#cursorForPoint
      * @see #setOverrideCursor
      */
    public int cursor() {
        return _currentCursor;
    }

    /** Forces the RootView's currently displayed cursor to the cursor
      * identified by <b>cursorIdent</b>, regardless of the cursor requested by
      * the View currently under the mouse.  To return the cursor to what it
      * should be, call <b>removeOverrideCursor()</b>.<p>
      * You will almost never call this method - instead, your View subclasses
      * will override their <b>cursorForPoint()</b> method to return the
      * correct cursor for that point within the View.
      * @see View#cursorForPoint
      * @see #removeOverrideCursor
      */
     public void setOverrideCursor(int cursorIdent) {
        if (cursorIdent < DEFAULT_CURSOR || cursorIdent > MOVE_CURSOR) {
            throw new InconsistencyException("Unknown cursor type: " +
                                             cursorIdent);
        }

        if (_overrideCursor != cursorIdent) {
            _overrideCursor = cursorIdent;
            flushCursor();
        }
    }

    /** Removes the override cursor, returning the cursor to the cursor
      * requested by the View currently under the mouse.
      * @see #setOverrideCursor
      */
    public void removeOverrideCursor() {
        setOverrideCursor(DEFAULT_CURSOR);
    }

    /** Forces the RootView to immediately compute and set the cursor
      * based on the View under the mouse.
      */
    public void updateCursor() {
        computeCursor();
    }

    /** Forces the RootView to recompute the current cursor upon
      * reaching the top of the EventLoop.
      */
    public void updateCursorLater() {
        recomputeCursor = true;
    }

    void _updateCursorAndMoveView() {
        if (recomputeMoveView) {
            createMouseEnter();
            recomputeMoveView = false;
            recomputeCursor = false;
        } else if (recomputeCursor) {
            computeCursor();
            recomputeCursor = false;
        }
    }



/* mouse events */


    void _keyDown(KeyEvent event) {
        View            keyView;
        ExternalWindow  externalWindow;
        boolean         eventHandled = false;

        keyView = _focusedView;
        externalWindow = externalWindow();

        if (externalWindow != null && event.isControlKeyDown()) {
            if (externalWindow.menu() != null) {
                eventHandled = externalWindow.menu().handleCommandKeyEvent(event);
            }
        }
        if (!eventHandled) {
            if (keyView != null && !viewExcludedFromModalSession(keyView)) {
                keyView.keyDown(event);
            } else {
                application().keyDown(event);
            }
        }
    }

    void _keyUp(KeyEvent event) {
        View    keyView;

        keyView = _focusedView;

        if (keyView != null && !viewExcludedFromModalSession(keyView)) {
            keyView.keyUp(event);
        } else {
            application().keyUp(event);
        }
    }

/* panels */

    /** Makes the ColorChooser visible.
      * @see ColorChooser
      */
    public void showColorChooser() {
        colorChooser().show();
    }

    /** Returns a reference to the shared ColorChooser.
      * @see ColorChooser
      */
    public ColorChooser colorChooser() {
        if (colorChooser == null) {
            InternalWindow window = new InternalWindow(40, 220, 10, 10);

            window.setRootView(this);
            colorChooser = new ColorChooser();
            colorChooser.setWindow(window);
        }
        return colorChooser;
    }

    /** Makes the FontChooser visible.
     * @see FontChooser
     */
    public void showFontChooser() {
        fontChooser().show();
    }

    /** Returns a reference to the shared FontChooser.
      * @see FontChooser
      */
    public FontChooser fontChooser() {
        if (fontChooser == null) {
            InternalWindow fontWindow = new InternalWindow(0, 0, 1, 1);

            fontWindow.setRootView(this);
            fontChooser = new FontChooser();
            fontChooser.setWindow(fontWindow);
            fontWindow.moveTo(30, 210);
        }

        return fontChooser;
    }

    /** Returns the RootView's ExternalWindow, if any.
      */
    public ExternalWindow externalWindow() {
        java.awt.Component component;

        for (component = panel;
             component != null;
             component = component.getParent()) {
            if (component instanceof FoundationFrame)
                return ((FoundationFrame) component).externalWindow;
            else if(component instanceof FoundationDialog)
                return ((FoundationDialog) component).externalWindow;
            else if(component instanceof FoundationWindow)
                return ((FoundationWindow) component).externalWindow;
        }
        return null;
    }

  /* running */

    MouseEvent removeMouseEvents(MouseEvent mouseEvent) {
        MouseEvent newEvent;

        newEvent = (MouseEvent)application.eventLoop().filterEvents(mouseFilter);

        return newEvent != null ? newEvent : mouseEvent;
    }

    void _convertMouseEventToMouseView(MouseEvent mouseEvent) {
        Point        mousePoint;

        if (_mouseView == null || mouseEvent == null) {
            return;
        }

        mousePoint = Point.newPoint();
        convertToView(_mouseView, mouseEvent.x, mouseEvent.y, mousePoint);

        mouseEvent.x = mousePoint.x;
        mouseEvent.y = mousePoint.y;

        Point.returnPoint(mousePoint);
    }

    /** Processes the various Events directed at the RootView.
      * You should never call this method.
      */
    public void processEvent(Event event) {
        View            newMoveView;
        MouseEvent      mouseEvent;
        int             type;

        /* If someone closes a window by calling dispose(), we may
         * have some event pending for a rootView that's no longer connected
         * to the application. When the rootView is removed, Application
         * call setApplet(null). This sets the application ivar to null.
         * if application is null, we should just ignore the event.
         */
        if (application == null)
            return;

        if (event instanceof MouseEvent) {
            mouseEvent = (MouseEvent)event;
            type = mouseEvent.type;

            if (type == MouseEvent.MOUSE_MOVED && mouseDownCount > 0 &&
                _mouseView != null) {
                /* something has gone wrong - we got a move with the
                 * mouse still down.  Pretend we got a mouse up at the
                 * last known point.
                 */
                _mouseUp(convertEventToView(_mouseView, mouseEvent));
            }

            if ((type == MouseEvent.MOUSE_DRAGGED) && _mouseView != null &&
                 _mouseView.wantsMouseEventCoalescing()) {

                mouseEvent = removeMouseEvents(mouseEvent);
                type = mouseEvent.type();
            } else if ((type == MouseEvent.MOUSE_MOVED) && _moveView != null &&
                 _moveView.wantsMouseEventCoalescing()) {

                mouseEvent = removeMouseEvents(mouseEvent);
                type = mouseEvent.type();
            }

            _mouseX = mouseEvent.x;
            _mouseY = mouseEvent.y;

            switch (type) {
                case MouseEvent.MOUSE_DOWN:
                    _mouseDown(mouseEvent);
                    break;
                case MouseEvent.MOUSE_DRAGGED:
                    _convertMouseEventToMouseView(mouseEvent);
                    _mouseDrag(mouseEvent);
                    break;
                case MouseEvent.MOUSE_UP:
                    _convertMouseEventToMouseView(mouseEvent);
                    _mouseUp(mouseEvent);

                    /* if released outside of move view, need to generate
                     * a mouse move event
                     */
                    newMoveView = viewForMouse(_mouseX, _mouseY);
                    if (newMoveView != _moveView) {
                        createMouseEnter();
                    }
                    _mouseClickView = _mouseView;
                    _mouseView = null;
                    break;
                case MouseEvent.MOUSE_ENTERED:
                    if (_mouseView == null) {
                        _mouseEnter(mouseEvent);
                    }
                    break;
                case MouseEvent.MOUSE_MOVED:
                    _mouseMove(mouseEvent);
                    break;
                case MouseEvent.MOUSE_EXITED:
                    if (_mouseView == null) {
                        _mouseExit(mouseEvent);
                    }
                    break;
            }
        } else if (event instanceof KeyEvent) {
            KeyEvent keyEvent = (KeyEvent) event;
            if (keyEvent.type == KeyEvent.KEY_DOWN)
                _keyDown(keyEvent);
            else
                _keyUp(keyEvent);
        } else if (event instanceof ApplicationEvent) {
            ExternalWindow externalWindow;

            switch (event.type) {
                case ApplicationEvent.GOT_FOCUS:

                    externalWindow = externalWindow();
                    application.makeFirstRootView(this);
                    if (externalWindow != null) {
                        externalWindow.didBecomeMain();
                    }
                    if (_focusedView != null) {
                        _focusedView.resumeFocus();
                    }
                    break;
                case ApplicationEvent.LOST_FOCUS:
                    externalWindow = externalWindow();
                    if (externalWindow != null) {
                        externalWindow.didResignMain();
                    }
                    if (_focusedView != null) {
                        _focusedView.pauseFocus();
                    }
                    break;
                case ApplicationEvent.UPDATE:
                    paint((ApplicationEvent)event);
                    break;
                case ApplicationEvent.RESIZE:
                    resize((ApplicationEvent)event);
                    /* There is no way to receive a notification when
                     * an external window get resized. This call to
                     * validateBounds() is a hack to give the external window
                     * a chance to call windowWillSizeBy() and to
                     * update its bounds
                     */
                    externalWindow = externalWindow();
                    if (externalWindow != null) {
                        externalWindow.validateBounds();
                    }
                    break;
                case ApplicationEvent.APPLET_STARTED:
                default:
            }
        }
    }

    void setFocusedView(View view, boolean hard) {
        if (view != _focusedView) {
            if (_focusedView != null) {
                if (hard)
                    _focusedView.stopFocus();
                else
                    _focusedView.pauseFocus();
            }
            _focusedView = view;
            if (_focusedView != null) {
                if (hard)
                    _focusedView.startFocus();
                else
                    _focusedView.resumeFocus();
            }
        }
    }

    /** Directs all key Events to <b>view</b>.
      */
    public void setFocusedView(View view) {
        setFocusedView(view, true);
    }

    /** Returns the View set to receive all key Events.
      * @see #setFocusedView
      */
    public View focusedView() {
        return _focusedView;
    }

    /** Implements the RootView's commands:
      * <ul>
      * <li>ExtendedTarget.SHOW_FONT_CHOOSER - makes the FontChooser visible.
      * <li>ExtendedTarget.SHOW_COLOR_CHOOSER - makes the ColorChooser visible.
      * <li>ExtendedTarget.NEW_FONT_SELECTION - causes the FontChooser to
      *                                         display the Font that is passed
      *                                         as the object.
      * </ul>
      */
    public void performCommand(String command, Object data) {
        if (SHOW_FONT_CHOOSER.equals(command)) {
            showFontChooser();
        } else if (SHOW_COLOR_CHOOSER.equals(command)) {
            showColorChooser();
        } else if (NEW_FONT_SELECTION.equals(command)) {
            if (fontChooser != null) {
                fontChooser.setFont((Font)data);
            }
        } else {
            throw new NoSuchMethodError("unknown command: " + command);
        }
    }

    /** Returns <b>true</b> for the commands that the RootView can perform.
      * @see #performCommand
      */
    public boolean canPerformCommand(String command) {
       return _commands.contains(command);
    }

    /** Sets the RootView's Color.
      */
    public void setColor(Color aColor) {
        _backgroundColor = aColor;
    }

    /** Returns the RootView's Color.
      * @see #setColor
      */
    public Color color() {
        return _backgroundColor;
    }

    /** Sets the RootView's Image.
      * @see #setImageDisplayStyle
      */
    public void setImage(Image anImage) {
        _image = anImage;
    }

    /** Returns the RootView's Image.
      * @see #setImage
      */
    public Image image() {
        return _image;
    }

    /** Sets the style the RootView uses to display its Image
      * (Image.CENTERED, Image.TILED, or Image.SCALED).
      */
    public void setImageDisplayStyle(int aStyle) {
        if (aStyle != Image.CENTERED && aStyle != Image.TILED &&
            aStyle != Image.SCALED) {
            throw new InconsistencyException("Unknown image display style: " +
                aStyle);
        }

        _imageDisplayStyle = aStyle;
    }

    /** Returns the style the RootView uses to display its Image.
      * @see #setImageDisplayStyle
      */
    public int imageDisplayStyle() {
        return _imageDisplayStyle;
    }

    /** Returns <b>false</b> - RootViews are not transparent.
      */
    public boolean isTransparent() {
        return false;
    }

    /** Draws the RootView's contents.
      */
    public void drawView(Graphics g) {
        if (_image == null || (_imageDisplayStyle == Image.CENTERED &&
                            (_image.width() < bounds.width ||
                             _image.height() < bounds.height))) {
            if (_backgroundColor != null) {
                g.setColor(_backgroundColor);
                g.fillRect(g.clipRect());
            }
        }

        if (_image != null) {
            _image.drawWithStyle(g, 0, 0,
                                 bounds.width, bounds.height,
                                 _imageDisplayStyle);
        }
    }

    /** Overridden to draw just the <b>aRect</b> portion of the RootView,
      * ignoring any Windows that may intersect the Rect.  To draw everything
      * within a given Rect, call <b>redraw()</b>.
      * @see #redraw
      */
    public void draw(Graphics g, Rect aRect) {
        InternalWindow  nextWindow;
        Vector          enableVector;
        int             i;

        enableVector = new Vector();

        i = windows.count();
        while (i-- > 0) {
            nextWindow = (InternalWindow)windows.elementAt(i);
            if (nextWindow.isDrawingEnabled()) {
                nextWindow.disableDrawing();
                enableVector.addElement(nextWindow);
            }
        }

        super.draw(g, aRect);

        i = enableVector.count();
        while (i-- > 0) {
            nextWindow = (InternalWindow)enableVector.elementAt(i);
            nextWindow.reenableDrawing();
        }
    }

    // opaqueView is dead.  ALERT!

    View viewWithBuffer(View someView, Rect updateRect) {
        View    nextView, bufferView, opaqueView = null;
        int     i;
        Rect    subRect;
        Vector  views;

        i = someView.subviewCount();
        if (i == 0)
            return null;

        views = someView.subviews();
        subRect = Rect.newRect(0, 0, updateRect.width, updateRect.height);

        while (i-- > 0) {
            nextView = (View)views.elementAt(i);
            if (nextView instanceof InternalWindow ||
                !nextView.bounds.contains(updateRect)) {
                continue;
            }

            if (nextView.isBuffered()) {
                Rect.returnRect(subRect);
                return nextView;
            }

            if (!nextView.isTransparent()) {
//              opaqueView = nextView;
            }

            subRect.x = updateRect.x - nextView.bounds.x;
            subRect.y = updateRect.y - nextView.bounds.y;
            bufferView = viewWithBuffer(nextView, subRect);
            if (bufferView != null) {
                Rect.returnRect(subRect);
                return bufferView;
            }
            if (opaqueView != null) {
                Rect.returnRect(subRect);
                return opaqueView;
            }
        }

        Rect.returnRect(subRect);

        return null;
    }

    /** Similar to <b>draw()</b>, except that it draws everything intersecting
      * the Rect <b>aRect</b> (RootView and InternalWindows).
      */
    void redraw(Graphics g, Rect aRect) {
        InternalWindow  nextWindow;
        View            bufferView, viewForRect;
        Vector          intersectedWindows;
        int             i, count;
        boolean         foundWindow;
        Rect            subRect;

        subRect = Rect.newRect(0, 0, aRect.width, aRect.height);

        if (aRect == null) {
            aRect = new Rect(0, 0, bounds.width, bounds.height);
        }

        intersectedWindows = VectorCache.newVector();

        setRedrawTransparentWindows(false);

        /* which windows intersect aRect, and is there one that contains it? */
        count = windows.count();
        foundWindow = false;
        for (i = count; (i-- > 0) && !foundWindow; ) {
            nextWindow = (InternalWindow)windows.elementAt(i);
            if (nextWindow.bounds.intersects(aRect)) {
                intersectedWindows.addElement(nextWindow);
                if (!nextWindow.isTransparent() &&
                    nextWindow.bounds.contains(aRect)) {
                    foundWindow = true;
                }
            }
        }

        /* no window completely contains aRect, so find an opaque view that
         * does
         */
        if (!foundWindow) {
            viewForRect = _viewForRect(aRect, null);

            if (viewForRect != null) {
                convertRectToView(viewForRect, aRect, subRect);
                g.pushState();
                g.translate(aRect.x - subRect.x, aRect.y - subRect.y);
                viewForRect.draw(g, subRect);
                g.popState();
            } else {
                draw(g, aRect);
            }
        }

        count = intersectedWindows.count();
        for (i = 0; i < count; i++) {
            View windowSuperview;

            nextWindow = (InternalWindow)intersectedWindows.elementAt(i);

            windowSuperview = nextWindow.superview();
            convertRectToView(windowSuperview, aRect, subRect);
            viewForRect = nextWindow._viewForRect(subRect, windowSuperview);
            if (viewForRect == null) {
                viewForRect = nextWindow;
            }
            convertRectToView(viewForRect, aRect, subRect);

            g.pushState();
            g.translate(aRect.x - subRect.x, aRect.y - subRect.y);
            viewForRect.draw(g, subRect);
            g.popState();
        }

        setRedrawTransparentWindows(true);
        Rect.returnRect(subRect);
        VectorCache.returnVector(intersectedWindows);
    }

    /** Similar to <b>draw()</b>, except that it draws everything intersecting
      * the Rect <b>aRect</b> (RootView and InternalWindows).
      */
    public void redraw(Rect aRect) {
        Graphics graphics = createGraphics();

        redraw(graphics, aRect);
        graphics.dispose();
    }

    /** Convenience method for drawing the entire RootView.  Equivalent
      * to the following code:
      * <pre>
      *     redraw(new Graphics(rootView()), null);
      * </pre>
      */
    void redraw() {
        Graphics graphics = createGraphics();
        Rect    tmpRect;

        tmpRect = Rect.newRect(0, 0, bounds.width, bounds.height);
        redraw(graphics, tmpRect);
        Rect.returnRect(tmpRect);

        graphics.dispose();
    }

    /** View calls this method when it goes from not dirty to dirty.
      */
    synchronized void markDirty(View dirtyView) {
        // Add the view to the list of views of dirtyViews.  If we are
        // currently processing dirtyViews then dirtyViews will be set to
        // null.  Nobody should mark themselves dirty while drawing.

        if (dirtyViews != null) {
            dirtyViews.addElement(dirtyView);
        } else {
            throw new InconsistencyException("Don't dirty a View while the list of dirty views is being drawn!");
        }
    }

    /** View calls this method when it goes from dirty to not dirty.
      */
    synchronized void markClean(View dirtyView) {
        // If a view becomes clean, remove it from the dirtyViews Vector.  If
        // dirtyViews is null then we are in the process of cleaning the
        // dirtyViews so we can just ignore the request since the vector is
        // about to be emptied anyway.

        if (dirtyViews != null)
            dirtyViews.removeElement(dirtyView);
    }

    /** Marks all of the RootView's dirty subviews as clean, without
      * drawing them.
      */
    public synchronized void resetDirtyViews() {
        int i, count;
        View dirtyView;
        Vector tmpDirtyViews;

        tmpDirtyViews = dirtyViews;
        dirtyViews = null;

        count = tmpDirtyViews.count();
        for (i = 0; i < count; i++) {
            dirtyView = (View)tmpDirtyViews.elementAt(i);
            dirtyView.setDirty(false);
        }

        tmpDirtyViews.removeAllElements();
        dirtyViews = tmpDirtyViews;
    }

    /** Draws all of the RootView's dirty subviews. This method
      * is called automatically after the EventLoop processes an Event.
      */
    public synchronized void drawDirtyViews() {
        int i, count;
        Rect tmp;
        Vector roots, tmpDirtyViews;
        View dirtyView;

        count = dirtyViews.count();
        if (count == 0)
            return;

        // While we are processing the vector of dirty views, move it aside
        // so that it can't be mucked with by errant views.  The method
        // markDirty() will throw an exception if someone tries to mark a view
        // dirty while we are processing the list.

        tmpDirtyViews = dirtyViews;

        try {
            dirtyViews = null;

            roots = new Vector(count);
            tmp = new Rect();

            for (i = 0; i < count; i++) {
                dirtyView = (View)tmpDirtyViews.elementAt(i);
                collectDirtyViews(dirtyView, roots, tmp);
            }

            count = roots.count();
            for (i = 0; i < count; i++) {
                dirtyView = (View)roots.elementAt(i);
                dirtyView.draw(dirtyView.dirtyRect);
            }
        } finally {
            dirtyViews = tmpDirtyViews;
            resetDirtyViews();
        }
    }

    void collectDirtyViews(View dirtyView, Vector roots, Rect tmp) {
        int dx, dy, rootDx, rootDy;
        View view, rootDirtyView;

        // Find the highest superview which is dirty.  When we get out of this
        // rootDx and rootDy will contain the translation from the
        // rootDirtyView's coordinate system to the coordinates of the
        // original dirtyView.  The tmp Rect is also used to compute the
        // visible portion of the dirtyRect.

        view = rootDirtyView = dirtyView;
        dx = rootDx = 0;
        dy = rootDy = 0;
        tmp.setBounds(0, 0, view.width(), view.height());

        // If the dirtyRect is null, then the whole View is dirty.

        // If this can be done last, it might optimize some special cases.
        // ALERT!
        if (dirtyView.dirtyRect != null)
            tmp.intersectWith(dirtyView.dirtyRect);

        if (tmp.isEmpty())
            return;

        do {
            dx += view.bounds.x;
            dy += view.bounds.y;
            tmp.moveBy(view.bounds.x, view.bounds.y);

            view = view.superview();

            if (view != null) {
                tmp.intersectWith(0, 0, view.width(), view.height());
                if (tmp.isEmpty())
                    return;

                if (view.isDirty()) {
                    rootDirtyView = view;
                    rootDx = dx;
                    rootDy = dy;
                }
            }
        } while (view != null && !(view instanceof InternalWindow));

        // At this point the visible portion of the dirtyRect is in the
        // RootView's coordinate system.  Put it in the coordinate
        // system of the rootDirtyView.  Also add the dirtyRect of the
        // dirtyView to the rootDirtyView.

        if (dirtyView != rootDirtyView) {
            tmp.moveBy(rootDx - dx, rootDy - dy);
            rootDirtyView.addDirtyRect(tmp);
        }

        // If we haven't seen this root before, then we need to add it to the
        // list of root dirty Views.

        if (!roots.containsIdentical(rootDirtyView))
            roots.addElement(rootDirtyView);
    }

    /** Overridden to prevent mouse Events from being sent to the
      * RootView. Returns <b>false</b>.
      */
    public boolean mouseDown(MouseEvent event) {
        return false;
    }

    /** Overridden to return this View.
      */
    public RootView rootView() {
        if (panel == null)
            return super.rootView();
        else
            return this;
    }

    void setPanel(FoundationPanel p) {
        panel = p;
    }

    /** Returns the FoundationPanel the RootView is being displayed in. */
    public FoundationPanel panel() {
        return panel;
    }

    /** Returns the Application that owns the RootView.
      */
    Application application() {
        return application;
    }

    void setApplication(Application a) {
        application = a;
    }

    /** @private
      */
    public void setWindowClipView(View aView) {
        _windowClipView = aView;
    }

    /** @private
      */
    public View windowClipView() {
        return _windowClipView;
    }

    void addComponentView(AWTComponentView componentView) {
        if (componentViews == null) {
            componentViews = new Vector();
        }

        componentViews.addElement(componentView);
        componentView.setComponentBounds();
        panel.add(componentView.component);
    }

    void removeComponentView(AWTComponentView componentView) {
        if (componentViews == null) {
            componentViews = new Vector();
        }

        componentViews.removeElement(componentView);
        panel.remove(componentView.component);
    }

    private final void subviewDidResizeOrMove(View aView) {
        AWTComponentView        view;
        int                     count, i;

        if (componentViews != null) {
            count = componentViews.count();

            for (i = 0; i < count; i++) {
               view = (AWTComponentView)componentViews.elementAt(i);

                if (view.descendsFrom(aView)) {
                    view.setComponentBounds();
                }
            }
        }

        createMouseEnterLater();
    }

    /** @private
      */
    public void subviewDidResize(View aView) {
        subviewDidResizeOrMove(aView);
    }

    /** @private
      */
    public void subviewDidMove(View aView) {
        subviewDidResizeOrMove(aView);
    }

    /** @private
      */
    public boolean canDraw() {
        if (panel.getParent() == null)
            return false;
        else
            return isVisible;
    }

    /** Returns a newly-allocated Point containing the mouse's last known
      * location, in the RootView's coordinate system.
      */
    public Point mousePoint() {
        return new Point(_mouseX, _mouseY);
    }

    boolean viewExcludedFromModalSession( View aView ) {
        View modalView;

        if(aView == null)
            return true;

        modalView = Application.application().modalView();
        if( modalView != null && !aView.descendsFrom(modalView)) {
            if(aView instanceof DragView || aView instanceof InternalWindow)
                return false;
            else
                return true;
        } else
            return false;
    }

    /** @private */
    public void setRedrawAll(boolean flag) {
        redrawAll = flag;
    }

    /** @private */
    public boolean redrawAll() {
        return redrawAll;
    }
}
