1 /*******************************************************************************
\r
2 * Copyright (c) 2007, 2012 Association for Decentralized Information Management in
\r
4 * All rights reserved. This program and the accompanying materials
\r
5 * are made available under the terms of the Eclipse Public License v1.0
\r
6 * which accompanies this distribution, and is available at
\r
7 * http://www.eclipse.org/legal/epl-v10.html
\r
10 * VTT Technical Research Centre of Finland - initial API and implementation
\r
11 *******************************************************************************/
\r
12 package org.simantics.sysdyn.ui.properties.widgets;
\r
14 import java.awt.Font;
\r
15 import java.awt.GraphicsEnvironment;
\r
16 import java.util.ArrayList;
\r
17 import java.util.List;
\r
18 import java.util.Locale;
\r
19 import java.util.TreeMap;
\r
21 import org.eclipse.core.runtime.ListenerList;
\r
22 import org.eclipse.jface.layout.GridDataFactory;
\r
23 import org.eclipse.jface.layout.GridLayoutFactory;
\r
24 import org.eclipse.jface.resource.FontDescriptor;
\r
25 import org.eclipse.jface.resource.JFaceResources;
\r
26 import org.eclipse.jface.resource.LocalResourceManager;
\r
27 import org.eclipse.swt.SWT;
\r
28 import org.eclipse.swt.events.KeyEvent;
\r
29 import org.eclipse.swt.events.KeyListener;
\r
30 import org.eclipse.swt.events.ModifyEvent;
\r
31 import org.eclipse.swt.events.ModifyListener;
\r
32 import org.eclipse.swt.events.SelectionEvent;
\r
33 import org.eclipse.swt.events.SelectionListener;
\r
34 import org.eclipse.swt.graphics.FontData;
\r
35 import org.eclipse.swt.graphics.Point;
\r
36 import org.eclipse.swt.graphics.Rectangle;
\r
37 import org.eclipse.swt.widgets.Composite;
\r
38 import org.eclipse.swt.widgets.Event;
\r
39 import org.eclipse.swt.widgets.Listener;
\r
40 import org.eclipse.swt.widgets.ScrollBar;
\r
41 import org.eclipse.swt.widgets.Table;
\r
42 import org.eclipse.swt.widgets.TableColumn;
\r
43 import org.eclipse.swt.widgets.TableItem;
\r
44 import org.eclipse.swt.widgets.Text;
\r
47 * Composite for displaying font selection tools. By default, the composite contains
\r
48 * font family, font style and font size.
\r
50 * @author Teemu Lempinen
\r
53 public class FontSelectionComposite extends Composite {
\r
56 protected Text fontName, fontStyle, fontSize;
\r
57 protected ArrayList<String> familyIndex = new ArrayList<String>();
\r
58 protected TreeMap<String, ArrayList<Font>> fonts = getFonts(familyIndex);
\r
59 protected Table fontFamilyTable, fontStyleTable, fontSizeTable;
\r
60 protected String[] sizes = new String[] {"8", "9", "10", "11", "12", "14", "16", "18", "20", "24", "26", "28", "36", "48", "72"};
\r
62 private ListenerList modifyListeners;
\r
64 private final LocalResourceManager resourceManager;
\r
67 * Gets all available fonts
\r
68 * @param familyIndex Optional list for indexing font families
\r
69 * @return Tree where key is font family name and object a list of fonts belonging to that family
\r
71 private static TreeMap<String, ArrayList<Font>> getFonts(ArrayList<String> familyIndex) {
\r
72 TreeMap<String, ArrayList<Font>> fonts = new TreeMap<String, ArrayList<Font>>();
\r
74 GraphicsEnvironment gEnv = GraphicsEnvironment
\r
75 .getLocalGraphicsEnvironment();
\r
76 Font allFonts[] = gEnv.getAllFonts();
\r
78 for(Font font : allFonts) {
\r
79 String family = font.getFamily(Locale.ROOT);
\r
80 if(!fonts.containsKey(family)) {
\r
81 if(familyIndex != null)
\r
82 familyIndex.add(family);
\r
83 fonts.put(family, new ArrayList<Font>());
\r
87 for(Font f : fonts.get(family)) {
\r
88 if(f.getFontName().equals(font.getFontName())) {
\r
95 fonts.get(family).add(font);
\r
101 * Composite containing components for selecting a font
\r
103 * @param parent Parent composite
\r
104 * @param style SWT style
\r
106 public FontSelectionComposite(Composite parent, int style) {
\r
107 super(parent, style);
\r
109 // Create a ResourceManager to dispose images when the widget is disposed.
\r
110 this.resourceManager = new LocalResourceManager(JFaceResources.getResources(), this);
\r
112 modifyListeners = new ListenerList();
\r
114 GridLayoutFactory.fillDefaults().numColumns(3).applyTo(this);
\r
115 GridDataFactory.fillDefaults().applyTo(this);
\r
118 * Two-row layout. First row consists of editable text boxes,
\r
119 * second row consists of tables containing possible options
\r
123 fontName = new Text(this, SWT.BORDER);
\r
124 GridDataFactory.fillDefaults().applyTo(fontName);
\r
126 fontStyle = new Text(this, SWT.BORDER);
\r
127 GridDataFactory.fillDefaults().applyTo(fontStyle);
\r
129 fontSize = new Text(this, SWT.BORDER);
\r
130 GridDataFactory.fillDefaults().applyTo(fontSize);
\r
133 fontFamilyTable = new Table (this, SWT.VIRTUAL | SWT.BORDER | SWT.FULL_SELECTION);
\r
134 fontFamilyTable.setLinesVisible (false);
\r
135 fontFamilyTable.setHeaderVisible (false);
\r
136 GridDataFactory.fillDefaults().hint(SWT.DEFAULT, 100).applyTo(fontFamilyTable);
\r
137 fontFamilyTable.setItemCount(familyIndex.size());
\r
138 TableColumn column = new TableColumn (fontFamilyTable, SWT.NONE);
\r
139 column.setWidth(200);
\r
142 fontStyleTable = new Table (this, SWT.BORDER | SWT.FULL_SELECTION);
\r
143 fontStyleTable.setLinesVisible (false);
\r
144 fontStyleTable.setHeaderVisible (false);
\r
145 GridDataFactory.fillDefaults().hint(100, 100).applyTo(fontStyleTable);
\r
146 column = new TableColumn (fontStyleTable, SWT.NONE);
\r
147 setFontStyleTableWidth();
\r
151 fontSizeTable = new Table (this, SWT.VIRTUAL | SWT.BORDER | SWT.FULL_SELECTION);
\r
152 fontSizeTable.setLinesVisible (false);
\r
153 fontSizeTable.setHeaderVisible (false);
\r
154 GridDataFactory.fillDefaults().hint(SWT.DEFAULT, 100).applyTo(fontSizeTable);
\r
155 column = new TableColumn (fontSizeTable, SWT.NONE);
\r
156 column.setWidth(70);
\r
157 for(String size : sizes) {
\r
158 TableItem item = new TableItem(fontSizeTable, SWT.NONE);
\r
159 item.setText(0, size);
\r
162 // Listeners for components
\r
163 addFontFamilyListeners();
\r
164 addFontStyleListeners();
\r
165 addFontSizeListeners();
\r
170 * Set controls to display given font
\r
172 public void setFont(Font font, boolean notify) {
\r
176 Object[] listeners = new Object[0];
\r
178 listeners = modifyListeners.getListeners();
\r
179 for(Object listener : listeners)
\r
180 modifyListeners.remove(listener);
\r
183 String fontFamily = font.getFamily(Locale.ROOT);
\r
184 String fontName = font.getFontName(Locale.ROOT);
\r
185 this.fontName.setText(fontFamily);
\r
186 this.fontFamilyTable.setTopIndex(this.fontFamilyTable.getSelectionIndex());
\r
188 String style = "Regular";
\r
189 if(fontName.length() > fontFamily.length())
\r
190 style = fontName.substring(fontFamily.length() + 1);
\r
191 this.fontStyle.setText(style);
\r
193 int size = font.getSize();
\r
194 fontSize.setText("" + size);
\r
196 for(int i = 0; i < sizes.length; i++) {
\r
197 if(sizes[i].equals("" + size)) {
\r
198 fontSizeTable.select(i);
\r
199 fontSizeTable.setTopIndex(i);
\r
206 for(Object listener : listeners)
\r
207 modifyListeners.add(listener);
\r
212 * Get the AWT font defined in this composite
\r
215 public Font getAWTFont() {
\r
216 String family = fontName.getText();
\r
217 String style = fontStyle.getText();
\r
218 if(style.equals("Regular"))
\r
222 if(style != null) {
\r
223 if(style.toLowerCase().contains("bold"))
\r
224 stylebits |= SWT.BOLD;
\r
225 if(style.toLowerCase().contains("italic"))
\r
226 stylebits |= SWT.ITALIC;
\r
229 String name = family + (style != null ? " " + style : "");
\r
233 size = Integer.parseInt(fontSize.getText());
\r
234 } catch (NumberFormatException e) {
\r
237 if(name != null && name.length() > 0)
\r
238 return new Font(name, stylebits, size);
\r
244 * Adds listeners for font family name text and table
\r
246 protected void addFontFamilyListeners() {
\r
248 // Font name modify listener
\r
249 fontName.addModifyListener(new ModifyListener() {
\r
252 public void modifyText(ModifyEvent e) {
\r
253 Text text = (Text) e.widget;
\r
254 String name = text.getText();
\r
255 fontFamilyTextModified(name, false);
\r
259 // Font name key listener for auto-completion
\r
260 fontName.addKeyListener(new KeyListener() {
\r
263 public void keyReleased(KeyEvent e) {
\r
267 public void keyPressed(KeyEvent e) {
\r
268 if ((e.character == ' ') && ((e.stateMask & SWT.CTRL) != 0) ) {
\r
269 fontFamilyTextModified(fontName.getText(), true);
\r
271 } else if(e.keyCode == SWT.CR || e.keyCode == SWT.LF || e.keyCode == SWT.KEYPAD_CR) {
\r
277 // Call listener when editing has ended
\r
278 fontName.addListener(SWT.FocusOut, new Listener() {
\r
279 public void handleEvent(Event e) {
\r
284 // Font family data listener for lazy initialization of the table
\r
285 fontFamilyTable.addListener (SWT.SetData, new Listener () {
\r
286 public void handleEvent (Event event) {
\r
287 TableItem item = (TableItem) event.item;
\r
288 int index = fontFamilyTable.indexOf (item);
\r
290 String family = familyIndex.get(index);
\r
291 item.setText (family);
\r
293 Font font = fonts.get(family).get(0);
\r
294 if(font.canDisplay('a')) {
\r
295 FontData fontData = toSwtFontData(font, 10);
\r
296 org.eclipse.swt.graphics.Font swtFont = resourceManager.createFont(FontDescriptor.createFrom(fontData));
\r
297 item.setFont(swtFont);
\r
303 // Updates selected font to font name text
\r
304 fontFamilyTable.addSelectionListener(new SelectionListener() {
\r
307 public void widgetSelected(SelectionEvent e) {
\r
308 TableItem[] selection = fontFamilyTable.getSelection();
\r
310 if(selection.length > 0) {
\r
311 String family = selection[0].getText();
\r
312 fontName.setText(family);
\r
318 public void widgetDefaultSelected(SelectionEvent e) {
\r
323 * Forces focus to font name texts and starts editing it,
\r
324 * if user presses a letter when focus is in font family table
\r
326 fontFamilyTable.addKeyListener(new KeyListener() {
\r
328 public void keyReleased(KeyEvent e) {
\r
332 public void keyPressed(KeyEvent e) {
\r
333 if(Character.isLetter(e.character)) {
\r
334 fontName.forceFocus();
\r
335 fontName.setTextChars(new char[] {e.character});
\r
336 fontName.setSelection(1, 1);
\r
343 * Implements interactions between font name text and font family table, when
\r
344 * the text in font name text has been changed.
\r
345 * @param name New name
\r
346 * @param autoComplete Has autocomplete been called
\r
348 protected void fontFamilyTextModified(String name, boolean autoComplete) {
\r
351 for(int i = 0; i < familyIndex.size(); i++) {
\r
352 String family = familyIndex.get(i);
\r
353 if(family.equals(name)) {
\r
355 fontFamilyTable.select(i);
\r
356 selectFontFamily(name);
\r
358 } else if(family.toLowerCase().equals(name.toLowerCase())) {
\r
359 // Wrong case but correct name
\r
360 fontName.setText(family);
\r
361 fontName.setSelection(family.length(), family.length());
\r
362 fontFamilyTable.setTopIndex(i);
\r
365 } else if(family.toLowerCase().startsWith(name.toLowerCase())) {
\r
367 // Fill in the rest of the name
\r
368 fontName.setText(family);
\r
369 fontName.setSelection(family.length());
\r
371 // The beginning is correct, help user by displaying the nearest name
\r
373 fontFamilyTable.setTopIndex(i);
\r
382 * Adds listeners for font style text and table
\r
384 protected void addFontStyleListeners() {
\r
386 // Font style modify listener
\r
387 fontStyle.addModifyListener(new ModifyListener() {
\r
390 public void modifyText(ModifyEvent e) {
\r
391 Text text = (Text) e.widget;
\r
392 String name = text.getText();
\r
393 fontStyleTextModified(name, false);
\r
397 // Font style key listener for auto-complete
\r
398 fontStyle.addKeyListener(new KeyListener() {
\r
401 public void keyReleased(KeyEvent e) {
\r
405 public void keyPressed(KeyEvent e) {
\r
406 if ((e.character == ' ') && ((e.stateMask & SWT.CTRL) != 0) ) {
\r
407 fontStyleTextModified(fontStyle.getText(), true);
\r
409 } else if(e.keyCode == SWT.CR || e.keyCode == SWT.LF || e.keyCode == SWT.KEYPAD_CR) {
\r
415 // Update selected style to font style text
\r
416 fontStyleTable.addSelectionListener(new SelectionListener() {
\r
419 public void widgetSelected(SelectionEvent e) {
\r
420 TableItem[] selection = fontStyleTable.getSelection();
\r
422 if(selection.length > 0) {
\r
423 String family = selection[0].getText();
\r
424 fontStyle.setText(family);
\r
430 public void widgetDefaultSelected(SelectionEvent e) {
\r
435 * Forces focus to font style text and starts editing it,
\r
436 * if user presses a letter when focus is in font style table
\r
438 fontStyleTable.addKeyListener(new KeyListener() {
\r
440 public void keyReleased(KeyEvent e) {
\r
444 public void keyPressed(KeyEvent e) {
\r
445 if(Character.isLetter(e.character)) {
\r
446 fontStyle.forceFocus();
\r
447 fontStyle.setTextChars(new char[] {e.character});
\r
448 fontStyle.setSelection(1, 1);
\r
450 } else if(e.keyCode == SWT.CR || e.keyCode == SWT.LF || e.keyCode == SWT.KEYPAD_CR) {
\r
458 * Handles interactions between font style text and font style table, when
\r
459 * font style text has been changed
\r
461 * @param name New text for font style
\r
462 * @param autoComplete is auto-completion used
\r
464 protected void fontStyleTextModified(String name, boolean autoComplete) {
\r
468 for(int i = 0; i < fontStyleTable.getItemCount(); i++) {
\r
469 TableItem item = fontStyleTable.getItem(i);
\r
470 String style = item.getText();
\r
472 if(style.equals(name)) {
\r
474 fontStyleTable.select(i);
\r
476 } else if(style.toLowerCase().equals(name.toLowerCase())) {
\r
477 // Wrong case, correct style. -> fix the case
\r
478 fontStyle.setText(style);
\r
479 fontStyle.setSelection(style.length());
\r
480 fontStyleTable.setTopIndex(i);
\r
483 } else if(style.toLowerCase().startsWith(name.toLowerCase())) {
\r
484 // The beginning of the word is correct
\r
485 fontStyleTable.setTopIndex(i);
\r
487 // Fill in the rest of the name
\r
488 fontStyle.setText(style);
\r
489 fontStyle.setSelection(style.length());
\r
491 // The beginning is correct, help user by displaying the nearest name
\r
493 fontStyleTable.setTopIndex(i);
\r
502 * Listeners for font size text and font size table
\r
504 protected void addFontSizeListeners() {
\r
506 // Select an item from size table, if there is a matching item
\r
507 fontSize.addKeyListener(new KeyListener() {
\r
510 public void keyReleased(KeyEvent e) {
\r
511 Text text = (Text) e.widget;
\r
512 String size = text.getText();
\r
514 for(int i = 0; i < sizes.length; i++) {
\r
515 if(sizes[i].equals(size)) {
\r
516 fontSizeTable.select(i);
\r
517 fontSizeTable.setTopIndex(i);
\r
525 public void keyPressed(KeyEvent e) {
\r
529 // Change the text in size text according to the selection in size table
\r
530 fontSizeTable.addSelectionListener(new SelectionListener() {
\r
533 public void widgetSelected(SelectionEvent e) {
\r
534 fontSize.setText(fontSizeTable.getSelection()[0].getText());
\r
539 public void widgetDefaultSelected(SelectionEvent e) {
\r
543 // Change focus from table to text, if user starts to write a number
\r
544 fontSizeTable.addKeyListener(new KeyListener() {
\r
547 public void keyReleased(KeyEvent e) { }
\r
550 public void keyPressed(KeyEvent e) {
\r
551 if(Character.isDigit(e.character)) {
\r
552 fontSize.setTextChars(new char[]{e.character});
\r
553 fontSize.setSelection(1);
\r
554 fontSize.forceFocus();
\r
563 * Creates the contents of fontStyleTable according to the selected font family
\r
564 * @param family Selected font family
\r
566 protected void selectFontFamily(String family) {
\r
567 String old = fontStyle.getText();
\r
568 String selection = null, optionalSelection = null;
\r
571 fontStyleTable.removeAll();
\r
573 if(familyIndex.indexOf(family) > -1) {
\r
574 for(int i = 0; i < fonts.get(family).size(); i++) {
\r
575 Font font = fonts.get(family).get(i);
\r
577 String name = font.getFontName(Locale.ROOT);
\r
579 // Style is "Regular", unless otherwise defined
\r
580 String style = "Regular";
\r
581 if(name.length() > family.length())
\r
582 style = name.substring(family.length() + 1);
\r
584 // If previous font was bold, try to conserve the style
\r
585 if(old.equals(style))
\r
587 else if((!old.isEmpty() && style.contains(old)) || optionalSelection == null)
\r
588 optionalSelection = style;
\r
590 TableItem item = new TableItem (fontStyleTable, SWT.NONE);
\r
591 item.setText (0, style);
\r
592 item.setData(font);
\r
594 // If the font is not symbolic, use the font in the created item
\r
595 if(font.canDisplay('a')) {
\r
596 FontData fontData = toSwtFontData(font, 10);
\r
597 org.eclipse.swt.graphics.Font swtFont = resourceManager.createFont(FontDescriptor.createFrom(fontData));
\r
598 item.setFont(swtFont);
\r
601 fontStyleTable.setItemCount(fonts.get(family).size());
\r
603 if(selection == null)
\r
604 selection = optionalSelection;
\r
606 fontStyle.setText(selection);
\r
607 fontStyle.setSelection(selection.length(), selection.length());
\r
609 setFontStyleTableWidth();
\r
614 * Set width for style table column. Width can change, if scroll bar appears
\r
616 protected void setFontStyleTableWidth() {
\r
617 Rectangle area = fontStyleTable.getClientArea();
\r
618 Point size = fontStyleTable.computeSize(SWT.DEFAULT, SWT.DEFAULT);
\r
619 ScrollBar vBar = fontStyleTable.getVerticalBar();
\r
621 if (area.height == 0 || size.y <= area.height) {
\r
622 Point vBarSize = vBar.getSize();
\r
623 width += vBarSize.x;
\r
625 fontStyleTable.getColumn(0).setWidth(width);
\r
630 * Builds SWT FontData from AWT font. Simple conversion.
\r
632 * @param font AWT font
\r
633 * @param height Height for the created data (or -1 if inherited directly from awt font, size matching not guaranteed)
\r
636 protected static FontData toSwtFontData(Font font, int height) {
\r
637 FontData fontData = new FontData();
\r
638 fontData.setName(font.getFontName());
\r
639 fontData.setStyle(font.getStyle());
\r
640 fontData.setHeight(height > 0 ? height : font.getSize());
\r
645 public void addFontModifiedListener(FontModifyListener listener) {
\r
646 modifyListeners.add(listener);
\r
649 public void removeFontModifiedListener(FontModifyListener listener) {
\r
650 modifyListeners.remove(listener);
\r
653 public List<FontModifyListener> getFontModifiedListener() {
\r
654 ArrayList<FontModifyListener> listeners = new ArrayList<FontModifyListener>(modifyListeners.size());
\r
655 for(Object l : modifyListeners.getListeners())
\r
656 listeners.add((FontModifyListener)l);
\r
661 * Called when some property of the font definiton has changed.
\r
662 * Calls font change listeners.
\r
664 protected void fontChanged() {
\r
665 Font font = getAWTFont();
\r
669 style |= (font.getFontName(Locale.ROOT).contains("Bold") ? SWT.BOLD : 0);
\r
670 style |= (font.getFontName(Locale.ROOT).contains("Italic") ? SWT.ITALIC : 0);
\r
671 FontData fontData = new FontData(font.getFamily(Locale.ROOT), font.getSize(), style);
\r
673 Object[] listenersArray = modifyListeners.getListeners();
\r
674 for (int i = 0; i < listenersArray.length; i++) {
\r
675 ((FontModifyListener)listenersArray[i]).awtFontChanged(font);
\r
676 ((FontModifyListener)listenersArray[i]).swtFontDataChanged(fontData);
\r