1 /*
2 * Copyright (c) 2011, 2013, 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
26 package sun.lwawt.macosx;
27
28 import java.awt.*;
29 import java.awt.Dialog.ModalityType;
30 import java.awt.event.*;
31 import java.awt.peer.WindowPeer;
32 import java.beans.*;
33 import java.lang.reflect.InvocationTargetException;
34 import java.util.List;
35 import java.util.Objects;
36
37 import javax.swing.*;
38
39 import sun.awt.*;
40 import sun.java2d.SurfaceData;
41 import sun.java2d.opengl.CGLSurfaceData;
42 import sun.lwawt.*;
43 import sun.util.logging.PlatformLogger;
44
45 import com.apple.laf.*;
46 import com.apple.laf.ClientPropertyApplicator.Property;
47 import com.sun.awt.AWTUtilities;
48
49 public class CPlatformWindow extends CFRetainedResource implements PlatformWindow {
50 private native long nativeCreateNSWindow(long nsViewPtr, long styleBits, double x, double y, double w, double h);
51 private static native void nativeSetNSWindowStyleBits(long nsWindowPtr, int mask, int data);
52 private static native void nativeSetNSWindowMenuBar(long nsWindowPtr, long menuBarPtr);
53 private static native Insets nativeGetNSWindowInsets(long nsWindowPtr);
54 private static native void nativeSetNSWindowBounds(long nsWindowPtr, double x, double y, double w, double h);
55 private static native void nativeSetNSWindowMinMax(long nsWindowPtr, double minW, double minH, double maxW, double maxH);
56 private static native void nativePushNSWindowToBack(long nsWindowPtr);
57 private static native void nativePushNSWindowToFront(long nsWindowPtr);
58 private static native void nativeSetNSWindowTitle(long nsWindowPtr, String title);
59 private static native void nativeRevalidateNSWindowShadow(long nsWindowPtr);
60 private static native void nativeSetNSWindowMinimizedIcon(long nsWindowPtr, long nsImage);
61 private static native void nativeSetNSWindowRepresentedFilename(long nsWindowPtr, String representedFilename);
62 private static native void nativeSetEnabled(long nsWindowPtr, boolean isEnabled);
63 private static native void nativeSynthesizeMouseEnteredExitedEvents();
64 private static native void nativeDispose(long nsWindowPtr);
65 private static native CPlatformWindow nativeGetTopmostPlatformWindowUnderMouse();
66
67 // Loger to report issues happened during execution but that do not affect functionality
68 private static final PlatformLogger logger = PlatformLogger.getLogger("sun.lwawt.macosx.CPlatformWindow");
69 private static final PlatformLogger focusLogger = PlatformLogger.getLogger("sun.lwawt.macosx.focus.CPlatformWindow");
70
71 // for client properties
72 public static final String WINDOW_BRUSH_METAL_LOOK = "apple.awt.brushMetalLook";
73 public static final String WINDOW_DRAGGABLE_BACKGROUND = "apple.awt.draggableWindowBackground";
74
75 public static final String WINDOW_ALPHA = "Window.alpha";
76 public static final String WINDOW_SHADOW = "Window.shadow";
77
78 public static final String WINDOW_STYLE = "Window.style";
79 public static final String WINDOW_SHADOW_REVALIDATE_NOW = "apple.awt.windowShadow.revalidateNow";
80
81 public static final String WINDOW_DOCUMENT_MODIFIED = "Window.documentModified";
82 public static final String WINDOW_DOCUMENT_FILE = "Window.documentFile";
83
84 public static final String WINDOW_CLOSEABLE = "Window.closeable";
85 public static final String WINDOW_MINIMIZABLE = "Window.minimizable";
86 public static final String WINDOW_ZOOMABLE = "Window.zoomable";
87 public static final String WINDOW_HIDES_ON_DEACTIVATE="Window.hidesOnDeactivate";
88
89 public static final String WINDOW_DOC_MODAL_SHEET = "apple.awt.documentModalSheet";
90 public static final String WINDOW_FADE_DELEGATE = "apple.awt._windowFadeDelegate";
91 public static final String WINDOW_FADE_IN = "apple.awt._windowFadeIn";
92 public static final String WINDOW_FADE_OUT = "apple.awt._windowFadeOut";
93 public static final String WINDOW_FULLSCREENABLE = "apple.awt.fullscreenable";
94
95
96 // Yeah, I know. But it's easier to deal with ints from JNI
97 static final int MODELESS = 0;
98 static final int DOCUMENT_MODAL = 1;
99 static final int APPLICATION_MODAL = 2;
100 static final int TOOLKIT_MODAL = 3;
101
102 // window style bits
103 static final int _RESERVED_FOR_DATA = 1 << 0;
104
105 // corresponds to native style mask bits
106 static final int DECORATED = 1 << 1;
107 static final int TEXTURED = 1 << 2;
108 static final int UNIFIED = 1 << 3;
109 static final int UTILITY = 1 << 4;
110 static final int HUD = 1 << 5;
111 static final int SHEET = 1 << 6;
112
113 static final int CLOSEABLE = 1 << 7;
114 static final int MINIMIZABLE = 1 << 8;
115
116 static final int RESIZABLE = 1 << 9; // both a style bit and prop bit
117 static final int NONACTIVATING = 1 << 24;
118 static final int IS_DIALOG = 1 << 25;
119 static final int IS_MODAL = 1 << 26;
120
121 static final int _STYLE_PROP_BITMASK = DECORATED | TEXTURED | UNIFIED | UTILITY | HUD | SHEET | CLOSEABLE | MINIMIZABLE | RESIZABLE;
122
123 // corresponds to method-based properties
124 static final int HAS_SHADOW = 1 << 10;
125 static final int ZOOMABLE = 1 << 11;
126
127 static final int ALWAYS_ON_TOP = 1 << 15;
128 static final int HIDES_ON_DEACTIVATE = 1 << 17;
129 static final int DRAGGABLE_BACKGROUND = 1 << 19;
130 static final int DOCUMENT_MODIFIED = 1 << 21;
131 static final int FULLSCREENABLE = 1 << 23;
132
133 static final int _METHOD_PROP_BITMASK = RESIZABLE | HAS_SHADOW | ZOOMABLE | ALWAYS_ON_TOP | HIDES_ON_DEACTIVATE | DRAGGABLE_BACKGROUND | DOCUMENT_MODIFIED | FULLSCREENABLE;
134
135 // corresponds to callback-based properties
136 static final int SHOULD_BECOME_KEY = 1 << 12;
137 static final int SHOULD_BECOME_MAIN = 1 << 13;
138 static final int MODAL_EXCLUDED = 1 << 16;
139
140 static final int _CALLBACK_PROP_BITMASK = SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN | MODAL_EXCLUDED;
141
142 static int SET(final int bits, final int mask, final boolean value) {
143 if (value) return (bits | mask);
144 return bits & ~mask;
145 }
146
147 static boolean IS(final int bits, final int mask) {
148 return (bits & mask) != 0;
149 }
150
151 @SuppressWarnings("unchecked")
152 static ClientPropertyApplicator<JRootPane, CPlatformWindow> CLIENT_PROPERTY_APPLICATOR = new ClientPropertyApplicator<JRootPane, CPlatformWindow>(new Property[] {
153 new Property<CPlatformWindow>(WINDOW_DOCUMENT_MODIFIED) { public void applyProperty(final CPlatformWindow c, final Object value) {
154 c.setStyleBits(DOCUMENT_MODIFIED, value == null ? false : Boolean.parseBoolean(value.toString()));
155 }},
156 new Property<CPlatformWindow>(WINDOW_BRUSH_METAL_LOOK) { public void applyProperty(final CPlatformWindow c, final Object value) {
157 c.setStyleBits(TEXTURED, Boolean.parseBoolean(value.toString()));
158 }},
159 new Property<CPlatformWindow>(WINDOW_ALPHA) { public void applyProperty(final CPlatformWindow c, final Object value) {
160 AWTUtilities.setWindowOpacity(c.target, value == null ? 1.0f : Float.parseFloat(value.toString()));
161 }},
162 new Property<CPlatformWindow>(WINDOW_SHADOW) { public void applyProperty(final CPlatformWindow c, final Object value) {
163 c.setStyleBits(HAS_SHADOW, value == null ? true : Boolean.parseBoolean(value.toString()));
164 }},
165 new Property<CPlatformWindow>(WINDOW_MINIMIZABLE) { public void applyProperty(final CPlatformWindow c, final Object value) {
166 c.setStyleBits(MINIMIZABLE, Boolean.parseBoolean(value.toString()));
167 }},
168 new Property<CPlatformWindow>(WINDOW_CLOSEABLE) { public void applyProperty(final CPlatformWindow c, final Object value) {
169 c.setStyleBits(CLOSEABLE, Boolean.parseBoolean(value.toString()));
170 }},
171 new Property<CPlatformWindow>(WINDOW_ZOOMABLE) { public void applyProperty(final CPlatformWindow c, final Object value) {
172 c.setStyleBits(ZOOMABLE, Boolean.parseBoolean(value.toString()));
173 }},
174 new Property<CPlatformWindow>(WINDOW_FULLSCREENABLE) { public void applyProperty(final CPlatformWindow c, final Object value) {
175 c.setStyleBits(FULLSCREENABLE, Boolean.parseBoolean(value.toString()));
176 }},
177 new Property<CPlatformWindow>(WINDOW_SHADOW_REVALIDATE_NOW) { public void applyProperty(final CPlatformWindow c, final Object value) {
178 nativeRevalidateNSWindowShadow(c.getNSWindowPtr());
179 }},
180 new Property<CPlatformWindow>(WINDOW_DOCUMENT_FILE) { public void applyProperty(final CPlatformWindow c, final Object value) {
181 if (value == null || !(value instanceof java.io.File)) {
182 nativeSetNSWindowRepresentedFilename(c.getNSWindowPtr(), null);
183 return;
184 }
185
186 final String filename = ((java.io.File)value).getAbsolutePath();
187 nativeSetNSWindowRepresentedFilename(c.getNSWindowPtr(), filename);
188 }}
189 }) {
190 public CPlatformWindow convertJComponentToTarget(final JRootPane p) {
191 Component root = SwingUtilities.getRoot(p);
192 if (root == null || (LWWindowPeer)root.getPeer() == null) return null;
193 return (CPlatformWindow)((LWWindowPeer)root.getPeer()).getPlatformWindow();
194 }
195 };
196
197 // Bounds of the native widget but in the Java coordinate system.
198 // In order to keep it up-to-date we will update them on
199 // 1) setting native bounds via nativeSetBounds() call
200 // 2) getting notification from the native level via deliverMoveResizeEvent()
201 private Rectangle nativeBounds = new Rectangle(0, 0, 0, 0);
202 private volatile boolean isFullScreenMode;
203 private boolean isFullScreenAnimationOn;
204
205 private Window target;
206 private LWWindowPeer peer;
207 private CPlatformView contentView;
208 private CPlatformWindow owner;
209 private boolean visible = false; // visibility status from native perspective
210 private boolean undecorated; // initialized in getInitialStyleBits()
211 private Rectangle normalBounds = null; // not-null only for undecorated maximized windows
212 private CPlatformResponder responder;
213 private volatile boolean zoomed = false; // from native perspective
214
215 public CPlatformWindow() {
216 super(0, true);
217 }
218
219 /*
220 * Delegate initialization (create native window and all the
221 * related resources).
222 */
223 @Override // PlatformWindow
224 public void initialize(Window _target, LWWindowPeer _peer, PlatformWindow _owner) {
225 initializeBase(_target, _peer, _owner, new CPlatformView());
226
227 final int styleBits = getInitialStyleBits();
228
229 // TODO: handle these misc properties
230 final long parentNSWindowPtr = (owner != null ? owner.getNSWindowPtr() : 0);
231 String warningString = target.getWarningString();
232
233 responder = new CPlatformResponder(peer, false);
234 contentView.initialize(peer, responder);
235
236 final long nativeWindowPtr = nativeCreateNSWindow(contentView.getAWTView(), styleBits, 0, 0, 0, 0);
237 setPtr(nativeWindowPtr);
238
239 // TODO: implement on top of JObjC bridged class
240 // NSWindow window = JObjC.getInstance().AppKit().NSWindow().getInstance(nativeWindowPtr, JObjCRuntime.getInstance());
241
242 if (target instanceof javax.swing.RootPaneContainer) {
243 final javax.swing.JRootPane rootpane = ((javax.swing.RootPaneContainer)target).getRootPane();
244 if (rootpane != null) rootpane.addPropertyChangeListener("ancestor", new PropertyChangeListener() {
245 public void propertyChange(final PropertyChangeEvent evt) {
246 CLIENT_PROPERTY_APPLICATOR.attachAndApplyClientProperties(rootpane);
247 rootpane.removePropertyChangeListener("ancestor", this);
248 }
249 });
250 }
251
252 validateSurface();
253 }
254
255 protected void initializeBase(Window target, LWWindowPeer peer, PlatformWindow owner, CPlatformView view) {
256 this.peer = peer;
257 this.target = target;
258 if (owner instanceof CPlatformWindow) {
259 this.owner = (CPlatformWindow)owner;
260 }
261 this.contentView = view;
262 }
263
264 private int getInitialStyleBits() {
265 // defaults style bits
266 int styleBits = DECORATED | HAS_SHADOW | CLOSEABLE | MINIMIZABLE | ZOOMABLE | RESIZABLE;
267
268 if (isNativelyFocusableWindow()) {
269 styleBits = SET(styleBits, SHOULD_BECOME_KEY, true);
270 styleBits = SET(styleBits, SHOULD_BECOME_MAIN, true);
271 }
272
273 final boolean isFrame = (target instanceof Frame);
274 final boolean isDialog = (target instanceof Dialog);
275 final boolean isPopup = (target.getType() == Window.Type.POPUP);
276 if (isDialog) {
277 styleBits = SET(styleBits, MINIMIZABLE, false);
278 }
279
280 // Either java.awt.Frame or java.awt.Dialog can be undecorated, however java.awt.Window always is undecorated.
281 {
282 this.undecorated = isFrame ? ((Frame)target).isUndecorated() : (isDialog ? ((Dialog)target).isUndecorated() : true);
283 if (this.undecorated) styleBits = SET(styleBits, DECORATED, false);
284 }
285
286 // Either java.awt.Frame or java.awt.Dialog can be resizable, however java.awt.Window is never resizable
287 {
288 final boolean resizable = isFrame ? ((Frame)target).isResizable() : (isDialog ? ((Dialog)target).isResizable() : false);
289 styleBits = SET(styleBits, RESIZABLE, resizable);
290 if (!resizable) {
291 styleBits = SET(styleBits, ZOOMABLE, false);
292 }
293 }
294
295 if (target.isAlwaysOnTop()) {
296 styleBits = SET(styleBits, ALWAYS_ON_TOP, true);
297 }
298
299 if (target.getModalExclusionType() == Dialog.ModalExclusionType.APPLICATION_EXCLUDE) {
300 styleBits = SET(styleBits, MODAL_EXCLUDED, true);
301 }
302
303 // If the target is a dialog, popup or tooltip we want it to ignore the brushed metal look.
304 if (isPopup) {
305 styleBits = SET(styleBits, TEXTURED, false);
306 // Popups in applets don't activate applet's process
307 styleBits = SET(styleBits, NONACTIVATING, true);
308 }
309
310 if (Window.Type.UTILITY.equals(target.getType())) {
311 styleBits = SET(styleBits, UTILITY, true);
312 }
313
314 if (target instanceof javax.swing.RootPaneContainer) {
315 javax.swing.JRootPane rootpane = ((javax.swing.RootPaneContainer)target).getRootPane();
316 Object prop = null;
317
318 prop = rootpane.getClientProperty(WINDOW_BRUSH_METAL_LOOK);
319 if (prop != null) {
320 styleBits = SET(styleBits, TEXTURED, Boolean.parseBoolean(prop.toString()));
321 }
322
323 if (isDialog && ((Dialog)target).getModalityType() == ModalityType.DOCUMENT_MODAL) {
324 prop = rootpane.getClientProperty(WINDOW_DOC_MODAL_SHEET);
325 if (prop != null) {
326 styleBits = SET(styleBits, SHEET, Boolean.parseBoolean(prop.toString()));
327 }
328 }
329
330 prop = rootpane.getClientProperty(WINDOW_STYLE);
331 if (prop != null) {
332 if ("small".equals(prop)) {
333 styleBits = SET(styleBits, UTILITY, true);
334 if (target.isAlwaysOnTop() && rootpane.getClientProperty(WINDOW_HIDES_ON_DEACTIVATE) == null) {
335 styleBits = SET(styleBits, HIDES_ON_DEACTIVATE, true);
336 }
337 }
338 if ("textured".equals(prop)) styleBits = SET(styleBits, TEXTURED, true);
339 if ("unified".equals(prop)) styleBits = SET(styleBits, UNIFIED, true);
340 if ("hud".equals(prop)) styleBits = SET(styleBits, HUD, true);
341 }
342
343 prop = rootpane.getClientProperty(WINDOW_HIDES_ON_DEACTIVATE);
344 if (prop != null) {
345 styleBits = SET(styleBits, HIDES_ON_DEACTIVATE, Boolean.parseBoolean(prop.toString()));
346 }
347
348 prop = rootpane.getClientProperty(WINDOW_CLOSEABLE);
349 if (prop != null) {
350 styleBits = SET(styleBits, CLOSEABLE, Boolean.parseBoolean(prop.toString()));
351 }
352
353 prop = rootpane.getClientProperty(WINDOW_MINIMIZABLE);
354 if (prop != null) {
355 styleBits = SET(styleBits, MINIMIZABLE, Boolean.parseBoolean(prop.toString()));
356 }
357
358 prop = rootpane.getClientProperty(WINDOW_ZOOMABLE);
359 if (prop != null) {
360 styleBits = SET(styleBits, ZOOMABLE, Boolean.parseBoolean(prop.toString()));
361 }
362
363 prop = rootpane.getClientProperty(WINDOW_FULLSCREENABLE);
364 if (prop != null) {
365 styleBits = SET(styleBits, FULLSCREENABLE, Boolean.parseBoolean(prop.toString()));
366 }
367
368 prop = rootpane.getClientProperty(WINDOW_SHADOW);
369 if (prop != null) {
370 styleBits = SET(styleBits, HAS_SHADOW, Boolean.parseBoolean(prop.toString()));
371 }
372
373 prop = rootpane.getClientProperty(WINDOW_DRAGGABLE_BACKGROUND);
374 if (prop != null) {
375 styleBits = SET(styleBits, DRAGGABLE_BACKGROUND, Boolean.parseBoolean(prop.toString()));
376 }
377 }
378
379 if (isDialog) {
380 styleBits = SET(styleBits, IS_DIALOG, true);
381 if (((Dialog) target).isModal()) {
382 styleBits = SET(styleBits, IS_MODAL, true);
383 }
384 }
385
386 peer.setTextured(IS(TEXTURED, styleBits));
387
388 return styleBits;
389 }
390
391 // this is the counter-point to -[CWindow _nativeSetStyleBit:]
392 private void setStyleBits(final int mask, final boolean value) {
393 nativeSetNSWindowStyleBits(getNSWindowPtr(), mask, value ? mask : 0);
394 }
395
396 private native void _toggleFullScreenMode(final long model);
397
398 public void toggleFullScreen() {
399 _toggleFullScreenMode(getNSWindowPtr());
400 }
401
402 @Override // PlatformWindow
403 public void setMenuBar(MenuBar mb) {
404 final long nsWindowPtr = getNSWindowPtr();
405 CMenuBar mbPeer = (CMenuBar)LWToolkit.targetToPeer(mb);
406 if (mbPeer != null) {
407 nativeSetNSWindowMenuBar(nsWindowPtr, mbPeer.getModel());
408 } else {
409 nativeSetNSWindowMenuBar(nsWindowPtr, 0);
410 }
411 }
412
413 @Override // PlatformWindow
414 public void dispose() {
415 if (owner != null) {
416 CWrapper.NSWindow.removeChildWindow(owner.getNSWindowPtr(), getNSWindowPtr());
417 }
418 contentView.dispose();
419 nativeDispose(getNSWindowPtr());
420 CPlatformWindow.super.dispose();
421 }
422
423 @Override // PlatformWindow
424 public FontMetrics getFontMetrics(Font f) {
425 // TODO: not implemented
426 (new RuntimeException("unimplemented")).printStackTrace();
427 return null;
428 }
429
430 @Override // PlatformWindow
431 public Insets getInsets() {
432 if (!isFullScreenMode) {
433 return nativeGetNSWindowInsets(getNSWindowPtr());
434 }
435 return new Insets(0, 0, 0, 0);
436 }
437
438 @Override // PlatformWindow
439 public Point getLocationOnScreen() {
440 return new Point(nativeBounds.x, nativeBounds.y);
441 }
442
443 @Override
444 public GraphicsDevice getGraphicsDevice() {
445 return contentView.getGraphicsDevice();
446 }
447
448 @Override // PlatformWindow
449 public SurfaceData getScreenSurface() {
450 // TODO: not implemented
451 return null;
452 }
453
454 @Override // PlatformWindow
455 public SurfaceData replaceSurfaceData() {
456 return contentView.replaceSurfaceData();
457 }
458
459 @Override // PlatformWindow
460 public void setBounds(int x, int y, int w, int h) {
461 // assert CThreading.assertEventQueue();
462 nativeSetNSWindowBounds(getNSWindowPtr(), x, y, w, h);
463 }
464
465 private boolean isMaximized() {
466 return undecorated ? this.normalBounds != null : zoomed;
467 }
468
469 private void maximize() {
470 if (isMaximized()) {
471 return;
472 }
473 if (!undecorated) {
474 zoomed = true;
475 CWrapper.NSWindow.zoom(getNSWindowPtr());
476 } else {
477 deliverZoom(true);
478
479 this.normalBounds = peer.getBounds();
480 long screen = CWrapper.NSWindow.screen(getNSWindowPtr());
481 Rectangle toBounds = CWrapper.NSScreen.visibleFrame(screen).getBounds();
482 // Flip the y coordinate
483 Rectangle frame = CWrapper.NSScreen.frame(screen).getBounds();
484 toBounds.y = frame.height - toBounds.y - toBounds.height;
485 setBounds(toBounds.x, toBounds.y, toBounds.width, toBounds.height);
486 }
487 }
488
489 private void unmaximize() {
490 if (!isMaximized()) {
491 return;
492 }
493 if (!undecorated) {
494 zoomed = false;
495 CWrapper.NSWindow.zoom(getNSWindowPtr());
496 } else {
497 deliverZoom(false);
498
499 Rectangle toBounds = this.normalBounds;
500 this.normalBounds = null;
501 setBounds(toBounds.x, toBounds.y, toBounds.width, toBounds.height);
502 }
503 }
504
505 private boolean isVisible() {
506 return this.visible;
507 }
508
509 @Override // PlatformWindow
510 public void setVisible(boolean visible) {
511 final long nsWindowPtr = getNSWindowPtr();
512
513 // Process parent-child relationship when hiding
514 if (!visible) {
515 // Unparent my children
516 for (Window w : target.getOwnedWindows()) {
517 WindowPeer p = (WindowPeer)w.getPeer();
518 if (p instanceof LWWindowPeer) {
519 CPlatformWindow pw = (CPlatformWindow)((LWWindowPeer)p).getPlatformWindow();
520 if (pw != null && pw.isVisible()) {
521 CWrapper.NSWindow.removeChildWindow(nsWindowPtr, pw.getNSWindowPtr());
522 }
523 }
524 }
525
526 // Unparent myself
527 if (owner != null && owner.isVisible()) {
528 CWrapper.NSWindow.removeChildWindow(owner.getNSWindowPtr(), nsWindowPtr);
529 }
530 }
531
532 // Configure stuff
533 updateIconImages();
534 updateFocusabilityForAutoRequestFocus(false);
535
536 // Actually show or hide the window
537 LWWindowPeer blocker = peer.getBlocker();
538 if (blocker == null || !visible) {
539 // If it ain't blocked, or is being hidden, go regular way
540 if (visible) {
541 CWrapper.NSWindow.makeFirstResponder(nsWindowPtr, contentView.getAWTView());
542
543 boolean isPopup = (target.getType() == Window.Type.POPUP);
544 if (isPopup) {
545 // Popups in applets don't activate applet's process
546 CWrapper.NSWindow.orderFrontRegardless(nsWindowPtr);
547 } else {
548 CWrapper.NSWindow.orderFront(nsWindowPtr);
549 }
550
551 boolean isKeyWindow = CWrapper.NSWindow.isKeyWindow(nsWindowPtr);
552 if (!isKeyWindow) {
553 CWrapper.NSWindow.makeKeyWindow(nsWindowPtr);
554 }
555 } else {
556 CWrapper.NSWindow.orderOut(nsWindowPtr);
557 }
558 } else {
559 // otherwise, put it in a proper z-order
560 CWrapper.NSWindow.orderWindow(nsWindowPtr, CWrapper.NSWindow.NSWindowBelow,
561 ((CPlatformWindow)blocker.getPlatformWindow()).getNSWindowPtr());
562 }
563 this.visible = visible;
564
565 // Manage the extended state when showing
566 if (visible) {
567 // Apply the extended state as expected in shared code
568 if (target instanceof Frame) {
569 switch (((Frame)target).getExtendedState()) {
570 case Frame.ICONIFIED:
571 CWrapper.NSWindow.miniaturize(nsWindowPtr);
572 break;
573 case Frame.MAXIMIZED_BOTH:
574 maximize();
575 break;
576 default: // NORMAL
577 unmaximize(); // in case it was maximized, otherwise this is a no-op
578 break;
579 }
580 }
581 }
582
583 nativeSynthesizeMouseEnteredExitedEvents();
584
585 // Configure stuff #2
586 updateFocusabilityForAutoRequestFocus(true);
587
588 // Manage parent-child relationship when showing
589 if (visible) {
590 // Add myself as a child
591 if (owner != null && owner.isVisible()) {
592 CWrapper.NSWindow.addChildWindow(owner.getNSWindowPtr(), nsWindowPtr, CWrapper.NSWindow.NSWindowAbove);
593 if (target.isAlwaysOnTop()) {
594 CWrapper.NSWindow.setLevel(nsWindowPtr, CWrapper.NSWindow.NSFloatingWindowLevel);
595 }
596 }
597
598 // Add my own children to myself
599 for (Window w : target.getOwnedWindows()) {
600 WindowPeer p = (WindowPeer)w.getPeer();
601 if (p instanceof LWWindowPeer) {
602 CPlatformWindow pw = (CPlatformWindow)((LWWindowPeer)p).getPlatformWindow();
603 if (pw != null && pw.isVisible()) {
604 CWrapper.NSWindow.addChildWindow(nsWindowPtr, pw.getNSWindowPtr(), CWrapper.NSWindow.NSWindowAbove);
605 if (w.isAlwaysOnTop()) {
606 CWrapper.NSWindow.setLevel(pw.getNSWindowPtr(), CWrapper.NSWindow.NSFloatingWindowLevel);
607 }
608 }
609 }
610 }
611 }
612
613 // Deal with the blocker of the window being shown
614 if (blocker != null && visible) {
615 // Make sure the blocker is above its siblings
616 ((CPlatformWindow)blocker.getPlatformWindow()).orderAboveSiblings();
617 }
618 }
619
620 @Override // PlatformWindow
621 public void setTitle(String title) {
622 nativeSetNSWindowTitle(getNSWindowPtr(), title);
623 }
624
625 // Should be called on every window key property change.
626 @Override // PlatformWindow
627 public void updateIconImages() {
628 final long nsWindowPtr = getNSWindowPtr();
629 final CImage cImage = getImageForTarget();
630 nativeSetNSWindowMinimizedIcon(nsWindowPtr, cImage == null ? 0L : cImage.ptr);
631 }
632
633 public long getNSWindowPtr() {
634 final long nsWindowPtr = ptr;
635 if (nsWindowPtr == 0L) {
636 if(logger.isLoggable(PlatformLogger.FINE)) {
637 logger.fine("NSWindow already disposed?", new Exception("Pointer to native NSWindow is invalid."));
638 }
639 }
640 return nsWindowPtr;
641 }
642
643 public SurfaceData getSurfaceData() {
644 return contentView.getSurfaceData();
645 }
646
647 @Override // PlatformWindow
648 public void toBack() {
649 final long nsWindowPtr = getNSWindowPtr();
650 nativePushNSWindowToBack(nsWindowPtr);
651 }
652
653 @Override // PlatformWindow
654 public void toFront() {
655 final long nsWindowPtr = getNSWindowPtr();
656 updateFocusabilityForAutoRequestFocus(false);
657 nativePushNSWindowToFront(nsWindowPtr);
658 updateFocusabilityForAutoRequestFocus(true);
659 }
660
661 @Override
662 public void setResizable(final boolean resizable) {
663 setStyleBits(RESIZABLE, resizable);
664 }
665
666 @Override
667 public void setSizeConstraints(int minW, int minH, int maxW, int maxH) {
668 nativeSetNSWindowMinMax(getNSWindowPtr(), minW, minH, maxW, maxH);
669 }
670
671 @Override
672 public boolean rejectFocusRequest(CausedFocusEvent.Cause cause) {
673 // Cross-app activation requests are not allowed.
674 if (cause != CausedFocusEvent.Cause.MOUSE_EVENT &&
675 !((LWCToolkit)Toolkit.getDefaultToolkit()).isApplicationActive())
676 {
677 focusLogger.fine("the app is inactive, so the request is rejected");
678 return true;
679 }
680 return false;
681 }
682
683 @Override
684 public boolean requestWindowFocus() {
685
686 long ptr = getNSWindowPtr();
687 if (CWrapper.NSWindow.canBecomeMainWindow(ptr)) {
688 CWrapper.NSWindow.makeMainWindow(ptr);
689 }
690 CWrapper.NSWindow.makeKeyAndOrderFront(ptr);
691 return true;
692 }
693
694 @Override
695 public boolean isActive() {
696 long ptr = getNSWindowPtr();
697 return CWrapper.NSWindow.isKeyWindow(ptr);
698 }
699
700 @Override
701 public void updateFocusableWindowState() {
702 final boolean isFocusable = isNativelyFocusableWindow();
703 setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, isFocusable); // set both bits at once
704 }
705
706 @Override
707 public Graphics transformGraphics(Graphics g) {
708 // is this where we can inject a transform for HiDPI?
709 return g;
710 }
711
712 @Override
713 public void setAlwaysOnTop(boolean isAlwaysOnTop) {
714 setStyleBits(ALWAYS_ON_TOP, isAlwaysOnTop);
715 }
716
717 public PlatformWindow getTopmostPlatformWindowUnderMouse(){
718 return CPlatformWindow.nativeGetTopmostPlatformWindowUnderMouse();
719 }
720
721 @Override
722 public void setOpacity(float opacity) {
723 CWrapper.NSWindow.setAlphaValue(getNSWindowPtr(), opacity);
724 }
725
726 @Override
727 public void setOpaque(boolean isOpaque) {
728 CWrapper.NSWindow.setOpaque(getNSWindowPtr(), isOpaque);
729 if (!isOpaque && !peer.isTextured()) {
730 long clearColor = CWrapper.NSColor.clearColor();
731 CWrapper.NSWindow.setBackgroundColor(getNSWindowPtr(), clearColor);
732 }
733
734 //This is a temporary workaround. Looks like after 7124236 will be fixed
735 //the correct place for invalidateShadow() is CGLayer.drawInCGLContext.
736 SwingUtilities.invokeLater(new Runnable() {
737 @Override
738 public void run() {
739 invalidateShadow();
740 }
741 });
742 }
743
744 @Override
745 public void enterFullScreenMode() {
746 isFullScreenMode = true;
747 contentView.enterFullScreenMode();
748 // the move/size notification from the underlying system comes
749 // but it contains a bounds smaller than the whole screen
750 // and therefore we need to create the synthetic notifications
751 Rectangle screenBounds;
752 final long screenPtr = CWrapper.NSWindow.screen(getNSWindowPtr());
753 try {
754 screenBounds = CWrapper.NSScreen.frame(screenPtr).getBounds();
755 } finally {
756 CWrapper.NSObject.release(screenPtr);
757 }
758 peer.notifyReshape(screenBounds.x, screenBounds.y, screenBounds.width,
759 screenBounds.height);
760 }
761
762 @Override
763 public void exitFullScreenMode() {
764 contentView.exitFullScreenMode();
765 isFullScreenMode = false;
766 }
767
768 @Override
769 public void setWindowState(int windowState) {
770 if (!peer.isVisible()) {
771 // setVisible() applies the state
772 return;
773 }
774
775 int prevWindowState = peer.getState();
776 if (prevWindowState == windowState) return;
777
778 final long nsWindowPtr = getNSWindowPtr();
779 switch (windowState) {
780 case Frame.ICONIFIED:
781 if (prevWindowState == Frame.MAXIMIZED_BOTH) {
782 // let's return into the normal states first
783 // the zoom call toggles between the normal and the max states
784 unmaximize();
785 }
786 CWrapper.NSWindow.miniaturize(nsWindowPtr);
787 break;
788 case Frame.MAXIMIZED_BOTH:
789 if (prevWindowState == Frame.ICONIFIED) {
790 // let's return into the normal states first
791 CWrapper.NSWindow.deminiaturize(nsWindowPtr);
792 }
793 maximize();
794 break;
795 case Frame.NORMAL:
796 if (prevWindowState == Frame.ICONIFIED) {
797 CWrapper.NSWindow.deminiaturize(nsWindowPtr);
798 } else if (prevWindowState == Frame.MAXIMIZED_BOTH) {
799 // the zoom call toggles between the normal and the max states
800 unmaximize();
801 }
802 break;
803 default:
804 throw new RuntimeException("Unknown window state: " + windowState);
805 }
806
807 nativeSynthesizeMouseEnteredExitedEvents();
808
809 // NOTE: the SWP.windowState field gets updated to the newWindowState
810 // value when the native notification comes to us
811 }
812
813 @Override
814 public void setModalBlocked(boolean blocked) {
815 if (target.getModalExclusionType() == Dialog.ModalExclusionType.APPLICATION_EXCLUDE) {
816 return;
817 }
818
819 nativeSetEnabled(getNSWindowPtr(), !blocked);
820 }
821
822 public final void invalidateShadow(){
823 nativeRevalidateNSWindowShadow(getNSWindowPtr());
824 }
825
826 // ----------------------------------------------------------------------
827 // UTILITY METHODS
828 // ----------------------------------------------------------------------
829
830 /*
831 * Find image to install into Title or into Application icon.
832 * First try icons installed for toplevel. If there is no icon
833 * use default Duke image.
834 * This method shouldn't return null.
835 */
836 private CImage getImageForTarget() {
837 List<Image> icons = target.getIconImages();
838 if (icons == null || icons.size() == 0) {
839 return null;
840 }
841 return CImage.getCreator().createFromImages(icons);
842 }
843
844 /*
845 * Returns LWWindowPeer associated with this delegate.
846 */
847 @Override
848 public LWWindowPeer getPeer() {
849 return peer;
850 }
851
852 @Override
853 public boolean isUnderMouse() {
854 return contentView.isUnderMouse();
855 }
856
857 public CPlatformView getContentView() {
858 return contentView;
859 }
860
861 @Override
862 public long getLayerPtr() {
863 return contentView.getWindowLayerPtr();
864 }
865
866 private void validateSurface() {
867 SurfaceData surfaceData = getSurfaceData();
868 if (surfaceData instanceof CGLSurfaceData) {
869 ((CGLSurfaceData)surfaceData).validate();
870 }
871 }
872
873 void flushBuffers() {
874 if (isVisible() && !nativeBounds.isEmpty() && !isFullScreenMode) {
875 try {
876 LWCToolkit.invokeAndWait(new Runnable() {
877 @Override
878 public void run() {
879 //Posting an empty to flush the EventQueue without blocking the main thread
880 }
881 }, target);
882 } catch (InterruptedException | InvocationTargetException e) {
883 e.printStackTrace();
884 }
885 }
886 }
887
888 /**
889 * Helper method to get a pointer to the native view from the PlatformWindow.
890 */
891 static long getNativeViewPtr(PlatformWindow platformWindow) {
892 long nativePeer = 0L;
893 if (platformWindow instanceof CPlatformWindow) {
894 nativePeer = ((CPlatformWindow) platformWindow).getContentView().getAWTView();
895 } else if (platformWindow instanceof CViewPlatformEmbeddedFrame){
896 nativePeer = ((CViewPlatformEmbeddedFrame) platformWindow).getNSViewPtr();
897 } else {
898 throw new IllegalArgumentException("Unsupported platformWindow implementation");
899 }
900 return nativePeer;
901 }
902
903 /*************************************************************
904 * Callbacks from the AWTWindow and AWTView objc classes.
905 *************************************************************/
906 private void deliverWindowFocusEvent(boolean gained, CPlatformWindow opposite){
907 // Fix for 7150349: ingore "gained" notifications when the app is inactive.
908 if (gained && !((LWCToolkit)Toolkit.getDefaultToolkit()).isApplicationActive()) {
909 focusLogger.fine("the app is inactive, so the notification is ignored");
910 return;
911 }
912
913 LWWindowPeer oppositePeer = (opposite == null)? null : opposite.getPeer();
914 responder.handleWindowFocusEvent(gained, oppositePeer);
915 }
916
917 private void deliverMoveResizeEvent(int x, int y, int width, int height,
918 boolean byUser) {
919 // when the content view enters the full-screen mode, the native
920 // move/resize notifications contain a bounds smaller than
921 // the whole screen and therefore we ignore the native notifications
922 // and the content view itself creates correct synthetic notifications
923 if (isFullScreenMode) {
924 return;
925 }
926
927 final Rectangle oldB = nativeBounds;
928 nativeBounds = new Rectangle(x, y, width, height);
929 final GraphicsConfiguration oldGC = peer.getGraphicsConfiguration();
930 peer.notifyReshape(x, y, width, height);
931 final GraphicsConfiguration newGC = peer.getGraphicsConfiguration();
932 // System-dependent appearance optimization.
933 if ((byUser && !oldB.getSize().equals(nativeBounds.getSize()))
934 || isFullScreenAnimationOn || !Objects.equals(newGC, oldGC)) {
935 flushBuffers();
936 }
937 }
938
939 private void deliverWindowClosingEvent() {
940 if (peer.getBlocker() == null) {
941 peer.postEvent(new WindowEvent(target, WindowEvent.WINDOW_CLOSING));
942 }
943 }
944
945 private void deliverIconify(final boolean iconify) {
946 peer.notifyIconify(iconify);
947 }
948
949 private void deliverZoom(final boolean isZoomed) {
950 peer.notifyZoom(isZoomed);
951 }
952
953 private void deliverNCMouseDown() {
954 peer.notifyNCMouseDown();
955 }
956
957 /*
958 * Our focus model is synthetic and only non-simple window
959 * may become natively focusable window.
960 */
961 private boolean isNativelyFocusableWindow() {
962 return !peer.isSimpleWindow() && target.getFocusableWindowState();
963 }
964
965 /*
966 * An utility method for the support of the auto request focus.
967 * Updates the focusable state of the window under certain
968 * circumstances.
969 */
970 private void updateFocusabilityForAutoRequestFocus(boolean isFocusable) {
971 if (target.isAutoRequestFocus() || !isNativelyFocusableWindow()) return;
972 setStyleBits(SHOULD_BECOME_KEY | SHOULD_BECOME_MAIN, isFocusable); // set both bits at once
973 }
974
975 private boolean checkBlocking() {
976 LWWindowPeer blocker = peer.getBlocker();
977 if (blocker == null) {
978 return false;
979 }
980
981 if (blocker instanceof CPrinterDialogPeer) {
982 return true;
983 }
984
985 CPlatformWindow pWindow = (CPlatformWindow)blocker.getPlatformWindow();
986
987 pWindow.orderAboveSiblings();
988
989 final long nsWindowPtr = pWindow.getNSWindowPtr();
990 CWrapper.NSWindow.orderFrontRegardless(nsWindowPtr);
991 CWrapper.NSWindow.makeKeyAndOrderFront(nsWindowPtr);
992 CWrapper.NSWindow.makeMainWindow(nsWindowPtr);
993
994 return true;
995 }
996
997 private void orderAboveSiblings() {
998 if (owner == null) {
999 return;
1000 }
1001
1002 // NOTE: the logic will fail if we have a hierarchy like:
1003 // visible root owner
1004 // invisible owner
1005 // visible dialog
1006 // However, this is an unlikely scenario for real life apps
1007 if (owner.isVisible()) {
1008 // Recursively pop up the windows from the very bottom so that only
1009 // the very top-most one becomes the main window
1010 owner.orderAboveSiblings();
1011
1012 // Order the window to front of the stack of child windows
1013 final long nsWindowSelfPtr = getNSWindowPtr();
1014 final long nsWindowOwnerPtr = owner.getNSWindowPtr();
1015 CWrapper.NSWindow.removeChildWindow(nsWindowOwnerPtr, nsWindowSelfPtr);
1016 CWrapper.NSWindow.addChildWindow(nsWindowOwnerPtr, nsWindowSelfPtr, CWrapper.NSWindow.NSWindowAbove);
1017 }
1018
1019 if (target.isAlwaysOnTop()) {
1020 CWrapper.NSWindow.setLevel(getNSWindowPtr(), CWrapper.NSWindow.NSFloatingWindowLevel);
1021 }
1022 }
1023
1024 // ----------------------------------------------------------------------
1025 // NATIVE CALLBACKS
1026 // ----------------------------------------------------------------------
1027
1028 private void windowDidBecomeMain() {
1029 assert CThreading.assertAppKit();
1030
1031 if (checkBlocking()) return;
1032 // If it's not blocked, make sure it's above its siblings
1033 orderAboveSiblings();
1034 }
1035
1036 private void windowWillEnterFullScreen() {
1037 isFullScreenAnimationOn = true;
1038 }
1039
1040 private void windowDidEnterFullScreen() {
1041 isFullScreenAnimationOn = false;
1042 }
1043
1044 private void windowWillExitFullScreen() {
1045 isFullScreenAnimationOn = true;
1046 }
1047
1048 private void windowDidExitFullScreen() {
1049 isFullScreenAnimationOn = false;
1050 }
1051 }
--- EOF ---