• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright (c) 2011 Google, Inc.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *    Google, Inc. - initial API and implementation
10  *******************************************************************************/
11 package org.eclipse.wb.core.controls;
12 
13 import org.eclipse.swt.SWT;
14 import org.eclipse.swt.custom.CCombo;
15 import org.eclipse.swt.events.DisposeEvent;
16 import org.eclipse.swt.events.DisposeListener;
17 import org.eclipse.swt.events.SelectionListener;
18 import org.eclipse.swt.graphics.Color;
19 import org.eclipse.swt.graphics.Image;
20 import org.eclipse.swt.graphics.Point;
21 import org.eclipse.swt.graphics.Rectangle;
22 import org.eclipse.swt.layout.FillLayout;
23 import org.eclipse.swt.widgets.Button;
24 import org.eclipse.swt.widgets.Combo;
25 import org.eclipse.swt.widgets.Composite;
26 import org.eclipse.swt.widgets.Control;
27 import org.eclipse.swt.widgets.Display;
28 import org.eclipse.swt.widgets.Event;
29 import org.eclipse.swt.widgets.Listener;
30 import org.eclipse.swt.widgets.Shell;
31 import org.eclipse.swt.widgets.Table;
32 import org.eclipse.swt.widgets.TableColumn;
33 import org.eclipse.swt.widgets.TableItem;
34 import org.eclipse.swt.widgets.TypedListener;
35 
36 import java.util.Locale;
37 
38 /**
39  * {@link Control} like {@link Combo} or {@link CCombo} that shows {@link Table} with image/text as
40  * drop-down.
41  *
42  * @author mitin_aa
43  * @author scheglov_ke
44  * @coverage core.control
45  */
46 public class CTableCombo extends Composite {
47   protected Button m_arrow;
48   protected CImageLabel m_text;
49   protected Shell m_popup;
50   protected Table m_table;
51   protected boolean hasFocus;
52 
53   //
CTableCombo(Composite parent, int style)54   public CTableCombo(Composite parent, int style) {
55     super(parent, style = checkStyle(style));
56     init(parent, style);
57   }
58 
checkStyle(int style)59   static int checkStyle(int style) {
60     int mask = SWT.BORDER | SWT.READ_ONLY | SWT.FLAT;
61     return style & mask;
62   }
63 
init(Composite parent, int style)64   private void init(Composite parent, int style) {
65     m_arrow = new Button(this, SWT.ARROW | SWT.DOWN | SWT.NO_FOCUS);
66     m_text = new CImageLabel(this, style & ~SWT.BORDER);
67     m_text.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
68     final Shell shell = getShell();
69     m_popup = new Shell(shell, SWT.NONE);
70     m_table = new Table(m_popup, SWT.FULL_SELECTION);
71     new TableColumn(m_table, SWT.NONE);
72     Listener listener = new Listener() {
73       public void handleEvent(Event event) {
74         if (m_popup == event.widget) {
75           handlePopupEvent(event);
76           return;
77         }
78         if (m_text == event.widget) {
79           handleTextEvent(event);
80           return;
81         }
82         if (m_table == event.widget) {
83           handleTableEvent(event);
84           return;
85         }
86         if (m_arrow == event.widget) {
87           handleArrowEvent(event);
88           return;
89         }
90         if (CTableCombo.this == event.widget) {
91           handleComboEvent(event);
92           return;
93         }
94       }
95     };
96     final Listener shellListener = new Listener() {
97       public void handleEvent(Event event) {
98         switch (event.type) {
99           case SWT.Dispose :
100           case SWT.Move :
101           case SWT.Resize :
102             if (!isDisposed()) {
103               dropDown(false);
104             }
105             break;
106         }
107       }
108     };
109     final int[] comboEvents = {SWT.Dispose, SWT.Move, SWT.Resize};
110     for (int i = 0; i < comboEvents.length; i++) {
111       addListener(comboEvents[i], listener);
112       // HACK: hide popup when parent changed
113       shell.addListener(comboEvents[i], shellListener);
114     }
115     addDisposeListener(new DisposeListener() {
116       public void widgetDisposed(DisposeEvent e) {
117         for (int i = 0; i < comboEvents.length; i++) {
118           shell.removeListener(comboEvents[i], shellListener);
119         }
120       }
121     });
122     int[] popupEvents = {SWT.Close, SWT.Paint, SWT.Deactivate};
123     for (int i = 0; i < popupEvents.length; i++) {
124       m_popup.addListener(popupEvents[i], listener);
125     }
126     int[] textEvents =
127         {
128             SWT.KeyDown,
129             SWT.KeyUp,
130             SWT.Modify,
131             SWT.MouseDown,
132             SWT.MouseUp,
133             SWT.MouseDoubleClick,
134             SWT.Traverse,
135             SWT.FocusIn,
136             SWT.FocusOut};
137     for (int i = 0; i < textEvents.length; i++) {
138       m_text.addListener(textEvents[i], listener);
139     }
140     int[] tableEvents =
141         {
142             SWT.MouseUp,
143             SWT.Selection,
144             SWT.Traverse,
145             SWT.KeyDown,
146             SWT.KeyUp,
147             SWT.FocusIn,
148             SWT.FocusOut};
149     for (int i = 0; i < tableEvents.length; i++) {
150       m_table.addListener(tableEvents[i], listener);
151     }
152     int[] arrowEvents = {SWT.Selection, SWT.FocusIn, SWT.FocusOut};
153     for (int i = 0; i < arrowEvents.length; i++) {
154       m_arrow.addListener(arrowEvents[i], listener);
155     }
156   }
157 
handleTableEvent(Event event)158   protected void handleTableEvent(Event event) {
159     switch (event.type) {
160       case SWT.FocusIn : {
161         if (hasFocus) {
162           return;
163         }
164         hasFocus = true;
165         Event e = new Event();
166         e.time = event.time;
167         notifyListeners(SWT.FocusIn, e);
168         break;
169       }
170       case SWT.FocusOut : {
171         final int time = event.time;
172         event.display.asyncExec(new Runnable() {
173           public void run() {
174             if (CTableCombo.this.isDisposed()) {
175               return;
176             }
177             Control focusControl = getDisplay().getFocusControl();
178             if (focusControl == m_text || focusControl == m_arrow) {
179               return;
180             }
181             hasFocus = false;
182             Event e = new Event();
183             e.time = time;
184             notifyListeners(SWT.FocusOut, e);
185           }
186         });
187         break;
188       }
189       case SWT.MouseUp : {
190         if (event.button != 1) {
191           return;
192         }
193         dropDown(false);
194         Event e = new Event();
195         e.time = event.time;
196         notifyListeners(SWT.DefaultSelection, e);
197         break;
198       }
199       case SWT.Selection : {
200         int index = m_table.getSelectionIndex();
201         if (index == -1) {
202           return;
203         }
204         TableItem item = m_table.getItem(index);
205         m_text.setText(item.getText());
206         m_text.setImage(item.getImage());
207         //m_text.selectAll();
208         m_table.setSelection(index);
209         Event e = new Event();
210         e.time = event.time;
211         e.stateMask = event.stateMask;
212         e.doit = event.doit;
213         notifyListeners(SWT.Selection, e);
214         event.doit = e.doit;
215         dropDown(false);
216         break;
217       }
218       case SWT.Traverse : {
219         switch (event.detail) {
220           case SWT.TRAVERSE_TAB_NEXT :
221           case SWT.TRAVERSE_RETURN :
222           case SWT.TRAVERSE_ESCAPE :
223           case SWT.TRAVERSE_ARROW_PREVIOUS :
224           case SWT.TRAVERSE_ARROW_NEXT :
225             event.doit = false;
226             break;
227         }
228         Event e = new Event();
229         e.time = event.time;
230         e.detail = event.detail;
231         e.doit = event.doit;
232         e.keyCode = event.keyCode;
233         notifyListeners(SWT.Traverse, e);
234         event.doit = e.doit;
235         break;
236       }
237       case SWT.KeyUp : {
238         Event e = new Event();
239         e.time = event.time;
240         e.character = event.character;
241         e.keyCode = event.keyCode;
242         e.stateMask = event.stateMask;
243         notifyListeners(SWT.KeyUp, e);
244         break;
245       }
246       case SWT.KeyDown : {
247         if (event.character == SWT.ESC) {
248           // escape key cancels popups
249           dropDown(false);
250         }
251         if (event.character == SWT.CR || event.character == '\t') {
252           // Enter and Tab cause default selection
253           dropDown(false);
254           Event e = new Event();
255           e.time = event.time;
256           e.stateMask = event.stateMask;
257           notifyListeners(SWT.DefaultSelection, e);
258         }
259         // At this point the widget may have been disposed.
260         // If so, do not continue.
261         if (isDisposed()) {
262           break;
263         }
264         Event e = new Event();
265         e.time = event.time;
266         e.character = event.character;
267         e.keyCode = event.keyCode;
268         e.stateMask = event.stateMask;
269         notifyListeners(SWT.KeyDown, e);
270         break;
271       }
272     }
273   }
274 
handlePopupEvent(Event event)275   protected void handlePopupEvent(Event event) {
276     switch (event.type) {
277       case SWT.Paint :
278         // draw black rectangle around list
279         Rectangle listRect = m_table.getBounds();
280         Color black = getDisplay().getSystemColor(SWT.COLOR_BLACK);
281         event.gc.setForeground(black);
282         event.gc.drawRectangle(0, 0, listRect.width + 1, listRect.height + 1);
283         break;
284       case SWT.Close :
285         event.doit = false;
286         dropDown(false);
287         break;
288     }
289   }
290 
handleComboEvent(Event event)291   protected void handleComboEvent(Event event) {
292     switch (event.type) {
293       case SWT.Dispose :
294         if (m_popup != null && !m_popup.isDisposed()) {
295           m_popup.dispose();
296         }
297         m_popup = null;
298         m_text = null;
299         m_arrow = null;
300         break;
301       case SWT.Move :
302         dropDown(false);
303         break;
304       case SWT.Resize :
305         internalLayout();
306         break;
307     }
308   }
309 
handleArrowEvent(Event event)310   protected void handleArrowEvent(Event event) {
311     switch (event.type) {
312       case SWT.FocusIn : {
313         if (hasFocus) {
314           return;
315         }
316         hasFocus = true;
317         Event e = new Event();
318         e.time = event.time;
319         notifyListeners(SWT.FocusIn, e);
320         break;
321       }
322       case SWT.Selection : {
323         boolean wasDropped = isDropped();
324         dropDown(!wasDropped);
325         if (wasDropped) {
326           m_text.forceFocus();
327         }
328         break;
329       }
330     }
331   }
332 
handleTextEvent(Event event)333   protected void handleTextEvent(Event event) {
334     switch (event.type) {
335       case SWT.FocusIn : {
336         if (hasFocus) {
337           return;
338         }
339         hasFocus = true;
340         //if (getEditable())
341         Event e = new Event();
342         e.time = event.time;
343         notifyListeners(SWT.FocusIn, e);
344         break;
345       }
346       case SWT.FocusOut : {
347         final int time = event.time;
348         event.display.asyncExec(new Runnable() {
349           public void run() {
350             if (CTableCombo.this.isDisposed()) {
351               return;
352             }
353             Control focusControl = getDisplay().getFocusControl();
354             if (focusControl == m_table
355                 || focusControl == m_arrow
356                 || focusControl != null
357                 && focusControl.getParent() == CTableCombo.this) {
358               return;
359             }
360             hasFocus = false;
361             Event e = new Event();
362             e.time = time;
363             notifyListeners(SWT.FocusOut, e);
364           }
365         });
366         break;
367       }
368       case SWT.KeyDown : {
369         if (event.character == SWT.ESC) { // escape key cancels popup
370           dropDown(false);
371         }
372         if (event.character == SWT.CR) {
373           dropDown(false);
374           Event e = new Event();
375           e.time = event.time;
376           e.stateMask = event.stateMask;
377           notifyListeners(SWT.DefaultSelection, e);
378         }
379         // At this point the widget may have been disposed.
380         // If so, do not continue.
381         if (isDisposed()) {
382           break;
383         }
384         if (event.character == '+') {
385           dropDown(true);
386         }
387         if (isDropped()) {
388           if (event.keyCode == SWT.ARROW_UP || event.keyCode == SWT.ARROW_DOWN) {
389             int oldIndex = getSelectionIndex();
390             if (event.keyCode == SWT.ARROW_UP) {
391               select(Math.max(oldIndex - 1, 0));
392             } else {
393               select(Math.min(oldIndex + 1, getItemCount() - 1));
394             }
395             if (oldIndex != getSelectionIndex()) {
396               Event e = new Event();
397               e.time = event.time;
398               e.stateMask = event.stateMask;
399               notifyListeners(SWT.Selection, e);
400             }
401             // At this point the widget may have been disposed.
402             // If so, do not continue.
403             if (isDisposed()) {
404               break;
405             }
406           }
407         }
408         if (Character.isLetter(event.character)) {
409           int oldIndex = getSelectionIndex();
410           int index = -1;
411           for (int i = 0; i < getItemCount(); i++) {
412             String item = getItem(i).toUpperCase(Locale.ENGLISH);
413             if (item.length() != 0 && item.charAt(0) == Character.toUpperCase(event.character)) {
414               index = i;
415               break;
416             }
417           }
418           if (index != -1) {
419             select(Math.max(index, 0));
420             if (oldIndex != getSelectionIndex()) {
421               Event e = new Event();
422               e.time = event.time;
423               e.stateMask = event.stateMask;
424               notifyListeners(SWT.Selection, e);
425             }
426           }
427         }
428         Event e = new Event();
429         e.time = event.time;
430         e.character = event.character;
431         e.keyCode = event.keyCode;
432         e.stateMask = event.stateMask;
433         if (m_text != null && !m_text.isDisposed()) {
434           notifyListeners(SWT.KeyDown, e);
435         }
436         break;
437       }
438       case SWT.KeyUp : {
439         Event e = new Event();
440         e.time = event.time;
441         e.character = event.character;
442         e.keyCode = event.keyCode;
443         e.stateMask = event.stateMask;
444         notifyListeners(SWT.KeyUp, e);
445         break;
446       }
447       case SWT.Modify : {
448         m_table.deselectAll();
449         Event e = new Event();
450         e.time = event.time;
451         notifyListeners(SWT.Modify, e);
452         break;
453       }
454       case SWT.MouseDown : {
455         if (event.button != 1) {
456           return;
457         }
458         m_text.forceFocus();
459         boolean dropped = isDropped();
460         dropDown(!dropped);
461         if (!dropped) {
462           m_text.forceFocus();
463         }
464         break;
465       }
466       case SWT.MouseDoubleClick : {
467         notifyListeners(SWT.MouseDoubleClick, event);
468         break;
469       }
470       case SWT.Traverse : {
471         switch (event.detail) {
472           case SWT.TRAVERSE_RETURN :
473           case SWT.TRAVERSE_ARROW_PREVIOUS :
474           case SWT.TRAVERSE_ARROW_NEXT :
475             // The enter causes default selection and
476             // the arrow keys are used to manipulate the list contents so
477             // do not use them for traversal.
478             event.doit = false;
479             break;
480           case SWT.TRAVERSE_TAB_NEXT :
481           case SWT.TRAVERSE_TAB_PREVIOUS :
482             event.doit = true;
483             break;
484         }
485         Event e = new Event();
486         e.time = event.time;
487         e.detail = event.detail;
488         e.doit = event.doit;
489         e.keyCode = event.keyCode;
490         notifyListeners(SWT.Traverse, e);
491         event.doit = e.doit;
492         break;
493       }
494     }
495   }
496 
dropDown(boolean drop)497   private void dropDown(boolean drop) {
498     if (drop == isDropped()) {
499       return;
500     }
501     if (!drop) {
502       m_popup.setVisible(false);
503       m_text.setFocus();
504       return;
505     }
506     int index = m_table.getSelectionIndex();
507     if (index != -1) {
508       m_table.setTopIndex(index);
509       m_table.setSelection(index);
510     }
511     m_table.pack();
512     Point point = getParent().toDisplay(getLocation());
513     Point comboSize = getSize();
514     //Rectangle tableRect = m_table.getBounds();
515     //int width = Math.max(comboSize.x, tableRect.width + 2);
516     int width = comboSize.x - 1;
517     // only one column
518     m_table.getColumn(0).setWidth(width - 5);
519     if (!(m_popup.getLayout() instanceof FillLayout)) {
520       m_popup.setLayout(new FillLayout());
521     }
522     int itemCount = m_table.getItemCount();
523     if (itemCount > 20) {
524       itemCount = 20;
525     }
526     int height =
527         Math.min(
528             m_table.getItemHeight() * itemCount + 5,
529             Display.getCurrent().getClientArea().height - point.y - 20);
530     m_popup.setBounds(point.x, point.y + comboSize.y, width, height);
531     m_popup.layout();
532     m_popup.setVisible(true);
533     m_text.setFocus();
534     if (index != -1) {
535       m_table.setTopIndex(index);
536       m_table.setSelection(index);
537     }
538   }
539 
540   @Override
computeSize(int wHint, int hHint, boolean changed)541   public Point computeSize(int wHint, int hHint, boolean changed) {
542     checkWidget();
543     int width = 0, height = 0;
544     Point textSize = m_text.computeSize(wHint, SWT.DEFAULT, changed);
545     Point arrowSize = m_arrow.computeSize(SWT.DEFAULT, SWT.DEFAULT, changed);
546     int tableWidth;
547     {
548       TableColumn column = m_table.getColumn(0);
549       column.pack();
550       tableWidth = column.getWidth();
551     }
552     //
553     int borderWidth = getBorderWidth();
554     height = Math.max(hHint, Math.max(textSize.y, arrowSize.y) + 2 * borderWidth);
555     width = Math.max(wHint, Math.max(textSize.x + arrowSize.x, tableWidth) + 2 * borderWidth);
556     //
557     return new Point(width, height);
558   }
559 
internalLayout()560   private void internalLayout() {
561     if (isDropped()) {
562       dropDown(false);
563     }
564     Rectangle rect = getClientArea();
565     int width = rect.width;
566     int height = rect.height;
567     Point arrowSize = m_arrow.computeSize(SWT.DEFAULT, height);
568     m_text.setBounds(rect.x, rect.y, width - arrowSize.x, height);
569     m_arrow.setBounds(rect.x + width - arrowSize.x, rect.y, arrowSize.x, arrowSize.y);
570   }
571 
isDropped()572   private boolean isDropped() {
573     return m_popup.isVisible();
574   }
575 
576   @Override
isFocusControl()577   public boolean isFocusControl() {
578     checkWidget();
579     if (m_text.isFocusControl()
580         || m_arrow.isFocusControl()
581         || m_table.isFocusControl()
582         || m_popup.isFocusControl()) {
583       return true;
584     }
585     return super.isFocusControl();
586   }
587 
select(int index)588   public void select(int index) {
589     checkWidget();
590     if (index == -1) {
591       m_table.deselectAll();
592       m_text.setText(""); //$NON-NLS-1$
593       m_text.setImage(null);
594       return;
595     }
596     if (0 <= index && index < m_table.getItemCount()) {
597       if (index != getSelectionIndex()) {
598         TableItem item = m_table.getItem(index);
599         m_text.setText(item.getText());
600         m_text.setImage(item.getImage());
601         m_table.select(index);
602         m_table.showSelection();
603       }
604     }
605   }
606 
607   @Override
setEnabled(boolean enabled)608   public void setEnabled(boolean enabled) {
609     super.setEnabled(enabled);
610     if (enabled) {
611       m_text.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
612     } else {
613       m_text.setBackground(Display.getDefault().getSystemColor(SWT.COLOR_WIDGET_LIGHT_SHADOW));
614     }
615   }
616 
getItem(int index)617   public String getItem(int index) {
618     checkWidget();
619     return m_table.getItem(index).getText();
620   }
621 
getSelectionIndex()622   public int getSelectionIndex() {
623     checkWidget();
624     return m_table.getSelectionIndex();
625   }
626 
removeAll()627   public void removeAll() {
628     checkWidget();
629     m_text.setText(""); //$NON-NLS-1$
630     m_text.setImage(null);
631     m_table.removeAll();
632   }
633 
indexOf(String string)634   public int indexOf(String string) {
635     return indexOf(string, 0);
636   }
637 
indexOf(String string, int start)638   public int indexOf(String string, int start) {
639     checkWidget();
640     if (string == null) {
641       return -1;
642     }
643     TableItem[] items = m_table.getItems();
644     for (int i = start; i < items.length; i++) {
645       TableItem item = items[i];
646       if (item.getText().equalsIgnoreCase(string)) {
647         return i;
648       }
649     }
650     return -1;
651   }
652 
getText()653   public String getText() {
654     return m_text.getText();
655   }
656 
getItemCount()657   public int getItemCount() {
658     checkWidget();
659     return m_table.getItemCount();
660   }
661 
setText(String string)662   protected void setText(String string) {
663     m_text.setText(string);
664   }
665 
setImage(Image image)666   protected void setImage(Image image) {
667     m_text.setImage(image);
668   }
669 
add(String text)670   public void add(String text) {
671     add(text, null);
672   }
673 
add(String text, Image image)674   public void add(String text, Image image) {
675     checkWidget();
676     TableItem item = new TableItem(m_table, SWT.NONE);
677     item.setText(text);
678     item.setImage(image);
679   }
680 
addSelectionListener(SelectionListener listener)681   public void addSelectionListener(SelectionListener listener) {
682     checkWidget();
683     if (listener == null) {
684       SWT.error(SWT.ERROR_NULL_ARGUMENT);
685     }
686     TypedListener typedListener = new TypedListener(listener);
687     addListener(SWT.Selection, typedListener);
688     addListener(SWT.DefaultSelection, typedListener);
689   }
690 }
691