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.views.controls;
22 
23 import java.awt.Component;
24 import java.awt.ComponentOrientation;
25 import java.awt.Container;
26 import java.awt.Dialog.ModalityType;
27 import java.awt.HeadlessException;
28 import java.awt.event.InputEvent;
29 import java.awt.event.KeyEvent;
30 import java.io.File;
31 
32 import javax.swing.BorderFactory;
33 import javax.swing.JDialog;
34 import javax.swing.JFileChooser;
35 import javax.swing.JList;
36 import javax.swing.JScrollPane;
37 import javax.swing.JSplitPane;
38 import javax.swing.JTable;
39 import javax.swing.JTextArea;
40 import javax.swing.JTextField;
41 import javax.swing.JTextPane;
42 import javax.swing.JTree;
43 import javax.swing.KeyStroke;
44 import javax.swing.SwingConstants;
45 import javax.swing.text.DefaultEditorKit;
46 import javax.swing.text.JTextComponent;
47 import javax.swing.text.StyleConstants;
48 import javax.swing.tree.TreeCellRenderer;
49 
50 import net.sourceforge.atunes.model.IBeanFactory;
51 import net.sourceforge.atunes.model.IButtonPanel;
52 import net.sourceforge.atunes.model.IColumnModel;
53 import net.sourceforge.atunes.model.IColumnSetPopupMenu;
54 import net.sourceforge.atunes.model.IColumnSetTableModel;
55 import net.sourceforge.atunes.model.IControlsBuilder;
56 import net.sourceforge.atunes.model.IDesktop;
57 import net.sourceforge.atunes.model.ILocaleBean;
58 import net.sourceforge.atunes.model.ILookAndFeelManager;
59 import net.sourceforge.atunes.model.IOSManager;
60 import net.sourceforge.atunes.model.IPopUpButton;
61 import net.sourceforge.atunes.model.IStateCore;
62 import net.sourceforge.atunes.model.ITreeCellRendererCode;
63 import net.sourceforge.atunes.utils.ClipboardFacade;
64 
65 /**
66  * Factory to build UI components
67  *
68  * @author alex
69  *
70  */
71 public class ControlsBuilder implements IControlsBuilder {
72 
73 	private static final JTextComponent.KeyBinding[] MAC_OS_BINDINGS = {
74 			new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_C,
75 					InputEvent.CTRL_MASK),
76 					DefaultEditorKit.defaultKeyTypedAction),
77 			new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V,
78 					InputEvent.CTRL_MASK),
79 					DefaultEditorKit.defaultKeyTypedAction),
80 			new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_X,
81 					InputEvent.CTRL_MASK),
82 					DefaultEditorKit.defaultKeyTypedAction),
83 			new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_A,
84 					InputEvent.CTRL_MASK),
85 					DefaultEditorKit.defaultKeyTypedAction),
86 			new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(
87 					KeyEvent.VK_LEFT, InputEvent.CTRL_MASK),
88 					DefaultEditorKit.defaultKeyTypedAction),
89 			new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(
90 					KeyEvent.VK_RIGHT, InputEvent.CTRL_MASK),
91 					DefaultEditorKit.defaultKeyTypedAction),
92 
93 			new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_C,
94 					InputEvent.META_MASK), DefaultEditorKit.copyAction),
95 			new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_V,
96 					InputEvent.META_MASK), DefaultEditorKit.pasteAction),
97 			new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_X,
98 					InputEvent.META_MASK), DefaultEditorKit.cutAction),
99 			new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(KeyEvent.VK_A,
100 					InputEvent.META_MASK), DefaultEditorKit.selectAllAction),
101 			new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(
102 					KeyEvent.VK_LEFT, InputEvent.META_MASK),
103 					DefaultEditorKit.beginLineAction),
104 			new JTextComponent.KeyBinding(KeyStroke.getKeyStroke(
105 					KeyEvent.VK_RIGHT, InputEvent.META_MASK),
106 					DefaultEditorKit.endLineAction) };
107 
108 	private IOSManager osManager;
109 
110 	private ILookAndFeelManager lookAndFeelManager;
111 
112 	private ClipboardFacade clipboard;
113 
114 	private IBeanFactory beanFactory;
115 
116 	/** The component orientation. */
117 	private ComponentOrientation componentOrientation;
118 
119 	private IDesktop desktop;
120 
121 	/**
122 	 * @param desktop
123 	 */
setDesktop(final IDesktop desktop)124 	public void setDesktop(final IDesktop desktop) {
125 		this.desktop = desktop;
126 	}
127 
128 	/**
129 	 * @param beanFactory
130 	 */
setBeanFactory(final IBeanFactory beanFactory)131 	public void setBeanFactory(final IBeanFactory beanFactory) {
132 		this.beanFactory = beanFactory;
133 	}
134 
135 	/**
136 	 * @param clipboard
137 	 */
setClipboard(final ClipboardFacade clipboard)138 	public void setClipboard(final ClipboardFacade clipboard) {
139 		this.clipboard = clipboard;
140 	}
141 
142 	/**
143 	 * @param lookAndFeelManager
144 	 */
setLookAndFeelManager( final ILookAndFeelManager lookAndFeelManager)145 	public void setLookAndFeelManager(
146 			final ILookAndFeelManager lookAndFeelManager) {
147 		this.lookAndFeelManager = lookAndFeelManager;
148 	}
149 
150 	/**
151 	 * @param osManager
152 	 */
setOsManager(final IOSManager osManager)153 	public void setOsManager(final IOSManager osManager) {
154 		this.osManager = osManager;
155 	}
156 
157 	@Override
createTextArea()158 	public JTextArea createTextArea() {
159 		JTextArea textArea = new JTextArea();
160 		new EditionPopUpMenu(textArea, this.clipboard, this.osManager);
161 		return textArea;
162 	}
163 
164 	@Override
createTextField()165 	public JTextField createTextField() {
166 		JTextField textField = new JTextField();
167 		initializeTextField(textField);
168 		new EditionPopUpMenu(textField, this.clipboard, this.osManager);
169 		return textField;
170 	}
171 
172 	@Override
createTextPane(final Integer alignJustified)173 	public JTextPane createTextPane(final Integer alignJustified) {
174 		CustomTextPane textPane = new CustomTextPane(alignJustified, this);
175 		// Register look and feel change listener
176 		this.lookAndFeelManager.addLookAndFeelChangeListener(textPane);
177 		new EditionPopUpMenu(textPane, this.clipboard, this.osManager);
178 		return textPane;
179 	}
180 
181 	@Override
createReadOnlyTextPane(final String text)182 	public JTextPane createReadOnlyTextPane(final String text) {
183 		JTextPane textPane = createTextPane(null);
184 		textPane.setEditable(false);
185 		textPane.setBorder(BorderFactory.createEmptyBorder());
186 		textPane.setOpaque(false);
187 		textPane.setText(text);
188 		return textPane;
189 	}
190 
191 	/**
192 	 * Sets custom properties to text fields
193 	 *
194 	 * @param customTextField
195 	 */
initializeTextField(final JTextField customTextField)196 	private void initializeTextField(final JTextField customTextField) {
197 		if (this.osManager.isMacOsX()) {
198 			JTextComponent.loadKeymap(customTextField.getKeymap(),
199 					MAC_OS_BINDINGS, customTextField.getActions());
200 		}
201 	}
202 
203 	@Override
createPlayPauseButton()204 	public PlayPauseButton createPlayPauseButton() {
205 		return this.beanFactory.getBean(PlayPauseButton.class);
206 	}
207 
208 	@Override
createNextButton()209 	public NextButton createNextButton() {
210 		return this.beanFactory.getBean(NextButton.class);
211 	}
212 
213 	@Override
createPreviousButton()214 	public PreviousButton createPreviousButton() {
215 		return this.beanFactory.getBean(PreviousButton.class);
216 	}
217 
218 	@Override
createStopButton()219 	public StopButton createStopButton() {
220 		return this.beanFactory.getBean(StopButton.class);
221 	}
222 
223 	@Override
applyComponentOrientation(final Container... containers)224 	public void applyComponentOrientation(final Container... containers) {
225 		if (this.componentOrientation == null) {
226 			setComponentOrientation();
227 		}
228 		for (Container container : containers) {
229 			container.applyComponentOrientation(this.componentOrientation);
230 		}
231 	}
232 
233 	@Override
getComponentOrientation()234 	public ComponentOrientation getComponentOrientation() {
235 		if (this.componentOrientation == null) {
236 			setComponentOrientation();
237 		}
238 		return this.componentOrientation;
239 	}
240 
241 	/**
242 	 * Returns the component orientation as a SwingConstant.
243 	 *
244 	 * @param locale
245 	 * @return The component orientation as a SwingConstant
246 	 */
247 	@Override
getComponentOrientationAsSwingConstant()248 	public int getComponentOrientationAsSwingConstant() {
249 		if (this.componentOrientation == null) {
250 			setComponentOrientation();
251 		}
252 		return this.componentOrientation.isLeftToRight() ? SwingConstants.LEFT
253 				: SwingConstants.RIGHT;
254 	}
255 
256 	/**
257 	 * Returns the component orientation as a text style constant.
258 	 *
259 	 * @return The component orientation as a SwingConstant
260 	 */
261 	@Override
getComponentOrientationAsTextStyleConstant()262 	public int getComponentOrientationAsTextStyleConstant() {
263 		if (this.componentOrientation == null) {
264 			setComponentOrientation();
265 		}
266 		return this.componentOrientation.isLeftToRight() ? StyleConstants.ALIGN_LEFT
267 				: StyleConstants.ALIGN_RIGHT;
268 	}
269 
270 	/**
271 	 * Sets the component orientation.
272 	 *
273 	 * @param locale
274 	 */
setComponentOrientation()275 	private void setComponentOrientation() {
276 		this.componentOrientation = ComponentOrientation.LEFT_TO_RIGHT;
277 		ILocaleBean locale = this.beanFactory.getBean(IStateCore.class)
278 				.getLocale();
279 		if (locale != null) {
280 			if ("ug".equalsIgnoreCase(locale.getLocale().getLanguage())) {
281 				this.componentOrientation = ComponentOrientation.RIGHT_TO_LEFT;
282 			} else {
283 				this.componentOrientation = ComponentOrientation
284 						.getOrientation(locale.getLocale());
285 			}
286 		}
287 	}
288 
289 	@Override
createPopUpButton(final int menuPosition)290 	public IPopUpButton createPopUpButton(final int menuPosition) {
291 		return new PopUpButton(menuPosition, this.lookAndFeelManager, this);
292 	}
293 
294 	@Override
createSplitPane(final int type)295 	public JSplitPane createSplitPane(final int type) {
296 		return new CustomSplitPane(type, this, this.lookAndFeelManager);
297 	}
298 
299 	@Override
getSplitPaneDividerSize()300 	public int getSplitPaneDividerSize() {
301 		return new CustomSplitPane(JSplitPane.HORIZONTAL_SPLIT, this,
302 				this.lookAndFeelManager).getDividerSize();
303 	}
304 
305 	@Override
createColumnSetPopupMenu(final JTable table, final IColumnModel columnModel, final IColumnSetTableModel tableModel)306 	public IColumnSetPopupMenu createColumnSetPopupMenu(final JTable table,
307 			final IColumnModel columnModel,
308 			final IColumnSetTableModel tableModel) {
309 		ColumnSetPopupMenu popup = this.beanFactory
310 				.getBean(ColumnSetPopupMenu.class);
311 		popup.bindTo(table, columnModel, tableModel);
312 		return popup;
313 	}
314 
315 	@Override
createButtonPanel()316 	public IButtonPanel createButtonPanel() {
317 		ToggleButtonFlowPanel panel = this.beanFactory
318 				.getBean(ToggleButtonFlowPanel.class);
319 		this.lookAndFeelManager.addLookAndFeelChangeListener(panel);
320 		return panel;
321 	}
322 
323 	@Override
getFileChooser()324 	public JFileChooser getFileChooser() {
325 		return new CustomFileChooser();
326 	}
327 
328 	@Override
getFileChooser(final String path, final String name)329 	public JFileChooser getFileChooser(final String path, final String name) {
330 		return new CustomFileChooser(path, name);
331 	}
332 
333 	@SuppressWarnings("rawtypes")
334 	@Override
createScrollPane(final Component component)335 	public JScrollPane createScrollPane(final Component component) {
336 		if (component instanceof JTable) {
337 			return this.lookAndFeelManager.getCurrentLookAndFeel()
338 					.getTableScrollPane((JTable) component);
339 		} else if (component instanceof JTree) {
340 			return this.lookAndFeelManager.getCurrentLookAndFeel()
341 					.getTreeScrollPane((JTree) component);
342 		} else if (component instanceof JList) {
343 			return this.lookAndFeelManager.getCurrentLookAndFeel()
344 					.getListScrollPane((JList) component);
345 		} else {
346 			return this.lookAndFeelManager.getCurrentLookAndFeel()
347 					.getScrollPane(component);
348 		}
349 	}
350 
351 	@Override
getUrlLabel(final String text, final String url)352 	public UrlLabel getUrlLabel(final String text, final String url) {
353 		return new UrlLabel(this.desktop, text, url);
354 	}
355 
356 	@Override
getUrlLabel()357 	public UrlLabel getUrlLabel() {
358 		return new UrlLabel(this.desktop);
359 	}
360 
361 	@Override
getTreeCellRenderer( final ITreeCellRendererCode<?, ?> code)362 	public TreeCellRenderer getTreeCellRenderer(
363 			final ITreeCellRendererCode<?, ?> code) {
364 		return this.lookAndFeelManager.getCurrentLookAndFeel()
365 				.getTreeCellRenderer(code);
366 	}
367 
368 	@Override
createTable()369 	public JTable createTable() {
370 		JTable table = new JTable();
371 		this.lookAndFeelManager.getCurrentLookAndFeel().decorateTable(table);
372 		return table;
373 	}
374 
375 	private static class CustomFileChooser extends JFileChooser {
376 		/**
377 		 *
378 		 */
379 		private static final long serialVersionUID = -5589049318786579475L;
380 
381 		/**
382 		 * @param path
383 		 * @param name
384 		 */
CustomFileChooser(final String path, final String name)385 		public CustomFileChooser(final String path, final String name) {
386 			super(path);
387 			if (name != null) {
388 				setSelectedFile(new File(name));
389 			}
390 		}
391 
392 		/**
393 		 * Default constructor
394 		 */
CustomFileChooser()395 		public CustomFileChooser() {
396 			super();
397 		}
398 
399 		@Override
createDialog(final Component parent)400 		protected JDialog createDialog(final Component parent)
401 				throws HeadlessException {
402 			JDialog dialog = super.createDialog(parent);
403 			// Use DOCUMENT_MODAL to avoid problems with Linux platforms:
404 			// ERROR: Trying to unblock window blocked by another dialog
405 			dialog.setModalityType(ModalityType.DOCUMENT_MODAL);
406 			return dialog;
407 		}
408 	};
409 }
410