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-2023 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 javax.swing.JSplitPane;
29 import org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
31 
37 public class BorderSplitPane extends Container {
38 
42  private static final long serialVersionUID = 1L;
43 
48  @NotNull
49  private final Component component;
50 
55  @NotNull
56  private final Location location;
57 
62  @NotNull
63  private final Component @NotNull [] optionalComponents = new Component[2];
64 
70  @NotNull
71  private final JSplitPane splitPane;
72 
78  @NotNull
79  private final JSplitPane splitPane2;
80 
85  private boolean showing;
86 
93  private int size = -1;
94 
101  private int size2;
102 
106  private boolean ignoreSizeChange;
107 
115  public BorderSplitPane(@NotNull final Component component, @NotNull final Location location, @NotNull final BorderSplitPaneListener borderSplitPaneListener, final int size2) {
116  this.component = component;
117  this.location = location;
118  this.size2 = size2;
119  splitPane = new JSplitPane(location.getSplitPaneOrientation(), true, null, null);
120  splitPane.setOneTouchExpandable(false);
121  splitPane.setResizeWeight(location.getSplitPaneResizeWeight());
122  splitPane2 = new JSplitPane(location.getSplitPaneOppositeOrientation(), true, null, null);
123  splitPane2.setOneTouchExpandable(false);
124  splitPane2.setResizeWeight(0.5);
125  setLayout(new BorderLayout());
126  setOptionalComponent(null, false, 0, true);
127 
128  splitPane.addPropertyChangeListener(evt -> {
129  if (ignoreSizeChange) {
130  return;
131  }
132  if (optionalComponents[0] != null || optionalComponents[1] != null) {
133  //Property names are interned strings.
134  //noinspection StringEquality
135  if (evt.getPropertyName() == JSplitPane.DIVIDER_LOCATION_PROPERTY) {
136  final Object value = evt.getNewValue();
137  if (value instanceof Integer) {
138  final int newSize = (Integer) value;
139  final int splitPaneSize = getSplitPaneSize(newSize);
140  for (final Component optionalComponent : optionalComponents) {
141  if (optionalComponent != null) {
142  borderSplitPaneListener.sizeChanged(optionalComponent, splitPaneSize);
143  }
144  }
145  }
146  }
147  }
148  });
149 
150  splitPane2.addPropertyChangeListener(evt -> {
151  if (ignoreSizeChange) {
152  return;
153  }
154  if (optionalComponents[0] != null && optionalComponents[1] != null) {
155  //Property names are interned strings.
156  //noinspection StringEquality
157  if (evt.getPropertyName() == JSplitPane.DIVIDER_LOCATION_PROPERTY) {
158  final Object value = evt.getNewValue();
159  if (value instanceof Integer) {
160  final int newSize = (Integer) value;
161  BorderSplitPane.this.size2 = newSize;
162  borderSplitPaneListener.size2Changed(newSize);
163  }
164  }
165  }
166  });
167 
168  final HierarchyListener hierarchyListener = new HierarchyListener() {
169 
170  @Override
171  public void hierarchyChanged(final HierarchyEvent e) {
172  if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0L && isShowing()) {
173  removeHierarchyListener(this);
174  assert !showing;
175  showing = true;
176  if (size != -1) {
177  setDividerSize(size);
178  size = -1;
179  }
180  setDividerSize2();
181  }
182  }
183 
184  };
185  addHierarchyListener(hierarchyListener);
186  }
187 
197  public void setOptionalComponent(@Nullable final Component optionalComponent, final boolean alternativeLocation, final int size) {
198  setOptionalComponent(optionalComponent, alternativeLocation, size, false);
199  }
200 
211  private void setOptionalComponent(@Nullable final Component optionalComponent, final boolean alternativeLocation, final int size, final boolean force) {
212  final int index = alternativeLocation ? 1 : 0;
213  if (!force && optionalComponents[index] == optionalComponent) {
214  return;
215  }
216 
217  optionalComponents[index] = optionalComponent;
218 
219  final Component centerComponent;
220  final boolean setSize2;
221  if (optionalComponents[0] == null && optionalComponents[1] == null) {
222  setSize2 = false;
223  centerComponent = component;
224  } else {
225  centerComponent = splitPane;
226 
227  final Component tmp;
228  if (optionalComponents[0] == null) {
229  setSize2 = false;
230  tmp = optionalComponents[1];
231  assert tmp != null;
232  } else if (optionalComponents[1] == null) {
233  setSize2 = false;
234  tmp = optionalComponents[0];
235  assert tmp != null;
236  } else {
237  setSize2 = true;
238  tmp = splitPane2;
239  splitPane2.setTopComponent(optionalComponents[0]);
240  splitPane2.setBottomComponent(optionalComponents[1]);
241  }
242 
243  if (location.isTopOrLeft()) {
244  splitPane.setTopComponent(tmp);
245  splitPane.setBottomComponent(component);
246  } else {
247  splitPane.setTopComponent(component);
248  splitPane.setBottomComponent(tmp);
249  }
250  }
251 
252  removeAll();
253  add(centerComponent, BorderLayout.CENTER);
254  ignoreSizeChange = true;
255  try {
256  validate();
257  } finally {
258  ignoreSizeChange = false;
259  }
260 
261  final Component otherComponent = optionalComponents[1 - index];
262  if (optionalComponent == null) {
263  if (otherComponent == null) {
264  // no resize
265  } else {
266  final Dimension preferredSizeTmp = otherComponent.getPreferredSize();
267  final int preferredSize = getSplitPaneSize(preferredSizeTmp == null ? 50 : location.isTopOrBottom() ? preferredSizeTmp.height : preferredSizeTmp.width);
268  setDividerSizeInt(preferredSize);
269  }
270  } else {
271  if (otherComponent == null) {
272  setDividerSizeInt(size);
273  } else {
274  final Dimension preferredSizeTmp = otherComponent.getPreferredSize();
275  final int preferredSize = getSplitPaneSize(preferredSizeTmp == null ? 50 : location.isTopOrBottom() ? preferredSizeTmp.height : preferredSizeTmp.width);
276  setDividerSizeInt(Math.max(size, preferredSize));
277  }
278  }
279 
280  if (setSize2) {
281  setDividerSize2();
282  }
283 
284  validate();
285  }
286 
291  private void setDividerSizeInt(final int size) {
292  if (showing) {
293  setDividerSize(size);
294  } else {
295  this.size = size;
296  }
297  }
298 
303  private void setDividerSize(final int size) {
304  splitPane.setDividerLocation(getSplitPaneSize(size));
305  }
306 
310  private void setDividerSize2() {
311  if (size2 == -1) {
312  return;
313  }
314  splitPane2.setDividerLocation(size2);
315  }
316 
322  private int getSplitPaneSize(final int size) {
323  final Dimension preferredSize = getSplitPanePreferredSize();
324  if (location.isTopOrBottom()) {
325  preferredSize.height = size;
326  } else {
327  preferredSize.width = size;
328  }
329  setSplitPanePreferredSize(preferredSize);
330 
331  final int result;
332  if (location.isTopOrLeft()) {
333  result = size;
334  } else {
335  final int splitPaneSize = (location.isTopOrBottom() ? splitPane.getHeight() : splitPane.getWidth()) - splitPane.getDividerSize();
336  result = Math.max(0, splitPaneSize - size);
337  }
338  return result;
339  }
340 
345  @NotNull
346  private Dimension getSplitPanePreferredSize() {
347  for (final Component optionalComponent : optionalComponents) {
348  if (optionalComponent != null) {
349  final Dimension preferredSize = optionalComponent.getPreferredSize();
350  if (preferredSize != null) {
351  return preferredSize;
352  }
353  }
354  }
355 
356  for (final Component optionalComponent : optionalComponents) {
357  if (optionalComponent != null) {
358  return optionalComponent.getSize();
359  }
360  }
361 
362  return splitPane.getSize();
363  }
364 
369  private void setSplitPanePreferredSize(@NotNull final Dimension preferredSize) {
370  for (final Component optionalComponent : optionalComponents) {
371  if (optionalComponent != null) {
372  optionalComponent.setPreferredSize(preferredSize);
373  }
374  }
375  }
376 
377 }
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.serialVersionUID
static final long serialVersionUID
The serial version UID.
Definition: BorderSplitPane.java:42
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.location
final Location location
The Location of the optional component.
Definition: BorderSplitPane.java:56
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.setDividerSize2
void setDividerSize2()
Sets the new size of splitPane2.
Definition: BorderSplitPane.java:310
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.optionalComponents
final Component[] optionalComponents
The two optionally shown Components.
Definition: BorderSplitPane.java:63
net.sf.gridarta.gui.utils.borderpanel.Location.isTopOrLeft
abstract boolean isTopOrLeft()
Returns whether this location is TOP or LEFT.
net.sf.gridarta.gui.utils.borderpanel.Location.getSplitPaneResizeWeight
abstract double getSplitPaneResizeWeight()
Returns the resize weight for a JSplitPane.
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.splitPane2
final JSplitPane splitPane2
The JSplitPane to separate both elements of {}.
Definition: BorderSplitPane.java:79
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.splitPane
final JSplitPane splitPane
The JSplitPane to separate component and {}.
Definition: BorderSplitPane.java:71
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPaneListener
Interface for listeners interested in BorderSplitPane related events.
Definition: BorderSplitPaneListener.java:30
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.component
final Component component
The permanently shown Component.
Definition: BorderSplitPane.java:49
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.setDividerSize
void setDividerSize(final int size)
Sets the new size of splitPane.
Definition: BorderSplitPane.java:303
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane
A Component that permanently shows another component and optionally displays a JSplitPane and one or ...
Definition: BorderSplitPane.java:37
net.sf.gridarta.gui.utils.borderpanel.Location.getSplitPaneOrientation
abstract int getSplitPaneOrientation()
Returns the orientation for a JSplitPane.
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.showing
boolean showing
Whether this component is visible.
Definition: BorderSplitPane.java:85
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.getSplitPaneSize
int getSplitPaneSize(final int size)
Converts between logical and physical sizes of splitPane.
Definition: BorderSplitPane.java:322
net.sf.gridarta.gui.utils.borderpanel.Location.isTopOrBottom
abstract boolean isTopOrBottom()
Returns whether this location is TOP or BOTTOM.
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.setOptionalComponent
void setOptionalComponent(@Nullable final Component optionalComponent, final boolean alternativeLocation, final int size)
Sets the optional Component.
Definition: BorderSplitPane.java:197
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.BorderSplitPane
BorderSplitPane(@NotNull final Component component, @NotNull final Location location, @NotNull final BorderSplitPaneListener borderSplitPaneListener, final int size2)
Creates a new instance.
Definition: BorderSplitPane.java:115
net.sf.gridarta.gui.utils.borderpanel.Location
A location.
Definition: Location.java:33
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.setDividerSizeInt
void setDividerSizeInt(final int size)
Sets the new size of splitPane.
Definition: BorderSplitPane.java:291
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.setSplitPanePreferredSize
void setSplitPanePreferredSize(@NotNull final Dimension preferredSize)
Sets the preferred size of all shown optional components.
Definition: BorderSplitPane.java:369
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.getSplitPanePreferredSize
Dimension getSplitPanePreferredSize()
Calculates the preferred size of the split pane.
Definition: BorderSplitPane.java:346
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.size
int size
The pending size of splitPane.
Definition: BorderSplitPane.java:93
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.setOptionalComponent
void setOptionalComponent(@Nullable final Component optionalComponent, final boolean alternativeLocation, final int size, final boolean force)
Sets the optional Component.
Definition: BorderSplitPane.java:211
net.sf.gridarta.gui.utils.borderpanel.Location.getSplitPaneOppositeOrientation
abstract int getSplitPaneOppositeOrientation()
Returns the opposite orientation for a JSplitPane.
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.ignoreSizeChange
boolean ignoreSizeChange
If set, do not report split pane size changes.
Definition: BorderSplitPane.java:106
net.sf.gridarta.gui.utils.borderpanel.BorderSplitPane.size2
int size2
The pending size of splitPane2.
Definition: BorderSplitPane.java:101