View Javadoc

1   /**
2    * Copyright 2010 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   */
15  
16  package org.kuali.student.common.ui.client.widgets.rules;
17  
18  import java.util.List;
19  
20  import org.kuali.student.common.ui.client.mvc.Callback;
21  import org.kuali.student.common.ui.client.widgets.KSButton;
22  import org.kuali.student.common.ui.client.widgets.KSLabel;
23  import org.kuali.student.common.ui.client.widgets.KSPlaceholder;
24  import org.kuali.student.common.ui.client.widgets.KSProgressIndicator;
25  import org.kuali.student.common.ui.client.widgets.field.layout.element.SpanPanel;
26  import org.kuali.student.common.ui.client.widgets.table.Node;
27  import org.kuali.student.core.statement.dto.ReqComponentInfo;
28  import org.kuali.student.core.statement.dto.StatementOperatorTypeKey;
29  import org.kuali.student.core.statement.dto.StatementTreeViewInfo;
30  
31  import com.google.gwt.event.dom.client.ClickEvent;
32  import com.google.gwt.event.dom.client.ClickHandler;
33  import com.google.gwt.event.shared.HandlerRegistration;
34  import com.google.gwt.user.client.Timer;
35  import com.google.gwt.user.client.ui.FlowPanel;
36  import com.google.gwt.user.client.ui.SimplePanel;
37  import com.google.gwt.user.client.ui.HTMLTable.Cell;
38  
39  public class RuleTableManipulationWidget extends FlowPanel {
40  
41       //rule table manipulation buttons
42      private KSButton btnMakeOR = new KSButton("OR");
43      private KSButton btnMakeAND = new KSButton("AND");
44      private KSButton btnAddToGroup = new KSButton("Group");
45      private KSButton btnMoveRuleDown = new KSButton("Move Down");
46      private KSButton btnMoveRuleUp = new KSButton("Move Up");
47      private KSButton btnUndo = new KSButton("Undo");
48      private KSButton btnRedo = new KSButton("Redo");
49      private KSButton btnDelete = new KSButton("Delete");
50      private SpanPanel separator1 = new SpanPanel(" | ");
51      private SpanPanel separator2 = new SpanPanel(" | ");
52      private FlowPanel topButtonsPanel = new FlowPanel();
53      private FlowPanel ruleTablePanel = new FlowPanel();
54  
55      private KSProgressIndicator twiddler = new KSProgressIndicator();
56  
57      //rule table
58      private RuleTable  ruleTable = new RuleTable(true);
59      private ClickHandler ruleTableSelectionHandler = null;
60      private ClickHandler ruleTableEditClauseHandler = null;
61      private HandlerRegistration textClickHandler = null;
62  
63      //view's data
64      private RuleInfo rule = new RuleInfo();
65      private Callback reqCompEditCallback;
66      private Callback ruleChangedCallback;
67      private boolean isEnabled = true;
68      private boolean isOperatorChecked = false;
69  
70      public RuleTableManipulationWidget() {
71          createButtonsPanel();
72  
73          twiddler.setVisible(false);
74          SimplePanel twiddlerPanel = new SimplePanel();
75          twiddlerPanel.setHeight("30px");
76          twiddlerPanel.setWidget(twiddler);
77  
78          add(topButtonsPanel);
79  
80          ruleTablePanel.setStyleName("KS-Program-Rule-ObjectView-RulePanel");
81          ruleTablePanel.add(ruleTable);
82          add(ruleTablePanel);
83  
84          setupHandlers();
85      }
86  
87      private void setupHandlers() {
88  
89          ruleTableSelectionHandler = new ClickHandler() {
90  
91              @Override
92              public void onClick(ClickEvent event) {
93  
94                  Cell cell = ruleTable.getCellForEvent(event);
95                  if (cell == null) {
96                      return;
97                  }
98  
99                  RuleNodeWidget widget = (RuleNodeWidget) ruleTable.getWidget(cell.getRowIndex(), cell.getCellIndex());
100 
101                 //we don't want to select a cell if user clicked on 'edit' link or the table is disabled for editing
102                 if (widget.isEditMode() || !isEnabled) {
103                     widget.setEditMode(false);
104                     return;
105                 }
106 
107                 //select operator or rule
108                 Object userObject = widget.getNode().getUserObject();
109                 if (userObject instanceof StatementVO) {
110                     StatementVO statementVO = (StatementVO) userObject;
111                     statementVO.setCheckBoxOn(!statementVO.isCheckBoxOn());
112                 } else if (userObject instanceof ReqComponentVO) {
113                     ReqComponentVO reqComponentVO = (ReqComponentVO) userObject;
114                     reqComponentVO.setCheckBoxOn(!reqComponentVO.isCheckBoxOn());
115                 }
116                 updateTable();
117             }
118         };
119         textClickHandler = ruleTable.addTextClickHandler(ruleTableSelectionHandler);
120 
121         ruleTableEditClauseHandler = new ClickHandler() {
122             @Override
123             public void onClick(ClickEvent event) {
124                 Cell cell = ruleTable.getCellForEvent(event);
125                 if (cell == null) {
126                     return;
127                 }
128 
129                 RuleNodeWidget widget = (RuleNodeWidget) ruleTable.getWidget(cell.getRowIndex(), cell.getCellIndex());
130                 Object userObject = widget.getNode().getUserObject();
131                 if (userObject instanceof ReqComponentVO) {
132                     widget.setEditMode(true);
133                     final ReqComponentVO rule = (ReqComponentVO) userObject;
134                     reqCompEditCallback.exec(rule.getReqComponentInfo());
135                 }
136             }
137         };
138 
139         btnMoveRuleDown.addClickHandler(new ClickHandler() {
140             public void onClick(ClickEvent event) {
141                 StatementVO statementVO = rule.getStatementVO();
142                 if (statementVO != null) {
143                     //only allow moving up to 1 req. component but not operands
144                     if (isAbletoMoveReqComp()) {
145                         ReqComponentVO selectedReqCompVO = statementVO.getSelectedReqComponentVOs().get(0);
146                         StatementVO enclosingStatementVO =  statementVO.getEnclosingStatementVO(statementVO, selectedReqCompVO);
147                         enclosingStatementVO.shiftReqComponent("RIGHT", selectedReqCompVO);
148                         rule.getEditHistory().save(statementVO);
149                         redraw(rule.getStatementVO().getStatementTreeViewInfo(), false, true);
150                     }
151                 }
152             }
153         });
154 
155         btnMoveRuleUp.addClickHandler(new ClickHandler() {
156             public void onClick(ClickEvent event) {
157                 StatementVO statementVO = rule.getStatementVO();
158                 if (statementVO != null) {
159                     //only allow moving up to 1 req. component but not operands
160                     if (isAbletoMoveReqComp()) {
161                         ReqComponentVO selectedReqCompVO = statementVO.getSelectedReqComponentVOs().get(0);
162                         StatementVO enclosingStatementVO =  statementVO.getEnclosingStatementVO(statementVO, selectedReqCompVO);
163                         enclosingStatementVO.shiftReqComponent("LEFT", selectedReqCompVO);
164                         rule.getEditHistory().save(statementVO);
165                         redraw(rule.getStatementVO().getStatementTreeViewInfo(), false, true);
166                     }
167                 }
168             }
169         });
170 
171         btnMakeOR.addClickHandler(new ClickHandler() {
172             public void onClick(ClickEvent event) {
173 
174                 //did user select an operand to change from OR to AND ?
175                 if (isMatchingOperandSelected(Token.Or)) {
176                     StatementVO statementVO = rule.getSelectedStatementVOs().get(0);
177                     if (statementVO != null) {
178                         statementVO.getStatementInfo().setOperator(StatementOperatorTypeKey.OR);
179                         statementVO.toggleAndOr();
180                     }
181                 } else {
182                     rule.insertOR();
183                 }
184 
185                 // clone a copy of the unsimplified form for showing intermediate step on the UI
186                 StatementVO unsimplified = RulesUtil.clone(rule.getStatementVO());
187                 boolean structureChanged = rule.getStatementVO().simplify();
188                 rule.getEditHistory().save(rule.getStatementVO());
189 
190                 // sets the statementVO to be the version that hasn't been simplified yet temporarily
191                 if (structureChanged) {
192                     showRuleBeforeSimplify(unsimplified);
193                 } else {
194                     redraw(rule.getStatementVO().getStatementTreeViewInfo(), false, true);
195                 }
196             }
197         });
198 
199         btnMakeAND.addClickHandler(new ClickHandler() {
200             public void onClick(ClickEvent event) {
201 
202                 //did user select an operand to change from AND to OR ?
203                 if (isMatchingOperandSelected(Token.And)) {
204                     StatementVO statementVO = rule.getSelectedStatementVOs().get(0);
205                     if (statementVO != null) {
206                         statementVO.getStatementInfo().setOperator(StatementOperatorTypeKey.AND);
207                         statementVO.toggleAndOr();
208                     }
209                 } else {
210                     rule.insertAND();
211                 }
212 
213                 // clone a copy of the unsimplified form for showing intermediate step on the UI
214                 StatementVO unsimplified = RulesUtil.clone(rule.getStatementVO());
215                 boolean structureChanged = rule.getStatementVO().simplify();
216                 rule.getEditHistory().save(rule.getStatementVO());
217 
218                 // sets the statementVO to be the version that hasn't been simplified yet temporarily
219                 if (structureChanged) {
220                     showRuleBeforeSimplify(unsimplified);
221                 } else {
222                     redraw(rule.getStatementVO().getStatementTreeViewInfo(), false, true);
223                 }
224             }
225         });
226 
227         btnDelete.addClickHandler(new ClickHandler() {
228             public void onClick(ClickEvent event) {
229                 rule.deleteItem();
230 
231                 // clone a copy of the unsimplified form for showing intermediate step on the UI
232                 StatementVO unsimplified = RulesUtil.clone(rule.getStatementVO());
233                 boolean structureChanged = false;
234                 if (rule.getStatementVO() != null) {
235                     structureChanged = rule.getStatementVO().simplify();
236                 }
237 
238                 rule.getEditHistory().save(rule.getStatementVO());
239 
240                 // sets the statementVO to be the version that hasn't been simplified yet temporarily
241                 if (structureChanged) {
242                     showRuleBeforeSimplify(unsimplified);
243                 } else {
244                     redraw((rule.getStatementVO() == null ? null : rule.getStatementVO().getStatementTreeViewInfo()), false, true);
245                 }
246             }
247         });
248 
249         btnAddToGroup.addClickHandler(new ClickHandler() {
250             public void onClick(ClickEvent event) {
251                 rule.addToGroup();
252 
253                 // clone a copy of the unsimplified form for showing intermediate step on the UI
254                 StatementVO unsimplified = RulesUtil.clone(rule.getStatementVO());
255                 boolean structureChanged = false;
256                 if (rule.getStatementVO() != null) {
257                     structureChanged = rule.getStatementVO().simplify();
258                 }
259                 rule.getEditHistory().save(rule.getStatementVO());
260 
261                 // sets the statementVO to be the version that hasn't been simplified yet temporarily
262                 if (structureChanged) {
263                     showRuleBeforeSimplify(unsimplified);
264                 } else {
265                     redraw(rule.getStatementVO().getStatementTreeViewInfo(), false, true);
266                 }
267             }
268         });
269 
270         btnUndo.addClickHandler(new ClickHandler() {
271             public void onClick(ClickEvent event) {
272                 StatementVO previousState = rule.getEditHistory().undo();
273                 if (previousState != null) {
274                     rule.setStatementVO(previousState);
275                 }
276                 redraw(rule.getStatementVO().getStatementTreeViewInfo(), false, true);
277             }
278         });
279 
280         btnRedo.addClickHandler(new ClickHandler() {
281             public void onClick(ClickEvent event) {
282                 StatementVO nextState = rule.getEditHistory().redo();
283                 if (nextState != null) {
284                     rule.setStatementVO(nextState);
285                 }
286                 redraw(rule.getStatementVO().getStatementTreeViewInfo(), false, true);
287             }
288         });
289     }
290 
291     //called when user switches between Logic/Preview/Object view
292     public void redraw() {
293         updateTable();
294     }
295 
296     // called by requirement display widget when user wants to edit a specific piece of rule
297     public void redraw(StatementTreeViewInfo stmtTreeInfo, boolean newRule, Boolean ruleChanged) {
298         rule.getStatementVO().clearStatementAndReqComponents();
299         rule.getStatementVO().setStatementTreeViewInfo(stmtTreeInfo);    //TODO remove req. compon.t
300         if (newRule) {
301             rule.setEditHistory(new EditHistory(rule.getStatementVO()));
302         }
303 
304         rule.getStatementVO().clearSelections();
305         updateTable();
306 
307         if (ruleChanged != null) {
308             ruleChangedCallback.exec(ruleChanged);
309         }
310     }
311 
312     private void updateTable() {
313         setEnableButtons(true);
314         ruleTable.clear();
315         Node tree = rule.getStatementTree();
316         if ((tree != null) && (rule.getStatementVO().getChildCount() > 0)) {
317 
318             //if we didn't have rule before, now we do so add back the rule table
319             if (ruleTablePanel.getWidgetIndex(ruleTable) == -1) {
320                 ruleTablePanel.clear();
321                 ruleTablePanel.add(ruleTable);
322             }
323 
324             if (rule.isSimplifying()) {
325                 twiddler.setText("Simplifying...");
326                 twiddler.setVisible(true);
327             } else {
328                 twiddler.setText("");
329                 twiddler.setVisible(false);
330             }
331             ruleTable.buildTable(tree);
332             textClickHandler.removeHandler();
333             ruleTable.addTextClickHandler(ruleTableSelectionHandler);
334             ruleTable.addEditClauseHandler(ruleTableEditClauseHandler);
335         } else { //no rule exist so don't show rule table and show a message instead
336             ruleTablePanel.clear();
337             ruleTablePanel.add(new KSLabel("No rules have been added"));
338         }
339     }
340 
341     private void showRuleBeforeSimplify(StatementVO unsimplified) {
342         rule.setStatementVO(unsimplified);
343         rule.setSimplifying(true);
344         redraw(rule.getStatementVO().getStatementTreeViewInfo(), false, false);
345         // sleep for a while to show the user how the rule looks like before simplification
346         Timer simplifyingTimer = new Timer() {
347             public void run() {
348                 rule.setSimplifying(false);
349                 rule.setStatementVO(rule.getEditHistory().getLastHistoricStmtVO());
350                 redraw(rule.getStatementVO().getStatementTreeViewInfo(), false, true);
351             }
352         };
353         simplifyingTimer.schedule(1000);
354     }
355 
356     public void setEnabledView(boolean enabled) {
357         setEnableButtons(enabled);
358         ruleTable.setEnabled(enabled);
359         isEnabled = enabled;
360     }
361 
362     public void setEnableButtons(boolean enabled) {
363         if (enabled) {
364             btnMakeAND.setEnabled(isMatchingOperandSelected(Token.And) || rule.statementVOIsGroupAble());
365             btnMakeOR.setEnabled(isMatchingOperandSelected(Token.Or) || rule.statementVOIsGroupAble());
366             btnAddToGroup.setEnabled(rule.isAddToGroupOK());
367             btnUndo.setEnabled(rule.getEditHistory().isUndoable());
368             btnRedo.setEnabled(rule.getEditHistory().isRedoable());
369             btnDelete.setEnabled(isAbleToDelete());
370             btnMoveRuleUp.setEnabled(rule.getStatementVO().isNodeSelected() && !rule.getStatementVO().isFirstSelectedReqComp() && isAbletoMoveReqComp());
371             btnMoveRuleDown.setEnabled(rule.getStatementVO().isNodeSelected() && !rule.getStatementVO().isLastSelectedReqComp() && isAbletoMoveReqComp());
372         } else {
373             btnMakeAND.setEnabled(false);
374             btnMakeOR.setEnabled(false);
375             btnAddToGroup.setEnabled(false);
376             btnUndo.setEnabled(false);
377             btnRedo.setEnabled(false);
378             btnDelete.setEnabled(false);
379             btnMoveRuleUp.setEnabled(false);
380             btnMoveRuleDown.setEnabled(false);
381         }
382     }
383 
384     //right now only allow moving of up to 1 req. component but not operands
385     private boolean isAbletoMoveReqComp() {
386         List<ReqComponentVO> selectedRCs = rule.getStatementVO().getSelectedReqComponentVOs();
387         List<StatementVO> selectedSs = rule.getStatementVO().getSelectedStatementVOs();
388         int numSelectedRCs = (selectedRCs == null)? 0 : selectedRCs.size();
389         int numSelectedSs = (selectedSs == null)? 0 : selectedSs.size();
390         return (numSelectedRCs == 1 && numSelectedSs == 0);
391     }
392 
393     //check whether user selected only 1 operator of opossite type
394     private boolean isMatchingOperandSelected(int type) {
395         List<StatementVO> selectedStmts = rule.getSelectedStatementVOs();
396         if (((rule.getSelectedReqComponentVOs() == null) || (rule.getSelectedReqComponentVOs().isEmpty())) && (selectedStmts.size() == 1)
397                 && (((StatementVO)selectedStmts.get(0)).getType() != type)) {
398             return true;
399         }
400         return false;
401     }
402 
403     private boolean isAbleToDelete() {
404         //return ((rule.getSelectedStatementVOs().size() > 0) || (rule.getSelectedReqComponentVOs().size() > 0));
405         if ((rule.getSelectedStatementVOs() == null || rule.getSelectedStatementVOs().size() == 0) && (rule.getSelectedReqComponentVOs().size() > 1)) {
406             return true;
407         }
408 
409         return rule.statementVOIsDegroupAble();
410     }
411 
412     private void createButtonsPanel() {
413         final String ruleEditButton = "KS-Program-Rule-ObjectView-Button";
414 
415         topButtonsPanel.setStyleName("KS-Program-Rule-ObjectView-ButtonPanel");
416 		btnMakeAND.addStyleName(ruleEditButton);
417         btnMakeAND.addStyleName("KS-Program-Rule-ObjectView-AND-Button");
418         topButtonsPanel.add(btnMakeAND);
419         btnMakeOR.addStyleName("KS-Program-Rule-ObjectView-OR-Button");
420         btnMakeOR.addStyleName(ruleEditButton);
421         topButtonsPanel.add(btnMakeOR);
422         // topButtonsPanel.add(separator1);
423 
424         btnAddToGroup.addStyleName("KS-Program-Rule-ObjectView-Group-Button");
425         btnAddToGroup.addStyleName(ruleEditButton);
426         topButtonsPanel.add(btnAddToGroup);
427         btnMoveRuleUp.addStyleName("KS-Program-Rule-ObjectView-Up-Button");
428         btnMoveRuleUp.addStyleName(ruleEditButton);
429         topButtonsPanel.add(btnMoveRuleUp);
430         btnMoveRuleDown.addStyleName("KS-Program-Rule-ObjectView-Down-Button");
431         btnMoveRuleDown.addStyleName(ruleEditButton);
432         topButtonsPanel.add(btnMoveRuleDown);
433         // topButtonsPanel.add(separator2);
434 
435         btnUndo.addStyleName("KS-Program-Rule-ObjectView-Undo-Button");
436         btnUndo.addStyleName(ruleEditButton);
437         topButtonsPanel.add(btnUndo);
438         btnRedo.addStyleName("KS-Program-Rule-ObjectView-Redo-Button");
439         btnRedo.addStyleName(ruleEditButton);
440         topButtonsPanel.add(btnRedo);
441         btnDelete.addStyleName("KS-Program-Rule-ObjectView-Delete-Button");
442         btnDelete.addStyleName(ruleEditButton);
443         topButtonsPanel.add(btnDelete);
444     }
445 
446     public RuleInfo getRule() {
447         return rule;
448     }
449 
450     public void setRule(RuleInfo rule) {
451         this.rule = rule;
452     }
453 
454     public void addReqCompEditButtonClickCallback(Callback<ReqComponentInfo> callback) {
455         reqCompEditCallback = callback;
456     }
457 
458     public void addRuleChangedButtonClickCallback(Callback<Boolean> callback) {
459         ruleChangedCallback = callback;
460     }
461 }