OSDN Git Service

Set optimal mime types and executable settings.
[mikumikustudio/MikuMikuStudio.git] / src / com / jme / system / PropertiesDialog2.java
1 /*
2  * Copyright (c) 2003-2009 jMonkeyEngine
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  * * Redistributions of source code must retain the above copyright
10  *   notice, this list of conditions and the following disclaimer.
11  *
12  * * Redistributions in binary form must reproduce the above copyright
13  *   notice, this list of conditions and the following disclaimer in the
14  *   documentation and/or other materials provided with the distribution.
15  *
16  * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
17  *   may be used to endorse or promote products derived from this software
18  *   without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
22  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 package com.jme.system;
34
35 import java.awt.BorderLayout;
36 import java.awt.DisplayMode;
37 import java.awt.GraphicsEnvironment;
38 import java.awt.Toolkit;
39 import java.awt.event.ActionEvent;
40 import java.awt.event.ActionListener;
41 import java.awt.event.WindowAdapter;
42 import java.awt.event.WindowEvent;
43 import java.net.MalformedURLException;
44 import java.net.URL;
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Comparator;
48 import java.util.logging.Logger;
49 import java.util.logging.Level;
50 import java.io.IOException;
51
52 import javax.swing.DefaultComboBoxModel;
53 import javax.swing.ImageIcon;
54 import javax.swing.JButton;
55 import javax.swing.JCheckBox;
56 import javax.swing.JComboBox;
57 import javax.swing.JDialog;
58 import javax.swing.JLabel;
59 import javax.swing.JOptionPane;
60 import javax.swing.JPanel;
61 import javax.swing.UIManager;
62
63 /**
64  * <code>PropertiesDialog</code> provides an interface to make use of the
65  * <code>GameSettings</code> class. It provides a simple clean method of
66  * creating a properties file. The <code>GameSettings</code> is still created
67  * by the client application, and passed during construction.
68  * 
69  * @see com.jme.system.GameSettings
70  * @author Mark Powell
71  * @author Eric Woroshow
72  * @version $Id: PropertiesDialog2.java,v 1.7 2007/08/02 22:14:06 nca Exp $
73  */
74 public final class PropertiesDialog2 extends JDialog {
75     private static final Logger logger = Logger.getLogger(PropertiesDialog2.class.getName());
76
77     private static final long serialVersionUID = 1L;
78
79     //connection to properties file.
80     private final GameSettings source;
81
82     //Title Image
83     private URL imageFile = null;
84     
85     //Array of supported display modes
86     private final DisplayMode[] modes;
87
88     //UI components
89     private JCheckBox fullscreenBox = null;
90     private JComboBox displayResCombo = null;
91     private JComboBox colorDepthCombo = null;
92     private JComboBox displayFreqCombo = null;
93     private JComboBox rendererCombo = null;
94     private JLabel icon = null;
95     
96     /**
97      * Constructor for the <code>PropertiesDialog</code>. Creates a
98      * properties dialog initialized for the primary display.
99      * @param source the <code>GameSettings</code> object to use for working with
100      *               the properties file.
101      * @param imageFile the image file to use as the title of the dialog;
102      *                  <code>null</code> will result in to image being displayed
103      * @throws JmeException if the source is <code>null</code>
104      */
105     public PropertiesDialog2(GameSettings source, String imageFile) {
106         this(source, getURL(imageFile));
107     }
108
109
110     /**
111      * Constructor for the <code>PropertiesDialog</code>. Creates a
112      * properties dialog initialized for the primary display.
113      * @param source the <code>GameSettings</code> object to use for working with
114      *               the properties file.
115      * @param imageFile the image file to use as the title of the dialog;
116      *                  <code>null</code> will result in to image being displayed
117      * @throws JmeException if the source is <code>null</code>
118      */
119     public PropertiesDialog2(GameSettings source, URL imageFile) {
120         if (null == source)
121                 throw new JmeException("PropertyIO source cannot be null");
122
123         this.source = source;
124         this.imageFile = imageFile;
125         this.modes = GraphicsEnvironment.getLocalGraphicsEnvironment()
126                      .getDefaultScreenDevice().getDisplayModes();
127         Arrays.sort(modes, new DisplayModeSorter());
128
129         createUI();
130     }
131
132     /**
133      * <code>setImage</code> sets the background image of the dialog.
134      * @param image <code>String</code> representing the image file.
135      */
136     public void setImage(String image) {
137         try {
138             URL file = new URL("file:" + image);
139             setImage(file);
140             //We can safely ignore the exception - it just means that the user gave us a bogus file
141         } catch (MalformedURLException e) {}
142     }
143
144     /**
145      * <code>setImage</code> sets the background image of this dialog.
146      * @param image <code>URL</code> pointing to the image file.
147      */
148     public void setImage(URL image) {
149         icon.setIcon(new ImageIcon(image));
150         pack(); //Resize to accomodate the new image
151         center();
152     }
153
154     /**
155      * <code>showDialog</code> sets this dialog as visble, and brings it to the
156      * front.
157      */
158     private void showDialog() {
159         setVisible(true);
160         toFront();
161     }
162
163     /**
164      * <code>center</code> places this <code>PropertiesDialog</code> in the
165      * center of the screen.
166      */
167     private void center() {
168         int x, y;
169         x = (Toolkit.getDefaultToolkit().getScreenSize().width - this.getWidth()) / 2;
170         y = (Toolkit.getDefaultToolkit().getScreenSize().height - this.getHeight()) / 2;
171         this.setLocation(x, y);
172     }
173
174     /**
175      * <code>init</code> creates the components to use the dialog.
176      */
177     private void createUI() {
178         try {
179             UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
180         } catch (Exception e) {
181             logger.warning("Could not set native look and feel.");
182         }
183
184         addWindowListener(new WindowAdapter() {
185             public void windowClosing(WindowEvent e) {
186                 dispose();
187                 System.exit(0);
188             }
189         });
190
191         setTitle("Select Display Settings");
192
193         //The panels...
194         JPanel mainPanel = new JPanel();
195         JPanel centerPanel = new JPanel();
196         JPanel optionsPanel = new JPanel();
197         JPanel buttonPanel = new JPanel();
198         //The buttons...
199         JButton ok = new JButton("Ok");
200         JButton cancel = new JButton("Cancel");
201         
202         icon = new JLabel(new ImageIcon(imageFile));
203
204         mainPanel.setLayout(new BorderLayout());
205         
206         centerPanel.setLayout(new BorderLayout());
207         
208         displayResCombo = setUpResolutionChooser();
209         colorDepthCombo = new JComboBox();
210         displayFreqCombo = new JComboBox();
211         fullscreenBox = new JCheckBox("Fullscreen?");
212         fullscreenBox.setSelected(source.isFullscreen());
213         rendererCombo = setUpRendererChooser();
214         
215         updateDisplayChoices();
216         
217         optionsPanel.add(displayResCombo);
218         optionsPanel.add(colorDepthCombo);
219         optionsPanel.add(displayFreqCombo);
220         optionsPanel.add(fullscreenBox);
221         optionsPanel.add(rendererCombo);
222
223         //Set the button action listeners. Cancel disposes without saving, OK saves.
224         ok.addActionListener(new ActionListener() {
225             public void actionPerformed(ActionEvent e) {
226                 if (verifyAndSaveCurrentSelection())
227                     dispose();
228             }
229         });
230
231         cancel.addActionListener(new ActionListener() {
232             public void actionPerformed(ActionEvent e) {
233                 dispose();
234                 System.exit(0);
235             }
236         });
237
238         buttonPanel.add(ok);
239         buttonPanel.add(cancel);
240         
241         centerPanel.add(icon, BorderLayout.NORTH);
242         centerPanel.add(optionsPanel, BorderLayout.SOUTH);
243
244         mainPanel.add(centerPanel, BorderLayout.CENTER);
245         mainPanel.add(buttonPanel, BorderLayout.SOUTH);
246
247         this.getContentPane().add(mainPanel);
248
249         pack();
250         center();
251         showDialog();
252     }
253
254     /**
255      * <code>verifyAndSaveCurrentSelection</code> first verifies that the
256      * display mode is valid for this system, and then saves the current
257      * selection as a properties.cfg file.
258      * 
259      * @return if the selection is valid
260      */
261     private boolean verifyAndSaveCurrentSelection() {
262         String display = (String) displayResCombo.getSelectedItem();
263
264         int width = Integer.parseInt(display.substring(0, display.indexOf(" x ")));
265         display = display.substring(display.indexOf(" x ") + 3);
266         int height = Integer.parseInt(display);
267
268         String depthString = (String) colorDepthCombo.getSelectedItem();
269         int depth = Integer.parseInt(depthString.substring(0, depthString.indexOf(' ')));
270
271         String freqString = (String) displayFreqCombo.getSelectedItem();
272         int freq = Integer.parseInt(freqString.substring(0, freqString.indexOf(' ')));
273
274         boolean fullscreen = fullscreenBox.isSelected();
275         if (!fullscreen) {
276             //query the current bit depth of the desktop
277             int curDepth = GraphicsEnvironment.getLocalGraphicsEnvironment()
278                     .getDefaultScreenDevice().getDisplayMode().getBitDepth();
279             if (depth > curDepth) {
280                 showError(this,"Cannot choose a higher bit depth in windowed " +
281                                "mode than your current desktop bit depth");
282                 return false;
283             }            
284         }
285         
286         String renderer = (String) rendererCombo.getSelectedItem();
287
288         //test valid display mode
289         DisplaySystem disp = DisplaySystem.getDisplaySystem(renderer);
290         boolean valid = (disp != null) ? disp.isValidDisplayMode(width, height, depth, freq) : false;
291
292         if (valid) {
293             //use the GameSettings class to save it.
294             source.setWidth(width);
295             source.setHeight(height);
296             source.setDepth(depth);
297             source.setFrequency(freq);
298             source.setFullscreen(fullscreen);
299             source.setRenderer(renderer);
300             try {
301                 source.save();
302             } catch (IOException ioe) {
303                 logger.log(Level.WARNING,
304                         "Failed to save setting changes", ioe);
305             }
306         } else
307             showError(this, "Your monitor claims to not support the display mode you've selected.\n" +
308                             "The combination of bit depth and refresh rate is not supported.");
309
310         return valid;
311     }
312
313     /**
314      * <code>setUpChooser</code> retrieves all available display modes and
315      * places them in a <code>JComboBox</code>. The resolution specified
316      * by GameSettings is used as the default value.
317      * @return the combo box of display modes.
318      */
319     private JComboBox setUpResolutionChooser() {
320         String[] res = getResolutions(modes);
321         JComboBox resolutionBox = new JComboBox(res);
322
323         resolutionBox.setSelectedItem(source.getWidth() + " x " + source.getHeight());
324         resolutionBox.addActionListener(new ActionListener() {
325             public void actionPerformed(ActionEvent e) {
326                 updateDisplayChoices();
327             }
328         });
329
330         return resolutionBox;
331     }
332
333     /**
334      * <code>setUpRendererChooser</code> sets the list of available renderers.
335      * Data is obtained from the <code>DisplaySystem</code> class. The renderer
336      * specified by GameSettings is used as the default value.
337      * @return the list of renderers.
338      */
339     private JComboBox setUpRendererChooser() {
340         String modes[] = DisplaySystem.getSystemProviderIdentifiers();
341         JComboBox nameBox = new JComboBox(modes);
342         nameBox.setSelectedItem(source.getRenderer());
343         return nameBox;
344     }
345     
346     private void updateDisplayChoices() {
347         String resolution = (String)displayResCombo.getSelectedItem();
348         //grab available depths
349         String[] depths = getDepths(resolution, modes);
350         colorDepthCombo.setModel(new DefaultComboBoxModel(depths));
351         colorDepthCombo.setSelectedItem(source.getDepth() + " bpp");
352         //grab available frequencies
353         String[] freqs = getFrequencies(resolution, modes);
354         displayFreqCombo.setModel(new DefaultComboBoxModel(freqs));
355         displayFreqCombo.setSelectedItem(source.getFrequency() + " Hz");
356     }
357     
358     //
359     //Utility methods
360     //
361     
362     /**
363      * Utility method for converting a String denoting a file
364      * into a URL.
365      * @return a URL pointing to the file or null
366      */
367     private static URL getURL(String file) {
368         URL url = null;
369         try {
370             url = new URL("file:" + file);
371         } catch (MalformedURLException e) {}
372         return url;
373     }
374     
375     private static void showError(java.awt.Component parent, String message) {
376         JOptionPane.showMessageDialog(
377                 parent,
378                 message,
379                 "Error",
380                 JOptionPane.ERROR_MESSAGE);
381     }
382     
383     /**
384      * Reutrns every unique resolution from an array of <code>DisplayMode</code>s.
385      */
386     private static String[] getResolutions(DisplayMode[] modes) {
387         ArrayList<String> resolutions = new ArrayList<String>(16);
388         for (int i = 0; i < modes.length; i++) {
389             String res = modes[i].getWidth() + " x " + modes[i].getHeight();
390             if (!resolutions.contains(res))
391                 resolutions.add(res);
392         }
393         
394         String[] res = new String[resolutions.size()];
395         resolutions.toArray(res);
396         return res;
397     }
398     
399     /**
400      * Returns every possible bit depth for the given resolution.
401      */
402     private static String[] getDepths(String resolution, DisplayMode[] modes) {
403         ArrayList<String> depths = new ArrayList<String>(4);
404         for (int i = 0; i < modes.length; i++) {
405             //Filter out all bit depths lower than 16 - Java incorrectly reports
406             //them as valid depths though the monitor does not support them
407             if (modes[i].getBitDepth() < 16) continue;
408             
409             String res = modes[i].getWidth() + " x " + modes[i].getHeight();
410             String depth = modes[i].getBitDepth() + " bpp";
411             if (res.equals(resolution) && !depths.contains(depth))
412                 depths.add(depth);
413         }
414         
415         String[] res = new String[depths.size()];
416         depths.toArray(res);
417         return res;
418     }
419     
420     /**
421      * Returns every possible refresh rate for the given resolution.
422      */
423     private static String[] getFrequencies(String resolution, DisplayMode[] modes) {
424         ArrayList<String> freqs = new ArrayList<String>(4);
425         for (int i = 0; i < modes.length; i++) {
426             String res = modes[i].getWidth() + " x " + modes[i].getHeight();
427             String freq = modes[i].getRefreshRate() + " Hz";
428             if (res.equals(resolution) && !freqs.contains(freq))
429                 freqs.add(freq);
430         }
431         
432         String[] res = new String[freqs.size()];
433         freqs.toArray(res);
434         return res;
435     }
436     
437     /**
438      * Utility class for sorting <code>DisplayMode</code>s. Sorts by resolution,
439      * then bit depth, and then finally refresh rate.
440      */
441     private class DisplayModeSorter implements Comparator<DisplayMode> {
442         /**
443          * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
444          */
445         public int compare(DisplayMode a, DisplayMode b) {
446             //Width
447             if (a.getWidth() != b.getWidth())
448                 return (a.getWidth() > b.getWidth()) ?  1 : -1;
449             //Height
450             if (a.getHeight() != b.getHeight())
451                 return (a.getHeight() > b.getHeight()) ?  1 : -1;
452             //Bit depth
453             if (a.getBitDepth() != b.getBitDepth())
454                 return (a.getBitDepth() > b.getBitDepth()) ?  1 : -1;
455             //Refresh rate
456             if (a.getRefreshRate() != b.getRefreshRate())
457                 return (a.getRefreshRate() > b.getRefreshRate()) ?  1 : -1;
458             //All fields are equal
459             return 0;
460         }
461     }
462 }