Gridarta Editor
BorderSplitPane.java
Go to the documentation of this file.
1 /*
2  * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games.
3  * Copyright (C) 2000-2015 The Gridarta Developers.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 package net.sf.gridarta.gui.utils.borderpanel;
21 
22 import java.awt.BorderLayout;
23 import java.awt.Component;
24 import java.awt.Container;
25 import java.awt.Dimension;
26 import java.awt.event.HierarchyEvent;
27 import java.awt.event.HierarchyListener;
28 import java.beans.PropertyChangeEvent;
29 import java.beans.PropertyChangeListener;
30 import javax.swing.JSplitPane;
31 import org.jetbrains.annotations.NotNull;
32 import org.jetbrains.annotations.Nullable;
33 
39 public class BorderSplitPane extends Container {
40 
44  private static final long serialVersionUID = 1L;
45 
50  @NotNull
51  private final Component component;
52 
57  @NotNull
58  private final Location location;
59 
64  @NotNull
65  private final Component[] optionalComponents = new Component[2];
66 
72  @NotNull
73  private final JSplitPane splitPane;
74 
80  @NotNull
81  private final JSplitPane splitPane2;
82 
87  private boolean showing;
88 
95  private int size = -1;
96 
103  private int size2;
104 
108  private boolean ignoreSizeChange;
109 
117  public BorderSplitPane(@NotNull final Component component, @NotNull final Location location, @NotNull final BorderSplitPaneListener borderSplitPaneListener, final int size2) {
118  this.component = component;
119  this.location = location;
120  this.size2 = size2;
121  splitPane = new JSplitPane(location.getSplitPaneOrientation(), true, null, null);
122  splitPane.setOneTouchExpandable(false);
123  splitPane.setResizeWeight(location.getSplitPaneResizeWeight());
124  splitPane2 = new JSplitPane(location.getSplitPaneOppositeOrientation(), true, null, null);
125  splitPane2.setOneTouchExpandable(false);
126  splitPane2.setResizeWeight(0.5);
127  setLayout(new BorderLayout());
128  setOptionalComponent(null, false, 0, true);
129 
130  splitPane.addPropertyChangeListener(new PropertyChangeListener() {
131 
132  @Override
133  public void propertyChange(final PropertyChangeEvent evt) {
134  if (ignoreSizeChange) {
135  return;
136  }
137  if (optionalComponents[0] != null || optionalComponents[1] != null) {
138  //Property names are interned strings.
139  //noinspection StringEquality
140  if (evt.getPropertyName() == JSplitPane.DIVIDER_LOCATION_PROPERTY) {
141  final Object value = evt.getNewValue();
142  if (value != null && value instanceof Integer) {
143  final int newSize = (Integer) value;
144  final int splitPaneSize = getSplitPaneSize(newSize);
145  for (final Component optionalComponent : optionalComponents) {
146  if (optionalComponent != null) {
147  borderSplitPaneListener.sizeChanged(optionalComponent, splitPaneSize);
148  }
149  }
150  }
151  }
152  }
153  }
154 
155  });
156 
157  splitPane2.addPropertyChangeListener(new PropertyChangeListener() {
158 
159  @Override
160  public void propertyChange(final PropertyChangeEvent evt) {
161  if (ignoreSizeChange) {
162  return;
163  }
164  if (optionalComponents[0] != null && optionalComponents[1] != null) {
165  //Property names are interned strings.
166  //noinspection StringEquality
167  if (evt.getPropertyName() == JSplitPane.DIVIDER_LOCATION_PROPERTY) {
168  final Object value = evt.getNewValue();
169  if (value != null && value instanceof Integer) {
170  final int newSize = (Integer) value;
171  BorderSplitPane.this.size2 = newSize;
172  borderSplitPaneListener.size2Changed(newSize);
173  }
174  }
175  }
176  }
177 
178  });
179 
180  final HierarchyListener hierarchyListener = new HierarchyListener() {
181 
182  @Override
183  public void hierarchyChanged(final HierarchyEvent e) {
184  if ((e.getChangeFlags() & (long) HierarchyEvent.SHOWING_CHANGED) != 0L && isShowing()) {
185  removeHierarchyListener(this);
186  assert !showing;
187  showing = true;
188  if (size != -1) {
189  setDividerSize(size);
190  size = -1;
191  }
192  setDividerSize2();
193  }
194  }
195 
196  };
197  addHierarchyListener(hierarchyListener);
198  }
199 
209  public void setOptionalComponent(@Nullable final Component optionalComponent, final boolean alternativeLocation, final int size) {
210  setOptionalComponent(optionalComponent, alternativeLocation, size, false);
211  }
212 
223  public final void setOptionalComponent(@Nullable final Component optionalComponent, final boolean alternativeLocation, final int size, final boolean force) {
224  final int index = alternativeLocation ? 1 : 0;
225  if (!force && optionalComponents[index] == optionalComponent) {
226  return;
227  }
228 
229  optionalComponents[index] = optionalComponent;
230 
231  final Component centerComponent;
232  final boolean setSize2;
233  if (optionalComponents[0] == null && optionalComponents[1] == null) {
234  setSize2 = false;
235  centerComponent = component;
236  } else {
237  centerComponent = splitPane;
238 
239  final Component tmp;
240  if (optionalComponents[0] == null) {
241  setSize2 = false;
242  tmp = optionalComponents[1];
243  assert tmp != null;
244  } else if (optionalComponents[1] == null) {
245  setSize2 = false;
246  tmp = optionalComponents[0];
247  assert tmp != null;
248  } else {
249  setSize2 = true;
250  tmp = splitPane2;
251  splitPane2.setTopComponent(optionalComponents[0]);
252  splitPane2.setBottomComponent(optionalComponents[1]);
253  }
254 
255  if (location.isTopOrLeft()) {
256  splitPane.setTopComponent(tmp);
257  splitPane.setBottomComponent(component);
258  } else {
259  splitPane.setTopComponent(component);
260  splitPane.setBottomComponent(tmp);
261  }
262  }
263 
264  removeAll();
265  add(centerComponent, BorderLayout.CENTER);
266  ignoreSizeChange = true;
267  try {
268  validate();
269  } finally {
270  ignoreSizeChange = false;
271  }
272 
273  final Component otherComponent = optionalComponents[1 - index];
274  if (optionalComponent == null) {
275  if (otherComponent == null) {
276  // no resize
277  } else {
278  final Dimension preferredSizeTmp = otherComponent.getPreferredSize();
279  final int preferredSize = getSplitPaneSize(preferredSizeTmp == null ? 50 : location.isTopOrBottom() ? preferredSizeTmp.height : preferredSizeTmp.width);
280  setDividerSizeInt(preferredSize);
281  }
282  } else {
283  if (otherComponent == null) {
284  setDividerSizeInt(size);
285  } else {
286  final Dimension preferredSizeTmp = otherComponent.getPreferredSize();
287  final int preferredSize = getSplitPaneSize(preferredSizeTmp == null ? 50 : location.isTopOrBottom() ? preferredSizeTmp.height : preferredSizeTmp.width);
288  setDividerSizeInt(Math.max(size, preferredSize));
289  }
290  }
291 
292  if (setSize2) {
293  setDividerSize2();
294  }
295 
296  validate();
297  }
298 
303  private void setDividerSizeInt(final int size) {
304  if (showing) {
305  setDividerSize(size);
306  } else {
307  this.size = size;
308  }
309  }
310 
315  private void setDividerSize(final int size) {
316  splitPane.setDividerLocation(getSplitPaneSize(size));
317  }
318 
322  private void setDividerSize2() {
323  if (size2 == -1) {
324  return;
325  }
326  splitPane2.setDividerLocation(size2);
327  }
328 
334  private int getSplitPaneSize(final int size) {
335  final Dimension preferredSize = getSplitPanePreferredSize();
336  if (location.isTopOrBottom()) {
337  preferredSize.height = size;
338  } else {
339  preferredSize.width = size;
340  }
341  setSplitPanePreferredSize(preferredSize);
342 
343  final int result;
344  if (location.isTopOrLeft()) {
345  result = size;
346  } else {
347  final int splitPaneSize = (location.isTopOrBottom() ? splitPane.getHeight() : splitPane.getWidth()) - splitPane.getDividerSize();
348  result = Math.max(0, splitPaneSize - size);
349  }
350  return result;
351  }
352 
357  @NotNull
358  private Dimension getSplitPanePreferredSize() {
359  for (final Component optionalComponent : optionalComponents) {
360  if (optionalComponent != null) {
361  final Dimension preferredSize = optionalComponent.getPreferredSize();
362  if (preferredSize != null) {
363  return preferredSize;
364  }
365  }
366  }
367 
368  for (final Component optionalComponent : optionalComponents) {
369  if (optionalComponent != null) {
370  return optionalComponent.getSize();
371  }
372  }
373 
374  return splitPane.getSize();
375  }
376 
381  private void setSplitPanePreferredSize(@NotNull final Dimension preferredSize) {
382  for (final Component optionalComponent : optionalComponents) {
383  if (optionalComponent != null) {
384  optionalComponent.setPreferredSize(preferredSize);
385  }
386  }
387  }
388 
389 }
boolean ignoreSizeChange
If set, do not report split pane size changes.
void setDividerSize(final int size)
Sets the new size of splitPane.
final JSplitPane splitPane
The JSplitPane to separate component and optionalComponents.
A Component that permanently shows another component and optionally displays a JSplitPane and one or ...
boolean showing
Whether this component is visible.
void setDividerSize2()
Sets the new size of splitPane2.
int getSplitPaneSize(final int size)
Converts between logical and physical sizes of splitPane.
static final long serialVersionUID
The serial version UID.
final Component [] optionalComponents
The two optionally shown Components.
Dimension getSplitPanePreferredSize()
Calculates the preferred size of the split pane.
abstract boolean isTopOrBottom()
Returns whether this location is TOP or BOTTOM.
final JSplitPane splitPane2
The JSplitPane to separate both elements of optionalComponents.
final Location location
The Location of the optional component.
final void setOptionalComponent(@Nullable final Component optionalComponent, final boolean alternativeLocation, final int size, final boolean force)
Sets the optional Component.
final Component component
The permanently shown Component.
void setSplitPanePreferredSize(@NotNull final Dimension preferredSize)
Sets the preferred size of all shown optional components.
Interface for listeners interested in BorderSplitPane related events.
BorderSplitPane(@NotNull final Component component, @NotNull final Location location, @NotNull final BorderSplitPaneListener borderSplitPaneListener, final int size2)
Creates a new instance.
void setDividerSizeInt(final int size)
Sets the new size of splitPane.
void setOptionalComponent(@Nullable final Component optionalComponent, final boolean alternativeLocation, final int size)
Sets the optional Component.
abstract boolean isTopOrLeft()
Returns whether this location is TOP or LEFT.