1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "GUIDialogSelect.h"
10
11 #include "FileItem.h"
12 #include "guilib/GUIMessage.h"
13 #include "guilib/LocalizeStrings.h"
14 #include "input/Key.h"
15 #include "utils/StringUtils.h"
16
17 #define CONTROL_HEADING 1
18 #define CONTROL_NUMBER_OF_ITEMS 2
19 #define CONTROL_SIMPLE_LIST 3
20 #define CONTROL_DETAILED_LIST 6
21 #define CONTROL_EXTRA_BUTTON 5
22 #define CONTROL_EXTRA_BUTTON2 8
23 #define CONTROL_CANCEL_BUTTON 7
24
CGUIDialogSelect()25 CGUIDialogSelect::CGUIDialogSelect() : CGUIDialogSelect(WINDOW_DIALOG_SELECT) {}
26
CGUIDialogSelect(int windowId)27 CGUIDialogSelect::CGUIDialogSelect(int windowId)
28 : CGUIDialogBoxBase(windowId, "DialogSelect.xml"),
29 m_bButtonEnabled(false),
30 m_bButton2Enabled(false),
31 m_bButtonPressed(false),
32 m_bButton2Pressed(false),
33 m_selectedItem(nullptr),
34 m_useDetails(false),
35 m_multiSelection(false),
36 m_selectedItems(),
37 m_vecList(new CFileItemList())
38 {
39 m_bConfirmed = false;
40 m_loadType = KEEP_IN_MEMORY;
41 }
42
43 CGUIDialogSelect::~CGUIDialogSelect(void) = default;
44
OnMessage(CGUIMessage & message)45 bool CGUIDialogSelect::OnMessage(CGUIMessage& message)
46 {
47 switch (message.GetMessage())
48 {
49 case GUI_MSG_WINDOW_DEINIT:
50 {
51 CGUIDialogBoxBase::OnMessage(message);
52
53 m_bButtonEnabled = false;
54 m_bButton2Enabled = false;
55 m_useDetails = false;
56 m_multiSelection = false;
57
58 // construct selected items list
59 m_selectedItems.clear();
60 m_selectedItem = nullptr;
61 for (int i = 0 ; i < m_vecList->Size() ; i++)
62 {
63 CFileItemPtr item = m_vecList->Get(i);
64 if (item->IsSelected())
65 {
66 m_selectedItems.push_back(i);
67 if (!m_selectedItem)
68 m_selectedItem = item;
69 }
70 }
71 m_vecList->Clear();
72 return true;
73 }
74 break;
75
76 case GUI_MSG_WINDOW_INIT:
77 {
78 m_bButtonPressed = false;
79 m_bButton2Pressed = false;
80 m_bConfirmed = false;
81 CGUIDialogBoxBase::OnMessage(message);
82 return true;
83 }
84 break;
85
86
87 case GUI_MSG_CLICKED:
88 {
89 int iControl = message.GetSenderId();
90 if (m_viewControl.HasControl(CONTROL_SIMPLE_LIST))
91 {
92 int iAction = message.GetParam1();
93 if (ACTION_SELECT_ITEM == iAction || ACTION_MOUSE_LEFT_CLICK == iAction)
94 {
95 int iSelected = m_viewControl.GetSelectedItem();
96 if (iSelected >= 0 && iSelected < m_vecList->Size())
97 {
98 CFileItemPtr item(m_vecList->Get(iSelected));
99 if (m_multiSelection)
100 item->Select(!item->IsSelected());
101 else
102 {
103 for (int i = 0 ; i < m_vecList->Size() ; i++)
104 m_vecList->Get(i)->Select(false);
105 item->Select(true);
106 OnSelect(iSelected);
107 }
108 }
109 }
110 }
111 if (iControl == CONTROL_EXTRA_BUTTON2)
112 {
113 m_bButton2Pressed = true;
114 if (m_multiSelection)
115 m_bConfirmed = true;
116 Close();
117 }
118 if (iControl == CONTROL_EXTRA_BUTTON)
119 {
120 m_selectedItem = nullptr;
121 m_bButtonPressed = true;
122 if (m_multiSelection)
123 m_bConfirmed = true;
124 Close();
125 }
126 else if (iControl == CONTROL_CANCEL_BUTTON)
127 {
128 m_selectedItem = nullptr;
129 m_vecList->Clear();
130 m_selectedItems.clear();
131 m_bConfirmed = false;
132 Close();
133 }
134 }
135 break;
136 case GUI_MSG_SETFOCUS:
137 {
138 if (m_viewControl.HasControl(message.GetControlId()))
139 {
140 if (m_vecList->IsEmpty())
141 {
142 if (m_bButtonEnabled)
143 SET_CONTROL_FOCUS(CONTROL_EXTRA_BUTTON, 0);
144 else
145 SET_CONTROL_FOCUS(CONTROL_CANCEL_BUTTON, 0);
146 return true;
147 }
148 if (m_viewControl.GetCurrentControl() != message.GetControlId())
149 {
150 m_viewControl.SetFocused();
151 return true;
152 }
153 }
154 }
155 break;
156 }
157
158 return CGUIDialogBoxBase::OnMessage(message);
159 }
160
OnSelect(int idx)161 void CGUIDialogSelect::OnSelect(int idx)
162 {
163 m_bConfirmed = true;
164 Close();
165 }
166
OnBack(int actionID)167 bool CGUIDialogSelect::OnBack(int actionID)
168 {
169 m_selectedItem = nullptr;
170 m_vecList->Clear();
171 m_selectedItems.clear();
172 m_bConfirmed = false;
173 return CGUIDialogBoxBase::OnBack(actionID);
174 }
175
Reset()176 void CGUIDialogSelect::Reset()
177 {
178 m_bButtonEnabled = false;
179 m_bButtonPressed = false;
180 m_bButton2Enabled = false;
181 m_bButton2Pressed = false;
182
183 m_useDetails = false;
184 m_multiSelection = false;
185 m_focusToButton = false;
186 m_selectedItem = nullptr;
187 m_vecList->Clear();
188 m_selectedItems.clear();
189 }
190
Add(const std::string & strLabel)191 int CGUIDialogSelect::Add(const std::string& strLabel)
192 {
193 CFileItemPtr pItem(new CFileItem(strLabel));
194 m_vecList->Add(pItem);
195 return m_vecList->Size() - 1;
196 }
197
Add(const CFileItem & item)198 int CGUIDialogSelect::Add(const CFileItem& item)
199 {
200 m_vecList->Add(CFileItemPtr(new CFileItem(item)));
201 return m_vecList->Size() - 1;
202 }
203
SetItems(const CFileItemList & pList)204 void CGUIDialogSelect::SetItems(const CFileItemList& pList)
205 {
206 // need to make internal copy of list to be sure dialog is owner of it
207 m_vecList->Clear();
208 m_vecList->Copy(pList);
209
210 m_viewControl.SetItems(*m_vecList);
211 }
212
GetSelectedItem() const213 int CGUIDialogSelect::GetSelectedItem() const
214 {
215 return m_selectedItems.size() > 0 ? m_selectedItems[0] : -1;
216 }
217
GetSelectedFileItem() const218 const CFileItemPtr CGUIDialogSelect::GetSelectedFileItem() const
219 {
220 if (m_selectedItem)
221 return m_selectedItem;
222 return CFileItemPtr(new CFileItem);
223 }
224
GetSelectedItems() const225 const std::vector<int>& CGUIDialogSelect::GetSelectedItems() const
226 {
227 return m_selectedItems;
228 }
229
EnableButton(bool enable,int label)230 void CGUIDialogSelect::EnableButton(bool enable, int label)
231 {
232 m_bButtonEnabled = enable;
233 m_buttonLabel = g_localizeStrings.Get(label);
234 }
235
EnableButton(bool enable,const std::string & label)236 void CGUIDialogSelect::EnableButton(bool enable, const std::string& label)
237 {
238 m_bButtonEnabled = enable;
239 m_buttonLabel = label;
240 }
241
EnableButton2(bool enable,int label)242 void CGUIDialogSelect::EnableButton2(bool enable, int label)
243 {
244 m_bButton2Enabled = enable;
245 m_button2Label = g_localizeStrings.Get(label);
246 }
247
EnableButton2(bool enable,const std::string & label)248 void CGUIDialogSelect::EnableButton2(bool enable, const std::string& label)
249 {
250 m_bButton2Enabled = enable;
251 m_button2Label = label;
252 }
253
IsButtonPressed()254 bool CGUIDialogSelect::IsButtonPressed()
255 {
256 return m_bButtonPressed;
257 }
258
IsButton2Pressed()259 bool CGUIDialogSelect::IsButton2Pressed()
260 {
261 return m_bButton2Pressed;
262 }
263
Sort(bool bSortOrder)264 void CGUIDialogSelect::Sort(bool bSortOrder /*=true*/)
265 {
266 m_vecList->Sort(SortByLabel, bSortOrder ? SortOrderAscending : SortOrderDescending);
267 }
268
SetSelected(int iSelected)269 void CGUIDialogSelect::SetSelected(int iSelected)
270 {
271 if (iSelected < 0 || iSelected >= m_vecList->Size() ||
272 m_vecList->Get(iSelected).get() == NULL)
273 return;
274
275 // only set m_iSelected if there is no multi-select
276 // or if it doesn't have a valid value yet
277 // or if the current value is bigger than the new one
278 // so that we always focus the item nearest to the beginning of the list
279 if (!m_multiSelection || !m_selectedItem ||
280 (!m_selectedItems.empty() && m_selectedItems.back() > iSelected))
281 m_selectedItem = m_vecList->Get(iSelected);
282 m_vecList->Get(iSelected)->Select(true);
283 m_selectedItems.push_back(iSelected);
284 }
285
SetSelected(const std::string & strSelectedLabel)286 void CGUIDialogSelect::SetSelected(const std::string &strSelectedLabel)
287 {
288 for (int index = 0; index < m_vecList->Size(); index++)
289 {
290 if (strSelectedLabel == m_vecList->Get(index)->GetLabel())
291 {
292 SetSelected(index);
293 return;
294 }
295 }
296 }
297
SetSelected(const std::vector<int> & selectedIndexes)298 void CGUIDialogSelect::SetSelected(const std::vector<int>& selectedIndexes)
299 {
300 for (auto i : selectedIndexes)
301 SetSelected(i);
302 }
303
SetSelected(const std::vector<std::string> & selectedLabels)304 void CGUIDialogSelect::SetSelected(const std::vector<std::string> &selectedLabels)
305 {
306 for (const auto& label : selectedLabels)
307 SetSelected(label);
308 }
309
SetUseDetails(bool useDetails)310 void CGUIDialogSelect::SetUseDetails(bool useDetails)
311 {
312 m_useDetails = useDetails;
313 }
314
SetMultiSelection(bool multiSelection)315 void CGUIDialogSelect::SetMultiSelection(bool multiSelection)
316 {
317 m_multiSelection = multiSelection;
318 }
319
SetButtonFocus(bool buttonFocus)320 void CGUIDialogSelect::SetButtonFocus(bool buttonFocus)
321 {
322 m_focusToButton = buttonFocus;
323 }
324
GetFirstFocusableControl(int id)325 CGUIControl *CGUIDialogSelect::GetFirstFocusableControl(int id)
326 {
327 if (m_viewControl.HasControl(id))
328 id = m_viewControl.GetCurrentControl();
329 return CGUIDialogBoxBase::GetFirstFocusableControl(id);
330 }
331
OnWindowLoaded()332 void CGUIDialogSelect::OnWindowLoaded()
333 {
334 CGUIDialogBoxBase::OnWindowLoaded();
335 m_viewControl.Reset();
336 m_viewControl.SetParentWindow(GetID());
337 m_viewControl.AddView(GetControl(CONTROL_SIMPLE_LIST));
338 m_viewControl.AddView(GetControl(CONTROL_DETAILED_LIST));
339 }
340
OnInitWindow()341 void CGUIDialogSelect::OnInitWindow()
342 {
343 m_viewControl.SetItems(*m_vecList);
344 m_selectedItems.clear();
345 for(int i = 0 ; i < m_vecList->Size(); i++)
346 {
347 auto item = m_vecList->Get(i);
348 if (item->IsSelected())
349 {
350 m_selectedItems.push_back(i);
351 if (m_selectedItem == nullptr)
352 m_selectedItem = item;
353 }
354 }
355 m_viewControl.SetCurrentView(m_useDetails ? CONTROL_DETAILED_LIST : CONTROL_SIMPLE_LIST);
356
357 SET_CONTROL_LABEL(CONTROL_NUMBER_OF_ITEMS, StringUtils::Format("%i %s",
358 m_vecList->Size(), g_localizeStrings.Get(127).c_str()));
359
360 if (m_multiSelection)
361 EnableButton(true, 186);
362
363 if (m_bButtonEnabled)
364 {
365 SET_CONTROL_LABEL(CONTROL_EXTRA_BUTTON, m_buttonLabel);
366 SET_CONTROL_VISIBLE(CONTROL_EXTRA_BUTTON);
367 }
368 else
369 SET_CONTROL_HIDDEN(CONTROL_EXTRA_BUTTON);
370
371 if (m_bButton2Enabled)
372 {
373 SET_CONTROL_LABEL(CONTROL_EXTRA_BUTTON2, m_button2Label);
374 SET_CONTROL_VISIBLE(CONTROL_EXTRA_BUTTON2);
375 }
376 else
377 SET_CONTROL_HIDDEN(CONTROL_EXTRA_BUTTON2);
378
379 SET_CONTROL_LABEL(CONTROL_CANCEL_BUTTON, g_localizeStrings.Get(222));
380
381 CGUIDialogBoxBase::OnInitWindow();
382
383 // focus one of the buttons if explicitly requested
384 // ATTENTION: this must be done after calling CGUIDialogBoxBase::OnInitWindow()
385 if (m_focusToButton)
386 {
387 if (m_bButtonEnabled)
388 SET_CONTROL_FOCUS(CONTROL_EXTRA_BUTTON, 0);
389 else
390 SET_CONTROL_FOCUS(CONTROL_CANCEL_BUTTON, 0);
391 }
392
393 // if nothing is selected, select first item
394 m_viewControl.SetSelectedItem(std::max(GetSelectedItem(), 0));
395 }
396
OnDeinitWindow(int nextWindowID)397 void CGUIDialogSelect::OnDeinitWindow(int nextWindowID)
398 {
399 m_viewControl.Clear();
400 CGUIDialogBoxBase::OnDeinitWindow(nextWindowID);
401 }
402
OnWindowUnload()403 void CGUIDialogSelect::OnWindowUnload()
404 {
405 CGUIDialogBoxBase::OnWindowUnload();
406 m_viewControl.Reset();
407 }
408