001 /*
002 * Copyright (C) 2003 Adam Olsen
003 * This program is free software; you can redistribute it and/or modify
004 * it under the terms of the GNU General Public License as published by
005 * the Free Software Foundation; either version 1, or (at your option)
006 * any later version.
007 * This program is distributed in the hope that it will be useful,
008 * but WITHOUT ANY WARRANTY; without even the implied warranty of
009 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
010 * GNU General Public License for more details.
011 * You should have received a copy of the GNU General Public License
012 * along with this program; if not, write to the Free Software
013 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
014 */
015 package com.valhalla.jbother.preferences;
016
017 import java.awt.Component;
018 import java.awt.Container;
019 import java.awt.Font;
020 import java.awt.Frame;
021 import java.awt.GridBagConstraints;
022 import java.awt.GridBagLayout;
023 import java.awt.Window;
024 import java.awt.event.ActionEvent;
025 import java.awt.event.ActionListener;
026 import java.io.BufferedReader;
027 import java.io.File;
028 import java.io.IOException;
029 import java.io.InputStream;
030 import java.io.InputStreamReader;
031 import java.util.*;
032
033 import javax.swing.BorderFactory;
034 import javax.swing.JButton;
035 import javax.swing.JComboBox;
036 import javax.swing.JComponent;
037 import javax.swing.JLabel;
038 import javax.swing.JMenu;
039 import javax.swing.JPanel;
040 import javax.swing.JToolBar;
041 import javax.swing.UIManager;
042
043 import org.jfree.ui.FontChooserDialog;
044
045 import com.valhalla.gui.CopyPasteContextMenu;
046 import com.valhalla.jbother.BuddyList;
047 import com.valhalla.jbother.ConversationFormatter;
048 import com.valhalla.jbother.JBother;
049 import com.valhalla.jbother.JBotherLoader;
050 import com.valhalla.jbother.jabber.BuddyStatus;
051 import com.valhalla.settings.Settings;
052 import com.valhalla.settings.TempSettings;
053
054 /**
055 * Allows the user to change appearance preferences
056 *
057 * @author Adam Olsen
058 * @created March 9, 2005
059 * @version 1.0
060 */
061 public class AppearancePreferencesPanel extends JPanel implements
062 PreferencesPanel {
063 private ResourceBundle resources = ResourceBundle.getBundle(
064 "JBotherBundle", Locale.getDefault());
065
066 private PreferencesDialog prefs;
067
068 private GridBagLayout grid = new GridBagLayout();
069
070 private GridBagConstraints c = new GridBagConstraints();
071
072 private JLabel lafLabel = new JLabel(resources.getString("themeLabel")
073 + ": ");
074
075 private JLabel statusLabel = new JLabel(resources.getString("statusLabel")
076 + ": ");
077
078 private JLabel emoticonLabel = new JLabel(resources
079 .getString("emoticonLabel")
080 + ": ");
081
082 private JLabel messageLabel = new JLabel(resources
083 .getString("messageWindowFont")
084 + ": ");
085
086 private JButton messageFontButton = new JButton("Font");
087
088 private JLabel appFontLabel = new JLabel(resources
089 .getString("appFontLabel")
090 + ": ");
091
092 private JButton appFontButton = new JButton("Font");
093
094 private JComboBox lookAndFeel, statusTheme, emoticonTheme;
095
096 private int current = -1;
097
098 private UIManager.LookAndFeelInfo[] lfs;
099
100 private String[] names;
101
102 /**
103 * Sets up the AppearancePreferences
104 *
105 * @param dialog
106 * the enclosing PreferencesDialog
107 */
108 public AppearancePreferencesPanel(PreferencesDialog dialog) {
109 this.prefs = dialog;
110 setBorder(BorderFactory.createTitledBorder(resources
111 .getString("appearancePreferences")));
112 setLayout(grid);
113
114 lfs = UIManager.getInstalledLookAndFeels();
115 String lf = UIManager.getLookAndFeel().getClass().getName();
116
117 ArrayList list = new ArrayList();
118 Properties displayed = new Properties();
119
120 int index = 0;
121 for (int i = 0; i < lfs.length; i++) {
122
123 if( displayed.getProperty( lfs[i].getName() ) == null )
124 {
125 list.add(lfs[i].getName());
126
127 if (lf.equals(lfs[i].getClassName()) && current == -1) {
128 index = i;
129 current = index;
130 }
131 }
132
133 displayed.setProperty(lfs[i].getName(), "1" );
134 }
135
136 if (current == -1)
137 index = 0;
138
139 names = new String[list.size()];
140 list.toArray(names);
141
142 lookAndFeel = new JComboBox(names);
143 lookAndFeel.setSelectedIndex(index);
144 lookAndFeel.validate();
145 lookAndFeel.repaint();
146
147 c.gridx = 0;
148 c.gridy = 0;
149 c.fill = GridBagConstraints.HORIZONTAL;
150 c.anchor = GridBagConstraints.WEST;
151 c.gridwidth = 1;
152
153 //display name stuff
154 lafLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
155 c.weightx = -.1;
156 c.fill = GridBagConstraints.NONE;
157 grid.setConstraints(lafLabel, c);
158 add(lafLabel);
159
160 c.gridx = 1;
161
162 grid.setConstraints(lookAndFeel, c);
163 add(lookAndFeel);
164
165 c.gridx = 0;
166 c.gridy++;
167
168 statusLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
169 grid.setConstraints(statusLabel, c);
170 add(statusLabel);
171
172 statusTheme = getStatusThemes();
173 c.gridx++;
174 grid.setConstraints(statusTheme, c);
175 add(statusTheme);
176
177 c.gridx = 0;
178 c.gridy++;
179
180 emoticonLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
181 grid.setConstraints(emoticonLabel, c);
182 add(emoticonLabel);
183
184 emoticonTheme = getEmoticonThemes();
185 c.gridx++;
186 grid.setConstraints(emoticonTheme, c);
187 add(emoticonTheme);
188
189 FontChangeListener fontListener = new FontChangeListener();
190
191 // get the message window font settings
192 String messageFont = Settings.getInstance().getProperty(
193 "messageWindowFont");
194 if (messageFont == null) {
195 messageFont = "Default-PLAIN-12";
196 }
197
198 Font font = Font.decode(messageFont);
199 messageFontButton
200 .setText(getEncodedFontName(font).replaceAll("-", " "));
201 messageFontButton.setFont(font);
202
203 c.gridx = 0;
204 c.gridy++;
205 messageLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
206 grid.setConstraints(messageLabel, c);
207 add(messageLabel);
208 c.gridx++;
209 messageFontButton.addActionListener(fontListener);
210 grid.setConstraints(messageFontButton, c);
211 add(messageFontButton);
212
213 // get the application font settings
214 String appFont = Settings.getInstance().getProperty("applicationFont");
215 if (appFont == null) {
216 appFont = "Default-PLAIN-12";
217 }
218
219 font = Font.decode(appFont);
220 appFontButton.setText(getEncodedFontName(font).replaceAll("-", " "));
221 appFontButton.setFont(font);
222
223 c.gridx = 0;
224 c.gridy++;
225 appFontLabel.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
226 grid.setConstraints(appFontLabel, c);
227 add(appFontLabel);
228 c.gridx++;
229 appFontButton.addActionListener(fontListener);
230 grid.setConstraints(appFontButton, c);
231 add(appFontButton);
232
233 //this is the space taker
234 JLabel blankLabel = new JLabel("");
235 c.weighty = 1;
236 c.weightx = 1;
237 c.gridx = 0;
238 c.gridwidth = 3;
239 c.gridy++;
240 grid.setConstraints(blankLabel, c);
241 add(blankLabel);
242 }
243
244 /**
245 * Listens for one of the font buttons to be clicked and displays a
246 * FontChooserDialog for it
247 *
248 * @author Adam Olsen
249 * @created March 9, 2005
250 * @version 1.0
251 */
252 private class FontChangeListener implements ActionListener {
253 /**
254 * Description of the Method
255 *
256 * @param e
257 * Description of the Parameter
258 */
259 public void actionPerformed(ActionEvent e) {
260 JButton button = (JButton) e.getSource();
261 FontChooserDialog dialog = new FontChooserDialog(prefs, resources
262 .getString("messageWindowFont"), true, button.getFont());
263
264 dialog.setVisible(true);
265
266 if (!dialog.isCancelled()) {
267 Font newFont = dialog.getSelectedFont();
268 button.setFont(newFont);
269 button
270 .setText(getEncodedFontName(newFont).replaceAll("-",
271 " "));
272 }
273 }
274 }
275
276 /**
277 * Checks the default jar file and the user theme directory for status
278 * themes
279 *
280 * @return The statusThemes value
281 */
282 private JComboBox getStatusThemes() {
283 JComboBox box = new JComboBox();
284 String current = Settings.getInstance().getProperty("statusTheme");
285 if (current == null) {
286 current = "default";
287 }
288 box.addItem(current);
289
290 // user defined themes
291 File path = new File(JBother.settingsDir + File.separatorChar
292 + "themes" + File.separatorChar + "statusicons");
293
294 String[] userThemes = new String[0];
295
296 if (!path.isDirectory() && !path.mkdirs()) {
297 com.valhalla.Logger
298 .debug("Could not create user defined settings directories.");
299 } else {
300 userThemes = path.list();
301 for (int i = 0; i < userThemes.length; i++) {
302 if (!current.equals(userThemes[i])) {
303 box.addItem(userThemes[i]);
304 }
305 }
306 }
307
308 // default themes in the jar
309 InputStream stream = getClass().getClassLoader().getResourceAsStream(
310 "imagethemes/statusicons/index.dat");
311 if (stream == null) {
312 com.valhalla.Logger.debug("Bad status theme file.");
313 return box;
314 }
315
316 InputStreamReader in = new InputStreamReader(stream);
317 BufferedReader reader = new BufferedReader(in);
318 String line;
319 try {
320 while ((line = reader.readLine()) != null) {
321 boolean userTheme = false;
322 for (int i = 0; i < userThemes.length; i++) {
323 if (line.equals(userThemes[i])) {
324 userTheme = true;
325 break;
326 }
327 }
328 if (!userTheme && !line.equals(current)) {
329 box.addItem(line);
330 }
331 }
332 } catch (IOException e) {
333 }
334
335 return box;
336 }
337
338 /**
339 * Gets a list of emoticon themes
340 *
341 * @return The emoticonThemes value
342 */
343 private JComboBox getEmoticonThemes() {
344 JComboBox box = new JComboBox();
345 String current = Settings.getInstance().getProperty("emoticonTheme");
346 if (current == null) {
347 current = "default";
348 }
349 box.addItem(current);
350
351 // default themes in the jar
352 InputStream stream = getClass().getClassLoader().getResourceAsStream(
353 "imagethemes/emoticons/index.dat");
354 if (stream == null) {
355 com.valhalla.Logger.debug("Bad emoticon theme file.");
356 return box;
357 }
358
359 InputStreamReader in = new InputStreamReader(stream);
360 BufferedReader reader = new BufferedReader(in);
361 String line;
362 try {
363 while ((line = reader.readLine()) != null) {
364 if (!line.equals(current)) {
365 box.addItem(line);
366 }
367 }
368 } catch (IOException e) {
369 }
370
371 return box;
372 }
373
374 /**
375 * Gets the String representation of a font
376 *
377 * @param font
378 * Description of the Parameter
379 * @return the string representation of a font
380 */
381 private String getEncodedFontName(Font font) {
382 String fontString = font.getName() + "-";
383
384 if (font.isPlain()) {
385 fontString += "PLAIN";
386 }
387 if (font.isBold()) {
388 fontString += "BOLD";
389 }
390 if (font.isItalic()) {
391 fontString += "ITALIC";
392 }
393 fontString += "-" + font.getSize();
394
395 return fontString;
396 }
397
398 /**
399 * Gets temporary settings
400 *
401 * @return temporary settings
402 */
403 public TempSettings getSettings() {
404 TempSettings tempSettings = new TempSettings();
405
406 if (lookAndFeel.getSelectedIndex() != current) {
407 //Standard.noticeMessage( prefs, resources.getString( "themeLabel"
408 // ),
409 // resources.getString( "themeSettingsApplied" ) );
410
411 String laf = lfs[lookAndFeel.getSelectedIndex()].getClassName();
412
413 JBotherLoader.loadLAF(laf);
414 tempSettings.setProperty("lookAndFeel", laf);
415
416 current = lookAndFeel.getSelectedIndex();
417 }
418
419 tempSettings.setProperty("messageWindowFont",
420 getEncodedFontName(messageFontButton.getFont()));
421 // update all open conversation areas
422 Hashtable buddyStatuses = BuddyList.getInstance().getBuddyStatuses();
423 if (buddyStatuses != null) {
424 Iterator i = buddyStatuses.keySet().iterator();
425
426 while (i.hasNext()) {
427 BuddyStatus buddy = BuddyList.getInstance().getBuddyStatus(
428 (String) i.next());
429 if (buddy.getConversation() != null) {
430 buddy.getConversation().updateStyle(
431 messageFontButton.getFont());
432 }
433 }
434 }
435
436 if (BuddyList.getInstance().getTabFrame() != null) {
437 BuddyList.getInstance().getTabFrame().updateStyles(
438 messageFontButton.getFont());
439 }
440
441 tempSettings.setProperty("applicationFont",
442 getEncodedFontName(appFontButton.getFont()));
443 updateApplicationFonts(appFontButton.getFont(), prefs);
444
445 String selectedStatus = (String) statusTheme.getItemAt(statusTheme
446 .getSelectedIndex());
447 tempSettings.setProperty("statusTheme", selectedStatus);
448 tempSettings.setProperty("emoticonTheme", (String) emoticonTheme
449 .getItemAt(emoticonTheme.getSelectedIndex()));
450
451 BuddyList.getInstance().getBuddyListTree().repaint();
452 if (BuddyList.getInstance().getTabFrame() != null) {
453 BuddyList.getInstance().getTabFrame().repaint();
454 }
455 BuddyList.getInstance().updateButtons(selectedStatus);
456
457 ConversationFormatter.getInstance().switchTheme(
458 tempSettings.getProperty("emoticonTheme"));
459
460 return tempSettings;
461 }
462
463 /**
464 * Updates all the fonts in the application
465 *
466 * @param font
467 * the font to update to
468 * @param prefs
469 * Description of the Parameter
470 */
471 public static void updateApplicationFonts(Font font, PreferencesDialog prefs) {
472 com.valhalla.jbother.JBotherLoader.setupFont(font);
473 updateAllUIs();
474
475 if (prefs != null) {
476 updateComponentTreeUI0(prefs.getTree());
477 Iterator i = prefs.getPanels().keySet().iterator();
478 while (i.hasNext()) {
479 updateComponentTreeUI0((Component) prefs.getPanels().get(
480 i.next()));
481 }
482
483 i = PreferencesDialog.getPluginPanels().keySet().iterator();
484 while (i.hasNext()) {
485 updateComponentTreeUI0((Component) prefs.getPanels().get(
486 i.next()));
487 }
488 }
489 }
490
491 /**
492 * Method to attempt a dynamic update for any GUI accessible by this JVM. It
493 * will filter through all frames and sub-components of the frames.
494 */
495 public static void updateAllUIs() {
496 CopyPasteContextMenu.newInstance();
497 Frame frames[];
498 int i1;
499 frames = Frame.getFrames();
500 i1 = 0;
501
502 for (int i = 0; i < frames.length; i++) {
503 updateWindowUI(frames[i]);
504 }
505 }
506
507 /**
508 * Method to attempt a dynamic update for all components of the given
509 * <code>Window</code>.
510 *
511 * @param window
512 * The <code>Window</code> for which the look and feel update
513 * has to be performed against.
514 */
515 public static void updateWindowUI(Window window) {
516 try {
517 updateComponentTreeUI(window);
518 } catch (Exception exception) {
519 }
520
521 Window windows[] = window.getOwnedWindows();
522
523 for (int i = 0; i < windows.length; i++) {
524 updateWindowUI(windows[i]);
525 }
526 }
527
528 /**
529 * A simple minded look and feel change: ask each node in the tree to
530 * <code>updateUI()</code>-- that is, to initialize its UI property with
531 * the current look and feel. Based on the Sun
532 * SwingUtilities.updateComponentTreeUI, but ensures that the update happens
533 * on the components of a JToolbar before the JToolbar itself.
534 *
535 * @param c
536 * Description of the Parameter
537 */
538 public static void updateComponentTreeUI(Component c) {
539 updateComponentTreeUI0(c);
540 c.invalidate();
541 c.validate();
542 c.repaint();
543 }
544
545 /**
546 * Description of the Method
547 *
548 * @param c
549 * Description of the Parameter
550 */
551 private static void updateComponentTreeUI0(Component c) {
552
553 Component[] children = null;
554
555 if (c instanceof JToolBar) {
556 children = ((JToolBar) c).getComponents();
557
558 if (children != null) {
559 for (int i = 0; i < children.length; i++) {
560 updateComponentTreeUI0(children[i]);
561 }
562 }
563
564 ((JComponent) c).updateUI();
565 } else {
566 if (c instanceof JComponent) {
567 ((JComponent) c).updateUI();
568 }
569
570 if (c instanceof JMenu) {
571 children = ((JMenu) c).getMenuComponents();
572 } else if (c instanceof Container) {
573 children = ((Container) c).getComponents();
574 }
575
576 if (children != null) {
577 for (int i = 0; i < children.length; i++) {
578 updateComponentTreeUI0(children[i]);
579 }
580 }
581 }
582 }
583 }
584