001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package com.sun.rave.web.ui.component;
042:
043: import com.sun.data.provider.SortCriteria;
044: import com.sun.data.provider.RowKey;
045: import com.sun.rave.web.ui.component.Hyperlink;
046: import com.sun.rave.web.ui.component.IconHyperlink;
047: import com.sun.rave.web.ui.event.TableSortActionListener;
048: import com.sun.rave.web.ui.theme.Theme;
049: import com.sun.rave.web.ui.theme.ThemeImages;
050: import com.sun.rave.web.ui.theme.ThemeStyles;
051: import com.sun.rave.web.ui.util.LogUtil;
052: import com.sun.rave.web.ui.util.RenderingUtilities;
053: import com.sun.rave.web.ui.util.ThemeUtilities;
054:
055: import java.io.IOException;
056:
057: import javax.faces.component.NamingContainer;
058: import javax.faces.component.UIComponent;
059: import javax.faces.context.FacesContext;
060:
061: /**
062: * Component that represents various table headers, including sortable,
063: * selection, and group headers.
064: * <p>
065: * Note: Column headers and footers are rendered by TableRowGroupRenderer. Table
066: * column footers are rendered by TableRenderer.
067: * </p><p>
068: * Note: To see the messages logged by this class, set the following global
069: * defaults in your JDK's "jre/lib/logging.properties" file.
070: * </p><p><pre>
071: * java.util.logging.ConsoleHandler.level = FINE
072: * com.sun.rave.web.ui.component.TableHeader.level = FINE
073: * </pre></p>
074: */
075: public class TableHeader extends TableHeaderBase implements
076: NamingContainer {
077: /** The component id for the add sort button. */
078: public static final String ADD_SORT_BUTTON_ID = "_addSortButton"; //NOI18N
079:
080: /** The facet name for the add sort button. */
081: public static final String ADD_SORT_BUTTON_FACET = "addSortButton"; //NOI18N
082:
083: /** The component id for the collapsed hidden field. */
084: public static final String COLLAPSED_HIDDEN_FIELD_ID = "_collapsedHiddenField"; //NOI18N
085:
086: /** The facet name for the collapsed hidden field. */
087: public static final String COLLAPSED_HIDDEN_FIELD_FACET = "collapsedHiddenField"; //NOI18N
088:
089: /** The component id for the table row group toggle button. */
090: public static final String GROUP_PANEL_TOGGLE_BUTTON_ID = "_groupPanelToggleButton"; //NOI18N
091:
092: /** The facet name for the table row group toggle button. */
093: public static final String GROUP_PANEL_TOGGLE_BUTTON_FACET = "groupPanelToggleButton"; //NOI18N
094:
095: /** The component id for the primary sort button. */
096: public static final String PRIMARY_SORT_BUTTON_ID = "_primarySortButton"; //NOI18N
097:
098: /** The facet name for the primary sort button. */
099: public static final String PRIMARY_SORT_BUTTON_FACET = "primarySortButton"; //NOI18N
100:
101: /** The component id for the primary sort link. */
102: public static final String PRIMARY_SORT_LINK_ID = "_primarySortLink"; //NOI18N
103:
104: /** The facet name for the primary sort link. */
105: public static final String PRIMARY_SORT_LINK_FACET = "primarySortLink"; //NOI18N
106:
107: /** The component id for the select multiple toggle button. */
108: public static final String SELECT_MULTIPLE_TOGGLE_BUTTON_ID = "_selectMultipleToggleButton"; //NOI18N
109:
110: /** The facet name for the select multiple toggle button. */
111: public static final String SELECT_MULTIPLE_TOGGLE_BUTTON_FACET = "selectMultipleToggleButton"; //NOI18N
112:
113: /** The component id for the selection column sort button. */
114: public static final String SELECT_SORT_BUTTON_ID = "_selectSortButton"; //NOI18N
115:
116: /** The facet name for the selection column sort button. */
117: public static final String SELECT_SORT_BUTTON_FACET = "selectSortButton"; //NOI18N
118:
119: /** The component id for the sort level text. */
120: public static final String SORT_LEVEL_TEXT_ID = "_sortLevelText"; //NOI18N
121:
122: /** The facet name for the sort level text. */
123: public static final String SORT_LEVEL_TEXT_FACET = "sortLevelText"; //NOI18N
124:
125: /** The component id for the toggle sort button. */
126: public static final String TOGGLE_SORT_BUTTON_ID = "_toggleSortButton"; //NOI18N
127:
128: /** The facet name for the toggle sort button. */
129: public static final String TOGGLE_SORT_BUTTON_FACET = "toggleSortButton"; //NOI18N
130:
131: /** The component id for the warning icon. */
132: public static final String WARNING_ICON_ID = "_warningIcon"; //NOI18N
133:
134: /** The facet name for the warning icon. */
135: public static final String WARNING_ICON_FACET = "warningIcon"; //NOI18N
136:
137: // The Table ancestor enclosing this component.
138: private Table table = null;
139:
140: // The TableColumn ancestor enclosing this component.
141: private TableColumn tableColumn = null;
142:
143: // The TableRowGroup ancestor enclosing this component.
144: private TableRowGroup tableRowGroup = null;
145:
146: // Flag indicating that the next sort order is descending.
147: private boolean descending = false;
148: private boolean descending_set = false;
149:
150: // The total number of selected rows.
151: private int selectedRowsCount = -1;
152:
153: // The total number of sorts applied.
154: private int sortCount = -1;
155:
156: // Sort level for this component.
157: private int sortLevel = -1;
158:
159: /** Default constructor */
160: public TableHeader() {
161: super ();
162: }
163:
164: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
165: // Child methods
166: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
167:
168: /**
169: * Helper method to get the total number of sorts applied.
170: *
171: * @return The sort count.
172: */
173: public int getSortCount() {
174: if (sortCount == -1) {
175: TableRowGroup group = getTableRowGroupAncestor();
176: sortCount = (group != null) ? group.getSortCount() : 0;
177: }
178: return sortCount;
179: }
180:
181: /**
182: * Helper method to get sort level for this component.
183: *
184: * @return The sort level or 0 if sort does not apply.
185: */
186: public int getSortLevel() {
187: if (sortLevel == -1) {
188: TableColumn col = getTableColumnAncestor();
189: TableRowGroup group = getTableRowGroupAncestor();
190: if (col != null && group != null) {
191: sortLevel = group.getSortLevel(col.getSortCriteria());
192: } else {
193: log("getSortLevel", //NOI18N
194: "Cannot obtain sort level, TableColumn or TableRowGroup is null"); //NOI18N
195: }
196: }
197: return sortLevel;
198: }
199:
200: /**
201: * Get the closest Table ancestor that encloses this component.
202: *
203: * @return The Table ancestor.
204: */
205: public Table getTableAncestor() {
206: if (table == null) {
207: UIComponent component = this ;
208: while (component != null) {
209: component = component.getParent();
210: if (component instanceof Table) {
211: table = (Table) component;
212: break;
213: }
214: }
215: }
216: return table;
217: }
218:
219: /**
220: * Get the closest TableColumn ancestor that encloses this component.
221: *
222: * @return The TableColumn ancestor.
223: */
224: public TableColumn getTableColumnAncestor() {
225: if (tableColumn == null) {
226: UIComponent component = this ;
227: while (component != null) {
228: component = component.getParent();
229: if (component instanceof TableColumn) {
230: tableColumn = (TableColumn) component;
231: break;
232: }
233: }
234: }
235: return tableColumn;
236: }
237:
238: /**
239: * Get the closest TableRowGroup ancestor that encloses this component.
240: *
241: * @return The TableRowGroup ancestor.
242: */
243: public TableRowGroup getTableRowGroupAncestor() {
244: if (tableRowGroup == null) {
245: UIComponent component = this ;
246: while (component != null) {
247: component = component.getParent();
248: if (component instanceof TableRowGroup) {
249: tableRowGroup = (TableRowGroup) component;
250: break;
251: }
252: }
253: }
254: return tableRowGroup;
255: }
256:
257: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
258: // Group methods
259: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
260:
261: /**
262: * Get select multiple toggle button.
263: *
264: * @return The select multiple toggle button.
265: */
266: public UIComponent getCollapsedHiddenField() {
267: UIComponent facet = getFacet(COLLAPSED_HIDDEN_FIELD_FACET);
268: if (facet != null) {
269: return facet;
270: }
271:
272: // Get child.
273: HiddenField child = new HiddenField();
274: child.setId(COLLAPSED_HIDDEN_FIELD_ID);
275:
276: // Set value.
277: TableRowGroup group = getTableRowGroupAncestor();
278: if (group != null) {
279: child.setValue(new Boolean(group.isCollapsed()));
280: } else {
281: log("getCollapsedHiddenField", //NOI18N
282: "Cannot set collapsed hidden field value, TableRowGroup is null"); //NOI18N
283: }
284:
285: // Save facet and return child.
286: getFacets().put(child.getId(), child);
287: return child;
288: }
289:
290: /**
291: * Get group panel toggle button.
292: *
293: * @return The group panel toggle button.
294: */
295: public UIComponent getGroupPanelToggleButton() {
296: UIComponent facet = getFacet(GROUP_PANEL_TOGGLE_BUTTON_FACET);
297: if (facet != null) {
298: return facet;
299: }
300:
301: Theme theme = getTheme();
302: Table table = getTableAncestor();
303: TableRowGroup group = getTableRowGroupAncestor();
304:
305: // Get child.
306: IconHyperlink child = new IconHyperlink();
307: child.setId(GROUP_PANEL_TOGGLE_BUTTON_ID);
308: child
309: .setIcon((group != null && group.isCollapsed()) ? ThemeImages.TABLE_GROUP_PANEL
310: : ThemeImages.TABLE_GROUP_PANEL_FLIP);
311: child.setBorder(0);
312:
313: // Set JS to display table preferences panel.
314: StringBuffer buff = new StringBuffer(128);
315: if (table != null && group != null) {
316: buff
317: .append("document.getElementById('")
318: //NOI18N
319: .append(table.getClientId(getFacesContext()))
320: .append("').toggleGroupPanel('")
321: //NOI18N
322: .append(group.getClientId(getFacesContext()))
323: .append("'); return false"); //NOI18N
324: child.setOnClick(buff.toString());
325: } else {
326: log("getGroupPanelToggleButton", //NOI18N
327: "onClick not set, Table or TableRowGroup is null"); //NOI18N
328: }
329:
330: // Set tool tip.
331: String toolTip = (group != null && group.isCollapsed()) ? theme
332: .getMessage("table.group.expand") //NOI18N
333: : theme.getMessage("table.group.collapse"); //NOI18N
334: child.setAlt(toolTip);
335: child.setToolTip(toolTip);
336:
337: // Set tab index.
338: if (table != null) {
339: child.setTabIndex(table.getTabIndex());
340: } else {
341: log("getGroupPanelToggleButton",
342: "Tab index not set, Table is null"); //NOI18N
343: }
344:
345: // Save facet and return child.
346: getFacets().put(child.getId(), child);
347: return child;
348: }
349:
350: /**
351: * Get select multiple toggle button.
352: *
353: * @return The select multiple toggle button.
354: */
355: public UIComponent getSelectMultipleToggleButton() {
356: UIComponent facet = getFacet(SELECT_MULTIPLE_TOGGLE_BUTTON_FACET);
357: if (facet != null) {
358: return facet;
359: }
360:
361: Table table = getTableAncestor();
362: TableRowGroup group = getTableRowGroupAncestor();
363:
364: // Get child.
365: Checkbox child = new Checkbox();
366: child.setId(SELECT_MULTIPLE_TOGGLE_BUTTON_ID);
367: child.setSelectedValue(Boolean.TRUE);
368:
369: // Set JS to display table preferences panel.
370: StringBuffer buff = new StringBuffer(128);
371: if (table != null && group != null) {
372: buff
373: .append("document.getElementById('")
374: //NOI18N
375: .append(table.getClientId(getFacesContext()))
376: .append("').selectGroupRows('")
377: //NOI18N
378: .append(group.getClientId(getFacesContext()))
379: .append("', this.checked)"); //NOI18N
380: child.setOnClick(buff.toString());
381: } else {
382: log("getSelectMultipleToggleButton", //NOI18N
383: "onClick not set, Table or TableRowgroup is null"); //NOI18N
384: }
385:
386: // Set selected property.
387: if (group != null) {
388: // Checkbox is checked only if all rendered rows are selected.
389: RowKey[] rowKeys = group.getRenderedRowKeys();
390: if (rowKeys != null && rowKeys.length > 0
391: && rowKeys.length == getSelectedRowsCount()) {
392: child.setSelected(Boolean.TRUE);
393: child.setToolTip(getTheme().getMessage(
394: "table.group.deselectMultiple")); //NOI18N
395: } else {
396: child.setToolTip(getTheme().getMessage(
397: "table.group.selectMultiple")); //NOI18N
398: }
399: } else {
400: log("getSelectMultipleToggleButton", //NOI18N
401: "Tool tip & selected not set, TableRowGroup is null"); //NOI18N
402: }
403:
404: // Set tab index.
405: if (table != null) {
406: child.setTabIndex(table.getTabIndex());
407: } else {
408: log("getSelectMultipleToggleButton", //NOI18N
409: "Tab index not set, Table is null"); //NOI18N
410: }
411:
412: // Save facet and return child.
413: getFacets().put(child.getId(), child);
414: return child;
415: }
416:
417: /**
418: * Get warning icon.
419: *
420: * @return The warning icon.
421: */
422: public UIComponent getWarningIcon() {
423: UIComponent facet = getFacet(WARNING_ICON_FACET);
424: if (facet != null) {
425: return facet;
426: }
427:
428: Theme theme = getTheme();
429: TableRowGroup group = getTableRowGroupAncestor();
430:
431: // Get child.
432: Icon child = theme.getIcon(ThemeImages.ALERT_WARNING_SMALL);
433:
434: // Warning icon is only rendered if at least one row is selected and the
435: // select multiple toggle is not checked.
436: RowKey[] rowKeys = group.getRenderedRowKeys();
437: int rows = getSelectedRowsCount();
438: if (group != null && !group.isCollapsed() || rows == 0
439: || rowKeys != null && rowKeys.length > 0
440: && rowKeys.length == rows) {
441: // Replace default icon with place holder.
442: Icon placeHolder = theme.getIcon(ThemeImages.DOT);
443: placeHolder.setHeight(child.getHeight());
444: placeHolder.setWidth(child.getWidth());
445: child = placeHolder;
446: } else {
447: log("getWarningIcon", //NOI18N
448: "Height & width not set, TableRowGroup is null"); //NOI18N
449: }
450: child.setId(WARNING_ICON_ID);
451: child.setBorder(0);
452:
453: // Set tool tip.
454: String toolTip = (group != null && group.isCollapsed()) ? theme
455: .getMessage("table.group.warning") : null; //NOI18N
456: child.setAlt(toolTip);
457: child.setToolTip(toolTip);
458:
459: // Save facet and return child.
460: getFacets().put(child.getId(), child);
461: return child;
462: }
463:
464: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
465: // Sort methods
466: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
467:
468: /**
469: * Get add sort button.
470: *
471: * @return The add sort button.
472: */
473: public UIComponent getAddSortButton() {
474: UIComponent facet = getFacet(ADD_SORT_BUTTON_FACET);
475: if (facet != null) {
476: return facet;
477: }
478:
479: Theme theme = getTheme();
480: Table table = getTableAncestor();
481: TableColumn col = getTableColumnAncestor();
482:
483: // Get child.
484: IconHyperlink child = new IconHyperlink();
485: child.setId(ADD_SORT_BUTTON_ID);
486: child.setIcon(ThemeImages.TABLE_SORT_ADD);
487: child.setBorder(0);
488: child.setAlign("top"); //NOI18N
489: child.setStyleClass(ThemeStyles.TABLE_HEADER_LINK_IMG);
490: child.addActionListener(new TableSortActionListener());
491:
492: // Set tool tip.
493: String toolTip = getTheme().getMessage("table.sort.button.add", //NOI18N
494: new String[] { getNextSortToolTipAugment() });
495: child.setToolTip(toolTip);
496:
497: // Set alt.
498: if (isSelectHeader()) {
499: child
500: .setAlt(theme
501: .getMessage(
502: "table.sort.alt.add", //NOI18N
503: new String[] { theme
504: .getMessage("table.select.selectionColumn") })); //NOI18N
505: } else {
506: String header = (col != null && col.getHeaderText() != null) ? col
507: .getHeaderText()
508: : ""; //NOI18N
509: // Select column does not have header text.
510: child.setAlt(theme.getMessage("table.sort.alt.add", //NOI18N
511: new String[] { header }));
512: }
513:
514: // Set tab index.
515: if (table != null) {
516: child.setTabIndex(table.getTabIndex());
517: } else {
518: log("getAddSortButton", "Tab index not set, Table is null"); //NOI18N
519: }
520:
521: // Add sort level text child.
522: if (getSortCount() > 0 && getSortLevel() > 0) {
523: // Span must appear within hyperlink for style to render properly.
524: child.getChildren().add(getSortLevelText());
525: }
526:
527: // Save facet and return child.
528: getFacets().put(child.getId(), child);
529:
530: // Set focus when sort buttons are displayed -- bugtraq #6316565.
531: setSortFocus(child);
532: return child;
533: }
534:
535: /**
536: * Get primary sort button.
537: *
538: * @return The primary sort button.
539: */
540: public UIComponent getPrimarySortButton() {
541: UIComponent facet = getFacet(PRIMARY_SORT_BUTTON_FACET);
542: if (facet != null) {
543: return facet;
544: }
545:
546: Theme theme = getTheme();
547: Table table = getTableAncestor();
548: TableColumn col = getTableColumnAncestor();
549:
550: // Get child.
551: IconHyperlink child = new IconHyperlink();
552: child.setId(PRIMARY_SORT_BUTTON_ID);
553: child.setIcon(ThemeImages.TABLE_SORT_PRIMARY);
554: child.setBorder(0);
555: child.setAlign("top"); //NOI18N
556: child.setStyleClass(ThemeStyles.TABLE_HEADER_LINK_IMG);
557: child.addActionListener(new TableSortActionListener());
558:
559: // Set tool tip.
560: String toolTip = theme.getMessage("table.sort.button.primary", //NOI18N
561: new String[] { getNextSortToolTipAugment() });
562: child.setToolTip(toolTip);
563:
564: // Set alt.
565: if (col != null) {
566: String header = (col.getHeaderText() != null) ? col
567: .getHeaderText() : ""; //NOI18N
568: child.setAlt(theme.getMessage("table.sort.alt.primary", //NOI18N
569: new String[] { header }));
570: } else {
571: log("getPrimarySortButton",
572: "Alt text not set, TableColumn is null"); //NOI18N
573: }
574:
575: // Set tab index.
576: if (table != null) {
577: child.setTabIndex(table.getTabIndex());
578: } else {
579: log("getPrimarySortButton",
580: "Tab index not set, Table is null"); //NOI18N
581: }
582:
583: // Save facet and return child.
584: getFacets().put(child.getId(), child);
585:
586: // Set focus when sort buttons are displayed -- bugtraq #6316565.
587: setSortFocus(child);
588: return child;
589: }
590:
591: /**
592: * Get primary sort link.
593: *
594: * @return The primary sort link.
595: */
596: public UIComponent getPrimarySortLink() {
597: UIComponent facet = getFacet(PRIMARY_SORT_LINK_FACET);
598: if (facet != null) {
599: return facet;
600: }
601:
602: Table table = getTableAncestor();
603: TableColumn col = getTableColumnAncestor();
604:
605: // Get child.
606: IconHyperlink child = new IconHyperlink();
607: child.setId(PRIMARY_SORT_LINK_ID);
608: child.setStyleClass(ThemeStyles.TABLE_HEADER_LINK);
609: child.addActionListener(new TableSortActionListener());
610:
611: // Get tool tip.
612: String toolTip = "table.sort.link.other"; //NOI18N
613: if (getSortLevel() == 1 && getSortCount() == 1) {
614: // Primary sort column, only sort applied.
615: toolTip = "table.sort.link.primary"; //NOI18N
616: } else if (getSortCount() == 0) {
617: // No sorts applied.
618: toolTip = "table.sort.link.none"; //NOI18N
619: }
620:
621: // Set column properties.
622: if (col != null) {
623: child.setIcon(col.getSortIcon());
624: child.setText(col.getHeaderText());
625: child.setImageURL(col.getSortImageURL());
626: child.setToolTip(getTheme().getMessage(
627: toolTip,
628: new String[] { col.getSortToolTipAugment(col
629: .isDescending()) }));
630: } else {
631: log("getPrimarySortLink", //NOI18N
632: "Tool tip, icon, text, & image URL not set, TableColumn is null"); //NOI18N
633: }
634:
635: // Set tab index.
636: if (table != null) {
637: child.setTabIndex(table.getTabIndex());
638: } else {
639: log("getPrimarySortLink",
640: "Tab index not set, Table is null"); //NOI18N
641: }
642:
643: // Save facet and return child.
644: getFacets().put(child.getId(), child);
645: return child;
646: }
647:
648: /**
649: * Get select sort button.
650: *
651: * @return The title sort button.
652: */
653: public UIComponent getSelectSortButton() {
654: UIComponent facet = getFacet(SELECT_SORT_BUTTON_FACET);
655: if (facet != null) {
656: return facet;
657: }
658:
659: Theme theme = getTheme();
660: Table table = getTableAncestor();
661:
662: // Get child.
663: IconHyperlink child = new IconHyperlink();
664: child.setId(SELECT_SORT_BUTTON_ID);
665: child.setIcon(ThemeImages.TABLE_SORT_SELECT);
666: child.setBorder(0);
667: child.setAlign("top"); //NOI18N
668: child.setStyleClass(ThemeStyles.TABLE_HEADER_LINK);
669: child.addActionListener(new TableSortActionListener());
670:
671: // Set tool tip.
672: String toolTip = theme.getMessage("table.sort.button.primary", //NOI18N
673: new String[] { getNextSortToolTipAugment() });
674: child.setToolTip(toolTip);
675:
676: // Set alt.
677: String alt = theme.getMessage("table.sort.alt.primary", //NOI18N
678: new String[] { theme
679: .getMessage("table.select.selectionColumn") }); //NOI18N
680: child.setAlt(alt);
681:
682: // Set tab index.
683: if (table != null) {
684: child.setTabIndex(table.getTabIndex());
685: } else {
686: log("getSelectSortButton",
687: "Tab index not set, Table is null"); //NOI18N
688: }
689:
690: // Save facet and return child.
691: getFacets().put(child.getId(), child);
692: return child;
693: }
694:
695: /**
696: * Get sort level static text.
697: *
698: * @return The sort level static text.
699: */
700: public UIComponent getSortLevelText() {
701: UIComponent facet = getFacet(SORT_LEVEL_TEXT_FACET);
702: if (facet != null) {
703: return facet;
704: }
705:
706: // Get child.
707: StaticText child = new StaticText();
708: child.setId(SORT_LEVEL_TEXT_ID);
709: child.setText(Integer.toString(getSortLevel()));
710: child.setStyleClass(getTheme().getStyleClass(
711: ThemeStyles.TABLE_HEADER_SORTNUM));
712:
713: // Save facet and return child.
714: getFacets().put(child.getId(), child);
715: return child;
716: }
717:
718: /**
719: * Get toggle sort button.
720: *
721: * @return The toggle sort button.
722: */
723: public UIComponent getToggleSortButton() {
724: UIComponent facet = getFacet(TOGGLE_SORT_BUTTON_FACET);
725: if (facet != null) {
726: return facet;
727: }
728:
729: Theme theme = getTheme();
730: Table table = getTableAncestor();
731: TableColumn col = getTableColumnAncestor();
732: TableRowGroup group = getTableRowGroupAncestor();
733:
734: // Get child.
735: IconHyperlink child = new IconHyperlink();
736: child.setId(TOGGLE_SORT_BUTTON_ID);
737: child.setBorder(0);
738: child.setAlign("top"); //NOI18N
739: child.addActionListener(new TableSortActionListener());
740:
741: // Disable descending sort so selections don't move off page.
742: if (table != null && col != null) {
743: if (!isDescending() && group.isPaginated()
744: && col.getSelectId() != null
745: && !table.isHiddenSelectedRows()) {
746: child.setDisabled(true);
747: }
748: } else {
749: log("getToggleSortButton", //NOI18N
750: "Disabled state not set, Table or TableColumn is null"); //NOI18N
751: }
752:
753: // Set alt and tool tip for the next sort applied.
754: if (col != null) {
755: // Get tool tip.
756: child.setToolTip(theme.getMessage(
757: "table.sort.button.toggle", //NOI18N
758: new String[] { col
759: .getSortToolTipAugment(!isDescending()) }));
760:
761: // Get alt.
762: if (isSelectHeader()) {
763: // Select column does not have header text.
764: child
765: .setAlt(theme
766: .getMessage(
767: "table.sort.alt.primary", //NOI18N
768: new String[] {
769: theme
770: .getMessage("table.select.selectionColumn"), //NOI18N
771: col
772: .getSortToolTipAugment(isDescending()),
773: Integer
774: .toString(getSortLevel()) }));
775: } else {
776: String header = (col.getHeaderText() != null) ? col
777: .getHeaderText() : ""; //NOI18N
778: child
779: .setAlt(theme
780: .getMessage(
781: "table.sort.alt.toggle", //NOI18N
782: new String[] {
783: header,
784: col
785: .getSortToolTipAugment(isDescending()),
786: Integer
787: .toString(getSortLevel()) }));
788: }
789: } else {
790: log("getToggleSortButton",
791: "Alt text not set, TableColumn is null"); //NOI18N
792: }
793:
794: // Set icon for the next sort applied.
795: if (child.isDisabled()) {
796: child.setIcon(ThemeImages.TABLE_SORT_DESCENDING_DISABLED);
797: } else if (!isDescending()) {
798: child.setIcon(ThemeImages.TABLE_SORT_DESCENDING);
799: } else {
800: child.setIcon(ThemeImages.TABLE_SORT_ASCENDING);
801: }
802:
803: // Set styleClass.
804: if (child.isDisabled()) {
805: if (getSortLevel() == 1) {
806: child
807: .setStyleClass(ThemeStyles.TABLE_HEADER_SORT_DISABLED);
808: } else {
809: child
810: .setStyleClass(ThemeStyles.TABLE_HEADER_SELECTCOL_DISABLED);
811: }
812: } else {
813: child.setStyleClass(ThemeStyles.TABLE_HEADER_LINK_IMG);
814: }
815:
816: // Add sort level text.
817: if (getSortLevel() > 0 && getSortCount() > 0) {
818: // Span must appear within hyperlink for style to render properly.
819: child.getChildren().add(getSortLevelText());
820: }
821:
822: // Set tab index.
823: if (table != null) {
824: child.setTabIndex(table.getTabIndex());
825: } else {
826: log("getToggleSortButton",
827: "Tab index not set, Table is null"); //NOI18N
828: }
829:
830: // Save facet and return child.
831: getFacets().put(child.getId(), child);
832:
833: // Set focus when sort buttons are displayed -- bugtraq #6316565.
834: setSortFocus(child);
835: return child;
836: }
837:
838: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
839: // UIComponent methods
840: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
841:
842: /**
843: * If the rendered property is true, render the begining of the current
844: * state of this UIComponent to the response contained in the specified
845: * FacesContext.
846: *
847: * If a Renderer is associated with this UIComponent, the actual encoding
848: * will be delegated to Renderer.encodeBegin(FacesContext, UIComponent).
849: *
850: * @param context FacesContext for the current request.
851: *
852: * @exception IOException if an input/output error occurs while rendering.
853: * @exception NullPointerException if FacesContext is null.
854: */
855: public void encodeBegin(FacesContext context) throws IOException {
856: // Clear cached variables -- bugtraq #6300020.
857: table = null;
858: tableColumn = null;
859: tableRowGroup = null;
860: descending = false;
861: descending_set = false;
862: selectedRowsCount = -1;
863: sortCount = -1;
864: sortLevel = -1;
865: super .encodeBegin(context);
866: }
867:
868: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
869: // Private methods
870: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
871:
872: /**
873: * Helper method to get next sort tool tip augment based on the value for
874: * the align property of TableColumn.
875: *
876: * @param descending Flag indicating descending sort.
877: * @return The sort tool tip augment.
878: */
879: private String getNextSortToolTipAugment() {
880: TableColumn col = getTableColumnAncestor();
881: return (col != null) ? col
882: .getSortToolTipAugment(isDescending()) : ""; //NOI18N
883: }
884:
885: /**
886: * Helper method to get the total number of selected rows.
887: *
888: * @return The number of selected rows.
889: */
890: private int getSelectedRowsCount() {
891: if (selectedRowsCount == -1) {
892: TableRowGroup group = getTableRowGroupAncestor();
893: if (group != null) {
894: selectedRowsCount = group
895: .getRenderedSelectedRowsCount();
896: }
897: }
898: return selectedRowsCount;
899: }
900:
901: /**
902: * Helper method to get Theme objects.
903: *
904: * @return The current theme.
905: */
906: private Theme getTheme() {
907: return ThemeUtilities.getTheme(getFacesContext());
908: }
909:
910: /**
911: * Helper method to test if the next sort order is descending.
912: *
913: * @return true if descending, else false.
914: */
915: private boolean isDescending() {
916: if (!descending_set) {
917: TableColumn col = getTableColumnAncestor();
918: TableRowGroup group = getTableRowGroupAncestor();
919:
920: // Get next sort order.
921: if (col != null && group != null) {
922: descending = (getSortLevel() > 0) ? group
923: .isDescendingSort(col.getSortCriteria()) : col
924: .isDescending();
925: descending_set = true;
926: }
927: }
928: return descending;
929: }
930:
931: /**
932: * Log fine messages.
933: */
934: private void log(String method, String message) {
935: // Get class.
936: Class clazz = this .getClass();
937: if (LogUtil.fineEnabled(clazz)) {
938: // Log method name and message.
939: LogUtil.fine(clazz, clazz.getName() + "." + method + ": "
940: + message); //NOI18N
941: }
942: }
943:
944: /**
945: * Set focus when sort buttons are displayed.
946: */
947: private void setSortFocus(UIComponent component) {
948: if (component == null) {
949: return;
950: }
951:
952: // Get prefix for all IDs.
953: FacesContext context = getFacesContext();
954: String prefix = getClientId(context)
955: + NamingContainer.SEPARATOR_CHAR;
956:
957: // Get the client ID of the last component to have focus.
958: String id = RenderingUtilities.getLastClientID(context);
959: if (id == null) {
960: return;
961: }
962:
963: // Set component focus if any match was found. Don't include select
964: // sort button here as that component does not change.
965: if (id.equals(prefix + ADD_SORT_BUTTON_ID)
966: || id.equals(prefix + PRIMARY_SORT_BUTTON_ID)
967: || id.equals(prefix + TOGGLE_SORT_BUTTON_ID)) {
968: RenderingUtilities.setLastClientID(context, component
969: .getClientId(context));
970: }
971: }
972: }
|