/* * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.awt.X11; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Frame; import java.awt.Graphics; import java.awt.Insets; import java.awt.MenuBar; import java.awt.Rectangle; import java.awt.peer.FramePeer; import sun.util.logging.PlatformLogger; import sun.awt.AWTAccessor; class XFramePeer extends XDecoratedPeer implements FramePeer { private static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XFramePeer"); private static PlatformLogger stateLog = PlatformLogger.getLogger("sun.awt.X11.states"); private static PlatformLogger insLog = PlatformLogger.getLogger("sun.awt.X11.insets.XFramePeer"); XMenuBarPeer menubarPeer; MenuBar menubar; int state; private Boolean undecorated; private static final int MENUBAR_HEIGHT_IF_NO_MENUBAR = 0; private int lastAppliedMenubarHeight = MENUBAR_HEIGHT_IF_NO_MENUBAR; XFramePeer(Frame target) { super(target); } XFramePeer(XCreateWindowParams params) { super(params); } void preInit(XCreateWindowParams params) { super.preInit(params); Frame target = (Frame)(this.target); // set the window attributes for this Frame winAttr.initialState = target.getExtendedState(); state = 0; undecorated = Boolean.valueOf(target.isUndecorated()); winAttr.nativeDecor = !target.isUndecorated(); if (winAttr.nativeDecor) { winAttr.decorations = XWindowAttributesData.AWT_DECOR_ALL; } else { winAttr.decorations = XWindowAttributesData.AWT_DECOR_NONE; } winAttr.functions = MWMConstants.MWM_FUNC_ALL; winAttr.isResizable = true; // target.isResizable(); winAttr.title = target.getTitle(); winAttr.initialResizability = target.isResizable(); if (log.isLoggable(PlatformLogger.Level.FINE)) { log.fine("Frame''s initial attributes: decor {0}, resizable {1}, undecorated {2}, initial state {3}", Integer.valueOf(winAttr.decorations), Boolean.valueOf(winAttr.initialResizability), Boolean.valueOf(!winAttr.nativeDecor), Integer.valueOf(winAttr.initialState)); } } void postInit(XCreateWindowParams params) { super.postInit(params); setupState(true); } @Override boolean isTargetUndecorated() { if (undecorated != null) { return undecorated.booleanValue(); } else { return ((Frame)target).isUndecorated(); } } void setupState(boolean onInit) { if (onInit) { state = winAttr.initialState; } if ((state & Frame.ICONIFIED) != 0) { setInitialState(XUtilConstants.IconicState); } else { setInitialState(XUtilConstants.NormalState); } setExtendedState(state); } public void setMenuBar(MenuBar mb) { // state_lock should always be the second after awt_lock XToolkit.awtLock(); try { synchronized(getStateLock()) { if (mb == menubar) return; if (mb == null) { if (menubar != null) { menubarPeer.xSetVisible(false); menubar = null; menubarPeer.dispose(); menubarPeer = null; } } else { menubar = mb; menubarPeer = (XMenuBarPeer) mb.getPeer(); if (menubarPeer != null) { menubarPeer.init((Frame)target); } } } } finally { XToolkit.awtUnlock(); } reshapeMenubarPeer(); } XMenuBarPeer getMenubarPeer() { return menubarPeer; } int getMenuBarHeight() { if (menubarPeer != null) { return menubarPeer.getDesiredHeight(); } else { return MENUBAR_HEIGHT_IF_NO_MENUBAR; } } void updateChildrenSizes() { super.updateChildrenSizes(); int height = getMenuBarHeight(); // XWindow.reshape calls XBaseWindow.xSetBounds, which acquires // the AWT lock, so we have to acquire the AWT lock here // before getStateLock() to avoid a deadlock with the Toolkit thread // when this method is called on the EDT. XToolkit.awtLock(); try { synchronized(getStateLock()) { int width = dimensions.getClientSize().width; if (menubarPeer != null) { menubarPeer.reshape(0, 0, width, height); } } } finally { XToolkit.awtUnlock(); } } /** * In addition to reshaping menubarPeer (by using 'updateChildrenSizes') * this method also performs some frame reaction on this (i.e. layouts * other frame children, if required) */ final void reshapeMenubarPeer() { XToolkit.executeOnEventHandlerThread( target, new Runnable() { public void run() { updateChildrenSizes(); boolean heightChanged = false; int height = getMenuBarHeight(); // Neither 'XToolkit.awtLock()' nor 'getStateLock()' // is acquired under this call, and it looks to run // thread-safely. I currently see no reason to move // it under following 'synchronized' clause. synchronized(getStateLock()) { if (height != lastAppliedMenubarHeight) { lastAppliedMenubarHeight = height; heightChanged = true; } } if (heightChanged) { // To make frame contents be re-layout (copied from // 'XDecoratedPeer.revalidate()'). These are not // 'synchronized', because can recursively call client // methods, which are not supposed to be called with locks // acquired. target.invalidate(); target.validate(); } } } ); } public void setMaximizedBounds(Rectangle b) { if (insLog.isLoggable(PlatformLogger.Level.FINE)) { insLog.fine("Setting maximized bounds to " + b); } if (b == null) return; maxBounds = new Rectangle(b); XToolkit.awtLock(); try { XSizeHints hints = getHints(); hints.set_flags(hints.get_flags() | (int)XUtilConstants.PMaxSize); if (b.width != Integer.MAX_VALUE) { hints.set_max_width(b.width); } else { hints.set_max_width((int)XlibWrapper.DisplayWidth(XToolkit.getDisplay(), XlibWrapper.DefaultScreen(XToolkit.getDisplay()))); } if (b.height != Integer.MAX_VALUE) { hints.set_max_height(b.height); } else { hints.set_max_height((int)XlibWrapper.DisplayHeight(XToolkit.getDisplay(), XlibWrapper.DefaultScreen(XToolkit.getDisplay()))); } if (insLog.isLoggable(PlatformLogger.Level.FINER)) { insLog.finer("Setting hints, flags " + XlibWrapper.hintsToString(hints.get_flags())); } XlibWrapper.XSetWMNormalHints(XToolkit.getDisplay(), window, hints.pData); } finally { XToolkit.awtUnlock(); } } public int getState() { synchronized(getStateLock()) { return state; } } public void setState(int newState) { synchronized(getStateLock()) { if (!isShowing()) { stateLog.finer("Frame is not showing"); state = newState; return; } } changeState(newState); } void changeState(int newState) { int changed = state ^ newState; int changeIconic = changed & Frame.ICONIFIED; boolean iconic = (newState & Frame.ICONIFIED) != 0; if (stateLog.isLoggable(PlatformLogger.Level.FINER)) { stateLog.finer("Changing state, old state {0}, new state {1}(iconic {2})", Integer.valueOf(state), Integer.valueOf(newState), Boolean.valueOf(iconic)); } if (changeIconic != 0 && iconic) { if (stateLog.isLoggable(PlatformLogger.Level.FINER)) { stateLog.finer("Iconifying shell " + getShell() + ", this " + this + ", screen " + getScreenNumber()); } XToolkit.awtLock(); try { int res = XlibWrapper.XIconifyWindow(XToolkit.getDisplay(), getShell(), getScreenNumber()); if (stateLog.isLoggable(PlatformLogger.Level.FINER)) { stateLog.finer("XIconifyWindow returned " + res); } } finally { XToolkit.awtUnlock(); } } if ((changed & ~Frame.ICONIFIED) != 0) { setExtendedState(newState); } if (changeIconic != 0 && !iconic) { if (stateLog.isLoggable(PlatformLogger.Level.FINER)) { stateLog.finer("DeIconifying " + this); } xSetVisible(true); } } void setExtendedState(int newState) { XWM.getWM().setExtendedState(this, newState); } public void handlePropertyNotify(XEvent xev) { super.handlePropertyNotify(xev); XPropertyEvent ev = xev.get_xproperty(); if (log.isLoggable(PlatformLogger.Level.FINER)) { log.finer("Property change {0}", ev); } /* * Let's see if this is a window state protocol message, and * if it is - decode a new state in terms of java constants. */ if (!XWM.getWM().isStateChange(this, ev)) { stateLog.finer("either not a state atom or state has not been changed"); return; } final int newState = XWM.getWM().getState(this); int changed = state ^ newState; if (changed == 0) { if (stateLog.isLoggable(PlatformLogger.Level.FINER)) { stateLog.finer("State is the same: " + state); } return; } int old_state = state; state = newState; // sync target with peer AWTAccessor.getFrameAccessor().setExtendedState((Frame)target, state); if ((changed & Frame.ICONIFIED) != 0) { if ((state & Frame.ICONIFIED) != 0) { stateLog.finer("Iconified"); handleIconify(); } else { stateLog.finer("DeIconified"); content.purgeIconifiedExposeEvents(); handleDeiconify(); } } handleStateChange(old_state, state); } // NOTE: This method may be called by privileged threads. // DO NOT INVOKE CLIENT CODE ON THIS THREAD! public void handleStateChange(int oldState, int newState) { super.handleStateChange(oldState, newState); for (ToplevelStateListener topLevelListenerTmp : toplevelStateListeners) { topLevelListenerTmp.stateChangedJava(oldState, newState); } } public void setVisible(boolean vis) { if (vis) { setupState(false); } else { if ((state & Frame.MAXIMIZED_BOTH) != 0) { XWM.getWM().setExtendedState(this, state & ~Frame.MAXIMIZED_BOTH); } } super.setVisible(vis); if (vis && maxBounds != null) { setMaximizedBounds(maxBounds); } } void setInitialState(int wm_state) { XToolkit.awtLock(); try { XWMHints hints = getWMHints(); hints.set_flags((int)XUtilConstants.StateHint | hints.get_flags()); hints.set_initial_state(wm_state); if (stateLog.isLoggable(PlatformLogger.Level.FINE)) { stateLog.fine("Setting initial WM state on " + this + " to " + wm_state); } XlibWrapper.XSetWMHints(XToolkit.getDisplay(), getWindow(), hints.pData); } finally { XToolkit.awtUnlock(); } } public void dispose() { if (menubarPeer != null) { menubarPeer.dispose(); } super.dispose(); } boolean isMaximized() { return (state & (Frame.MAXIMIZED_VERT | Frame.MAXIMIZED_HORIZ)) != 0; } static final int CROSSHAIR_INSET = 5; static final int BUTTON_Y = CROSSHAIR_INSET + 1; static final int BUTTON_W = 17; static final int BUTTON_H = 17; static final int SYS_MENU_X = CROSSHAIR_INSET + 1; static final int SYS_MENU_CONTAINED_X = SYS_MENU_X + 5; static final int SYS_MENU_CONTAINED_Y = BUTTON_Y + 7; static final int SYS_MENU_CONTAINED_W = 8; static final int SYS_MENU_CONTAINED_H = 3; static final int MAXIMIZE_X_DIFF = CROSSHAIR_INSET + BUTTON_W; static final int MAXIMIZE_CONTAINED_X_DIFF = MAXIMIZE_X_DIFF - 5; static final int MAXIMIZE_CONTAINED_Y = BUTTON_Y + 5; static final int MAXIMIZE_CONTAINED_W = 8; static final int MAXIMIZE_CONTAINED_H = 8; static final int MINIMIZE_X_DIFF = MAXIMIZE_X_DIFF + BUTTON_W; static final int MINIMIZE_CONTAINED_X_DIFF = MINIMIZE_X_DIFF - 7; static final int MINIMIZE_CONTAINED_Y = BUTTON_Y + 7; static final int MINIMIZE_CONTAINED_W = 3; static final int MINIMIZE_CONTAINED_H = 3; static final int TITLE_X = SYS_MENU_X + BUTTON_W; static final int TITLE_W_DIFF = BUTTON_W * 3 + CROSSHAIR_INSET * 2 - 1; static final int TITLE_MID_Y = BUTTON_Y + (BUTTON_H / 2); static final int MENUBAR_X = CROSSHAIR_INSET + 1; static final int MENUBAR_Y = BUTTON_Y + BUTTON_H; static final int HORIZ_RESIZE_INSET = CROSSHAIR_INSET + BUTTON_H; static final int VERT_RESIZE_INSET = CROSSHAIR_INSET + BUTTON_W; /* * Print the native component by rendering the Motif look ourselves. * We also explicitly print the MenuBar since a MenuBar isn't a subclass * of Component (and thus it has no "print" method which gets called by * default). */ public void print(Graphics g) { super.print(g); Frame f = (Frame)target; Insets finsets = f.getInsets(); Dimension fsize = f.getSize(); Color bg = f.getBackground(); Color fg = f.getForeground(); Color highlight = bg.brighter(); Color shadow = bg.darker(); // Well, we could query for the currently running window manager // and base the look on that, or we could just always do dtwm. // aim, tball, and levenson all agree we'll just do dtwm. if (hasDecorations(XWindowAttributesData.AWT_DECOR_BORDER)) { // top outer -- because we'll most likely be drawing on white paper, // for aesthetic reasons, don't make any part of the outer border // pure white if (highlight.equals(Color.white)) { g.setColor(new Color(230, 230, 230)); } else { g.setColor(highlight); } g.drawLine(0, 0, fsize.width, 0); g.drawLine(0, 1, fsize.width - 1, 1); // left outer // if (highlight.equals(Color.white)) { // g.setColor(new Color(230, 230, 230)); // } // else { // g.setColor(highlight); // } g.drawLine(0, 0, 0, fsize.height); g.drawLine(1, 0, 1, fsize.height - 1); // bottom cross-hair g.setColor(highlight); g.drawLine(CROSSHAIR_INSET + 1, fsize.height - CROSSHAIR_INSET, fsize.width - CROSSHAIR_INSET, fsize.height - CROSSHAIR_INSET); // right cross-hair // g.setColor(highlight); g.drawLine(fsize.width - CROSSHAIR_INSET, CROSSHAIR_INSET + 1, fsize.width - CROSSHAIR_INSET, fsize.height - CROSSHAIR_INSET); // bottom outer g.setColor(shadow); g.drawLine(1, fsize.height, fsize.width, fsize.height); g.drawLine(2, fsize.height - 1, fsize.width, fsize.height - 1); // right outer // g.setColor(shadow); g.drawLine(fsize.width, 1, fsize.width, fsize.height); g.drawLine(fsize.width - 1, 2, fsize.width - 1, fsize.height); // top cross-hair // g.setColor(shadow); g.drawLine(CROSSHAIR_INSET, CROSSHAIR_INSET, fsize.width - CROSSHAIR_INSET, CROSSHAIR_INSET); // left cross-hair // g.setColor(shadow); g.drawLine(CROSSHAIR_INSET, CROSSHAIR_INSET, CROSSHAIR_INSET, fsize.height - CROSSHAIR_INSET); } if (hasDecorations(XWindowAttributesData.AWT_DECOR_TITLE)) { if (hasDecorations(XWindowAttributesData.AWT_DECOR_MENU)) { // system menu g.setColor(bg); g.fill3DRect(SYS_MENU_X, BUTTON_Y, BUTTON_W, BUTTON_H, true); g.fill3DRect(SYS_MENU_CONTAINED_X, SYS_MENU_CONTAINED_Y, SYS_MENU_CONTAINED_W, SYS_MENU_CONTAINED_H, true); } // title bar // g.setColor(bg); g.fill3DRect(TITLE_X, BUTTON_Y, fsize.width - TITLE_W_DIFF, BUTTON_H, true); if (hasDecorations(XWindowAttributesData.AWT_DECOR_MINIMIZE)) { // minimize button // g.setColor(bg); g.fill3DRect(fsize.width - MINIMIZE_X_DIFF, BUTTON_Y, BUTTON_W, BUTTON_H, true); g.fill3DRect(fsize.width - MINIMIZE_CONTAINED_X_DIFF, MINIMIZE_CONTAINED_Y, MINIMIZE_CONTAINED_W, MINIMIZE_CONTAINED_H, true); } if (hasDecorations(XWindowAttributesData.AWT_DECOR_MAXIMIZE)) { // maximize button // g.setColor(bg); g.fill3DRect(fsize.width - MAXIMIZE_X_DIFF, BUTTON_Y, BUTTON_W, BUTTON_H, true); g.fill3DRect(fsize.width - MAXIMIZE_CONTAINED_X_DIFF, MAXIMIZE_CONTAINED_Y, MAXIMIZE_CONTAINED_W, MAXIMIZE_CONTAINED_H, true); } // title bar text g.setColor(fg); Font sysfont = new Font(Font.SANS_SERIF, Font.PLAIN, 10); g.setFont(sysfont); FontMetrics sysfm = g.getFontMetrics(); String ftitle = f.getTitle(); g.drawString(ftitle, ((TITLE_X + TITLE_X + fsize.width - TITLE_W_DIFF) / 2) - (sysfm.stringWidth(ftitle) / 2), TITLE_MID_Y + sysfm.getMaxDescent()); } if (f.isResizable() && hasDecorations(XWindowAttributesData.AWT_DECOR_RESIZEH)) { // add resize cross hairs // upper-left horiz (shadow) g.setColor(shadow); g.drawLine(1, HORIZ_RESIZE_INSET, CROSSHAIR_INSET, HORIZ_RESIZE_INSET); // upper-left vert (shadow) // g.setColor(shadow); g.drawLine(VERT_RESIZE_INSET, 1, VERT_RESIZE_INSET, CROSSHAIR_INSET); // upper-right horiz (shadow) // g.setColor(shadow); g.drawLine(fsize.width - CROSSHAIR_INSET + 1, HORIZ_RESIZE_INSET, fsize.width, HORIZ_RESIZE_INSET); // upper-right vert (shadow) // g.setColor(shadow); g.drawLine(fsize.width - VERT_RESIZE_INSET - 1, 2, fsize.width - VERT_RESIZE_INSET - 1, CROSSHAIR_INSET + 1); // lower-left horiz (shadow) // g.setColor(shadow); g.drawLine(1, fsize.height - HORIZ_RESIZE_INSET - 1, CROSSHAIR_INSET, fsize.height - HORIZ_RESIZE_INSET - 1); // lower-left vert (shadow) // g.setColor(shadow); g.drawLine(VERT_RESIZE_INSET, fsize.height - CROSSHAIR_INSET + 1, VERT_RESIZE_INSET, fsize.height); // lower-right horiz (shadow) // g.setColor(shadow); g.drawLine(fsize.width - CROSSHAIR_INSET + 1, fsize.height - HORIZ_RESIZE_INSET - 1, fsize.width, fsize.height - HORIZ_RESIZE_INSET - 1); // lower-right vert (shadow) // g.setColor(shadow); g.drawLine(fsize.width - VERT_RESIZE_INSET - 1, fsize.height - CROSSHAIR_INSET + 1, fsize.width - VERT_RESIZE_INSET - 1, fsize.height); // upper-left horiz (highlight) g.setColor(highlight); g.drawLine(2, HORIZ_RESIZE_INSET + 1, CROSSHAIR_INSET, HORIZ_RESIZE_INSET + 1); // upper-left vert (highlight) // g.setColor(highlight); g.drawLine(VERT_RESIZE_INSET + 1, 2, VERT_RESIZE_INSET + 1, CROSSHAIR_INSET); // upper-right horiz (highlight) // g.setColor(highlight); g.drawLine(fsize.width - CROSSHAIR_INSET + 1, HORIZ_RESIZE_INSET + 1, fsize.width - 1, HORIZ_RESIZE_INSET + 1); // upper-right vert (highlight) // g.setColor(highlight); g.drawLine(fsize.width - VERT_RESIZE_INSET, 2, fsize.width - VERT_RESIZE_INSET, CROSSHAIR_INSET); // lower-left horiz (highlight) // g.setColor(highlight); g.drawLine(2, fsize.height - HORIZ_RESIZE_INSET, CROSSHAIR_INSET, fsize.height - HORIZ_RESIZE_INSET); // lower-left vert (highlight) // g.setColor(highlight); g.drawLine(VERT_RESIZE_INSET + 1, fsize.height - CROSSHAIR_INSET + 1, VERT_RESIZE_INSET + 1, fsize.height - 1); // lower-right horiz (highlight) // g.setColor(highlight); g.drawLine(fsize.width - CROSSHAIR_INSET + 1, fsize.height - HORIZ_RESIZE_INSET, fsize.width - 1, fsize.height - HORIZ_RESIZE_INSET); // lower-right vert (highlight) // g.setColor(highlight); g.drawLine(fsize.width - VERT_RESIZE_INSET, fsize.height - CROSSHAIR_INSET + 1, fsize.width - VERT_RESIZE_INSET, fsize.height - 1); } XMenuBarPeer peer = menubarPeer; if (peer != null) { Insets insets = getInsets(); Graphics ng = g.create(); int menubarX = 0; int menubarY = 0; if (hasDecorations(XWindowAttributesData.AWT_DECOR_BORDER)) { menubarX += CROSSHAIR_INSET + 1; menubarY += CROSSHAIR_INSET + 1; } if (hasDecorations(XWindowAttributesData.AWT_DECOR_TITLE)) { menubarY += BUTTON_H; } try { ng.translate(menubarX, menubarY); peer.print(ng); } finally { ng.dispose(); } } } public void setBoundsPrivate(int x, int y, int width, int height) { setBounds(x, y, width, height, SET_BOUNDS); } public Rectangle getBoundsPrivate() { return getBounds(); } public void emulateActivation(boolean doActivate) { if (doActivate) { handleWindowFocusIn(0); } else { handleWindowFocusOut(null, 0); } } }