001 /** 002 * Copyright 2010 The Kuali Foundation Licensed under the 003 * Educational Community License, Version 2.0 (the "License"); you may 004 * not use this file except in compliance with the License. You may 005 * obtain a copy of the License at 006 * 007 * http://www.osedu.org/licenses/ECL-2.0 008 * 009 * Unless required by applicable law or agreed to in writing, 010 * software distributed under the License is distributed on an "AS IS" 011 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 012 * or implied. See the License for the specific language governing 013 * permissions and limitations under the License. 014 */ 015 016 package org.kuali.student.lum.common.client.lo; 017 018 import java.util.ArrayList; 019 import java.util.List; 020 021 import org.kuali.student.common.ui.client.application.Application; 022 import org.kuali.student.common.ui.client.configurable.mvc.CanProcessValidationResults; 023 import org.kuali.student.common.ui.client.configurable.mvc.FieldDescriptor; 024 import org.kuali.student.common.ui.client.configurable.mvc.sections.Section; 025 import org.kuali.student.common.ui.client.configurable.mvc.sections.VerticalSection; 026 import org.kuali.student.common.ui.client.widgets.KSButton; 027 import org.kuali.student.common.ui.client.widgets.KSLabel; 028 import org.kuali.student.common.ui.client.widgets.KSButtonAbstract.ButtonStyle; 029 import org.kuali.student.common.ui.client.widgets.list.SelectionChangeEvent; 030 import org.kuali.student.common.ui.client.widgets.list.SelectionChangeHandler; 031 import org.kuali.student.common.ui.client.widgets.search.KSPicker; 032 import org.kuali.student.core.assembly.data.Metadata; 033 import org.kuali.student.core.validation.dto.ValidationResultInfo; 034 import org.kuali.student.core.validation.dto.ValidationResultInfo.ErrorLevel; 035 import org.kuali.student.lum.common.client.lu.LUUIConstants; 036 037 import com.google.gwt.event.dom.client.ChangeEvent; 038 import com.google.gwt.event.dom.client.ChangeHandler; 039 import com.google.gwt.event.dom.client.ClickEvent; 040 import com.google.gwt.event.dom.client.ClickHandler; 041 import com.google.gwt.event.logical.shared.ValueChangeEvent; 042 import com.google.gwt.event.logical.shared.ValueChangeHandler; 043 import com.google.gwt.event.shared.HandlerRegistration; 044 import com.google.gwt.user.client.ui.HasValue; 045 import com.google.gwt.user.client.ui.HorizontalPanel; 046 047 /** 048 * This class manages the users interactions when building/updating Learning 049 * Objectives within the context of managing CLUs. It allows the user to type in 050 * LO text directly or execute a search and select one or more of the returned 051 * LOs. 052 * 053 * Users can then re-organize LOs on the screen including altering the sequence 054 * and creating sub LOs 055 * 056 * @author Kuali Student Team 057 * 058 */ 059 public class LOBuilder extends VerticalSection implements HasValue<List<OutlineNode<LOPicker>>>, CanProcessValidationResults { 060 061 private static String type; 062 private static String state; 063 private static String repoKey; 064 private static String messageGroup; 065 private static String startOfPath; 066 private static String endOfPath = "loInfo/desc/plain"; 067 private static String middleOfPath = "loDisplayInfoList"; 068 HorizontalPanel searchMainPanel = new HorizontalPanel(); 069 KSPicker searchWindow; 070 071 LearningObjectiveList loList; 072 KSLabel instructions; 073 074 protected LOBuilder() { 075 } 076 077 public LOBuilder(String luType, String luState, String luGroup, String loRepoKey, String queryPathStart, final Metadata metadata) { 078 super(); 079 080 type = luType; 081 state = luState; 082 repoKey = loRepoKey; 083 messageGroup = luGroup; 084 startOfPath = queryPathStart; 085 086 if(metadata.getInitialLookup() != null) { 087 searchWindow = new KSPicker(metadata.getInitialLookup(), metadata.getAdditionalLookups()); 088 searchWindow.addValuesChangeHandler(new ValueChangeHandler<List<String>>() { 089 public void onValueChange(ValueChangeEvent<List<String>> event) { 090 List<String> selection = event.getValue(); 091 loList.addSelectedLOs(selection); 092 } 093 } 094 ); 095 searchMainPanel.add(searchWindow); 096 } 097 098 099 instructions = new KSLabel(getLabel(LUUIConstants.LO_INSTRUCTIONS_KEY)); 100 101 loList = new LearningObjectiveList(); 102 103 searchMainPanel.addStyleName("KS-LOBuilder-Search-Panel"); 104 105 loList.addStyleName(LUUIConstants.STYLE_SECTION); 106 loList.addStyleName(LUUIConstants.STYLE_SECTION_DIVIDER); 107 108 instructions.addStyleName("KS-LOBuilder-Instructions"); 109 110 this.add(searchMainPanel); 111 this.add(instructions); 112 this.addSection(loList); 113 } 114 115 /** 116 * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object, 117 * boolean) 118 */ 119 @Override 120 public void setValue(List<OutlineNode<LOPicker>> value, boolean fireEvents) { 121 setValue(value); 122 } 123 124 /** 125 * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object) 126 */ 127 @Override 128 public void setValue(List<OutlineNode<LOPicker>> data) { 129 loList.setValue(data); 130 } 131 132 /** 133 * @see com.google.gwt.user.client.ui.HasValue#getValue() 134 */ 135 @Override 136 public List<OutlineNode<LOPicker>> getValue() { 137 return loList.getValue(); 138 } 139 140 /** 141 * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler) 142 */ 143 @Override 144 public HandlerRegistration addValueChangeHandler(ValueChangeHandler<List<OutlineNode<LOPicker>>> handler) { 145 return loList.addValueChangeHandler(handler); 146 } 147 148 private static String getLabel(String labelKey) { 149 return Application.getApplicationContext().getUILabel(messageGroup, 150 type, state, labelKey); 151 } 152 153 /** 154 * @return the type 155 */ 156 public static String getType() { 157 return type; 158 } 159 160 /** 161 * @return the state 162 */ 163 public static String getState() { 164 return state; 165 } 166 167 public static String getRepoKey() { 168 return repoKey; 169 } 170 171 /** 172 * @return the messageGroup 173 */ 174 public static String getMessageGroup() { 175 return messageGroup; 176 } 177 178 public static class LearningObjectiveList extends VerticalSection implements HasValue<List<OutlineNode<LOPicker>>> { 179 OutlineNodeModel<LOPicker> outlineModel = new OutlineNodeModel<LOPicker>(); 180 KSButton addNew; 181 OutlineManager outlineComposite; 182 183 SelectionChangeHandler loPickerChangeHandler = new SelectionChangeHandler(){ 184 public void onSelectionChange(SelectionChangeEvent event) { 185 fireChangeEvent(); 186 } 187 }; 188 189 public LearningObjectiveList() { 190 addNew = new KSButton("Add item", ButtonStyle.SECONDARY, new ClickHandler() { 191 public void onClick(ClickEvent event) { 192 setValue(getValue()); 193 appendLO(""); 194 reDraw(); 195 } 196 }); 197 198 addNew.addStyleName("KS-LOBuilder-New"); 199 200 outlineModel.addChangeHandler(new ChangeHandler() { 201 public void onChange(ChangeEvent event) { 202 reDraw(); 203 fireChangeEvent(); 204 } 205 }); 206 207 initEmptyLoList(); 208 } 209 210 protected void initEmptyLoList(){ 211 List<String> list = new ArrayList<String>(); 212 list.add(""); 213 list.add(""); 214 list.add(""); 215 list.add(""); 216 list.add(""); 217 addSelectedLOs(list); 218 } 219 220 protected void fireChangeEvent(){ 221 ValueChangeEvent.fire(this, outlineModel.getOutlineNodes()); 222 } 223 224 public List<OutlineNode<LOPicker>> getValue() { 225 return outlineModel.getOutlineNodes(); 226 } 227 228 public void setValue(List<OutlineNode<LOPicker>> value) { 229 outlineModel.clearNodes(); 230 outlineModel.getOutlineNodes().addAll(value); 231 232 if (value == null || value.isEmpty()){ 233 initEmptyLoList(); 234 } else { 235 //Add selection change handler to LOPickers if not set 236 for (OutlineNode<LOPicker> node:value){ 237 LOPicker picker = node.getUserObject(); 238 if (!picker.hasChangeHandler()){ 239 picker.addSelectionChangeHandler(loPickerChangeHandler); 240 } 241 } 242 243 reDraw(); 244 } 245 } 246 247 private void appendLO(String loValue) { 248 OutlineNode<LOPicker> aNode = new OutlineNode<LOPicker>(); 249 LOPicker newPicker = new LOPicker(messageGroup, type, state, 250 repoKey); 251 252 newPicker.addSelectionChangeHandler(loPickerChangeHandler); 253 newPicker.setLOText(loValue); 254 aNode.setUserObject(newPicker); 255 aNode.setModel(outlineModel); 256 257 outlineModel.addOutlineNode(aNode); 258 } 259 260 // add one or more description by going through existing LO box and 261 // populating the empty ones 262 // if not enough empty LO boxes then add new ones 263 public void addSelectedLOs(List<String> loDescription) { 264 265 List<OutlineNode<LOPicker>> existingLOs = outlineModel.getOutlineNodes(); 266 267 int ix = existingLOs.size(); 268 for (String strValue : loDescription) { 269 270 boolean foundEmptyBox = false; 271 while (ix > 0) { 272 ix--; 273 if (existingLOs.get(ix).getUserObject().getLOText().trim() 274 .length() == 0) { 275 existingLOs.get(ix).getUserObject().setLOText(strValue); 276 foundEmptyBox = true; 277 break; 278 } 279 } 280 281 // we didn't find empty LO box so add a new one 282 if (foundEmptyBox == false) { 283 appendLO(strValue); 284 } 285 } 286 reDraw(); 287 } 288 289 private void reDraw() { 290 if (null != outlineComposite) { 291 this.removeSection(outlineComposite); 292 } 293 this.removeWidget(addNew); // no error if it's not currently there 294 outlineComposite = new OutlineManager(startOfPath, middleOfPath, endOfPath); 295 outlineComposite.setValue(outlineModel); 296 this.addSection(outlineComposite); 297 this.addWidget(addNew); 298 outlineComposite.render(); 299 } 300 301 public HandlerRegistration addValueChangeHandler(ValueChangeHandler<List<OutlineNode<LOPicker>>> handler) { 302 return addHandler(handler, ValueChangeEvent.getType()); 303 } 304 305 public SelectionChangeHandler getChangeHandlerForLOPicker(){ 306 return loPickerChangeHandler; 307 } 308 309 @Override 310 public void setValue(List<OutlineNode<LOPicker>> value, 311 boolean fireEvents) { 312 setValue(value); 313 } 314 } 315 316 @Override 317 public ErrorLevel processValidationResults(FieldDescriptor fd, List<ValidationResultInfo> results) { 318 return processValidationResults(fd, results, true); 319 } 320 321 @Override 322 public ErrorLevel processValidationResults(FieldDescriptor fd, List<ValidationResultInfo> results, boolean clearAllValidation) { 323 324 ErrorLevel status = ErrorLevel.OK; 325 326 for (Section section : getSections()) { 327 ErrorLevel level = section.processValidationResults(results, clearAllValidation); 328 if (level.getLevel() > status.getLevel()) { 329 status = level; 330 } 331 } 332 return status; 333 } 334 335 }