~ubuntu-branches/ubuntu/utopic/sweethome3d/utopic

« back to all changes in this revision

Viewing changes to .pc/01noMacOSX/src/com/eteks/sweethome3d/HomeFramePane.java

  • Committer: Package Import Robot
  • Author(s): Gabriele Giacone
  • Date: 2013-11-05 13:02:16 UTC
  • mfrom: (1.1.14)
  • Revision ID: package-import@ubuntu.com-20131105130216-0e7dgqqvdq4pwm96
Tags: 4.2+dfsg-1
New upstream release.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * HomeFramePane.java 1 sept. 2006
 
3
 *
 
4
 * Sweet Home 3D, Copyright (c) 2006 Emmanuel PUYBARET / eTeks <info@eteks.com>
 
5
 *
 
6
 * This program is free software; you can redistribute it and/or modify
 
7
 * it under the terms of the GNU General Public License as published by
 
8
 * the Free Software Foundation; either version 2 of the License, or
 
9
 * (at your option) any later version.
 
10
 *
 
11
 * This program is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
14
 * GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License
 
17
 * along with this program; if not, write to the Free Software
 
18
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
19
 */
 
20
package com.eteks.sweethome3d;
 
21
 
 
22
import java.awt.Component;
 
23
import java.awt.ComponentOrientation;
 
24
import java.awt.Dimension;
 
25
import java.awt.EventQueue;
 
26
import java.awt.Frame;
 
27
import java.awt.Image;
 
28
import java.awt.Insets;
 
29
import java.awt.Rectangle;
 
30
import java.awt.Window;
 
31
import java.awt.event.ActionEvent;
 
32
import java.awt.event.ActionListener;
 
33
import java.awt.event.ComponentAdapter;
 
34
import java.awt.event.ComponentEvent;
 
35
import java.awt.event.WindowAdapter;
 
36
import java.awt.event.WindowEvent;
 
37
import java.beans.PropertyChangeEvent;
 
38
import java.beans.PropertyChangeListener;
 
39
import java.io.File;
 
40
import java.lang.ref.WeakReference;
 
41
import java.util.ArrayList;
 
42
import java.util.List;
 
43
import java.util.Locale;
 
44
 
 
45
import javax.swing.ImageIcon;
 
46
import javax.swing.JComponent;
 
47
import javax.swing.JFrame;
 
48
import javax.swing.JRootPane;
 
49
import javax.swing.Timer;
 
50
 
 
51
import com.eteks.sweethome3d.model.CollectionEvent;
 
52
import com.eteks.sweethome3d.model.CollectionListener;
 
53
import com.eteks.sweethome3d.model.Home;
 
54
import com.eteks.sweethome3d.model.HomeApplication;
 
55
import com.eteks.sweethome3d.model.UserPreferences;
 
56
import com.eteks.sweethome3d.swing.SwingTools;
 
57
import com.eteks.sweethome3d.tools.OperatingSystem;
 
58
import com.eteks.sweethome3d.viewcontroller.ContentManager;
 
59
import com.eteks.sweethome3d.viewcontroller.HomeController;
 
60
import com.eteks.sweethome3d.viewcontroller.HomeView;
 
61
import com.eteks.sweethome3d.viewcontroller.View;
 
62
 
 
63
/**
 
64
 * A pane that displays a 
 
65
 * {@link com.eteks.sweethome3d.swing.HomePane home pane} in a frame.
 
66
 * @author Emmanuel Puybaret
 
67
 */
 
68
public class HomeFramePane extends JRootPane implements View {
 
69
  private static final String FRAME_X_VISUAL_PROPERTY         = "com.eteks.sweethome3d.SweetHome3D.FrameX";
 
70
  private static final String FRAME_Y_VISUAL_PROPERTY         = "com.eteks.sweethome3d.SweetHome3D.FrameY";
 
71
  private static final String FRAME_WIDTH_VISUAL_PROPERTY     = "com.eteks.sweethome3d.SweetHome3D.FrameWidth";
 
72
  private static final String FRAME_HEIGHT_VISUAL_PROPERTY    = "com.eteks.sweethome3d.SweetHome3D.FrameHeight";
 
73
  private static final String FRAME_MAXIMIZED_VISUAL_PROPERTY = "com.eteks.sweethome3d.SweetHome3D.FrameMaximized";
 
74
  private static final String SCREEN_WIDTH_VISUAL_PROPERTY    = "com.eteks.sweethome3d.SweetHome3D.ScreenWidth";
 
75
  private static final String SCREEN_HEIGHT_VISUAL_PROPERTY   = "com.eteks.sweethome3d.SweetHome3D.ScreenHeight";
 
76
  
 
77
  private final Home                    home;
 
78
  private final HomeApplication         application;
 
79
  private final ContentManager          contentManager;
 
80
  private final HomeFrameController     controller;
 
81
  private static int                    newHomeCount;
 
82
  private int                           newHomeNumber;
 
83
  
 
84
  public HomeFramePane(Home home,
 
85
                       HomeApplication application,
 
86
                       ContentManager contentManager, 
 
87
                       HomeFrameController controller) {
 
88
    this.home = home;
 
89
    this.controller = controller;
 
90
    this.application = application;
 
91
    this.contentManager = contentManager;
 
92
    // If home is unnamed, give it a number
 
93
    if (home.getName() == null) {
 
94
      this.newHomeNumber = ++newHomeCount;
 
95
    }
 
96
    // Set controller view as content pane
 
97
    HomeView homeView = this.controller.getHomeController().getView();
 
98
    setContentPane((JComponent)homeView);
 
99
  }
 
100
 
 
101
  /**
 
102
   * Builds and shows the frame that displays this pane.
 
103
   */
 
104
  public void displayView() {
 
105
    final JFrame homeFrame = new JFrame() {
 
106
      {
 
107
        // Replace frame rootPane by home controller view
 
108
        setRootPane(HomeFramePane.this);
 
109
      }
 
110
    };
 
111
    // Update frame image and title 
 
112
    List<Image> frameImages = new ArrayList<Image>(3);
 
113
    frameImages.add(new ImageIcon(HomeFramePane.class.getResource("resources/frameIcon.png")).getImage());
 
114
    frameImages.add(new ImageIcon(HomeFramePane.class.getResource("resources/frameIcon32x32.png")).getImage());
 
115
    if (OperatingSystem.isMacOSXLeopardOrSuperior()) {
 
116
      frameImages.add(new ImageIcon(HomeFramePane.class.getResource("resources/frameIcon128x128.png")).getImage());
 
117
    }
 
118
    try {
 
119
      // Call Java 1.6 setIconImages by reflection
 
120
      homeFrame.getClass().getMethod("setIconImages", List.class).invoke(homeFrame, frameImages);
 
121
    } catch (Exception ex) {
 
122
      // Call setIconImage available in previous versions
 
123
      homeFrame.setIconImage(frameImages.get(0));
 
124
    }
 
125
    if (OperatingSystem.isMacOSXLionOrSuperior()) {
 
126
      MacOSXConfiguration.installToolBar(this);
 
127
    }
 
128
    updateFrameTitle(homeFrame, this.home, this.application);
 
129
    // Change component orientation
 
130
    applyComponentOrientation(ComponentOrientation.getOrientation(Locale.getDefault()));    
 
131
    // Compute frame size and location
 
132
    computeFrameBounds(this.home, homeFrame);
 
133
    // Enable windows to update their content while window resizing
 
134
    getToolkit().setDynamicLayout(true); 
 
135
    // The best MVC solution should be to avoid the following statements 
 
136
    // but Mac OS X accepts to display the menu bar of a frame in the screen 
 
137
    // menu bar only if this menu bar depends directly on its root pane  
 
138
    HomeView homeView = this.controller.getHomeController().getView();
 
139
    if (homeView instanceof JRootPane) {
 
140
      JRootPane homePane = (JRootPane)homeView;
 
141
      setJMenuBar(homePane.getJMenuBar());
 
142
      homePane.setJMenuBar(null);
 
143
    }
 
144
    
 
145
    // Add listeners to model and frame    
 
146
    addListeners(this.home, this.application, this.controller.getHomeController(), homeFrame);
 
147
    
 
148
    homeFrame.setVisible(true);
 
149
    // Request the frame to go to front again because closing waiting dialog meanwhile  
 
150
    // could put in front the already opened frame  
 
151
    EventQueue.invokeLater(new Runnable() {
 
152
        public void run() {
 
153
          homeFrame.toFront();
 
154
        }
 
155
      });
 
156
  }
 
157
  
 
158
  /**
 
159
   * Adds listeners to <code>frame</code> and model objects.
 
160
   */
 
161
  private void addListeners(final Home home,
 
162
                            final HomeApplication application,
 
163
                            final HomeController controller,
 
164
                            final JFrame frame) {
 
165
    // Add a listener that keeps track of window location and size
 
166
    frame.addComponentListener(new ComponentAdapter() {
 
167
        @Override
 
168
        public void componentResized(ComponentEvent ev) {
 
169
          // Store new size only if frame isn't maximized
 
170
          if ((frame.getExtendedState() & JFrame.MAXIMIZED_BOTH) != JFrame.MAXIMIZED_BOTH) {
 
171
            controller.setVisualProperty(FRAME_WIDTH_VISUAL_PROPERTY, frame.getWidth());
 
172
            controller.setVisualProperty(FRAME_HEIGHT_VISUAL_PROPERTY, frame.getHeight());
 
173
          }
 
174
          Dimension userScreenSize = getUserScreenSize();
 
175
          controller.setVisualProperty(SCREEN_WIDTH_VISUAL_PROPERTY, userScreenSize.width);
 
176
          controller.setVisualProperty(SCREEN_HEIGHT_VISUAL_PROPERTY, userScreenSize.height);
 
177
        }
 
178
        
 
179
        @Override
 
180
        public void componentMoved(ComponentEvent ev) {
 
181
          // Store new location only if frame isn't maximized
 
182
          if ((frame.getExtendedState() & JFrame.MAXIMIZED_BOTH) != JFrame.MAXIMIZED_BOTH) {
 
183
            controller.setVisualProperty(FRAME_X_VISUAL_PROPERTY, frame.getX());
 
184
            controller.setVisualProperty(FRAME_Y_VISUAL_PROPERTY, frame.getY());
 
185
          }
 
186
        }
 
187
      });
 
188
    // Control frame closing and activation 
 
189
    frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
 
190
    WindowAdapter windowListener = new WindowAdapter () {
 
191
        private Component mostRecentFocusOwner;
 
192
 
 
193
        @Override
 
194
        public void windowStateChanged(WindowEvent ev) {
 
195
          controller.setVisualProperty(FRAME_MAXIMIZED_VISUAL_PROPERTY, 
 
196
              (frame.getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH);
 
197
        }
 
198
        
 
199
        @Override
 
200
        public void windowClosing(WindowEvent ev) {
 
201
          controller.close();
 
202
        }
 
203
        
 
204
        @Override
 
205
        public void windowDeactivated(WindowEvent ev) {
 
206
          // Java 3D 1.5 bug : windowDeactivated notifications should not be sent to this frame
 
207
          // while canvases 3D are created in a child modal dialog like the one managing 
 
208
          // ImportedFurnitureWizardStepsPanel. As this makes Swing loose the most recent focus owner
 
209
          // let's store it in a field to use it when this frame will be reactivated. 
 
210
          Component mostRecentFocusOwner = frame.getMostRecentFocusOwner();          
 
211
          if (!(mostRecentFocusOwner instanceof JFrame)
 
212
              && mostRecentFocusOwner != null) {
 
213
            this.mostRecentFocusOwner = mostRecentFocusOwner;
 
214
          }
 
215
        }
 
216
 
 
217
        @Override
 
218
        public void windowActivated(WindowEvent ev) {                    
 
219
          // Java 3D 1.5 bug : let's request focus in window for the most recent focus owner when
 
220
          // this frame is reactivated
 
221
          if (this.mostRecentFocusOwner != null) {
 
222
            EventQueue.invokeLater(new Runnable() {
 
223
                public void run() {
 
224
                  mostRecentFocusOwner.requestFocusInWindow();
 
225
                }
 
226
              });
 
227
          }
 
228
        } 
 
229
      };
 
230
    frame.addWindowListener(windowListener);    
 
231
    frame.addWindowStateListener(windowListener);    
 
232
    // Add a listener to preferences to apply component orientation to frame matching current language
 
233
    application.getUserPreferences().addPropertyChangeListener(UserPreferences.Property.LANGUAGE, 
 
234
        new LanguageChangeListener(frame, this));
 
235
    // Dispose window when a home is deleted 
 
236
    application.addHomesListener(new CollectionListener<Home>() {
 
237
        public void collectionChanged(CollectionEvent<Home> ev) {
 
238
          if (ev.getItem() == home
 
239
              && ev.getType() == CollectionEvent.Type.DELETE) {
 
240
            application.removeHomesListener(this);
 
241
            frame.dispose();
 
242
          }
 
243
        };
 
244
      });
 
245
    // Update title when the name or the modified state of home changes
 
246
    PropertyChangeListener frameTitleChangeListener = new PropertyChangeListener () {
 
247
        public void propertyChange(PropertyChangeEvent ev) {
 
248
          updateFrameTitle(frame, home, application);
 
249
        }
 
250
      };
 
251
    home.addPropertyChangeListener(Home.Property.NAME, frameTitleChangeListener);
 
252
    home.addPropertyChangeListener(Home.Property.MODIFIED, frameTitleChangeListener);
 
253
    home.addPropertyChangeListener(Home.Property.RECOVERED, frameTitleChangeListener);
 
254
  }
 
255
 
 
256
  /**
 
257
   * Preferences property listener bound to this component with a weak reference to avoid
 
258
   * strong link between preferences and this component.  
 
259
   */
 
260
  private static class LanguageChangeListener implements PropertyChangeListener {
 
261
    private WeakReference<JFrame>        frame;
 
262
    private WeakReference<HomeFramePane> homeFramePane;
 
263
 
 
264
    public LanguageChangeListener(JFrame frame, HomeFramePane homeFramePane) {
 
265
      this.frame = new WeakReference<JFrame>(frame);
 
266
      this.homeFramePane = new WeakReference<HomeFramePane>(homeFramePane);
 
267
    }
 
268
    
 
269
    public void propertyChange(PropertyChangeEvent ev) {
 
270
      // If frame was garbage collected, remove this listener from preferences
 
271
      HomeFramePane homeFramePane = this.homeFramePane.get();
 
272
      UserPreferences preferences = (UserPreferences)ev.getSource();
 
273
      if (homeFramePane == null) {
 
274
        preferences.removePropertyChangeListener(
 
275
            UserPreferences.Property.LANGUAGE, this);
 
276
      } else {
 
277
        this.frame.get().applyComponentOrientation(ComponentOrientation.getOrientation(Locale.getDefault()));
 
278
        homeFramePane.updateFrameTitle(this.frame.get(), homeFramePane.home, homeFramePane.application);
 
279
      }
 
280
    }
 
281
  }
 
282
  
 
283
  /**
 
284
   * Computes <code>frame</code> size and location to fit into screen.
 
285
   */
 
286
  private void computeFrameBounds(Home home, final JFrame frame) {
 
287
    Integer x = (Integer)home.getVisualProperty(FRAME_X_VISUAL_PROPERTY);
 
288
    Integer y = (Integer)home.getVisualProperty(FRAME_Y_VISUAL_PROPERTY);
 
289
    Integer width = (Integer)home.getVisualProperty(FRAME_WIDTH_VISUAL_PROPERTY);
 
290
    Integer height = (Integer)home.getVisualProperty(FRAME_HEIGHT_VISUAL_PROPERTY);
 
291
    Boolean maximized = (Boolean)home.getVisualProperty(FRAME_MAXIMIZED_VISUAL_PROPERTY);
 
292
    Integer screenWidth = (Integer)home.getVisualProperty(SCREEN_WIDTH_VISUAL_PROPERTY);
 
293
    Integer screenHeight = (Integer)home.getVisualProperty(SCREEN_HEIGHT_VISUAL_PROPERTY);
 
294
    
 
295
    Dimension screenSize = getUserScreenSize();
 
296
    // If home frame bounds exist and screen resolution didn't reduce 
 
297
    if (x != null && y != null 
 
298
        && width != null && height != null 
 
299
        && screenWidth != null && screenHeight != null
 
300
        && screenWidth <= screenSize.width
 
301
        && screenHeight <= screenSize.height) {
 
302
      final Rectangle frameBounds = new Rectangle(x, y, width, height);
 
303
      if (maximized != null && maximized) {
 
304
        if (OperatingSystem.isMacOSX() 
 
305
            && OperatingSystem.isJavaVersionGreaterOrEqual("1.7")) {
 
306
          // Display the frame at its maximum size because calling setExtendedState to maximize 
 
307
          // the frame moves it to the bottom left at its minimum size  
 
308
          Insets insets = frame.getInsets();
 
309
          frame.setSize(screenSize.width + insets.left + insets.right, 
 
310
              screenSize.height + insets.bottom);
 
311
        } else if (OperatingSystem.isLinux()) {
 
312
          EventQueue.invokeLater(new Runnable() {
 
313
            public void run() {
 
314
              // Under Linux, maximize frame once it's displayed
 
315
              frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
 
316
            }
 
317
          });
 
318
        } else {
 
319
          frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
 
320
        }
 
321
        // Add a listener that will set the normal size when the frame leaves the maximized state
 
322
        frame.addWindowStateListener(new WindowAdapter() {
 
323
            @Override
 
324
            public void windowStateChanged(WindowEvent ev) {
 
325
              if ((ev.getOldState() == JFrame.MAXIMIZED_BOTH 
 
326
                    || (OperatingSystem.isMacOSX() 
 
327
                        && OperatingSystem.isJavaVersionGreaterOrEqual("1.7")
 
328
                        && ev.getOldState() == JFrame.NORMAL))
 
329
                  && ev.getNewState() == JFrame.NORMAL) {
 
330
                if (OperatingSystem.isMacOSXLionOrSuperior()) {
 
331
                  // Set back frame size later once frame reduce animation is finished 
 
332
                  new Timer(20, new ActionListener() {
 
333
                      public void actionPerformed(ActionEvent ev) {
 
334
                        if (frame.getHeight() < 40) {
 
335
                          ((Timer)ev.getSource()).stop();
 
336
                          frame.setBounds(frameBounds);
 
337
                        }
 
338
                      }
 
339
                    }).start();
 
340
                } else {
 
341
                  frame.setBounds(frameBounds);
 
342
                }
 
343
                frame.removeWindowStateListener(this);
 
344
              }
 
345
            }
 
346
          });
 
347
      } else {
 
348
        // Reuse home bounds
 
349
        frame.setBounds(frameBounds);
 
350
        frame.setLocationByPlatform(!SwingTools.isRectangleVisibleAtScreen(frameBounds));
 
351
      }
 
352
    } else {      
 
353
      frame.setLocationByPlatform(true);
 
354
      frame.pack();
 
355
      frame.setSize(Math.min(screenSize.width * 4 / 5, frame.getWidth()), 
 
356
              Math.min(screenSize.height * 4 / 5, frame.getHeight()));
 
357
      if (OperatingSystem.isMacOSX() 
 
358
          && OperatingSystem.isJavaVersionGreaterOrEqual("1.7")) {
 
359
        // JFrame#setLocationByPlatform does nothing under Java 7
 
360
        int minX = Integer.MAX_VALUE;
 
361
        int minY = Integer.MAX_VALUE;
 
362
        int maxX = Integer.MIN_VALUE;
 
363
        int maxY = Integer.MIN_VALUE;
 
364
        for (Frame applicationFrame : Frame.getFrames()) {
 
365
          if (applicationFrame.isShowing() 
 
366
              && applicationFrame.getBackground().getAlpha() != 0) {
 
367
            minX = Math.min(minX, applicationFrame.getX());
 
368
            minY = Math.min(minY, applicationFrame.getY());
 
369
            maxX = Math.max(maxX, applicationFrame.getX());
 
370
            maxY = Math.max(maxY, applicationFrame.getY());
 
371
          }
 
372
        }
 
373
        
 
374
        if (minX == Integer.MAX_VALUE || minX >= 23) {
 
375
          x = 0;
 
376
        } else {
 
377
          x = maxX + 23;
 
378
        }
 
379
        if (minY == Integer.MAX_VALUE || minY >= 23) {
 
380
          y = 0;
 
381
        } else {
 
382
          y = maxY + 23;
 
383
        }
 
384
        frame.setLocation(x, y);
 
385
      }
 
386
    }
 
387
  }
 
388
 
 
389
  /**
 
390
   * Returns the screen size available to user. 
 
391
   */
 
392
  private Dimension getUserScreenSize() {
 
393
    Dimension screenSize = getToolkit().getScreenSize();
 
394
    Insets screenInsets = getToolkit().getScreenInsets(getGraphicsConfiguration());
 
395
    screenSize.width -= screenInsets.left + screenInsets.right;
 
396
    screenSize.height -= screenInsets.top + screenInsets.bottom;
 
397
    return screenSize;
 
398
  }
 
399
  
 
400
  /**
 
401
   * Updates <code>frame</code> title from <code>home</code> and <code>application</code> name.
 
402
   */
 
403
  private void updateFrameTitle(JFrame frame, 
 
404
                                Home home,
 
405
                                HomeApplication application) {
 
406
    String homeName = home.getName();
 
407
    String homeDisplayedName;
 
408
    if (homeName == null) {
 
409
      homeDisplayedName = application.getUserPreferences().getLocalizedString(HomeFramePane.class, "untitled"); 
 
410
      if (newHomeNumber > 1) {
 
411
        homeDisplayedName += " " + newHomeNumber;
 
412
      }
 
413
    } else {
 
414
      homeDisplayedName = this.contentManager.getPresentationName(
 
415
          homeName, ContentManager.ContentType.SWEET_HOME_3D);
 
416
    }
 
417
    
 
418
    if (home.isRecovered()) {
 
419
      homeDisplayedName += " " + application.getUserPreferences().getLocalizedString(HomeFramePane.class, "recovered");
 
420
    }
 
421
    
 
422
    String title = homeDisplayedName;
 
423
    if (OperatingSystem.isMacOSX()) {
 
424
      // Use black indicator in close icon for a modified home 
 
425
      Boolean homeModified = Boolean.valueOf(home.isModified() || home.isRecovered());
 
426
      // Set Mac OS X 10.4 property for backward compatibility
 
427
      putClientProperty("windowModified", homeModified);
 
428
      
 
429
      if (OperatingSystem.isMacOSXLeopardOrSuperior()) {
 
430
        putClientProperty("Window.documentModified", homeModified);
 
431
        
 
432
        if (homeName != null) {        
 
433
          File homeFile = new File(homeName);
 
434
          if (homeFile.exists()) {
 
435
            // Update the home icon in window title bar for home files
 
436
            putClientProperty("Window.documentFile", homeFile);
 
437
          }
 
438
        }
 
439
      }
 
440
 
 
441
      if (!frame.isVisible() 
 
442
          && OperatingSystem.isMacOSXLionOrSuperior()) {
 
443
        try {
 
444
          // Call Mac OS X specific FullScreenUtilities.setWindowCanFullScreen(homeFrame, true) by reflection 
 
445
          Class.forName("com.apple.eawt.FullScreenUtilities").
 
446
              getMethod("setWindowCanFullScreen", new Class<?> [] {Window.class, boolean.class}).
 
447
              invoke(null, frame, true);
 
448
        } catch (Exception ex) {
 
449
          // Full screen mode is not supported
 
450
        }
 
451
      }
 
452
    } else {
 
453
      title += " - " + application.getName(); 
 
454
      if (home.isModified() || home.isRecovered()) {
 
455
        title = "* " + title;
 
456
      }
 
457
    }
 
458
    frame.setTitle(title);
 
459
  }
 
460
}