Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/unix/classes/sun/awt/X11/XBaseMenuWindow.java
41159 views
1
/*
2
* Copyright (c) 2005, 2017, 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.X11;
26
27
import java.awt.*;
28
import java.awt.event.*;
29
30
import sun.awt.*;
31
32
import java.awt.peer.ComponentPeer;
33
import java.util.ArrayList;
34
import java.util.Vector;
35
import sun.util.logging.PlatformLogger;
36
import sun.java2d.SurfaceData;
37
38
/**
39
* The abstract class XBaseMenuWindow is the superclass
40
* of all menu windows.
41
*/
42
public abstract class XBaseMenuWindow extends XWindow {
43
44
/************************************************
45
*
46
* Data members
47
*
48
************************************************/
49
50
private static PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XBaseMenuWindow");
51
52
/*
53
* Colors are calculated using MotifColorUtilities class
54
* from backgroundColor and are contained in these vars.
55
*/
56
private Color backgroundColor;
57
private Color foregroundColor;
58
private Color lightShadowColor;
59
private Color darkShadowColor;
60
private Color selectedColor;
61
private Color disabledColor;
62
63
/**
64
* Array of items.
65
*/
66
private ArrayList<XMenuItemPeer> items;
67
68
/**
69
* Index of selected item in array of items
70
*/
71
private int selectedIndex = -1;
72
73
/**
74
* Specifies currently showing submenu.
75
*/
76
private XMenuPeer showingSubmenu = null;
77
78
/**
79
* Static synchronizational object.
80
* Following operations should be synchronized
81
* using this object:
82
* 1. Access to items vector
83
* 2. Access to selection
84
* 3. Access to showing menu window member
85
*
86
* This is lowest level lock,
87
* no other locks should be taken when
88
* thread own this lock.
89
*/
90
private static Object menuTreeLock = new Object();
91
92
/************************************************
93
*
94
* Event processing
95
*
96
************************************************/
97
98
/**
99
* If mouse button is clicked on item showing submenu
100
* we have to hide its submenu.
101
* And if mouse button is pressed on such item and
102
* dragged to another, getShowingSubmenu() is changed.
103
* So this member saves the item that the user
104
* presses mouse button on _only_ if it's showing submenu.
105
*/
106
private XMenuPeer showingMousePressedSubmenu = null;
107
108
/**
109
* If the PopupMenu is invoked as a result of right button click
110
* first mouse event after grabInput would be MouseReleased.
111
* We need to check if the user has moved mouse after input grab.
112
* If yes - hide the PopupMenu. If no - do nothing
113
*/
114
protected Point grabInputPoint = null;
115
protected boolean hasPointerMoved = false;
116
117
private AppContext disposeAppContext;
118
119
/************************************************
120
*
121
* Mapping data
122
*
123
************************************************/
124
125
/**
126
* Mapping data that is filled in getMappedItems function
127
* and reset in resetSize function. It contains array of
128
* items in order that they appear on screen and may contain
129
* additional data defined by descendants.
130
*/
131
private MappingData mappingData;
132
133
static class MappingData implements Cloneable {
134
135
/**
136
* Array of item in order that they appear on screen
137
*/
138
private XMenuItemPeer[] items;
139
140
/**
141
* Constructs MappingData object with list
142
* of menu items
143
*/
144
MappingData(XMenuItemPeer[] items) {
145
this.items = items;
146
}
147
148
/**
149
* Constructs MappingData without items
150
* This constructor should be used in case of errors
151
*/
152
MappingData() {
153
this.items = new XMenuItemPeer[0];
154
}
155
156
public Object clone() {
157
try {
158
return super.clone();
159
} catch (CloneNotSupportedException ex) {
160
throw new InternalError(ex);
161
}
162
}
163
164
public XMenuItemPeer[] getItems() {
165
return this.items;
166
}
167
}
168
169
/************************************************
170
*
171
* Construction
172
*
173
************************************************/
174
XBaseMenuWindow() {
175
super(new XCreateWindowParams(new Object[] {
176
DELAYED, Boolean.TRUE}));
177
178
disposeAppContext = AppContext.getAppContext();
179
}
180
181
/************************************************
182
*
183
* Abstract methods
184
*
185
************************************************/
186
187
/**
188
* Returns parent menu window (not the X-hierarchy parent window)
189
*/
190
protected abstract XBaseMenuWindow getParentMenuWindow();
191
192
/**
193
* Performs mapping of items in window.
194
* This function creates and fills specific
195
* descendant of MappingData
196
* and sets mapping coordinates of items
197
* This function should return default menu data
198
* if errors occur
199
*/
200
protected abstract MappingData map();
201
202
/**
203
* Calculates placement of submenu window
204
* given bounds of item with submenu and
205
* size of submenu window. Returns suggested
206
* rectangle for submenu window in global coordinates
207
* @param itemBounds the bounding rectangle of item
208
* in local coordinates
209
* @param windowSize the desired size of submenu's window
210
*/
211
protected abstract Rectangle getSubmenuBounds(Rectangle itemBounds, Dimension windowSize);
212
213
214
/**
215
* This function is to be called if it's likely that size
216
* of items was changed. It can be called from any thread
217
* in any locked state, so it should not take locks
218
*/
219
protected abstract void updateSize();
220
221
/************************************************
222
*
223
* Initialization
224
*
225
************************************************/
226
227
/**
228
* Overrides XBaseWindow.instantPreInit
229
*/
230
void instantPreInit(XCreateWindowParams params) {
231
super.instantPreInit(params);
232
items = new ArrayList<>();
233
}
234
235
/************************************************
236
*
237
* General-purpose functions
238
*
239
************************************************/
240
241
/**
242
* Returns static lock used for menus
243
*/
244
static Object getMenuTreeLock() {
245
return menuTreeLock;
246
}
247
248
/**
249
* This function is called to clear all saved
250
* size data.
251
*/
252
protected void resetMapping() {
253
mappingData = null;
254
}
255
256
/**
257
* Invokes repaint procedure on eventHandlerThread
258
*/
259
void postPaintEvent() {
260
if (isShowing()) {
261
PaintEvent pe = new PaintEvent(target, PaintEvent.PAINT,
262
new Rectangle(0, 0, width, height));
263
postEvent(pe);
264
}
265
}
266
267
/************************************************
268
*
269
* Utility functions for manipulating items
270
*
271
************************************************/
272
273
/**
274
* Thread-safely returns item at specified index
275
* @param index the position of the item to be returned.
276
*/
277
XMenuItemPeer getItem(int index) {
278
if (index >= 0) {
279
synchronized(getMenuTreeLock()) {
280
if (items.size() > index) {
281
return items.get(index);
282
}
283
}
284
}
285
return null;
286
}
287
288
/**
289
* Thread-safely creates a copy of the items vector
290
*/
291
XMenuItemPeer[] copyItems() {
292
synchronized(getMenuTreeLock()) {
293
return items.toArray(new XMenuItemPeer[] {});
294
}
295
}
296
297
298
/**
299
* Thread-safely returns selected item
300
*/
301
XMenuItemPeer getSelectedItem() {
302
synchronized(getMenuTreeLock()) {
303
if (selectedIndex >= 0) {
304
if (items.size() > selectedIndex) {
305
return items.get(selectedIndex);
306
}
307
}
308
return null;
309
}
310
}
311
312
/**
313
* Returns showing submenu, if any
314
*/
315
XMenuPeer getShowingSubmenu() {
316
synchronized(getMenuTreeLock()) {
317
return showingSubmenu;
318
}
319
}
320
321
/**
322
* Adds item to end of items vector.
323
* Note that this function does not perform
324
* check for adding duplicate items
325
* @param item item to add
326
*/
327
public void addItem(MenuItem item) {
328
XMenuItemPeer mp = AWTAccessor.getMenuComponentAccessor().getPeer(item);
329
if (mp != null) {
330
mp.setContainer(this);
331
synchronized(getMenuTreeLock()) {
332
items.add(mp);
333
}
334
} else {
335
if (log.isLoggable(PlatformLogger.Level.FINE)) {
336
log.fine("WARNING: Attempt to add menu item without a peer");
337
}
338
}
339
updateSize();
340
}
341
342
/**
343
* Removes item at the specified index from items vector.
344
* @param index the position of the item to be removed
345
*/
346
public void delItem(int index) {
347
synchronized(getMenuTreeLock()) {
348
if (selectedIndex == index) {
349
selectItem(null, false);
350
} else if (selectedIndex > index) {
351
selectedIndex--;
352
}
353
if (index < items.size()) {
354
items.remove(index);
355
} else {
356
if (log.isLoggable(PlatformLogger.Level.FINE)) {
357
log.fine("WARNING: Attempt to remove non-existing menu item, index : " + index + ", item count : " + items.size());
358
}
359
}
360
}
361
updateSize();
362
}
363
364
/**
365
* Clears items vector and loads specified vector
366
* @param items vector to be loaded
367
*/
368
public void reloadItems(Vector<? extends MenuItem> items) {
369
synchronized(getMenuTreeLock()) {
370
this.items.clear();
371
MenuItem[] itemArray = items.toArray(new MenuItem[] {});
372
int itemCnt = itemArray.length;
373
for(int i = 0; i < itemCnt; i++) {
374
addItem(itemArray[i]);
375
}
376
}
377
}
378
379
/**
380
* Select specified item and shows/hides submenus if necessary
381
* We can not select by index, so we need to select by ref.
382
* @param item the item to be selected, null to clear selection
383
* @param showWindowIfMenu if the item is XMenuPeer then its
384
* window is shown/hidden according to this param.
385
*/
386
void selectItem(XMenuItemPeer item, boolean showWindowIfMenu) {
387
synchronized(getMenuTreeLock()) {
388
XMenuPeer showingSubmenu = getShowingSubmenu();
389
int newSelectedIndex = (item != null) ? items.indexOf(item) : -1;
390
if (this.selectedIndex != newSelectedIndex) {
391
if (log.isLoggable(PlatformLogger.Level.FINEST)) {
392
log.finest("Selected index changed, was : " + this.selectedIndex + ", new : " + newSelectedIndex);
393
}
394
this.selectedIndex = newSelectedIndex;
395
postPaintEvent();
396
}
397
final XMenuPeer submenuToShow = (showWindowIfMenu && (item instanceof XMenuPeer)) ? (XMenuPeer)item : null;
398
if (submenuToShow != showingSubmenu) {
399
XToolkit.executeOnEventHandlerThread(target, new Runnable() {
400
public void run() {
401
doShowSubmenu(submenuToShow);
402
}
403
});
404
}
405
}
406
}
407
408
/**
409
* Performs hiding of currently showing submenu
410
* and showing of submenuToShow.
411
* This function should be executed on eventHandlerThread
412
* @param submenuToShow submenu to be shown or null
413
* to hide currently showing submenu
414
*/
415
private void doShowSubmenu(XMenuPeer submenuToShow) {
416
XMenuWindow menuWindowToShow = (submenuToShow != null) ? submenuToShow.getMenuWindow() : null;
417
Dimension dim = null;
418
Rectangle bounds = null;
419
//ensureCreated can invoke XWindowPeer.init() ->
420
//XWindowPeer.initGraphicsConfiguration() ->
421
//Window.getGraphicsConfiguration()
422
//that tries to obtain Component.AWTTreeLock.
423
//So it should be called outside awtLock()
424
if (menuWindowToShow != null) {
425
menuWindowToShow.ensureCreated();
426
}
427
XToolkit.awtLock();
428
try {
429
synchronized(getMenuTreeLock()) {
430
if (showingSubmenu != submenuToShow) {
431
if (log.isLoggable(PlatformLogger.Level.FINEST)) {
432
log.finest("Changing showing submenu");
433
}
434
if (showingSubmenu != null) {
435
XMenuWindow showingSubmenuWindow = showingSubmenu.getMenuWindow();
436
if (showingSubmenuWindow != null) {
437
showingSubmenuWindow.hide();
438
}
439
}
440
if (submenuToShow != null) {
441
dim = menuWindowToShow.getDesiredSize();
442
bounds = menuWindowToShow.getParentMenuWindow().getSubmenuBounds(submenuToShow.getBounds(), dim);
443
menuWindowToShow.show(bounds);
444
}
445
showingSubmenu = submenuToShow;
446
}
447
}
448
} finally {
449
XToolkit.awtUnlock();
450
}
451
}
452
453
final void setItemsFont( Font font ) {
454
XMenuItemPeer[] items = copyItems();
455
int itemCnt = items.length;
456
for (int i = 0; i < itemCnt; i++) {
457
items[i].setFont(font);
458
}
459
}
460
461
/************************************************
462
*
463
* Utility functions for manipulating mapped items
464
*
465
************************************************/
466
467
/**
468
* Returns array of mapped items, null if error
469
* This function has to be not synchronized
470
* and we have to guarantee that we return
471
* some MappingData to user. It's OK if
472
* this.mappingData is replaced meanwhile
473
*/
474
MappingData getMappingData() {
475
MappingData mappingData = this.mappingData;
476
if (mappingData == null) {
477
mappingData = map();
478
this.mappingData = mappingData;
479
}
480
return (MappingData)mappingData.clone();
481
}
482
483
/**
484
* returns item thats mapped coordinates contain
485
* specified point, null of none.
486
* @param pt the point in this window's coordinate system
487
*/
488
XMenuItemPeer getItemFromPoint(Point pt) {
489
XMenuItemPeer[] items = getMappingData().getItems();
490
int cnt = items.length;
491
for (int i = 0; i < cnt; i++) {
492
if (items[i].getBounds().contains(pt)) {
493
return items[i];
494
}
495
}
496
return null;
497
}
498
499
/**
500
* Returns first item after currently selected
501
* item that can be selected according to mapping array.
502
* (no separators and no disabled items).
503
* Currently selected item if it's only selectable,
504
* null if no item can be selected
505
*/
506
XMenuItemPeer getNextSelectableItem() {
507
XMenuItemPeer[] mappedItems = getMappingData().getItems();
508
XMenuItemPeer selectedItem = getSelectedItem();
509
int cnt = mappedItems.length;
510
//Find index of selected item
511
int selIdx = -1;
512
for (int i = 0; i < cnt; i++) {
513
if (mappedItems[i] == selectedItem) {
514
selIdx = i;
515
break;
516
}
517
}
518
int idx = (selIdx == cnt - 1) ? 0 : selIdx + 1;
519
//cycle through mappedItems to find selectable item
520
//beginning from the next item and moving to the
521
//beginning of array when end is reached.
522
//Cycle is finished on selected item itself
523
for (int i = 0; i < cnt; i++) {
524
XMenuItemPeer item = mappedItems[idx];
525
if (!item.isSeparator() && item.isTargetItemEnabled()) {
526
return item;
527
}
528
idx++;
529
if (idx >= cnt) {
530
idx = 0;
531
}
532
}
533
//return null if no selectable item was found
534
return null;
535
}
536
537
/**
538
* Returns first item before currently selected
539
* see getNextSelectableItem() for comments
540
*/
541
XMenuItemPeer getPrevSelectableItem() {
542
XMenuItemPeer[] mappedItems = getMappingData().getItems();
543
XMenuItemPeer selectedItem = getSelectedItem();
544
int cnt = mappedItems.length;
545
//Find index of selected item
546
int selIdx = -1;
547
for (int i = 0; i < cnt; i++) {
548
if (mappedItems[i] == selectedItem) {
549
selIdx = i;
550
break;
551
}
552
}
553
int idx = (selIdx <= 0) ? cnt - 1 : selIdx - 1;
554
//cycle through mappedItems to find selectable item
555
for (int i = 0; i < cnt; i++) {
556
XMenuItemPeer item = mappedItems[idx];
557
if (!item.isSeparator() && item.isTargetItemEnabled()) {
558
return item;
559
}
560
idx--;
561
if (idx < 0) {
562
idx = cnt - 1;
563
}
564
}
565
//return null if no selectable item was found
566
return null;
567
}
568
569
/**
570
* Returns first selectable item
571
* This function is intended for clearing selection
572
*/
573
XMenuItemPeer getFirstSelectableItem() {
574
XMenuItemPeer[] mappedItems = getMappingData().getItems();
575
int cnt = mappedItems.length;
576
for (int i = 0; i < cnt; i++) {
577
XMenuItemPeer item = mappedItems[i];
578
if (!item.isSeparator() && item.isTargetItemEnabled()) {
579
return item;
580
}
581
}
582
583
return null;
584
}
585
586
/************************************************
587
*
588
* Utility functions for manipulating
589
* hierarchy of windows
590
*
591
************************************************/
592
593
/**
594
* returns leaf menu window or
595
* this if no children are showing
596
*/
597
XBaseMenuWindow getShowingLeaf() {
598
synchronized(getMenuTreeLock()) {
599
XBaseMenuWindow leaf = this;
600
XMenuPeer leafchild = leaf.getShowingSubmenu();
601
while (leafchild != null) {
602
leaf = leafchild.getMenuWindow();
603
leafchild = leaf.getShowingSubmenu();
604
}
605
return leaf;
606
}
607
}
608
609
/**
610
* returns root menu window
611
* or this if this window is topmost
612
*/
613
XBaseMenuWindow getRootMenuWindow() {
614
synchronized(getMenuTreeLock()) {
615
XBaseMenuWindow t = this;
616
XBaseMenuWindow tparent = t.getParentMenuWindow();
617
while (tparent != null) {
618
t = tparent;
619
tparent = t.getParentMenuWindow();
620
}
621
return t;
622
}
623
}
624
625
/**
626
* Returns window that contains pt.
627
* search is started from leaf window
628
* to return first window in Z-order
629
* @param pt point in global coordinates
630
*/
631
XBaseMenuWindow getMenuWindowFromPoint(Point pt) {
632
synchronized(getMenuTreeLock()) {
633
XBaseMenuWindow t = getShowingLeaf();
634
while (t != null) {
635
Rectangle r = new Rectangle(t.toGlobal(new Point(0, 0)), t.getSize());
636
if (r.contains(pt)) {
637
return t;
638
}
639
t = t.getParentMenuWindow();
640
}
641
return null;
642
}
643
}
644
645
/************************************************
646
*
647
* Primitives for getSubmenuBounds
648
*
649
* These functions are invoked from getSubmenuBounds
650
* implementations in different order. They check if window
651
* of size windowSize fits to the specified edge of
652
* rectangle itemBounds on the screen of screenSize.
653
* Return rectangle that occupies the window if it fits or null.
654
*
655
************************************************/
656
657
GraphicsConfiguration getCurrentGraphicsConfiguration() {
658
Component hw = SunToolkit.getHeavyweightComponent(target);
659
XWindow peer = AWTAccessor.getComponentAccessor().getPeer(hw);
660
if (peer != null && peer.graphicsConfig != null) {
661
return peer.graphicsConfig;
662
}
663
return graphicsConfig;
664
}
665
666
/**
667
* Checks if window fits below specified item
668
* returns rectangle that the window fits to or null.
669
* @param itemBounds rectangle of item in global coordinates
670
* @param windowSize size of submenu window to fit
671
* @param screenBounds size of screen
672
*/
673
Rectangle fitWindowBelow(Rectangle itemBounds, Dimension windowSize, Rectangle screenBounds) {
674
int width = windowSize.width;
675
int height = windowSize.height;
676
//Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened
677
//near the periphery of the screen, XToolkit
678
//Window should be moved if it's outside top-left screen bounds
679
int x = (itemBounds.x > screenBounds.x) ? itemBounds.x : screenBounds.x;
680
int y = (itemBounds.y + itemBounds.height > screenBounds.y) ? itemBounds.y + itemBounds.height : screenBounds.y;
681
if (y + height <= screenBounds.y + screenBounds.height) {
682
//move it to the left if needed
683
if (width > screenBounds.width) {
684
width = screenBounds.width;
685
}
686
if (x + width > screenBounds.x + screenBounds.width) {
687
x = screenBounds.x + screenBounds.width - width;
688
}
689
return new Rectangle(x, y, width, height);
690
} else {
691
return null;
692
}
693
}
694
695
/**
696
* Checks if window fits above specified item
697
* returns rectangle that the window fits to or null.
698
* @param itemBounds rectangle of item in global coordinates
699
* @param windowSize size of submenu window to fit
700
* @param screenBounds size of screen
701
*/
702
Rectangle fitWindowAbove(Rectangle itemBounds, Dimension windowSize, Rectangle screenBounds) {
703
int width = windowSize.width;
704
int height = windowSize.height;
705
//Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened
706
//near the periphery of the screen, XToolkit
707
//Window should be moved if it's outside bottom-left screen bounds
708
int x = (itemBounds.x > screenBounds.x) ? itemBounds.x : screenBounds.x;
709
int y = (itemBounds.y > screenBounds.y + screenBounds.height) ? screenBounds.y + screenBounds.height - height : itemBounds.y - height;
710
if (y >= screenBounds.y) {
711
//move it to the left if needed
712
if (width > screenBounds.width) {
713
width = screenBounds.width;
714
}
715
if (x + width > screenBounds.x + screenBounds.width) {
716
x = screenBounds.x + screenBounds.width - width;
717
}
718
return new Rectangle(x, y, width, height);
719
} else {
720
return null;
721
}
722
}
723
724
/**
725
* Checks if window fits to the right specified item
726
* returns rectangle that the window fits to or null.
727
* @param itemBounds rectangle of item in global coordinates
728
* @param windowSize size of submenu window to fit
729
* @param screenBounds size of screen
730
*/
731
Rectangle fitWindowRight(Rectangle itemBounds, Dimension windowSize, Rectangle screenBounds) {
732
int width = windowSize.width;
733
int height = windowSize.height;
734
//Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened
735
//near the periphery of the screen, XToolkit
736
//Window should be moved if it's outside top-left screen bounds
737
int x = (itemBounds.x + itemBounds.width > screenBounds.x) ? itemBounds.x + itemBounds.width : screenBounds.x;
738
int y = (itemBounds.y > screenBounds.y) ? itemBounds.y : screenBounds.y;
739
if (x + width <= screenBounds.x + screenBounds.width) {
740
//move it to the top if needed
741
if (height > screenBounds.height) {
742
height = screenBounds.height;
743
}
744
if (y + height > screenBounds.y + screenBounds.height) {
745
y = screenBounds.y + screenBounds.height - height;
746
}
747
return new Rectangle(x, y, width, height);
748
} else {
749
return null;
750
}
751
}
752
753
/**
754
* Checks if window fits to the left specified item
755
* returns rectangle that the window fits to or null.
756
* @param itemBounds rectangle of item in global coordinates
757
* @param windowSize size of submenu window to fit
758
* @param screenBounds size of screen
759
*/
760
Rectangle fitWindowLeft(Rectangle itemBounds, Dimension windowSize, Rectangle screenBounds) {
761
int width = windowSize.width;
762
int height = windowSize.height;
763
//Fix for 6267162: PIT: Popup Menu gets hidden below the screen when opened
764
//near the periphery of the screen, XToolkit
765
//Window should be moved if it's outside top-right screen bounds
766
int x = (itemBounds.x < screenBounds.x + screenBounds.width) ? itemBounds.x - width : screenBounds.x + screenBounds.width - width;
767
int y = (itemBounds.y > screenBounds.y) ? itemBounds.y : screenBounds.y;
768
if (x >= screenBounds.x) {
769
//move it to the top if needed
770
if (height > screenBounds.height) {
771
height = screenBounds.height;
772
}
773
if (y + height > screenBounds.y + screenBounds.height) {
774
y = screenBounds.y + screenBounds.height - height;
775
}
776
return new Rectangle(x, y, width, height);
777
} else {
778
return null;
779
}
780
}
781
782
/**
783
* The last thing we can do with the window
784
* to fit it on screen - move it to the
785
* top-left edge and cut by screen dimensions
786
* @param windowSize size of submenu window to fit
787
* @param screenBounds size of screen
788
*/
789
Rectangle fitWindowToScreen(Dimension windowSize, Rectangle screenBounds) {
790
int width = (windowSize.width < screenBounds.width) ? windowSize.width : screenBounds.width;
791
int height = (windowSize.height < screenBounds.height) ? windowSize.height : screenBounds.height;
792
return new Rectangle(screenBounds.x, screenBounds.y, width, height);
793
}
794
795
796
/************************************************
797
*
798
* Utility functions for manipulating colors
799
*
800
************************************************/
801
802
/**
803
* This function is called before every painting.
804
* TODO:It would be better to add PropertyChangeListener
805
* to target component
806
* TODO:It would be better to access background color
807
* not invoking user-overridable function
808
*/
809
void resetColors() {
810
replaceColors((target == null) ? SystemColor.window : target.getBackground());
811
}
812
813
/**
814
* Calculates colors of various elements given
815
* background color. Uses MotifColorUtilities
816
* @param backgroundColor the color of menu window's
817
* background.
818
*/
819
void replaceColors(Color backgroundColor) {
820
if (backgroundColor != this.backgroundColor) {
821
this.backgroundColor = backgroundColor;
822
823
int red = backgroundColor.getRed();
824
int green = backgroundColor.getGreen();
825
int blue = backgroundColor.getBlue();
826
827
foregroundColor = new Color(MotifColorUtilities.calculateForegroundFromBackground(red,green,blue));
828
lightShadowColor = new Color(MotifColorUtilities.calculateTopShadowFromBackground(red,green,blue));
829
darkShadowColor = new Color(MotifColorUtilities.calculateBottomShadowFromBackground(red,green,blue));
830
selectedColor = new Color(MotifColorUtilities.calculateSelectFromBackground(red,green,blue));
831
disabledColor = (backgroundColor.equals(Color.BLACK)) ? foregroundColor.darker() : backgroundColor.darker();
832
}
833
}
834
835
Color getBackgroundColor() {
836
return backgroundColor;
837
}
838
839
Color getForegroundColor() {
840
return foregroundColor;
841
}
842
843
Color getLightShadowColor() {
844
return lightShadowColor;
845
}
846
847
Color getDarkShadowColor() {
848
return darkShadowColor;
849
}
850
851
Color getSelectedColor() {
852
return selectedColor;
853
}
854
855
Color getDisabledColor() {
856
return disabledColor;
857
}
858
859
/************************************************
860
*
861
* Painting utility functions
862
*
863
************************************************/
864
865
/**
866
* Draws raised or sunken rectangle on specified graphics
867
* @param g the graphics on which to draw
868
* @param x the coordinate of left edge in coordinates of graphics
869
* @param y the coordinate of top edge in coordinates of graphics
870
* @param width the width of rectangle
871
* @param height the height of rectangle
872
* @param raised true to draw raised rectangle, false to draw sunken
873
*/
874
void draw3DRect(Graphics g, int x, int y, int width, int height, boolean raised) {
875
if ((width <= 0) || (height <= 0)) {
876
return;
877
}
878
Color c = g.getColor();
879
g.setColor(raised ? getLightShadowColor() : getDarkShadowColor());
880
g.drawLine(x, y, x, y + height - 1);
881
g.drawLine(x + 1, y, x + width - 1, y);
882
g.setColor(raised ? getDarkShadowColor() : getLightShadowColor());
883
g.drawLine(x + 1, y + height - 1, x + width - 1, y + height - 1);
884
g.drawLine(x + width - 1, y + 1, x + width - 1, y + height - 1);
885
g.setColor(c);
886
}
887
888
/************************************************
889
*
890
* Overriden utility functions of XWindow
891
*
892
************************************************/
893
894
/**
895
* Filters X events
896
*/
897
protected boolean isEventDisabled(XEvent e) {
898
switch (e.get_type()) {
899
case XConstants.Expose :
900
case XConstants.GraphicsExpose :
901
case XConstants.ButtonPress:
902
case XConstants.ButtonRelease:
903
case XConstants.MotionNotify:
904
case XConstants.KeyPress:
905
case XConstants.KeyRelease:
906
case XConstants.DestroyNotify:
907
return super.isEventDisabled(e);
908
default:
909
return true;
910
}
911
}
912
913
/**
914
* Invokes disposal procedure on eventHandlerThread
915
*/
916
public void dispose() {
917
setDisposed(true);
918
919
SunToolkit.invokeLaterOnAppContext(disposeAppContext, new Runnable() {
920
public void run() {
921
doDispose();
922
}
923
});
924
}
925
926
/**
927
* Performs disposal of menu window.
928
* Should be called only on eventHandlerThread
929
*/
930
protected void doDispose() {
931
xSetVisible(false);
932
SurfaceData oldData = surfaceData;
933
surfaceData = null;
934
if (oldData != null) {
935
oldData.invalidate();
936
}
937
destroy();
938
}
939
940
/**
941
* Invokes event processing on eventHandlerThread
942
* This function needs to be overriden since
943
* XBaseMenuWindow has no corresponding component
944
* so events can not be processed using standart means
945
*/
946
void postEvent(final AWTEvent event) {
947
InvocationEvent ev = new InvocationEvent(event.getSource(), new Runnable() {
948
public void run() {
949
handleEvent(event);
950
}
951
});
952
super.postEvent(ev);
953
}
954
955
/**
956
* The implementation of base window performs processing
957
* of paint events only. This behaviour is changed in
958
* descendants.
959
*/
960
protected void handleEvent(AWTEvent event) {
961
switch(event.getID()) {
962
case PaintEvent.PAINT:
963
doHandleJavaPaintEvent((PaintEvent)event);
964
break;
965
}
966
}
967
968
/**
969
* Save location of pointer for further use
970
* then invoke superclass
971
*/
972
public boolean grabInput() {
973
int rootX;
974
int rootY;
975
boolean res;
976
XToolkit.awtLock();
977
try {
978
long root = XlibWrapper.RootWindow(XToolkit.getDisplay(),
979
getScreenNumber());
980
res = XlibWrapper.XQueryPointer(XToolkit.getDisplay(), root,
981
XlibWrapper.larg1, //root
982
XlibWrapper.larg2, //child
983
XlibWrapper.larg3, //root_x
984
XlibWrapper.larg4, //root_y
985
XlibWrapper.larg5, //child_x
986
XlibWrapper.larg6, //child_y
987
XlibWrapper.larg7);//mask
988
rootX = Native.getInt(XlibWrapper.larg3);
989
rootY = Native.getInt(XlibWrapper.larg4);
990
res &= super.grabInput();
991
} finally {
992
XToolkit.awtUnlock();
993
}
994
if (res) {
995
//Mouse pointer is on the same display
996
this.grabInputPoint = new Point(rootX, rootY);
997
this.hasPointerMoved = false;
998
} else {
999
this.grabInputPoint = null;
1000
this.hasPointerMoved = true;
1001
}
1002
return res;
1003
}
1004
/************************************************
1005
*
1006
* Overridable event processing functions
1007
*
1008
************************************************/
1009
1010
/**
1011
* Performs repainting
1012
*/
1013
void doHandleJavaPaintEvent(PaintEvent event) {
1014
Rectangle rect = event.getUpdateRect();
1015
repaint(rect.x, rect.y, rect.width, rect.height);
1016
}
1017
1018
/************************************************
1019
*
1020
* User input handling utility functions
1021
*
1022
************************************************/
1023
1024
/**
1025
* Performs handling of java mouse event
1026
* Note that this function should be invoked
1027
* only from root of menu window's hierarchy
1028
* that grabs input focus
1029
*/
1030
void doHandleJavaMouseEvent( MouseEvent mouseEvent ) {
1031
if (!XToolkit.isLeftMouseButton(mouseEvent) && !XToolkit.isRightMouseButton(mouseEvent)) {
1032
return;
1033
}
1034
//Window that owns input
1035
XBaseWindow grabWindow = XAwtState.getGrabWindow();
1036
//Point of mouse event in global coordinates
1037
Point ptGlobal = mouseEvent.getLocationOnScreen();
1038
if (!hasPointerMoved) {
1039
//Fix for 6301307: NullPointerException while dispatching mouse events, XToolkit
1040
if (grabInputPoint == null ||
1041
(Math.abs(ptGlobal.x - grabInputPoint.x) > getMouseMovementSmudge()) ||
1042
(Math.abs(ptGlobal.y - grabInputPoint.y) > getMouseMovementSmudge())) {
1043
hasPointerMoved = true;
1044
}
1045
}
1046
//Z-order first descendant of current menu window
1047
//hierarchy that contain mouse point
1048
XBaseMenuWindow wnd = getMenuWindowFromPoint(ptGlobal);
1049
//Item in wnd that contains mouse point, if any
1050
XMenuItemPeer item = (wnd != null) ? wnd.getItemFromPoint(wnd.toLocal(ptGlobal)) : null;
1051
//Currently showing leaf window
1052
XBaseMenuWindow cwnd = getShowingLeaf();
1053
switch (mouseEvent.getID()) {
1054
case MouseEvent.MOUSE_PRESSED:
1055
//This line is to get rid of possible problems
1056
//That may occur if mouse events are lost
1057
showingMousePressedSubmenu = null;
1058
if ((grabWindow == this) && (wnd == null)) {
1059
//Menus grab input and the user
1060
//presses mouse button outside
1061
ungrabInput();
1062
} else {
1063
//Menus grab input OR mouse is pressed on menu window
1064
grabInput();
1065
if (item != null && !item.isSeparator() && item.isTargetItemEnabled()) {
1066
//Button is pressed on enabled item
1067
if (wnd.getShowingSubmenu() == item) {
1068
//Button is pressed on item that shows
1069
//submenu. We have to hide its submenu
1070
//if user clicks on it
1071
showingMousePressedSubmenu = (XMenuPeer)item;
1072
}
1073
wnd.selectItem(item, true);
1074
} else {
1075
//Button is pressed on disabled item or empty space
1076
if (wnd != null) {
1077
wnd.selectItem(null, false);
1078
}
1079
}
1080
}
1081
break;
1082
case MouseEvent.MOUSE_RELEASED:
1083
//Note that if item is not null, wnd has to be not null
1084
if (item != null && !item.isSeparator() && item.isTargetItemEnabled()) {
1085
if (item instanceof XMenuPeer) {
1086
if (showingMousePressedSubmenu == item) {
1087
//User clicks on item that shows submenu.
1088
//Hide the submenu
1089
if (wnd instanceof XMenuBarPeer) {
1090
ungrabInput();
1091
} else {
1092
wnd.selectItem(item, false);
1093
}
1094
}
1095
} else {
1096
//Invoke action event
1097
@SuppressWarnings("deprecation")
1098
final int modifiers = mouseEvent.getModifiers();
1099
item.action(mouseEvent.getWhen(), modifiers);
1100
ungrabInput();
1101
}
1102
} else {
1103
//Mouse is released outside menu items
1104
if (hasPointerMoved || (wnd instanceof XMenuBarPeer)) {
1105
ungrabInput();
1106
}
1107
}
1108
showingMousePressedSubmenu = null;
1109
break;
1110
case MouseEvent.MOUSE_DRAGGED:
1111
if (wnd != null) {
1112
//Mouse is dragged over menu window
1113
//Move selection to item under cursor
1114
if (item != null && !item.isSeparator() && item.isTargetItemEnabled()) {
1115
if (grabWindow == this){
1116
wnd.selectItem(item, true);
1117
}
1118
} else {
1119
wnd.selectItem(null, false);
1120
}
1121
} else {
1122
//Mouse is dragged outside menu windows
1123
//clear selection in leaf to reflect it
1124
if (cwnd != null) {
1125
cwnd.selectItem(null, false);
1126
}
1127
}
1128
break;
1129
}
1130
}
1131
1132
/**
1133
* Performs handling of java keyboard event
1134
* Note that this function should be invoked
1135
* only from root of menu window's hierarchy
1136
* that grabs input focus
1137
*/
1138
void doHandleJavaKeyEvent(KeyEvent event) {
1139
if (log.isLoggable(PlatformLogger.Level.FINER)) {
1140
log.finer(event.toString());
1141
}
1142
if (event.getID() != KeyEvent.KEY_PRESSED) {
1143
return;
1144
}
1145
final int keyCode = event.getKeyCode();
1146
XBaseMenuWindow cwnd = getShowingLeaf();
1147
XMenuItemPeer citem = cwnd.getSelectedItem();
1148
switch(keyCode) {
1149
case KeyEvent.VK_UP:
1150
case KeyEvent.VK_KP_UP:
1151
if (!(cwnd instanceof XMenuBarPeer)) {
1152
//If active window is not menu bar,
1153
//move selection up
1154
cwnd.selectItem(cwnd.getPrevSelectableItem(), false);
1155
}
1156
break;
1157
case KeyEvent.VK_DOWN:
1158
case KeyEvent.VK_KP_DOWN:
1159
if (cwnd instanceof XMenuBarPeer) {
1160
//If active window is menu bar show current submenu
1161
selectItem(getSelectedItem(), true);
1162
} else {
1163
//move selection down
1164
cwnd.selectItem(cwnd.getNextSelectableItem(), false);
1165
}
1166
break;
1167
case KeyEvent.VK_LEFT:
1168
case KeyEvent.VK_KP_LEFT:
1169
if (cwnd instanceof XMenuBarPeer) {
1170
//leaf window is menu bar
1171
//select previous item
1172
selectItem(getPrevSelectableItem(), false);
1173
} else if (cwnd.getParentMenuWindow() instanceof XMenuBarPeer) {
1174
//leaf window is direct child of menu bar
1175
//select previous item of menu bar
1176
//and show its submenu
1177
selectItem(getPrevSelectableItem(), true);
1178
} else {
1179
//hide leaf moving focus to its parent
1180
//(equvivalent of pressing ESC)
1181
XBaseMenuWindow pwnd = cwnd.getParentMenuWindow();
1182
//Fix for 6272952: PIT: Pressing LEFT ARROW on a popup menu throws NullPointerException, XToolkit
1183
if (pwnd != null) {
1184
pwnd.selectItem(pwnd.getSelectedItem(), false);
1185
}
1186
}
1187
break;
1188
case KeyEvent.VK_RIGHT:
1189
case KeyEvent.VK_KP_RIGHT:
1190
if (cwnd instanceof XMenuBarPeer) {
1191
//leaf window is menu bar
1192
//select next item
1193
selectItem(getNextSelectableItem(), false);
1194
} else if (citem instanceof XMenuPeer) {
1195
//current item is menu, show its window
1196
//(equivalent of ENTER)
1197
cwnd.selectItem(citem, true);
1198
} else if (this instanceof XMenuBarPeer) {
1199
//if this is menu bar (not popup menu)
1200
//and the user presses RIGHT on item (not submenu)
1201
//select next top-level menu
1202
selectItem(getNextSelectableItem(), true);
1203
}
1204
break;
1205
case KeyEvent.VK_SPACE:
1206
case KeyEvent.VK_ENTER:
1207
//If the current item has submenu show it
1208
//Perform action otherwise
1209
if (citem instanceof XMenuPeer) {
1210
cwnd.selectItem(citem, true);
1211
} else if (citem != null) {
1212
@SuppressWarnings("deprecation")
1213
final int modifiers = event.getModifiers();
1214
citem.action(event.getWhen(), modifiers);
1215
ungrabInput();
1216
}
1217
break;
1218
case KeyEvent.VK_ESCAPE:
1219
//If current window is menu bar or its child - close it
1220
//If current window is popup menu - close it
1221
//go one level up otherwise
1222
1223
//Fixed 6266513: Incorrect key handling in XAWT popup menu
1224
//Popup menu should be closed on 'ESC'
1225
if ((cwnd instanceof XMenuBarPeer) || (cwnd.getParentMenuWindow() instanceof XMenuBarPeer)) {
1226
ungrabInput();
1227
} else if (cwnd instanceof XPopupMenuPeer) {
1228
ungrabInput();
1229
} else {
1230
XBaseMenuWindow pwnd = cwnd.getParentMenuWindow();
1231
pwnd.selectItem(pwnd.getSelectedItem(), false);
1232
}
1233
break;
1234
case KeyEvent.VK_F10:
1235
//Fixed 6266513: Incorrect key handling in XAWT popup menu
1236
//All menus should be closed on 'F10'
1237
ungrabInput();
1238
break;
1239
default:
1240
break;
1241
}
1242
}
1243
1244
} //class XBaseMenuWindow
1245
1246