1: <?php
2:
3: /**
4: * This file is part of the Nette Framework (https://nette.org)
5: *
6: * Copyright (c) 2004 David Grudl (http://davidgrudl.com)
7: *
8: * For the full copyright and license information, please view
9: * the file license.txt that was distributed with this source code.
10: */
11:
12: namespace Nette\Forms;
13:
14: use Nette;
15:
16:
17:
18: /**
19: * Select box control that allows single item selection.
20: *
21: * @author David Grudl
22: *
23: * @property-read mixed $rawValue
24: * @property array $items
25: * @property-read mixed $selectedItem
26: * @property-read bool $firstSkipped
27: */
28: class SelectBox extends FormControl
29: {
30: /** @var array */
31: private $items = array();
32:
33: /** @var array */
34: protected $allowed = array();
35:
36: /** @var bool */
37: private $skipFirst = FALSE;
38:
39: /** @var bool */
40: private $useKeys = TRUE;
41:
42:
43:
44: /**
45: * @param string label
46: * @param array items from which to choose
47: * @param int number of rows that should be visible
48: */
49: public function __construct($label = NULL, array $items = NULL, $size = NULL)
50: {
51: parent::__construct($label);
52: $this->control->setName('select');
53: $this->control->size = $size > 1 ? (int) $size : NULL;
54: $this->control->onfocus = 'this.onmousewheel=function(){return false}'; // prevents accidental change in IE
55: $this->label->onclick = 'document.getElementById(this.htmlFor).focus();return false'; // prevents deselect in IE 5 - 6
56: if ($items !== NULL) {
57: $this->setItems($items);
58: }
59: }
60:
61:
62:
63: /**
64: * Returns selected item key.
65: * @return mixed
66: */
67: public function getValue()
68: {
69: $allowed = $this->allowed;
70: if ($this->skipFirst) {
71: $allowed = array_slice($allowed, 1, count($allowed), TRUE);
72: }
73:
74: return is_scalar($this->value) && isset($allowed[$this->value]) ? $this->value : NULL;
75: }
76:
77:
78:
79: /**
80: * Returns selected item key (not checked).
81: * @return mixed
82: */
83: public function getRawValue()
84: {
85: return is_scalar($this->value) ? $this->value : NULL;
86: }
87:
88:
89:
90: /**
91: * Ignores the first item in select box.
92: * @param string
93: * @return SelectBox provides a fluent interface
94: */
95: public function skipFirst($item = NULL)
96: {
97: if (is_bool($item)) {
98: $this->skipFirst = $item;
99: } else {
100: $this->skipFirst = TRUE;
101: if ($item !== NULL) {
102: $this->items = array('' => $item) + $this->items;
103: $this->allowed = array('' => '') + $this->allowed;
104: }
105: }
106: return $this;
107: }
108:
109:
110:
111: /**
112: * Is first item in select box ignored?
113: * @return bool
114: */
115: final public function isFirstSkipped()
116: {
117: return $this->skipFirst;
118: }
119:
120:
121:
122: /**
123: * Are the keys used?
124: * @return bool
125: */
126: final public function areKeysUsed()
127: {
128: return $this->useKeys;
129: }
130:
131:
132:
133: /**
134: * Sets items from which to choose.
135: * @param array
136: * @return SelectBox provides a fluent interface
137: */
138: public function setItems(array $items, $useKeys = TRUE)
139: {
140: $this->items = $items;
141: $this->allowed = array();
142: $this->useKeys = (bool) $useKeys;
143:
144: foreach ($items as $key => $value) {
145: if (!is_array($value)) {
146: $value = array($key => $value);
147: }
148:
149: foreach ($value as $key2 => $value2) {
150: if (!$this->useKeys) {
151: if (!is_scalar($value2)) {
152: throw new \InvalidArgumentException("All items must be scalars.");
153: }
154: $key2 = $value2;
155: }
156:
157: if (isset($this->allowed[$key2])) {
158: throw new \InvalidArgumentException("Items contain duplication for key '$key2'.");
159: }
160:
161: $this->allowed[$key2] = $value2;
162: }
163: }
164: return $this;
165: }
166:
167:
168:
169: /**
170: * Returns items from which to choose.
171: * @return array
172: */
173: final public function getItems()
174: {
175: return $this->items;
176: }
177:
178:
179:
180: /**
181: * Returns selected value.
182: * @return string
183: */
184: public function getSelectedItem()
185: {
186: if (!$this->useKeys) {
187: return $this->getValue();
188:
189: } else {
190: $value = $this->getValue();
191: return $value === NULL ? NULL : $this->allowed[$value];
192: }
193: }
194:
195:
196:
197: /**
198: * Generates control's HTML element.
199: * @return Nette\Web\Html
200: */
201: public function getControl()
202: {
203: $control = parent::getControl();
204: $selected = $this->getValue();
205: $selected = is_array($selected) ? array_flip($selected) : array($selected => TRUE);
206: $option = Nette\Web\Html::el('option');
207:
208: foreach ($this->items as $key => $value) {
209: if (!is_array($value)) {
210: $value = array($key => $value);
211: $dest = $control;
212:
213: } else {
214: $dest = $control->create('optgroup')->label($key);
215: }
216:
217: foreach ($value as $key2 => $value2) {
218: if ($value2 instanceof Nette\Web\Html) {
219: $dest->add((string) $value2->selected(isset($selected[$key2])));
220:
221: } elseif ($this->useKeys) {
222: $dest->add((string) $option->value($key2)->selected(isset($selected[$key2]))->setText($this->translate($value2)));
223:
224: } else {
225: $dest->add((string) $option->selected(isset($selected[$value2]))->setText($this->translate($value2)));
226: }
227: }
228: }
229: return $control;
230: }
231:
232:
233:
234: /**
235: * Filled validator: has been any item selected?
236: * @param IFormControl
237: * @return bool
238: */
239: public static function validateFilled(IFormControl $control)
240: {
241: $value = $control->getValue();
242: return is_array($value) ? count($value) > 0 : $value !== NULL;
243: }
244:
245: }
246: