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(232, 242,
078 254);
079
080 /** Color for line highlighter and marked areas. */
081 private static Color markedAndHiglightedBackgroundColor = new Color(232,
082 242, 254, 128);
083
084 /** Color for error text areas. */
085 private static Color errorTextBackgroundColor = new Color(180, 206, 255);
086
087 /** Color for warning text areas. */
088 private static Color warningTextBackgroundColor = new Color(255, 255, 190);
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(18, 18));
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(0, 1));
457 JPanel withTitle = new JPanel();
458 withTitle.setBorder(BorderFactory.createTitledBorder(title));
459 withTitle.add(withSpace);
460 withTitle.setLayout(new GridLayout(0, 1));
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((char) input.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()), "'", "\'"),
490 "\n", "<br>") + "</html>";
491 }
492 }
|