    001    // Copyright 2007, 2008, 2009, 2011 The Apache Software Foundation
    002    //
    003    // Licensed under the Apache License, Version 2.0 (the "License");
    004    // you may not use this file except in compliance with the License.
    005    // You may obtain a copy of the License at
    006    //
    007    //     http://www.apache.org/licenses/LICENSE-2.0
    008    //
    009    // Unless required by applicable law or agreed to in writing, software
    010    // distributed under the License is distributed on an "AS IS" BASIS,
    011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012    // See the License for the specific language governing permissions and
    013    // limitations under the License.
    014
    015    package org.apache.tapestry5.corelib.components;
    016
    017    import org.apache.tapestry5.*;
    018    import org.apache.tapestry5.annotations.*;
    019    import org.apache.tapestry5.beaneditor.PropertyModel;
    020    import org.apache.tapestry5.grid.ColumnSort;
    021    import org.apache.tapestry5.grid.GridConstants;
    022    import org.apache.tapestry5.grid.GridModel;
    023    import org.apache.tapestry5.grid.GridSortModel;
    024    import org.apache.tapestry5.internal.InternalConstants;
    025    import org.apache.tapestry5.internal.TapestryInternalUtils;
    026    import org.apache.tapestry5.ioc.Messages;
    027    import org.apache.tapestry5.ioc.annotations.Inject;
    028    import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    029
    030    import java.util.List;
    031
    032    /**
    033     * Renders out the column headers for the grid, including links (where appropriate) to control column sorting.
    034     *
    035     * @tapestrydoc
    036     */
    037    @SupportsInformalParameters
    038    @Events(InternalConstants.GRID_INPLACE_UPDATE + " (internal event)")
    039    public class GridColumns
    040    {
    041        /**
    042         * The object that provides access to bean and data models, which is typically the enclosing Grid component.
    043         */
    044        @Parameter(value = "componentResources.container")
    045        private GridModel gridModel;
    046
    047        /**
    048         * If true, then the CSS class on each &lt;TH&gt; element will be omitted, which can reduce the amount of output
    049         * from the component overall by a considerable amount. Leave this as false, the default, when you are leveraging
    050         * the CSS to customize the look and feel of particular columns.
    051         */
    052        @Parameter
    053        private boolean lean;
    054
    055        /**
    056         * Where to look for informal parameter Blocks used to override column headers.  The default is to look for such
    057         * overrides in the GridColumns component itself, but this is usually overridden.
    058         */
    059        @Parameter("this")
    060        private PropertyOverrides overrides;
    061
    062        /**
    063         * If not null, then each link is output as a link to update the specified zone.
    064         */
    065        @Parameter
    066        private String zone;
    067
    068        @SuppressWarnings("unused")
    069        @Component(
    070                parameters = { "event=sort", "disabled=sortDisabled", "context=columnContext", "class=sortLinkClass",
    071                        "zone=inherit:zone" })
    072        private EventLink sort, sort2;
    073
    074        @Inject
    075        @Path("${" + ComponentParameterConstants.GRIDCOLUMNS_ASCENDING_ASSET + "}")
    076        private Asset ascendingAsset;
    077
    078        @Inject
    079        @Path("${" + ComponentParameterConstants.GRIDCOLUMNS_DESCENDING_ASSET + "}")
    080        private Asset descendingAsset;
    081
    082        @Inject
    083        @Path("${" + ComponentParameterConstants.GRIDCOLUMNS_SORTABLE_ASSET + "}")
    084        private Asset sortableAsset;
    085
    086        @Inject
    087        private Messages messages;
    088
    089        @Inject
    090        private Block standardHeader;
    091
    092        /**
    093         * Optional output parameter that stores the current column index.
    094         */
    095        @Parameter
    096        @Property
    097        private int index;
    098
    099        /**
    100         * Caches the index of the last column.
    101         */
    102        private int lastColumnIndex;
    103
    104        @Property(write = false)
    105        private PropertyModel columnModel;
    106
    107        @Inject
    108        private ComponentResources resources;
    109
    110        void setupRender()
    111        {
    112            lastColumnIndex = gridModel.getDataModel().getPropertyNames().size() - 1;
    113        }
    114
    115        public boolean isSortDisabled()
    116        {
    117            return !columnModel.isSortable();
    118        }
    119
    120        public String getSortLinkClass()
    121        {
    122            switch (getSortForColumn())
    123            {
    124                case ASCENDING:
    125                    return GridConstants.SORT_ASCENDING_CLASS;
    126
    127                case DESCENDING:
    128                    return GridConstants.SORT_DESCENDING_CLASS;
    129
    130                default:
    131                    return null;
    132            }
    133        }
    134
    135        private ColumnSort getSortForColumn()
    136        {
    137            GridSortModel sortModel = gridModel.getSortModel();
    138
    139            String columnId = columnModel.getId();
    140
    141            return sortModel.getColumnSort(columnId);
    142        }
    143
    144        public String getHeaderClass()
    145        {
    146            List<String> classes = CollectionFactory.newList();
    147
    148            if (!lean) classes.add(columnModel.getId());
    149
    150            String sort = getSortLinkClass();
    151
    152            if (sort != null) classes.add(sort);
    153
    154            if (index == 0) classes.add(GridConstants.FIRST_CLASS);
    155
    156            if (index == lastColumnIndex) classes.add(GridConstants.LAST_CLASS);
    157
    158            return TapestryInternalUtils.toClassAttributeValue(classes);
    159        }
    160
    161        public boolean isActiveSortColumn()
    162        {
    163            return getSortForColumn() != ColumnSort.UNSORTED;
    164        }
    165
    166        /**
    167         * Normal, non-Ajax event handler.
    168         */
    169
    170        void onSort(String columnId)
    171        {
    172            gridModel.getSortModel().updateSort(columnId);
    173        }
    174
    175        /**
    176         * Ajax event handler, which carries the zone id.
    177         */
    178        boolean onSort(String columnId, String zone)
    179        {
    180            onSort(columnId);
    181
    182            resources.triggerEvent(InternalConstants.GRID_INPLACE_UPDATE, new Object[] { zone }, null);
    183
    184            // Event is handled, don't trigger further event handler methods.
    185
    186            return true;
    187        }
    188
    189        public Asset getIcon()
    190        {
    191            switch (getSortForColumn())
    192            {
    193                case ASCENDING:
    194                    return ascendingAsset;
    195
    196                case DESCENDING:
    197                    return descendingAsset;
    198
    199                default:
    200                    return sortableAsset;
    201            }
    202        }
    203
    204        public Object getColumnContext()
    205        {
    206            if (zone == null) return columnModel.getId();
    207
    208            return new Object[] { columnModel.getId(), zone };
    209        }
    210
    211        public String getIconLabel()
    212        {
    213            switch (getSortForColumn())
    214            {
    215                case ASCENDING:
    216                    return messages.get("ascending");
    217                case DESCENDING:
    218                    return messages.get("descending");
    219                default:
    220                    return messages.get("sortable");
    221            }
    222        }
    223
    224        public List<String> getColumnNames()
    225        {
    226            return gridModel.getDataModel().getPropertyNames();
    227        }
    228
    229
    230        public void setColumnName(String columnName)
    231        {
    232            columnModel = gridModel.getDataModel().get(columnName);
    233        }
    234
    235
    236        public Block getBlockForColumn()
    237        {
    238            Block override = overrides.getOverrideBlock(columnModel.getId() + "Header");
    239
    240            if (override != null) return override;
    241
    242            return standardHeader;
    243        }
    244    }