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/XChoicePeer.java
32288 views
1
/*
2
* Copyright (c) 2003, 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.awt.X11;
27
28
import java.awt.*;
29
import java.awt.peer.*;
30
import java.awt.event.*;
31
import sun.util.logging.PlatformLogger;
32
33
// FIXME: tab traversal should be disabled when mouse is captured (4816336)
34
35
// FIXME: key and mouse events should not be delivered to listeners when the Choice is unfurled. Must override handleNativeKey/MouseEvent (4816336)
36
37
// FIXME: test programmatic add/remove/clear/etc
38
39
// FIXME: account for unfurling at the edge of the screen
40
// Note: can't set x,y on layout(), 'cause moving the top-level to the
41
// edge of the screen won't call layout(). Just do it on paint, I guess
42
43
// TODO: make painting more efficient (i.e. when down arrow is pressed, only two items should need to be repainted.
44
45
public class XChoicePeer extends XComponentPeer implements ChoicePeer, ToplevelStateListener {
46
private static final PlatformLogger log = PlatformLogger.getLogger("sun.awt.X11.XChoicePeer");
47
48
private static final int MAX_UNFURLED_ITEMS = 10; // Maximum number of
49
// items to be displayed
50
// at a time in an
51
// unfurled Choice
52
// Description of these constants in ListHelper
53
public final static int TEXT_SPACE = 1;
54
public final static int BORDER_WIDTH = 1;
55
public final static int ITEM_MARGIN = 1;
56
public final static int SCROLLBAR_WIDTH = 15;
57
58
59
// SHARE THESE!
60
private static final Insets focusInsets = new Insets(0,0,0,0);
61
62
63
static final int WIDGET_OFFSET = 18;
64
65
// Stolen from Tiny
66
static final int TEXT_XPAD = 8;
67
static final int TEXT_YPAD = 6;
68
69
// FIXME: Motif uses a different focus color for the item within
70
// the unfurled Choice list and for when the Choice itself is focused and
71
// popped up.
72
static final Color focusColor = Color.black;
73
74
// TODO: there is a time value that the mouse is held down. If short
75
// enough, the Choice stays popped down. If long enough, Choice
76
// is furled when the mouse is released
77
78
private boolean unfurled = false; // Choice list is popped down
79
80
private boolean dragging = false; // Mouse was pressed and is being
81
// dragged over the (unfurled)
82
// Choice
83
84
private boolean mouseInSB = false; // Mouse is interacting with the
85
// scrollbar
86
87
private boolean firstPress = false; // mouse was pressed on
88
// furled Choice so we
89
// not need to furl the
90
// Choice when MOUSE_RELEASED occurred
91
92
// 6425067. Mouse was pressed on furled choice and dropdown list appeared over Choice itself
93
// and then there were no mouse movements until MOUSE_RELEASE.
94
// This scenario leads to ItemStateChanged as the choice logic uses
95
// MouseReleased event to send ItemStateChanged. To prevent it we should
96
// use a combination of firstPress and wasDragged variables.
97
// The only difference in dragging and wasDragged is: last one will not
98
// set to false on mouse ungrab. It become false after MouseRelased() finishes.
99
private boolean wasDragged = false;
100
private ListHelper helper;
101
private UnfurledChoice unfurledChoice;
102
103
// TODO: Choice remembers where it was scrolled to when unfurled - it's not
104
// always to the currently selected item.
105
106
// Indicates whether or not to paint selected item in the choice.
107
// Default is to paint
108
private boolean drawSelectedItem = true;
109
110
// If set, indicates components under which choice popup should be showed.
111
// The choice's popup width and location should be adjust to appear
112
// under both choice and alignUnder component.
113
private Component alignUnder;
114
115
// If cursor is outside of an unfurled Choice when the mouse is
116
// released, Choice item should NOT be updated. Remember the proper index.
117
private int dragStartIdx = -1;
118
119
// Holds the listener (XFileDialogPeer) which the processing events from the choice
120
// See 6240074 for more information
121
private XChoicePeerListener choiceListener;
122
123
XChoicePeer(Choice target) {
124
super(target);
125
}
126
127
void preInit(XCreateWindowParams params) {
128
super.preInit(params);
129
Choice target = (Choice)this.target;
130
int numItems = target.getItemCount();
131
unfurledChoice = new UnfurledChoice(target);
132
getToplevelXWindow().addToplevelStateListener(this);
133
helper = new ListHelper(unfurledChoice,
134
getGUIcolors(),
135
numItems,
136
false,
137
true,
138
false,
139
target.getFont(),
140
MAX_UNFURLED_ITEMS,
141
TEXT_SPACE,
142
ITEM_MARGIN,
143
BORDER_WIDTH,
144
SCROLLBAR_WIDTH);
145
}
146
147
void postInit(XCreateWindowParams params) {
148
super.postInit(params);
149
Choice target = (Choice)this.target;
150
int numItems = target.getItemCount();
151
152
// Add all items
153
for (int i = 0; i < numItems; i++) {
154
helper.add(target.getItem(i));
155
}
156
if (!helper.isEmpty()) {
157
helper.select(target.getSelectedIndex());
158
helper.setFocusedIndex(target.getSelectedIndex());
159
}
160
helper.updateColors(getGUIcolors());
161
updateMotifColors(getPeerBackground());
162
}
163
164
public boolean isFocusable() { return true; }
165
166
// 6399679. check if super.setBounds() actually changes the size of the
167
// component and then compare current Choice size with a new one. If
168
// they differs then hide dropdown menu
169
public void setBounds(int x, int y, int width, int height, int op) {
170
int oldX = this.x;
171
int oldY = this.y;
172
int oldWidth = this.width;
173
int oldHeight = this.height;
174
super.setBounds(x, y, width, height, op);
175
if (unfurled && (oldX != this.x || oldY != this.y || oldWidth != this.width || oldHeight != this.height) ) {
176
hidePopdownMenu();
177
}
178
}
179
180
public void focusGained(FocusEvent e) {
181
// TODO: only need to paint the focus bit
182
super.focusGained(e);
183
repaint();
184
}
185
186
/*
187
* Fix for 6246503 : Disabling a choice after selection locks keyboard, mouse and makes the system unusable, Xtoolkit
188
* if setEnabled(false) invoked we should close opened choice in
189
* order to prevent keyboard/mouse lock.
190
*/
191
public void setEnabled(boolean value) {
192
super.setEnabled(value);
193
helper.updateColors(getGUIcolors());
194
if (!value && unfurled){
195
hidePopdownMenu();
196
}
197
}
198
199
public void focusLost(FocusEvent e) {
200
// TODO: only need to paint the focus bit?
201
super.focusLost(e);
202
repaint();
203
}
204
205
void ungrabInputImpl() {
206
if (unfurled) {
207
unfurled = false;
208
dragging = false;
209
mouseInSB = false;
210
unfurledChoice.setVisible(false);
211
}
212
213
super.ungrabInputImpl();
214
}
215
216
void handleJavaKeyEvent(KeyEvent e) {
217
if (e.getID() == KeyEvent.KEY_PRESSED) {
218
keyPressed(e);
219
}
220
}
221
222
public void keyPressed(KeyEvent e) {
223
switch(e.getKeyCode()) {
224
// UP & DOWN are same if furled or unfurled
225
case KeyEvent.VK_DOWN:
226
case KeyEvent.VK_KP_DOWN: {
227
if (helper.getItemCount() > 1) {
228
helper.down();
229
int newIdx = helper.getSelectedIndex();
230
231
((Choice)target).select(newIdx);
232
postEvent(new ItemEvent((Choice)target,
233
ItemEvent.ITEM_STATE_CHANGED,
234
((Choice)target).getItem(newIdx),
235
ItemEvent.SELECTED));
236
repaint();
237
}
238
break;
239
}
240
case KeyEvent.VK_UP:
241
case KeyEvent.VK_KP_UP: {
242
if (helper.getItemCount() > 1) {
243
helper.up();
244
int newIdx = helper.getSelectedIndex();
245
246
((Choice)target).select(newIdx);
247
postEvent(new ItemEvent((Choice)target,
248
ItemEvent.ITEM_STATE_CHANGED,
249
((Choice)target).getItem(newIdx),
250
ItemEvent.SELECTED));
251
repaint();
252
}
253
break;
254
}
255
case KeyEvent.VK_PAGE_DOWN:
256
if (unfurled && !dragging) {
257
int oldIdx = helper.getSelectedIndex();
258
helper.pageDown();
259
int newIdx = helper.getSelectedIndex();
260
if (oldIdx != newIdx) {
261
((Choice)target).select(newIdx);
262
postEvent(new ItemEvent((Choice)target,
263
ItemEvent.ITEM_STATE_CHANGED,
264
((Choice)target).getItem(newIdx),
265
ItemEvent.SELECTED));
266
repaint();
267
}
268
}
269
break;
270
case KeyEvent.VK_PAGE_UP:
271
if (unfurled && !dragging) {
272
int oldIdx = helper.getSelectedIndex();
273
helper.pageUp();
274
int newIdx = helper.getSelectedIndex();
275
if (oldIdx != newIdx) {
276
((Choice)target).select(newIdx);
277
postEvent(new ItemEvent((Choice)target,
278
ItemEvent.ITEM_STATE_CHANGED,
279
((Choice)target).getItem(newIdx),
280
ItemEvent.SELECTED));
281
repaint();
282
}
283
}
284
break;
285
case KeyEvent.VK_ESCAPE:
286
case KeyEvent.VK_ENTER:
287
if (unfurled) {
288
if (dragging){
289
if (e.getKeyCode() == KeyEvent.VK_ESCAPE){
290
//This also happens on
291
// - MouseButton2,3, etc. press
292
// - ENTER press
293
helper.select(dragStartIdx);
294
} else { //KeyEvent.VK_ENTER:
295
int newIdx = helper.getSelectedIndex();
296
((Choice)target).select(newIdx);
297
postEvent(new ItemEvent((Choice)target,
298
ItemEvent.ITEM_STATE_CHANGED,
299
((Choice)target).getItem(newIdx),
300
ItemEvent.SELECTED));
301
}
302
}
303
hidePopdownMenu();
304
dragging = false;
305
wasDragged = false;
306
mouseInSB = false;
307
308
// See 6240074 for more information
309
if (choiceListener != null){
310
choiceListener.unfurledChoiceClosing();
311
}
312
}
313
break;
314
default:
315
if (unfurled) {
316
Toolkit.getDefaultToolkit().beep();
317
}
318
break;
319
}
320
}
321
322
public boolean handlesWheelScrolling() { return true; }
323
324
void handleJavaMouseWheelEvent(MouseWheelEvent e) {
325
if (unfurled && helper.isVSBVisible()) {
326
if (ListHelper.doWheelScroll(helper.getVSB(), null, e)) {
327
repaint();
328
}
329
}
330
}
331
332
void handleJavaMouseEvent(MouseEvent e) {
333
super.handleJavaMouseEvent(e);
334
int i = e.getID();
335
switch (i) {
336
case MouseEvent.MOUSE_PRESSED:
337
mousePressed(e);
338
break;
339
case MouseEvent.MOUSE_RELEASED:
340
mouseReleased(e);
341
break;
342
case MouseEvent.MOUSE_DRAGGED:
343
mouseDragged(e);
344
break;
345
}
346
}
347
348
public void mousePressed(MouseEvent e) {
349
/*
350
* fix for 5003166: a Choice on XAWT shouldn't react to any
351
* mouse button presses except left. This involves presses on
352
* Choice but not on opened part of choice.
353
*/
354
if (e.getButton() == MouseEvent.BUTTON1){
355
dragStartIdx = helper.getSelectedIndex();
356
if (unfurled) {
357
//fix 6259328: PIT: Choice scrolls when dragging the parent frame while drop-down is active, XToolkit
358
if (! (isMouseEventInChoice(e) ||
359
unfurledChoice.isMouseEventInside(e)))
360
{
361
hidePopdownMenu();
362
}
363
// Press on unfurled Choice. Highlight the item under the cursor,
364
// but don't send item event or set the text on the button yet
365
unfurledChoice.trackMouse(e);
366
}
367
else {
368
// Choice is up - unfurl it
369
grabInput();
370
unfurledChoice.toFront();
371
firstPress = true;
372
wasDragged = false;
373
unfurled = true;
374
}
375
}
376
}
377
378
/*
379
* helper method for mouseReleased routine
380
*/
381
void hidePopdownMenu(){
382
ungrabInput();
383
unfurledChoice.setVisible(false);
384
unfurled = false;
385
}
386
387
public void mouseReleased(MouseEvent e) {
388
if (unfurled) {
389
if (mouseInSB) {
390
unfurledChoice.trackMouse(e);
391
}
392
else {
393
// We pressed and dragged onto the Choice, or, this is the
394
// second release after clicking to make the Choice "stick"
395
// unfurled.
396
// This release should ungrab/furl, and set the new item if
397
// release was over the unfurled Choice.
398
399
// Fix for 6239944 : Choice shouldn't close its
400
// pop-down menu if user presses Mouse on Choice's Scrollbar
401
// some additional cases like releasing mouse outside
402
// of Choice are considered too
403
boolean isMouseEventInside = unfurledChoice.isMouseEventInside( e );
404
boolean isMouseInListArea = unfurledChoice.isMouseInListArea( e );
405
406
// Fixed 6318746: REG: File Selection is failing
407
// We shouldn't restore the selected item
408
// if the mouse was dragged outside the drop-down choice area
409
if (!helper.isEmpty() && !isMouseInListArea && dragging) {
410
// Set the selected item back how it was.
411
((Choice)target).select(dragStartIdx);
412
}
413
414
// Choice must be closed if user releases mouse on
415
// pop-down menu on the second click
416
if ( !firstPress && isMouseInListArea) {
417
hidePopdownMenu();
418
}
419
// Choice must be closed if user releases mouse
420
// outside of Choice's pop-down menu on the second click
421
if ( !firstPress && !isMouseEventInside) {
422
hidePopdownMenu();
423
}
424
//if user drags Mouse on pop-down menu, Scrollbar or
425
// outside the Choice
426
if ( firstPress && dragging) {
427
hidePopdownMenu();
428
}
429
/* this could happen when user has opened a Choice and
430
* released mouse button. Then he drags mouse on the
431
* Scrollbar and releases mouse again.
432
*/
433
if ( !firstPress && !isMouseInListArea &&
434
isMouseEventInside && dragging)
435
{
436
hidePopdownMenu();
437
}
438
439
if (!helper.isEmpty()) {
440
// Only update the Choice if the mouse button is released
441
// over the list of items.
442
if (unfurledChoice.isMouseInListArea(e)) {
443
int newIdx = helper.getSelectedIndex();
444
if (newIdx >= 0) {
445
// Update the selected item in the target now that
446
// the mouse selection is complete.
447
if (newIdx != dragStartIdx) {
448
((Choice)target).select(newIdx);
449
// NOTE: We get a repaint when Choice.select()
450
// calls our peer.select().
451
}
452
if (wasDragged && e.getButton() != MouseEvent.BUTTON1){
453
((Choice)target).select(dragStartIdx);
454
}
455
456
/*fix for 6239941 : Choice triggers ItemEvent when selecting an item with right mouse button, Xtoolkit
457
* We should generate ItemEvent if only
458
* LeftMouseButton used */
459
if (e.getButton() == MouseEvent.BUTTON1 &&
460
(!firstPress || wasDragged ))
461
{
462
postEvent(new ItemEvent((Choice)target,
463
ItemEvent.ITEM_STATE_CHANGED,
464
((Choice)target).getItem(newIdx),
465
ItemEvent.SELECTED));
466
}
467
468
// see 6240074 for more information
469
if (choiceListener != null) {
470
choiceListener.unfurledChoiceClosing();
471
}
472
}
473
}
474
}
475
// See 6243382 for more information
476
unfurledChoice.trackMouse(e);
477
}
478
}
479
480
dragging = false;
481
wasDragged = false;
482
firstPress = false;
483
dragStartIdx = -1;
484
}
485
486
public void mouseDragged(MouseEvent e) {
487
/*
488
* fix for 5003166. On Motif user are unable to drag
489
* mouse inside opened Choice if he drags the mouse with
490
* different from LEFT mouse button ( e.g. RIGHT or MIDDLE).
491
* This fix make impossible to drag mouse inside opened choice
492
* with other mouse buttons rather then LEFT one.
493
*/
494
if ( e.getModifiers() == MouseEvent.BUTTON1_MASK ){
495
dragging = true;
496
wasDragged = true;
497
unfurledChoice.trackMouse(e);
498
}
499
}
500
501
// Stolen from TinyChoicePeer
502
public Dimension getMinimumSize() {
503
// TODO: move this impl into ListHelper?
504
FontMetrics fm = getFontMetrics(target.getFont());
505
Choice c = (Choice)target;
506
int w = 0;
507
for (int i = c.countItems() ; i-- > 0 ;) {
508
w = Math.max(fm.stringWidth(c.getItem(i)), w);
509
}
510
return new Dimension(w + TEXT_XPAD + WIDGET_OFFSET,
511
fm.getMaxAscent() + fm.getMaxDescent() + TEXT_YPAD);
512
}
513
514
/*
515
* Layout the...
516
*/
517
public void layout() {
518
/*
519
Dimension size = target.getSize();
520
Font f = target.getFont();
521
FontMetrics fm = target.getFontMetrics(f);
522
String text = ((Choice)target).getLabel();
523
524
textRect.height = fm.getHeight();
525
526
checkBoxSize = getChoiceSize(fm);
527
528
// Note - Motif appears to use an left inset that is slightly
529
// scaled to the checkbox/font size.
530
cbX = borderInsets.left + checkBoxInsetFromText;
531
cbY = size.height / 2 - checkBoxSize / 2;
532
int minTextX = borderInsets.left + 2 * checkBoxInsetFromText + checkBoxSize;
533
// FIXME: will need to account for alignment?
534
// FIXME: call layout() on alignment changes
535
//textRect.width = fm.stringWidth(text);
536
textRect.width = fm.stringWidth(text == null ? "" : text);
537
textRect.x = Math.max(minTextX, size.width / 2 - textRect.width / 2);
538
textRect.y = size.height / 2 - textRect.height / 2 + borderInsets.top;
539
540
focusRect.x = focusInsets.left;
541
focusRect.y = focusInsets.top;
542
focusRect.width = size.width-(focusInsets.left+focusInsets.right)-1;
543
focusRect.height = size.height-(focusInsets.top+focusInsets.bottom)-1;
544
545
myCheckMark = AffineTransform.getScaleInstance((double)target.getFont().getSize() / MASTER_SIZE, (double)target.getFont().getSize() / MASTER_SIZE).createTransformedShape(MASTER_CHECKMARK);
546
*/
547
548
}
549
550
/**
551
* Paint the choice
552
*/
553
@Override
554
void paintPeer(final Graphics g) {
555
flush();
556
Dimension size = getPeerSize();
557
// TODO: when mouse is down over button, widget should be drawn depressed
558
g.setColor(getPeerBackground());
559
g.fillRect(0, 0, width, height);
560
561
drawMotif3DRect(g, 1, 1, width-2, height-2, false);
562
drawMotif3DRect(g, width - WIDGET_OFFSET, (height / 2) - 3, 12, 6, false);
563
564
if (!helper.isEmpty() && helper.getSelectedIndex() != -1) {
565
g.setFont(getPeerFont());
566
FontMetrics fm = g.getFontMetrics();
567
String lbl = helper.getItem(helper.getSelectedIndex());
568
if (lbl != null && drawSelectedItem) {
569
g.setClip(1, 1, width - WIDGET_OFFSET - 2, height);
570
if (isEnabled()) {
571
g.setColor(getPeerForeground());
572
g.drawString(lbl, 5, (height + fm.getMaxAscent()-fm.getMaxDescent())/2);
573
}
574
else {
575
g.setColor(getPeerBackground().brighter());
576
g.drawString(lbl, 5, (height + fm.getMaxAscent()-fm.getMaxDescent())/2);
577
g.setColor(getPeerBackground().darker());
578
g.drawString(lbl, 4, ((height + fm.getMaxAscent()-fm.getMaxDescent())/2)-1);
579
}
580
g.setClip(0, 0, width, height);
581
}
582
}
583
if (hasFocus()) {
584
paintFocus(g,focusInsets.left,focusInsets.top,size.width-(focusInsets.left+focusInsets.right)-1,size.height-(focusInsets.top+focusInsets.bottom)-1);
585
}
586
if (unfurled) {
587
unfurledChoice.repaint();
588
}
589
flush();
590
}
591
592
protected void paintFocus(Graphics g,
593
int x, int y, int w, int h) {
594
g.setColor(focusColor);
595
g.drawRect(x,y,w,h);
596
}
597
598
599
600
/*
601
* ChoicePeer methods stolen from TinyChoicePeer
602
*/
603
604
public void select(int index) {
605
helper.select(index);
606
helper.setFocusedIndex(index);
607
repaint();
608
}
609
610
public void add(String item, int index) {
611
helper.add(item, index);
612
repaint();
613
}
614
615
public void remove(int index) {
616
boolean selected = (index == helper.getSelectedIndex());
617
boolean visibled = (index >= helper.firstDisplayedIndex() && index <= helper.lastDisplayedIndex());
618
helper.remove(index);
619
if (selected) {
620
if (helper.isEmpty()) {
621
helper.select(-1);
622
}
623
else {
624
helper.select(0);
625
}
626
}
627
/*
628
* Fix for 6248016
629
* After removing the item of the choice we need to reshape unfurled choice
630
* in order to keep actual bounds of the choice
631
*/
632
633
/*
634
* condition added only for performance
635
*/
636
if (!unfurled) {
637
// Fix 6292186: PIT: Choice is not refreshed properly when the last item gets removed, XToolkit
638
// We should take into account that there is no 'select' invoking (hence 'repaint')
639
// if the choice is empty (see Choice.java method removeNoInvalidate())
640
// The condition isn't 'visibled' since it would be cause of the twice repainting
641
if (helper.isEmpty()) {
642
repaint();
643
}
644
return;
645
}
646
647
/*
648
* condition added only for performance
649
* the count of the visible items changed
650
*/
651
if (visibled){
652
Rectangle r = unfurledChoice.placeOnScreen();
653
unfurledChoice.reshape(r.x, r.y, r.width, r.height);
654
return;
655
}
656
657
/*
658
* condition added only for performance
659
* the structure of visible items changed
660
* if removable item is non visible and non selected then there is no repaint
661
*/
662
if (visibled || selected){
663
repaint();
664
}
665
}
666
667
public void removeAll() {
668
helper.removeAll();
669
helper.select(-1);
670
/*
671
* Fix for 6248016
672
* After removing the item of the choice we need to reshape unfurled choice
673
* in order to keep actual bounds of the choice
674
*/
675
Rectangle r = unfurledChoice.placeOnScreen();
676
unfurledChoice.reshape(r.x, r.y, r.width, r.height);
677
repaint();
678
}
679
680
/**
681
* DEPRECATED: Replaced by add(String, int).
682
*/
683
public void addItem(String item, int index) {
684
add(item, index);
685
}
686
687
public void setFont(Font font) {
688
super.setFont(font);
689
helper.setFont(this.font);
690
}
691
692
public void setForeground(Color c) {
693
super.setForeground(c);
694
helper.updateColors(getGUIcolors());
695
}
696
697
public void setBackground(Color c) {
698
super.setBackground(c);
699
unfurledChoice.setBackground(c);
700
helper.updateColors(getGUIcolors());
701
updateMotifColors(c);
702
}
703
704
public void setDrawSelectedItem(boolean value) {
705
drawSelectedItem = value;
706
}
707
708
public void setAlignUnder(Component comp) {
709
alignUnder = comp;
710
}
711
712
// see 6240074 for more information
713
public void addXChoicePeerListener(XChoicePeerListener l){
714
choiceListener = l;
715
}
716
717
// see 6240074 for more information
718
public void removeXChoicePeerListener(){
719
choiceListener = null;
720
}
721
722
public boolean isUnfurled(){
723
return unfurled;
724
}
725
726
/* fix for 6261352. We should detect if current parent Window (containing a Choice) become iconified and hide pop-down menu with grab release.
727
* In this case we should hide pop-down menu.
728
*/
729
//calls from XWindowPeer. Could accept X-styled state events
730
public void stateChangedICCCM(int oldState, int newState) {
731
if (unfurled && oldState != newState){
732
hidePopdownMenu();
733
}
734
}
735
736
//calls from XFramePeer. Could accept Frame's states.
737
public void stateChangedJava(int oldState, int newState) {
738
if (unfurled && oldState != newState){
739
hidePopdownMenu();
740
}
741
}
742
743
/**************************************************************************/
744
/* Common functionality between List & Choice
745
/**************************************************************************/
746
747
/**
748
* Inner class for the unfurled Choice list
749
* Much, much more docs
750
*/
751
class UnfurledChoice extends XWindow /*implements XScrollbarClient*/ {
752
753
// First try - use Choice as the target
754
755
public UnfurledChoice(Component target) {
756
super(target);
757
}
758
759
// Override so we can do our own create()
760
public void preInit(XCreateWindowParams params) {
761
// A parent of this window is the target, at this point: wrong.
762
// Remove parent window; in the following preInit() call we'll calculate as a default
763
// a correct root window which is the proper parent for override redirect.
764
params.delete(PARENT_WINDOW);
765
super.preInit(params);
766
// Reset bounds(we'll set them later), set overrideRedirect
767
params.remove(BOUNDS);
768
params.add(OVERRIDE_REDIRECT, Boolean.TRUE);
769
}
770
771
// Generally, bounds should be:
772
// x = target.x
773
// y = target.y + target.height
774
// w = Max(target.width, getLongestItemWidth) + possible vertScrollbar
775
// h = Min(MAX_UNFURLED_ITEMS, target.getItemCount()) * itemHeight
776
Rectangle placeOnScreen() {
777
int numItemsDisplayed;
778
// Motif paints an empty Choice the same size as a single item
779
if (helper.isEmpty()) {
780
numItemsDisplayed = 1;
781
}
782
else {
783
int numItems = helper.getItemCount();
784
numItemsDisplayed = Math.min(MAX_UNFURLED_ITEMS, numItems);
785
}
786
Point global = XChoicePeer.this.toGlobal(0,0);
787
Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
788
789
if (alignUnder != null) {
790
Rectangle choiceRec = XChoicePeer.this.getBounds();
791
choiceRec.setLocation(0, 0);
792
choiceRec = XChoicePeer.this.toGlobal(choiceRec);
793
Rectangle alignUnderRec = new Rectangle(alignUnder.getLocationOnScreen(), alignUnder.getSize()); // TODO: Security?
794
Rectangle result = choiceRec.union(alignUnderRec);
795
// we've got the left and width, calculate top and height
796
width = result.width;
797
x = result.x;
798
y = result.y + result.height;
799
height = 2*BORDER_WIDTH +
800
numItemsDisplayed*(helper.getItemHeight()+2*ITEM_MARGIN);
801
} else {
802
x = global.x;
803
y = global.y + XChoicePeer.this.height;
804
width = Math.max(XChoicePeer.this.width,
805
helper.getMaxItemWidth() + 2 * (BORDER_WIDTH + ITEM_MARGIN + TEXT_SPACE) + (helper.isVSBVisible() ? SCROLLBAR_WIDTH : 0));
806
height = 2*BORDER_WIDTH +
807
numItemsDisplayed*(helper.getItemHeight()+2*ITEM_MARGIN);
808
}
809
// Don't run off the edge of the screen
810
if (x < 0) {
811
x = 0;
812
}
813
else if (x + width > screen.width) {
814
x = screen.width - width;
815
}
816
817
if (y + height > screen.height) {
818
y = global.y - height;
819
}
820
if (y < 0) {
821
y = 0;
822
}
823
return new Rectangle(x, y, width, height);
824
}
825
826
public void toFront() {
827
// see 6240074 for more information
828
if (choiceListener != null)
829
choiceListener.unfurledChoiceOpening(helper);
830
831
Rectangle r = placeOnScreen();
832
reshape(r.x, r.y, r.width, r.height);
833
super.toFront();
834
setVisible(true);
835
}
836
837
/*
838
* Track a MouseEvent (either a drag or a press) and paint a new
839
* selected item, if necessary.
840
*/
841
// FIXME: first unfurl after move is not at edge of the screen onto second monitor doesn't
842
// track mouse correctly. Problem is w/ UnfurledChoice coords
843
public void trackMouse(MouseEvent e) {
844
// Event coords are relative to the button, so translate a bit
845
Point local = toLocalCoords(e);
846
847
// If x,y is over unfurled Choice,
848
// highlight item under cursor
849
850
switch (e.getID()) {
851
case MouseEvent.MOUSE_PRESSED:
852
// FIXME: If the Choice is unfurled and the mouse is pressed
853
// outside of the Choice, the mouse should ungrab on the
854
// the press, not the release
855
if (helper.isInVertSB(getBounds(), local.x, local.y)) {
856
mouseInSB = true;
857
helper.handleVSBEvent(e, getBounds(), local.x, local.y);
858
}
859
else {
860
trackSelection(local.x, local.y);
861
}
862
break;
863
case MouseEvent.MOUSE_RELEASED:
864
if (mouseInSB) {
865
mouseInSB = false;
866
helper.handleVSBEvent(e, getBounds(), local.x, local.y);
867
}else{
868
// See 6243382 for more information
869
helper.trackMouseReleasedScroll();
870
}
871
/*
872
else {
873
trackSelection(local.x, local.y);
874
}
875
*/
876
break;
877
case MouseEvent.MOUSE_DRAGGED:
878
if (mouseInSB) {
879
helper.handleVSBEvent(e, getBounds(), local.x, local.y);
880
}
881
else {
882
// See 6243382 for more information
883
helper.trackMouseDraggedScroll(local.x, local.y, width, height);
884
trackSelection(local.x, local.y);
885
}
886
break;
887
}
888
}
889
890
private void trackSelection(int transX, int transY) {
891
if (!helper.isEmpty()) {
892
if (transX > 0 && transX < width &&
893
transY > 0 && transY < height) {
894
int newIdx = helper.y2index(transY);
895
if (log.isLoggable(PlatformLogger.Level.FINE)) {
896
log.fine("transX=" + transX + ", transY=" + transY
897
+ ",width=" + width + ", height=" + height
898
+ ", newIdx=" + newIdx + " on " + target);
899
}
900
if ((newIdx >=0) && (newIdx < helper.getItemCount())
901
&& (newIdx != helper.getSelectedIndex()))
902
{
903
helper.select(newIdx);
904
unfurledChoice.repaint();
905
}
906
}
907
}
908
// FIXME: If dragged off top or bottom, scroll if there's a vsb
909
// (ICK - we'll need a timer or our own event or something)
910
}
911
912
/*
913
* fillRect with current Background color on the whole dropdown list.
914
*/
915
public void paintBackground() {
916
final Graphics g = getGraphics();
917
if (g != null) {
918
try {
919
g.setColor(getPeerBackground());
920
g.fillRect(0, 0, width, height);
921
} finally {
922
g.dispose();
923
}
924
}
925
}
926
/*
927
* 6405689. In some cases we should erase background to eliminate painting
928
* artefacts.
929
*/
930
@Override
931
public void repaint() {
932
if (!isVisible()) {
933
return;
934
}
935
if (helper.checkVsbVisibilityChangedAndReset()){
936
paintBackground();
937
}
938
super.repaint();
939
}
940
@Override
941
public void paintPeer(Graphics g) {
942
//System.out.println("UC.paint()");
943
Choice choice = (Choice)target;
944
Color colors[] = XChoicePeer.this.getGUIcolors();
945
draw3DRect(g, getSystemColors(), 0, 0, width - 1, height - 1, true);
946
draw3DRect(g, getSystemColors(), 1, 1, width - 3, height - 3, true);
947
948
helper.paintAllItems(g,
949
colors,
950
getBounds());
951
}
952
953
public void setVisible(boolean vis) {
954
xSetVisible(vis);
955
956
if (!vis && alignUnder != null) {
957
alignUnder.requestFocusInWindow();
958
}
959
}
960
961
/**
962
* Return a MouseEvent's Point in coordinates relative to the
963
* UnfurledChoice.
964
*/
965
private Point toLocalCoords(MouseEvent e) {
966
// Event coords are relative to the button, so translate a bit
967
Point global = e.getLocationOnScreen();
968
969
global.x -= x;
970
global.y -= y;
971
return global;
972
}
973
974
/* Returns true if the MouseEvent coords (which are based on the Choice)
975
* are inside of the UnfurledChoice.
976
*/
977
private boolean isMouseEventInside(MouseEvent e) {
978
Point local = toLocalCoords(e);
979
if (local.x > 0 && local.x < width &&
980
local.y > 0 && local.y < height) {
981
return true;
982
}
983
return false;
984
}
985
986
/**
987
* Tests if the mouse cursor is in the Unfurled Choice, yet not
988
* in the vertical scrollbar
989
*/
990
private boolean isMouseInListArea(MouseEvent e) {
991
if (isMouseEventInside(e)) {
992
Point local = toLocalCoords(e);
993
Rectangle bounds = getBounds();
994
if (!helper.isInVertSB(bounds, local.x, local.y)) {
995
return true;
996
}
997
}
998
return false;
999
}
1000
1001
/*
1002
* Overridden from XWindow() because we don't want to send
1003
* ComponentEvents
1004
*/
1005
public void handleConfigureNotifyEvent(XEvent xev) {}
1006
public void handleMapNotifyEvent(XEvent xev) {}
1007
public void handleUnmapNotifyEvent(XEvent xev) {}
1008
} //UnfurledChoice
1009
1010
public void dispose() {
1011
if (unfurledChoice != null) {
1012
unfurledChoice.destroy();
1013
}
1014
super.dispose();
1015
}
1016
1017
/*
1018
* fix for 6239938 : Choice drop-down does not disappear when it loses
1019
* focus, on XToolkit
1020
* We are able to handle all _Key_ events received by Choice when
1021
* it is in opened state without sending it to EventQueue.
1022
* If Choice is in closed state we should behave like before: send
1023
* all events to EventQueue.
1024
* To be compatible with Motif we should handle all KeyEvents in
1025
* Choice if it is opened. KeyEvents should be sent into Java if Choice is not opened.
1026
*/
1027
boolean prePostEvent(final AWTEvent e) {
1028
if (unfurled){
1029
// fix for 6253211: PIT: MouseWheel events not triggered for Choice drop down in XAWT
1030
if (e instanceof MouseWheelEvent){
1031
return super.prePostEvent(e);
1032
}
1033
//fix 6252982: PIT: Keyboard FocusTraversal not working when choice's drop-down is visible, on XToolkit
1034
if (e instanceof KeyEvent){
1035
// notify XWindow that this event had been already handled and no need to post it again
1036
InvocationEvent ev = new InvocationEvent(target, new Runnable() {
1037
public void run() {
1038
if(target.isFocusable() &&
1039
getParentTopLevel().isFocusableWindow() )
1040
{
1041
handleJavaKeyEvent((KeyEvent)e);
1042
}
1043
}
1044
});
1045
postEvent(ev);
1046
1047
return true;
1048
} else {
1049
if (e instanceof MouseEvent){
1050
// Fix for 6240046 : REG:Choice's Drop-down does not disappear when clicking somewhere, after popup menu is disposed
1051
// if user presses Right Mouse Button on opened (unfurled)
1052
// Choice then we mustn't open a popup menu. We could filter
1053
// Mouse Events and handle them in XChoicePeer if Choice
1054
// currently in opened state.
1055
MouseEvent me = (MouseEvent)e;
1056
int eventId = e.getID();
1057
// fix 6251983: PIT: MouseDragged events not triggered
1058
// fix 6251988: PIT: Choice consumes MouseReleased, MouseClicked events when clicking it with left button,
1059
if ((unfurledChoice.isMouseEventInside(me) ||
1060
(!firstPress && eventId == MouseEvent.MOUSE_DRAGGED)))
1061
{
1062
return handleMouseEventByChoice(me);
1063
}
1064
// MouseMoved events should be fired in Choice's comp if it's not opened
1065
// Shouldn't generate Moved Events. CR : 6251995
1066
if (eventId == MouseEvent.MOUSE_MOVED){
1067
return handleMouseEventByChoice(me);
1068
}
1069
//fix for 6272965: PIT: Choice triggers MousePressed when pressing mouse outside comp while drop-down is active, XTkt
1070
if ( !firstPress && !( isMouseEventInChoice(me) ||
1071
unfurledChoice.isMouseEventInside(me)) &&
1072
( eventId == MouseEvent.MOUSE_PRESSED ||
1073
eventId == MouseEvent.MOUSE_RELEASED ||
1074
eventId == MouseEvent.MOUSE_CLICKED )
1075
)
1076
{
1077
return handleMouseEventByChoice(me);
1078
}
1079
}
1080
}//else KeyEvent
1081
}//if unfurled
1082
return super.prePostEvent(e);
1083
}
1084
1085
//convenient method
1086
//do not generate this kind of Events
1087
public boolean handleMouseEventByChoice(final MouseEvent me){
1088
InvocationEvent ev = new InvocationEvent(target, new Runnable() {
1089
public void run() {
1090
handleJavaMouseEvent(me);
1091
}
1092
});
1093
postEvent(ev);
1094
1095
return true;
1096
}
1097
1098
/* Returns true if the MouseEvent coords
1099
* are inside of the Choice itself (it doesnt's depends on
1100
* if this choice opened or not).
1101
*/
1102
private boolean isMouseEventInChoice(MouseEvent e) {
1103
int x = e.getX();
1104
int y = e.getY();
1105
Rectangle choiceRect = getBounds();
1106
1107
if (x < 0 || x > choiceRect.width ||
1108
y < 0 || y > choiceRect.height)
1109
{
1110
return false;
1111
}
1112
return true;
1113
}
1114
}
1115
1116