Gridarta Editor
GSplitPane.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;
21 
22 import java.awt.Component;
23 import java.awt.Container;
24 import java.awt.Dimension;
25 import java.awt.event.ActionEvent;
26 import java.awt.event.ActionListener;
27 import java.awt.event.HierarchyEvent;
28 import java.awt.event.HierarchyListener;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.prefs.Preferences;
32 import javax.swing.AbstractButton;
33 import javax.swing.JSplitPane;
34 import javax.swing.border.EmptyBorder;
35 import net.sf.gridarta.MainControl;
36 import org.jetbrains.annotations.NotNull;
37 import org.jetbrains.annotations.Nullable;
38 
50 public class GSplitPane extends JSplitPane {
51 
55  @NotNull
56  private static final String MIN = "min";
57 
61  @NotNull
62  private static final String MAX = "max";
63 
67  private static final long serialVersionUID = 1L;
68 
72  @NotNull
73  private static final Preferences PREFERENCES = Preferences.userNodeForPackage(MainControl.class);
74 
78  @NotNull
79  private final String preferencesKey;
80 
88  @NotNull
89  private final List<ActionListener> actionListeners = new ArrayList<>();
90 
95  @Nullable
96  private final String defaultDividerLocation;
97 
101  @NotNull
102  private final HierarchyListener hierarchyListener = new HierarchyListener() {
103 
104  @Override
105  public void hierarchyChanged(final HierarchyEvent e) {
106  if ((e.getChangeFlags() & (long) HierarchyEvent.SHOWING_CHANGED) != 0L && isShowing()) {
107  removeHierarchyListener(hierarchyListener);
108  if (defaultDividerLocation == null) { // should not happen
109  // ignore
110  } else if (defaultDividerLocation.equals(MIN)) {
111  if (!actionListeners.isEmpty()) {
112  actionListeners.get(0).actionPerformed(new ActionEvent(GSplitPane.this, 0, ""));
113  }
114  } else if (defaultDividerLocation.equals(MAX)) {
115  if (!actionListeners.isEmpty()) {
116  actionListeners.get(1).actionPerformed(new ActionEvent(GSplitPane.this, 0, ""));
117  }
118  } else {
119  try {
120  setDividerLocation(Integer.parseInt(defaultDividerLocation));
121  } catch (final NumberFormatException ignored) {
122  // ignore
123  }
124  }
125  }
126  }
127  };
128 
143  public GSplitPane(final int newOrientation, @NotNull final Component newLeftComponent, @NotNull final Component newRightComponent, @NotNull final String preferencesKey, final int defaultDividerLocation) {
144  super(newOrientation, newLeftComponent, newRightComponent);
145  this.preferencesKey = preferencesKey;
146  setOneTouchExpandable(true);
147  this.defaultDividerLocation = PREFERENCES.get(preferencesKey, defaultDividerLocation == -1 ? null : Integer.toString(defaultDividerLocation));
148 
149  // Hacks to circumvent undesired behavior when maximizing/de-maximizing
150  // the application's main window: without this code, the minimized or
151  // maximized state (oneTouchExpandable) does not stick and cannot be
152  // restored when the editor is restarted.
153  //
154  // This code walks the component hierarchy trying to find the
155  // oneTouchExpandable buttons. The contained ActionListeners are
156  // replaced with a proxy that shows the left/right component as
157  // appropriate for the action.
158  setBorder(new EmptyBorder(0, 0, 0, 0));
159  for (final Component component : getComponents()) {
160  if (component != newLeftComponent && component != newRightComponent) {
161  if (component instanceof Container) {
162  final Container container = (Container) component;
163  for (final Component component2 : container.getComponents()) {
164  if (component2 instanceof AbstractButton) {
165  final AbstractButton button = (AbstractButton) component2;
166  for (final ActionListener actionListener : button.getActionListeners()) {
167  if (actionListener.getClass().getName().endsWith("$OneTouchActionHandler")) {
168  actionListeners.add(actionListener);
169  button.removeActionListener(actionListener);
170  if (actionListeners.size() == 1) {
171  button.addActionListener(new ActionListener() {
172 
173  @Override
174  public void actionPerformed(@NotNull final ActionEvent e) {
175  getRightComponent().setVisible(true);
176  actionListener.actionPerformed(e);
177  }
178 
179  });
180  } else {
181  button.addActionListener(new ActionListener() {
182 
183  @Override
184  public void actionPerformed(@NotNull final ActionEvent e) {
185  getLeftComponent().setVisible(true);
186  actionListener.actionPerformed(e);
187  }
188 
189  });
190  }
191  break;
192  }
193  }
194  }
195  }
196  }
197  }
198  }
199  if (actionListeners.size() != 2) {
200  actionListeners.clear();
201  }
202  //noinspection VariableNotUsedInsideIf
203  if (this.defaultDividerLocation != null) {
204  addHierarchyListener(hierarchyListener);
205  }
206  }
207 
211  public void saveLocation() {
212  final int dividerLocation = getDividerLocation();
213  if (dividerLocation != -1) {
214  final String value;
215  switch (getState(dividerLocation)) {
216  case -1:
217  value = MIN;
218  break;
219 
220  default:
221  value = Integer.toString(dividerLocation);
222  break;
223 
224  case +1:
225  value = MAX;
226  break;
227  }
228  PREFERENCES.put(preferencesKey, value);
229  }
230  }
231 
232  @Override
233  public void setDividerLocation(final int location) {
234  super.setDividerLocation(location);
235  final int state = getState(location);
236  getLeftComponent().setVisible(state != -1);
237  getRightComponent().setVisible(state != +1);
238  }
239 
246  private int getState(final int dividerLocation) {
247  final Dimension leftSize = leftComponent.getMinimumSize();
248  final Dimension rightSize = rightComponent.getMinimumSize();
249  final int minLimit = getOrientation() == HORIZONTAL_SPLIT ? leftSize.width : leftSize.height;
250  final int maxLimit = getOrientation() == HORIZONTAL_SPLIT ? getWidth() - rightSize.width - getDividerSize() : getHeight() - rightSize.height - getDividerSize();
251  if (dividerLocation < minLimit) {
252  return -1;
253  } else if (dividerLocation > maxLimit) {
254  return +1;
255  } else {
256  return 0;
257  }
258  }
259 
260 }
void setDividerLocation(final int location)
static final Preferences PREFERENCES
The preferences.
Definition: GSplitPane.java:73
final List< ActionListener > actionListeners
The ActionListeners attached to the "oneTouchExpandable" buttons of the JSplitPane.
Definition: GSplitPane.java:89
A JSplitPane that keeps its size even upon ancestor layout changes and is restored upon editor restar...
Definition: GSplitPane.java:50
Base package of all Gridarta classes.
final String preferencesKey
The preferences key for restoring the divider location.
Definition: GSplitPane.java:79
Interface used as preferences location.
final String defaultDividerLocation
The default divider location.
Definition: GSplitPane.java:96
static final long serialVersionUID
Serial Version UID.
Definition: GSplitPane.java:67
static final String MAX
The preferences value for a maximized divider.
Definition: GSplitPane.java:62
void saveLocation()
Saves the current divider location into the preferences.
static final String MIN
The preferences value for a minimized divider.
Definition: GSplitPane.java:56
GSplitPane(final int newOrientation, @NotNull final Component newLeftComponent, @NotNull final Component newRightComponent, @NotNull final String preferencesKey, final int defaultDividerLocation)
Create a new GSplitPane.
final HierarchyListener hierarchyListener
The HierarchyListener for updating the initial divider location.
int getState(final int dividerLocation)
Returns the minimized/maximized state for a given divider location.