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