blob: cf7a9a9081f3b29f312aad9a9a26dc0a7d5d0920 [file] [log] [blame]
Dees_Troya13d74f2013-03-24 08:54:55 -05001/*
2 Copyright 2012 bigbiff/Dees_Troy TeamWin
3 This file is part of TWRP/TeamWin Recovery Project.
4
5 TWRP is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 TWRP is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with TWRP. If not, see <http://www.gnu.org/licenses/>.
17*/
Dees_Troy51a0e822012-09-05 15:24:24 -040018
19#include <linux/input.h>
20#include <pthread.h>
21#include <stdarg.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <fcntl.h>
26#include <sys/reboot.h>
27#include <sys/stat.h>
28#include <sys/time.h>
29#include <sys/mman.h>
30#include <sys/types.h>
31#include <sys/ioctl.h>
32#include <time.h>
33#include <unistd.h>
34#include <stdlib.h>
35#include <dirent.h>
36#include <ctype.h>
37
38#include <algorithm>
39
40extern "C" {
Dees_Troy2673cec2013-04-02 20:22:16 +000041#include "../twcommon.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040042#include "../minuitwrp/minui.h"
Dees_Troy51a0e822012-09-05 15:24:24 -040043}
44
45#include "rapidxml.hpp"
46#include "objects.hpp"
47#include "../data.hpp"
Dees_Troy3ee47bc2013-01-25 21:47:37 +000048#include "../twrp-functions.hpp"
Dees_Troy51a0e822012-09-05 15:24:24 -040049
50#define TW_FILESELECTOR_UP_A_LEVEL "(Up A Level)"
51
52#define SCROLLING_SPEED_DECREMENT 6
53#define SCROLLING_FLOOR 10
54#define SCROLLING_MULTIPLIER 6
55
56int GUIFileSelector::mSortOrder = 0;
57
Vojtech Bocekede51c52014-02-07 23:58:09 +010058GUIFileSelector::GUIFileSelector(xml_node<>* node) : GUIObject(node)
Dees_Troy51a0e822012-09-05 15:24:24 -040059{
60 xml_attribute<>* attr;
61 xml_node<>* child;
62 int header_separator_color_specified = 0, header_separator_height_specified = 0, header_text_color_specified = 0, header_background_color_specified = 0;
63
64 mStart = mLineSpacing = startY = mFontHeight = mSeparatorH = scrollingY = scrollingSpeed = 0;
65 mIconWidth = mIconHeight = mFolderIconHeight = mFileIconHeight = mFolderIconWidth = mFileIconWidth = mHeaderIconHeight = mHeaderIconWidth = 0;
66 mHeaderSeparatorH = mLineHeight = mHeaderIsStatic = mHeaderH = actualLineHeight = 0;
67 mFolderIcon = mFileIcon = mBackground = mFont = mHeaderIcon = NULL;
68 mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = 0;
69 mShowFolders = mShowFiles = mShowNavFolders = 1;
Vojtech Bocek7cc278b2013-02-24 01:40:19 +010070 mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0;
71 mFastScrollRectX = mFastScrollRectY = -1;
Dees_Troy51a0e822012-09-05 15:24:24 -040072 mUpdate = 0;
73 touchDebounce = 6;
74 mPathVar = "cwd";
75 ConvertStrToColor("black", &mBackgroundColor);
76 ConvertStrToColor("black", &mHeaderBackgroundColor);
77 ConvertStrToColor("black", &mSeparatorColor);
78 ConvertStrToColor("black", &mHeaderSeparatorColor);
79 ConvertStrToColor("white", &mFontColor);
80 ConvertStrToColor("white", &mHeaderFontColor);
Vojtech Bocek7cc278b2013-02-24 01:40:19 +010081 ConvertStrToColor("white", &mFastScrollLineColor);
82 ConvertStrToColor("white", &mFastScrollRectColor);
Dees_Troye7585ca2013-02-15 11:42:29 -060083 hasHighlightColor = false;
84 hasFontHighlightColor = false;
85 isHighlighted = false;
Dees_Troyc0583f52013-02-28 11:19:57 -060086 updateFileList = false;
Dees_Troye7585ca2013-02-15 11:42:29 -060087 startSelection = -1;
Dees_Troy51a0e822012-09-05 15:24:24 -040088
89 // Load header text
90 child = node->first_node("header");
91 if (child)
92 {
93 attr = child->first_attribute("icon");
94 if (attr)
95 mHeaderIcon = PageManager::FindResource(attr->value());
96
97 attr = child->first_attribute("background");
98 if (attr)
99 {
100 std::string color = attr->value();
101 ConvertStrToColor(color, &mHeaderBackgroundColor);
102 header_background_color_specified = -1;
103 }
104 attr = child->first_attribute("textcolor");
105 if (attr)
106 {
107 std::string color = attr->value();
108 ConvertStrToColor(color, &mHeaderFontColor);
109 header_text_color_specified = -1;
110 }
111 attr = child->first_attribute("separatorcolor");
112 if (attr)
113 {
114 std::string color = attr->value();
115 ConvertStrToColor(color, &mHeaderSeparatorColor);
116 header_separator_color_specified = -1;
117 }
118 attr = child->first_attribute("separatorheight");
119 if (attr) {
120 string parsevalue = gui_parse_text(attr->value());
121 mHeaderSeparatorH = atoi(parsevalue.c_str());
122 header_separator_height_specified = -1;
123 }
124 }
125 child = node->first_node("text");
126 if (child) mHeaderText = child->value();
127
Dees_Troye7585ca2013-02-15 11:42:29 -0600128 memset(&mHighlightColor, 0, sizeof(COLOR));
129 child = node->first_node("highlight");
130 if (child) {
131 attr = child->first_attribute("color");
132 if (attr) {
133 hasHighlightColor = true;
134 std::string color = attr->value();
135 ConvertStrToColor(color, &mHighlightColor);
136 }
137 }
138
Dees_Troy51a0e822012-09-05 15:24:24 -0400139 // Simple way to check for static state
140 mLastValue = gui_parse_text(mHeaderText);
141 if (mLastValue != mHeaderText)
142 mHeaderIsStatic = 0;
143 else
144 mHeaderIsStatic = -1;
145
146 child = node->first_node("icon");
147 if (child)
148 {
149 attr = child->first_attribute("folder");
150 if (attr)
151 mFolderIcon = PageManager::FindResource(attr->value());
152 attr = child->first_attribute("file");
153 if (attr)
154 mFileIcon = PageManager::FindResource(attr->value());
155 }
156 child = node->first_node("background");
157 if (child)
158 {
159 attr = child->first_attribute("resource");
160 if (attr)
161 mBackground = PageManager::FindResource(attr->value());
162 attr = child->first_attribute("color");
163 if (attr)
164 {
165 std::string color = attr->value();
166 ConvertStrToColor(color, &mBackgroundColor);
167 if (!header_background_color_specified)
168 ConvertStrToColor(color, &mHeaderBackgroundColor);
169 }
170 }
171
172 // Load the placement
173 LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
174 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
175
176 // Load the font, and possibly override the color
177 child = node->first_node("font");
178 if (child)
179 {
180 attr = child->first_attribute("resource");
181 if (attr)
182 mFont = PageManager::FindResource(attr->value());
183
184 attr = child->first_attribute("color");
185 if (attr)
186 {
187 std::string color = attr->value();
188 ConvertStrToColor(color, &mFontColor);
189 if (!header_text_color_specified)
190 ConvertStrToColor(color, &mHeaderFontColor);
191 }
192
193 attr = child->first_attribute("spacing");
194 if (attr) {
195 string parsevalue = gui_parse_text(attr->value());
196 mLineSpacing = atoi(parsevalue.c_str());
197 }
Dees_Troye7585ca2013-02-15 11:42:29 -0600198
199 attr = child->first_attribute("highlightcolor");
200 memset(&mFontHighlightColor, 0, sizeof(COLOR));
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200201 if (attr)
202 {
203 std::string color = attr->value();
Dees_Troye7585ca2013-02-15 11:42:29 -0600204 ConvertStrToColor(color, &mFontHighlightColor);
205 hasFontHighlightColor = true;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200206 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400207 }
208
209 // Load the separator if it exists
210 child = node->first_node("separator");
211 if (child)
212 {
213 attr = child->first_attribute("color");
214 if (attr)
215 {
216 std::string color = attr->value();
217 ConvertStrToColor(color, &mSeparatorColor);
218 if (!header_separator_color_specified)
219 ConvertStrToColor(color, &mHeaderSeparatorColor);
220 }
221
222 attr = child->first_attribute("height");
223 if (attr) {
224 string parsevalue = gui_parse_text(attr->value());
225 mSeparatorH = atoi(parsevalue.c_str());
226 if (!header_separator_height_specified)
227 mHeaderSeparatorH = mSeparatorH;
228 }
229 }
230
231 child = node->first_node("filter");
232 if (child)
233 {
234 attr = child->first_attribute("extn");
235 if (attr)
236 mExtn = attr->value();
237 attr = child->first_attribute("folders");
238 if (attr)
239 mShowFolders = atoi(attr->value());
240 attr = child->first_attribute("files");
241 if (attr)
242 mShowFiles = atoi(attr->value());
243 attr = child->first_attribute("nav");
244 if (attr)
245 mShowNavFolders = atoi(attr->value());
246 }
247
248 // Handle the path variable
249 child = node->first_node("path");
250 if (child)
251 {
252 attr = child->first_attribute("name");
253 if (attr)
254 mPathVar = attr->value();
255 attr = child->first_attribute("default");
256 if (attr)
257 DataManager::SetValue(mPathVar, attr->value());
258 }
259
260 // Handle the result variable
261 child = node->first_node("data");
262 if (child)
263 {
264 attr = child->first_attribute("name");
265 if (attr)
266 mVariable = attr->value();
267 attr = child->first_attribute("default");
268 if (attr)
269 DataManager::SetValue(mVariable, attr->value());
270 }
271
272 // Handle the sort variable
273 child = node->first_node("sort");
274 if (child)
275 {
276 attr = child->first_attribute("name");
277 if (attr)
278 mSortVariable = attr->value();
279 attr = child->first_attribute("default");
280 if (attr)
281 DataManager::SetValue(mSortVariable, attr->value());
282
283 DataManager::GetValue(mSortVariable, mSortOrder);
284 }
285
286 // Handle the selection variable
287 child = node->first_node("selection");
288 if (child)
289 {
290 attr = child->first_attribute("name");
291 if (attr)
292 mSelection = attr->value();
293 else
294 mSelection = "0";
295 } else
296 mSelection = "0";
297
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100298 // Fast scroll colors
299 child = node->first_node("fastscroll");
300 if (child)
301 {
302 attr = child->first_attribute("linecolor");
303 if(attr)
304 ConvertStrToColor(attr->value(), &mFastScrollLineColor);
305
306 attr = child->first_attribute("rectcolor");
307 if(attr)
308 ConvertStrToColor(attr->value(), &mFastScrollRectColor);
309
310 attr = child->first_attribute("w");
311 if (attr) {
312 string parsevalue = gui_parse_text(attr->value());
313 mFastScrollW = atoi(parsevalue.c_str());
314 }
315
316 attr = child->first_attribute("linew");
317 if (attr) {
318 string parsevalue = gui_parse_text(attr->value());
319 mFastScrollLineW = atoi(parsevalue.c_str());
320 }
321
322 attr = child->first_attribute("rectw");
323 if (attr) {
324 string parsevalue = gui_parse_text(attr->value());
325 mFastScrollRectW = atoi(parsevalue.c_str());
326 }
327
328 attr = child->first_attribute("recth");
329 if (attr) {
330 string parsevalue = gui_parse_text(attr->value());
331 mFastScrollRectH = atoi(parsevalue.c_str());
332 }
333 }
334
Dees_Troy51a0e822012-09-05 15:24:24 -0400335 // Retrieve the line height
Vojtech Bocek76ee9032014-09-07 15:01:56 +0200336 mFontHeight = gr_getMaxFontHeight(mFont ? mFont->GetResource() : NULL);
Dees_Troy51a0e822012-09-05 15:24:24 -0400337 mLineHeight = mFontHeight;
338 mHeaderH = mFontHeight;
339
340 if (mFolderIcon && mFolderIcon->GetResource())
341 {
342 mFolderIconWidth = gr_get_width(mFolderIcon->GetResource());
343 mFolderIconHeight = gr_get_height(mFolderIcon->GetResource());
344 if (mFolderIconHeight > (int)mLineHeight)
345 mLineHeight = mFolderIconHeight;
346 mIconWidth = mFolderIconWidth;
347 }
348
349 if (mFileIcon && mFileIcon->GetResource())
350 {
351 mFileIconWidth = gr_get_width(mFileIcon->GetResource());
352 mFileIconHeight = gr_get_height(mFileIcon->GetResource());
353 if (mFileIconHeight > (int)mLineHeight)
354 mLineHeight = mFileIconHeight;
355 if (mFileIconWidth > mIconWidth)
356 mIconWidth = mFileIconWidth;
357 }
358
359 if (mHeaderIcon && mHeaderIcon->GetResource())
360 {
361 mHeaderIconWidth = gr_get_width(mHeaderIcon->GetResource());
362 mHeaderIconHeight = gr_get_height(mHeaderIcon->GetResource());
363 if (mHeaderIconHeight > mHeaderH)
364 mHeaderH = mHeaderIconHeight;
365 if (mHeaderIconWidth > mIconWidth)
366 mIconWidth = mHeaderIconWidth;
367 }
368
369 mHeaderH += mLineSpacing + mHeaderSeparatorH;
370 actualLineHeight = mLineHeight + mLineSpacing + mSeparatorH;
371 if (mHeaderH < actualLineHeight)
372 mHeaderH = actualLineHeight;
373
374 if (actualLineHeight / 3 > 6)
375 touchDebounce = actualLineHeight / 3;
376
377 if (mBackground && mBackground->GetResource())
378 {
379 mBackgroundW = gr_get_width(mBackground->GetResource());
380 mBackgroundH = gr_get_height(mBackground->GetResource());
381 }
382
383 // Fetch the file/folder list
384 std::string value;
385 DataManager::GetValue(mPathVar, value);
Dees_Troy80a11d92013-01-25 16:36:07 +0000386 GetFileList(value);
Dees_Troy51a0e822012-09-05 15:24:24 -0400387}
388
389GUIFileSelector::~GUIFileSelector()
390{
391}
392
393int GUIFileSelector::Render(void)
394{
Vojtech Bocekede51c52014-02-07 23:58:09 +0100395 if(!isConditionTrue())
396 return 0;
397
Dees_Troy51a0e822012-09-05 15:24:24 -0400398 // First step, fill background
399 gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
400 gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
401
402 // Next, render the background resource (if it exists)
403 if (mBackground && mBackground->GetResource())
404 {
405 mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
406 mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
407 gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
408 }
409
Dees_Troy4622cf92013-03-01 15:29:36 -0600410 // Update the file list if needed
Dees_Troy4622cf92013-03-01 15:29:36 -0600411 if (updateFileList) {
Dees_Troy4622cf92013-03-01 15:29:36 -0600412 string value;
413 DataManager::GetValue(mPathVar, value);
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200414 if (GetFileList(value) == 0)
Dees_Troy4622cf92013-03-01 15:29:36 -0600415 updateFileList = false;
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200416 else
Dees_Troy4622cf92013-03-01 15:29:36 -0600417 return 0;
Dees_Troy4622cf92013-03-01 15:29:36 -0600418 }
419
Dees_Troy51a0e822012-09-05 15:24:24 -0400420 // This tells us how many lines we can actually render
421 int lines = (mRenderH - mHeaderH) / (actualLineHeight);
422 int line;
423
424 int folderSize = mShowFolders ? mFolderList.size() : 0;
425 int fileSize = mShowFiles ? mFileList.size() : 0;
426
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100427 int listW = mRenderW;
428
Dees_Troy51a0e822012-09-05 15:24:24 -0400429 if (folderSize + fileSize < lines) {
430 lines = folderSize + fileSize;
431 scrollingY = 0;
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100432 mFastScrollRectX = mFastScrollRectY = -1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400433 } else {
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100434 listW -= mFastScrollW; // space for fast scroll
Dees_Troy51a0e822012-09-05 15:24:24 -0400435 lines++;
436 if (lines < folderSize + fileSize)
437 lines++;
438 }
439
440 void* fontResource = NULL;
441 if (mFont) fontResource = mFont->GetResource();
442
443 int yPos = mRenderY + mHeaderH + scrollingY;
444 int fontOffsetY = (int)((actualLineHeight - mFontHeight) / 2);
445 int currentIconHeight = 0, currentIconWidth = 0;
446 int currentIconOffsetY = 0, currentIconOffsetX = 0;
447 int folderIconOffsetY = (int)((actualLineHeight - mFolderIconHeight) / 2), fileIconOffsetY = (int)((actualLineHeight - mFileIconHeight) / 2);
448 int folderIconOffsetX = (mIconWidth - mFolderIconWidth) / 2, fileIconOffsetX = (mIconWidth - mFileIconWidth) / 2;
Dees_Troye7585ca2013-02-15 11:42:29 -0600449 int actualSelection = mStart;
450
451 if (isHighlighted) {
452 int selectY = scrollingY;
453
454 // Locate the correct line for highlighting
455 while (selectY + actualLineHeight < startSelection) {
456 selectY += actualLineHeight;
457 actualSelection++;
458 }
459 if (hasHighlightColor) {
460 // Highlight the area
461 gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, 255);
462 int HighlightHeight = actualLineHeight;
463 if (mRenderY + mHeaderH + selectY + actualLineHeight > mRenderH + mRenderY) {
464 HighlightHeight = actualLineHeight - (mRenderY + mHeaderH + selectY + actualLineHeight - mRenderH - mRenderY);
465 }
466 gr_fill(mRenderX, mRenderY + mHeaderH + selectY, mRenderW, HighlightHeight);
467 }
468 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400469
470 for (line = 0; line < lines; line++)
471 {
472 Resource* icon;
473 std::string label;
474
Dees_Troye7585ca2013-02-15 11:42:29 -0600475 if (isHighlighted && hasFontHighlightColor && line + mStart == actualSelection) {
476 // Use the highlight color for the font
477 gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, 255);
478 } else {
479 // Set the color for the font
480 gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, 255);
481 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400482
483 if (line + mStart < folderSize)
484 {
485 icon = mFolderIcon;
486 label = mFolderList.at(line + mStart).fileName;
487 currentIconHeight = mFolderIconHeight;
488 currentIconWidth = mFolderIconWidth;
489 currentIconOffsetY = folderIconOffsetY;
490 currentIconOffsetX = folderIconOffsetX;
491 }
492 else if (line + mStart < folderSize + fileSize)
493 {
494 icon = mFileIcon;
495 label = mFileList.at((line + mStart) - folderSize).fileName;
496 currentIconHeight = mFileIconHeight;
497 currentIconWidth = mFileIconWidth;
498 currentIconOffsetY = fileIconOffsetY;
499 currentIconOffsetX = fileIconOffsetX;
500 } else {
501 continue;
502 }
503
504 if (icon && icon->GetResource())
505 {
506 int rect_y = 0, image_y = (yPos + currentIconOffsetY);
507 if (image_y + currentIconHeight > mRenderY + mRenderH)
508 rect_y = mRenderY + mRenderH - image_y;
509 else
510 rect_y = currentIconHeight;
511 gr_blit(icon->GetResource(), 0, 0, currentIconWidth, rect_y, mRenderX + currentIconOffsetX, image_y);
512 }
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100513 gr_textExWH(mRenderX + mIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource, mRenderX + listW, mRenderY + mRenderH);
Dees_Troy51a0e822012-09-05 15:24:24 -0400514
515 // Add the separator
516 if (yPos + actualLineHeight < mRenderH + mRenderY) {
517 gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, 255);
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100518 gr_fill(mRenderX, yPos + actualLineHeight - mSeparatorH, listW, mSeparatorH);
Dees_Troy51a0e822012-09-05 15:24:24 -0400519 }
520
521 // Move the yPos
522 yPos += actualLineHeight;
523 }
524
525 // Render the Header (last so that it overwrites the top most row for per pixel scrolling)
526 // First step, fill background
527 gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, 255);
528 gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
529
530 // Now, we need the header (icon + text)
531 yPos = mRenderY;
532 {
533 Resource* headerIcon;
534 int mIconOffsetX = 0;
535
536 // render the icon if it exists
537 headerIcon = mHeaderIcon;
538 if (headerIcon && headerIcon->GetResource())
539 {
540 gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - mIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
541 mIconOffsetX = mIconWidth;
542 }
543
544 // render the text
545 gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, 255);
546 gr_textExWH(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastValue.c_str(), fontResource, mRenderX + mRenderW, mRenderY + mRenderH);
547
548 // Add the separator
549 gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, 255);
550 gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
551 }
552
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100553 // render fast scroll
554 lines = (mRenderH - mHeaderH) / (actualLineHeight);
555 if(mFastScrollW > 0 && folderSize + fileSize > lines)
556 {
557 int startX = listW + mRenderX;
558 int fWidth = mRenderW - listW;
559 int fHeight = mRenderH - mHeaderH;
560
561 // line
562 gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, 255);
563 gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
564
565 // rect
566 int pct = ((mStart*actualLineHeight - scrollingY)*100)/((folderSize + fileSize)*actualLineHeight-lines*actualLineHeight);
567 mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
568 mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
569
570 gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, 255);
571 gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
572 }
573
Dees_Troy4622cf92013-03-01 15:29:36 -0600574 // If a change came in during the render then we need to do another redraw so leave mUpdate alone if updateFileList is true.
Dees_Troy4622cf92013-03-01 15:29:36 -0600575 if (!updateFileList) {
576 mUpdate = 0;
577 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400578 return 0;
579}
580
581int GUIFileSelector::Update(void)
582{
Vojtech Bocekede51c52014-02-07 23:58:09 +0100583 if(!isConditionTrue())
584 return 0;
585
Dees_Troy51a0e822012-09-05 15:24:24 -0400586 if (!mHeaderIsStatic) {
587 std::string newValue = gui_parse_text(mHeaderText);
588 if (mLastValue != newValue) {
589 mLastValue = newValue;
590 mUpdate = 1;
591 }
592 }
593
594 if (mUpdate)
595 {
596 mUpdate = 0;
597 if (Render() == 0)
598 return 2;
599 }
600
601 // Handle kinetic scrolling
602 if (scrollingSpeed == 0) {
603 // Do nothing
604 } else if (scrollingSpeed > 0) {
605 if (scrollingSpeed < ((int) (actualLineHeight * 2.5))) {
606 scrollingY += scrollingSpeed;
607 scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
608 } else {
609 scrollingY += ((int) (actualLineHeight * 2.5));
610 scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
611 }
612 while (mStart && scrollingY > 0) {
613 mStart--;
614 scrollingY -= actualLineHeight;
615 }
616 if (mStart == 0 && scrollingY > 0) {
617 scrollingY = 0;
618 scrollingSpeed = 0;
619 } else if (scrollingSpeed < SCROLLING_FLOOR)
620 scrollingSpeed = 0;
621 mUpdate = 1;
622 } else if (scrollingSpeed < 0) {
623 int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
624 int lines = (mRenderH - mHeaderH) / (actualLineHeight);
625
626 if (totalSize > lines) {
627 int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
628
629 bottom_offset -= actualLineHeight;
630
631 if (abs(scrollingSpeed) < ((int) (actualLineHeight * 2.5))) {
632 scrollingY += scrollingSpeed;
633 scrollingSpeed += SCROLLING_SPEED_DECREMENT;
634 } else {
635 scrollingY -= ((int) (actualLineHeight * 2.5));
636 scrollingSpeed += SCROLLING_SPEED_DECREMENT;
637 }
638 while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
639 mStart++;
640 scrollingY += actualLineHeight;
641 }
642 if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
643 mStart = totalSize - lines - 1;
644 scrollingY = bottom_offset;
645 } else if (mStart + lines >= totalSize && scrollingY < 0) {
646 mStart = totalSize - lines;
647 scrollingY = 0;
648 } else if (scrollingSpeed * -1 < SCROLLING_FLOOR)
649 scrollingSpeed = 0;
650 mUpdate = 1;
651 }
652 }
653
654 return 0;
655}
656
657int GUIFileSelector::GetSelection(int x, int y)
658{
659 // We only care about y position
Vojtech Bocekfafb0c52013-07-25 22:53:02 +0200660 if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH)
661 return -1;
662
Dees_Troy51a0e822012-09-05 15:24:24 -0400663 return (y - mRenderY - mHeaderH);
664}
665
666int GUIFileSelector::NotifyTouch(TOUCH_STATE state, int x, int y)
667{
Vojtech Bocekede51c52014-02-07 23:58:09 +0100668 if(!isConditionTrue())
669 return -1;
670
Vojtech Bocek59e51a42014-01-29 19:11:15 +0100671 static int lastY = 0, last2Y = 0, fastScroll = 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400672 int selection = 0;
673
674 switch (state)
675 {
676 case TOUCH_START:
677 if (scrollingSpeed != 0)
678 startSelection = -1;
679 else
680 startSelection = GetSelection(x,y);
Dees_Troye7585ca2013-02-15 11:42:29 -0600681 isHighlighted = (startSelection > -1);
682 if (isHighlighted)
683 mUpdate = 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400684 startY = lastY = last2Y = y;
685 scrollingSpeed = 0;
Vojtech Bocek59e51a42014-01-29 19:11:15 +0100686
687 if(mFastScrollRectX != -1 && x >= mRenderX + mRenderW - mFastScrollW)
688 fastScroll = 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400689 break;
Dees_Troy51a0e822012-09-05 15:24:24 -0400690 case TOUCH_DRAG:
691 // Check if we dragged out of the selection window
692 if (GetSelection(x, y) == -1) {
693 last2Y = lastY = 0;
Dees_Troye7585ca2013-02-15 11:42:29 -0600694 if (isHighlighted) {
695 isHighlighted = false;
696 mUpdate = 1;
697 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400698 break;
699 }
700
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100701 // Fast scroll
Vojtech Bocek59e51a42014-01-29 19:11:15 +0100702 if(fastScroll)
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100703 {
704 int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
705 int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
706 int lines = (mRenderH - mHeaderH) / (actualLineHeight);
707
708 float l = float((totalSize-lines)*pct)/100;
709 if(l + lines >= totalSize)
710 {
711 mStart = totalSize - lines;
712 scrollingY = 0;
713 }
714 else
715 {
716 mStart = l;
717 scrollingY = -(l - int(l))*actualLineHeight;
718 }
719
720 startSelection = -1;
721 mUpdate = 1;
722 scrollingSpeed = 0;
723 isHighlighted = false;
724 break;
725 }
726
Dees_Troy51a0e822012-09-05 15:24:24 -0400727 // Provide some debounce on initial touches
728 if (startSelection != -1 && abs(y - startY) < touchDebounce) {
Dees_Troye7585ca2013-02-15 11:42:29 -0600729 isHighlighted = true;
730 mUpdate = 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400731 break;
732 }
733
Dees_Troye7585ca2013-02-15 11:42:29 -0600734 isHighlighted = false;
Dees_Troy51a0e822012-09-05 15:24:24 -0400735 last2Y = lastY;
Matt Mowerfb1c4ff2014-04-16 13:43:36 -0500736 lastY = y;
Dees_Troy51a0e822012-09-05 15:24:24 -0400737 startSelection = -1;
738
739 // Handle scrolling
740 scrollingY += y - startY;
741 startY = y;
742 while(mStart && scrollingY > 0) {
743 mStart--;
744 scrollingY -= actualLineHeight;
745 }
746 if (mStart == 0 && scrollingY > 0)
747 scrollingY = 0;
748 {
749 int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
750 int lines = (mRenderH - mHeaderH) / (actualLineHeight);
751
752 if (totalSize > lines) {
753 int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
754
755 bottom_offset -= actualLineHeight;
756
757 while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
758 mStart++;
759 scrollingY += actualLineHeight;
760 }
761 if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
762 mStart = totalSize - lines - 1;
763 scrollingY = bottom_offset;
764 } else if (mStart + lines >= totalSize && scrollingY < 0) {
765 mStart = totalSize - lines;
766 scrollingY = 0;
767 }
768 } else
769 scrollingY = 0;
770 }
771 mUpdate = 1;
772 break;
773
774 case TOUCH_RELEASE:
Dees_Troye7585ca2013-02-15 11:42:29 -0600775 isHighlighted = false;
Vojtech Bocek59e51a42014-01-29 19:11:15 +0100776 fastScroll = 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400777 if (startSelection >= 0)
778 {
779 // We've selected an item!
780 std::string str;
781
782 int folderSize = mShowFolders ? mFolderList.size() : 0;
783 int fileSize = mShowFiles ? mFileList.size() : 0;
784 int selectY = scrollingY, actualSelection = mStart;
785
786 // Move the selection to the proper place in the array
787 while (selectY + actualLineHeight < startSelection) {
788 selectY += actualLineHeight;
789 actualSelection++;
790 }
791 startSelection = actualSelection;
792
793 if (startSelection < folderSize + fileSize)
794 {
Vojtech Bocek5af8f3f2014-02-08 02:21:23 +0100795 DataManager::Vibrate("tw_button_vibrate");
796
Dees_Troy51a0e822012-09-05 15:24:24 -0400797 if (startSelection < folderSize)
798 {
799 std::string oldcwd;
800 std::string cwd;
801
802 str = mFolderList.at(startSelection).fileName;
803 if (mSelection != "0")
804 DataManager::SetValue(mSelection, str);
805 DataManager::GetValue(mPathVar, cwd);
806
807 oldcwd = cwd;
808 // Ignore requests to do nothing
809 if (str == ".") return 0;
810 if (str == TW_FILESELECTOR_UP_A_LEVEL)
811 {
812 if (cwd != "/")
813 {
814 size_t found;
815 found = cwd.find_last_of('/');
816 cwd = cwd.substr(0,found);
817
818 if (cwd.length() < 2) cwd = "/";
819 }
820 }
821 else
822 {
823 // Add a slash if we're not the root folder
824 if (cwd != "/") cwd += "/";
825 cwd += str;
826 }
827
828 if (mShowNavFolders == 0 && mShowFiles == 0)
829 {
830 // This is a "folder" selection
831 DataManager::SetValue(mVariable, cwd);
832 }
833 else
834 {
835 DataManager::SetValue(mPathVar, cwd);
Dees_Troy51a0e822012-09-05 15:24:24 -0400836 mStart = 0;
837 scrollingY = 0;
838 mUpdate = 1;
839 }
840 }
841 else if (!mVariable.empty())
842 {
843 str = mFileList.at(startSelection - folderSize).fileName;
844 if (mSelection != "0")
845 DataManager::SetValue(mSelection, str);
846
847 std::string cwd;
848 DataManager::GetValue(mPathVar, cwd);
849 if (cwd != "/") cwd += "/";
850 DataManager::SetValue(mVariable, cwd + str);
851 }
852 }
853 } else {
854 // This is for kinetic scrolling
855 scrollingSpeed = lastY - last2Y;
856 if (abs(scrollingSpeed) > SCROLLING_FLOOR)
857 scrollingSpeed *= SCROLLING_MULTIPLIER;
858 else
859 scrollingSpeed = 0;
860 }
861 case TOUCH_REPEAT:
862 case TOUCH_HOLD:
863 break;
864 }
865 return 0;
866}
867
Vojtech Bocek07220562014-02-08 02:05:33 +0100868int GUIFileSelector::NotifyVarChange(const std::string& varName, const std::string& value)
Dees_Troy51a0e822012-09-05 15:24:24 -0400869{
Vojtech Bocek07220562014-02-08 02:05:33 +0100870 GUIObject::NotifyVarChange(varName, value);
871
Vojtech Bocekede51c52014-02-07 23:58:09 +0100872 if(!isConditionTrue())
873 return 0;
874
Dees_Troy146d72a2013-03-11 17:46:19 +0000875 if (varName.empty()) {
876 // Always clear the data variable so we know to use it
877 DataManager::SetValue(mVariable, "");
878 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400879 if (!mHeaderIsStatic) {
880 std::string newValue = gui_parse_text(mHeaderText);
881 if (mLastValue != newValue) {
882 mLastValue = newValue;
883 mStart = 0;
884 scrollingY = 0;
885 scrollingSpeed = 0;
886 mUpdate = 1;
887 }
888 }
889 if (varName == mPathVar || varName == mSortVariable)
890 {
Dees_Troy4622cf92013-03-01 15:29:36 -0600891 if (varName == mSortVariable) {
892 DataManager::GetValue(mSortVariable, mSortOrder);
Dees_Troyc0583f52013-02-28 11:19:57 -0600893 }
894 updateFileList = true;
Dees_Troy51a0e822012-09-05 15:24:24 -0400895 mStart = 0;
896 scrollingY = 0;
897 scrollingSpeed = 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400898 mUpdate = 1;
899 return 0;
900 }
901 return 0;
902}
903
904int GUIFileSelector::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
905{
906 mRenderX = x;
907 mRenderY = y;
908 if (w || h)
909 {
910 mRenderW = w;
911 mRenderH = h;
912 }
913 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
914 mUpdate = 1;
915 return 0;
916}
917
918bool GUIFileSelector::fileSort(FileData d1, FileData d2)
919{
920 if (d1.fileName == ".")
921 return -1;
922 if (d2.fileName == ".")
923 return 0;
924 if (d1.fileName == TW_FILESELECTOR_UP_A_LEVEL)
925 return -1;
926 if (d2.fileName == TW_FILESELECTOR_UP_A_LEVEL)
927 return 0;
Matt Mowerfb1c4ff2014-04-16 13:43:36 -0500928
Dees_Troy51a0e822012-09-05 15:24:24 -0400929 switch (mSortOrder) {
930 case 3: // by size largest first
931 if (d1.fileSize == d2.fileSize || d1.fileType == DT_DIR) // some directories report a different size than others - but this is not the size of the files inside the directory, so we just sort by name on directories
932 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600933 return d1.fileSize < d2.fileSize;
Dees_Troy51a0e822012-09-05 15:24:24 -0400934 case -3: // by size smallest first
935 if (d1.fileSize == d2.fileSize || d1.fileType == DT_DIR) // some directories report a different size than others - but this is not the size of the files inside the directory, so we just sort by name on directories
936 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600937 return d1.fileSize > d2.fileSize;
Dees_Troy51a0e822012-09-05 15:24:24 -0400938 case 2: // by last modified date newest first
939 if (d1.lastModified == d2.lastModified)
940 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600941 return d1.lastModified < d2.lastModified;
Dees_Troy51a0e822012-09-05 15:24:24 -0400942 case -2: // by date oldest first
943 if (d1.lastModified == d2.lastModified)
944 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600945 return d1.lastModified > d2.lastModified;
Dees_Troy51a0e822012-09-05 15:24:24 -0400946 case -1: // by name descending
947 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
948 default: // should be a 1 - sort by name ascending
949 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
950 }
951}
952
953int GUIFileSelector::GetFileList(const std::string folder)
954{
955 DIR* d;
956 struct dirent* de;
957 struct stat st;
958
959 // Clear all data
960 mFolderList.clear();
961 mFileList.clear();
962
963 d = opendir(folder.c_str());
964 if (d == NULL)
965 {
Dees_Troy2673cec2013-04-02 20:22:16 +0000966 LOGINFO("Unable to open '%s'\n", folder.c_str());
Dees_Troy80a11d92013-01-25 16:36:07 +0000967 if (folder != "/" && (mShowNavFolders != 0 || mShowFiles != 0)) {
968 size_t found;
969 found = folder.find_last_of('/');
970 if (found != string::npos) {
971 string new_folder = folder.substr(0, found);
972
973 if (new_folder.length() < 2)
974 new_folder = "/";
975 DataManager::SetValue(mPathVar, new_folder);
976 }
977 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400978 return -1;
979 }
980
981 while ((de = readdir(d)) != NULL)
982 {
983 FileData data;
984
985 data.fileName = de->d_name;
986 if (data.fileName == ".")
987 continue;
988 if (data.fileName == ".." && folder == "/")
989 continue;
Dees_Troy3ee47bc2013-01-25 21:47:37 +0000990 if (data.fileName == "..") {
Dees_Troy51a0e822012-09-05 15:24:24 -0400991 data.fileName = TW_FILESELECTOR_UP_A_LEVEL;
Dees_Troy3ee47bc2013-01-25 21:47:37 +0000992 data.fileType = DT_DIR;
993 } else {
994 data.fileType = de->d_type;
995 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400996
997 std::string path = folder + "/" + data.fileName;
998 stat(path.c_str(), &st);
999 data.protection = st.st_mode;
1000 data.userId = st.st_uid;
1001 data.groupId = st.st_gid;
1002 data.fileSize = st.st_size;
1003 data.lastAccess = st.st_atime;
1004 data.lastModified = st.st_mtime;
1005 data.lastStatChange = st.st_ctime;
1006
Dees_Troy3ee47bc2013-01-25 21:47:37 +00001007 if (data.fileType == DT_UNKNOWN) {
1008 data.fileType = TWFunc::Get_D_Type_From_Stat(path);
1009 }
Dees_Troy51a0e822012-09-05 15:24:24 -04001010 if (data.fileType == DT_DIR)
1011 {
1012 if (mShowNavFolders || (data.fileName != "." && data.fileName != TW_FILESELECTOR_UP_A_LEVEL))
1013 mFolderList.push_back(data);
1014 }
Dees_Troyaf4d0ce2012-09-26 20:19:06 -04001015 else if (data.fileType == DT_REG || data.fileType == DT_LNK || data.fileType == DT_BLK)
Dees_Troy51a0e822012-09-05 15:24:24 -04001016 {
1017 if (mExtn.empty() || (data.fileName.length() > mExtn.length() && data.fileName.substr(data.fileName.length() - mExtn.length()) == mExtn))
1018 {
1019 mFileList.push_back(data);
1020 }
1021 }
1022 }
1023 closedir(d);
1024
1025 std::sort(mFolderList.begin(), mFolderList.end(), fileSort);
1026 std::sort(mFileList.begin(), mFileList.end(), fileSort);
Dees_Troyc0583f52013-02-28 11:19:57 -06001027
Dees_Troy51a0e822012-09-05 15:24:24 -04001028 return 0;
1029}
1030
1031void GUIFileSelector::SetPageFocus(int inFocus)
1032{
1033 if (inFocus)
1034 {
Dees_Troyc0583f52013-02-28 11:19:57 -06001035 updateFileList = true;
1036 scrollingY = 0;
1037 scrollingSpeed = 0;
1038 mUpdate = 1;
Dees_Troy51a0e822012-09-05 15:24:24 -04001039 }
Dees_Troya13d74f2013-03-24 08:54:55 -05001040}