1: <?php
2:
3: /**
4: * This file is part of the Nette Framework.
5: *
6: * Copyright (c) 2004, 2010 David Grudl (http://davidgrudl.com)
7: *
8: * This source file is subject to the "Nette license", and/or
9: * GPL license. For more information please see http://nette.org
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: */
25: class NSelectBox extends NFormControl
26: {
27: /** @var array */
28: private $items = array();
29:
30: /** @var array */
31: protected $allowed = array();
32:
33: /** @var bool */
34: private $skipFirst = FALSE;
35:
36: /** @var bool */
37: private $useKeys = TRUE;
38:
39:
40:
41: /**
42: * @param string label
43: * @param array items from which to choose
44: * @param int number of rows that should be visible
45: */
46: public function __construct($label = NULL, array $items = NULL, $size = NULL)
47: {
48: parent::__construct($label);
49: $this->control->setName('select');
50: $this->control->size = $size > 1 ? (int) $size : NULL;
51: if ($items !== NULL) {
52: $this->setItems($items);
53: }
54: }
55:
56:
57:
58: /**
59: * Returns selected item key.
60: * @return mixed
61: */
62: public function getValue()
63: {
64: $allowed = $this->allowed;
65: if ($this->skipFirst) {
66: $allowed = array_slice($allowed, 1, count($allowed), TRUE);
67: }
68:
69: return is_scalar($this->value) && isset($allowed[$this->value]) ? $this->value : NULL;
70: }
71:
72:
73:
74: /**
75: * Returns selected item key (not checked).
76: * @return mixed
77: */
78: public function getRawValue()
79: {
80: return is_scalar($this->value) ? $this->value : NULL;
81: }
82:
83:
84:
85: /**
86: * Ignores the first item in select box.
87: * @param string
88: * @return NSelectBox provides a fluent interface
89: */
90: public function skipFirst($item = NULL)
91: {
92: if (is_bool($item)) {
93: $this->skipFirst = $item;
94: } else {
95: $this->skipFirst = TRUE;
96: if ($item !== NULL) {
97: $this->items = array('' => $item) + $this->items;
98: $this->allowed = array('' => '') + $this->allowed;
99: }
100: }
101: return $this;
102: }
103:
104:
105:
106: /**
107: * Is first item in select box ignored?
108: * @return bool
109: */
110: final public function isFirstSkipped()
111: {
112: return $this->skipFirst;
113: }
114:
115:
116:
117: /**
118: * Are the keys used?
119: * @return bool
120: */
121: final public function areKeysUsed()
122: {
123: return $this->useKeys;
124: }
125:
126:
127:
128: /**
129: * Sets items from which to choose.
130: * @param array
131: * @return NSelectBox provides a fluent interface
132: */
133: public function setItems(array $items, $useKeys = TRUE)
134: {
135: $this->items = $items;
136: $this->allowed = array();
137: $this->useKeys = (bool) $useKeys;
138:
139: foreach ($items as $key => $value) {
140: if (!is_array($value)) {
141: $value = array($key => $value);
142: }
143:
144: foreach ($value as $key2 => $value2) {
145: if (!$this->useKeys) {
146: if (!is_scalar($value2)) {
147: throw new InvalidArgumentException("All items must be scalar.");
148: }
149: $key2 = $value2;
150: }
151:
152: if (isset($this->allowed[$key2])) {
153: throw new InvalidArgumentException("Items contain duplication for key '$key2'.");
154: }
155:
156: $this->allowed[$key2] = $value2;
157: }
158: }
159: return $this;
160: }
161:
162:
163:
164: /**
165: * Returns items from which to choose.
166: * @return array
167: */
168: final public function getItems()
169: {
170: return $this->items;
171: }
172:
173:
174:
175: /**
176: * Returns selected value.
177: * @return string
178: */
179: public function getSelectedItem()
180: {
181: if (!$this->useKeys) {
182: return $this->getValue();
183:
184: } else {
185: $value = $this->getValue();
186: return $value === NULL ? NULL : $this->allowed[$value];
187: }
188: }
189:
190:
191:
192: /**
193: * Generates control's HTML element.
194: * @return NHtml
195: */
196: public function getControl()
197: {
198: $control = parent::getControl();
199: if ($this->skipFirst) {
200: reset($this->items);
201: $control->data['nette-empty-value'] = $this->useKeys ? key($this->items) : current($this->items);
202: }
203: $selected = $this->getValue();
204: $selected = is_array($selected) ? array_flip($selected) : array($selected => TRUE);
205: $option = NHtml::el('option');
206:
207: foreach ($this->items as $key => $value) {
208: if (!is_array($value)) {
209: $value = array($key => $value);
210: $dest = $control;
211:
212: } else {
213: $dest = $control->create('optgroup')->label($key);
214: }
215:
216: foreach ($value as $key2 => $value2) {
217: if ($value2 instanceof NHtml) {
218: $dest->add((string) $value2->selected(isset($selected[$key2])));
219:
220: } else {
221: $key2 = $this->useKeys ? $key2 : $value2;
222: $value2 = $this->translate($value2);
223: $dest->add((string) $option->value($key2 === $value2 ? NULL : $key2)->selected(isset($selected[$key2]))->setText($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: