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
015package org.apache.tapestry5.corelib.components;
016
017import org.apache.tapestry5.*;
018import org.apache.tapestry5.annotations.*;
019import org.apache.tapestry5.beaneditor.PropertyModel;
020import org.apache.tapestry5.grid.ColumnSort;
021import org.apache.tapestry5.grid.GridConstants;
022import org.apache.tapestry5.grid.GridModel;
023import org.apache.tapestry5.grid.GridSortModel;
024import org.apache.tapestry5.internal.InternalConstants;
025import org.apache.tapestry5.internal.TapestryInternalUtils;
026import org.apache.tapestry5.ioc.Messages;
027import org.apache.tapestry5.ioc.annotations.Inject;
028import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
029
030import 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)")
039public 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 <TH> 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}