/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db.gui; import java.awt.Component; import java.awt.KeyboardFocusManager; import java.awt.Rectangle; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.JComponent; import javax.swing.JScrollPane; import javax.swing.JViewport; import javax.swing.ScrollPaneConstants; import com.servoy.j2db.dataprocessing.IDisplayData; import com.servoy.j2db.smart.dataui.DataCalendar; import com.servoy.j2db.smart.dataui.DataChoice; import com.servoy.j2db.smart.dataui.DataComboBox; import com.servoy.j2db.smart.dataui.DataImgMediaField; import com.servoy.j2db.smart.dataui.DataTextArea; import com.servoy.j2db.smart.dataui.DataTextEditor; import com.servoy.j2db.smart.dataui.ScriptButton; import com.servoy.j2db.smart.dataui.SpecialTabPanel; /** * Listens to focus change in smart client and makes sure that the focused * component is always visible on the screen. * * @author gerzse */ public class GlobalAutoScrollerFocusListener implements PropertyChangeListener { public void propertyChange(PropertyChangeEvent e) { if ("focusOwner".equals(e.getPropertyName())) //$NON-NLS-1$ { Component owner = KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); // Object newVal = owner; // if (newVal != null) // { // System.out.print("======== "); // System.out.println(newVal.getClass().getCanonicalName()); // if (newVal instanceof Component) // { // Component c = (Component)newVal; // while (c != null) // { // String name = c.getName(); // if (c instanceof SwingForm) // name = ((SwingForm)c).getController().getName(); // System.out.println("->" + c.getClass().getCanonicalName() + "/" + name); // c = c.getParent(); // } // } // System.out.println("==========="); // } if (owner != null) { // System.out.println("focus moved to ->" + owner.getClass().getCanonicalName() + "/" + owner + "<-"); // if (owner instanceof Component) System.out.println("\towner name is: " + (owner).getName()); // Container focusRoot = KeyboardFocusManager.getCurrentKeyboardFocusManager().getCurrentFocusCycleRoot(); // System.out.println("focus cycle root is now ->" + focusRoot.getClass().getCanonicalName() + "/" + focusRoot + "<-"); owner = getEventualParentScrollPane(owner); /* Auto scroll only when our specific fields receive focus. */ if (owner instanceof IDisplayData || owner instanceof ScriptButton) { /* Find a scrollable parent, if any. */ JComponent scrollableParent = findScrollableParent(owner); while (scrollableParent != null) { /* Compute the coordinates of the owner relative to the scrollable parent. */ Rectangle r = owner.getBounds(); Component c = owner.getParent(); while (c != scrollableParent && c != null) { Rectangle s = c.getBounds(); r.setLocation(r.x + s.x, r.y + s.y); c = c.getParent(); } scrollableParent.scrollRectToVisible(r); // If we are inside a tab panel, then we go up one level and scroll everything again, // because the tab panel itself can be out of the screen now. scrollableParent = findTabPanelParent(scrollableParent); if (scrollableParent != null) scrollableParent = findScrollableParent(scrollableParent.getParent()); } } } } } // Checks two levels of parenthood, in order to detect special controls like // combobox, radios, checkboxes, etc. private Component getEventualParentScrollPane(Component owner) { if (owner != null) { Component parent = owner.getParent(); if (parent != null) { if ((parent instanceof DataCalendar) || (parent instanceof DataComboBox)) return parent; if (parent instanceof JViewport) { parent = parent.getParent(); if ((parent instanceof DataChoice) || (parent instanceof DataTextArea) || (parent instanceof DataTextEditor) || (parent instanceof DataImgMediaField)) return parent; } } } return owner; } private JComponent findTabPanelParent(Component c) { while ((c != null) && !(c instanceof SpecialTabPanel)) c = c.getParent(); if (c != null) return (JComponent)c; else return null; } private JComponent findScrollableParent(Component owner) { if (owner == null) return null; Component c = owner.getParent(); Component prev = null; //if it is an element that has a column header (ex ListView with header) , skip the first JScrollPane while ((c != null) && (!((c instanceof JScrollPane) && (prev != ((JScrollPane)c).getColumnHeader()) && (prev != ((JScrollPane)c).getRowHeader()) && (prev != ((JScrollPane)c).getCorner(ScrollPaneConstants.LOWER_LEFT_CORNER)) && (prev != ((JScrollPane)c).getCorner(ScrollPaneConstants.LOWER_RIGHT_CORNER)) && (prev != ((JScrollPane)c).getCorner(ScrollPaneConstants.UPPER_LEFT_CORNER)) && (prev != ((JScrollPane)c).getCorner(ScrollPaneConstants.UPPER_RIGHT_CORNER))))) { prev = c; c = c.getParent(); } if (c != null) { JScrollPane sp = (JScrollPane)c; Component view = sp.getViewport().getView(); if (view instanceof JComponent) return (JComponent)view; else return null; } else return null; } }