Home | History | Annotate | Download | only in tko
      1 package autotest.tko;
      2 
      3 import autotest.common.JsonRpcCallback;
      4 import autotest.common.JsonRpcProxy;
      5 import autotest.common.Utils;
      6 import autotest.common.CustomHistory.HistoryToken;
      7 import autotest.common.spreadsheet.Spreadsheet;
      8 import autotest.common.spreadsheet.SpreadsheetSelectionManager;
      9 import autotest.common.spreadsheet.Spreadsheet.CellInfo;
     10 import autotest.common.spreadsheet.Spreadsheet.SpreadsheetListener;
     11 import autotest.common.ui.ContextMenu;
     12 import autotest.common.ui.NotifyManager;
     13 import autotest.common.ui.TableActionsPanel;
     14 import autotest.common.ui.TableActionsPanel.TableActionsWithExportCsvListener;
     15 import autotest.common.ui.TableSelectionPanel.SelectionPanelListener;
     16 import autotest.tko.CommonPanel.CommonPanelListener;
     17 import autotest.tko.TableView.TableSwitchListener;
     18 import autotest.tko.TableView.TableViewConfig;
     19 import autotest.tko.TkoSpreadsheetUtils.DrilldownType;
     20 
     21 import com.google.gwt.event.dom.client.ClickEvent;
     22 import com.google.gwt.event.dom.client.ClickHandler;
     23 import com.google.gwt.event.logical.shared.ResizeEvent;
     24 import com.google.gwt.event.logical.shared.ResizeHandler;
     25 import com.google.gwt.event.logical.shared.ValueChangeEvent;
     26 import com.google.gwt.event.logical.shared.ValueChangeHandler;
     27 import com.google.gwt.json.client.JSONArray;
     28 import com.google.gwt.json.client.JSONObject;
     29 import com.google.gwt.json.client.JSONString;
     30 import com.google.gwt.json.client.JSONValue;
     31 import com.google.gwt.user.client.Command;
     32 import com.google.gwt.user.client.Event;
     33 import com.google.gwt.user.client.History;
     34 import com.google.gwt.user.client.Window;
     35 import com.google.gwt.user.client.ui.Anchor;
     36 import com.google.gwt.user.client.ui.Button;
     37 import com.google.gwt.user.client.ui.CheckBox;
     38 import com.google.gwt.user.client.ui.HTML;
     39 import com.google.gwt.user.client.ui.MenuBar;
     40 import com.google.gwt.user.client.ui.Panel;
     41 import com.google.gwt.user.client.ui.SimplePanel;
     42 import com.google.gwt.user.client.ui.VerticalPanel;
     43 
     44 import java.util.HashMap;
     45 import java.util.List;
     46 import java.util.Map;
     47 
     48 public class SpreadsheetView extends ConditionTabView
     49                              implements SpreadsheetListener, TableActionsWithExportCsvListener,
     50                                         CommonPanelListener, SelectionPanelListener {
     51     private static final String HISTORY_ONLY_LATEST = "show_only_latest";
     52     public static final String DEFAULT_ROW = "kernel";
     53     public static final String DEFAULT_COLUMN = "platform";
     54     public static final String DEFAULT_DRILLDOWN = "job_tag";
     55 
     56     private static final String HISTORY_SHOW_INCOMPLETE = "show_incomplete";
     57     private static final String HISTORY_COLUMN = "column";
     58     private static final String HISTORY_ROW = "row";
     59     private static final String HISTORY_CONTENT = "content";
     60 
     61     private static JsonRpcProxy rpcProxy = JsonRpcProxy.getProxy();
     62     private static JsonRpcProxy afeRpcProxy = JsonRpcProxy.getProxy(JsonRpcProxy.AFE_BASE_URL);
     63     private TableSwitchListener listener;
     64     protected Map<String,String[]> drilldownMap = new HashMap<String,String[]>();
     65     private HeaderFieldCollection headerFields = commonPanel.getHeaderFields();
     66 
     67     private SpreadsheetHeaderSelect rowSelect = new SpreadsheetHeaderSelect(headerFields);
     68     private SpreadsheetHeaderSelectorView rowSelectDisplay = new SpreadsheetHeaderSelectorView();
     69     private SpreadsheetHeaderSelect columnSelect = new SpreadsheetHeaderSelect(headerFields);
     70     private SpreadsheetHeaderSelectorView columnSelectDisplay = new SpreadsheetHeaderSelectorView();
     71     private ContentSelect contentSelect = new ContentSelect(headerFields);
     72     private CheckBox showIncomplete = new CheckBox("Show incomplete tests");
     73     private CheckBox showOnlyLatest = new CheckBox("Show only latest test per cell");
     74     private Button queryButton = new Button("Query");
     75     private TestGroupDataSource normalDataSource = TestGroupDataSource.getStatusCountDataSource();
     76     private TestGroupDataSource latestDataSource = TestGroupDataSource.getLatestTestsDataSource();
     77     private Spreadsheet spreadsheet = new Spreadsheet();
     78     private SpreadsheetDataProcessor spreadsheetProcessor =
     79         new SpreadsheetDataProcessor(spreadsheet);
     80     private SpreadsheetSelectionManager selectionManager =
     81         new SpreadsheetSelectionManager(spreadsheet, null);
     82     private TableActionsPanel actionsPanel = new TableActionsPanel(false);
     83     private Panel jobCompletionPanel = new SimplePanel();
     84     private boolean currentShowIncomplete, currentShowOnlyLatest;
     85     private boolean notYetQueried = true;
     86 
     87     public SpreadsheetView(TableSwitchListener listener) {
     88         this.listener = listener;
     89         commonPanel.addListener(this);
     90         rowSelect.bindDisplay(rowSelectDisplay);
     91         columnSelect.bindDisplay(columnSelectDisplay);
     92     }
     93 
     94     @Override
     95     public String getElementId() {
     96         return "spreadsheet_view";
     97     }
     98 
     99     @Override
    100     public void initialize() {
    101         super.initialize();
    102 
    103         setHeaderSelectField(rowSelect, DEFAULT_ROW);
    104         setHeaderSelectField(columnSelect, DEFAULT_COLUMN);
    105 
    106         actionsPanel.setActionsWithCsvListener(this);
    107         actionsPanel.setSelectionListener(this);
    108         actionsPanel.setVisible(false);
    109 
    110         contentSelect.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
    111             public void onValueChange(ValueChangeEvent<Boolean> event) {
    112                 if (event.getValue()) {
    113                     showOnlyLatest.setValue(true);
    114                     showOnlyLatest.setEnabled(false);
    115                 } else {
    116                     showOnlyLatest.setEnabled(true);
    117                 }
    118             }
    119         });
    120 
    121         updateViewFromState();
    122 
    123         queryButton.addClickHandler(new ClickHandler() {
    124             public void onClick(ClickEvent event) {
    125                 doQueryWithCommonPanelCheck();
    126                 updateHistory();
    127             }
    128         });
    129 
    130         spreadsheet.setVisible(false);
    131         spreadsheet.setListener(this);
    132 
    133         Anchor swapLink = new Anchor("swap");
    134         swapLink.addClickHandler(new ClickHandler() {
    135             public void onClick(ClickEvent event) {
    136                 SpreadsheetHeaderSelect.State rowState = rowSelect.getStateFromView();
    137                 rowSelect.loadFromState(columnSelect.getStateFromView());
    138                 columnSelect.loadFromState(rowState);
    139             }
    140         });
    141 
    142         Panel filterOptions = new VerticalPanel();
    143         filterOptions.add(showIncomplete);
    144         filterOptions.add(showOnlyLatest);
    145 
    146         addWidget(filterOptions, "ss_filter_options");
    147         addWidget(rowSelectDisplay, "ss_row_select");
    148         addWidget(columnSelectDisplay, "ss_column_select");
    149         addWidget(contentSelect, "ss_additional_content");
    150         addWidget(swapLink, "ss_swap");
    151         addWidget(queryButton, "ss_query_controls");
    152         addWidget(actionsPanel, "ss_actions");
    153         addWidget(spreadsheet, "ss_spreadsheet");
    154         addWidget(jobCompletionPanel, "ss_job_completion");
    155 
    156         Window.addResizeHandler(new ResizeHandler() {
    157             public void onResize(ResizeEvent event) {
    158                 if(spreadsheet.isVisible()) {
    159                     spreadsheet.fillWindow(true);
    160                 }
    161             }
    162         });
    163 
    164         setupDrilldownMap();
    165     }
    166 
    167     private void setHeaderSelectField(SpreadsheetHeaderSelect headerSelect,
    168                                       String defaultField) {
    169         headerSelect.setSelectedItem(headerFields.getFieldBySqlName(defaultField));
    170     }
    171 
    172     protected TestSet getWholeTableTestSet() {
    173         boolean isSingleTest = spreadsheetProcessor.getNumTotalTests() == 1;
    174         if (isSingleTest) {
    175             return getTestSet(spreadsheetProcessor.getLastCellInfo());
    176         }
    177 
    178         if (currentShowOnlyLatest) {
    179             List<Integer> testIndices = spreadsheet.getAllTestIndices();
    180             String filter = "test_idx IN (" + Utils.joinStrings(",", testIndices) + ")";
    181             ConditionTestSet tests = new ConditionTestSet();
    182             tests.addCondition(filter);
    183             return tests;
    184         }
    185 
    186         return new ConditionTestSet(getFullConditionArgs());
    187     }
    188 
    189     protected void setupDrilldownMap() {
    190         drilldownMap.put("platform", new String[] {"hostname", "test_name"});
    191         drilldownMap.put("hostname", new String[] {"job_tag", "status"});
    192 
    193         drilldownMap.put("kernel", new String[] {"test_name", "status"});
    194         drilldownMap.put("test_name", new String[] {"subdir", "job_name", "job_tag"});
    195 
    196         drilldownMap.put("status", new String[] {"reason", "job_tag"});
    197 
    198         drilldownMap.put("job_owner", new String[] {"job_name", "job_tag"});
    199 
    200         drilldownMap.put("test_finished_time", new String[] {"status", "job_tag"});
    201         drilldownMap.put("DATE(test_finished_time)",
    202                          new String[] {"test_finished_time", "job_tag"});
    203 
    204         drilldownMap.put("job_tag", new String[] {"subdir"});
    205     }
    206 
    207     protected void setSelectedHeader(HeaderSelect list, List<HeaderField> fields) {
    208         list.setSelectedItems(fields);
    209     }
    210 
    211     @Override
    212     public void refresh() {
    213         notYetQueried = false;
    214         actionsPanel.setVisible(true);
    215         spreadsheet.setVisible(false);
    216         selectionManager.clearSelection();
    217         spreadsheet.clear();
    218         setJobCompletionHtml("&nbsp");
    219 
    220         final JSONObject condition = getFullConditionArgs();
    221 
    222         contentSelect.addToCondition(condition);
    223 
    224         setLoading(true);
    225         if (currentShowOnlyLatest) {
    226             spreadsheetProcessor.setDataSource(latestDataSource);
    227         } else {
    228             spreadsheetProcessor.setDataSource(normalDataSource);
    229         }
    230         spreadsheetProcessor.setHeaders(rowSelect.getSelectedItems(),
    231                                         columnSelect.getSelectedItems(),
    232                                         getQueryParameters());
    233         spreadsheetProcessor.refresh(condition, new Command() {
    234             public void execute() {
    235                 condition.put("extra_info", null);
    236 
    237                 if (isJobFilteringCondition(condition)) {
    238                     showCompletionPercentage(condition);
    239                 } else {
    240                     setLoading(false);
    241                 }
    242             }
    243         });
    244     }
    245 
    246     private JSONObject getQueryParameters() {
    247         JSONObject parameters = new JSONObject();
    248         rowSelect.addQueryParameters(parameters);
    249         columnSelect.addQueryParameters(parameters);
    250         return parameters;
    251     }
    252 
    253     private JSONObject getFullConditionArgs() {
    254         JSONObject args = commonPanel.getConditionArgs();
    255         String condition = TkoUtils.getSqlCondition(args);
    256         if (!condition.equals("")) {
    257             condition = "(" + condition + ") AND ";
    258         }
    259         condition += "status != 'TEST_NA'";
    260         if (!currentShowIncomplete) {
    261             condition += " AND status != 'RUNNING'";
    262         }
    263         args.put("extra_where", new JSONString(condition));
    264         return args;
    265     }
    266 
    267     private void updateStateFromView() {
    268         rowSelect.updateStateFromView();
    269         columnSelect.updateStateFromView();
    270         currentShowIncomplete = showIncomplete.getValue();
    271         currentShowOnlyLatest = showOnlyLatest.getValue();
    272         commonPanel.updateStateFromView();
    273     }
    274 
    275     @Override
    276     public void doQuery() {
    277         List<HeaderField> rows = rowSelect.getSelectedItems();
    278         List<HeaderField> columns = columnSelect.getSelectedItems();
    279         if (rows.isEmpty() || columns.isEmpty()) {
    280             NotifyManager.getInstance().showError("You must select row and column fields");
    281             return;
    282         }
    283 
    284         updateStateFromView();
    285         refresh();
    286     }
    287 
    288     private void showCompletionPercentage(JSONObject condition) {
    289         rpcProxy.rpcCall("get_job_ids", condition, new JsonRpcCallback() {
    290             @Override
    291             public void onSuccess(JSONValue result) {
    292                 finishShowCompletionPercentage(result.isArray());
    293                 setLoading(false);
    294             }
    295 
    296             @Override
    297             public void onError(JSONObject errorObject) {
    298                 super.onError(errorObject);
    299                 setLoading(false);
    300             }
    301         });
    302     }
    303 
    304     private void finishShowCompletionPercentage(JSONArray jobIds) {
    305         final int jobCount = jobIds.size();
    306         if (jobCount == 0) {
    307             return;
    308         }
    309 
    310         JSONObject args = new JSONObject();
    311         args.put("job__id__in", jobIds);
    312         afeRpcProxy.rpcCall("get_hqe_percentage_complete", args, new JsonRpcCallback() {
    313             @Override
    314             public void onSuccess(JSONValue result) {
    315                 int percentage = (int) (result.isNumber().doubleValue() * 100);
    316                 StringBuilder message = new StringBuilder("Matching ");
    317                 if (jobCount == 1) {
    318                     message.append("job is ");
    319                 } else {
    320                     message.append("jobs are ");
    321                 }
    322                 message.append(percentage);
    323                 message.append("% complete");
    324                 setJobCompletionHtml(message.toString());
    325             }
    326         });
    327     }
    328 
    329     private void setJobCompletionHtml(String html) {
    330         jobCompletionPanel.clear();
    331         jobCompletionPanel.add(new HTML(html));
    332     }
    333 
    334     private boolean isJobFilteringCondition(JSONObject condition) {
    335         return TkoUtils.getSqlCondition(condition).indexOf("job_tag") != -1;
    336     }
    337 
    338     @Override
    339     public void onCellClicked(CellInfo cellInfo, boolean isRightClick) {
    340         Event event = Event.getCurrentEvent();
    341         TestSet testSet = getTestSet(cellInfo);
    342         DrilldownType drilldownType = TkoSpreadsheetUtils.getDrilldownType(cellInfo);
    343         if (isRightClick) {
    344             if (!selectionManager.isEmpty()) {
    345                 testSet = getTestSet(selectionManager.getSelectedCells());
    346                 drilldownType = DrilldownType.DRILLDOWN_BOTH;
    347             }
    348             ContextMenu menu = getContextMenu(testSet, drilldownType);
    349             menu.showAtWindow(event.getClientX(), event.getClientY());
    350             return;
    351         }
    352 
    353         if (isSelectEvent(event)) {
    354             selectionManager.toggleSelected(cellInfo);
    355             return;
    356         }
    357 
    358         HistoryToken historyToken;
    359         if (testSet.isSingleTest()) {
    360             historyToken = listener.getSelectTestHistoryToken(testSet.getTestIndex());
    361         } else {
    362             historyToken = getDrilldownHistoryToken(testSet,
    363                                                     getDefaultDrilldownRow(drilldownType),
    364                                                     getDefaultDrilldownColumn(drilldownType));
    365         }
    366         openHistoryToken(historyToken);
    367     }
    368 
    369     private TestSet getTestSet(CellInfo cellInfo) {
    370         return TkoSpreadsheetUtils.getTestSet(cellInfo, getFullConditionArgs(),
    371                 rowSelect.getSelectedItems(), columnSelect.getSelectedItems());
    372     }
    373 
    374     private TestSet getTestSet(List<CellInfo> cells) {
    375         CompositeTestSet tests = new CompositeTestSet();
    376         for (CellInfo cell : cells) {
    377             tests.add(getTestSet(cell));
    378         }
    379         return tests;
    380     }
    381 
    382     private HistoryToken getDrilldownHistoryToken(TestSet tests, String newRowField,
    383                                                   String newColumnField) {
    384         saveHistoryState();
    385         commonPanel.refineCondition(tests);
    386         rowSelect.setSelectedItem(headerFields.getFieldBySqlName(newRowField));
    387         columnSelect.setSelectedItem(headerFields.getFieldBySqlName(newColumnField));
    388         HistoryToken historyArguments = getHistoryArguments();
    389         restoreHistoryState();
    390         return historyArguments;
    391     }
    392 
    393     private void doDrilldown(TestSet tests, String newRowField, String newColumnField) {
    394         History.newItem(getDrilldownHistoryToken(tests, newRowField, newColumnField).toString());
    395     }
    396 
    397     private String getDefaultDrilldownRow(DrilldownType type) {
    398         return getDrilldownRows(type)[0];
    399     }
    400 
    401     private String getDefaultDrilldownColumn(DrilldownType type) {
    402         return getDrilldownColumns(type)[0];
    403     }
    404 
    405     private ContextMenu getContextMenu(final TestSet tests, DrilldownType drilldownType) {
    406         TestContextMenu menu = new TestContextMenu(tests, listener);
    407 
    408         if (!menu.addViewDetailsIfSingleTest()) {
    409             MenuBar drilldownMenu = menu.addSubMenuItem("Drill down");
    410             fillDrilldownMenu(tests, drilldownType, drilldownMenu);
    411         }
    412 
    413         menu.addItem("View in table", new Command() {
    414             public void execute() {
    415                 switchToTable(tests, false);
    416             }
    417         });
    418         menu.addItem("Triage failures", new Command() {
    419             public void execute() {
    420                 switchToTable(tests, true);
    421             }
    422         });
    423 
    424         menu.addLabelItems();
    425         return menu;
    426     }
    427 
    428     private void fillDrilldownMenu(final TestSet tests, DrilldownType drilldownType, MenuBar menu) {
    429         for (final String rowField : getDrilldownRows(drilldownType)) {
    430             for (final String columnField : getDrilldownColumns(drilldownType)) {
    431                 if (rowField.equals(columnField)) {
    432                     continue;
    433                 }
    434                 menu.addItem(rowField + " vs. " + columnField, new Command() {
    435                     public void execute() {
    436                         doDrilldown(tests, rowField, columnField);
    437                     }
    438                 });
    439             }
    440         }
    441     }
    442 
    443     private String[] getDrilldownFields(List<HeaderField> fields, DrilldownType type,
    444                                         DrilldownType otherType) {
    445         HeaderField lastField = fields.get(fields.size() - 1);
    446         String lastFieldName = lastField.getSqlName();
    447         if (type == otherType) {
    448             return new String[] {lastFieldName};
    449         } else {
    450             if (lastField instanceof MachineLabelField) {
    451                 // treat machine label fields like platform, for the purpose of default drilldown
    452                 lastFieldName = "platform";
    453             }
    454             if (drilldownMap.containsKey(lastFieldName)) {
    455                 return drilldownMap.get(lastFieldName);
    456             }
    457             return new String[] {DEFAULT_DRILLDOWN};
    458         }
    459     }
    460 
    461     private String[] getDrilldownRows(DrilldownType type) {
    462         return getDrilldownFields(rowSelect.getSelectedItems(), type,
    463                                   DrilldownType.DRILLDOWN_COLUMN);
    464     }
    465 
    466     private String[] getDrilldownColumns(DrilldownType type) {
    467         return getDrilldownFields(columnSelect.getSelectedItems(), type,
    468                                   DrilldownType.DRILLDOWN_ROW);
    469     }
    470 
    471     private void updateViewFromState() {
    472         rowSelect.updateViewFromState();
    473         columnSelect.updateViewFromState();
    474         showIncomplete.setValue(currentShowIncomplete);
    475         showOnlyLatest.setValue(currentShowOnlyLatest);
    476         commonPanel.updateViewFromState();
    477     }
    478 
    479     @Override
    480     public HistoryToken getHistoryArguments() {
    481         HistoryToken arguments = super.getHistoryArguments();
    482         if (!notYetQueried) {
    483             rowSelect.addHistoryArguments(arguments, HISTORY_ROW);
    484             columnSelect.addHistoryArguments(arguments, HISTORY_COLUMN);
    485             contentSelect.addHistoryArguments(arguments, HISTORY_CONTENT);
    486             arguments.put(HISTORY_SHOW_INCOMPLETE, Boolean.toString(currentShowIncomplete));
    487             arguments.put(HISTORY_ONLY_LATEST, Boolean.toString(showOnlyLatest.getValue()));
    488             commonPanel.addHistoryArguments(arguments);
    489         }
    490         return arguments;
    491     }
    492 
    493     @Override
    494     public void handleHistoryArguments(Map<String, String> arguments) {
    495         super.handleHistoryArguments(arguments);
    496         commonPanel.handleHistoryArguments(arguments);
    497         rowSelect.handleHistoryArguments(arguments, HISTORY_ROW);
    498         columnSelect.handleHistoryArguments(arguments, HISTORY_COLUMN);
    499         contentSelect.handleHistoryArguments(arguments, HISTORY_CONTENT);
    500 
    501         currentShowIncomplete = Boolean.valueOf(arguments.get(HISTORY_SHOW_INCOMPLETE));
    502         currentShowOnlyLatest = Boolean.valueOf(arguments.get(HISTORY_ONLY_LATEST));
    503         updateViewFromState();
    504     }
    505 
    506     @Override
    507     protected void fillDefaultHistoryValues(Map<String, String> arguments) {
    508         Utils.setDefaultValue(arguments, HISTORY_ROW, DEFAULT_ROW);
    509         Utils.setDefaultValue(arguments, HISTORY_COLUMN, DEFAULT_COLUMN);
    510         Utils.setDefaultValue(arguments,
    511                               HISTORY_ROW + SpreadsheetHeaderSelect.HISTORY_FIXED_VALUES, "");
    512         Utils.setDefaultValue(arguments,
    513                               HISTORY_COLUMN + SpreadsheetHeaderSelect.HISTORY_FIXED_VALUES, "");
    514         Utils.setDefaultValue(arguments, HISTORY_SHOW_INCOMPLETE, Boolean.toString(false));
    515         Utils.setDefaultValue(arguments, HISTORY_ONLY_LATEST, Boolean.toString(false));
    516     }
    517 
    518     private void switchToTable(final TestSet tests, boolean isTriageView) {
    519         commonPanel.refineCondition(tests);
    520         TableViewConfig config;
    521         if (isTriageView) {
    522             config = TableViewConfig.TRIAGE;
    523             refineConditionForTriage();
    524         } else {
    525             config = TableViewConfig.DEFAULT;
    526         }
    527         listener.onSwitchToTable(config);
    528     }
    529 
    530     private void refineConditionForTriage() {
    531         commonPanel.refineCondition("status != 'GOOD'");
    532     }
    533 
    534     public ContextMenu getActionMenu() {
    535         TestSet tests;
    536         if (selectionManager.isEmpty()) {
    537             tests = getWholeTableTestSet();
    538         } else {
    539             tests = getTestSet(selectionManager.getSelectedCells());
    540         }
    541         return getContextMenu(tests, DrilldownType.DRILLDOWN_BOTH);
    542     }
    543 
    544     public void onExportCsv() {
    545         JSONObject params = new JSONObject();
    546         contentSelect.addToCondition(params);
    547         TkoUtils.doCsvRequest(spreadsheetProcessor.getDataSource(),
    548                               spreadsheetProcessor.getCurrentQuery(), params);
    549     }
    550 
    551     public void onSelectAll(boolean ignored) {
    552         selectionManager.selectAll();
    553     }
    554 
    555     public void onSelectNone() {
    556         selectionManager.clearSelection();
    557     }
    558 
    559     @Override
    560     protected boolean hasFirstQueryOccurred() {
    561         return !notYetQueried;
    562     }
    563 
    564     private void setLoading(boolean loading) {
    565         queryButton.setEnabled(!loading);
    566         NotifyManager.getInstance().setLoading(loading);
    567     }
    568 
    569     @Override
    570     public void onSetControlsVisible(boolean visible) {
    571         TkoUtils.setElementVisible("ss_all_controls", visible);
    572         if (isTabVisible()) {
    573             spreadsheet.fillWindow(true);
    574         }
    575     }
    576 
    577     @Override
    578     public void onFieldsChanged() {
    579         rowSelect.refreshFields();
    580         columnSelect.refreshFields();
    581         contentSelect.refreshFields();
    582     }
    583 }
    584