Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

311
LINES

< > BotCompany Repo | #1016948 // installInternalFrameSwitcher_v2

JavaX fragment (include)

1  
sbool installInternalFrameSwitcher_v2_debug;
2  
3  
svoid installInternalFrameSwitcher_v2(final JDesktopPane desktop) {
4  
  swing {
5  
    new DesktopListener desktopListener;
6  
    desktop.addContainerListener(desktopListener);
7  
  
8  
    new SwitchDispatcher<JInternalFrame>(new DesktopSwitcher(desktop), desktopListener).start();
9  
  }
10  
}
11  
12  
sclass JInternalFrameCellRenderer extends DefaultListCellRenderer {
13  
  static int marginW = 20, marginH = 3;
14  
  
15  
  public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
16  
    Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
17  
    setText(((JInternalFrame) value).getTitle());
18  
    setMargin(marginW, marginH, this);
19  
    ret c;
20  
  }
21  
}
22  
23  
sclass DesktopListener extends ContainerAdapter implements SwitchableComponentsListener<JInternalFrame> {
24  
  private final List<JInternalFrame> frames = new ArrayList<>();
25  
  private final boolean titleOptional;
26  
  private final InternalFrameAdapter frameActivatedListener = new InternalFrameAdapter() {
27  
      @Override
28  
      public void internalFrameActivated(InternalFrameEvent e) {
29  
          JInternalFrame frame = e.getInternalFrame();
30  
          frames.remove(frame);
31  
          frames.add(0, frame);
32  
      }
33  
  };
34  
35  
  public DesktopListener() {
36  
      this(false);
37  
  }
38  
39  
  public DesktopListener(boolean titleOptional) {
40  
      this.titleOptional = titleOptional;
41  
  }
42  
43  
  @Override
44  
  public void componentAdded(final ContainerEvent e) {
45  
      if (e.getChild() instanceof JInternalFrame) {
46  
          final JInternalFrame frame = (JInternalFrame) e.getChild();
47  
          if (isTitleOptional() || frame.getTitle() != null && !frame.getTitle().trim().isEmpty()) {
48  
              frames.add(frame);
49  
              frame.addInternalFrameListener(frameActivatedListener);
50  
          }
51  
      }
52  
  }
53  
54  
  @Override
55  
  public void componentRemoved(final ContainerEvent e) {
56  
      if (e.getChild() instanceof JInternalFrame) {
57  
          JInternalFrame frame = (JInternalFrame) e.getChild();
58  
          frames.remove(frame);
59  
          frame.removeInternalFrameListener(frameActivatedListener);
60  
      }
61  
  }
62  
63  
  private boolean isTitleOptional() {
64  
      return titleOptional;
65  
  }
66  
67  
  public List<JInternalFrame> getSwitchableComponentList() {
68  
      return frames;
69  
  }
70  
}
71  
72  
sclass DesktopSwitcher implements Switcher<JInternalFrame> {
73  
  private final JDesktopPane jDesktopPane;
74  
  private SwitchDialog<JInternalFrame> switchDialog;
75  
76  
  public DesktopSwitcher(JDesktopPane jDesktopPane) {
77  
      this.jDesktopPane = jDesktopPane;
78  
      switchDialog = new SwitchDialog<>(SwingUtilities.getWindowAncestor(jDesktopPane), new ArrayList<JInternalFrame>());
79  
      initMouseListener();
80  
  }
81  
82  
  public void previous(List<JInternalFrame> switchableComponentList) {
83  
      showMenu(switchableComponentList);
84  
      switchDialog.selectPrevious();
85  
  }
86  
87  
  public void next(List<JInternalFrame> switchableComponentList) {
88  
      showMenu(switchableComponentList);
89  
      switchDialog.selectNext();
90  
  }
91  
92  
  public void dismiss() {
93  
      hideMenu();
94  
  }
95  
96  
  @Override
97  
  public JDesktopPane getDesktop() {
98  
      return jDesktopPane;
99  
  }
100  
101  
  @Override
102  
  public boolean hasFocus() {
103  
      return switchDialog.hasFocus() || switchDialog.getList().hasFocus();
104  
  }
105  
106  
  public SwitchDialog<JInternalFrame> getSwitchDialog() {
107  
      return switchDialog;
108  
  }
109  
110  
  private void showMenu(List<JInternalFrame> switchableComponentList) {
111  
      switchDialog.setSwitchableComponents(switchableComponentList);
112  
      if (!switchableComponentList.isEmpty()) {
113  
          switchDialog.pack();
114  
          switchDialog.setLocationRelativeTo(jDesktopPane);
115  
          switchDialog.setVisible(true);
116  
      }
117  
  }
118  
119  
  private void hideMenu() {
120  
    if (switchDialog != null && switchDialog.isVisible()) {
121  
      pcall {
122  
        JInternalFrame selected = switchDialog.getSelected();
123  
        if (selected != null) {
124  
            switchDialog.unselect();
125  
            selected.setSelected(true);
126  
            selected.toFront();
127  
        }
128  
      }
129  
      switchDialog.dispose();
130  
    }
131  
  }
132  
133  
  private void initMouseListener() {
134  
      switchDialog.getList().addMouseListener(new MouseAdapter() {
135  
          @Override
136  
          public void mouseReleased(final MouseEvent e) {
137  
              hideMenu();
138  
          }
139  
      });
140  
  }
141  
}
142  
143  
sclass SwitchDispatcher<T> implements KeyEventDispatcher {
144  
  private static final KeyStroke nextStroke = KeyStroke.getKeyStroke("ctrl TAB");
145  
  private static final KeyStroke previousStroke = KeyStroke.getKeyStroke("ctrl shift TAB");
146  
147  
  private final Switcher<T> switcher;
148  
  private final SwitchableComponentsListener<T> desktopListener;
149  
150  
  public SwitchDispatcher(Switcher<T> switcher, SwitchableComponentsListener<T> switchableComponentsListener) {
151  
      this.switcher = switcher;
152  
      this.desktopListener = switchableComponentsListener;
153  
  }
154  
155  
  public void start() {
156  
      KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this);
157  
  }
158  
159  
  public void stop() {
160  
      KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(this);
161  
  }
162  
163  
  public boolean dispatchKeyEvent(KeyEvent e) {
164  
      boolean shouldSwitch = isDesktopPaneFocused(e.getComponent(), switcher.getDesktop())
165  
              || switcher.hasFocus();
166  
      if (!shouldSwitch) {
167  
          return false;
168  
      }
169  
170  
      KeyStroke keyStrokeForEvent = KeyStroke.getKeyStrokeForEvent(e);
171  
      boolean next = nextStroke.equals(keyStrokeForEvent);
172  
      boolean previous = previousStroke.equals(keyStrokeForEvent);
173  
      boolean dismiss = e.getKeyCode() == KeyEvent.VK_CONTROL && !e.isControlDown();
174  
175  
      if (installInternalFrameSwitcher_v2_debug) print("Have keystroke: " + keyStrokeForEvent + ", " + next + "/" + previous + "/" + dismiss);
176  
      
177  
      if (next) {
178  
          switcher.next(desktopListener.getSwitchableComponentList());
179  
      } else if (previous) {
180  
          switcher.previous(desktopListener.getSwitchableComponentList());
181  
      } else if (dismiss) {
182  
          switcher.dismiss();
183  
      }
184  
      return next || previous || dismiss;
185  
  }
186  
187  
  private boolean isDesktopPaneFocused(Component component, JDesktopPane desktop) {
188  
      return getDesktopPaneParent(component) == desktop;
189  
  }
190  
191  
  private JDesktopPane getDesktopPaneParent(Component component) {
192  
      Component parent = component;
193  
      while (parent != null && !(parent instanceof JDesktopPane)) {
194  
          parent = parent.getParent();
195  
      }
196  
      return (JDesktopPane) parent;
197  
  }
198  
}
199  
200  
sinterface SwitchableComponentsListener<T> {
201  
  /**
202  
   * Get a list of the components that the user can switch through.
203  
   * Ideally, this would be sorted by the last focused order.
204  
   *
205  
   * @return - list of switchable components of type T
206  
   */
207  
  List<T> getSwitchableComponentList();
208  
}
209  
210  
/**
211  
 * This class will do the actual switching based on the lists of components it receives.
212  
 *
213  
 * @param <T> - The type of the components that can be switched. {@link javax.swing.JInternalFrame} initially but
214  
 *            will be extended for tabs and other components.
215  
 */
216  
sinterface Switcher<T> {
217  
  /**
218  
   * Trigger this to select the previous component in the list
219  
   *
220  
   * @param switchableComponentList - list of all the switchable components
221  
   */
222  
  void previous(List<T> switchableComponentList);
223  
224  
  /**
225  
   * Trigger this to select the next component in the list
226  
   *
227  
   * @param switchableComponentList - list of all the switchable components
228  
   */
229  
  void next(List<T> switchableComponentList);
230  
231  
  /**
232  
   * This method will hide the dialog that shows all the switchable components if it exists.
233  
   */
234  
  void dismiss();
235  
236  
  /**
237  
   * Returns the desktop pane on which the switcher is working
238  
   */
239  
  JDesktopPane getDesktop();
240  
241  
  /**
242  
   * Checks whether the switcher component currently has focus
243  
   * @return - true if the switch dialog is open and has focus; false otherwise
244  
   */
245  
  boolean hasFocus();
246  
}
247  
248  
sclass SwitchDialog<T> extends JDialog {
249  
  private final JList<T> list;
250  
251  
  public SwitchDialog(Window owner, List<T> titles) {
252  
    super(owner);
253  
    setUndecorated(true);
254  
    list = new JList<>(new Vector<>(titles));
255  
    addFocusListener(focusLostListener);
256  
    initList();
257  
    setContentPane(jCenteredSection(" Frames ", list));
258  
    pack();
259  
  }
260  
261  
  private void initList() {
262  
    list.setCellRenderer(new JInternalFrameCellRenderer);
263  
    list.setSelectedIndex(0);
264  
    list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
265  
    list.addFocusListener(focusLostListener);
266  
    list.setSelectionModel(new DefaultListSelectionModel() {
267  
      public void removeSelectionInterval(int index0, int index1) {}
268  
      public void addSelectionInterval(int index0, int index1) {
269  
        super.setSelectionInterval(index0, index1);
270  
      }
271  
    });
272  
  }
273  
274  
  public void selectNext() {
275  
    int indexToSelect = list.getSelectedIndex() != list.getModel().getSize() - 1
276  
            ? list.getSelectedIndex() + 1
277  
            : 0;
278  
    list.setSelectedIndex(indexToSelect);
279  
  }
280  
281  
  public void selectPrevious() {
282  
    int indexToSelect = list.getSelectedIndex() != 0
283  
            ? list.getSelectedIndex() - 1
284  
            : list.getModel().getSize() - 1;
285  
    list.setSelectedIndex(indexToSelect > -1 ? indexToSelect : 0);
286  
  }
287  
288  
  public T getSelected() {
289  
    return list.getSelectedValue();
290  
  }
291  
  
292  
  public void unselect() {
293  
    list.setSelectedIndex(-1);
294  
  }
295  
296  
  public void setSwitchableComponents(List<T> switchableComponents) {
297  
    T oldSelection = list.getSelectedValue();
298  
    list.setListData(new Vector<>(switchableComponents));
299  
    list.setSelectedValue(oldSelection, false);
300  
  }
301  
302  
  public JList<T> getList() {
303  
    return list;
304  
  }
305  
306  
  private final FocusAdapter focusLostListener = new FocusAdapter {
307  
    public void focusLost(FocusEvent e) {
308  
      dispose();
309  
    }
310  
  };
311  
}

Author comment

Began life as a copy of #1016825

download  show line numbers  debug dex  old transpilations   

Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, irmadwmeruwu, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1016948
Snippet name: installInternalFrameSwitcher_v2
Eternal ID of this version: #1016948/14
Text MD5: f0c4e3ee36cf88d4eaff3456c7eb859f
Author: stefan
Category: javax / gui
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2018-09-26 17:21:32
Source code size: 10033 bytes / 311 lines
Pitched / IR pitched: No / No
Views / Downloads: 338 / 396
Version history: 13 change(s)
Referenced in: [show references]