001: package org.drools.brms.client.decisiontable;
002:
003: /*
004: * Copyright 2005 JBoss Inc
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.util.Iterator;
020:
021: import org.drools.brms.client.decisiontable.model.Cell;
022: import org.drools.brms.client.decisiontable.model.Column;
023: import org.drools.brms.client.decisiontable.model.DecisionTable;
024: import org.drools.brms.client.decisiontable.model.Row;
025:
026: import com.google.gwt.user.client.ui.Composite;
027: import com.google.gwt.user.client.ui.FlexTable;
028: import com.google.gwt.user.client.ui.HorizontalPanel;
029: import com.google.gwt.user.client.ui.Image;
030: import com.google.gwt.user.client.ui.Label;
031: import com.google.gwt.user.client.ui.SourcesTableEvents;
032: import com.google.gwt.user.client.ui.TableListener;
033: import com.google.gwt.user.client.ui.TextBox;
034: import com.google.gwt.user.client.ui.VerticalPanel;
035: import com.google.gwt.user.client.ui.Widget;
036: import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
037:
038: /**
039: * The decision table viewer and editor.
040: *
041: * @author Michael Neale
042: * @author Steven Williams
043: *
044: * TODO: add editors for header stuff, and ability to add rows/cols and shift
045: * rows around This probably can be done from a seperate "editor" such that it
046: * is re-rendered when you need to add/move a col or row.
047: *
048: * Should be able to add/shift stuff around by entering a row number to deal
049: * with.
050: *
051: */
052: public class EditableDTGrid extends Composite {
053:
054: private static final int START_DATA_ROW = 1;
055:
056: private DecisionTable dt;
057:
058: private FlexTable table = new FlexTable();
059:
060: private int currentRow;
061:
062: private int currentCol;
063:
064: private Cell currentCell;
065:
066: private FlexCellFormatter cellFormatter = table
067: .getFlexCellFormatter();
068:
069: public EditableDTGrid(String dtName) {
070: this (dtName, createDecisionTable());
071: }
072:
073: private static DecisionTable createDecisionTable() {
074: DecisionTable dt = new DecisionTable();
075: Column[] columns = { dt.createColumn(), dt.createColumn(),
076: dt.createColumn(), dt.createColumn(),
077: dt.createColumn(), dt.createColumn() };
078: createHeader(dt, columns);
079: for (int i = 0; i < 15; i++) {
080: Row row = dt.createRow();
081: for (int j = 0; j < columns.length; j++) {
082: Cell cell = dt.createCell(row, columns[j]);
083: cell.setValue("boo " + i + ":" + j);
084: }
085: }
086: return dt;
087: }
088:
089: private static void createHeader(DecisionTable dt, Column[] columns) {
090: Row row = dt.getHeaderRow();
091: for (int i = 0; i < 6; i++) {
092: Cell cell = dt.createCell(row, columns[i]);
093: cell.setValue("some header " + i);
094: }
095: }
096:
097: public EditableDTGrid(final String dtName, final DecisionTable dt) {
098: this .dt = dt;
099: VerticalPanel vert = new VerticalPanel();
100:
101: Label title = new Label(dtName);
102: title.setStyleName("dt-editor-Title");
103:
104: HorizontalPanel header = new HorizontalPanel();
105: header.add(new Image("images/decision_table.gif"));
106: header.add(title);
107:
108: Toolbar toolbar = new Toolbar(this );
109:
110: vert.add(header);
111: vert.add(toolbar);
112: vert.add(table);
113:
114: table.setStyleName("dt-editor-Grid");
115: table.addTableListener(new TableListener() {
116:
117: public void onCellClicked(SourcesTableEvents ste, int row,
118: int col) {
119: setCurrentCell(row, col);
120: }
121: });
122:
123: // set up the header
124: populateHeader();
125:
126: // and the data follows
127: populateDataGrid();
128:
129: // needed for Composite
130: initWidget(vert);
131: }
132:
133: protected void setCurrentCell(int row, int column) {
134: if (column > 0 && column <= numCols() && row >= START_DATA_ROW) {
135: Cell selectedCell = dt.getRow(row - 1).getCell(column - 1);
136: if (selectedCell != currentCell) {
137: int col = selectedCell.getColumnIndex() + 1;
138:
139: if (currentRow >= START_DATA_ROW) {
140: TextBox text = (TextBox) table.getWidget(
141: currentRow, currentCol);
142: newCell(currentRow, currentCol, text.getText());
143: }
144: if (currentCell == null || currentRow != row) {
145: if (currentCell != null) {
146: cellFormatter.setStyleName(currentRow, 0,
147: "dt-editor-CountColumn");
148: }
149: cellFormatter.setStyleName(row, 0,
150: "dt-editor-CountColumn-selected");
151: }
152: if (currentCell == null
153: || (currentCell.getColumnIndex() + 1) != col) {
154: if (currentCell != null) {
155: cellFormatter.setStyleName(0, currentCell
156: .getColumnIndex() + 1,
157: "dt-editor-DescriptionCell");
158: }
159: cellFormatter.setStyleName(0, col,
160: "dt-editor-DescriptionCell-selected");
161: }
162: cellFormatter.setStyleName(row, column,
163: "dt-editor-Cell-selected");
164: currentRow = row;
165: currentCol = column;
166: currentCell = selectedCell;
167: editColumn(row, column);
168: }
169: }
170: }
171:
172: private void populateHeader() {
173: Row row = dt.getHeaderRow();
174: // for the count column
175: cellFormatter.setStyleName(0, 0, "dt-editor-DescriptionCell");
176:
177: table.setText(0, 0, "");
178: cellFormatter.setStyleName(0, 0, "dt-editor-CountColumn");
179:
180: for (Iterator it = dt.getColumns().iterator(); it.hasNext();) {
181: Column column = (Column) it.next();
182: newHeaderCell(row.getCell(column));
183: }
184:
185: }
186:
187: /**
188: * This populates the "data" part of the decision table (not the header
189: * bits). It starts at the row offset.
190: *
191: * @param cellFormatter
192: * So it can set the style of each cell that is created.
193: */
194: private void populateDataGrid() {
195: int i = 1;
196: for (Iterator it = dt.getRows().iterator(); it.hasNext(); i++) {
197: Row row = (Row) it.next();
198: table.setText(i, 0, Integer.toString(i));
199: cellFormatter.setStyleName(i, 0, "dt-editor-CountColumn");
200: for (Iterator it2 = dt.getColumns().iterator(); it2
201: .hasNext();) {
202: Column column = (Column) it2.next();
203: newCell(row.getCell(column));
204: }
205:
206: }
207: }
208:
209: private void newCell(Cell cell) {
210: int rowIndex = cell.getRowIndex() + START_DATA_ROW;
211: int columnIndex = cell.getColumnIndex() + 1;
212: newCell(cell, rowIndex, columnIndex, "dt-editor-Cell");
213: }
214:
215: private void newHeaderCell(Cell cell) {
216: int columnIndex = cell.getColumnIndex() + 1;
217: newCell(cell, 0, columnIndex, "dt-editor-DescriptionCell");
218: }
219:
220: private void newCell(Cell cell, int rowIndex, int columnIndex,
221: String style) {
222: table.setText(rowIndex, columnIndex, cell.getValue());
223: cellFormatter.setStyleName(rowIndex, columnIndex, style);
224: cellFormatter.setColSpan(rowIndex, columnIndex, cell
225: .getColspan());
226: cellFormatter.setRowSpan(rowIndex, columnIndex, cell
227: .getRowspan());
228: }
229:
230: private void newCell(int row, int column, String text) {
231: table.setText(row, column, text);
232: cellFormatter.setStyleName(row, column, "dt-editor-Cell");
233: }
234:
235: public void mergeCol() {
236: if (currentCell != null) {
237: dt.mergeCol(currentCell);
238: // if (currentRow >= START_DATA_ROW && currentCol > 0) {
239: int currentSpan = cellFormatter.getColSpan(currentRow,
240: currentCol);
241: if (currentCol + currentSpan <= numCols()) {
242: int nextSpan = cellFormatter.getColSpan(currentRow,
243: currentCol + 1);
244: int currentRowSpan = cellFormatter.getRowSpan(
245: currentRow, currentCol);
246: int nextRowSpan = cellFormatter.getRowSpan(currentRow,
247: currentCol + 1);
248: while (nextRowSpan < currentRowSpan) {
249: mergeRow(currentRow, currentCol + 1);
250: nextRowSpan++;
251: }
252: table.removeCell(currentRow, currentCol + 1);
253: cellFormatter.setColSpan(currentRow, currentCol,
254: currentSpan + nextSpan);
255: }
256: }
257: }
258:
259: public void mergeRow() {
260: if (currentCell != null) {
261: // if (currentRow >= START_DATA_ROW && currentCol > 0) {
262: mergeRow(currentRow, currentCol);
263: }
264: }
265:
266: private void mergeRow(int row, int col) {
267: dt.mergeRow(currentCell);
268: int currentSpan = cellFormatter.getRowSpan(row, col);
269: if (row + currentSpan <= numRows) {
270: int nextSpan = cellFormatter.getRowSpan(row + currentSpan,
271: col);
272: table.removeCell(row + currentSpan, col);
273: cellFormatter.setRowSpan(row, col, currentSpan + nextSpan);
274: }
275: }
276:
277: public void splitCol() {
278: if (currentRow >= START_DATA_ROW && currentCol > 0) {
279: int currentSpan = cellFormatter.getColSpan(currentRow,
280: currentCol);
281: if (currentSpan > 1) {
282: cellFormatter.setColSpan(currentRow, currentCol,
283: currentSpan - 1);
284: int newCol = currentCol + 1;
285: table.insertCell(currentRow, newCol);
286: newCell(currentRow, newCol, "");
287: cellFormatter.setStyleName(currentRow, newCol,
288: "dt-editor-Cell");
289: }
290: }
291: }
292:
293: public void splitRow() {
294: if (currentRow >= START_DATA_ROW && currentCol > 0) {
295: int currentSpan = cellFormatter.getRowSpan(currentRow,
296: currentCol);
297: if (currentSpan > 1) {
298: cellFormatter.setRowSpan(currentRow, currentCol,
299: currentSpan - 1);
300: int newRow = currentRow + currentSpan - 1;
301: table.insertCell(newRow, currentCol);
302: newCell(newRow, currentCol, "");
303: cellFormatter.setStyleName(newRow, currentCol,
304: "dt-editor-Cell");
305: }
306: }
307: }
308:
309: /**
310: * Listener for click events that affect a whole row
311: */
312: interface RowClickListener {
313: void onClick(Widget w, int row);
314: }
315:
316: private int numRows = 14;
317:
318: /**
319: * Shuffle the given row up one
320: *
321: * @param row
322: */
323: void moveUp() {
324: if (currentRow > START_DATA_ROW) {
325: // create a new row above the given row
326: // remember that insertRow will insert above the given row
327: // so this is two above the original row
328: int newRow = addNewRow(currentRow - 1);
329:
330: copyRow(currentRow + 1, newRow);
331:
332: // remove the original row
333: table.removeRow(currentRow + 1);
334:
335: renumberRow(currentRow);
336: currentRow = -1;
337: setCurrentCell(newRow, currentCol);
338: }
339:
340: }
341:
342: /**
343: * Copy the data from the source row to the target row
344: *
345: * @param sourceRow
346: * @param targetRow
347: */
348: private void copyRow(int sourceRow, int targetRow) {
349: int column = 1;
350: int colIndex = 1;
351: while (column <= numCols()) {
352: if (column == currentCol) {
353: TextBox box = (TextBox) table.getWidget(sourceRow,
354: column);
355: newCell(targetRow, colIndex, box.getText());
356: } else {
357: newCell(targetRow, colIndex, table.getText(sourceRow,
358: colIndex));
359: }
360: int colSpan = cellFormatter.getColSpan(sourceRow, colIndex);
361: column = column + colSpan;
362: cellFormatter.setColSpan(targetRow, colIndex++, colSpan);
363: }
364: }
365:
366: /**
367: * Add a new row and set the row number
368: *
369: * @param row
370: * @return
371: */
372: private int addNewRow(int row) {
373: int newRow = table.insertRow(row);
374: table.setText(newRow, 0, Integer.toString(newRow));
375: cellFormatter.setStyleName(newRow, 0, "dt-editor-CountColumn");
376: return newRow;
377: }
378:
379: /**
380: * Shuffle the given row down one
381: *
382: * @param row
383: */
384: void moveDown() {
385: if (currentRow < numRows) {
386: // create a new row below the given row
387: // remember that insertRow will insert above the given row
388: // so this is two below the original row
389: int newRow = addNewRow(currentRow + 2);
390:
391: copyRow(currentRow, newRow);
392: // remove row before adding action widgets so that the row number
393: // is correct (otherwise the shuffle down button may not be
394: // displayed
395: table.removeRow(currentRow);
396: renumberRow(currentRow);
397: renumberRow(currentRow + 1);
398: currentRow = -1;
399: setCurrentCell(newRow - 1, currentCol);
400: }
401: }
402:
403: /**
404: * Delete the specified row
405: */
406: void deleteRow() {
407: numRows--;
408: table.removeRow(currentRow);
409: reorderRows(currentRow);
410: currentRow = -1;
411: clearCurrentCell();
412: }
413:
414: private void clearCurrentCell() {
415: if (currentRow > 0) {
416: cellFormatter.setStyleName(currentRow, currentCol,
417: "dt-editor-Cell");
418: currentRow = -1;
419: }
420: if (currentCol > 0) {
421: cellFormatter.setStyleName(0, currentCol,
422: "dt-editor-DescriptionCell");
423: currentCol = -1;
424: }
425: }
426:
427: /**
428: * Renumber the rows from the start row and change the row the actions will
429: * act on
430: *
431: * @param startRow
432: */
433: private void reorderRows(final int startRow) {
434: for (int r = startRow; r < table.getRowCount(); r++) {
435: renumberRow(r);
436: }
437: }
438:
439: /**
440: * Renumber the given row and change the row the actions will act on
441: *
442: * @param row
443: */
444: private void renumberRow(int row) {
445: table.setText(row, 0, Integer.toString(row));
446: }
447:
448: /**
449: * Add a new row after the specified row
450: */
451: void insertRow() {
452: if (currentRow >= START_DATA_ROW) {
453: numRows++;
454: int newRow = addNewRow(currentRow + 1);
455: int column = 1;
456: for (; column < numCols() + 1; column++) {
457: newCell(newRow, column, "");
458: }
459: // addActions(column, newRow, true);
460: reorderRows(newRow);
461: setCurrentCell(newRow, 1);
462: }
463: }
464:
465: private void editColumn(int row, int column) {
466: String text = table.getText(row, column);
467: editColumn(row, column, text);
468: }
469:
470: private void editColumn(int row, int column, String text) {
471: TextBox box = new TextBox();
472: box.setText(text);
473: box.setStyleName("dt-field-TextBox");
474: table.setWidget(row, column, box);
475: box.setFocus(true);
476: }
477:
478: private int numCols() {
479: return 6;
480: }
481:
482: }
|