sbool installInternalFrameSwitcher_v3_debug; static SwitchDispatcher installInternalFrameSwitcher_v3(final JDesktopPane desktop) { ret swing -> SwitchDispatcher { new DesktopListener desktopListener; desktop.addContainerListener(desktopListener); SwitchDispatcher dispatcher = new(new DesktopSwitcher(desktop), desktopListener); dispatcher.start(); ret dispatcher; }; } sclass JInternalFrameCellRenderer extends DefaultListCellRenderer { static int marginW = 20, marginH = 3; public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { Component c = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); setText(((JInternalFrame) value).getTitle()); setMargin(marginW, marginH, this); ret c; } } sclass DesktopListener extends ContainerAdapter implements SwitchableComponentsListener { private final List frames = new ArrayList<>(); private final boolean titleOptional; private final InternalFrameAdapter frameActivatedListener = new InternalFrameAdapter() { @Override public void internalFrameActivated(InternalFrameEvent e) { JInternalFrame frame = e.getInternalFrame(); frames.remove(frame); frames.add(0, frame); } }; public DesktopListener() { this(false); } public DesktopListener(boolean titleOptional) { this.titleOptional = titleOptional; } @Override public void componentAdded(final ContainerEvent e) { if (e.getChild() instanceof JInternalFrame) { final JInternalFrame frame = (JInternalFrame) e.getChild(); if (isTitleOptional() || frame.getTitle() != null && !frame.getTitle().trim().isEmpty()) { frames.add(frame); frame.addInternalFrameListener(frameActivatedListener); } } } @Override public void componentRemoved(final ContainerEvent e) { if (e.getChild() instanceof JInternalFrame) { JInternalFrame frame = (JInternalFrame) e.getChild(); frames.remove(frame); frame.removeInternalFrameListener(frameActivatedListener); } } private boolean isTitleOptional() { return titleOptional; } public List getSwitchableComponentList() { return frames; } } sclass DesktopSwitcher implements Switcher { private final JDesktopPane jDesktopPane; private SwitchDialog switchDialog; public DesktopSwitcher(JDesktopPane jDesktopPane) { this.jDesktopPane = jDesktopPane; switchDialog = new SwitchDialog(SwingUtilities.getWindowAncestor(jDesktopPane), new ArrayList()); initMouseListener(); } public void previous(List switchableComponentList) { showMenu(switchableComponentList); switchDialog.selectPrevious(); } public void next(List switchableComponentList) { showMenu(switchableComponentList); switchDialog.selectNext(); } public void dismiss() { hideMenu(); } @Override public JDesktopPane getDesktop() { return jDesktopPane; } @Override public boolean hasFocus() { return switchDialog.hasFocus() || switchDialog.getList().hasFocus(); } public SwitchDialog getSwitchDialog() { return switchDialog; } private void showMenu(List switchableComponentList) { switchDialog.setSwitchableComponents(switchableComponentList); if (!switchableComponentList.isEmpty()) { switchDialog.pack(); switchDialog.setLocationRelativeTo(jDesktopPane); switchDialog.setVisible(true); } } private void hideMenu() { if (switchDialog != null && switchDialog.isVisible()) { pcall { JInternalFrame selected = switchDialog.getSelected(); if (selected != null) { switchDialog.unselect(); selected.setSelected(true); selected.toFront(); } } switchDialog.dispose(); } } private void initMouseListener() { switchDialog.getList().addMouseListener(new MouseAdapter() { @Override public void mouseReleased(final MouseEvent e) { hideMenu(); } }); } } sclass SwitchDispatcher implements KeyEventDispatcher { private static final KeyStroke nextStroke = KeyStroke.getKeyStroke("ctrl TAB"); private static final KeyStroke previousStroke = KeyStroke.getKeyStroke("ctrl shift TAB"); Switcher switcher; SwitchableComponentsListener desktopListener; F0 shouldSwitch; // check for additional focused components here public SwitchDispatcher(Switcher switcher, SwitchableComponentsListener switchableComponentsListener) { this.switcher = switcher; this.desktopListener = switchableComponentsListener; } public void start() { KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(this); } public void stop() { KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher(this); } public boolean dispatchKeyEvent(KeyEvent e) { boolean _shouldSwitch = isDesktopPaneFocused(e.getComponent(), switcher.getDesktop()) || switcher.hasFocus() || isTrue(callF(shouldSwitch)); if (!_shouldSwitch) false; KeyStroke keyStrokeForEvent = KeyStroke.getKeyStrokeForEvent(e); boolean next = nextStroke.equals(keyStrokeForEvent); boolean previous = previousStroke.equals(keyStrokeForEvent); boolean dismiss = e.getKeyCode() == KeyEvent.VK_CONTROL && !e.isControlDown(); if (installInternalFrameSwitcher_v3_debug) print("Have keystroke: " + keyStrokeForEvent + ", " + next + "/" + previous + "/" + dismiss); if (next) { switcher.next(desktopListener.getSwitchableComponentList()); } else if (previous) { switcher.previous(desktopListener.getSwitchableComponentList()); } else if (dismiss) { switcher.dismiss(); } return next || previous || dismiss; } private boolean isDesktopPaneFocused(Component component, JDesktopPane desktop) { return getDesktopPaneParent(component) == desktop; } private JDesktopPane getDesktopPaneParent(Component component) { Component parent = component; while (parent != null && !(parent instanceof JDesktopPane)) { parent = parent.getParent(); } return (JDesktopPane) parent; } } sinterface SwitchableComponentsListener { /** * Get a list of the components that the user can switch through. * Ideally, this would be sorted by the last focused order. * * @return - list of switchable components of type JInternalFrame */ List getSwitchableComponentList(); } /** * This class will do the actual switching based on the lists of components it receives. */ sinterface Switcher { /** * Trigger this to select the previous component in the list * * @param switchableComponentList - list of all the switchable components */ void previous(List switchableComponentList); /** * Trigger this to select the next component in the list * * @param switchableComponentList - list of all the switchable components */ void next(List switchableComponentList); /** * This method will hide the dialog that shows all the switchable components if it exists. */ void dismiss(); /** * Returns the desktop pane on which the switcher is working */ JDesktopPane getDesktop(); /** * Checks whether the switcher component currently has focus * @return - true if the switch dialog is open and has focus; false otherwise */ boolean hasFocus(); } sclass SwitchDialog extends JDialog { private final JList list; public SwitchDialog(Window owner, List titles) { super(owner); setUndecorated(true); list = new JList<>(new Vector<>(titles)); addFocusListener(focusLostListener); initList(); setContentPane(jCenteredSection(" Frames ", list)); pack(); } private void initList() { list.setCellRenderer(new JInternalFrameCellRenderer); list.setSelectedIndex(0); list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); list.addFocusListener(focusLostListener); list.setSelectionModel(new DefaultListSelectionModel() { public void removeSelectionInterval(int index0, int index1) {} public void addSelectionInterval(int index0, int index1) { super.setSelectionInterval(index0, index1); } }); } public void selectNext() { int indexToSelect = list.getSelectedIndex() != list.getModel().getSize() - 1 ? list.getSelectedIndex() + 1 : 0; setSelectedIndexAndScroll(list, indexToSelect); } public void selectPrevious() { int indexToSelect = list.getSelectedIndex() != 0 ? list.getSelectedIndex() - 1 : list.getModel().getSize() - 1; setSelectedIndexAndScroll(list, indexToSelect > -1 ? indexToSelect : 0); } public JInternalFrame getSelected() { return list.getSelectedValue(); } public void unselect() { list.setSelectedIndex(-1); } public void setSwitchableComponents(List switchableComponents) { JInternalFrame oldSelection = list.getSelectedValue(); list.setListData(new Vector<>(switchableComponents)); list.setSelectedValue(oldSelection, false); } public JList getList() { return list; } private final FocusAdapter focusLostListener = new FocusAdapter { public void focusLost(FocusEvent e) { dispose(); } }; }