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