001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.wicket.markup.html.form; 018 019import java.util.ArrayList; 020import java.util.HashSet; 021import java.util.List; 022import java.util.Set; 023 024import org.apache.wicket.Component; 025import org.apache.wicket.behavior.Behavior; 026import org.apache.wicket.markup.html.WebMarkupContainer; 027 028/** 029 * A Javascript-based "Select All" checkbox component that works with a loose collection of 030 * {@link CheckBox} components. By default, clicking on any of the controlled checkboxes 031 * automatically updates the state of the "select all" checkbox. Override 032 * {@link AbstractCheckSelector#wantAutomaticUpdate()} to change this. 033 * 034 * @author Carl-Eric Menzel 035 */ 036public class CheckBoxSelector extends AbstractCheckSelector 037{ 038 private static final long serialVersionUID = 1L; 039 040 private final Set<CheckBox> connectedCheckBoxes = new HashSet<>(); 041 042 private final Behavior cleanup = new Behavior() 043 { 044 private static final long serialVersionUID = 1L; 045 046 @Override 047 public void onRemove(Component component) 048 { 049 connectedCheckBoxes.remove(component); 050 component.remove(this); 051 } 052 }; 053 054 /** 055 * @param id 056 * The component ID 057 * 058 * @see CheckBoxSelector#getCheckBoxes() 059 */ 060 public CheckBoxSelector(String id) 061 { 062 super(id); 063 } 064 065 /** 066 * @param id 067 * The component ID 068 * @param boxes 069 * checkBoxes this selector will control 070 */ 071 public CheckBoxSelector(String id, CheckBox... boxes) 072 { 073 super(id); 074 075 for (CheckBox box : boxes) 076 { 077 connectedCheckBoxes.add(box); 078 box.add(cleanup); 079 } 080 } 081 082 @Override 083 protected CharSequence getFindCheckboxesFunction() 084 { 085 return String.format("Wicket.CheckboxSelector.getCheckboxesFunction(%s)", 086 buildMarkupIdJSArrayLiteral(getCheckBoxes())); 087 } 088 089 /** 090 * Override this method to control a dynamic collection of {@link CheckBox}es. 091 * 092 * @return by default returns the checkBoxes passed to the constructor 093 */ 094 protected Iterable<? extends CheckBox> getCheckBoxes() 095 { 096 return connectedCheckBoxes; 097 } 098 099 /** 100 * Builds a JavaScript array literal containing the markup IDs of the given components. Example: 101 * "['foo', 'bar', 'baz']". 102 * 103 * @param components 104 * The components whose IDs we need 105 * @return a properly formatted JS array literal 106 */ 107 private String buildMarkupIdJSArrayLiteral(final Iterable<? extends CheckBox> components) 108 { 109 StringBuilder buf = new StringBuilder(); 110 buf.append('['); 111 if (components.iterator().hasNext()) 112 { 113 for (Component component : components) 114 { 115 buf.append('\'').append(component.getMarkupId()).append("', "); 116 } 117 buf.delete(buf.length() - 2, buf.length()); 118 } 119 buf.append(']'); 120 return buf.toString(); 121 } 122 123 /** 124 * Utility method to collect all {@link CheckBox}es inside a container. 125 * 126 * @param container 127 * container with checkBoxes 128 * @return all contained checkBoxes 129 */ 130 public static final Iterable<CheckBox> collectCheckBoxes(WebMarkupContainer container) 131 { 132 List<CheckBox> checkBoxes = new ArrayList<>(); 133 134 container.<CheckBox, Void> visitChildren(CheckBox.class, (child, visit) -> { 135 checkBoxes.add(child); 136 }); 137 138 return checkBoxes; 139 } 140}