/*
* Copyright (c) 2010, grossmann
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the jo-widgets.org nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL jo-widgets.org BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.jowidgets.impl.base.delegate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import org.jowidgets.api.command.IAction;
import org.jowidgets.api.model.IListModelListener;
import org.jowidgets.api.model.item.IActionItemModel;
import org.jowidgets.api.model.item.ICheckedItemModel;
import org.jowidgets.api.model.item.IItemModel;
import org.jowidgets.api.model.item.IItemModelListener;
import org.jowidgets.api.model.item.IMenuItemModel;
import org.jowidgets.api.model.item.IMenuModel;
import org.jowidgets.api.model.item.IRadioItemModel;
import org.jowidgets.api.model.item.ISeparatorItemModel;
import org.jowidgets.api.toolkit.Toolkit;
import org.jowidgets.api.widgets.IActionMenuItem;
import org.jowidgets.api.widgets.IMenu;
import org.jowidgets.api.widgets.IMenuBar;
import org.jowidgets.api.widgets.IMenuItem;
import org.jowidgets.api.widgets.ISelectableMenuItem;
import org.jowidgets.api.widgets.ISubMenu;
import org.jowidgets.api.widgets.blueprint.factory.IBluePrintFactory;
import org.jowidgets.api.widgets.descriptor.IActionMenuItemDescriptor;
import org.jowidgets.api.widgets.descriptor.ICheckedMenuItemDescriptor;
import org.jowidgets.api.widgets.descriptor.IRadioMenuItemDescriptor;
import org.jowidgets.api.widgets.descriptor.ISeparatorMenuItemDescriptor;
import org.jowidgets.api.widgets.descriptor.ISubMenuDescriptor;
import org.jowidgets.api.widgets.descriptor.setup.IAccelerateableMenuItemSetup;
import org.jowidgets.api.widgets.descriptor.setup.IMenuItemSetup;
import org.jowidgets.api.widgets.descriptor.setup.ISelectableItemSetup;
import org.jowidgets.common.widgets.descriptor.IWidgetDescriptor;
import org.jowidgets.impl.model.item.CheckedItemModelBuilder;
import org.jowidgets.impl.model.item.RadioItemModelBuilder;
import org.jowidgets.impl.widgets.basic.ActionMenuItemImpl;
import org.jowidgets.impl.widgets.basic.SelectableMenuItemImpl;
import org.jowidgets.impl.widgets.basic.SeparatorMenuItemImpl;
import org.jowidgets.impl.widgets.basic.SubMenuImpl;
import org.jowidgets.impl.widgets.common.wrapper.invoker.SelectableMenuItemSpiInvoker;
import org.jowidgets.spi.widgets.IActionMenuItemSpi;
import org.jowidgets.spi.widgets.IMenuItemSpi;
import org.jowidgets.spi.widgets.IMenuSpi;
import org.jowidgets.spi.widgets.ISelectableMenuItemSpi;
import org.jowidgets.spi.widgets.ISubMenuSpi;
import org.jowidgets.tools.controller.ListModelAdapter;
import org.jowidgets.util.Assert;
public class MenuDelegate extends DisposableDelegate {
private final IMenuSpi menuSpi;
private final IMenu menu;
private final ItemModelBindingDelegate itemDelegate;
private final List<IMenuItem> children;
private final IListModelListener listModelListener;
private final IItemModelListener itemModelListener;
private final ModelViewIndexConverter<IItemModel> modelViewConverter;
private IMenuModel model;
private boolean onRemoveByDispose;
public MenuDelegate(
final IMenu menu,
final IMenuSpi menuSpi,
final IMenuModel model,
final ItemModelBindingDelegate itemDelegate) {
Assert.paramNotNull(menu, "menu");
Assert.paramNotNull(menuSpi, "menuSpi");
this.children = new ArrayList<IMenuItem>();
this.menu = menu;
this.menuSpi = menuSpi;
this.itemDelegate = itemDelegate;
this.onRemoveByDispose = false;
this.modelViewConverter = new ModelViewIndexConverter<IItemModel>();
this.listModelListener = new ListModelAdapter() {
@Override
public void beforeChildRemove(final int index) {
final IMenuItemModel childModel = getModel().getChildren().get(index);
final int viewIndex = modelViewConverter.removeModel(childModel, index);
childModel.removeItemModelListener(itemModelListener);
if (viewIndex != -1) {
removeItem(viewIndex);
}
}
@Override
public void afterChildAdded(final int index) {
final IMenuItemModel addedModel = getModel().getChildren().get(index);
addChild(index, addedModel);
}
};
this.itemModelListener = new IItemModelListener() {
@Override
public void itemChanged(final IItemModel item) {
final boolean visible = item.isVisible();
final int viewIndex = modelViewConverter.markVisibility(item, visible);
if (viewIndex != -1) {
if (visible) {
addChildToView(viewIndex, (IMenuItemModel) item);
}
else {
removeItem(viewIndex);
}
}
}
};
setModel(model);
}
public IMenuItem addSeparator() {
final IMenuItem result = new SeparatorMenuItemImpl(menu, menuSpi.addSeparator(null));
children.add(result);
addToModel(getModel().getChildren().size(), result);
return result;
}
public IMenuItem addSeparator(final int index) {
final IMenuItem result = new SeparatorMenuItemImpl(menu, menuSpi.addSeparator(index));
children.add(index, result);
addToModel(index, result);
return result;
}
public <WIDGET_TYPE extends IMenuItem> WIDGET_TYPE addMenuItem(final IWidgetDescriptor<? extends WIDGET_TYPE> descriptor) {
final WIDGET_TYPE result = addMenuItemInternal(null, descriptor);
addToModel(getModel().getChildren().size(), result);
return result;
}
public <WIDGET_TYPE extends IMenuItem> WIDGET_TYPE addMenuItem(
final int index,
final IWidgetDescriptor<? extends WIDGET_TYPE> descriptor) {
if (index < 0 || index > children.size()) {
throw new IllegalArgumentException("Index must be between '0' and '" + children.size() + "'.");
}
final WIDGET_TYPE result = addMenuItemInternal(Integer.valueOf(index), descriptor);
addToModel(index, result);
return result;
}
@SuppressWarnings("unchecked")
private <WIDGET_TYPE extends IMenuItem> WIDGET_TYPE addMenuItemInternal(
final Integer index,
final IWidgetDescriptor<? extends WIDGET_TYPE> descriptor) {
Assert.paramNotNull(descriptor, "descriptor");
WIDGET_TYPE result = null;
if (ISubMenuDescriptor.class.isAssignableFrom(descriptor.getDescriptorInterface())) {
final ISubMenuSpi subMenuSpi = menuSpi.addSubMenu(index);
final ISubMenu subMenuItem = new SubMenuImpl(subMenuSpi, menu, (IMenuItemSetup) descriptor);
result = (WIDGET_TYPE) subMenuItem;
}
else if (IActionMenuItemDescriptor.class.isAssignableFrom(descriptor.getDescriptorInterface())) {
final IActionMenuItemSpi actionMenuItemSpi = menuSpi.addActionItem(index);
final IActionMenuItem actionMenuItem = new ActionMenuItemImpl(
menu,
actionMenuItemSpi,
(IAccelerateableMenuItemSetup) descriptor);
result = (WIDGET_TYPE) actionMenuItem;
}
else if (ICheckedMenuItemDescriptor.class.isAssignableFrom(descriptor.getDescriptorInterface())) {
final ISelectableMenuItemSpi selectableMenuItemSpi = menuSpi.addCheckedItem(index);
final ISelectableMenuItem selectableMenuItem = new SelectableMenuItemImpl(
menu,
selectableMenuItemSpi,
(ISelectableItemSetup) descriptor,
new SelectableItemModelBindingDelegate(
new SelectableMenuItemSpiInvoker(selectableMenuItemSpi),
new CheckedItemModelBuilder().build()));
result = (WIDGET_TYPE) selectableMenuItem;
}
else if (IRadioMenuItemDescriptor.class.isAssignableFrom(descriptor.getDescriptorInterface())) {
final ISelectableMenuItemSpi selectableMenuItemSpi = menuSpi.addRadioItem(index);
final ISelectableMenuItem selectableMenuItem = new SelectableMenuItemImpl(
menu,
selectableMenuItemSpi,
(ISelectableItemSetup) descriptor,
new SelectableItemModelBindingDelegate(
new SelectableMenuItemSpiInvoker(selectableMenuItemSpi),
new RadioItemModelBuilder().build()));
result = (WIDGET_TYPE) selectableMenuItem;
}
else if (ISeparatorMenuItemDescriptor.class.isAssignableFrom(descriptor.getDescriptorInterface())) {
final IMenuItemSpi separatorMenuItemSpi = menuSpi.addSeparator(index);
final IMenuItem separatorMenuItem = new SeparatorMenuItemImpl(menu, separatorMenuItemSpi);
result = (WIDGET_TYPE) separatorMenuItem;
}
else {
throw new IllegalArgumentException("Descriptor with type '"
+ descriptor.getDescriptorInterface().getName()
+ "' is not supported");
}
addToChildren(index, result);
return result;
}
private void addToChildren(final Integer index, final IMenuItem menuItem) {
if (index != null) {
children.add(index.intValue(), menuItem);
}
else {
children.add(menuItem);
}
}
private void addToModel(final Integer index, final IMenuItem menuItem) {
model.removeListModelListener(listModelListener);
if (index != null) {
getModel().addItem(index.intValue(), menuItem.getModel());
}
else {
getModel().addItem(menuItem.getModel());
}
model.addListModelListener(listModelListener);
}
private void addChild(final int modelIndex, final IMenuItemModel model) {
final int viewIndex = modelViewConverter.addModel(model, model.isVisible(), modelIndex);
addChildToView(viewIndex, model);
model.addItemModelListener(itemModelListener);
}
private void addChildToView(final int viewIndex, final IMenuItemModel model) {
if (viewIndex != -1) {
final IBluePrintFactory bpf = Toolkit.getBluePrintFactory();
if (model instanceof IRadioItemModel) {
addMenuItemInternal(viewIndex, bpf.radioMenuItem()).setModel(model);
}
else if (model instanceof ICheckedItemModel) {
addMenuItemInternal(viewIndex, bpf.checkedMenuItem()).setModel(model);
}
else if (model instanceof IActionItemModel) {
addMenuItemInternal(viewIndex, bpf.menuItem()).setModel(model);
}
else if (model instanceof IMenuModel) {
addMenuItemInternal(viewIndex, bpf.subMenu()).setModel(model);
}
else if (model instanceof ISeparatorItemModel) {
addMenuItemInternal(viewIndex, bpf.menuSeparator()).setModel(model);
}
}
}
public List<IMenuItem> getChildren() {
return Collections.unmodifiableList(new LinkedList<IMenuItem>(children));
}
public boolean remove(final IMenuItem item) {
Assert.paramNotNull(item, "item");
final int index = children.indexOf(item);
if (index != -1) {
removeItem(index);
return true;
}
else {
return false;
}
}
public void removeItem(final int index) {
final IMenuItem item = children.get(index);
children.remove(index);
item.dispose();
menuSpi.remove(index);
}
public void removeAll() {
for (final IMenuItem item : getChildren()) {
remove(item);
}
}
public IActionMenuItem addAction(final int index, final IAction action) {
final IActionMenuItem result = menu.addItem(index, Toolkit.getBluePrintFactory().menuItem());
result.setAction(action);
return result;
}
public IActionMenuItem addAction(final IAction action) {
final IActionMenuItem result = menu.addItem(Toolkit.getBluePrintFactory().menuItem());
result.setAction(action);
return result;
}
public IMenuModel getModel() {
return model;
}
public void setModel(final IMenuModel model) {
modelViewConverter.clear();
if (this.model != null) {
this.model.removeListModelListener(listModelListener);
for (final IItemModel child : model.getChildren()) {
child.removeItemModelListener(itemModelListener);
}
removeAll();
}
this.model = model;
int childModelIndex = 0;
for (final IMenuItemModel childModel : model.getChildren()) {
addChild(childModelIndex, childModel);
childModelIndex++;
}
model.addListModelListener(listModelListener);
}
@Override
public void dispose() {
if (!isDisposed()) {
this.model.removeListModelListener(listModelListener);
for (final IItemModel child : model.getChildren()) {
child.removeItemModelListener(itemModelListener);
}
if (menu.getParent() instanceof IMenu
&& menu.getParent() != null
&& ((IMenu) menu.getParent()).getChildren().contains(menu)
&& !onRemoveByDispose) {
onRemoveByDispose = true;
((IMenu) menu.getParent()).remove((IMenuItem) menu); //this will invoke dispose by the parent menu
onRemoveByDispose = false;
}
else if (menu.getParent() instanceof IMenuBar
&& menu.getParent() != null
&& ((IMenuBar) menu.getParent()).getMenus().contains(menu)
&& !onRemoveByDispose) {
onRemoveByDispose = true;
((IMenuBar) menu.getParent()).remove(menu); //this will invoke dispose by the parent menu bar
onRemoveByDispose = false;
}
else {
itemDelegate.dispose();
final List<IMenuItem> childrenCopy = new LinkedList<IMenuItem>(children);
//clear the children to avoid that children will be removed
//unnecessarily from its parent container on dispose invocation
children.clear();
for (final IMenuItem child : childrenCopy) {
child.dispose();
}
super.dispose();
}
modelViewConverter.dispose();
}
}
}