/* * (c) Copyright 2010-2011 AgileBirds * * This file is part of OpenFlexo. * * OpenFlexo is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * OpenFlexo 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>. * */ package org.openflexo.inspector; import java.awt.Component; import java.util.Enumeration; import java.util.Hashtable; import java.util.Iterator; import java.util.Vector; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.JTabbedPane; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.openflexo.inspector.model.InspectorModel; import org.openflexo.inspector.model.TabModel; import org.openflexo.inspector.widget.DenaliWidget; import org.openflexo.inspector.widget.InnerTabWidgetView; import org.openflexo.localization.FlexoLocalization; /** * Main view for the inspector * * @author bmangez, sguerin */ public class InspectorModelView extends JTabbedPane implements ChangeListener { private static final Logger logger = Logger.getLogger(InspectorModelView.class.getPackage().getName()); private Vector<TabModelView> tabViews; private Vector<TabModel> tabs; // private Vector<TabModel> tabsToHide; private Vector<InnerTabWidgetView> _widgets; private InspectableObject _inspectable; private InspectingWidget _inspectingWidget; private InspectorModel _model; public InspectorModelView(InspectorModel _model, InspectingWidget inspectingWidget) { super(); setOpaque(false); _inspectingWidget = inspectingWidget; _widgets = new Vector<InnerTabWidgetView>(); _extraTabs = new Vector<TabModel>(); _extraTabViews = new Hashtable<TabModel, TabModelView>(); // tabsToHide = new Vector<TabModel>(); this._model = _model; build(_model, _inspectingWidget); addChangeListener(this); } public void setInspectedObject(InspectableObject inspectable) { if (_inspectable == null || !_inspectable.equals(inspectable)) { performObserverSwitch(inspectable); _inspectable = inspectable; refreshAllConditionals(); } else { // Only update values updateFromModel(); } } public void updateFromModel() { for (Enumeration e = _widgets.elements(); e.hasMoreElements();) { InnerTabWidgetView widget = (InnerTabWidgetView) e.nextElement(); if (widget instanceof DenaliWidget) { if (!((DenaliWidget) widget).widgetHasFocus()) { if (((DenaliWidget) widget).isWidgetVisible()) { ((DenaliWidget) widget).updateWidgetFromModel(); } } } else { widget.updateWidgetFromModel(); } } refreshAllConditionals(); } private void refreshAllConditionals() { if (logger.isLoggable(Level.FINE)) { logger.fine("REFRESH conditionals"); } for (Enumeration en = tabViews.elements(); en.hasMoreElements();) { TabModelView next = (TabModelView) en.nextElement(); next.valueChange(_inspectable); } refreshTabVisibility(); } public void valueChange(Object newValue, DenaliWidget widget) { if (logger.isLoggable(Level.FINE)) { logger.fine("valueChange with " + newValue + " for " + widget); } for (Enumeration en = tabViews.elements(); en.hasMoreElements();) { TabModelView next = (TabModelView) en.nextElement(); next.valueChange(newValue, widget); } refreshTabVisibility(); } private void refreshTabVisibility() { Component selectComponent = getSelectedComponent(); int selectedIndex = getSelectedIndex(); String title = selectedIndex > -1 ? getTitleAt(selectedIndex) : null; boolean hasChanged = false; int count = 0; try { removeChangeListener(this); for (TabModelView view : tabViews) { boolean tabIsVisible = getController().isTabPanelVisible(view.getTabModel(), getInspectedObject()); if (view.hasVisibleWidgets() && tabIsVisible) { if (view.getParent() == null) { try { add(view, Math.min(count, getTabCount())); hasChanged = true; } catch (IndexOutOfBoundsException e) { e.printStackTrace(); // BMA : catch an exception that seems to be caused by a bug in the APPLE JVM implementation if (getComponentCount() > 0) { setSelectedIndex(0); } revalidate(); repaint(); } } count++; } else if (!view.hasVisibleWidgets() || !tabIsVisible) { try { if (view.getParent() != null) { remove(view); hasChanged = true; } } catch (IndexOutOfBoundsException e) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Index Out Of Bounds Exception occured. Set level to FINE on logger named '" + logger.getName() + "' to see stacktrace: " + e.getMessage()); } if (logger.isLoggable(Level.FINE)) { logger.log(Level.FINE, "Exception occured: " + e.getMessage(), e); } if (getComponentCount() > 0) { setSelectedIndex(0); } revalidate(); repaint(); } } } } finally { addChangeListener(this); } // AJA : commented with approval of BMA to avoid a bug in the wysiwyg (kind of deadlock) // GPO : uncommented the next line because it is essential and logical since we potentially have added/removed components to/from // the tabbed pane if (hasChanged) { validate(); } boolean componentHasBeenSelected = false; if (selectedIndex > -1) { if (selectComponent != null && indexOfComponent(selectComponent) > -1) { setSelectedComponent(selectComponent); componentHasBeenSelected = true; } else if (title != null) { for (int i = 0; i < getTabCount() && !componentHasBeenSelected; i++) { if (title.equals(getTitleAt(i))) { setSelectedIndex(i); componentHasBeenSelected = true; } } } if (!componentHasBeenSelected && selectedIndex > -1) { setSelectedIndex(Math.min(getTabCount() - 1, selectedIndex)); componentHasBeenSelected = true; } } /*boolean hasChanged = false; for (TabModelView view : tabViews) { if (getController().isTabPanelVisible(view.getTabModel(), getInspectedObject())) { if (!view.isVisible()) { view.setVisible(true); hasChanged = true; } } else { if (view.isVisible()) { view.setVisible(false); hasChanged = true; } } } if (hasChanged) { revalidate(); repaint(); }*/ /* int count=0; try { removeChangeListener(this); for (TabModelView view : tabViews) { if (view.hasVisibleWidgets() && !getTabsToHide().contains(view.getTabModel())) { if (view.getParent() == null) { try { add(view, Math.min(count, getTabCount())); } catch (IndexOutOfBoundsException e) { // BMA : catch an exception that seems to be caused by a bug in the APPLE JVM implementation if (getComponentCount() > 0) setSelectedIndex(0); revalidate(); repaint(); } } count++; } else if (!view.hasVisibleWidgets() || getTabsToHide().contains(view.getTabModel())) { try { if (view.getParent() != null) remove(view); } catch (IndexOutOfBoundsException e) { if (logger.isLoggable(Level.WARNING)) logger.warning("Index Out Of Bounds Exception occured. Set level to FINE on logger named '" + logger.getName() + "' to see stacktrace: " + e.getMessage()); if (logger.isLoggable(Level.FINE)) logger.log(Level.FINE, "Exception occured: " + e.getMessage(), e); if (getComponentCount() > 0) setSelectedIndex(0); revalidate(); repaint(); } } } } finally { addChangeListener(this); } // AJA : commented with approval of BMA to avoid a bug in the wysiwyg (kind of deadlock) // GPO : uncommented the next line because it is essential and logical since we potentially have added/removed components to/from // the tabbed pane validate();*/ } public InspectableObject getInspectedObject() { return _inspectable; } public AbstractController getController() { return _inspectingWidget.getController(); } private void build(InspectorModel inspectorModel, InspectingWidget inspectingWidget) { setName(FlexoLocalization.localizedForKey(inspectorModel.title, this)); tabs = buildTabs(inspectorModel, inspectingWidget.getController()); tabViews = new Vector<TabModelView>(); Enumeration en = tabs.elements(); while (en.hasMoreElements()) { TabModel temp = (TabModel) en.nextElement(); TabModelView tabModelView = new TabModelView(temp, this, inspectingWidget.getController()); tabViews.add(tabModelView); if (inspectingWidget instanceof InspectorTabbedPanel) { if (temp.name.equals(((InspectorTabbedPanel) inspectingWidget).getLastInspectedTabName())) { ((InspectorTabbedPanel) inspectingWidget).setNextFocusedTab(tabModelView); } } add(tabModelView); } } private Vector<TabModel> buildTabs(InspectorModel inspectorModel, AbstractController c) { Vector<TabModel> answer = new Vector<TabModel>(); Vector<InspectorModel> inspectorHierarchy = createInspectorHierarchy(inspectorModel); Vector<Vector<TabModel>> vectorOfVectorOfTabs = createVectorOfVector(inspectorHierarchy); Enumeration<Vector<TabModel>> en = vectorOfVectorOfTabs.elements(); while (en.hasMoreElements()) { TabModel tab = TabModel.createMergedModel(en.nextElement(), c); if (tab.getProperties().size() > 0) { answer.add(tab); tab.setInspectorModel(inspectorModel); } } return answer; } private Vector<Vector<TabModel>> createVectorOfVector(Vector inspectorHierarchy) { Vector<Vector<TabModel>> answer = new Vector<Vector<TabModel>>(); Hashtable<String, Vector<TabModel>> allReadyStoredTabs = new Hashtable<String, Vector<TabModel>>(); Enumeration en = inspectorHierarchy.elements(); while (en.hasMoreElements()) { InspectorModel currentInspectorModel = (InspectorModel) en.nextElement(); Iterator tabIter = currentInspectorModel.getTabs().values().iterator(); while (tabIter.hasNext()) { TabModel currentTab = (TabModel) tabIter.next(); if (allReadyStoredTabs.get(currentTab.name) == null) { Vector<TabModel> v = new Vector<TabModel>(); v.add(currentTab); allReadyStoredTabs.put(currentTab.name, v); } else { Vector<TabModel> v = allReadyStoredTabs.get(currentTab.name); v.add(currentTab); } } } int tabCount = allReadyStoredTabs.size(); while (allReadyStoredTabs.size() > 0) { Enumeration en2 = allReadyStoredTabs.keys(); String key = (String) en2.nextElement(); Vector<TabModel> v = allReadyStoredTabs.remove(key); insertVectorInAnswer(v, answer); } if (answer.size() != tabCount) { if (logger.isLoggable(Level.WARNING)) { logger.warning("Error...missing a tab..."); } } return answer; } private void insertVectorInAnswer(Vector<TabModel> vToInsert, Vector<Vector<TabModel>> container) { if (container.size() == 0) { container.add(vToInsert); } else { int indexToInsert = vToInsert.elementAt(0).index.intValue(); Enumeration<Vector<TabModel>> en = container.elements(); boolean isInserted = false; while (!isInserted) { Vector<TabModel> currentV = en.nextElement(); int indexOfCurrent = currentV.elementAt(0).index.intValue(); if (indexToInsert < indexOfCurrent) { container.add(container.indexOf(currentV), vToInsert); isInserted = true; } else { if (!en.hasMoreElements()) { container.add(vToInsert); isInserted = true; } } } } } private Vector<InspectorModel> createInspectorHierarchy(InspectorModel startingModel) { Vector<InspectorModel> answer = new Vector<InspectorModel>(); InspectorModel fatherModel = startingModel; while (fatherModel != null) { answer.add(0, fatherModel); fatherModel = fatherModel.getSuperInspector(); } return answer; } private void performObserverSwitch(InspectableObject newInspectable) { Enumeration en = _widgets.elements(); while (en.hasMoreElements()) { ((InnerTabWidgetView) en.nextElement()).switchObserved(newInspectable); } _inspectingWidget.notifiedInspectedObjectChange(newInspectable); } public void addToWidgets(InnerTabWidgetView widget) { _widgets.add(widget); } public void removeFromWidgets(InnerTabWidgetView widget) { _widgets.remove(widget); } @Override public void stateChanged(ChangeEvent e) { if (ignoreStateChanged) { return; } updateFromModel(); if (_inspectingWidget != null && getSelectedComponent() != null && getSelectedComponent().getTabModel() != null) { _inspectingWidget.notifiedActiveTabChange(getSelectedComponent().getTabModel().name); // logger.info("state changed with "+e); } } @Override public TabModelView getSelectedComponent() { return (TabModelView) super.getSelectedComponent(); } public TabModelView getTabModelViewForName(String name) { for (TabModelView tabView : tabViews) { if (tabView.getTabModel().name.equals(name)) { return tabView; } } return null; } /* private String lastInspectedPropertyName; private String lastInspectedTabName; public void widgetGetFocus(DenaliWidget widget) { lastInspectedPropertyName = widget.getObservedPropertyName(); lastInspectedTabName = widget.getObservedTabName(); } public String getLastInspectedPropertyName() { return lastInspectedPropertyName; } public void setLastInspectedPropertyName(String lstInspectedPropertyName) { this.lastInspectedPropertyName = lstInspectedPropertyName; } public String getLastInspectedTabName() { return lastInspectedTabName; } public void setLastInspectedTabName(String lstInspectedTabName) { this.lastInspectedTabName = lstInspectedTabName; } */ public InspectingWidget getInspectingWidget() { return _inspectingWidget; } /*public Vector<TabModel> getTabsToHide() { return tabsToHide; } public void setTabsToHide(Vector<TabModel> tabsToHide) { this.tabsToHide = tabsToHide; } public void addToTabsToHide(String tabName) { for (TabModel tab: tabs) { if (tabName.equals(tab.name)) { tabsToHide.add(tab); refreshTabVisibility(); return; } } } public void removeFromTabsToHide(String tabName) { for (TabModel tab: tabsToHide) { if (tabName.equals(tab.name)) { tabsToHide.remove(tab); refreshTabVisibility(); return; } } }*/ public int getTabsNb() { return tabViews.size(); } public TabModelView getTabAtIndex(int i) { return tabViews.get(i); } public Vector<TabModelView> getTabViews() { return tabViews; } private Vector<TabModel> _extraTabs; private Hashtable<TabModel, TabModelView> _extraTabViews; boolean ignoreStateChanged = false; public void updateExtraTabs(Vector<TabModel> extraTabs) { if (extraTabs == null) { return; } boolean tabChanged = false; ignoreStateChanged = true; Vector<TabModel> extraTabsToRemove = new Vector<TabModel>(); extraTabsToRemove.addAll(_extraTabs); for (TabModel t : extraTabs) { if (extraTabsToRemove.contains(t)) { // Already contained, do nothing extraTabsToRemove.remove(t); } else { logger.fine("Adding tab " + t.name); TabModelView tabView = retrieveExtraTabModelView(t); tabView.registerWidgets(); insertTab(t.name, null, tabView, null, t.index); _extraTabs.add(t); tabChanged = true; } } for (TabModel t : extraTabsToRemove) { logger.fine("Removing tab " + t.name); TabModelView tabView = retrieveExtraTabModelView(t); tabView.unregisterWidgets(); remove(tabView); _extraTabs.remove(t); /*for (Enumeration<InnerTabWidgetView> e = retrieveExtraTabModelView(t).getAllWidgets(); e.hasMoreElements();) { InnerTabWidgetView w = e.nextElement(); if (w instanceof DenaliWidget) { DenaliWidget widget = (DenaliWidget)w; if (widget.getModel() != null) { //System.out.println("JE vire l'observer"); widget.getModel().deleteInspectorObserver(widget); } } }*/ tabChanged = true; } if (tabChanged) { SwingUtilities.invokeLater(new Runnable() { @Override public void run() { setSelectedIndex(0); } }); } ignoreStateChanged = false; /*_extraTabs = extraTabs; if (extraTabs != null) { for (TabModel t : extraTabs) { System.out.println("Je rajoute un tab "+t.name); insertTab(t.name, null, retrieveExtraTabModelView(t), null, t.index); } }*/ } /*public void removeExtraTabs() { System.out.println("Je vire les tabs en plus"); if (_extraTabs != null) { for (TabModel t : _extraTabs) { System.out.println("Je rajoute un tab "+t.name); remove(retrieveExtraTabModelView(t)); } } }*/ private TabModelView retrieveExtraTabModelView(TabModel tab) { TabModelView returned = _extraTabViews.get(tab); if (returned == null) { // System.out.println("Je cree la TabModelView pour "+tab.name); returned = new TabModelView(tab, this, _inspectingWidget.getController()); _extraTabViews.put(tab, returned); } return returned; } }