• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011-2015 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 package com.badlogic.gdx.tools.hiero;
18 
19 import static org.lwjgl.opengl.GL11.*;
20 
21 import java.awt.BorderLayout;
22 import java.awt.Component;
23 import java.awt.Container;
24 import java.awt.Dimension;
25 import java.awt.EventQueue;
26 import java.awt.FileDialog;
27 import java.awt.FlowLayout;
28 import java.awt.Font;
29 import java.awt.Frame;
30 import java.awt.GraphicsEnvironment;
31 import java.awt.GridBagConstraints;
32 import java.awt.GridBagLayout;
33 import java.awt.Insets;
34 import java.awt.LayoutManager;
35 import java.awt.event.ActionEvent;
36 import java.awt.event.ActionListener;
37 import java.awt.event.KeyAdapter;
38 import java.awt.event.KeyEvent;
39 import java.awt.event.MouseAdapter;
40 import java.awt.event.MouseEvent;
41 import java.awt.event.WindowAdapter;
42 import java.awt.event.WindowEvent;
43 import java.awt.image.BufferedImage;
44 import java.io.File;
45 import java.io.IOException;
46 import java.util.ArrayList;
47 import java.util.Iterator;
48 import java.util.List;
49 import java.util.prefs.Preferences;
50 
51 import javax.swing.BorderFactory;
52 import javax.swing.ButtonGroup;
53 import javax.swing.DefaultComboBoxModel;
54 import javax.swing.Icon;
55 import javax.swing.ImageIcon;
56 import javax.swing.JButton;
57 import javax.swing.JCheckBox;
58 import javax.swing.JColorChooser;
59 import javax.swing.JComboBox;
60 import javax.swing.JFormattedTextField;
61 import javax.swing.JFrame;
62 import javax.swing.JLabel;
63 import javax.swing.JList;
64 import javax.swing.JMenu;
65 import javax.swing.JMenuBar;
66 import javax.swing.JMenuItem;
67 import javax.swing.JPanel;
68 import javax.swing.JRadioButton;
69 import javax.swing.JScrollPane;
70 import javax.swing.JSpinner;
71 import javax.swing.JTextField;
72 import javax.swing.JTextPane;
73 import javax.swing.JWindow;
74 import javax.swing.KeyStroke;
75 import javax.swing.ScrollPaneConstants;
76 import javax.swing.SpinnerModel;
77 import javax.swing.SpinnerNumberModel;
78 import javax.swing.SwingUtilities;
79 import javax.swing.border.EmptyBorder;
80 import javax.swing.event.ChangeEvent;
81 import javax.swing.event.ChangeListener;
82 import javax.swing.event.DocumentEvent;
83 import javax.swing.event.DocumentListener;
84 import javax.swing.event.ListSelectionEvent;
85 import javax.swing.event.ListSelectionListener;
86 
87 import org.lwjgl.opengl.GL11;
88 
89 import com.badlogic.gdx.ApplicationAdapter;
90 import com.badlogic.gdx.Gdx;
91 import com.badlogic.gdx.backends.lwjgl.LwjglCanvas;
92 import com.badlogic.gdx.graphics.Color;
93 import com.badlogic.gdx.graphics.Texture;
94 import com.badlogic.gdx.graphics.g2d.BitmapFont;
95 import com.badlogic.gdx.graphics.g2d.SpriteBatch;
96 import com.badlogic.gdx.tools.hiero.unicodefont.GlyphPage;
97 import com.badlogic.gdx.tools.hiero.unicodefont.UnicodeFont;
98 import com.badlogic.gdx.tools.hiero.unicodefont.UnicodeFont.RenderType;
99 import com.badlogic.gdx.tools.hiero.unicodefont.effects.ColorEffect;
100 import com.badlogic.gdx.tools.hiero.unicodefont.effects.ConfigurableEffect;
101 import com.badlogic.gdx.tools.hiero.unicodefont.effects.ConfigurableEffect.Value;
102 import com.badlogic.gdx.utils.StringBuilder;
103 import com.badlogic.gdx.tools.hiero.unicodefont.effects.DistanceFieldEffect;
104 import com.badlogic.gdx.tools.hiero.unicodefont.effects.EffectUtil;
105 import com.badlogic.gdx.tools.hiero.unicodefont.effects.GradientEffect;
106 import com.badlogic.gdx.tools.hiero.unicodefont.effects.OutlineEffect;
107 import com.badlogic.gdx.tools.hiero.unicodefont.effects.OutlineWobbleEffect;
108 import com.badlogic.gdx.tools.hiero.unicodefont.effects.OutlineZigzagEffect;
109 import com.badlogic.gdx.tools.hiero.unicodefont.effects.ShadowEffect;
110 
111 /** A tool to visualize settings for {@link UnicodeFont} and to export BMFont files for use with {@link BitmapFont}.
112  * <p>
113  * This tool is quite old. It was originally written for Slick, then ported to libgdx. It has had a few different contributors
114  * over the years, the FreeType rendering was tacked on and in general the code isn't great. It does its job though!
115  * @author Nathan Sweet */
116 public class Hiero extends JFrame {
117 	UnicodeFont unicodeFont;
118 	Color renderingBackgroundColor = Color.BLACK;
119 	List<EffectPanel> effectPanels = new ArrayList<EffectPanel>();
120 	Preferences prefs;
121 	ColorEffect colorEffect;
122 	boolean batchMode = false;
123 
124 	JScrollPane appliedEffectsScroll;
125 	JPanel appliedEffectsPanel;
126 	JButton addEffectButton;
127 	JTextPane sampleTextPane;
128 	JSpinner padAdvanceXSpinner;
129 	JList effectsList;
130 	LwjglCanvas rendererCanvas;
131 	JPanel gamePanel;
132 	JTextField fontFileText;
133 	JRadioButton fontFileRadio;
134 	JRadioButton systemFontRadio;
135 	JSpinner padBottomSpinner;
136 	JSpinner padLeftSpinner;
137 	JSpinner padRightSpinner;
138 	JSpinner padTopSpinner;
139 	JList fontList;
140 	JSpinner fontSizeSpinner;
141 	JSpinner gammaSpinner;
142 	DefaultComboBoxModel fontListModel;
143 	JLabel backgroundColorLabel;
144 	JButton browseButton;
145 	JSpinner padAdvanceYSpinner;
146 	JCheckBox italicCheckBox;
147 	JCheckBox boldCheckBox;
148 	JCheckBox monoCheckBox;
149 	JRadioButton javaRadio;
150 	JRadioButton nativeRadio;
151 	JRadioButton freeTypeRadio;
152 	JLabel glyphsTotalLabel;
153 	JLabel glyphPagesTotalLabel;
154 	JComboBox glyphPageHeightCombo;
155 	JComboBox glyphPageWidthCombo;
156 	JComboBox glyphPageCombo;
157 	JPanel glyphCachePanel;
158 	JRadioButton glyphCacheRadio;
159 	JRadioButton sampleTextRadio;
160 	DefaultComboBoxModel glyphPageComboModel;
161 	JButton resetCacheButton;
162 	JButton sampleAsciiButton;
163 	JButton sampleNeheButton;
164 	JButton sampleExtendedButton;
165 	DefaultComboBoxModel effectsListModel;
166 	JMenuItem openMenuItem;
167 	JMenuItem saveMenuItem;
168 	JMenuItem exitMenuItem;
169 	JMenuItem saveBMFontMenuItem;
170 	File saveBmFontFile;
171 	String lastSaveFilename = "", lastSaveBMFilename = "", lastOpenFilename = "";
172 	JPanel effectsPanel;
173 	JScrollPane effectsScroll;
174 	JPanel unicodePanel, bitmapPanel;
175 
Hiero(String[] args)176 	public Hiero (String[] args) {
177 		super("Hiero v5 - Bitmap Font Tool");
178 		Splash splash = new Splash(this, "/splash.jpg", 2000);
179 		initialize();
180 		splash.close();
181 
182 		rendererCanvas = new LwjglCanvas(new Renderer());
183 		gamePanel.add(rendererCanvas.getCanvas());
184 
185 		prefs = Preferences.userNodeForPackage(Hiero.class);
186 		java.awt.Color backgroundColor = EffectUtil.fromString(prefs.get("background", "000000"));
187 		backgroundColorLabel.setIcon(getColorIcon(backgroundColor));
188 		renderingBackgroundColor = new Color(backgroundColor.getRed() / 255f, backgroundColor.getGreen() / 255f,
189 			backgroundColor.getBlue() / 255f, 1);
190 		fontList.setSelectedValue(prefs.get("system.font", "Arial"), true);
191 		fontFileText.setText(prefs.get("font.file", ""));
192 
193 		java.awt.Color foregroundColor = EffectUtil.fromString(prefs.get("foreground", "ffffff"));
194 		colorEffect = new ColorEffect();
195 		colorEffect.setColor(foregroundColor);
196 		effectsListModel.addElement(colorEffect);
197 		effectsListModel.addElement(new GradientEffect());
198 		effectsListModel.addElement(new OutlineEffect());
199 		effectsListModel.addElement(new OutlineWobbleEffect());
200 		effectsListModel.addElement(new OutlineZigzagEffect());
201 		effectsListModel.addElement(new ShadowEffect());
202 		effectsListModel.addElement(new DistanceFieldEffect());
203 		new EffectPanel(colorEffect);
204 
205 		parseArgs(args);
206 
207 		addWindowListener(new WindowAdapter() {
208 			public void windowClosed (WindowEvent event) {
209 				System.exit(0);
210 				// Gdx.app.quit();
211 			}
212 		});
213 
214 		updateFontSelector();
215 		setVisible(true);
216 	}
217 
initialize()218 	void initialize () {
219 		initializeComponents();
220 		initializeMenus();
221 		initializeEvents();
222 
223 		setSize(1024, 600);
224 		setLocationRelativeTo(null);
225 		setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
226 	}
227 
parseArgs(String[] args)228 	private void parseArgs (String[] args) {
229 		float scale = 1f;
230 
231 		for (int i = 0; i < args.length; i++) {
232 			final String param = args[i];
233 			final boolean more = i < args.length - 1;
234 
235 			if (param.equals("-b") || param.equals("--batch")) {
236 				batchMode = true;
237 			} else if (more && (param.equals("-s") || param.equals("--scale"))) {
238 				scale = Float.parseFloat(args[++i]);
239 			} else if (more && (param.equals("-i") || param.equals("--input"))) {
240 				File f = new File(args[++i]);
241 				open(f);
242 				fontFileRadio.setText("");
243 				updateFont();
244 			} else if (more && (param.equals("-o") || param.equals("--output"))) {
245 				File f = new File(args[++i]);
246 				saveBm(f);
247 			} else {
248 				System.err.println("Unknown parameter: " + param);
249 				exit(3);
250 			}
251 		}
252 
253 		fontSizeSpinner.setValue((int)(0.5f + Math.max(4, scale * ((Integer)fontSizeSpinner.getValue()))));
254 	}
255 
256 	void updateFontSelector () {
257 		final boolean useFile = fontFileRadio.isSelected();
258 		fontList.setEnabled(!useFile);
259 		fontFileText.setEnabled(useFile);
260 		browseButton.setEnabled(useFile);
261 	}
262 
263 	void updateFont () {
264 		int fontSize = ((Integer)fontSizeSpinner.getValue()).intValue();
265 
266 		File file = null;
267 		if (fontFileRadio.isSelected()) {
268 			file = new File(fontFileText.getText());
269 			if (!file.exists() || !file.isFile()) file = null;
270 		}
271 
272 		boolean isFreeType = freeTypeRadio.isSelected();
273 		boolean isNative = nativeRadio.isSelected();
274 		boolean isJava = javaRadio.isSelected();
275 		addEffectButton.setVisible(isJava);
276 		effectsScroll.setVisible(isJava);
277 		appliedEffectsScroll.setVisible(isJava);
278 		boldCheckBox.setEnabled(!isFreeType);
279 		italicCheckBox.setEnabled(!isFreeType);
280 		bitmapPanel.setVisible(isFreeType);
281 		unicodePanel.setVisible(!isFreeType);
282 		updateFontSelector();
283 
284 		UnicodeFont unicodeFont = null;
285 		if (file != null) {
286 			// Load from file.
287 			try {
288 				unicodeFont = new UnicodeFont(fontFileText.getText(), fontSize, boldCheckBox.isSelected(),
289 					italicCheckBox.isSelected());
290 			} catch (Throwable ex) {
291 				ex.printStackTrace();
292 				fontFileRadio.setSelected(false);
293 			}
294 		}
295 
296 		if (unicodeFont == null) {
297 			// Load from java.awt.Font (kerning not available!).
298 			unicodeFont = new UnicodeFont(Font.decode((String)fontList.getSelectedValue()), fontSize, boldCheckBox.isSelected(),
299 				italicCheckBox.isSelected());
300 		}
301 
302 		unicodeFont.setMono(monoCheckBox.isSelected());
303 		unicodeFont.setGamma(((Number)gammaSpinner.getValue()).floatValue());
304 		unicodeFont.setPaddingTop(((Number)padTopSpinner.getValue()).intValue());
305 		unicodeFont.setPaddingRight(((Number)padRightSpinner.getValue()).intValue());
306 		unicodeFont.setPaddingBottom(((Number)padBottomSpinner.getValue()).intValue());
307 		unicodeFont.setPaddingLeft(((Number)padLeftSpinner.getValue()).intValue());
308 		unicodeFont.setPaddingAdvanceX(((Number)padAdvanceXSpinner.getValue()).intValue());
309 		unicodeFont.setPaddingAdvanceY(((Number)padAdvanceYSpinner.getValue()).intValue());
310 		unicodeFont.setGlyphPageWidth(((Number)glyphPageWidthCombo.getSelectedItem()).intValue());
311 		unicodeFont.setGlyphPageHeight(((Number)glyphPageHeightCombo.getSelectedItem()).intValue());
312 		if (nativeRadio.isSelected())
313 			unicodeFont.setRenderType(RenderType.Native);
314 		else if (freeTypeRadio.isSelected())
315 			unicodeFont.setRenderType(RenderType.FreeType);
316 		else
317 			unicodeFont.setRenderType(RenderType.Java);
318 
319 		for (Iterator iter = effectPanels.iterator(); iter.hasNext();) {
320 			EffectPanel panel = (EffectPanel)iter.next();
321 			unicodeFont.getEffects().add(panel.getEffect());
322 		}
323 
324 		int size = sampleTextPane.getFont().getSize();
325 		if (size < 14) size = 14;
326 		sampleTextPane.setFont(unicodeFont.getFont().deriveFont((float)size));
327 
328 		if (this.unicodeFont != null) this.unicodeFont.dispose();
329 		this.unicodeFont = unicodeFont;
330 
331 		updateFontSelector();
332 	}
333 
334 	void saveBm (File file) {
335 		saveBmFontFile = file;
336 	}
337 
338 	void save (File file) throws IOException {
339 		HieroSettings settings = new HieroSettings();
340 		settings.setFontName((String)fontList.getSelectedValue());
341 		settings.setFontSize(((Integer)fontSizeSpinner.getValue()).intValue());
342 		settings.setFont2File(fontFileText.getText());
343 		settings.setFont2Active(fontFileRadio.isSelected());
344 		settings.setBold(boldCheckBox.isSelected());
345 		settings.setItalic(italicCheckBox.isSelected());
346 		settings.setMono(monoCheckBox.isSelected());
347 		settings.setGamma(((Number)gammaSpinner.getValue()).floatValue());
348 		settings.setPaddingTop(((Number)padTopSpinner.getValue()).intValue());
349 		settings.setPaddingRight(((Number)padRightSpinner.getValue()).intValue());
350 		settings.setPaddingBottom(((Number)padBottomSpinner.getValue()).intValue());
351 		settings.setPaddingLeft(((Number)padLeftSpinner.getValue()).intValue());
352 		settings.setPaddingAdvanceX(((Number)padAdvanceXSpinner.getValue()).intValue());
353 		settings.setPaddingAdvanceY(((Number)padAdvanceYSpinner.getValue()).intValue());
354 		settings.setGlyphPageWidth(((Number)glyphPageWidthCombo.getSelectedItem()).intValue());
355 		settings.setGlyphPageHeight(((Number)glyphPageHeightCombo.getSelectedItem()).intValue());
356 		settings.setGlyphText(sampleTextPane.getText());
357 		for (Iterator iter = effectPanels.iterator(); iter.hasNext();) {
358 			EffectPanel panel = (EffectPanel)iter.next();
359 			settings.getEffects().add(panel.getEffect());
360 		}
361 		settings.save(file);
362 	}
363 
364 	void open (File file) {
365 		EffectPanel[] panels = (EffectPanel[])effectPanels.toArray(new EffectPanel[effectPanels.size()]);
366 		for (int i = 0; i < panels.length; i++)
367 			panels[i].remove();
368 
369 		HieroSettings settings = new HieroSettings(file.getAbsolutePath());
370 		fontList.setSelectedValue(settings.getFontName(), true);
371 		fontSizeSpinner.setValue(new Integer(settings.getFontSize()));
372 		boldCheckBox.setSelected(settings.isBold());
373 		italicCheckBox.setSelected(settings.isItalic());
374 		monoCheckBox.setSelected(settings.isMono());
375 		gammaSpinner.setValue(new Float(settings.getGamma()));
376 		padTopSpinner.setValue(new Integer(settings.getPaddingTop()));
377 		padRightSpinner.setValue(new Integer(settings.getPaddingRight()));
378 		padBottomSpinner.setValue(new Integer(settings.getPaddingBottom()));
379 		padLeftSpinner.setValue(new Integer(settings.getPaddingLeft()));
380 		padAdvanceXSpinner.setValue(new Integer(settings.getPaddingAdvanceX()));
381 		padAdvanceYSpinner.setValue(new Integer(settings.getPaddingAdvanceY()));
382 		glyphPageWidthCombo.setSelectedItem(new Integer(settings.getGlyphPageWidth()));
383 		glyphPageHeightCombo.setSelectedItem(new Integer(settings.getGlyphPageHeight()));
384 		String gt = settings.getGlyphText();
385 		if (gt.length() > 0) {
386 			sampleTextPane.setText(settings.getGlyphText());
387 		}
388 
389 		final String font2 = settings.getFont2File();
390 		if (font2.length() > 0)
391 			fontFileText.setText(font2);
392 		else
393 			fontFileText.setText(prefs.get("font.file", ""));
394 
395 		fontFileRadio.setSelected(settings.isFont2Active());
396 		systemFontRadio.setSelected(!settings.isFont2Active());
397 
398 		for (Iterator iter = settings.getEffects().iterator(); iter.hasNext();) {
399 			ConfigurableEffect settingsEffect = (ConfigurableEffect)iter.next();
400 			for (int i = 0, n = effectsListModel.getSize(); i < n; i++) {
401 				ConfigurableEffect effect = (ConfigurableEffect)effectsListModel.getElementAt(i);
402 				if (effect.getClass() == settingsEffect.getClass()) {
403 					effect.setValues(settingsEffect.getValues());
404 					new EffectPanel(effect);
405 					break;
406 				}
407 			}
408 		}
409 
410 		updateFont();
411 	}
412 
413 	void exit (final int exitCode) {
414 		rendererCanvas.stop();
415 		EventQueue.invokeLater(new Runnable() {
416 			public void run () {
417 				System.exit(exitCode);
418 			}
419 		});
420 	}
421 
422 	private void initializeEvents () {
423 		fontList.addListSelectionListener(new ListSelectionListener() {
424 			public void valueChanged (ListSelectionEvent evt) {
425 				if (evt.getValueIsAdjusting()) return;
426 				prefs.put("system.font", (String)fontList.getSelectedValue());
427 				updateFont();
428 			}
429 		});
430 
431 		class FontUpdateListener implements ChangeListener, ActionListener {
432 			public void stateChanged (ChangeEvent evt) {
433 				updateFont();
434 			}
435 
436 			public void actionPerformed (ActionEvent evt) {
437 				updateFont();
438 			}
439 
440 			public void addSpinners (JSpinner[] spinners) {
441 				for (int i = 0; i < spinners.length; i++) {
442 					final JSpinner spinner = spinners[i];
443 					spinner.addChangeListener(this);
444 					((JSpinner.DefaultEditor)spinner.getEditor()).getTextField().addKeyListener(new KeyAdapter() {
445 						String lastText;
446 
447 						public void keyReleased (KeyEvent evt) {
448 							JFormattedTextField textField = ((JSpinner.DefaultEditor)spinner.getEditor()).getTextField();
449 							String text = textField.getText();
450 							if (text.length() == 0) return;
451 							if (text.equals(lastText)) return;
452 							lastText = text;
453 							int caretPosition = textField.getCaretPosition();
454 							try {
455 								spinner.setValue(Integer.valueOf(text));
456 							} catch (NumberFormatException ex) {
457 							}
458 							textField.setCaretPosition(caretPosition);
459 						}
460 					});
461 				}
462 			}
463 		}
464 		FontUpdateListener listener = new FontUpdateListener();
465 
466 		listener.addSpinners(new JSpinner[] {padTopSpinner, padRightSpinner, padBottomSpinner, padLeftSpinner, padAdvanceXSpinner,
467 			padAdvanceYSpinner});
468 		fontSizeSpinner.addChangeListener(listener);
469 		gammaSpinner.addChangeListener(listener);
470 
471 		glyphPageWidthCombo.addActionListener(listener);
472 		glyphPageHeightCombo.addActionListener(listener);
473 		boldCheckBox.addActionListener(listener);
474 		italicCheckBox.addActionListener(listener);
475 		monoCheckBox.addActionListener(listener);
476 		resetCacheButton.addActionListener(listener);
477 		javaRadio.addActionListener(listener);
478 		nativeRadio.addActionListener(listener);
479 		freeTypeRadio.addActionListener(listener);
480 
481 		sampleTextRadio.addActionListener(new ActionListener() {
482 			public void actionPerformed (ActionEvent evt) {
483 				glyphCachePanel.setVisible(false);
484 			}
485 		});
486 		glyphCacheRadio.addActionListener(new ActionListener() {
487 			public void actionPerformed (ActionEvent evt) {
488 				glyphCachePanel.setVisible(true);
489 			}
490 		});
491 
492 		fontFileText.getDocument().addDocumentListener(new DocumentListener() {
493 			public void removeUpdate (DocumentEvent evt) {
494 				changed();
495 			}
496 
497 			public void insertUpdate (DocumentEvent evt) {
498 				changed();
499 			}
500 
501 			public void changedUpdate (DocumentEvent evt) {
502 				changed();
503 			}
504 
505 			private void changed () {
506 				File file = new File(fontFileText.getText());
507 				if (fontList.isEnabled() && (!file.exists() || !file.isFile())) return;
508 				prefs.put("font.file", fontFileText.getText());
509 				updateFont();
510 			}
511 		});
512 
513 		final ActionListener al = new ActionListener() {
514 			public void actionPerformed (ActionEvent evt) {
515 				updateFontSelector();
516 				updateFont();
517 			}
518 		};
519 
520 		systemFontRadio.addActionListener(al);
521 		fontFileRadio.addActionListener(al);
522 
523 		browseButton.addActionListener(new ActionListener() {
524 			public void actionPerformed (ActionEvent evt) {
525 				FileDialog dialog = new FileDialog(Hiero.this, "Choose TrueType font file", FileDialog.LOAD);
526 				dialog.setLocationRelativeTo(null);
527 				dialog.setFile("*.ttf");
528 				dialog.setDirectory(prefs.get("dir.font", ""));
529 				dialog.setVisible(true);
530 				if (dialog.getDirectory() != null) {
531 					prefs.put("dir.font", dialog.getDirectory());
532 				}
533 
534 				String fileName = dialog.getFile();
535 				if (fileName == null) return;
536 				fontFileText.setText(new File(dialog.getDirectory(), fileName).getAbsolutePath());
537 			}
538 		});
539 
540 		backgroundColorLabel.addMouseListener(new MouseAdapter() {
541 			public void mouseClicked (MouseEvent evt) {
542 				java.awt.Color color = JColorChooser.showDialog(null, "Choose a background color",
543 					EffectUtil.fromString(prefs.get("background", "000000")));
544 				if (color == null) return;
545 				renderingBackgroundColor = new Color(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, 1);
546 				backgroundColorLabel.setIcon(getColorIcon(color));
547 				prefs.put("background", EffectUtil.toString(color));
548 			}
549 		});
550 
551 		effectsList.addListSelectionListener(new ListSelectionListener() {
552 			public void valueChanged (ListSelectionEvent evt) {
553 				ConfigurableEffect selectedEffect = (ConfigurableEffect)effectsList.getSelectedValue();
554 				boolean enabled = selectedEffect != null;
555 				for (Iterator iter = effectPanels.iterator(); iter.hasNext();) {
556 					ConfigurableEffect effect = ((EffectPanel)iter.next()).getEffect();
557 					if (effect == selectedEffect) {
558 						enabled = false;
559 						break;
560 					}
561 				}
562 				addEffectButton.setEnabled(enabled);
563 			}
564 		});
565 
566 		effectsList.addMouseListener(new MouseAdapter() {
567 			public void mouseClicked (MouseEvent evt) {
568 				if (evt.getClickCount() == 2 && addEffectButton.isEnabled()) addEffectButton.doClick();
569 			}
570 		});
571 
572 		addEffectButton.addActionListener(new ActionListener() {
573 			public void actionPerformed (ActionEvent evt) {
574 				new EffectPanel((ConfigurableEffect)effectsList.getSelectedValue());
575 			}
576 		});
577 
578 		openMenuItem.addActionListener(new ActionListener() {
579 			public void actionPerformed (ActionEvent evt) {
580 				FileDialog dialog = new FileDialog(Hiero.this, "Open Hiero settings file", FileDialog.LOAD);
581 				dialog.setLocationRelativeTo(null);
582 				dialog.setFile("*.hiero");
583 				dialog.setDirectory(prefs.get("dir.open", ""));
584 				dialog.setVisible(true);
585 				if (dialog.getDirectory() != null) {
586 					prefs.put("dir.open", dialog.getDirectory());
587 				}
588 
589 				String fileName = dialog.getFile();
590 				if (fileName == null) return;
591 				lastOpenFilename = fileName;
592 				open(new File(dialog.getDirectory(), fileName));
593 			}
594 		});
595 
596 		saveMenuItem.addActionListener(new ActionListener() {
597 			public void actionPerformed (ActionEvent evt) {
598 				FileDialog dialog = new FileDialog(Hiero.this, "Save Hiero settings file", FileDialog.SAVE);
599 				dialog.setLocationRelativeTo(null);
600 				dialog.setFile("*.hiero");
601 				dialog.setDirectory(prefs.get("dir.save", ""));
602 
603 				if (lastSaveFilename.length() > 0) {
604 					dialog.setFile(lastSaveFilename);
605 				} else if (lastOpenFilename.length() > 0) {
606 					dialog.setFile(lastOpenFilename);
607 				}
608 
609 				dialog.setVisible(true);
610 
611 				if (dialog.getDirectory() != null) {
612 					prefs.put("dir.save", dialog.getDirectory());
613 				}
614 
615 				String fileName = dialog.getFile();
616 				if (fileName == null) return;
617 				if (!fileName.endsWith(".hiero")) fileName += ".hiero";
618 				lastSaveFilename = fileName;
619 				File file = new File(dialog.getDirectory(), fileName);
620 				try {
621 					save(file);
622 				} catch (IOException ex) {
623 					throw new RuntimeException("Error saving Hiero settings file: " + file.getAbsolutePath(), ex);
624 				}
625 			}
626 		});
627 
628 		saveBMFontMenuItem.addActionListener(new ActionListener() {
629 			public void actionPerformed (ActionEvent evt) {
630 				FileDialog dialog = new FileDialog(Hiero.this, "Save BMFont files", FileDialog.SAVE);
631 				dialog.setLocationRelativeTo(null);
632 				dialog.setFile("*.fnt");
633 				dialog.setDirectory(prefs.get("dir.savebm", ""));
634 
635 				if (lastSaveBMFilename.length() > 0) {
636 					dialog.setFile(lastSaveBMFilename);
637 				} else if (lastOpenFilename.length() > 0) {
638 					dialog.setFile(lastOpenFilename.replace(".hiero", ".fnt"));
639 				}
640 
641 				dialog.setVisible(true);
642 				if (dialog.getDirectory() != null) {
643 					prefs.put("dir.savebm", dialog.getDirectory());
644 				}
645 
646 				String fileName = dialog.getFile();
647 				if (fileName == null) return;
648 				lastSaveBMFilename = fileName;
649 				saveBm(new File(dialog.getDirectory(), fileName));
650 			}
651 		});
652 
exitMenuItem.addActionListener(new ActionListener() { public void actionPerformed (ActionEvent evt) { dispose(); } })653 		exitMenuItem.addActionListener(new ActionListener() {
654 			public void actionPerformed (ActionEvent evt) {
655 				dispose();
656 			}
657 		});
658 
sampleNeheButton.addActionListener(new ActionListener() { public void actionPerformed (ActionEvent evt) { sampleTextPane.setText(NEHE_CHARS); resetCacheButton.doClick(); } })659 		sampleNeheButton.addActionListener(new ActionListener() {
660 			public void actionPerformed (ActionEvent evt) {
661 				sampleTextPane.setText(NEHE_CHARS);
662 				resetCacheButton.doClick();
663 			}
664 		});
665 
sampleAsciiButton.addActionListener(new ActionListener() { public void actionPerformed (ActionEvent evt) { StringBuilder buffer = new StringBuilder(); buffer.append(NEHE_CHARS); buffer.append('\\n'); int count = 0; for (int i = 33; i <= 255; i++) { if (buffer.indexOf(Character.toString((char)i)) != -1) continue; buffer.append((char)i); if (++count % 30 == 0) buffer.append('\\n'); } sampleTextPane.setText(buffer.toString()); resetCacheButton.doClick(); } })666 		sampleAsciiButton.addActionListener(new ActionListener() {
667 			public void actionPerformed (ActionEvent evt) {
668 				StringBuilder buffer = new StringBuilder();
669 				buffer.append(NEHE_CHARS);
670 				buffer.append('\n');
671 				int count = 0;
672 				for (int i = 33; i <= 255; i++) {
673 					if (buffer.indexOf(Character.toString((char)i)) != -1) continue;
674 					buffer.append((char)i);
675 					if (++count % 30 == 0) buffer.append('\n');
676 				}
677 				sampleTextPane.setText(buffer.toString());
678 				resetCacheButton.doClick();
679 			}
680 		});
681 
sampleExtendedButton.addActionListener(new ActionListener() { public void actionPerformed (ActionEvent evt) { sampleTextPane.setText(EXTENDED_CHARS); resetCacheButton.doClick(); } })682 		sampleExtendedButton.addActionListener(new ActionListener() {
683 			public void actionPerformed (ActionEvent evt) {
684 				sampleTextPane.setText(EXTENDED_CHARS);
685 				resetCacheButton.doClick();
686 			}
687 		});
688 	}
689 
initializeComponents()690 	private void initializeComponents () {
691 		getContentPane().setLayout(new GridBagLayout());
692 		JPanel leftSidePanel = new JPanel();
693 		leftSidePanel.setLayout(new GridBagLayout());
694 		getContentPane().add(leftSidePanel, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER,
695 			GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
696 		{
697 			JPanel fontPanel = new JPanel();
698 			leftSidePanel.add(fontPanel, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
699 				GridBagConstraints.BOTH, new Insets(5, 5, 5, 5), 0, 0));
700 			fontPanel.setLayout(new GridBagLayout());
701 			fontPanel.setBorder(BorderFactory.createTitledBorder("Font"));
702 			{
703 				fontSizeSpinner = new JSpinner(new SpinnerNumberModel(32, 0, 256, 1));
704 				fontPanel.add(fontSizeSpinner, new GridBagConstraints(1, 3, 1, 1, 0.0, 0.0, GridBagConstraints.WEST,
705 					GridBagConstraints.NONE, new Insets(0, 0, 5, 10), 0, 0));
706 				((JSpinner.DefaultEditor)fontSizeSpinner.getEditor()).getTextField().setColumns(2);
707 			}
708 			{
709 				JScrollPane fontScroll = new JScrollPane();
710 				fontPanel.add(fontScroll, new GridBagConstraints(1, 1, 3, 1, 1.0, 1.0, GridBagConstraints.CENTER,
711 					GridBagConstraints.BOTH, new Insets(0, 0, 5, 5), 0, 0));
712 				{
713 					fontListModel = new DefaultComboBoxModel(
714 						GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames());
715 					fontList = new JList();
716 					fontScroll.setViewportView(fontList);
717 					fontList.setModel(fontListModel);
718 					fontList.setVisibleRowCount(6);
719 					fontList.setSelectedIndex(0);
720 					fontScroll.setMinimumSize(new Dimension(220, fontList.getPreferredScrollableViewportSize().height));
721 				}
722 			}
723 			{
724 				systemFontRadio = new JRadioButton("System:", true);
725 				fontPanel.add(systemFontRadio, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHEAST,
726 					GridBagConstraints.NONE, new Insets(0, 5, 0, 5), 0, 0));
727 				systemFontRadio.setMargin(new Insets(0, 0, 0, 0));
728 			}
729 			{
730 				fontFileRadio = new JRadioButton("File:");
731 				fontPanel.add(fontFileRadio, new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0, GridBagConstraints.EAST,
732 					GridBagConstraints.NONE, new Insets(0, 5, 5, 5), 0, 0));
733 				fontFileRadio.setMargin(new Insets(0, 0, 0, 0));
734 			}
735 			{
736 				fontFileText = new JTextField();
737 				fontPanel.add(fontFileText, new GridBagConstraints(1, 2, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER,
738 					GridBagConstraints.HORIZONTAL, new Insets(0, 0, 5, 0), 0, 0));
739 			}
740 			{
741 				fontPanel.add(new JLabel("Size:"), new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.EAST,
742 					GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
743 			}
744 
745 			{
746 				unicodePanel = new JPanel(new GridBagLayout());
747 				fontPanel.add(unicodePanel, new GridBagConstraints(2, 3, 2, 1, 0.0, 0.0, GridBagConstraints.EAST,
748 					GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 5), 0, 0));
749 				{
750 					boldCheckBox = new JCheckBox("Bold");
751 					unicodePanel.add(boldCheckBox, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
752 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
753 				}
754 				{
755 					italicCheckBox = new JCheckBox("Italic");
756 					unicodePanel.add(italicCheckBox, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST,
757 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
758 				}
759 			}
760 			{
761 				bitmapPanel = new JPanel(new GridBagLayout());
762 				fontPanel.add(bitmapPanel, new GridBagConstraints(2, 3, 2, 1, 1.0, 0.0, GridBagConstraints.WEST,
763 					GridBagConstraints.NONE, new Insets(0, 0, 0, 5), 0, 0));
764 				{
765 					bitmapPanel.add(new JLabel("Gamma:"), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST,
766 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
767 				}
768 				{
769 					gammaSpinner = new JSpinner(new SpinnerNumberModel(1.8f, 0, 30, 0.01));
770 					((JSpinner.DefaultEditor)gammaSpinner.getEditor()).getTextField().setColumns(2);
771 					bitmapPanel.add(gammaSpinner, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST,
772 						GridBagConstraints.NONE, new Insets(0, 0, 5, 10), 0, 0));
773 				}
774 				{
775 					monoCheckBox = new JCheckBox("Mono");
776 					bitmapPanel.add(monoCheckBox, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST,
777 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
778 				}
779 			}
780 			{
781 				browseButton = new JButton("...");
782 				fontPanel.add(browseButton, new GridBagConstraints(3, 2, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
783 					GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
784 				browseButton.setMargin(new Insets(0, 0, 0, 0));
785 			}
786 			{
787 				fontPanel.add(new JLabel("Rendering:"), new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.NORTHEAST,
788 					GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
789 			}
790 			{
791 				JPanel renderingPanel = new JPanel(new GridBagLayout());
792 				fontPanel.add(renderingPanel, new GridBagConstraints(1, 4, 3, 1, 0.0, 0.0, GridBagConstraints.NORTHWEST,
793 					GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
794 				{
795 					freeTypeRadio = new JRadioButton("FreeType");
796 					renderingPanel.add(freeTypeRadio, new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
797 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
798 				}
799 				{
800 					javaRadio = new JRadioButton("Java");
801 					renderingPanel.add(javaRadio, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
802 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
803 				}
804 				{
805 					nativeRadio = new JRadioButton("Native");
806 					renderingPanel.add(nativeRadio, new GridBagConstraints(2, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST,
807 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
808 				}
809 			}
810 			ButtonGroup buttonGroup = new ButtonGroup();
811 			buttonGroup.add(systemFontRadio);
812 			buttonGroup.add(fontFileRadio);
813 			buttonGroup = new ButtonGroup();
814 			buttonGroup.add(freeTypeRadio);
815 			buttonGroup.add(javaRadio);
816 			buttonGroup.add(nativeRadio);
817 			freeTypeRadio.setSelected(true);
818 		}
819 		{
820 			JPanel samplePanel = new JPanel();
821 			leftSidePanel.add(samplePanel, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,
822 				GridBagConstraints.BOTH, new Insets(5, 0, 5, 5), 0, 0));
823 			samplePanel.setLayout(new GridBagLayout());
824 			samplePanel.setBorder(BorderFactory.createTitledBorder("Sample Text"));
825 			{
826 				JScrollPane textScroll = new JScrollPane();
827 				samplePanel.add(textScroll, new GridBagConstraints(0, 0, 4, 1, 1.0, 1.0, GridBagConstraints.CENTER,
828 					GridBagConstraints.BOTH, new Insets(0, 5, 5, 5), 0, 0));
829 				{
830 					sampleTextPane = new JTextPane();
831 					textScroll.setViewportView(sampleTextPane);
832 				}
833 			}
834 			{
835 				sampleNeheButton = new JButton();
836 				sampleNeheButton.setText("NEHE");
837 				samplePanel.add(sampleNeheButton, new GridBagConstraints(2, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
838 					GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
839 			}
840 			{
841 				sampleAsciiButton = new JButton();
842 				sampleAsciiButton.setText("ASCII");
843 				samplePanel.add(sampleAsciiButton, new GridBagConstraints(3, 1, 1, 1, 0, 0.0, GridBagConstraints.EAST,
844 					GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
845 			}
846 			{
847 				sampleExtendedButton = new JButton();
848 				sampleExtendedButton.setText("Extended");
849 				samplePanel.add(sampleExtendedButton, new GridBagConstraints(1, 1, 1, 1, 1.0, 0.0, GridBagConstraints.EAST,
850 					GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
851 			}
852 		}
853 		{
854 			JPanel renderingPanel = new JPanel();
855 			leftSidePanel.add(renderingPanel, new GridBagConstraints(0, 1, 2, 1, 1.0, 1.0, GridBagConstraints.CENTER,
856 				GridBagConstraints.BOTH, new Insets(0, 5, 5, 5), 0, 0));
857 			renderingPanel.setBorder(BorderFactory.createTitledBorder("Rendering"));
858 			renderingPanel.setLayout(new GridBagLayout());
859 			{
860 				JPanel wrapperPanel = new JPanel();
861 				renderingPanel.add(wrapperPanel, new GridBagConstraints(0, 1, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER,
862 					GridBagConstraints.BOTH, new Insets(0, 5, 5, 5), 0, 0));
863 				wrapperPanel.setLayout(new BorderLayout());
864 				wrapperPanel.setBackground(java.awt.Color.white);
865 				{
866 					gamePanel = new JPanel();
867 					wrapperPanel.add(gamePanel);
868 					gamePanel.setLayout(new BorderLayout());
869 					gamePanel.setBackground(java.awt.Color.white);
870 				}
871 			}
872 			{
873 				glyphCachePanel = new JPanel() {
874 					private int maxWidth;
875 
876 					public Dimension getPreferredSize () {
877 						// Keep glyphCachePanel width from ever going down so the CanvasGameContainer doesn't change sizes and flicker.
878 						Dimension size = super.getPreferredSize();
879 						maxWidth = Math.max(maxWidth, size.width);
880 						size.width = maxWidth;
881 						return size;
882 					}
883 				};
884 				glyphCachePanel.setVisible(false);
885 				renderingPanel.add(glyphCachePanel, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.NORTH,
886 					GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
887 				glyphCachePanel.setLayout(new GridBagLayout());
888 				{
889 					glyphCachePanel.add(new JLabel("Glyphs:"), new GridBagConstraints(0, 4, 1, 1, 0.0, 0.0, GridBagConstraints.EAST,
890 						GridBagConstraints.NONE, new Insets(0, 5, 5, 5), 0, 0));
891 				}
892 				{
893 					glyphCachePanel.add(new JLabel("Pages:"), new GridBagConstraints(0, 3, 1, 1, 0.0, 0.0, GridBagConstraints.EAST,
894 						GridBagConstraints.NONE, new Insets(0, 5, 5, 5), 0, 0));
895 				}
896 				{
897 					glyphCachePanel.add(new JLabel("Page width:"), new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0,
898 						GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 5, 5, 5), 0, 0));
899 				}
900 				{
901 					glyphCachePanel.add(new JLabel("Page height:"), new GridBagConstraints(0, 2, 1, 1, 0.0, 0.0,
902 						GridBagConstraints.EAST, GridBagConstraints.NONE, new Insets(0, 5, 5, 5), 0, 0));
903 				}
904 				{
905 					glyphPageWidthCombo = new JComboBox(new DefaultComboBoxModel(new Integer[] {new Integer(32), new Integer(64),
906 						new Integer(128), new Integer(256), new Integer(512), new Integer(1024), new Integer(2048)}));
907 					glyphCachePanel.add(glyphPageWidthCombo, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.WEST,
908 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
909 					glyphPageWidthCombo.setSelectedIndex(4);
910 				}
911 				{
912 					glyphPageHeightCombo = new JComboBox(new DefaultComboBoxModel(new Integer[] {new Integer(32), new Integer(64),
913 						new Integer(128), new Integer(256), new Integer(512), new Integer(1024), new Integer(2048)}));
914 					glyphCachePanel.add(glyphPageHeightCombo, new GridBagConstraints(1, 2, 1, 1, 0.0, 0.0, GridBagConstraints.WEST,
915 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
916 					glyphPageHeightCombo.setSelectedIndex(4);
917 				}
918 				{
919 					resetCacheButton = new JButton("Reset Cache");
920 					glyphCachePanel.add(resetCacheButton, new GridBagConstraints(0, 6, 2, 1, 1.0, 0.0, GridBagConstraints.CENTER,
921 						GridBagConstraints.NONE, new Insets(0, 5, 5, 5), 0, 0));
922 				}
923 				{
924 					glyphPagesTotalLabel = new JLabel("1");
925 					glyphCachePanel.add(glyphPagesTotalLabel, new GridBagConstraints(1, 3, 1, 1, 0.0, 0.0, GridBagConstraints.WEST,
926 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
927 				}
928 				{
929 					glyphsTotalLabel = new JLabel("0");
930 					glyphCachePanel.add(glyphsTotalLabel, new GridBagConstraints(1, 4, 1, 1, 0.0, 0.0, GridBagConstraints.WEST,
931 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
932 				}
933 				{
934 					glyphPageComboModel = new DefaultComboBoxModel();
935 					glyphPageCombo = new JComboBox();
936 					glyphCachePanel.add(glyphPageCombo, new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, GridBagConstraints.WEST,
937 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
938 					glyphPageCombo.setModel(glyphPageComboModel);
939 				}
940 				{
941 					glyphCachePanel.add(new JLabel("View:"), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0, GridBagConstraints.EAST,
942 						GridBagConstraints.NONE, new Insets(0, 5, 5, 5), 0, 0));
943 				}
944 			}
945 			{
946 				JPanel radioButtonsPanel = new JPanel();
947 				renderingPanel.add(radioButtonsPanel, new GridBagConstraints(0, 0, 2, 1, 0.0, 0.0, GridBagConstraints.CENTER,
948 					GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
949 				radioButtonsPanel.setLayout(new GridBagLayout());
950 				{
951 					sampleTextRadio = new JRadioButton("Sample text");
952 					radioButtonsPanel.add(sampleTextRadio, new GridBagConstraints(2, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
953 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
954 					sampleTextRadio.setSelected(true);
955 				}
956 				{
957 					glyphCacheRadio = new JRadioButton("Glyph cache");
958 					radioButtonsPanel.add(glyphCacheRadio, new GridBagConstraints(3, 0, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
959 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
960 				}
961 				{
962 					radioButtonsPanel.add(new JLabel("Background:"), new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0,
963 						GridBagConstraints.CENTER, GridBagConstraints.NONE, new Insets(0, 5, 5, 5), 0, 0));
964 				}
965 				{
966 					backgroundColorLabel = new JLabel();
967 					radioButtonsPanel.add(backgroundColorLabel, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.WEST,
968 						GridBagConstraints.NONE, new Insets(0, 0, 5, 5), 0, 0));
969 				}
970 				ButtonGroup buttonGroup = new ButtonGroup();
971 				buttonGroup.add(glyphCacheRadio);
972 				buttonGroup.add(sampleTextRadio);
973 			}
974 		}
975 		JPanel rightSidePanel = new JPanel();
976 		rightSidePanel.setLayout(new GridBagLayout());
977 		getContentPane().add(rightSidePanel, new GridBagConstraints(1, 0, 1, 2, 0.0, 0.0, GridBagConstraints.CENTER,
978 			GridBagConstraints.BOTH, new Insets(0, 0, 0, 0), 0, 0));
979 		{
980 			JPanel paddingPanel = new JPanel();
981 			paddingPanel.setLayout(new GridBagLayout());
982 			rightSidePanel.add(paddingPanel, new GridBagConstraints(0, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
983 				GridBagConstraints.BOTH, new Insets(0, 0, 5, 5), 0, 0));
984 			paddingPanel.setBorder(BorderFactory.createTitledBorder("Padding"));
985 			{
986 				padTopSpinner = new JSpinner(new SpinnerNumberModel(1, 0, 999, 1));
987 				paddingPanel.add(padTopSpinner, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
988 					GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
989 				((JSpinner.DefaultEditor)padTopSpinner.getEditor()).getTextField().setColumns(2);
990 			}
991 			{
992 				padRightSpinner = new JSpinner(new SpinnerNumberModel(1, 0, 999, 1));
993 				paddingPanel.add(padRightSpinner, new GridBagConstraints(2, 2, 1, 1, 1.0, 0.0, GridBagConstraints.WEST,
994 					GridBagConstraints.NONE, new Insets(0, 0, 0, 5), 0, 0));
995 				((JSpinner.DefaultEditor)padRightSpinner.getEditor()).getTextField().setColumns(2);
996 			}
997 			{
998 				padLeftSpinner = new JSpinner(new SpinnerNumberModel(1, 0, 999, 1));
999 				paddingPanel.add(padLeftSpinner, new GridBagConstraints(0, 2, 1, 1, 1.0, 0.0, GridBagConstraints.EAST,
1000 					GridBagConstraints.NONE, new Insets(0, 5, 0, 0), 0, 0));
1001 				((JSpinner.DefaultEditor)padLeftSpinner.getEditor()).getTextField().setColumns(2);
1002 			}
1003 			{
1004 				padBottomSpinner = new JSpinner(new SpinnerNumberModel(1, 0, 999, 1));
1005 				paddingPanel.add(padBottomSpinner, new GridBagConstraints(1, 3, 1, 1, 0.0, 0.0, GridBagConstraints.CENTER,
1006 					GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
1007 				((JSpinner.DefaultEditor)padBottomSpinner.getEditor()).getTextField().setColumns(2);
1008 			}
1009 			{
1010 				JPanel advancePanel = new JPanel();
1011 				FlowLayout advancePanelLayout = new FlowLayout();
1012 				advancePanel.setLayout(advancePanelLayout);
1013 				paddingPanel.add(advancePanel, new GridBagConstraints(0, 4, 3, 1, 1.0, 0.0, GridBagConstraints.CENTER,
1014 					GridBagConstraints.NONE, new Insets(0, 0, 0, 0), 0, 0));
1015 				{
1016 					advancePanel.add(new JLabel("X:"));
1017 				}
1018 				{
1019 					padAdvanceXSpinner = new JSpinner(new SpinnerNumberModel(-2, -999, 999, 1));
1020 					advancePanel.add(padAdvanceXSpinner);
1021 					((JSpinner.DefaultEditor)padAdvanceXSpinner.getEditor()).getTextField().setColumns(2);
1022 				}
1023 				{
1024 					advancePanel.add(new JLabel("Y:"));
1025 				}
1026 				{
1027 					padAdvanceYSpinner = new JSpinner(new SpinnerNumberModel(-2, -999, 999, 1));
1028 					advancePanel.add(padAdvanceYSpinner);
1029 					((JSpinner.DefaultEditor)padAdvanceYSpinner.getEditor()).getTextField().setColumns(2);
1030 				}
1031 			}
1032 		}
1033 		{
1034 			effectsPanel = new JPanel();
1035 			effectsPanel.setLayout(new GridBagLayout());
1036 			rightSidePanel.add(effectsPanel, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.CENTER,
1037 				GridBagConstraints.BOTH, new Insets(5, 0, 5, 5), 0, 0));
1038 			effectsPanel.setBorder(BorderFactory.createTitledBorder("Effects"));
1039 			effectsPanel.setMinimumSize(new Dimension(210, 1));
1040 			{
1041 				effectsScroll = new JScrollPane();
1042 				effectsPanel.add(effectsScroll, new GridBagConstraints(1, 0, 1, 1, 1.0, 0.0, GridBagConstraints.NORTH,
1043 					GridBagConstraints.HORIZONTAL, new Insets(0, 5, 5, 5), 0, 0));
1044 				{
1045 					effectsListModel = new DefaultComboBoxModel();
1046 					effectsList = new JList();
1047 					effectsScroll.setViewportView(effectsList);
1048 					effectsList.setModel(effectsListModel);
1049 					effectsList.setVisibleRowCount(7);
1050 					effectsScroll.setMinimumSize(effectsList.getPreferredScrollableViewportSize());
1051 				}
1052 			}
1053 			{
1054 				addEffectButton = new JButton("Add");
1055 				effectsPanel.add(addEffectButton, new GridBagConstraints(1, 1, 1, 1, 0.0, 0.0, GridBagConstraints.EAST,
1056 					GridBagConstraints.NONE, new Insets(0, 5, 6, 5), 0, 0));
1057 				addEffectButton.setEnabled(false);
1058 			}
1059 			{
1060 				appliedEffectsScroll = new JScrollPane();
1061 				effectsPanel.add(appliedEffectsScroll, new GridBagConstraints(1, 3, 1, 1, 1.0, 1.0, GridBagConstraints.NORTH,
1062 					GridBagConstraints.BOTH, new Insets(0, 0, 5, 0), 0, 0));
1063 				appliedEffectsScroll.setBorder(new EmptyBorder(0, 0, 0, 0));
1064 				appliedEffectsScroll.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
1065 				{
1066 					JPanel panel = new JPanel();
1067 					panel.setLayout(new GridBagLayout());
1068 					appliedEffectsScroll.setViewportView(panel);
1069 					{
1070 						appliedEffectsPanel = new JPanel();
1071 						appliedEffectsPanel.setLayout(new GridBagLayout());
1072 						panel.add(appliedEffectsPanel, new GridBagConstraints(0, 0, 1, 1, 1.0, 1.0, GridBagConstraints.NORTH,
1073 							GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
1074 						appliedEffectsPanel.setBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, java.awt.Color.black));
1075 					}
1076 				}
1077 			}
1078 		}
1079 	}
1080 
initializeMenus()1081 	private void initializeMenus () {
1082 		{
1083 			JMenuBar menuBar = new JMenuBar();
1084 			setJMenuBar(menuBar);
1085 			{
1086 				JMenu fileMenu = new JMenu();
1087 				menuBar.add(fileMenu);
1088 				fileMenu.setText("File");
1089 				fileMenu.setMnemonic(KeyEvent.VK_F);
1090 				{
1091 					openMenuItem = new JMenuItem("Open Hiero settings file...");
1092 					openMenuItem.setMnemonic(KeyEvent.VK_O);
1093 					openMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, KeyEvent.CTRL_MASK));
1094 					fileMenu.add(openMenuItem);
1095 				}
1096 				{
1097 					saveMenuItem = new JMenuItem("Save Hiero settings file...");
1098 					saveMenuItem.setMnemonic(KeyEvent.VK_S);
1099 					saveMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, KeyEvent.CTRL_MASK));
1100 					fileMenu.add(saveMenuItem);
1101 				}
1102 				fileMenu.addSeparator();
1103 				{
1104 					saveBMFontMenuItem = new JMenuItem("Save BMFont files (text)...");
1105 					saveBMFontMenuItem.setMnemonic(KeyEvent.VK_B);
1106 					saveBMFontMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, KeyEvent.CTRL_MASK));
1107 					fileMenu.add(saveBMFontMenuItem);
1108 				}
1109 				fileMenu.addSeparator();
1110 				{
1111 					exitMenuItem = new JMenuItem("Exit");
1112 					exitMenuItem.setMnemonic(KeyEvent.VK_X);
1113 					fileMenu.add(exitMenuItem);
1114 				}
1115 			}
1116 		}
1117 	}
1118 
getColorIcon(java.awt.Color color)1119 	static Icon getColorIcon (java.awt.Color color) {
1120 		BufferedImage image = new BufferedImage(32, 16, BufferedImage.TYPE_INT_RGB);
1121 		java.awt.Graphics g = image.getGraphics();
1122 		g.setColor(color);
1123 		g.fillRect(1, 1, 30, 14);
1124 		g.setColor(java.awt.Color.black);
1125 		g.drawRect(0, 0, 31, 15);
1126 		return new ImageIcon(image);
1127 	}
1128 
1129 	private class EffectPanel extends JPanel {
1130 		final java.awt.Color selectedColor = new java.awt.Color(0xb1d2e9);
1131 
1132 		final ConfigurableEffect effect;
1133 		List values;
1134 
1135 		JButton upButton;
1136 		JButton downButton;
1137 		JButton deleteButton;
1138 		private JPanel valuesPanel;
1139 		JLabel nameLabel;
1140 
1141 		GridBagConstraints constrains = new GridBagConstraints(0, -1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,
1142 			GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0);
1143 
EffectPanel(final ConfigurableEffect effect)1144 		EffectPanel (final ConfigurableEffect effect) {
1145 			this.effect = effect;
1146 			effectPanels.add(this);
1147 			effectsList.getListSelectionListeners()[0].valueChanged(null);
1148 
1149 			setLayout(new GridBagLayout());
1150 			setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, java.awt.Color.black));
1151 			appliedEffectsPanel.add(this, constrains);
1152 			{
1153 				JPanel titlePanel = new JPanel();
1154 				titlePanel.setLayout(new LayoutManager() {
1155 					public void removeLayoutComponent (Component comp) {
1156 					}
1157 
1158 					public Dimension preferredLayoutSize (Container parent) {
1159 						return null;
1160 					}
1161 
1162 					public Dimension minimumLayoutSize (Container parent) {
1163 						return null;
1164 					}
1165 
1166 					public void layoutContainer (Container parent) {
1167 						Dimension buttonSize = upButton.getPreferredSize();
1168 						int upButtonX = getWidth() - buttonSize.width * 3 - 6 - 5;
1169 						upButton.setBounds(upButtonX, 0, buttonSize.width, buttonSize.height);
1170 						downButton.setBounds(getWidth() - buttonSize.width * 2 - 3 - 5, 0, buttonSize.width, buttonSize.height);
1171 						deleteButton.setBounds(getWidth() - buttonSize.width - 5, 0, buttonSize.width, buttonSize.height);
1172 
1173 						Dimension labelSize = nameLabel.getPreferredSize();
1174 						nameLabel.setBounds(5, buttonSize.height / 2 - labelSize.height / 2, getWidth() - 5, labelSize.height);
1175 					}
1176 
1177 					public void addLayoutComponent (String name, Component comp) {
1178 					}
1179 				});
1180 				{
1181 					upButton = new JButton();
1182 					titlePanel.add(upButton);
1183 					upButton.setText("Up");
1184 					upButton.setMargin(new Insets(0, 0, 0, 0));
1185 					Font font = upButton.getFont();
1186 					upButton.setFont(new Font(font.getName(), font.getStyle(), font.getSize() - 2));
1187 				}
1188 				{
1189 					downButton = new JButton();
1190 					titlePanel.add(downButton);
1191 					downButton.setText("Down");
1192 					downButton.setMargin(new Insets(0, 0, 0, 0));
1193 					Font font = downButton.getFont();
1194 					downButton.setFont(new Font(font.getName(), font.getStyle(), font.getSize() - 2));
1195 				}
1196 				{
1197 					deleteButton = new JButton();
1198 					titlePanel.add(deleteButton);
1199 					deleteButton.setText("X");
1200 					deleteButton.setMargin(new Insets(0, 0, 0, 0));
1201 					Font font = deleteButton.getFont();
1202 					deleteButton.setFont(new Font(font.getName(), font.getStyle(), font.getSize() - 2));
1203 				}
1204 				{
1205 					nameLabel = new JLabel(effect.toString());
1206 					titlePanel.add(nameLabel);
1207 					Font font = nameLabel.getFont();
1208 					nameLabel.setFont(new Font(font.getName(), Font.BOLD, font.getSize()));
1209 				}
1210 				titlePanel.setPreferredSize(
1211 					new Dimension(0, Math.max(nameLabel.getPreferredSize().height, deleteButton.getPreferredSize().height)));
1212 				add(titlePanel, new GridBagConstraints(0, 0, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.BOTH,
1213 					new Insets(5, 0, 0, 5), 0, 0));
1214 				titlePanel.setOpaque(false);
1215 			}
1216 			{
1217 				valuesPanel = new JPanel();
1218 				valuesPanel.setOpaque(false);
1219 				valuesPanel.setLayout(new GridBagLayout());
1220 				add(valuesPanel, new GridBagConstraints(0, 1, 1, 1, 1.0, 0.0, GridBagConstraints.CENTER,
1221 					GridBagConstraints.HORIZONTAL, new Insets(0, 10, 5, 0), 0, 0));
1222 			}
1223 
1224 			upButton.addActionListener(new ActionListener() {
1225 				public void actionPerformed (ActionEvent evt) {
1226 					int currentIndex = effectPanels.indexOf(EffectPanel.this);
1227 					if (currentIndex > 0) {
1228 						moveEffect(currentIndex - 1);
1229 						updateFont();
1230 						updateUpDownButtons();
1231 					}
1232 				}
1233 			});
1234 
1235 			downButton.addActionListener(new ActionListener() {
1236 				public void actionPerformed (ActionEvent evt) {
1237 					int currentIndex = effectPanels.indexOf(EffectPanel.this);
1238 					if (currentIndex < effectPanels.size() - 1) {
1239 						moveEffect(currentIndex + 1);
1240 						updateFont();
1241 						updateUpDownButtons();
1242 					}
1243 				}
1244 			});
1245 
1246 			deleteButton.addActionListener(new ActionListener() {
1247 				public void actionPerformed (ActionEvent evt) {
1248 					remove();
1249 					updateFont();
1250 					updateUpDownButtons();
1251 				}
1252 			});
1253 
1254 			updateValues();
1255 			updateFont();
1256 			updateUpDownButtons();
1257 		}
1258 
remove()1259 		public void remove () {
1260 			effectPanels.remove(this);
1261 			appliedEffectsPanel.remove(EffectPanel.this);
1262 			getContentPane().validate();
1263 			effectsList.getListSelectionListeners()[0].valueChanged(null);
1264 		}
1265 
updateValues()1266 		public void updateValues () {
1267 			prefs.put("foreground", EffectUtil.toString(colorEffect.getColor()));
1268 			valuesPanel.removeAll();
1269 			values = effect.getValues();
1270 			for (Iterator iter = values.iterator(); iter.hasNext();)
1271 				addValue((Value)iter.next());
1272 		}
1273 
updateUpDownButtons()1274 		public void updateUpDownButtons () {
1275 			for (int index = 0; index < effectPanels.size(); index++) {
1276 				EffectPanel effectPanel = effectPanels.get(index);
1277 				if (index == 0) {
1278 					effectPanel.upButton.setEnabled(false);
1279 				} else {
1280 					effectPanel.upButton.setEnabled(true);
1281 				}
1282 
1283 				if (index == effectPanels.size() - 1) {
1284 					effectPanel.downButton.setEnabled(false);
1285 				} else {
1286 					effectPanel.downButton.setEnabled(true);
1287 				}
1288 			}
1289 		}
1290 
moveEffect(int newIndex)1291 		public void moveEffect (int newIndex) {
1292 			appliedEffectsPanel.remove(this);
1293 			effectPanels.remove(this);
1294 			appliedEffectsPanel.add(this, constrains, newIndex);
1295 			effectPanels.add(newIndex, this);
1296 		}
1297 
addValue(final Value value)1298 		public void addValue (final Value value) {
1299 			JLabel valueNameLabel = new JLabel(value.getName() + ":");
1300 			valuesPanel.add(valueNameLabel, new GridBagConstraints(0, -1, 1, 1, 0.0, 0.0, GridBagConstraints.EAST,
1301 				GridBagConstraints.NONE, new Insets(0, 0, 0, 5), 0, 0));
1302 
1303 			final JLabel valueValueLabel = new JLabel();
1304 			valuesPanel.add(valueValueLabel, new GridBagConstraints(1, -1, 1, 1, 1.0, 0.0, GridBagConstraints.WEST,
1305 				GridBagConstraints.BOTH, new Insets(0, 0, 0, 5), 0, 0));
1306 			valueValueLabel.setOpaque(true);
1307 			if (value.getObject() instanceof java.awt.Color)
1308 				valueValueLabel.setIcon(getColorIcon((java.awt.Color)value.getObject()));
1309 			else
1310 				valueValueLabel.setText(value.toString());
1311 
1312 			valueValueLabel.addMouseListener(new MouseAdapter() {
1313 				public void mouseEntered (MouseEvent evt) {
1314 					valueValueLabel.setBackground(selectedColor);
1315 				}
1316 
1317 				public void mouseExited (MouseEvent evt) {
1318 					valueValueLabel.setBackground(null);
1319 				}
1320 
1321 				public void mouseClicked (MouseEvent evt) {
1322 					Object oldObject = value.getObject();
1323 					value.showDialog();
1324 					if (!value.getObject().equals(oldObject)) {
1325 						effect.setValues(values);
1326 						updateValues();
1327 						updateFont();
1328 					}
1329 				}
1330 			});
1331 		}
1332 
getEffect()1333 		public ConfigurableEffect getEffect () {
1334 			return effect;
1335 		}
1336 
equals(Object obj)1337 		public boolean equals (Object obj) {
1338 			if (this == obj) return true;
1339 			if (obj == null) return false;
1340 			if (getClass() != obj.getClass()) return false;
1341 			final EffectPanel other = (EffectPanel)obj;
1342 			if (effect == null) {
1343 				if (other.effect != null) return false;
1344 			} else if (!effect.equals(other.effect)) return false;
1345 			return true;
1346 		}
1347 
1348 	}
1349 
1350 	static private class Splash extends JWindow {
1351 		final int minMillis;
1352 		final long startTime;
1353 
Splash(Frame frame, String imageFile, int minMillis)1354 		public Splash (Frame frame, String imageFile, int minMillis) {
1355 			super(frame);
1356 			this.minMillis = minMillis;
1357 			getContentPane().add(new JLabel(new ImageIcon(Splash.class.getResource(imageFile))), BorderLayout.CENTER);
1358 			pack();
1359 			setLocationRelativeTo(null);
1360 			setVisible(true);
1361 			startTime = System.currentTimeMillis();
1362 		}
1363 
close()1364 		public void close () {
1365 			final long endTime = System.currentTimeMillis();
1366 			new Thread(new Runnable() {
1367 				public void run () {
1368 					if (endTime - startTime < minMillis) {
1369 						addMouseListener(new MouseAdapter() {
1370 							public void mousePressed (MouseEvent evt) {
1371 								dispose();
1372 							}
1373 						});
1374 						try {
1375 							Thread.sleep(minMillis - (endTime - startTime));
1376 						} catch (InterruptedException ignored) {
1377 						}
1378 					}
1379 					EventQueue.invokeLater(new Runnable() {
1380 						public void run () {
1381 							dispose();
1382 						}
1383 					});
1384 				}
1385 			}, "Splash").start();
1386 		}
1387 	}
1388 
1389 	class Renderer extends ApplicationAdapter {
1390 		SpriteBatch batch;
1391 		int width, height;
1392 
create()1393 		public void create () {
1394 			glClearColor(0, 0, 0, 0);
1395 			glClearDepth(1);
1396 			glDisable(GL_LIGHTING);
1397 
1398 			batch = new SpriteBatch();
1399 
1400 			sampleNeheButton.doClick();
1401 		}
1402 
resize(int width, int height)1403 		public void resize (int width, int height) {
1404 			this.width = width;
1405 			this.height = height;
1406 			batch.getProjectionMatrix().setToOrtho2D(0, 0, width, height);
1407 		}
1408 
render()1409 		public void render () {
1410 			int viewWidth = Gdx.graphics.getWidth();
1411 			int viewHeight = Gdx.graphics.getHeight();
1412 
1413 			if (sampleTextRadio.isSelected()) {
1414 				GL11.glClearColor(renderingBackgroundColor.r, renderingBackgroundColor.g, renderingBackgroundColor.b,
1415 					renderingBackgroundColor.a);
1416 				GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
1417 			} else {
1418 				GL11.glClearColor(1, 1, 1, 1);
1419 				GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
1420 			}
1421 
1422 			String sampleText = sampleTextPane.getText();
1423 
1424 			glEnable(GL_TEXTURE_2D);
1425 			glEnableClientState(GL_TEXTURE_COORD_ARRAY);
1426 			glEnableClientState(GL_VERTEX_ARRAY);
1427 
1428 			glEnable(GL_BLEND);
1429 			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1430 
1431 			glViewport(0, 0, width, height);
1432 			glScissor(0, 0, width, height);
1433 
1434 			glMatrixMode(GL_PROJECTION);
1435 			glLoadIdentity();
1436 			glOrtho(0, width, height, 0, 1, -1);
1437 			glMatrixMode(GL_MODELVIEW);
1438 			glLoadIdentity();
1439 
1440 			unicodeFont.addGlyphs(sampleText);
1441 
1442 			if (!unicodeFont.getEffects().isEmpty() && unicodeFont.loadGlyphs(64)) {
1443 				glyphPageComboModel.removeAllElements();
1444 				int pageCount = unicodeFont.getGlyphPages().size();
1445 				int glyphCount = 0;
1446 				for (int i = 0; i < pageCount; i++) {
1447 					glyphPageComboModel.addElement("Page " + (i + 1));
1448 					glyphCount += ((GlyphPage)unicodeFont.getGlyphPages().get(i)).getGlyphs().size();
1449 				}
1450 				glyphPagesTotalLabel.setText(String.valueOf(pageCount));
1451 				glyphsTotalLabel.setText(String.valueOf(glyphCount));
1452 			}
1453 
1454 			if (sampleTextRadio.isSelected()) {
1455 				int offset = unicodeFont.getYOffset(sampleText);
1456 				if (offset > 0) offset = 0;
1457 				unicodeFont.drawString(10, 12, sampleText, Color.WHITE, 0, sampleText.length());
1458 			} else {
1459 				// GL11.glColor4f(renderingBackgroundColor.r, renderingBackgroundColor.g, renderingBackgroundColor.b,
1460 				// renderingBackgroundColor.a);
1461 				// fillRect(0, 0, unicodeFont.getGlyphPageWidth() + 2, unicodeFont.getGlyphPageHeight() + 2);
1462 				int index = glyphPageCombo.getSelectedIndex();
1463 				List pages = unicodeFont.getGlyphPages();
1464 				if (index >= 0 && index < pages.size()) {
1465 					Texture texture = ((GlyphPage)pages.get(glyphPageCombo.getSelectedIndex())).getTexture();
1466 
1467 					glDisable(GL_TEXTURE_2D);
1468 					glColor4f(renderingBackgroundColor.r, renderingBackgroundColor.g, renderingBackgroundColor.b,
1469 						renderingBackgroundColor.a);
1470 					glBegin(GL_QUADS);
1471 					glVertex3f(0, 0, 0);
1472 					glVertex3f(0, texture.getHeight(), 0);
1473 					glVertex3f(texture.getWidth(), texture.getHeight(), 0);
1474 					glVertex3f(texture.getWidth(), 0, 0);
1475 					glEnd();
1476 					glEnable(GL_TEXTURE_2D);
1477 
1478 					texture.bind();
1479 					glColor4f(1, 1, 1, 1);
1480 					glBegin(GL_QUADS);
1481 					glTexCoord2f(0, 0);
1482 					glVertex3f(0, 0, 0);
1483 
1484 					glTexCoord2f(0, 1);
1485 					glVertex3f(0, texture.getHeight(), 0);
1486 
1487 					glTexCoord2f(1, 1);
1488 					glVertex3f(texture.getWidth(), texture.getHeight(), 0);
1489 
1490 					glTexCoord2f(1, 0);
1491 					glVertex3f(texture.getWidth(), 0, 0);
1492 					glEnd();
1493 				}
1494 			}
1495 
1496 			glDisable(GL_TEXTURE_2D);
1497 			glDisableClientState(GL_TEXTURE_COORD_ARRAY);
1498 			glDisableClientState(GL_VERTEX_ARRAY);
1499 
1500 			if (saveBmFontFile != null) {
1501 				try {
1502 					BMFontUtil bmFont = new BMFontUtil(unicodeFont);
1503 					bmFont.save(saveBmFontFile);
1504 
1505 					if (batchMode) {
1506 						exit(0);
1507 					}
1508 				} catch (Throwable ex) {
1509 					System.out.println("Error saving BMFont files: " + saveBmFontFile.getAbsolutePath());
1510 					ex.printStackTrace();
1511 				} finally {
1512 					saveBmFontFile = null;
1513 				}
1514 			}
1515 		}
1516 
1517 	}
1518 
1519 	static final String NEHE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\n" //
1520 		+ "abcdefghijklmnopqrstuvwxyz\n1234567890 \n" //
1521 		+ "\"!`?'.,;:()[]{}<>|/@\\^$-%+=#_&~*\u0000\u007F";
1522 	static public final String EXTENDED_CHARS;
1523 
1524 	static {
1525 		StringBuilder buffer = new StringBuilder();
1526 		int i = 0;
1527 		for (int c : new int[] {0, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
1528 			57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86,
1529 			87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113,
1530 			114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170,
1531 			171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194,
1532 			195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218,
1533 			219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242,
1534 			243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266,
1535 			267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290,
1536 			291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314,
1537 			315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338,
1538 			339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362,
1539 			363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 884, 885, 890,
1540 			891, 892, 893, 894, 900, 901, 902, 903, 904, 905, 906, 908, 910, 911, 912, 913, 914, 915, 916, 917, 918, 919, 920, 921,
1541 			922, 923, 924, 925, 926, 927, 928, 929, 931, 932, 933, 934, 935, 936, 937, 938, 939, 940, 941, 942, 943, 944, 945, 946,
1542 			947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 970,
1543 			971, 972, 973, 974, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 986, 987, 988, 989, 990, 991, 992, 993, 994, 995,
1544 			996, 997, 998, 999, 1000, 1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008, 1009, 1010, 1011, 1012, 1013, 1014, 1015, 1016,
1545 			1017, 1018, 1019, 1020, 1021, 1022, 1023, 1024, 1025, 1026, 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1034, 1035, 1036,
1546 			1037, 1038, 1039, 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1050, 1051, 1052, 1053, 1054, 1055, 1056,
1547 			1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, 1066, 1067, 1068, 1069, 1070, 1071, 1072, 1073, 1074, 1075, 1076,
1548 			1077, 1078, 1079, 1080, 1081, 1082, 1083, 1084, 1085, 1086, 1087, 1088, 1089, 1090, 1091, 1092, 1093, 1094, 1095, 1096,
1549 			1097, 1098, 1099, 1100, 1101, 1102, 1103, 1104, 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1114, 1115, 1116,
1550 			1117, 1118, 1119, 1120, 1121, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1130, 1131, 1132, 1133, 1134, 1135, 1136,
1551 			1137, 1138, 1139, 1140, 1141, 1142, 1143, 1144, 1145, 1146, 1147, 1148, 1149, 1150, 1151, 1152, 1153, 1154, 1155, 1156,
1552 			1157, 1158, 1159, 1160, 1161, 1162, 1163, 1164, 1165, 1166, 1167, 1168, 1169, 1170, 1171, 1172, 1173, 1174, 1175, 1176,
1553 			1177, 1178, 1179, 1180, 1181, 1182, 1183, 1184, 1185, 1186, 1187, 1188, 1189, 1190, 1191, 1192, 1193, 1194, 1195, 1196,
1554 			1197, 1198, 1199, 1200, 1201, 1202, 1203, 1204, 1205, 1206, 1207, 1208, 1209, 1210, 1211, 1212, 1213, 1214, 1215, 1216,
1555 			1217, 1218, 1219, 1220, 1221, 1222, 1223, 1224, 1225, 1226, 1227, 1228, 1229, 1230, 1231, 1232, 1233, 1234, 1235, 1236,
1556 			1237, 1238, 1239, 1240, 1241, 1242, 1243, 1244, 1245, 1246, 1247, 1248, 1249, 1250, 1251, 1252, 1253, 1254, 1255, 1256,
1557 			1257, 1258, 1259, 1260, 1261, 1262, 1263, 1264, 1265, 1266, 1267, 1268, 1269, 1270, 1271, 1272, 1273, 1274, 1275, 1276,
1558 			1277, 1278, 1279, 1280, 1281, 1282, 1283, 1284, 1285, 1286, 1287, 1288, 1289, 1290, 1291, 1292, 1293, 1294, 1295, 1296,
1559 			1297, 1298, 1299, 1300, 1301, 1302, 1303, 1304, 1305, 1306, 1307, 1308, 1309, 1310, 1311, 1312, 1313, 1314, 1315, 1316,
1560 			1317, 1318, 1319, 8192, 8193, 8194, 8195, 8196, 8197, 8198, 8199, 8200, 8201, 8202, 8203, 8204, 8205, 8206, 8207, 8210,
1561 			8211, 8212, 8213, 8214, 8215, 8216, 8217, 8218, 8219, 8220, 8221, 8222, 8223, 8224, 8225, 8226, 8230, 8234, 8235, 8236,
1562 			8237, 8238, 8239, 8240, 8242, 8243, 8244, 8249, 8250, 8252, 8254, 8260, 8286, 8298, 8299, 8300, 8301, 8302, 8303, 8352,
1563 			8353, 8354, 8355, 8356, 8357, 8358, 8359, 8360, 8361, 8363, 8364, 8365, 8366, 8367, 8368, 8369, 8370, 8371, 8372, 8373,
1564 			8377, 8378, 11360, 11361, 11362, 11363, 11364, 11365, 11366, 11367, 11368, 11369, 11370, 11371, 11372, 11373, 11377,
1565 			11378, 11379, 11380, 11381, 11382, 11383}) {
1566 			i++;
1567 			if (i > 26) {
1568 				i = 0;
1569 				buffer.append("\r\n");
1570 			}
1571 			buffer.append((char)c);
1572 		}
1573 		EXTENDED_CHARS = buffer.toString();
1574 	}
1575 
main(final String[] args)1576 	public static void main (final String[] args) throws Exception {
1577 		SwingUtilities.invokeLater(new Runnable() {
1578 			public void run () {
1579 				new Hiero(args);
1580 			}
1581 		});
1582 	}
1583 }
1584