1 /*
2  * aTunes
3  * Copyright (C) Alex Aranda, Sylvain Gaudard and contributors
4  *
5  * See http://www.atunes.org/wiki/index.php?title=Contributing for information about contributors
6  *
7  * http://www.atunes.org
8  * http://sourceforge.net/projects/atunes
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  */
20 
21 package net.sourceforge.atunes.gui.lookandfeel;
22 
23 import java.awt.Color;
24 import java.awt.Font;
25 import java.awt.Window;
26 import java.util.ArrayList;
27 import java.util.List;
28 import java.util.Map;
29 
30 import javax.swing.BorderFactory;
31 import javax.swing.SwingUtilities;
32 import javax.swing.UIManager;
33 import javax.swing.plaf.ColorUIResource;
34 
35 import net.sourceforge.atunes.gui.GuiUtils;
36 import net.sourceforge.atunes.model.FontSettings;
37 import net.sourceforge.atunes.model.IApplicationArguments;
38 import net.sourceforge.atunes.model.IBeanFactory;
39 import net.sourceforge.atunes.model.IFontBeanFactory;
40 import net.sourceforge.atunes.model.ILookAndFeel;
41 import net.sourceforge.atunes.model.ILookAndFeelChangeListener;
42 import net.sourceforge.atunes.model.ILookAndFeelManager;
43 import net.sourceforge.atunes.model.IOSManager;
44 import net.sourceforge.atunes.model.IStateCore;
45 import net.sourceforge.atunes.model.IStateUI;
46 import net.sourceforge.atunes.model.LookAndFeelBean;
47 import net.sourceforge.atunes.utils.Logger;
48 
49 /**
50  * Responsible of change and manage look and feel
51  *
52  * @author alex
53  *
54  */
55 public final class LookAndFeelManager implements
56 		ILookAndFeelManager {
57 
58 	private static final boolean USE_FONT_SMOOTHING_SETTINGS_FROM_OS_DEFAULT_VALUE = false;
59 	private static final boolean USE_FONT_SMOOTHING_DEFAULT_VALUE = true;
60 
61 	/**
62 	 * Current look and feel
63 	 */
64 	private ILookAndFeel currentLookAndFeel;
65 
66 	/**
67 	 * Map containing look and feels
68 	 */
69 	private Map<String, Class<? extends ILookAndFeel>> lookAndFeels;
70 
71 	/**
72 	 * Default look and feel
73 	 */
74 	private Class<? extends ILookAndFeel> defaultLookAndFeelClass;
75 
76 	/**
77 	 * Look and Feel change listeners
78 	 */
79 	private List<ILookAndFeelChangeListener> changeListeners;
80 
81 	private IApplicationArguments applicationArguments;
82 
83 	private IBeanFactory beanFactory;
84 
85 	private IOSManager osManager;
86 
87 	/**
88 	 * @param osManager
89 	 */
setOsManager(final IOSManager osManager)90 	public void setOsManager(final IOSManager osManager) {
91 		this.osManager = osManager;
92 	}
93 
94 	/**
95 	 * @param beanFactory
96 	 */
setBeanFactory(final IBeanFactory beanFactory)97 	public void setBeanFactory(final IBeanFactory beanFactory) {
98 		this.beanFactory = beanFactory;
99 	}
100 
101 	/**
102 	 * Initialize manager
103 	 */
initialize()104 	public void initialize() {
105 		this.lookAndFeels = this.osManager.getLookAndFeels();
106 		this.defaultLookAndFeelClass = this.osManager.getDefaultLookAndFeel();
107 	}
108 
109 	/**
110 	 * @param applicationArguments
111 	 */
setApplicationArguments( final IApplicationArguments applicationArguments)112 	public void setApplicationArguments(
113 			final IApplicationArguments applicationArguments) {
114 		this.applicationArguments = applicationArguments;
115 	}
116 
117 	@Override
setLookAndFeel(final LookAndFeelBean bean, final IStateCore stateCore, final IStateUI stateUI, final IOSManager osManager)118 	public void setLookAndFeel(final LookAndFeelBean bean,
119 			final IStateCore stateCore, final IStateUI stateUI,
120 			final IOSManager osManager) {
121 		if (this.applicationArguments.isIgnoreLookAndFeel()) {
122 			return;
123 		}
124 
125 		LookAndFeelBean lookAndFeelBean = bean;
126 		if (lookAndFeelBean == null || lookAndFeelBean.getName() == null) {
127 			lookAndFeelBean = new LookAndFeelBean();
128 			ILookAndFeel defaultLookAndFeel = null;
129 			try {
130 				defaultLookAndFeel = this.defaultLookAndFeelClass.newInstance();
131 			} catch (InstantiationException e) {
132 				Logger.error(e);
133 			} catch (IllegalAccessException e) {
134 				Logger.error(e);
135 			}
136 			lookAndFeelBean.setName(defaultLookAndFeel.getName());
137 			lookAndFeelBean.setSkin(defaultLookAndFeel.getDefaultSkin());
138 			if (stateUI.getLookAndFeel() == null) {
139 				stateUI.setLookAndFeel(lookAndFeelBean);
140 			}
141 		}
142 
143 		Class<? extends ILookAndFeel> currentLookAndFeelClass = this.lookAndFeels
144 				.get(lookAndFeelBean.getName());
145 		if (currentLookAndFeelClass == null) {
146 			currentLookAndFeelClass = this.defaultLookAndFeelClass;
147 		}
148 
149 		try {
150 			this.currentLookAndFeel = currentLookAndFeelClass.newInstance();
151 			this.currentLookAndFeel.setOsManager(osManager);
152 			this.currentLookAndFeel.setBeanFactory(this.beanFactory);
153 		} catch (InstantiationException e) {
154 			Logger.error(e);
155 		} catch (IllegalAccessException e) {
156 			Logger.error(e);
157 		}
158 
159 		this.currentLookAndFeel.initializeLookAndFeel(this.beanFactory);
160 		this.currentLookAndFeel.setLookAndFeel(lookAndFeelBean.getSkin());
161 		initializeFonts(this.currentLookAndFeel, stateCore, stateUI);
162 		initializeColors();
163 	}
164 
165 	/**
166 	 * Color initialization for some ui components
167 	 */
initializeColors()168 	private void initializeColors() {
169 		UIManager.put("ToolTip.border",
170 				BorderFactory.createLineBorder(GuiUtils.getBorderColor()));
171 		UIManager.put("ToolTip.background", new ColorUIResource(Color.WHITE));
172 		UIManager.put("ToolTip.foreground", new ColorUIResource(Color.BLACK));
173 	}
174 
175 	/**
176 	 * Initializes fonts for look and feel
177 	 *
178 	 * @param lookAndFeel
179 	 * @param state
180 	 * @param stateUI
181 	 */
initializeFonts(final ILookAndFeel lookAndFeel, final IStateCore stateCore, final IStateUI stateUI)182 	private void initializeFonts(final ILookAndFeel lookAndFeel,
183 			final IStateCore stateCore, final IStateUI stateUI) {
184 		FontSettings fontSettings = stateUI.getFontSettings();
185 
186 		setAntialiasingProperties(lookAndFeel, fontSettings);
187 
188 		Font font = UIManager.getFont("Label.font");
189 		if (lookAndFeel.supportsCustomFontSettings()) {
190 			if (fontSettings != null) {
191 				font = fontSettings.getFont().toFont();
192 			} else {
193 				font = getFontToUse(lookAndFeel, stateCore.getLocale()
194 						.getLanguage());
195 				stateUI.setFontSettings(new FontSettings(this.beanFactory
196 						.getBean(IFontBeanFactory.class).getFontBean(font),
197 						USE_FONT_SMOOTHING_DEFAULT_VALUE,
198 						USE_FONT_SMOOTHING_SETTINGS_FROM_OS_DEFAULT_VALUE));
199 			}
200 		}
201 		lookAndFeel.setBaseFont(font);
202 		lookAndFeel.initializeFonts(font);
203 	}
204 
getFontToUse(final ILookAndFeel lookAndFeel, String language)205 	private Font getFontToUse(final ILookAndFeel lookAndFeel, String language) {
206 		/*
207 		 * Get appropriate font for the currently selected language. For Chinese
208 		 * or Japanese we should use default font.
209 		 */
210 		if ("zh".equals(language) || "ja".equals(language)) {
211 			return new Font(Font.SANS_SERIF, Font.PLAIN, 12);
212 		} else {
213 			return lookAndFeel.getSuggestedFont();
214 		}
215 	}
216 
setAntialiasingProperties(final ILookAndFeel lookAndFeel, FontSettings fontSettings)217 	private void setAntialiasingProperties(final ILookAndFeel lookAndFeel,
218 			FontSettings fontSettings) {
219 		if (lookAndFeel.supportsCustomFontSettings() && fontSettings != null
220 				&& !fontSettings.isUseFontSmoothingSettingsFromOs()) {
221 			if (fontSettings.isUseFontSmoothing()) {
222 				System.setProperty("awt.useSystemAAFontSettings", "lcd");
223 			} else {
224 				System.setProperty("awt.useSystemAAFontSettings", "false");
225 			}
226 		} else {
227 			System.setProperty("awt.useSystemAAFontSettings", "lcd");
228 		}
229 	}
230 
231 	@Override
getAvailableLookAndFeels()232 	public List<String> getAvailableLookAndFeels() {
233 		return new ArrayList<String>(this.lookAndFeels.keySet());
234 	}
235 
236 	@Override
getAvailableSkins(final String lookAndFeelName)237 	public List<String> getAvailableSkins(final String lookAndFeelName) {
238 		Class<? extends ILookAndFeel> clazz = this.lookAndFeels
239 				.get(lookAndFeelName);
240 		if (clazz != null) {
241 			ILookAndFeel lookAndFeel = null;
242 			try {
243 				lookAndFeel = clazz.newInstance();
244 			} catch (InstantiationException e) {
245 				Logger.error(e);
246 			} catch (IllegalAccessException e) {
247 				Logger.error(e);
248 			}
249 			if (lookAndFeel != null) {
250 				return lookAndFeel.getSkins() != null ? lookAndFeel.getSkins()
251 						: new ArrayList<String>();
252 			}
253 		}
254 		return new ArrayList<String>();
255 	}
256 
257 	@Override
getCurrentLookAndFeelName()258 	public String getCurrentLookAndFeelName() {
259 		return this.currentLookAndFeel.getName();
260 	}
261 
262 	@Override
applySkin(final String selectedSkin, final IStateCore stateCore, final IStateUI stateUI, final IOSManager osManager)263 	public void applySkin(final String selectedSkin,
264 			final IStateCore stateCore, final IStateUI stateUI,
265 			final IOSManager osManager) {
266 		LookAndFeelBean bean = new LookAndFeelBean();
267 		bean.setName(this.currentLookAndFeel.getName());
268 		bean.setSkin(selectedSkin);
269 		setLookAndFeel(bean, stateCore, stateUI, osManager);
270 		for (Window window : Window.getWindows()) {
271 			// Only update displayable windows
272 			// References to disposed windows (not displayable) are kept so
273 			// calling to update those windows can throw exceptions or errors
274 			if (window.isDisplayable()) {
275 				SwingUtilities.updateComponentTreeUI(window);
276 			}
277 		}
278 		// Notify listeners
279 		for (ILookAndFeelChangeListener listener : getChangeListeners()) {
280 			listener.lookAndFeelChanged();
281 		}
282 	}
283 
284 	@Override
getCurrentLookAndFeel()285 	public ILookAndFeel getCurrentLookAndFeel() {
286 		return this.currentLookAndFeel;
287 	}
288 
289 	@Override
getDefaultSkin(final String lookAndFeelName)290 	public String getDefaultSkin(final String lookAndFeelName) {
291 		Class<? extends ILookAndFeel> clazz = this.lookAndFeels
292 				.get(lookAndFeelName);
293 		if (clazz != null) {
294 			ILookAndFeel lookAndFeel = null;
295 			try {
296 				lookAndFeel = clazz.newInstance();
297 			} catch (InstantiationException e) {
298 				Logger.error(e);
299 			} catch (IllegalAccessException e) {
300 				Logger.error(e);
301 			}
302 			if (lookAndFeel != null) {
303 				return lookAndFeel.getDefaultSkin();
304 			}
305 		}
306 		return null;
307 	}
308 
309 	@Override
getDefaultLookAndFeel()310 	public ILookAndFeel getDefaultLookAndFeel() {
311 		try {
312 			return this.defaultLookAndFeelClass.newInstance();
313 		} catch (InstantiationException e) {
314 			Logger.error(e);
315 		} catch (IllegalAccessException e) {
316 			Logger.error(e);
317 		}
318 		return null;
319 	}
320 
321 	/**
322 	 * @return the changeListeners
323 	 */
getChangeListeners()324 	protected List<ILookAndFeelChangeListener> getChangeListeners() {
325 		if (this.changeListeners == null) {
326 			this.changeListeners = new ArrayList<ILookAndFeelChangeListener>();
327 		}
328 		return this.changeListeners;
329 	}
330 
331 	@Override
addLookAndFeelChangeListener( final ILookAndFeelChangeListener listener)332 	public void addLookAndFeelChangeListener(
333 			final ILookAndFeelChangeListener listener) {
334 		getChangeListeners().add(listener);
335 	}
336 
337 	@Override
removeLookAndFeelChangeListener( final ILookAndFeelChangeListener listener)338 	public void removeLookAndFeelChangeListener(
339 			final ILookAndFeelChangeListener listener) {
340 		getChangeListeners().remove(listener);
341 	}
342 }
343