1/*
2 * Copyright (c) 2002, 2014, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package javax.swing.plaf.synth;
27
28import javax.swing.*;
29import javax.swing.border.*;
30import javax.swing.plaf.*;
31import javax.swing.plaf.basic.BasicDesktopPaneUI;
32import java.beans.*;
33import java.awt.event.*;
34import java.awt.*;
35
36/**
37 * Provides the Synth L&F UI delegate for
38 * {@link javax.swing.JDesktopPane}.
39 *
40 * @author Joshua Outwater
41 * @author Steve Wilson
42 * @since 1.7
43 */
44public class SynthDesktopPaneUI extends BasicDesktopPaneUI implements
45                  PropertyChangeListener, SynthUI {
46    private SynthStyle style;
47    private TaskBar taskBar;
48    private DesktopManager oldDesktopManager;
49
50    /**
51     * Creates a new UI object for the given component.
52     *
53     * @param c component to create UI object for
54     * @return the UI object
55     */
56    public static ComponentUI createUI(JComponent c) {
57        return new SynthDesktopPaneUI();
58    }
59
60    /**
61     * {@inheritDoc}
62     */
63    @Override
64    protected void installListeners() {
65        super.installListeners();
66        desktop.addPropertyChangeListener(this);
67        if (taskBar != null) {
68            // Listen for desktop being resized
69            desktop.addComponentListener(taskBar);
70            // Listen for frames being added to desktop
71            desktop.addContainerListener(taskBar);
72        }
73    }
74
75    /**
76     * {@inheritDoc}
77     */
78    @Override
79    protected void installDefaults() {
80        updateStyle(desktop);
81
82        if (UIManager.getBoolean("InternalFrame.useTaskBar")) {
83            taskBar = new TaskBar();
84
85            for (Component comp : desktop.getComponents()) {
86                JInternalFrame.JDesktopIcon desktopIcon;
87
88                if (comp instanceof JInternalFrame.JDesktopIcon) {
89                    desktopIcon = (JInternalFrame.JDesktopIcon)comp;
90                } else if (comp instanceof JInternalFrame) {
91                    desktopIcon = ((JInternalFrame)comp).getDesktopIcon();
92                } else {
93                    continue;
94                }
95                // Move desktopIcon from desktop to taskBar
96                if (desktopIcon.getParent() == desktop) {
97                    desktop.remove(desktopIcon);
98                }
99                if (desktopIcon.getParent() != taskBar) {
100                    taskBar.add(desktopIcon);
101                    desktopIcon.getInternalFrame().addComponentListener(
102                        taskBar);
103                }
104            }
105            taskBar.setBackground(desktop.getBackground());
106            desktop.add(taskBar,
107                Integer.valueOf(JLayeredPane.PALETTE_LAYER.intValue() + 1));
108            if (desktop.isShowing()) {
109                taskBar.adjustSize();
110            }
111        }
112    }
113
114    private void updateStyle(JDesktopPane c) {
115        SynthStyle oldStyle = style;
116        SynthContext context = getContext(c, ENABLED);
117        style = SynthLookAndFeel.updateStyle(context, this);
118        if (oldStyle != null) {
119            uninstallKeyboardActions();
120            installKeyboardActions();
121        }
122    }
123
124    /**
125     * {@inheritDoc}
126     */
127    @Override
128    protected void uninstallListeners() {
129        if (taskBar != null) {
130            desktop.removeComponentListener(taskBar);
131            desktop.removeContainerListener(taskBar);
132        }
133        desktop.removePropertyChangeListener(this);
134        super.uninstallListeners();
135    }
136
137    /**
138     * {@inheritDoc}
139     */
140    @Override
141    protected void uninstallDefaults() {
142        SynthContext context = getContext(desktop, ENABLED);
143
144        style.uninstallDefaults(context);
145        style = null;
146
147        if (taskBar != null) {
148            for (Component comp : taskBar.getComponents()) {
149                JInternalFrame.JDesktopIcon desktopIcon =
150                    (JInternalFrame.JDesktopIcon)comp;
151                taskBar.remove(desktopIcon);
152                desktopIcon.setPreferredSize(null);
153                JInternalFrame f = desktopIcon.getInternalFrame();
154                if (f.isIcon()) {
155                    desktop.add(desktopIcon);
156                }
157                f.removeComponentListener(taskBar);
158            }
159            desktop.remove(taskBar);
160            taskBar = null;
161        }
162    }
163
164    /**
165     * {@inheritDoc}
166     */
167    @Override
168    protected void installDesktopManager() {
169        if (UIManager.getBoolean("InternalFrame.useTaskBar")) {
170            desktopManager = oldDesktopManager = desktop.getDesktopManager();
171            if (!(desktopManager instanceof SynthDesktopManager)) {
172                desktopManager = new SynthDesktopManager();
173                desktop.setDesktopManager(desktopManager);
174            }
175        } else {
176            super.installDesktopManager();
177        }
178    }
179
180    /**
181     * {@inheritDoc}
182     */
183    @Override
184    protected void uninstallDesktopManager() {
185        if (oldDesktopManager != null && !(oldDesktopManager instanceof UIResource)) {
186            desktopManager = desktop.getDesktopManager();
187            if (desktopManager == null || desktopManager instanceof UIResource) {
188                desktop.setDesktopManager(oldDesktopManager);
189            }
190        }
191        oldDesktopManager = null;
192        super.uninstallDesktopManager();
193    }
194
195    @SuppressWarnings("serial") // Same-version serialization only and
196                                // internal anonymous classes
197    static class TaskBar extends JPanel implements ComponentListener, ContainerListener {
198        TaskBar() {
199            setOpaque(true);
200            setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0) {
201                public void layoutContainer(Container target) {
202                    // First shrink buttons to fit
203                    Component[] comps = target.getComponents();
204                    int n = comps.length;
205                    if (n > 0) {
206                        // Start with the largest preferred width
207                        int prefWidth = 0;
208                        for (Component c : comps) {
209                            c.setPreferredSize(null);
210                            Dimension prefSize = c.getPreferredSize();
211                            if (prefSize.width > prefWidth) {
212                                prefWidth = prefSize.width;
213                            }
214                        }
215                        // Shrink equally to fit if needed
216                        Insets insets = target.getInsets();
217                        int tw = target.getWidth() - insets.left - insets.right;
218                        int w = Math.min(prefWidth, Math.max(10, tw/n));
219                        for (Component c : comps) {
220                            Dimension prefSize = c.getPreferredSize();
221                            c.setPreferredSize(new Dimension(w, prefSize.height));
222                        }
223                    }
224                    super.layoutContainer(target);
225                }
226            });
227
228            // PENDING: This should be handled by the painter
229            setBorder(new BevelBorder(BevelBorder.RAISED) {
230                protected void paintRaisedBevel(Component c, Graphics g,
231                                                int x, int y, int w, int h)  {
232                    Color oldColor = g.getColor();
233                    g.translate(x, y);
234                    g.setColor(getHighlightOuterColor(c));
235                    g.drawLine(0, 0, 0, h-2);
236                    g.drawLine(1, 0, w-2, 0);
237                    g.setColor(getShadowOuterColor(c));
238                    g.drawLine(0, h-1, w-1, h-1);
239                    g.drawLine(w-1, 0, w-1, h-2);
240                    g.translate(-x, -y);
241                    g.setColor(oldColor);
242                }
243            });
244        }
245
246        void adjustSize() {
247            JDesktopPane desktop = (JDesktopPane)getParent();
248            if (desktop != null) {
249                int height = getPreferredSize().height;
250                Insets insets = getInsets();
251                if (height == insets.top + insets.bottom) {
252                    if (getHeight() <= height) {
253                        // Initial size, because we have no buttons yet
254                        height += 21;
255                    } else {
256                        // We already have a good height
257                        height = getHeight();
258                    }
259                }
260                setBounds(0, desktop.getHeight() - height, desktop.getWidth(), height);
261                revalidate();
262                repaint();
263            }
264        }
265
266        // ComponentListener interface
267
268        public void componentResized(ComponentEvent e) {
269            if (e.getSource() instanceof JDesktopPane) {
270                adjustSize();
271            }
272        }
273
274        public void componentMoved(ComponentEvent e){}
275
276        public void componentShown(ComponentEvent e) {
277            if (e.getSource() instanceof JInternalFrame) {
278                adjustSize();
279            }
280        }
281
282        public void componentHidden(ComponentEvent e) {
283            if (e.getSource() instanceof JInternalFrame) {
284                ((JInternalFrame)e.getSource()).getDesktopIcon().setVisible(false);
285                revalidate();
286            }
287        }
288
289        // ContainerListener interface
290
291        public void componentAdded(ContainerEvent e) {
292            if (e.getChild() instanceof JInternalFrame) {
293                JDesktopPane desktop = (JDesktopPane)e.getSource();
294                JInternalFrame f = (JInternalFrame)e.getChild();
295                JInternalFrame.JDesktopIcon desktopIcon = f.getDesktopIcon();
296                for (Component comp : getComponents()) {
297                    if (comp == desktopIcon) {
298                        // We have it already
299                        return;
300                    }
301                }
302                add(desktopIcon);
303                f.addComponentListener(this);
304                if (getComponentCount() == 1) {
305                    adjustSize();
306                }
307            }
308        }
309
310        public void componentRemoved(ContainerEvent e) {
311            if (e.getChild() instanceof JInternalFrame) {
312                JInternalFrame f = (JInternalFrame)e.getChild();
313                if (!f.isIcon()) {
314                    // Frame was removed without using setClosed(true)
315                    remove(f.getDesktopIcon());
316                    f.removeComponentListener(this);
317                    revalidate();
318                    repaint();
319                }
320            }
321        }
322    }
323
324    @SuppressWarnings("serial") // Same-version serialization only
325    class SynthDesktopManager extends DefaultDesktopManager implements UIResource {
326
327        public void maximizeFrame(JInternalFrame f) {
328            if (f.isIcon()) {
329                try {
330                    f.setIcon(false);
331                } catch (PropertyVetoException e2) {
332                }
333            } else {
334                f.setNormalBounds(f.getBounds());
335                Component desktop = f.getParent();
336                setBoundsForFrame(f, 0, 0,
337                                  desktop.getWidth(),
338                                  desktop.getHeight() - taskBar.getHeight());
339            }
340
341            try {
342                f.setSelected(true);
343            } catch (PropertyVetoException e2) {
344            }
345        }
346
347        public void iconifyFrame(JInternalFrame f) {
348            JInternalFrame.JDesktopIcon desktopIcon;
349            Container c = f.getParent();
350            JDesktopPane d = f.getDesktopPane();
351            boolean findNext = f.isSelected();
352            if (c == null || d == null) {
353                return;
354            }
355            desktopIcon = f.getDesktopIcon();
356            if (!wasIcon(f)) {
357                Rectangle r = getBoundsForIconOf(f);
358                desktopIcon.setBounds(r.x, r.y, r.width, r.height);
359                desktopIcon.revalidate();
360                setWasIcon(f, Boolean.TRUE);
361            }
362
363            c.remove(f);
364            c.repaint(f.getX(), f.getY(), f.getWidth(), f.getHeight());
365            try {
366                f.setSelected(false);
367            } catch (PropertyVetoException e2) {
368            }
369
370            // Get topmost of the remaining frames
371            if (findNext) {
372                for (Component comp : c.getComponents()) {
373                    if (comp instanceof JInternalFrame) {
374                        try {
375                            ((JInternalFrame)comp).setSelected(true);
376                        } catch (PropertyVetoException e2) {
377                        }
378                        ((JInternalFrame)comp).moveToFront();
379                        return;
380                    }
381                }
382            }
383        }
384
385
386        public void deiconifyFrame(JInternalFrame f) {
387            JInternalFrame.JDesktopIcon desktopIcon = f.getDesktopIcon();
388            Container c = desktopIcon.getParent();
389            if (c != null) {
390                c = c.getParent();
391                if (c != null) {
392                    c.add(f);
393                    if (f.isMaximum()) {
394                        int w = c.getWidth();
395                        int h = c.getHeight() - taskBar.getHeight();
396                        if (f.getWidth() != w || f.getHeight() != h) {
397                            setBoundsForFrame(f, 0, 0, w, h);
398                        }
399                    }
400                    if (f.isSelected()) {
401                        f.moveToFront();
402                    } else {
403                        try {
404                            f.setSelected(true);
405                        } catch (PropertyVetoException e2) {
406                        }
407                    }
408                }
409            }
410        }
411
412        protected void removeIconFor(JInternalFrame f) {
413            super.removeIconFor(f);
414            taskBar.validate();
415        }
416
417        public void setBoundsForFrame(JComponent f, int newX, int newY, int newWidth, int newHeight) {
418            super.setBoundsForFrame(f, newX, newY, newWidth, newHeight);
419            if (taskBar != null && newY >= taskBar.getY()) {
420                f.setLocation(f.getX(), taskBar.getY()-f.getInsets().top);
421            }
422        }
423    }
424
425    /**
426     * {@inheritDoc}
427     */
428    @Override
429    public SynthContext getContext(JComponent c) {
430        return getContext(c, getComponentState(c));
431    }
432
433    private SynthContext getContext(JComponent c, int state) {
434        return SynthContext.getContext(c, style, state);
435    }
436
437    private int getComponentState(JComponent c) {
438        return SynthLookAndFeel.getComponentState(c);
439    }
440
441    /**
442     * Notifies this UI delegate to repaint the specified component.
443     * This method paints the component background, then calls
444     * the {@link #paint(SynthContext,Graphics)} method.
445     *
446     * <p>In general, this method does not need to be overridden by subclasses.
447     * All Look and Feel rendering code should reside in the {@code paint} method.
448     *
449     * @param g the {@code Graphics} object used for painting
450     * @param c the component being painted
451     * @see #paint(SynthContext,Graphics)
452     */
453    @Override
454    public void update(Graphics g, JComponent c) {
455        SynthContext context = getContext(c);
456
457        SynthLookAndFeel.update(context, g);
458        context.getPainter().paintDesktopPaneBackground(context, g, 0, 0,
459                                                  c.getWidth(), c.getHeight());
460        paint(context, g);
461    }
462
463    /**
464     * Paints the specified component according to the Look and Feel.
465     * <p>This method is not used by Synth Look and Feel.
466     * Painting is handled by the {@link #paint(SynthContext,Graphics)} method.
467     *
468     * @param g the {@code Graphics} object used for painting
469     * @param c the component being painted
470     * @see #paint(SynthContext,Graphics)
471     */
472    @Override
473    public void paint(Graphics g, JComponent c) {
474        SynthContext context = getContext(c);
475
476        paint(context, g);
477    }
478
479    /**
480     * Paints the specified component. This implementation does nothing.
481     *
482     * @param context context for the component being painted
483     * @param g the {@code Graphics} object used for painting
484     * @see #update(Graphics,JComponent)
485     */
486    protected void paint(SynthContext context, Graphics g) {
487    }
488
489    /**
490     * {@inheritDoc}
491     */
492    @Override
493    public void paintBorder(SynthContext context, Graphics g, int x,
494                            int y, int w, int h) {
495        context.getPainter().paintDesktopPaneBorder(context, g, x, y, w, h);
496    }
497
498    /**
499     * {@inheritDoc}
500     */
501    @Override
502    public void propertyChange(PropertyChangeEvent evt) {
503        if (SynthLookAndFeel.shouldUpdateStyle(evt)) {
504            updateStyle((JDesktopPane)evt.getSource());
505        }
506        if (evt.getPropertyName() == "ancestor" && taskBar != null) {
507            taskBar.adjustSize();
508        }
509    }
510}
511