Java tutorial
/* * Copyright (c) 2008-2016 Haulmont. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * */ package com.haulmont.cuba.desktop.gui.components; import com.haulmont.cuba.desktop.App; import com.haulmont.cuba.desktop.DetachedFrame; import com.haulmont.cuba.desktop.gui.data.DesktopContainerHelper; import com.haulmont.cuba.desktop.sys.ButtonTabComponent; import com.haulmont.cuba.desktop.sys.vcl.JTabbedPaneExt; import com.haulmont.cuba.gui.ComponentsHelper; import com.haulmont.cuba.gui.components.Component; import com.haulmont.cuba.gui.components.Frame; import com.haulmont.cuba.gui.components.TabSheet; import com.haulmont.cuba.gui.components.Window; import com.haulmont.cuba.gui.data.impl.DsContextImplementation; import com.haulmont.cuba.gui.settings.Settings; import com.haulmont.cuba.gui.xml.layout.ComponentLoader; import org.apache.commons.lang.StringUtils; import org.dom4j.Element; import javax.annotation.Nonnull; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import java.awt.event.HierarchyEvent; import java.awt.event.HierarchyListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.*; public class DesktopTabSheet extends DesktopAbstractComponent<JTabbedPane> implements TabSheet, DesktopContainer, AutoExpanding { protected Map<Component, String> components = new HashMap<>(); protected List<TabImpl> tabs = new ArrayList<>(); protected Map<JComponent, TabImpl> tabContents = new LinkedHashMap<>(); protected Set<LazyTabInfo> lazyTabs = new HashSet<>(); protected ComponentLoader.Context context; protected boolean initLazyTabListenerAdded; protected boolean postInitTaskAdded; protected boolean componentTabChangeListenerInitialized; protected Set<TabChangeListener> listeners = new HashSet<>(); // CAUTION do not add ChangeListeners directly to impl protected List<ChangeListener> implTabSheetChangeListeners = new ArrayList<>(); public DesktopTabSheet() { impl = new JTabbedPaneExt(); impl.addChangeListener(e -> { for (ChangeListener listener : new ArrayList<>(implTabSheetChangeListeners)) { listener.stateChanged(e); } }); setWidth("100%"); } @Override public void add(Component component) { throw new UnsupportedOperationException(); } @Override public void remove(Component component) { throw new UnsupportedOperationException(); } @Override public void removeAll() { throw new UnsupportedOperationException(); } @Override public Component getOwnComponent(String id) { for (Component tabComponent : components.keySet()) { if (StringUtils.equals(id, tabComponent.getId())) { return tabComponent; } } return null; } @Override public Component getComponent(String id) { return ComponentsHelper.getComponent(this, id); } @Nonnull @Override public Component getComponentNN(String id) { Component component = getComponent(id); if (component == null) { throw new IllegalArgumentException(String.format("Not found component with id '%s'", id)); } return component; } @Override public Collection<Component> getOwnComponents() { return components.keySet(); } @Override public Collection<Component> getComponents() { return ComponentsHelper.getComponents(this); } @Override public Tab addTab(String name, Component component) { if (component.getParent() != null && component.getParent() != this) { throw new IllegalStateException("Component already has parent"); } TabImpl tab = new TabImpl(name, component, false); tabs.add(tab); components.put(component, name); DesktopContainerHelper.assignContainer(component, this); if (component instanceof DesktopAbstractComponent && !isEnabledWithParent()) { ((DesktopAbstractComponent) component).setParentEnabled(false); } JComponent comp = DesktopComponentsHelper.getComposition(component); impl.addTab("", comp); int tabIndex = impl.indexOfComponent(comp); tabContents.put(comp, tab); setTabComponent(tab, tabIndex); if (frame != null) { if (component instanceof BelongToFrame && ((BelongToFrame) component).getFrame() == null) { ((BelongToFrame) component).setFrame(frame); } else { frame.registerComponent(component); } } adjustTabSize(component); component.setParent(this); return tab; } @Override public void setFrame(Frame frame) { super.setFrame(frame); if (frame != null) { for (Component childComponent : components.keySet()) { if (childComponent instanceof BelongToFrame && ((BelongToFrame) childComponent).getFrame() == null) { ((BelongToFrame) childComponent).setFrame(frame); } } } } @Override public void setWidth(String width) { super.setWidth(width); for (Component tab : components.keySet()) { adjustTabSize(tab); } } @Override public void setHeight(String height) { super.setHeight(height); for (Component tab : components.keySet()) { adjustTabSize(tab); } } protected void setTabComponent(TabImpl tab, int componentIndex) { ButtonTabComponent.CloseListener closeListener = new ButtonTabComponent.CloseListener() { @Override public void onTabClose(int tabIndex) { if (tab.getCloseHandler() != null) { tab.getCloseHandler().onTabClose(tab); } else { removeTab(tab.getName()); } } }; ButtonTabComponent.DetachListener detachListener = new ButtonTabComponent.DetachListener() { @Override public void onDetach(int tabIndex) { detachTab(tabIndex); } }; ButtonTabComponent btnTabComponent = new ButtonTabComponent(impl, false, false, closeListener, detachListener); tab.setButtonTabComponent(btnTabComponent); impl.setTabComponentAt(componentIndex, btnTabComponent); } @Override public Tab addLazyTab(String name, Element descriptor, ComponentLoader loader) { DesktopVBox tabContent = new DesktopVBox(); adjustTabSize(tabContent); TabImpl tab = new TabImpl(name, tabContent, true); tabs.add(tab); components.put(tabContent, name); DesktopContainerHelper.assignContainer(tabContent, this); if (!isEnabledWithParent()) { tabContent.setParentEnabled(false); } final JComponent comp = DesktopComponentsHelper.getComposition(tabContent); impl.addTab("", comp); int tabIndex = impl.indexOfComponent(comp); tabContents.put(comp, tab); setTabComponent(tab, tabIndex); lazyTabs.add(new LazyTabInfo(tab, tabContent, descriptor, loader)); if (!initLazyTabListenerAdded) { implTabSheetChangeListeners.add(new LazyTabChangeListener()); initLazyTabListenerAdded = true; } context = loader.getContext(); if (!postInitTaskAdded) { context.addPostInitTask((context1, window) -> initComponentTabChangeListener()); postInitTaskAdded = true; } return tab; } protected void adjustTabSize(Component tabComponent) { if (getWidth() >= 0) { tabComponent.setWidth("100%"); } else { tabComponent.setWidth(Component.AUTO_SIZE); } if (getHeight() >= 0) { tabComponent.setHeight("100%"); } else { tabComponent.setHeight(Component.AUTO_SIZE); } } @Override public void removeTab(String name) { TabImpl tab = getTabImpl(name); Component component = tab.getComponent(); components.remove(component); impl.remove(DesktopComponentsHelper.getComposition(component)); DesktopContainerHelper.assignContainer(component, null); if (component instanceof DesktopAbstractComponent && !isEnabledWithParent()) { ((DesktopAbstractComponent) component).setParentEnabled(true); } component.setParent(null); } @Override public void removeAllTabs() { impl.removeAll(); List<Component> innerComponents = new ArrayList<>(components.keySet()); components.clear(); for (Component component : innerComponents) { DesktopContainerHelper.assignContainer(component, null); if (component instanceof DesktopAbstractComponent && !isEnabledWithParent()) { ((DesktopAbstractComponent) component).setParentEnabled(true); } component.setParent(null); } } protected TabImpl getTabImpl(String name) { TabImpl tab = null; for (TabImpl t : tabs) { if (t.getName().equals(name)) { tab = t; break; } } if (tab == null) throw new IllegalStateException(String.format("Can't find tab '%s'", name)); return tab; } @Override public Tab getTab() { JComponent component = (JComponent) impl.getSelectedComponent(); if (component == null) { return null; // nothing selected } for (TabImpl tabImpl : tabs) { if (DesktopComponentsHelper.getComposition(tabImpl.getComponent()).equals(component)) return tabImpl; } return tabs.get(0); } @Override public void setTab(Tab tab) { Component component = ((TabImpl) tab).getComponent(); impl.setSelectedComponent(DesktopComponentsHelper.getComposition(component)); } @Override public void setTab(String name) { TabImpl tab = getTabImpl(name); impl.setSelectedComponent(DesktopComponentsHelper.getComposition(tab.getComponent())); } @Override public Tab getTab(String name) { return getTabImpl(name); } @Override public Component getTabComponent(String name) { TabImpl tab = getTabImpl(name); return tab.getComponent(); } @Override public Collection<Tab> getTabs() { return Collections.<Tab>unmodifiableCollection(tabs); } @Override public void addListener(TabChangeListener listener) { initComponentTabChangeListener(); listeners.add(listener); } protected void initComponentTabChangeListener() { // init component SelectedTabChangeListener only when needed, making sure it is // after all lazy tabs listeners if (!componentTabChangeListenerInitialized) { implTabSheetChangeListeners.add(new ComponentTabChangeListener()); componentTabChangeListenerInitialized = true; } } protected class ComponentTabChangeListener implements ChangeListener { @Override public void stateChanged(ChangeEvent e) { if (context != null) { context.executeInjectTasks(); context.executePostWrapTasks(); context.executeInitTasks(); } // Fire GUI listener fireTabChanged(); // Execute outstanding post init tasks after GUI listener. // We suppose that context.executePostInitTasks() executes a task once and then remove it from task list. if (context != null) { context.executePostInitTasks(); } Window window = ComponentsHelper.getWindow(DesktopTabSheet.this); if (window != null) { ((DsContextImplementation) window.getDsContext()).resumeSuspended(); } else { log.warn("Please specify Frame for TabSheet"); } } } protected class LazyTabChangeListener implements ChangeListener { @Override public void stateChanged(ChangeEvent e) { initLazyTab((JComponent) impl.getSelectedComponent()); } } protected void initLazyTab(JComponent tab) { LazyTabInfo lti = null; for (LazyTabInfo lazyTabInfo : lazyTabs) { if (lazyTabInfo.getTabComponent() == tab) { lti = lazyTabInfo; break; } } if (lti == null) { // already initialized return; } if (!lti.getTab().isEnabled()) { return; } lazyTabs.remove(lti); lti.loader.createComponent(); Component lazyContent = lti.loader.getResultComponent(); lazyContent.setWidth("100%"); lti.tabContent.add(lazyContent); lti.tabContent.expand(lazyContent, "", ""); lazyContent.setParent(this); lti.loader.loadComponent(); if (lazyContent instanceof DesktopAbstractComponent && !isEnabledWithParent()) { ((DesktopAbstractComponent) lazyContent).setParentEnabled(false); } final Window window = ComponentsHelper.getWindow(DesktopTabSheet.this); if (window != null) { ComponentsHelper.walkComponents(lti.tabContent, (component, name) -> { if (component instanceof HasSettings) { Settings settings = window.getSettings(); if (settings != null) { Element e = settings.get(name); ((HasSettings) component).applySettings(e); } } }); lti.getTab().setLazyInitialized(true); } } @Override public void removeListener(TabChangeListener listener) { listeners.remove(listener); } protected void fireTabChanged() { for (TabChangeListener listener : listeners) { listener.tabChanged(getTab()); } } protected void updateTabVisibility(TabImpl tab) { // find insert/remove index by visibility of existing tabs int currentIndex = tab.getTabIndex(); int idx = 0; for (TabImpl t : tabs) { if (t.equals(tab)) break; if (t.isVisible()) idx++; } JComponent comp = DesktopComponentsHelper.getComposition(tab.getComponent()); if (currentIndex >= 0 || tab.isVisible()) { if (tab.isVisible()) { impl.insertTab(tab.getCaption(), null, comp, null, idx); impl.setTabComponentAt(idx, tab.getButtonTabComponent()); } else { impl.removeTabAt(idx); } } else { //tab is detached DetachedFrame detachedFrame = (DetachedFrame) SwingUtilities.getWindowAncestor(comp); detachedFrame.dispose(); } // if we just detach component, it will return isVisible() == true if (!tab.isVisible()) comp.setVisible(false); } @Override public boolean expandsWidth() { return true; } @Override public boolean expandsHeight() { return false; } @Override public void updateComponent(Component child) { // do nothing requestContainerUpdate(); } protected class TabImpl implements TabSheet.Tab { private String name; private Component component; private String caption; private boolean enabled = true; private boolean visible = true; private boolean closable; private boolean detachable; private boolean lazy; private boolean lazyInitialized; private ButtonTabComponent buttonTabComponent; private String styleName; private TabCloseHandler closeHandler; public TabImpl(String name, Component component, boolean lazy) { this.name = name; this.component = component; this.lazy = lazy; } @Override public String getName() { return name; } @Override public void setName(String name) { this.name = name; } @Override public String getCaption() { return caption; } @Override public void setCaption(String caption) { this.caption = caption; DesktopTabSheet.this.impl.setTitleAt(getTabIndex(), caption); getButtonTabComponent().setCaption(caption); } @Override public boolean isEnabled() { return enabled; } @Override public void setEnabled(boolean enabled) { this.enabled = enabled; buttonTabComponent.setEnabled(enabled); int tabIndex = getTabIndex(); if (tabIndex >= 0) { impl.setEnabledAt(getTabIndex(), enabled); impl.setComponentAt(tabIndex, enabled ? DesktopComponentsHelper.getComposition(component) : null); if (impl.getSelectedIndex() == tabIndex && isLazy() && enabled) { initLazyTab(DesktopComponentsHelper.getComposition(component)); } } if (component instanceof DesktopAbstractComponent) { ((DesktopAbstractComponent) component).setParentEnabled(enabled); } } @Override public boolean isVisible() { return visible; } @Override public void setVisible(boolean visible) { if (visible != this.visible) { this.visible = visible; DesktopTabSheet.this.updateTabVisibility(this); } } @Override public boolean isClosable() { return closable; } @Override public void setClosable(boolean closable) { if (closable != this.closable) { getButtonTabComponent().setCloseable(closable); this.closable = closable; } } @Override public boolean isDetachable() { return detachable; } @Override public void setDetachable(boolean detachable) { if (detachable != this.detachable) { getButtonTabComponent().setDetachable(detachable); this.detachable = detachable; } } @Override public TabCloseHandler getCloseHandler() { return closeHandler; } @Override public void setCloseHandler(TabCloseHandler tabCloseHandler) { this.closeHandler = tabCloseHandler; } public Component getComponent() { return component; } public int getTabIndex() { return DesktopTabSheet.this.impl.indexOfTabComponent(buttonTabComponent); } @Override public String toString() { return name; } @Override public void setStyleName(String styleName) { this.styleName = styleName; ButtonTabComponent buttonTabComponent = getButtonTabComponent(); App.getInstance().getTheme().applyStyle(buttonTabComponent.getTitleLabel(), styleName); } @Override public String getStyleName() { return styleName; } public ButtonTabComponent getButtonTabComponent() { return buttonTabComponent; } public void setButtonTabComponent(ButtonTabComponent buttonTabComponent) { this.buttonTabComponent = buttonTabComponent; } public boolean isLazyInitialized() { return lazyInitialized; } public void setLazyInitialized(boolean lazyInitialized) { this.lazyInitialized = lazyInitialized; } public boolean isLazy() { return lazy; } @Override public String getIcon() { return null; } @Override public void setIcon(String icon) { // do nothing } } protected static class LazyTabInfo { protected DesktopAbstractBox tabContent; protected Element descriptor; protected ComponentLoader loader; protected TabImpl tabImpl; public LazyTabInfo(TabImpl tabImpl, DesktopAbstractBox tabContent, Element descriptor, ComponentLoader loader) { this.descriptor = descriptor; this.loader = loader; this.tabContent = tabContent; this.tabImpl = tabImpl; } protected JComponent getTabComponent() { return DesktopComponentsHelper.getComposition(tabContent); } protected TabImpl getTab() { return tabImpl; } } protected void detachTab(final int tabIndex) { final JComponent tabContent = (JComponent) impl.getComponentAt(tabIndex); TabImpl tabAtIndex = null; for (TabImpl tab : tabs) { if (DesktopComponentsHelper.getComposition(tab.getComponent()) == tabContent) { tabAtIndex = tab; if (tab.isLazy() && !tab.isLazyInitialized()) { initLazyTab(tabContent); } break; } } if (tabAtIndex == null) { throw new IllegalStateException("Unable to find tab to detach"); } final TabImpl tabToDetach = tabAtIndex; final ButtonTabComponent tabComponent = tabToDetach.getButtonTabComponent(); final JFrame frame = new DetachedFrame(tabComponent.getCaption(), impl); frame.setLocationRelativeTo(DesktopComponentsHelper.getTopLevelFrame(this)); impl.remove(tabContent); updateTabsEnabledState(); frame.setSize(impl.getSize()); frame.add(tabContent); final HierarchyListener listener = new HierarchyListener() { @Override public void hierarchyChanged(HierarchyEvent e) { if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) == HierarchyEvent.DISPLAYABILITY_CHANGED && !impl.isDisplayable()) { attachTab(frame, tabToDetach); impl.removeHierarchyListener(this); } } }; frame.addWindowListener(new WindowAdapter() { @Override public void windowClosing(WindowEvent e) { attachTab(frame, tabToDetach); impl.removeHierarchyListener(listener); } }); impl.addHierarchyListener(listener); frame.setVisible(true); } protected void attachTab(JFrame frame, TabImpl tab) { int tabIndex = 0; int attachedBeforeCount = 0; JComponent tabContent = DesktopComponentsHelper.getComposition(tab.getComponent()); String caption = tab.getCaption(); ButtonTabComponent tabComponent = tab.getButtonTabComponent(); for (Map.Entry<JComponent, TabImpl> entry : tabContents.entrySet()) { if (entry.getKey() == tabContent) { tabIndex = attachedBeforeCount; break; } else if (entry.getKey().getParent() == impl) { attachedBeforeCount++; } } impl.add(tabContent, tabIndex); if (!tab.isEnabled()) { impl.setComponentAt(tabIndex, null); } impl.setTitleAt(tabIndex, caption); impl.setTabComponentAt(tabIndex, tabComponent); updateTabsEnabledState(); tabComponent.revalidate(); tabComponent.repaint(); frame.dispose(); } protected void updateTabsEnabledState() { for (TabImpl tab : tabs) { int tabIndex = tab.getTabIndex(); if (tabIndex >= 0) { impl.setEnabledAt(tab.getTabIndex(), tab.isEnabled()); } } } @Override public void setEnabled(boolean enabled) { if (isEnabled() != enabled) { super.setEnabled(enabled); } } @Override public void updateEnabled() { super.updateEnabled(); for (Component component : components.keySet()) { if (component instanceof DesktopAbstractComponent) { JComponent composition = DesktopComponentsHelper.getComposition(component); TabImpl tab = tabContents.get(composition); ((DesktopAbstractComponent) component).setParentEnabled(tab.isEnabled() && isEnabledWithParent()); } } } }