Java tutorial
/********************************************************************************** * $URL: $ * $Id: $ *********************************************************************************** * * Copyright (c) 2007 The Sakai Foundation. * * Licensed under the Educational Community License, Version 1.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.opensource.org/licenses/ecl1.php * * 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 org.sakaiproject.scorm.ui.player.components; import java.io.Serializable; import java.net.URLDecoder; import java.util.Enumeration; import javax.swing.tree.DefaultMutableTreeNode; import javax.swing.tree.DefaultTreeModel; import javax.swing.tree.TreeModel; import javax.swing.tree.TreeNode; import org.adl.sequencer.ISeqActivity; import org.adl.sequencer.SeqActivity; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.wicket.AttributeModifier; import org.apache.wicket.Component; import org.apache.wicket.MarkupContainer; import org.apache.wicket.ResourceReference; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.IAjaxCallDecorator; import org.apache.wicket.ajax.calldecorator.CancelEventIfNoAjaxDecorator; import org.apache.wicket.ajax.markup.html.AjaxLink; import org.apache.wicket.markup.ComponentTag; import org.apache.wicket.markup.html.basic.Label; import org.apache.wicket.markup.html.tree.BaseTree; import org.apache.wicket.markup.html.tree.LinkIconPanel; import org.apache.wicket.markup.html.tree.LinkTree; import org.apache.wicket.model.IModel; import org.apache.wicket.model.Model; import org.apache.wicket.spring.injection.annot.SpringBean; import org.apache.wicket.util.time.Duration; import org.sakaiproject.scorm.model.api.SessionBean; import org.sakaiproject.scorm.service.api.LearningManagementSystem; import org.sakaiproject.scorm.service.api.ScormResourceService; import org.sakaiproject.scorm.service.api.ScormSequencingService; import org.sakaiproject.scorm.ui.ResourceNavigator; import org.sakaiproject.scorm.ui.UISynchronizerPanel; import org.sakaiproject.scorm.ui.player.behaviors.ActivityAjaxEventBehavior; public class ActivityTree extends LinkTree { private static Log log = LogFactory.getLog(ActivityTree.class); private static final long serialVersionUID = 1L; private static final String ARIA_TREE_ROLE = "wairole:tree"; protected SessionBean sessionBean; protected UISynchronizerPanel synchronizer; protected boolean wasEmpty = false; protected boolean isEmpty = true; @SpringBean LearningManagementSystem lms; @SpringBean(name = "org.sakaiproject.scorm.service.api.ScormResourceService") ScormResourceService resourceService; @SpringBean(name = "org.sakaiproject.scorm.service.api.ScormSequencingService") ScormSequencingService sequencingService; public ActivityTree(String id, SessionBean sessionBean, UISynchronizerPanel synchronizer) { super(id); this.synchronizer = synchronizer; this.sessionBean = sessionBean; this.setOutputMarkupId(true); bindModel(sessionBean); if (synchronizer != null) synchronizer.synchronizeState(sessionBean, null); add(new AttributeModifier("role", new Model(ARIA_TREE_ROLE))); getTreeState().expandAll(); } protected void bindModel(SessionBean sessionBean) { TreeModel treeModel = sequencingService.getTreeModel(sessionBean); if (null != treeModel) { setModel(new Model((Serializable) treeModel)); isEmpty = false; } else { setModel(new Model(new DefaultTreeModel(new DefaultMutableTreeNode()))); } if (!isEmpty && sessionBean != null) { selectNode(sessionBean.getActivityId()); } } public TreeNode selectNode() { if (sessionBean == null) return null; return selectNode(sessionBean.getActivityId()); } public TreeNode selectNode(String activityId) { if (activityId == null) return null; Model model = (Model) this.getModel(); DefaultTreeModel treeModel = (DefaultTreeModel) model.getObject(); DefaultMutableTreeNode root = (DefaultMutableTreeNode) treeModel.getRoot(); for (Enumeration<DefaultMutableTreeNode> e = root.breadthFirstEnumeration(); e.hasMoreElements();) { DefaultMutableTreeNode node = e.nextElement(); SeqActivity activity = (SeqActivity) node.getUserObject(); if (log.isDebugEnabled()) { log.debug("Activity: " + activity.getID()); log.debug("Title: " + activity.getTitle()); log.debug("Is Constrain Choice: " + activity.getConstrainChoice()); log.debug("Is Control Forward Only: " + activity.getControlForwardOnly()); log.debug("Is Control Mode Choice: " + activity.getControlModeChoice()); log.debug("Is Control Mode Choice Exit: " + activity.getControlModeChoiceExit()); log.debug("Is Control Mode Flow: " + activity.getControlModeFlow()); } String id = activity == null ? null : activity.getID(); if (id != null && id.equals(activityId)) { if (!getTreeState().isNodeSelected(node)) getTreeState().selectNode(node, true); return node; } } return null; } public boolean isChoice() { return sequencingService.isControlModeChoice(sessionBean); } public boolean isFlow() { return sequencingService.isControlModeFlow(sessionBean); } @Override protected void onNodeLinkClicked(Object node, BaseTree tree, AjaxRequestTarget target) { log.debug("onNodeLinkClicked"); ISeqActivity activity = (ISeqActivity) ((DefaultMutableTreeNode) node).getUserObject(); if (log.isDebugEnabled()) log.debug("ID: " + activity.getID() + " State ID: " + activity.getStateID()); sequencingService.navigateToActivity(activity.getID(), sessionBean, new LocalResourceNavigator(), target); if (synchronizer != null) synchronizer.synchronizeState(sessionBean, target); // FIXME: Turning this off to see if its necessary -- probably needs some more intricate solution // FIXME: Turning back on for the moment. ActivityTree.this.bindModel(sessionBean); if (isEmpty() && !wasEmpty) { ActivityTree.this.setVisible(false); if (synchronizer != null) target.addComponent(synchronizer); wasEmpty = true; } this.updateTree(target); } @Override protected Component newNodeComponent(String id, IModel model) { return new ActivityLinkIconPanel(id, model, this); } private static final LinkType ACTIVITY_AJAX_LinkType = new LinkType("ACTIVITY_AJAX"); @Override public LinkType getLinkType() { return ACTIVITY_AJAX_LinkType; } @Override public MarkupContainer newLink(String id, final ILinkCallback callback) { if (getLinkType() == ACTIVITY_AJAX_LinkType) { return new ActivityAjaxLink(id) { private static final long serialVersionUID = 1L; @Override public void onClick(AjaxRequestTarget target) { callback.onClick(target); } }; } return super.newLink(id, callback); } public abstract class ActivityAjaxLink extends AjaxLink { private static final String ARIA_TREEITEM_ROLE = "wairole:treeitem"; /** * Construct. * * @param id */ public ActivityAjaxLink(final String id) { this(id, null); } /** * Construct. * * @param id * @param model */ public ActivityAjaxLink(final String id, final IModel model) { super(id, model); /*if (lms.canUseRelativeUrls()) { add(new AjaxEventBehavior("onclick") { private static final long serialVersionUID = 1L; protected void onEvent(AjaxRequestTarget target) { onClick(target); } protected IAjaxCallDecorator getAjaxCallDecorator() { return new CancelEventIfNoAjaxDecorator(ActivityAjaxLink.this.getAjaxCallDecorator()); } protected void onComponentTag(ComponentTag tag) { // add the onclick handler only if link is enabled if (isLinkEnabled()) { super.onComponentTag(tag); } } }.setThrottleDelay(Duration.ONE_SECOND)); } else { */ add(new ActivityAjaxEventBehavior("onclick", lms.canUseRelativeUrls()) { private static final long serialVersionUID = 1L; @Override protected void onEvent(AjaxRequestTarget target) { onClick(target); } @Override protected IAjaxCallDecorator getAjaxCallDecorator() { return new CancelEventIfNoAjaxDecorator(ActivityAjaxLink.this.getAjaxCallDecorator()); } @Override protected void onComponentTag(ComponentTag tag) { // add the onclick handler only if link is enabled if (isLinkEnabled()) { super.onComponentTag(tag); } } }.setThrottleDelay(Duration.milliseconds(50))); //} add(new AttributeModifier("role", new Model(ARIA_TREEITEM_ROLE))); } } private static final ResourceReference RESOURCE_FOLDER_OPEN = new ResourceReference(ActivityLinkIconPanel.class, "res/application_side_tree.gif"); private static final ResourceReference RESOURCE_FOLDER_CLOSED = new ResourceReference( ActivityLinkIconPanel.class, "res/application_side_tree.gif"); private static final ResourceReference RESOURCE_ITEM = new ResourceReference(ActivityLinkIconPanel.class, "res/tag_blue.gif"); public class ActivityLinkIconPanel extends LinkIconPanel { private Log log = LogFactory.getLog(ActivityLinkIconPanel.class); private static final long serialVersionUID = 1L; public ActivityLinkIconPanel(String id, IModel model, ActivityTree tree) { super(id, model, tree); } @Override protected void onNodeLinkClicked(Object node, BaseTree tree, AjaxRequestTarget target) { ActivityTree.this.onNodeLinkClicked(node, tree, target); } @Override protected Component newContentComponent(String componentId, BaseTree tree, IModel model) { ISeqActivity activity = (ISeqActivity) ((DefaultMutableTreeNode) model.getObject()).getUserObject(); String text = ""; try { text = (null == activity) ? "" : URLDecoder.decode(activity.getTitle(), "UTF-8"); } catch (Exception e) { log.error("Caught exception ", e); } return new Label(componentId, text); } /** * Returns resource reference for closed folder icon. * @param node * @return resource reference */ protected ResourceReference getResourceFolderClosed(TreeNode node) { return RESOURCE_FOLDER_CLOSED; } /** * Returns resource reference for open folder icon. * @param node * @return resource reference */ protected ResourceReference getResourceFolderOpen(TreeNode node) { return RESOURCE_FOLDER_OPEN; } /** * Returns resource reference for a leaf icon. * @param node * @return resource reference */ protected ResourceReference getResourceItemLeaf(TreeNode node) { return RESOURCE_ITEM; } } public boolean isEmpty() { return isEmpty; } public class LocalResourceNavigator extends ResourceNavigator { private static final long serialVersionUID = 1L; @Override protected ScormResourceService resourceService() { return ActivityTree.this.resourceService; } @Override public Component getFrameComponent() { if (synchronizer != null && synchronizer.getContentPanel() != null) return synchronizer.getContentPanel(); return null; } @Override public boolean useLocationRedirect() { return false; } } }