GuiHelper.java
001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002  *
003  * Copyright 2000-2013,  Michael Meyling <mime@qedeq.org>.
004  *
005  * "Hilbert II" is free software; you can redistribute
006  * it and/or modify it under the terms of the GNU General Public
007  * License as published by the Free Software Foundation; either
008  * version 2 of the License, or (at your option) any later version.
009  *
010  * This program is distributed in the hope that it will be useful,
011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013  * GNU General Public License for more details.
014  */
015 
016 package org.qedeq.gui.se.util;
017 
018 import java.awt.Color;
019 import java.awt.Component;
020 import java.awt.Dimension;
021 import java.awt.FlowLayout;
022 import java.awt.Font;
023 import java.awt.Graphics;
024 import java.awt.GridLayout;
025 import java.awt.Rectangle;
026 import java.awt.Toolkit;
027 import java.awt.event.ActionListener;
028 import java.net.URL;
029 
030 import javax.swing.AbstractButton;
031 import javax.swing.BorderFactory;
032 import javax.swing.ImageIcon;
033 import javax.swing.JButton;
034 import javax.swing.JCheckBox;
035 import javax.swing.JComponent;
036 import javax.swing.JPanel;
037 import javax.swing.JRadioButton;
038 import javax.swing.JTable;
039 import javax.swing.JToggleButton;
040 import javax.swing.KeyStroke;
041 import javax.swing.LookAndFeel;
042 import javax.swing.UIManager;
043 import javax.swing.border.Border;
044 import javax.swing.plaf.FontUIResource;
045 import javax.swing.plaf.metal.DefaultMetalTheme;
046 import javax.swing.plaf.metal.MetalLookAndFeel;
047 import javax.swing.table.JTableHeader;
048 import javax.swing.table.TableCellRenderer;
049 import javax.swing.table.TableColumn;
050 import javax.swing.table.TableColumnModel;
051 import javax.swing.table.TableModel;
052 import javax.swing.text.BadLocationException;
053 import javax.swing.text.JTextComponent;
054 
055 import org.qedeq.base.io.IoUtility;
056 import org.qedeq.base.io.ResourceLoaderUtility;
057 import org.qedeq.base.io.StringOutput;
058 import org.qedeq.base.io.SubTextInput;
059 import org.qedeq.base.trace.Trace;
060 import org.qedeq.base.utility.StringUtility;
061 import org.qedeq.gui.se.main.GuiOptions;
062 
063 import com.jgoodies.looks.Options;
064 import com.jgoodies.looks.plastic.PlasticLookAndFeel;
065 
066 /**
067  * Various GUI utility methods.
068  *
069  @author Michael Meyling
070  */
071 public final class GuiHelper {
072 
073     /** This class. */
074     private static final Class CLASS = GuiHelper.class;
075 
076     /** Color for line highlighter. */
077     private static Color lineHighlighterBackgroundColor = new Color(232242,
078         254);
079 
080     /** Color for line highlighter and marked areas. */
081     private static Color markedAndHiglightedBackgroundColor = new Color(232,
082         242254128);
083 
084     /** Color for error text areas. */
085     private static Color errorTextBackgroundColor = new Color(180206255);
086 
087     /** Color for warning text areas. */
088     private static Color warningTextBackgroundColor = new Color(255255190);
089 
090     /** Width of empty border. */
091     private static final int DEFAULT_EMPTY_BORDER_PIXEL_X = 10;
092 
093     /** Width of empty border. */
094     private static final int DEFAULT_EMPTY_BORDER_PIXEL_Y = 10;
095 
096     /** Width of text box for search text. */
097     private static final int SEARCH_TEXT_BOX_WIDTH = 500;
098 
099     /** Contains Hex code for generic fallback 16x16 icon. */
100     private static final String UNKNOWN_STRING =
101         "47 49 46 38 37 61 10 00 10 00 C6 43 00 00 00 00 "
102         "07 02 02 0C 04 04 0E 04 04 10 05 05 14 06 06 15 "
103         "06 06 18 07 07 1E 09 09 21 0A 0A 26 0B 0B 2D 0D "
104         "0D 31 0E 0E 32 0F 0F 3D 12 12 3F 13 13 40 13 13 "
105         "44 14 14 46 15 15 49 15 15 4A 16 16 50 18 18 51 "
106         "18 18 52 18 18 68 1F 1F 6C 20 20 7A 24 24 7C 24 "
107         "24 8C 29 29 96 2C 2C 99 2D 2D 9B 2E 2E 9D 2E 2E "
108         "A0 2F 2F A3 30 30 A4 31 31 A7 31 31 B3 35 35 B6 "
109         "36 36 B8 36 36 BC 38 38 C4 3A 3A C9 3B 3B CC 3C "
110         "3C CD 3D 3D CE 3D 3D D0 3D 3D D1 3E 3E D5 3F 3F "
111         "D9 40 40 DB 41 41 E0 42 42 E3 43 43 E4 43 43 E9 "
112         "45 45 ED 46 46 EE 46 46 F1 47 47 F3 48 48 F4 48 "
113         "48 F7 49 49 F8 49 49 FA 4A 4A FB 4A 4A FC 4A 4A "
114         "FD 4B 4B FE 4B 4B FF FF FF FF FF FF FF FF FF FF "
115         "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF "
116         "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF "
117         "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF "
118         "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF "
119         "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF "
120         "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF "
121         "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF "
122         "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF "
123         "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF "
124         "FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF FF "
125         "FF FF FF FF FF FF FF FF FF FF FF FF FF 2C 00 00 "
126         "00 00 10 00 10 00 00 07 87 80 42 82 83 84 85 86 "
127         "87 88 89 85 3E 2E 23 22 30 83 36 26 1F 28 3A 83 "
128         "1C 0C 0E 0B 05 20 42 33 0F 0D 10 07 11 83 27 2B "
129         "82 15 06 3B 3B 24 38 42 29 01 87 17 09 37 84 2D "
130         "03 86 19 04 26 84 2A 0A 14 85 1B 02 25 82 40 42 "
131         "2F 08 16 85 33 03 18 83 3E 42 1A 00 32 85 35 12 "
132         "1E 42 3F D9 42 1D 13 35 85 3C 2C 34 85 34 2D 3D "
133         "85 31 00 19 42 D0 D0 19 00 31 85 39 21 2A 42 41 "
134         "F6 42 2A 21 39 84 F7 C5 83 C6 04 F9 53 54 28 10 "
135         "00 3B ";
136 
137     /** Generic ImageIcon. */
138     public static final ImageIcon UNKNOWN = new ImageIcon(StringUtility.hex2byte(UNKNOWN_STRING));
139 
140     /** Do we have a low screen resolution? */
141     public static final boolean IS_LOW_RESOLUTION
142         = Toolkit.getDefaultToolkit().getScreenSize().width <= 1000;
143 
144     /**
145      * Hidden constructor.
146      */
147     private GuiHelper() {
148         // nothing to do
149     }
150 
151     /**
152      * Configures the user interface; requests Swing settings and JGoodies Looks
153      * options from the launcher.
154      *
155      @param   options Set these options.
156      */
157     public static void configureUI(final GuiOptions options) {
158         UIManager.put("ClassLoader", CLASS.getClassLoader());
159 
160         Options.setDefaultIconSize(new Dimension(1818));
161         Options.setUseNarrowButtons(options.isUseNarrowButtons());
162         Options.setTabIconsEnabled(options.isTabIconsEnabled());
163         UIManager.put(Options.POPUP_DROP_SHADOW_ENABLED_KEY, options
164             .isPopupDropShadowEnabled());
165         // LATER m31 20100319: we make this now direct in QedeqPane, this line
166         // didn't help. Why?
167         // we want our disabled TextAreas to look same if not editable
168         UIManager.put("TextArea.disabledBackground", UIManager
169             .get("TextArea.background"));
170 
171         UIManager.put("ToolTip.font",
172             new FontUIResource("Lucida Sans Unicode", Font.PLAIN,
173             UIManager.getFont("ToolTip.font").getSize()));
174 
175            // Swing Settings
176         LookAndFeel selectedLaf = options.getSelectedLookAndFeel();
177         if (selectedLaf instanceof PlasticLookAndFeel) {
178             PlasticLookAndFeel.setPlasticTheme(options.getSelectedTheme());
179             PlasticLookAndFeel.setTabStyle(options.getPlasticTabStyle());
180             PlasticLookAndFeel.setHighContrastFocusColorsEnabled(options
181                 .isPlasticHighContrastFocusEnabled());
182         else if (selectedLaf.getClass() == MetalLookAndFeel.class) {
183             MetalLookAndFeel.setCurrentTheme(new DefaultMetalTheme());
184         }
185 
186         // Work around caching in MetalRadioButtonUI
187         JRadioButton radio = new JRadioButton();
188         radio.getUI().uninstallUI(radio);
189         JCheckBox checkBox = new JCheckBox();
190         checkBox.getUI().uninstallUI(checkBox);
191 
192         try {
193             UIManager.setLookAndFeel(selectedLaf);
194         catch (Exception e) {
195             Trace.trace(CLASS, "configureUI""Can't change L&F", e);
196         }
197 
198     }
199 
200     /**
201      * Creates a JButton configured for use in a JToolBar.
202      *
203      @param   iconName    Name of icon.
204      @param   toolTipText Tool tip text.
205      @return  Button.
206      */
207     public static AbstractButton createToolBarButton(final String iconName,
208             final String toolTipText) {
209         final JButton button = new JButton(GuiHelper.readImageIcon(iconName));
210         button.setToolTipText(toolTipText);
211         button.setFocusable(false);
212         return button;
213     }
214 
215     /**
216      * Creates a JButton configured for use in a JToolBar.
217      *
218      @param   iconName    Name of icon.
219      @param   toolTipText Tool tip text.
220      @param   action      Action for this button.
221      @param   keyStroke   Short cut key.
222      @return  Button.
223      */
224     public static AbstractButton createToolBarButton(final String iconName,
225             final String toolTipText, final ActionListener action,
226             final KeyStroke keyStroke) {
227         AbstractButton button = createToolBarButton(iconName, toolTipText);
228         button.registerKeyboardAction(action, keyStroke,
229             JComponent.WHEN_IN_FOCUSED_WINDOW);
230         return button;
231     }
232 
233     /**
234      * Creates a JToggleButton configured for use in a JToolBar.
235      *
236      @param   iconName    Name of icon.
237      @param   toolTipText Tool tip text.
238      @return  Radio button.
239      */
240     public static AbstractButton createToolBarRadioButton(
241             final String iconName, final String toolTipText) {
242         final JToggleButton button = new JToggleButton(GuiHelper
243             .readImageIcon(iconName));
244         button.setToolTipText(toolTipText);
245         button.setFocusable(false);
246         return button;
247     }
248 
249     /**
250      * Get an icon for given filename postfix.
251      *
252      @param   filename    Look for this icon.
253      @return  Icon.
254      */
255     public static ImageIcon readImageIcon(final String filename) {
256         final URL url = ResourceLoaderUtility.getResourceUrl("images/" + filename);
257         if (url == null) {
258             Trace.fatal(CLASS, "readImageIcon""image icon not found: images/" + filename,
259                 new RuntimeException());
260             return UNKNOWN;
261         }
262         return new ImageIcon(url);
263     }
264 
265     /**
266      * Get background color for highlighting the current line.
267      *
268      @return  Background color.
269      */
270     public static Color getLineHighlighterBackgroundColor() {
271         return lineHighlighterBackgroundColor;
272     }
273 
274     /**
275      * Get background color for error text areas.
276      *
277      @return Background color.
278      */
279     public static Color getErrorTextBackgroundColor() {
280         return errorTextBackgroundColor;
281     }
282 
283     /**
284      * Get background color for warning text areas.
285      *
286      @return Background color.
287      */
288     public static Color getWarningTextBackgroundColor() {
289         return warningTextBackgroundColor;
290     }
291 
292     /**
293      * Get background color for marked and highlightted text areas.
294      *
295      @return Background color.
296      */
297     public static Color getCurrentAndMarkedBackgroundColor() {
298         return markedAndHiglightedBackgroundColor;
299     }
300 
301     /**
302      * Paint current line background area with
303      {@link #getLineHighlighterBackgroundColor()}.
304      *
305      @param   g   Graphics to use.
306      @param   c   Text component to work on.
307      @param   col Color to work with.
308      */
309     public static void paintCurrentLineBackground(final Graphics g,
310             final JTextComponent c, final Color col) {
311         // if something is selected we don't highlight
312         if (c.getSelectionStart() != c.getSelectionEnd()) {
313             return;
314         }
315         Rectangle r;
316         try {
317             r = c.modelToView(c.getCaretPosition());
318         catch (BadLocationException couldNotHappen) {
319             throw new RuntimeException(couldNotHappen);
320         }
321         g.setColor(col);
322         g.fillRect(0, r.y, c.getWidth(), r.height);
323     }
324 
325     /**
326      * Adds boarder space and floats panel to the right.
327      *
328      @param   panel   Panel to decorate.
329      @return  Panel with more decorations.
330      */
331     public static JComponent addSpaceAndAlignRight(final JPanel panel) {
332         JPanel withSpace = new JPanel();
333         withSpace.add(panel);
334         JPanel alignRight = new JPanel();
335         alignRight.setLayout(new FlowLayout(FlowLayout.RIGHT));
336         alignRight.add(withSpace);
337         return alignRight;
338     }
339 
340     /**
341      * Adds boarder space and floats panel to the left.
342      *
343      @param   panel   Panel to decorate.
344      @return  Panel with more decorations.
345      */
346     public static JComponent alignLeft(final JPanel panel) {
347         JPanel withSpace = new JPanel();
348         withSpace.add(panel);
349         JPanel alignLeft = new JPanel();
350         alignLeft.setLayout(new FlowLayout(FlowLayout.LEFT));
351         alignLeft.add(withSpace);
352         return alignLeft;
353     }
354 
355     /**
356      * Width of horizontal empty border.
357      *
358      @return  Return horizontal empty boarder pixel distance.
359      */
360     public static int getEmptyBoderPixelsX() {
361         return DEFAULT_EMPTY_BORDER_PIXEL_X;
362     }
363 
364     /**
365      * Width of vertical empty border.
366      *
367      @return  Return vertical empty boarder pixel distance.
368      */
369     public static int getEmptyBorderPixelsY() {
370         return DEFAULT_EMPTY_BORDER_PIXEL_Y;
371     }
372 
373     /**
374      * A border that puts extra pixels at the sides and bottom of each pane.
375      *
376      @return  Border with extra space.
377      */
378     public static Border getEmptyBorder() {
379         return BorderFactory.createEmptyBorder(GuiHelper
380             .getEmptyBorderPixelsY(), GuiHelper.getEmptyBoderPixelsX(),
381             GuiHelper.getEmptyBorderPixelsY(), GuiHelper.getEmptyBoderPixelsX());
382     }
383 
384     /**
385      * A border that puts extra pixels at the sides and bottom of each pane.
386      *
387      @return  Border with extra space.
388      */
389     public static Border getEmptyBorderStackable() {
390         return BorderFactory.createEmptyBorder(GuiHelper
391             .getEmptyBorderPixelsY() 2, GuiHelper.getEmptyBoderPixelsX(),
392             GuiHelper.getEmptyBorderPixelsY() 2, GuiHelper.getEmptyBoderPixelsX());
393     }
394 
395     /**
396      * Calculate table column width according to contents. See
397      * <a href="http://www.chka.de/swing/table/cell-sizes.html">cell-sizes.html</a>
398      * (calculating initial column widths based on contents).
399      *
400      @author  Christian Kaufhold
401      *
402      @param   table   Calculate width for this table and update column width values.
403      */
404     public static void calcColumnWidths(final JTable table) {
405         final JTableHeader header = table.getTableHeader();
406         TableCellRenderer defaultHeaderRenderer = null;
407         if (header != null) {
408             defaultHeaderRenderer = header.getDefaultRenderer();
409         }
410         TableColumnModel columns = table.getColumnModel();
411         TableModel data = table.getModel();
412         int margin = columns.getColumnMargin()// only JDK1.3
413         int rowCount = data.getRowCount();
414         int totalWidth = 0;
415         for (int i = columns.getColumnCount() 1; i >= 0; --i) {
416             final TableColumn column = columns.getColumn(i);
417             final int columnIndex = column.getModelIndex();
418             int width = -1;
419             TableCellRenderer h = column.getHeaderRenderer();
420             if (h == null) {
421                 h = defaultHeaderRenderer;
422             }
423             if (h != null) { // Not explicitly impossible
424                 Component c = h.getTableCellRendererComponent(table, column.getHeaderValue(),
425                     false, false, -1, i);
426                 width = c.getPreferredSize().width;
427             }
428             for (int row = rowCount - 1; row >= 0; --row) {
429                 TableCellRenderer r = table.getCellRenderer(row, i);
430                 Component c = r.getTableCellRendererComponent(table, data.getValueAt(row,
431                     columnIndex), false, false, row, i);
432                 width = Math.max(width, c.getPreferredSize().width);
433             }
434             if (width >= 0) {
435                 column.setPreferredWidth(width + margin);
436             else {
437                 // ???
438             }
439             totalWidth += column.getPreferredWidth();
440         }
441     }
442 
443     /**
444      * Width for text search box.
445      *
446      @return  Width for text search box.
447      */
448     public static int getSearchTextBoxWidth() {
449         return SEARCH_TEXT_BOX_WIDTH;
450     }
451 
452     public static JComponent addSpaceAndTitle(final JPanel panel, final String title) {
453         JPanel withSpace = new JPanel();
454         withSpace.setBorder(getEmptyBorderStackable());
455         withSpace.add(panel);
456         withSpace.setLayout(new GridLayout(01));
457         JPanel withTitle = new JPanel();
458         withTitle.setBorder(BorderFactory.createTitledBorder(title));
459         withTitle.add(withSpace);
460         withTitle.setLayout(new GridLayout(01));
461         return withTitle;
462     }
463 
464     public static String getToolTipText(final String text) {
465 //        System.out.println("original:   " + text);
466         final StringOutput output = new StringOutput();
467         final SubTextInput input =  new SubTextInput(text);
468         int sw = Toolkit.getDefaultToolkit().getScreenSize().width;
469         if (sw >= 1200) {
470             output.setColumns(160);
471         else if (sw >= 1000) {
472             output.setColumns(120);
473         else {
474             output.setColumns(100);
475         }
476 
477         while (!input.isEmpty()) {
478             String token = input.readStringTilWhitespace();
479             output.addToken(token);
480             StringBuffer ws = new StringBuffer();
481             while (Character.isWhitespace(input.getChar())) {
482                 ws = ws.append((charinput.read());
483             }
484             output.addWs(ws.toString());
485         }
486 //        System.out.println("transformed:" + output.toString());
487         IoUtility.close(input);
488         return  "<html>" + StringUtility.replace(
489             StringUtility.replace(StringUtility.escapeXml(output.toString())"&apos;""\'"),
490             "\n""<br>""</html>";
491     }
492 }