1 /*
   2  * Copyright (c) 1995, 2007, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 package sun.awt.motif;
  26 
  27 import java.util.Vector;
  28 import java.awt.*;
  29 import java.awt.peer.*;
  30 import java.awt.event.*;
  31 import java.awt.image.BufferedImage;
  32 import java.awt.image.DataBuffer;
  33 import java.awt.image.DataBufferByte;
  34 import java.awt.image.DataBufferInt;
  35 import java.awt.image.ImageObserver;
  36 import sun.awt.image.ImageRepresentation;
  37 import sun.awt.motif.MInputMethod;
  38 import sun.awt.motif.MInputMethodControl;
  39 import sun.awt.im.*;
  40 import sun.awt.DisplayChangedListener;
  41 import sun.awt.SunToolkit;
  42 import sun.awt.X11GraphicsDevice;
  43 
  44 class MWindowPeer extends MPanelPeer implements WindowPeer,
  45 DisplayChangedListener {
  46 
  47     Insets insets = new Insets( 0, 0, 0, 0 );
  48     MWindowAttributes winAttr;
  49     static Vector allWindows = new Vector();
  50     int         iconWidth  = -1;
  51     int         iconHeight = -1;
  52 
  53     int dropTargetCount = 0;
  54     boolean alwaysOnTop;
  55 
  56     native void pCreate(MComponentPeer parent, String targetClassName, boolean isFocusableWindow);
  57     native void pShow();
  58     native void pToFront();
  59     native void pShowModal(boolean isModal);
  60     native void pHide();
  61     native void pReshape(int x, int y, int width, int height);
  62     native void pDispose();
  63     native void pSetTitle(String title);
  64     public native void setState(int state);
  65     public native int getState();
  66 
  67     public native void setResizable(boolean resizable);
  68     native void addTextComponentNative(MComponentPeer tc);
  69     native void removeTextComponentNative();
  70     native void pSetIMMOption(String option);
  71     native void pSetMenuBar(MMenuBarPeer mbpeer);
  72     native void setSaveUnder(boolean state);
  73 
  74     native void registerX11DropTarget(Component target);
  75     native void unregisterX11DropTarget(Component target);
  76     native void updateAlwaysOnTop(boolean isAlwaysOnTop);
  77 
  78     private static native void initIDs();
  79 
  80     static {
  81         initIDs();
  82     }
  83 
  84     // this function is privileged! do not change it to public!
  85     private static int getInset(final String name, final int def) {
  86         Integer tmp = (Integer) java.security.AccessController.doPrivileged(
  87             new sun.security.action.GetIntegerAction(name, def));
  88         return tmp.intValue();
  89     }
  90 
  91     MWindowPeer() {
  92         insets = new Insets(0,0,0,0);
  93         winAttr = new MWindowAttributes();
  94     }
  95 
  96     MWindowPeer(Window target) {
  97 
  98         this();
  99         init(target);
 100 
 101         allWindows.addElement(this);
 102     }
 103 
 104     void create(MComponentPeer parent) {
 105         pCreate(parent, target.getClass().getName(), ((Window)target).isFocusableWindow());
 106     }
 107 
 108     void init( Window target ) {
 109         if ( winAttr.nativeDecor == true ) {
 110             insets.top = getInset("awt.frame.topInset", -1);
 111             insets.left = getInset("awt.frame.leftInset", -1);
 112             insets.bottom = getInset("awt.frame.bottomInset", -1);
 113             insets.right = getInset("awt.frame.rightInset", -1);
 114         }
 115 
 116         Rectangle bounds = target.getBounds();
 117         sysX = bounds.x;
 118         sysY = bounds.y;
 119         sysW = bounds.width;
 120         sysH = bounds.height;
 121 
 122         super.init(target);
 123         InputMethodManager imm = InputMethodManager.getInstance();
 124         String menuString = imm.getTriggerMenuString();
 125         if (menuString != null)
 126         {
 127             pSetIMMOption(menuString);
 128         }
 129         pSetTitle(winAttr.title);
 130 
 131         /*
 132          * For Windows and undecorated Frames and Dialogs this just
 133          * disables/enables resizing functions in the system menu.
 134          */
 135         setResizable(winAttr.isResizable);
 136 
 137         setSaveUnder(true);
 138 
 139         Font f = target.getFont();
 140         if (f == null) {
 141             f = defaultFont;
 142             target.setFont(f);
 143             setFont(f);
 144         }
 145         Color c = target.getBackground();
 146         if (c == null) {
 147             target.setBackground(SystemColor.window);
 148             setBackground(SystemColor.window);
 149         }
 150         c = target.getForeground();
 151         if (c == null) {
 152             target.setForeground(SystemColor.windowText);
 153             setForeground(SystemColor.windowText);
 154         }
 155         alwaysOnTop = ((Window)target).isAlwaysOnTop() && ((Window)target).isAlwaysOnTopSupported();
 156 
 157         GraphicsConfiguration gc = getGraphicsConfiguration();
 158         ((X11GraphicsDevice)gc.getDevice()).addDisplayChangedListener(this);
 159 
 160     }
 161 
 162     /* Support for multiple icons is not implemented in MAWT */
 163     public void updateIconImages() {
 164         if (this instanceof MFramePeer) {
 165             ((MFramePeer)this).setIconImage(((Frame)target).getIconImage());
 166         }
 167     }
 168 
 169 
 170     /* Not implemented in MAWT */
 171     public void updateMinimumSize() {
 172     }
 173 
 174     protected void disposeImpl() {
 175         allWindows.removeElement(this);
 176         super.disposeImpl();
 177     }
 178 
 179     public native void toBack();
 180 
 181     public void setAlwaysOnTop(boolean alwaysOnTop) {
 182         this.alwaysOnTop = alwaysOnTop;
 183         updateAlwaysOnTop(alwaysOnTop);
 184     }
 185 
 186     public void updateAlwaysOnTopState() {
 187         setAlwaysOnTop(((Window)target).isAlwaysOnTop());
 188     }
 189 
 190     public void toFront() {
 191         if (target.isVisible()) {
 192             updateFocusableWindowState();
 193             pToFront();
 194         }
 195     }
 196 
 197     public void updateFocusableWindowState() {
 198         setFocusableWindow(((Window)target).isFocusableWindow());
 199     }
 200     native void setFocusableWindow(boolean value);
 201 
 202     public void setVisible( boolean b ) {
 203         if (b) {
 204             updateFocusableWindowState();
 205         }
 206         super.setVisible(b);
 207         updateAlwaysOnTop(alwaysOnTop);
 208     }
 209 
 210     public Insets getInsets() {
 211         return insets;
 212     }
 213 
 214     public void handleQuit() {
 215         postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_CLOSING));
 216     }
 217 
 218     // XXX: nasty WM, foul play.  spank WM author.
 219     public void handleDestroy() {
 220         final Window target = (Window)this.target;
 221         SunToolkit.executeOnEventHandlerThread(target,
 222                                                new Runnable() {
 223                                                    public void run() {
 224                                                        // This seems like the only reasonable thing we
 225                                                        // could do in this situation as the native window
 226                                                        // is already dead.
 227                                                        target.dispose();
 228                                                    }
 229                                                });
 230     }
 231 
 232 
 233     // NOTE: This method may be called by privileged threads.
 234     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
 235     public void handleIconify() {
 236         postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_ICONIFIED));
 237     }
 238 
 239     // NOTE: This method may be called by privileged threads.
 240     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
 241     public void handleDeiconify() {
 242         postEvent(new WindowEvent((Window)target, WindowEvent.WINDOW_DEICONIFIED));
 243     }
 244 
 245     // NOTE: This method may be called by privileged threads.
 246     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
 247     public void handleStateChange(int oldState, int newState) {
 248         postEvent(new WindowEvent((Window)target,
 249                                   WindowEvent.WINDOW_STATE_CHANGED,
 250                                   oldState, newState));
 251     }
 252 
 253     /**
 254      * Called to inform the Window that its size has changed and it
 255      * should layout its children.
 256      */
 257     // NOTE: This method may be called by privileged threads.
 258     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
 259     public void handleResize(int width, int height) {
 260         sysW = width;
 261         sysH = height;
 262 
 263         // REMIND: Is this secure? Can client code subclass input method?
 264         if (!tcList.isEmpty() &&
 265             !imList.isEmpty()){
 266             int i;
 267             for (i = 0; i < imList.size(); i++){
 268                 ((MInputMethod)imList.elementAt(i)).configureStatus();
 269             }
 270         }
 271         validateSurface(width, height);
 272         postEvent(new ComponentEvent(target, ComponentEvent.COMPONENT_RESIZED));
 273     }
 274 
 275 
 276     /**
 277      * DEPRECATED:  Replaced by getInsets().
 278      */
 279     public Insets insets() {
 280         return getInsets();
 281     }
 282 
 283     public void handleMoved(int x, int y) {
 284         sysX = x;
 285         sysY = y;
 286         postEvent(new ComponentEvent(target, ComponentEvent.COMPONENT_MOVED));
 287     }
 288 
 289     private native AWTEvent wrapInSequenced(AWTEvent event);
 290 
 291     // NOTE: This method may be called by privileged threads.
 292     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
 293     public void handleWindowFocusIn() {
 294         WindowEvent we = new WindowEvent((Window)target, WindowEvent.WINDOW_GAINED_FOCUS);
 295         /* wrap in Sequenced, then post*/
 296         postEvent(wrapInSequenced((AWTEvent) we));
 297     }
 298 
 299     // NOTE: This method may be called by privileged threads.
 300     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
 301     public void handleWindowFocusOut(Window oppositeWindow) {
 302         WindowEvent we = new WindowEvent((Window)target, WindowEvent.WINDOW_LOST_FOCUS,
 303                                          oppositeWindow);
 304         /* wrap in Sequenced, then post*/
 305         postEvent(wrapInSequenced((AWTEvent) we));
 306     }
 307 
 308 
 309 // relocation of Imm stuff
 310     private Vector imList = new Vector();
 311     private Vector tcList = new Vector();
 312 
 313     // NOTE: This method is called by privileged threads.
 314     //       DO NOT INVOKE CLIENT CODE ON THIS THREAD!
 315     void notifyIMMOptionChange(){
 316 
 317         // REMIND: IS THIS SECURE??? CAN USER CODE SUBCLASS INPUTMETHODMGR???
 318         InputMethodManager.getInstance().notifyChangeRequest(target);
 319     }
 320 
 321     public void addInputMethod(MInputMethod im) {
 322         if (!imList.contains(im))
 323             imList.addElement(im);
 324     }
 325 
 326     public void removeInputMethod(MInputMethod im) {
 327         if (imList.contains(im))
 328             imList.removeElement(im);
 329     }
 330 
 331     public void addTextComponent(MComponentPeer tc) {
 332         if (tcList.contains(tc))
 333             return;
 334         if (tcList.isEmpty()){
 335             addTextComponentNative(tc);
 336             if (!imList.isEmpty()) {
 337                 for (int i = 0; i < imList.size(); i++) {
 338                     ((MInputMethod)imList.elementAt(i)).reconfigureXIC((MInputMethodControl)this);
 339                 }
 340             }
 341             MToolkit.executeOnEventHandlerThread(target, new Runnable() {
 342                     public void run() {
 343                         synchronized(target.getTreeLock()) {
 344                             target.doLayout();
 345                         }
 346                     }
 347                 });
 348         }
 349         tcList.addElement(tc);
 350 
 351     }
 352 
 353     public void removeTextComponent(MComponentPeer tc) {
 354         if (!tcList.contains(tc))
 355             return;
 356         tcList.removeElement(tc);
 357         if (tcList.isEmpty()){
 358             removeTextComponentNative();
 359             if (!imList.isEmpty()) {
 360                 for (int i = 0; i < imList.size(); i++) {
 361                     ((MInputMethod)imList.elementAt(i)).reconfigureXIC((MInputMethodControl)this);
 362                 }
 363             }
 364             target.doLayout();
 365         }
 366     }
 367 
 368     public MComponentPeer getTextComponent() {
 369         if (!tcList.isEmpty()) {
 370             return (MComponentPeer)tcList.firstElement();
 371         } else {
 372             return null;
 373         }
 374     }
 375 
 376     boolean hasDecorations(int decor) {
 377         if (!winAttr.nativeDecor) {
 378             return false;
 379         }
 380         else {
 381             int myDecor = winAttr.decorations;
 382             boolean hasBits = ((myDecor & decor) == decor);
 383             if ((myDecor & MWindowAttributes.AWT_DECOR_ALL) != 0)
 384                 return !hasBits;
 385             else
 386                 return hasBits;
 387         }
 388     }
 389 
 390     /* Returns the native paint should be posted after setting new size
 391      */
 392     public boolean checkNativePaintOnSetBounds(int width, int height) {
 393         // Fix for 4418155. Window does not repaint
 394         // automticaly if shrinking. Should not wait for Expose
 395         return (width > oldWidth) || (height > oldHeight);
 396     }
 397 
 398 /* --- DisplayChangedListener Stuff --- */
 399 
 400     native void resetTargetGC(Component target);
 401 
 402     /* Xinerama
 403      * called to update our GC when dragged onto another screen
 404      */
 405     public void draggedToNewScreen(int screenNum) {
 406         final int finalScreenNum = screenNum;
 407 
 408         SunToolkit.executeOnEventHandlerThread((Component)target, new Runnable()
 409             {
 410                 public void run() {
 411                     displayChanged(finalScreenNum);
 412                 }
 413             });
 414     }
 415 
 416     /* Xinerama
 417      * called to update our GC when dragged onto another screen
 418      */
 419     public void displayChanged(int screenNum) {
 420         // update our GC
 421         resetLocalGC(screenNum);         /* upcall to MCanvasPeer */
 422         resetTargetGC(target);           /* call Window.resetGC() via native */
 423 
 424         //propagate to children
 425         super.displayChanged(screenNum); /* upcall to MPanelPeer */
 426     }
 427 
 428     /**
 429      * Helper method that executes the displayChanged(screen) method on
 430      * the event dispatch thread.  This method is used in the Xinerama case
 431      * and after display mode change events.
 432      */
 433     private void executeDisplayChangedOnEDT(int screenNum) {
 434         final int finalScreenNum = screenNum;
 435         Runnable dc = new Runnable() {
 436             public void run() {
 437                 displayChanged(finalScreenNum);
 438             }
 439         };
 440         SunToolkit.executeOnEventHandlerThread((Component)target, dc);
 441     }
 442 
 443     /**
 444      * From the DisplayChangedListener interface; called from
 445      * X11GraphicsDevice when the display mode has been changed.
 446      */
 447     public void displayChanged() {
 448         GraphicsConfiguration gc = getGraphicsConfiguration();
 449         int curScreenNum = ((X11GraphicsDevice)gc.getDevice()).getScreen();
 450         executeDisplayChangedOnEDT(curScreenNum);
 451     }
 452 
 453     /**
 454      * From the DisplayChangedListener interface; top-levels do not need
 455      * to react to this event.
 456      */
 457     public void paletteChanged() {
 458     }
 459 
 460     public synchronized void addDropTarget() {
 461         if (dropTargetCount == 0) {
 462             registerX11DropTarget(target);
 463         }
 464         dropTargetCount++;
 465     }
 466 
 467     public synchronized void removeDropTarget() {
 468         dropTargetCount--;
 469         if (dropTargetCount == 0) {
 470             unregisterX11DropTarget(target);
 471         }
 472     }
 473 
 474     protected synchronized void updateDropTarget() {
 475         if (dropTargetCount > 0) {
 476             unregisterX11DropTarget(target);
 477             registerX11DropTarget(target);
 478         }
 479     }
 480 
 481     public boolean requestWindowFocus() {
 482         return false;
 483     }
 484 
 485     public void setModalBlocked(Dialog blocker, boolean blocked) {
 486         // do nothing
 487     }
 488 
 489     public void postUngrabEvent() {
 490         postEvent(new sun.awt.UngrabEvent((Window)target));
 491     }
 492 
 493     boolean isOwnerOf(MComponentPeer child) {
 494         if (child == null) return false;
 495 
 496         Component comp = child.target;
 497         while (comp != null && !(comp instanceof Window)) {
 498             comp = getParent_NoClientCode(comp);
 499         }
 500         if (!(comp instanceof Window)) {
 501             return false;
 502         }
 503 
 504         while (comp != null && !(comp == target) && !(comp instanceof Dialog)) {
 505             comp = getParent_NoClientCode(comp);
 506         }
 507         return (comp == target);
 508     }
 509 
 510     boolean processUngrabMouseEvent(MComponentPeer compPeer, int x_root, int y_root, int type) {
 511         switch (type) {
 512           case 4: // ButtonPress
 513               // Check that the target is the child of the grabbed
 514               // window or the child of one of the owned windows of
 515               // the grabbed window
 516               if (!isOwnerOf(compPeer)) {
 517                   postUngrabEvent();
 518                   return true;
 519               }
 520         }
 521         return false;
 522     }
 523 
 524     private final boolean hasWarningWindow() {
 525         return ((Window)target).getWarningString() != null;
 526     }
 527 
 528     // This method is overriden at Dialog and Frame peers.
 529     boolean isTargetUndecorated() {
 530         return true;
 531     }
 532 
 533     private volatile int sysX = 0;
 534     private volatile int sysY = 0;
 535     private volatile int sysW = 0;
 536     private volatile int sysH = 0;
 537 
 538     Rectangle constrainBounds(int x, int y, int width, int height) {
 539         // We don't restrict the setBounds() operation if the code is trusted.
 540         if (!hasWarningWindow()) {
 541             return new Rectangle(x, y, width, height);
 542         }
 543 
 544         int newX = x;
 545         int newY = y;
 546         int newW = width;
 547         int newH = height;
 548 
 549         GraphicsConfiguration gc = ((Window)target).getGraphicsConfiguration();
 550         Rectangle sB = gc.getBounds();
 551         Insets sIn = ((Window)target).getToolkit().getScreenInsets(gc);
 552 
 553         int screenW = sB.width - sIn.left - sIn.right;
 554         int screenH = sB.height - sIn.top - sIn.bottom;
 555 
 556         // If it's undecorated or is not currently visible,
 557         // then check each point is within the visible part of the screen
 558         if (!target.isVisible() || isTargetUndecorated()) {
 559             int screenX = sB.x + sIn.left;
 560             int screenY = sB.y + sIn.top;
 561 
 562             // First make sure the size is withing the visible part of the screen
 563             if (newW > screenW) {
 564                 newW = screenW;
 565             }
 566 
 567             if (newH > screenH) {
 568                 newH = screenH;
 569             }
 570 
 571             // Tweak the location if needed
 572             if (newX < screenX) {
 573                 newX = screenX;
 574             } else if (newX + newW > screenX + screenW) {
 575                 newX = screenX + screenW - newW;
 576             }
 577 
 578             if (newY < screenY) {
 579                 newY = screenY;
 580             } else if (newY + newH > screenY + screenH) {
 581                 newY = screenY + screenH - newH;
 582             }
 583         } else {
 584             int maxW = Math.max(screenW, sysW);
 585             int maxH = Math.max(screenH, sysH);
 586 
 587             // Make sure the size is withing the visible part of the screen
 588             // OR is less that the current size of the window.
 589             if (newW > maxW) {
 590                 newW = maxW;
 591             }
 592 
 593             if (newH > maxH) {
 594                 newH = maxH;
 595             }
 596         }
 597 
 598         return new Rectangle(newX, newY, newW, newH);
 599     }
 600 
 601     public void setBounds(int x, int y, int width, int height, int op) {
 602         Rectangle newBounds = constrainBounds(x, y, width, height);
 603         super.setBounds(newBounds.x, newBounds.y, newBounds.width, newBounds.height, op);
 604     }
 605 
 606     @Override
 607     public void setOpacity(float opacity) {
 608         // not implemented
 609     }
 610 
 611     @Override
 612     public void setOpaque(boolean isOpaque) {
 613         // no-op
 614     }
 615 
 616     @Override
 617     public void updateWindow() {
 618         // no-op
 619     }
 620 
 621     public void repositionSecurityWarning() {
 622         // not implemented
 623     }
 624 
 625 }