/**
* <a href="http://www.openolat.org">
* OpenOLAT - Online Learning and Training</a><br>
* <p>
* Licensed under the Apache License, Version 2.0 (the "License"); <br>
* you may not use this file except in compliance with the License.<br>
* You may obtain a copy of the License at the
* <a href="http://www.apache.org/licenses/LICENSE-2.0">Apache homepage</a>
* <p>
* Unless required by applicable law or agreed to in writing,<br>
* software distributed under the License is distributed on an "AS IS" BASIS, <br>
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br>
* See the License for the specific language governing permissions and <br>
* limitations under the License.
* <p>
* Initial code contributed and copyrighted by<br>
* frentix GmbH, http://www.frentix.com
* <p>
*/
package org.olat.core.gui.control.generic.closablewrapper;
import java.util.List;
import org.apache.commons.lang.StringEscapeUtils;
import org.olat.core.gui.UserRequest;
import org.olat.core.gui.components.Component;
import org.olat.core.gui.components.form.flexible.elements.FormLink;
import org.olat.core.gui.components.link.Link;
import org.olat.core.gui.components.panel.Panel;
import org.olat.core.gui.components.velocity.VelocityContainer;
import org.olat.core.gui.control.Controller;
import org.olat.core.gui.control.Event;
import org.olat.core.gui.control.WindowBackOffice;
import org.olat.core.gui.control.WindowControl;
import org.olat.core.gui.control.controller.BasicController;
import org.olat.core.gui.control.util.ZIndexWrapper;
import org.olat.core.gui.render.ValidationResult;
import org.olat.core.logging.OLATRuntimeException;
/**
* Description:<br>
* <p>
* The closeable callout window controller provides an overlay view right next
* to the target component. The callout can be positioned left, right top or
* below the target.
* <p>
* In non-ajax mode this view will fall back to the CloseableModalController
* <p>
* To close this callout window you have to dispose the controller.
* <p>
* Events fired by this controller:
* <ul>
* <li>CLOSE_WINDOW_EVENT is fired when the callout window has been closed by
* the user (only possible when the option "closable" is set to true"</li>
* </ul>
* <p>
* Initial Date: 08.07.2010 <br>
*
* @author gnaegi
*/
public class CloseableCalloutWindowController extends BasicController {
public static final Event CLOSE_WINDOW_EVENT = new Event("CLOSE_WINDOW_EVENT");
private final CalloutSettings settings;
private VelocityContainer calloutVC;
private CloseableModalController cmc;
/**
* Constructor for a closable callout window controller. After calling the
* constructor, the callout window will be visible immediately, there is no
* need to activate the window. In contrast to the modal dialogs you have to
* render the content of this controller your self with the
* getInitialComponent() method.
*
* @param ureq
* The user request
* @param wControl
* The window control
* @param calloutWindowContent
* The component that should be displayed in the callout window
* @param targetDomID
* The DOM ID of the element which the callout should pop up
* from.
* @param title
* The title or NULL if no title should be used
* @param closable
* true: show close link; false: don't show close link. Note, in
* any case will the callout window be close when the user
* clickes on the background.
* @param cssClasses
* CSS classes that should be applied to the callout window or
* NULL to not use any css classes
*/
public CloseableCalloutWindowController(UserRequest ureq, WindowControl wControl, Component calloutWindowContent,
String targetDomID, String title, boolean closable, String cssClasses) {
this(ureq, wControl, calloutWindowContent, targetDomID, title, closable, cssClasses, new CalloutSettings());
}
public CloseableCalloutWindowController(UserRequest ureq, WindowControl wControl, Component calloutWindowContent,
String targetDomID, String title, boolean closable, String cssClasses, CalloutSettings settings) {
super(ureq, wControl);
this.settings = settings;
boolean ajax = getWindowControl().getWindowBackOffice().getWindowManager().isAjaxEnabled();
if (ajax) {
final Panel guiMsgPlace = new Panel("guimessage_place");
calloutVC = new VelocityContainer("closeablewrapper", velocity_root + "/callout.html", null, this) {
public void validate(UserRequest uureq, ValidationResult vr) {
super.validate(uureq, vr);
// just before rendering, we need to tell the windowbackoffice that we are a favorite for accepting gui-messages.
// the windowbackoffice doesn't know about guimessages, it is only a container that keeps them for one render cycle
WindowBackOffice wbo = getWindowControl().getWindowBackOffice();
List<ZIndexWrapper> zindexed = wbo.getGuiMessages();
zindexed.add(new ZIndexWrapper(guiMsgPlace, 20));
}
};
calloutVC.put("calloutWindowContent", calloutWindowContent);
// Target link
setDOMTarget(targetDomID);
// Config options, see setter methods
calloutVC.contextPut("closable", Boolean.toString(closable));
calloutVC.contextPut("cssClasses", (cssClasses == null ? "small" : cssClasses));
if (title != null) {
String escapedTitle = StringEscapeUtils.escapeJavaScript(StringEscapeUtils.escapeHtml(title));
calloutVC.contextPut("title", escapedTitle);
}
putInitialPanel(calloutVC);
} else {
// Fallback to old-school modal dialog
cmc = new CloseableModalController(wControl, "close", calloutWindowContent, true, title, closable);
listenTo(cmc);
putInitialPanel(new Panel("empty"));
}
}
/**
* Constructor for a closable callout window controller. After calling the
* constructor, the callout window will be visible immediately, there is no
* need to activate the window. In contrast to the modal dialogs you have to
* render the content of this controller your self with the
* getInitialComponent() method.
*
* @param ureq
* The user request
* @param wControl
* The window control
* @param calloutWindowContent
* The component that should be displayed in the callout window
* @param targetLink
* The link which the callout should popup from. The link object
* is not used to catch any events, it only used to get the DOM
* ID of the link for rendering purpose.
*/
public CloseableCalloutWindowController(UserRequest ureq,
WindowControl wControl, Component calloutWindowContent,
Link targetLink, String title, boolean closable, String cssClasses) {
this(ureq, wControl, calloutWindowContent, "o_c" + targetLink.getDispatchID(), title, closable, cssClasses);
}
/**
* Constructor for a closable callout window controller. After calling the
* constructor, the callout window will be visible immediately, there is no
* need to activate the window. In contrast to the modal dialogs you have to
* render the content of this controller your self with the
* getInitialComponent() method.
*
* @param ureq The user request
* @param wControl
* The window control
* @param calloutWindowContent
* The component that should be displayed in the callout window
* @param targetFormLink
* The link which the callout should popup from. The link object
* is not used to catch any events, it only used to get the DOM
* ID of the link for rendering purpose.
*/
public CloseableCalloutWindowController(UserRequest ureq,
WindowControl wControl, Component calloutWindowContent,
FormLink targetFormLink, String title, boolean closable, String cssClasses) {
this(ureq, wControl, calloutWindowContent, "o_fi"
+ targetFormLink.getComponent().getDispatchID(), title, closable, cssClasses);
}
/**
* Constructor for a closable callout window controller. After calling the
* constructor, the callout window will be visible immediately, there is no
* need to activate the window. In contrast to the modal dialogs you have to
* render the content of this controller your self with the
* getInitialComponent() method.
*
* @param ureq
* The user request
* @param wControl
* The window control
* @param calloutWindowContent
* The component that should be displayed in the callout window
* @param targetFormLink
* @param title
* @param closable
* @param cssClasses
* @param settings
*/
public CloseableCalloutWindowController(UserRequest ureq,
WindowControl wControl, Component calloutWindowContent,
FormLink targetFormLink, String title, boolean closable, String cssClasses, CalloutSettings settings) {
this(ureq, wControl, calloutWindowContent, "o_fi"
+ targetFormLink.getComponent().getDispatchID(), title, closable, cssClasses, settings);
}
public String getDOMTarget() {
if (calloutVC != null) {
// Setting the new target makes this callout VC dirty which redraws
// the callout window and re-initializes the ExtJS tooltip
return (String)calloutVC.getContext().get("target");
}
return null;
}
/**
* Set the DOM ID of the target element where the callout window should
* anchored.
*
* @param targetDomID
*/
public void setDOMTarget(String targetDomID) {
if (targetDomID == null)
throw new OLATRuntimeException("Can not set NULL as target DOM ID",
null);
if (calloutVC != null) {
// Setting the new target makes this callout VC dirty which redraws
// the callout window and re-initializes the ExtJS tooltip
String oldTarget = (String) calloutVC.getContext().get("target");
if (oldTarget != null && !oldTarget.equals(targetDomID)) {
// cleanup old window first
// TODO
}
calloutVC.contextPut("target", targetDomID);
}
}
/**
* @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest,
* org.olat.core.gui.components.Component,
* org.olat.core.gui.control.Event)
*/
@Override
protected void event(UserRequest ureq, Component source, Event event) {
if (source == calloutVC) {
if ("close".equals(event.getCommand())) {
deactivate();
// Forward event to parent controller
fireEvent(ureq, CLOSE_WINDOW_EVENT);
}
}
}
/**
* @see org.olat.core.gui.control.DefaultController#event(org.olat.core.gui.UserRequest,
* org.olat.core.gui.control.Controller,
* org.olat.core.gui.control.Event)
*/
protected void event(UserRequest ureq, Controller source, Event event) {
if (source == cmc) {
if (event == CloseableModalController.CLOSE_MODAL_EVENT) {
// Forward event to parent controller
fireEvent(ureq, CLOSE_WINDOW_EVENT);
}
}
}
/**
* @see org.olat.core.gui.control.DefaultController#doDispose()
*/
@Override
protected void doDispose() {
if (cmc != null) {
cmc.dispose();
cmc = null;
}
}
/**
* Add a controller to this callout controller that should be cleaned up
* when this callout controller is diposed. In most scenarios you should
* hold a reference to the content controllers that controls the
* calloutWindowContent, but in rare cases this is not the case and you have
* no local reference to your controller. You can then use this method to
* add your controller. At the dispose time of the callout controller your
* controller will be disposed as well.
*
* @param toBedisposedControllerOnDispose
*/
public void addDisposableChildController(
Controller toBedisposedControllerOnDispose) {
listenTo(toBedisposedControllerOnDispose);
}
/**
* @see org.olat.core.gui.control.DefaultController#getInitialComponent()
*/
public Component getInitialComponent() {
throw new RuntimeException("please use activate() instead");
}
/**
* Call this method to display the callout window now. Use deactivate() to
* close it later on
*/
public void activate() {
if (cmc != null) {
// delegate if in non-ajax mode
cmc.activate();
} else {
// push to modal stack
getWindowControl().pushAsCallout(calloutVC, getDOMTarget(), settings);
}
}
/**
* Call this method to close the callout window and continue with anything
* else.
*/
public void deactivate() {
if (cmc != null) {
// Delegate if in non-ajax mode to modal window
cmc.deactivate();
} else {
// Remove component from stack
getWindowControl().pop();
}
}
}