/*! * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2002-2013 Pentaho Corporation.. All rights reserved. */ package org.pentaho.reporting.engine.classic.wizard.ui.xul.steps; import org.pentaho.reporting.engine.classic.core.MetaAttributeNames; import org.pentaho.reporting.engine.classic.core.wizard.DataAttributes; import org.pentaho.reporting.engine.classic.core.wizard.DataSchema; import org.pentaho.reporting.engine.classic.core.wizard.DataSchemaModel; import org.pentaho.reporting.engine.classic.core.wizard.DefaultDataAttributeContext; import org.pentaho.reporting.engine.classic.wizard.model.DefaultDetailFieldDefinition; import org.pentaho.reporting.engine.classic.wizard.model.DefaultGroupDefinition; import org.pentaho.reporting.engine.classic.wizard.model.DetailFieldDefinition; import org.pentaho.reporting.engine.classic.wizard.model.GroupDefinition; import org.pentaho.reporting.engine.classic.wizard.ui.xul.components.AbstractWizardStep; import org.pentaho.reporting.engine.classic.wizard.ui.xul.util.FieldWrapper; import org.pentaho.reporting.engine.classic.wizard.ui.xul.util.SourceFieldDefinition; import org.pentaho.reporting.engine.classic.wizard.ui.xul.util.XulGroupDefinition; import org.pentaho.reporting.libraries.base.util.DebugLog; import org.pentaho.ui.xul.XulDomContainer; import org.pentaho.ui.xul.XulException; import org.pentaho.ui.xul.binding.Binding; import org.pentaho.ui.xul.binding.Binding.Type; import org.pentaho.ui.xul.binding.BindingConvertor; import org.pentaho.ui.xul.containers.XulListbox; import org.pentaho.ui.xul.impl.AbstractXulEventHandler; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class LayoutStep extends AbstractWizardStep { protected class SelectFieldsAndGroupsEventHandler extends AbstractXulEventHandler { protected SelectFieldsAndGroupsEventHandler() { } @Override public String getName() { return "layout_controller"; //$NON-NLS-1$ } private int[] getNewSelections( final int[] oldSelectedRows, final DIRECTION direction ) { final int offset; offset = direction == DIRECTION.DOWN ? 1 : -1; // update the selection to move with the items final int[] newSelectedRows = new int[ oldSelectedRows.length ]; for ( int i = 0; i < oldSelectedRows.length; i++ ) { newSelectedRows[ i ] = oldSelectedRows[ i ] + offset; } return newSelectedRows; } public void doMoveToGroups() { final XulListbox availableList = (XulListbox) getDocument().getElementById( AVAILABLE_COLUMNS_LIST_ID ); final int[] selectedIndices = availableList.getSelectedIndices(); final List<FieldWrapper> groups = getGroupFields(); final DataSchema schema = getEditorModel().getDataSchema().getDataSchema(); for ( final int i : selectedIndices ) { final SourceFieldDefinition group = getSelectableFields().get( i ); final GroupDefinition xulGroup = new DefaultGroupDefinition(); xulGroup.setField( group.getFieldName() ); FieldWrapper wrapper = new FieldWrapper( xulGroup, schema ); groups.add( wrapper ); final DefaultGroupDefinition definition = (DefaultGroupDefinition) wrapper.getFieldDefinition(); new XulGroupDefinition( definition, schema ); } setGroupFields( groups ); } public void doMoveToDetails() { final XulListbox availableList = (XulListbox) getDocument().getElementById( AVAILABLE_COLUMNS_LIST_ID ); final int[] selectedIndices = availableList.getSelectedIndices(); final List<FieldWrapper> details = getDetailFields(); final DataSchema schema = getEditorModel().getDataSchema().getDataSchema(); for ( final int i : selectedIndices ) { final SourceFieldDefinition field = getSelectableFields().get( i ); final DetailFieldDefinition xulField = new DefaultDetailFieldDefinition(); xulField.setField( field.getFieldName() ); details.add( new FieldWrapper( xulField, schema ) ); } setDetailFields( details ); } public void doMoveUpSelectedGroupItems() { final List<FieldWrapper> groups = getGroupFields(); final XulListbox groupList = (XulListbox) getDocument().getElementById( GROUP_FIELDS_LIST_ID ); final int[] selectedIndices = groupList.getSelectedIndices(); Arrays.sort( selectedIndices ); for ( final int selectedRow : selectedIndices ) { final FieldWrapper group = groups.remove( selectedRow ); groups.add( selectedRow - 1, group ); } setGroupFields( groups ); // update the selection to move with the items groupList.setSelectedIndices( getNewSelections( selectedIndices, DIRECTION.UP ) ); } public void doMoveDownSelectedGroupItems() { final List<FieldWrapper> groups = getGroupFields(); final XulListbox groupList = (XulListbox) getDocument().getElementById( GROUP_FIELDS_LIST_ID ); final int[] selectedIndices = groupList.getSelectedIndices(); Arrays.sort( selectedIndices ); reverseArray( selectedIndices ); for ( final int selectedRow : selectedIndices ) { final FieldWrapper group = groups.remove( selectedRow ); groups.add( selectedRow + 1, group ); } setGroupFields( groups ); // update the selection to move with the items groupList.setSelectedIndices( getNewSelections( selectedIndices, DIRECTION.DOWN ) ); } public void doRemoveSelectedGroupItems() { final List<FieldWrapper> groups = getGroupFields(); final XulListbox groupList = (XulListbox) getDocument().getElementById( GROUP_FIELDS_LIST_ID ); for ( int i = groupList.getSelectedIndices().length - 1; i >= 0; i-- ) { // Count from the end back groups.remove( groupList.getSelectedIndices()[ i ] ); } setGroupFields( groups ); groupList.setSelectedIndices( EMPTY_SELECTION ); // Clear any selections } public void doMoveUpSelectedDetailItems() { final List<FieldWrapper> details = getDetailFields(); final XulListbox detailList = (XulListbox) getDocument().getElementById( DETAIL_FIELDS_LIST_ID ); final int[] selectedIndices = detailList.getSelectedIndices(); Arrays.sort( selectedIndices ); for ( final int selectedRow : selectedIndices ) { final FieldWrapper detail = details.remove( selectedRow ); details.add( selectedRow - 1, detail ); } setDetailFields( details ); // update the selection to move with the items detailList.setSelectedIndices( getNewSelections( selectedIndices, DIRECTION.UP ) ); } public void doMoveDownSelectedDetailItems() { final List<FieldWrapper> details = getDetailFields(); final XulListbox detailList = (XulListbox) getDocument().getElementById( DETAIL_FIELDS_LIST_ID ); final int[] selectedIndices = detailList.getSelectedIndices(); Arrays.sort( selectedIndices ); reverseArray( selectedIndices ); for ( final int selectedRow : selectedIndices ) { final FieldWrapper detail = details.remove( selectedRow ); details.add( selectedRow + 1, detail ); } setDetailFields( details ); // update the selection to move with the items detailList.setSelectedIndices( getNewSelections( selectedIndices, DIRECTION.DOWN ) ); } public void doRemoveSelectedDetailItems() { final List<FieldWrapper> details = getDetailFields(); final XulListbox detailList = (XulListbox) getDocument().getElementById( DETAIL_FIELDS_LIST_ID ); for ( int i = detailList.getSelectedIndices().length - 1; i >= 0; i-- ) { // Count from the end back details.remove( detailList.getSelectedIndices()[ i ] ); } setDetailFields( details ); detailList.setSelectedIndices( EMPTY_SELECTION ); // Clear any selections } private void reverseArray( final int[] target ) { for ( int i = 0; i < target.length / 2; i++ ) { final int temp = target[ i ]; target[ i ] = target[ target.length - i - 1 ]; target[ target.length - i - 1 ] = temp; } } } /** * @author wseyler */ private static class ListSelectionToBooleanConverter extends BindingConvertor<int[], Boolean> { private ListSelectionToBooleanConverter() { } /* (non-Javadoc) * @see org.pentaho.ui.xul.binding.BindingConvertor#sourceToTarget(java.lang.Object) */ @Override public Boolean sourceToTarget( final int[] value ) { return value.length > 0; } /* (non-Javadoc) * @see org.pentaho.ui.xul.binding.BindingConvertor#targetToSource(java.lang.Object) */ @Override public int[] targetToSource( final Boolean value ) { return null; } } /** * @author wseyler */ private static class MoveUpBindingConverter extends BindingConvertor<int[], Boolean> { private MoveUpBindingConverter() { } /* (non-Javadoc) * @see org.pentaho.ui.xul.binding.BindingConvertor#sourceToTarget(java.lang.Object) */ @Override public Boolean sourceToTarget( final int[] value ) { Arrays.sort( value ); return value.length > 0 && value[ 0 ] > 0; } /* (non-Javadoc) * @see org.pentaho.ui.xul.binding.BindingConvertor#targetToSource(java.lang.Object) */ @Override public int[] targetToSource( final Boolean value ) { return null; } } /** * @author wseyler */ private static class MoveDownBindingConverter extends BindingConvertor<int[], Boolean> { private XulListbox list; private MoveDownBindingConverter( final XulListbox list ) { this.list = list; } /* (non-Javadoc) * @see org.pentaho.ui.xul.binding.BindingConvertor#sourceToTarget(java.lang.Object) */ @Override public Boolean sourceToTarget( final int[] value ) { Arrays.sort( value ); return value.length > 0 && value[ value.length - 1 ] < list.getElements().size() - 1; } /* (non-Javadoc) * @see org.pentaho.ui.xul.binding.BindingConvertor#targetToSource(java.lang.Object) */ @Override public int[] targetToSource( final Boolean value ) { return null; } } private enum DIRECTION { UP, DOWN } private static final String LAYOUT_STEP_OVERLAY = "org/pentaho/reporting/engine/classic/wizard/ui/xul/res/layout_step_Overlay.xul"; //$NON-NLS-1$ private static final String ELEMENTS_PROPERTY_NAME = "elements"; //$NON-NLS-1$ private static final String NOT_DISABLED_PROPERTY_NAME = "!disabled"; //$NON-NLS-1$ private static final String SELECTABLE_FIELDS_PROPERTY_NAME = "selectableFields"; //$NON-NLS-1$ private static final String GROUP_FIELDS_PROPERTY_NAME = "groupFields"; //$NON-NLS-1$ private static final String DETAIL_FIELDS_PROPERTY_NAME = "detailFields"; //$NON-NLS-1$ private static final String SELECTED_INDICES_PROPERTY_NAME = "selectedIndices"; //$NON-NLS-1$ protected static final String AVAILABLE_COLUMNS_LIST_ID = "available_columns_list"; //$NON-NLS-1$ protected static final String GROUP_FIELDS_LIST_ID = "group_fields_list"; //$NON-NLS-1$ protected static final String DETAIL_FIELDS_LIST_ID = "detail_fields_list"; //$NON-NLS-1$ private static final String MOVE_TO_GROUPS_BTN_ID = "move_to_groups_btn"; //$NON-NLS-1$ private static final String MOVE_TO_DETAILS_BTN_ID = "move_to_details_btn"; //$NON-NLS-1$ private static final String MOVE_GROUP_UP_BTN_ID = "move_group_up_btn"; //$NON-NLS-1$ private static final String MOVE_GROUP_DOWN_BTN_ID = "move_group_down_btn"; //$NON-NLS-1$ private static final String REMOVE_GROUP_ITEM_BTN_ID = "remove_group_item_btn"; //$NON-NLS-1$ private static final String MOVE_DETAIL_UP_BTN_ID = "move_detail_up_btn"; //$NON-NLS-1$ private static final String MOVE_DETAIL_DOWN_BTN_ID = "move_detail_down_btn"; //$NON-NLS-1$ private static final String REMOVE_DETAIL_ITEM_BTN_ID = "remove_detail_item_btn"; //$NON-NLS-1$ private static final String PREVIEW_BUTTON_ID = "layout_preview_btn"; //$NON-NLS-1$ private Binding previewBinding; private Binding selectableFieldsBinding; private Binding groupsBinding; private Binding detailsBinding; private ArrayList<SourceFieldDefinition> selectableFields; protected static final int[] EMPTY_SELECTION = new int[ 0 ]; public LayoutStep() { selectableFields = new ArrayList<SourceFieldDefinition>(); } /* (non-Javadoc) * @see org.pentaho.reporting.engine.classic.wizard.ui.xul.components.WizardStep#initialize() */ public void setBindings() { // Create the binding converters final ListSelectionToBooleanConverter listToBooleanConverter = new ListSelectionToBooleanConverter(); final MoveUpBindingConverter moveUpBindingConverter = new MoveUpBindingConverter(); // Bindings for the lists getBindingFactory().setBindingType( Type.BI_DIRECTIONAL ); selectableFieldsBinding = getBindingFactory() .createBinding( this, SELECTABLE_FIELDS_PROPERTY_NAME, AVAILABLE_COLUMNS_LIST_ID, ELEMENTS_PROPERTY_NAME ); groupsBinding = getBindingFactory() .createBinding( this, GROUP_FIELDS_PROPERTY_NAME, GROUP_FIELDS_LIST_ID, ELEMENTS_PROPERTY_NAME ); detailsBinding = getBindingFactory() .createBinding( this, DETAIL_FIELDS_PROPERTY_NAME, DETAIL_FIELDS_LIST_ID, ELEMENTS_PROPERTY_NAME ); // Bindings for the move to fields and groups buttons getBindingFactory().setBindingType( Type.ONE_WAY ); getBindingFactory().createBinding( AVAILABLE_COLUMNS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, MOVE_TO_GROUPS_BTN_ID, NOT_DISABLED_PROPERTY_NAME, listToBooleanConverter ); getBindingFactory() .createBinding( AVAILABLE_COLUMNS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, MOVE_TO_DETAILS_BTN_ID, NOT_DISABLED_PROPERTY_NAME, listToBooleanConverter ); // Bindings for the buttons on the groups panel getBindingFactory().setBindingType( Type.ONE_WAY ); getBindingFactory().createBinding( GROUP_FIELDS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, MOVE_GROUP_UP_BTN_ID, NOT_DISABLED_PROPERTY_NAME, moveUpBindingConverter ); getBindingFactory().createBinding( GROUP_FIELDS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, MOVE_GROUP_DOWN_BTN_ID, NOT_DISABLED_PROPERTY_NAME, new MoveDownBindingConverter( (XulListbox) getDocument().getElementById( GROUP_FIELDS_LIST_ID ) ) ); getBindingFactory().createBinding( GROUP_FIELDS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, REMOVE_GROUP_ITEM_BTN_ID, NOT_DISABLED_PROPERTY_NAME, listToBooleanConverter ); // Bindings for the buttons on the detail panel getBindingFactory().setBindingType( Type.ONE_WAY ); getBindingFactory().createBinding( DETAIL_FIELDS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, MOVE_DETAIL_UP_BTN_ID, NOT_DISABLED_PROPERTY_NAME, moveUpBindingConverter ); getBindingFactory().createBinding( DETAIL_FIELDS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, MOVE_DETAIL_DOWN_BTN_ID, NOT_DISABLED_PROPERTY_NAME, new MoveDownBindingConverter( (XulListbox) getDocument().getElementById( DETAIL_FIELDS_LIST_ID ) ) ); getBindingFactory().createBinding( DETAIL_FIELDS_LIST_ID, SELECTED_INDICES_PROPERTY_NAME, REMOVE_DETAIL_ITEM_BTN_ID, NOT_DISABLED_PROPERTY_NAME, listToBooleanConverter ); // Binding for the preview button getBindingFactory().setBindingType( Type.ONE_WAY ); previewBinding = getBindingFactory() .createBinding( this, PREVIEWABLE_PROPERTY_NAME, PREVIEW_BUTTON_ID, NOT_DISABLED_PROPERTY_NAME ); } public void stepActivating() { super.stepActivating(); try { previewBinding.fireSourceChanged(); selectableFieldsBinding.fireSourceChanged(); groupsBinding.fireSourceChanged(); detailsBinding.fireSourceChanged(); } catch ( Exception e ) { if ( getDesignTimeContext() != null ) { getDesignTimeContext().error( e ); } else { DebugLog.log( e ); } } if ( getEditorModel().isRelationalModel() ) { populateSourceList(); setDetailFields( getDetailFields() ); setGroupFields( getGroupFields() ); } else { setValid( false ); } } private void populateSourceList() { final DataSchemaModel dataSchemaModel = getEditorModel().getDataSchema(); final DataSchema dataSchema = dataSchemaModel.getDataSchema(); final String[] names = dataSchema.getNames(); Arrays.sort( names ); final ArrayList<SourceFieldDefinition> fields = new ArrayList<SourceFieldDefinition>(); for ( int i = 0; i < names.length; i++ ) { final String fieldName = names[ i ]; if ( fieldName == null ) { continue; } if ( isFilteredField( dataSchema, fieldName ) ) { continue; } final SourceFieldDefinition fieldDefinition = new SourceFieldDefinition( fieldName, dataSchema ); fields.add( fieldDefinition ); } this.setSelectableFields( fields ); } private boolean isFilteredField( final DataSchema dataSchema, final String fieldName ) { final DefaultDataAttributeContext dac = new DefaultDataAttributeContext(); final DataAttributes attributes = dataSchema.getAttributes( fieldName ); final Object source = attributes.getMetaAttribute ( MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.SOURCE, String.class, dac ); if ( MetaAttributeNames.Core.SOURCE_VALUE_ENVIRONMENT.equals( source ) ) { return true; } if ( MetaAttributeNames.Core.SOURCE_VALUE_PARAMETER.equals( source ) ) { return true; } final Object indexColumn = attributes.getMetaAttribute ( MetaAttributeNames.Core.NAMESPACE, MetaAttributeNames.Core.INDEXED_COLUMN, Boolean.class, dac ); if ( Boolean.TRUE.equals( indexColumn ) ) { return true; } return false; } public List<FieldWrapper> getDetailFields() { final ArrayList<FieldWrapper> fields = new ArrayList<FieldWrapper>(); final DataSchema schema = getEditorModel().getDataSchema().getDataSchema(); for ( final DetailFieldDefinition field : getEditorModel().getReportSpec().getDetailFieldDefinitions() ) { fields.add( new FieldWrapper( field, schema ) ); } return fields; } public void setDetailFields( final List<FieldWrapper> detailFields ) { final List<FieldWrapper> oldFields = getDetailFields(); final DetailFieldDefinition[] fields = new DetailFieldDefinition[ detailFields.size() ]; for ( int i = 0; i < detailFields.size(); i++ ) { final FieldWrapper fieldWrapper = detailFields.get( i ); fields[ i ] = (DetailFieldDefinition) fieldWrapper.getFieldDefinition(); } getEditorModel().getReportSpec().setDetailFieldDefinitions( fields ); // If we change the detail fields check and see if the list is populated // if it is we can enable preview // this should be refactored to a binding this.setPreviewable( !detailFields.isEmpty() ); this.setFinishable( !detailFields.isEmpty() ); this.setValid( !detailFields.isEmpty() ); this.firePropertyChange( DETAIL_FIELDS_PROPERTY_NAME, oldFields, detailFields ); } public List<FieldWrapper> getGroupFields() { final ArrayList<FieldWrapper> groups = new ArrayList<FieldWrapper>(); final DataSchema schema = getEditorModel().getDataSchema().getDataSchema(); for ( final GroupDefinition group : getEditorModel().getReportSpec().getGroupDefinitions() ) { groups.add( new FieldWrapper( group, schema ) ); } return groups; } public void setGroupFields( final List<FieldWrapper> groupFields ) { final List<FieldWrapper> oldGroups = getGroupFields(); final GroupDefinition[] fields = new GroupDefinition[ groupFields.size() ]; for ( int i = 0; i < groupFields.size(); i++ ) { final FieldWrapper fieldWrapper = groupFields.get( i ); fields[ i ] = (GroupDefinition) fieldWrapper.getFieldDefinition(); } getEditorModel().getReportSpec().setGroupDefinitions( fields ); this.firePropertyChange( GROUP_FIELDS_PROPERTY_NAME, oldGroups, groupFields ); } /* (non-Javadoc) * @see org.pentaho.reporting.engine.classic.wizard.ui.xul.components.WizardStep#createPresentationComponent(org * .pentaho.ui.xul.XulDomContainer) */ public void createPresentationComponent( final XulDomContainer mainWizardContainer ) throws XulException { super.createPresentationComponent( mainWizardContainer ); mainWizardContainer.loadOverlay( LAYOUT_STEP_OVERLAY ); mainWizardContainer.addEventHandler( new SelectFieldsAndGroupsEventHandler() ); } public ArrayList<SourceFieldDefinition> getSelectableFields() { return selectableFields; } public void setSelectableFields( final ArrayList<SourceFieldDefinition> selectableFields ) { if ( selectableFields == null ) { throw new NullPointerException(); } final ArrayList<SourceFieldDefinition> oldSelectableFields = this.selectableFields; //noinspection AssignmentToCollectionOrArrayFieldFromParameter this.selectableFields = selectableFields; this.firePropertyChange( SELECTABLE_FIELDS_PROPERTY_NAME, oldSelectableFields, this.selectableFields ); } /* (non-Javadoc) * @see org.pentaho.reporting.engine.classic.wizard.ui.xul.components.WizardStep#getStepName() */ public String getStepName() { return messages.getString( "LAYOUT_STEP.Step_Name" ); //$NON-NLS-1$ } }