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 }