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