/*
* Copyright 2010 IT Mill Ltd.
*
* 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.vaadin.ui;
import java.util.Iterator;
import java.util.Map;
import com.vaadin.event.Action;
import com.vaadin.event.ActionManager;
import com.vaadin.event.Action.Handler;
import com.vaadin.event.MouseEvents.ClickEvent;
import com.vaadin.event.MouseEvents.ClickListener;
import com.vaadin.terminal.PaintException;
import com.vaadin.terminal.PaintTarget;
import com.vaadin.terminal.Scrollable;
import com.vaadin.terminal.gwt.client.MouseEventDetails;
import com.vaadin.terminal.gwt.client.ui.VPanel;
import com.vaadin.ui.themes.Reindeer;
import com.vaadin.ui.themes.Runo;
/**
* Panel - a simple single component container.
*
* @author IT Mill Ltd.
* @version
* 6.3.4
* @since 3.0
*/
@SuppressWarnings("serial")
@ClientWidget(VPanel.class)
public class Panel extends AbstractComponentContainer implements Scrollable,
ComponentContainer.ComponentAttachListener,
ComponentContainer.ComponentDetachListener, Action.Notifier {
private static final String CLICK_EVENT = VPanel.CLICK_EVENT_IDENTIFIER;
/**
* Removes extra decorations from the Panel.
*
* @deprecated this style is no longer part of the core framework and this
* component, even though most built-in themes implement this
* style. Use the constant specified in the theme class file
* that you're using, if it provides one, e.g.
* {@link Reindeer#PANEL_LIGHT} or {@link Runo#PANEL_LIGHT} .
*/
@Deprecated
public static final String STYLE_LIGHT = "light";
/**
* Content of the panel.
*/
private ComponentContainer content;
/**
* Scroll X position.
*/
private int scrollOffsetX = 0;
/**
* Scroll Y position.
*/
private int scrollOffsetY = 0;
/**
* Scrolling mode.
*/
private boolean scrollable = false;
/**
* Keeps track of the Actions added to this component, and manages the
* painting and handling as well.
*/
protected ActionManager actionManager;
/**
* Creates a new empty panel. A VerticalLayout is used as content.
*/
public Panel() {
this((ComponentContainer) null);
}
/**
* Creates a new empty panel which contains the given content. The content
* cannot be null.
*
* @param content
* the content for the panel.
*/
public Panel(ComponentContainer content) {
setContent(content);
setWidth(100, UNITS_PERCENTAGE);
}
/**
* Creates a new empty panel with caption. Default layout is used.
*
* @param caption
* the caption used in the panel.
*/
public Panel(String caption) {
this(caption, null);
}
/**
* Creates a new empty panel with the given caption and content.
*
* @param caption
* the caption of the panel.
* @param content
* the content used in the panel.
*/
public Panel(String caption, ComponentContainer content) {
this(content);
setCaption(caption);
}
/**
* Gets the current layout of the panel.
*
* @return the Current layout of the panel.
* @deprecated A Panel can now contain a ComponentContainer which is not
* necessarily a Layout. Use {@link #getContent()} instead.
*/
@Deprecated
public Layout getLayout() {
if (content instanceof Layout) {
return (Layout) content;
} else if (content == null) {
return null;
}
throw new IllegalStateException(
"Panel does not contain a Layout. Use getContent() instead of getLayout().");
}
/**
* Sets the layout of the panel.
*
* If given layout is null, a VerticalLayout with margins set is used as a
* default.
*
* Components from old layout are not moved to new layout by default
* (changed in 5.2.2). Use function in Layout interface manually.
*
* @param newLayout
* the New layout of the panel.
* @deprecated A Panel can now contain a ComponentContainer which is not
* necessarily a Layout. Use
* {@link #setContent(ComponentContainer)} instead.
*/
@Deprecated
public void setLayout(Layout newLayout) {
setContent(newLayout);
}
/**
* Returns the content of the Panel.
*
* @return
*/
public ComponentContainer getContent() {
return content;
}
/**
*
* Set the content of the Panel. If null is given as the new content then a
* layout is automatically created and set as the content.
*
* @param content
* The new content
*/
public void setContent(ComponentContainer newContent) {
// If the content is null we create the default content
if (newContent == null) {
newContent = createDefaultContent();
}
// if (newContent == null) {
// throw new IllegalArgumentException("Content cannot be null");
// }
if (newContent == content) {
// don't set the same content twice
return;
}
// detach old content if present
if (content != null) {
content.setParent(null);
content
.removeListener((ComponentContainer.ComponentAttachListener) this);
content
.removeListener((ComponentContainer.ComponentDetachListener) this);
}
// Sets the panel to be parent for the content
newContent.setParent(this);
// Sets the new content
content = newContent;
// Adds the event listeners for new content
newContent
.addListener((ComponentContainer.ComponentAttachListener) this);
newContent
.addListener((ComponentContainer.ComponentDetachListener) this);
content = newContent;
}
/**
* Create a ComponentContainer which is added by default to the Panel if
* user does not specify any content.
*
* @return
*/
private ComponentContainer createDefaultContent() {
VerticalLayout layout = new VerticalLayout();
// Force margins by default
layout.setMargin(true);
return layout;
}
/**
* Paints the content of this component.
*
* @param target
* the Paint Event.
* @throws PaintException
* if the paint operation failed.
*/
@Override
public void paintContent(PaintTarget target) throws PaintException {
content.paint(target);
if (isScrollable()) {
target.addVariable(this, "scrollLeft", getScrollLeft());
target.addVariable(this, "scrollTop", getScrollTop());
}
if (actionManager != null) {
actionManager.paintActions(null, target);
}
}
@Override
public void requestRepaintAll() {
// Panel has odd structure, delegate to layout
requestRepaint();
if (getContent() != null) {
getContent().requestRepaintAll();
}
}
/**
* Adds the component into this container.
*
* @param c
* the component to be added.
* @see com.vaadin.ui.AbstractComponentContainer#addComponent(com.vaadin.ui.Component)
*/
@Override
public void addComponent(Component c) {
content.addComponent(c);
// No repaint request is made as we except the underlying container to
// request repaints
}
/**
* Removes the component from this container.
*
* @param c
* The component to be added.
* @see com.vaadin.ui.AbstractComponentContainer#removeComponent(com.vaadin.ui.Component)
*/
@Override
public void removeComponent(Component c) {
content.removeComponent(c);
// No repaint request is made as we except the underlying container to
// request repaints
}
/**
* Gets the component container iterator for going trough all the components
* in the container.
*
* @return the Iterator of the components inside the container.
* @see com.vaadin.ui.ComponentContainer#getComponentIterator()
*/
public Iterator<Component> getComponentIterator() {
return content.getComponentIterator();
}
/**
* Called when one or more variables handled by the implementing class are
* changed.
*
* @see com.vaadin.terminal.VariableOwner#changeVariables(Object, Map)
*/
@Override
public void changeVariables(Object source, Map variables) {
super.changeVariables(source, variables);
if (variables.containsKey(CLICK_EVENT)) {
fireClick((Map<String, Object>) variables.get(CLICK_EVENT));
}
// Get new size
final Integer newWidth = (Integer) variables.get("width");
final Integer newHeight = (Integer) variables.get("height");
if (newWidth != null && newWidth.intValue() != getWidth()) {
setWidth(newWidth.intValue(), UNITS_PIXELS);
}
if (newHeight != null && newHeight.intValue() != getHeight()) {
setHeight(newHeight.intValue(), UNITS_PIXELS);
}
// Scrolling
final Integer newScrollX = (Integer) variables.get("scrollLeft");
final Integer newScrollY = (Integer) variables.get("scrollTop");
if (newScrollX != null && newScrollX.intValue() != getScrollLeft()) {
// set internally, not to fire request repaint
scrollOffsetX = newScrollX.intValue();
}
if (newScrollY != null && newScrollY.intValue() != getScrollTop()) {
// set internally, not to fire request repaint
scrollOffsetY = newScrollY.intValue();
}
// Actions
if (actionManager != null) {
actionManager.handleActions(variables, this);
}
}
/* Scrolling functionality */
/* Documented in interface */
public int getScrollLeft() {
return scrollOffsetX;
}
/**
* @deprecated use getScrollLeft() instead
*/
@Deprecated
public int getScrollOffsetX() {
return getScrollLeft();
}
/* Documented in interface */
public int getScrollTop() {
return scrollOffsetY;
}
/**
* @deprecated use getScrollTop() instead
*/
@Deprecated
public int getScrollOffsetY() {
return getScrollTop();
}
/* Documented in interface */
public boolean isScrollable() {
return scrollable;
}
/* Documented in interface */
public void setScrollable(boolean isScrollingEnabled) {
if (scrollable != isScrollingEnabled) {
scrollable = isScrollingEnabled;
requestRepaint();
}
}
/* Documented in interface */
public void setScrollLeft(int pixelsScrolled) {
if (pixelsScrolled < 0) {
throw new IllegalArgumentException(
"Scroll offset must be at least 0");
}
if (scrollOffsetX != pixelsScrolled) {
scrollOffsetX = pixelsScrolled;
requestRepaint();
}
}
/**
* @deprecated use setScrollLeft() method instead
*/
@Deprecated
public void setScrollOffsetX(int pixels) {
setScrollLeft(pixels);
}
/* Documented in interface */
public void setScrollTop(int pixelsScrolledDown) {
if (pixelsScrolledDown < 0) {
throw new IllegalArgumentException(
"Scroll offset must be at least 0");
}
if (scrollOffsetY != pixelsScrolledDown) {
scrollOffsetY = pixelsScrolledDown;
requestRepaint();
}
}
/**
* @deprecated use setScrollTop() method instead
*/
@Deprecated
public void setScrollOffsetY(int pixels) {
setScrollTop(pixels);
}
/* Documented in superclass */
public void replaceComponent(Component oldComponent, Component newComponent) {
content.replaceComponent(oldComponent, newComponent);
}
/**
* A new component is attached to container.
*
* @see com.vaadin.ui.ComponentContainer.ComponentAttachListener#componentAttachedToContainer(com.vaadin.ui.ComponentContainer.ComponentAttachEvent)
*/
public void componentAttachedToContainer(ComponentAttachEvent event) {
if (event.getContainer() == content) {
fireComponentAttachEvent(event.getAttachedComponent());
}
}
/**
* A component has been detached from container.
*
* @see com.vaadin.ui.ComponentContainer.ComponentDetachListener#componentDetachedFromContainer(com.vaadin.ui.ComponentContainer.ComponentDetachEvent)
*/
public void componentDetachedFromContainer(ComponentDetachEvent event) {
if (event.getContainer() == content) {
fireComponentDetachEvent(event.getDetachedComponent());
}
}
/**
* Notifies the component that it is connected to an application.
*
* @see com.vaadin.ui.Component#attach()
*/
@Override
public void attach() {
// can't call parent here as this is Panels hierarchy is a hack
requestRepaint();
if (content != null) {
content.attach();
}
}
/**
* Notifies the component that it is detached from the application.
*
* @see com.vaadin.ui.Component#detach()
*/
@Override
public void detach() {
// can't call parent here as this is Panels hierarchy is a hack
if (content != null) {
content.detach();
}
}
/**
* Removes all components from this container.
*
* @see com.vaadin.ui.ComponentContainer#removeAllComponents()
*/
@Override
public void removeAllComponents() {
content.removeAllComponents();
}
/*
* ACTIONS
*/
protected ActionManager getActionManager() {
if (actionManager == null) {
actionManager = new ActionManager(this);
}
return actionManager;
}
public <T extends Action & com.vaadin.event.Action.Listener> void addAction(
T action) {
getActionManager().addAction(action);
}
public <T extends Action & com.vaadin.event.Action.Listener> void removeAction(
T action) {
if (actionManager != null) {
actionManager.removeAction(action);
}
}
public void addActionHandler(Handler actionHandler) {
getActionManager().addActionHandler(actionHandler);
}
public void removeActionHandler(Handler actionHandler) {
if (actionManager != null) {
actionManager.removeActionHandler(actionHandler);
}
}
/**
* Removes all action handlers
*/
public void removeAllActionHandlers() {
if (actionManager != null) {
actionManager.removeAllActionHandlers();
}
}
/**
* Add a click listener to the Panel. The listener is called whenever the
* user clicks inside the Panel. Also when the click targets a component
* inside the Panel, provided the targeted component does not prevent the
* click event from propagating.
*
* Use {@link #removeListener(ClickListener)} to remove the listener.
*
* @param listener
* The listener to add
*/
public void addListener(ClickListener listener) {
addListener(CLICK_EVENT, ClickEvent.class, listener,
ClickListener.clickMethod);
}
/**
* Remove a click listener from the Panel. The listener should earlier have
* been added using {@link #addListener(ClickListener)}.
*
* @param listener
* The listener to remove
*/
public void removeListener(ClickListener listener) {
removeListener(CLICK_EVENT, ClickEvent.class, listener);
}
/**
* Fire a click event to all click listeners.
*
* @param object
* The raw "value" of the variable change from the client side.
*/
private void fireClick(Map<String, Object> parameters) {
MouseEventDetails mouseDetails = MouseEventDetails
.deSerialize((String) parameters.get("mouseDetails"));
fireEvent(new ClickEvent(this, mouseDetails));
}
}