blob: 8b580449477b2f309398ee15fbab941d02ff0952 [file] [log] [blame]
Dees_Troy51a0e822012-09-05 15:24:24 -04001// FileSelector.cpp - GUIFileSelector object
2
3#include <linux/input.h>
4#include <pthread.h>
5#include <stdarg.h>
6#include <stdio.h>
7#include <stdlib.h>
8#include <string.h>
9#include <fcntl.h>
10#include <sys/reboot.h>
11#include <sys/stat.h>
12#include <sys/time.h>
13#include <sys/mman.h>
14#include <sys/types.h>
15#include <sys/ioctl.h>
16#include <time.h>
17#include <unistd.h>
18#include <stdlib.h>
19#include <dirent.h>
20#include <ctype.h>
21
22#include <algorithm>
23
24extern "C" {
25#include "../common.h"
26#include "../roots.h"
27#include "../minuitwrp/minui.h"
28#include "../recovery_ui.h"
29}
30
31#include "rapidxml.hpp"
32#include "objects.hpp"
33#include "../data.hpp"
Dees_Troy3ee47bc2013-01-25 21:47:37 +000034#include "../twrp-functions.hpp"
Dees_Troy51a0e822012-09-05 15:24:24 -040035
36#define TW_FILESELECTOR_UP_A_LEVEL "(Up A Level)"
37
38#define SCROLLING_SPEED_DECREMENT 6
39#define SCROLLING_FLOOR 10
40#define SCROLLING_MULTIPLIER 6
41
42int GUIFileSelector::mSortOrder = 0;
43
44GUIFileSelector::GUIFileSelector(xml_node<>* node)
45{
46 xml_attribute<>* attr;
47 xml_node<>* child;
48 int header_separator_color_specified = 0, header_separator_height_specified = 0, header_text_color_specified = 0, header_background_color_specified = 0;
49
50 mStart = mLineSpacing = startY = mFontHeight = mSeparatorH = scrollingY = scrollingSpeed = 0;
51 mIconWidth = mIconHeight = mFolderIconHeight = mFileIconHeight = mFolderIconWidth = mFileIconWidth = mHeaderIconHeight = mHeaderIconWidth = 0;
52 mHeaderSeparatorH = mLineHeight = mHeaderIsStatic = mHeaderH = actualLineHeight = 0;
53 mFolderIcon = mFileIcon = mBackground = mFont = mHeaderIcon = NULL;
54 mBackgroundX = mBackgroundY = mBackgroundW = mBackgroundH = 0;
55 mShowFolders = mShowFiles = mShowNavFolders = 1;
Vojtech Bocek7cc278b2013-02-24 01:40:19 +010056 mFastScrollW = mFastScrollLineW = mFastScrollRectW = mFastScrollRectH = 0;
57 mFastScrollRectX = mFastScrollRectY = -1;
Dees_Troy51a0e822012-09-05 15:24:24 -040058 mUpdate = 0;
59 touchDebounce = 6;
60 mPathVar = "cwd";
61 ConvertStrToColor("black", &mBackgroundColor);
62 ConvertStrToColor("black", &mHeaderBackgroundColor);
63 ConvertStrToColor("black", &mSeparatorColor);
64 ConvertStrToColor("black", &mHeaderSeparatorColor);
65 ConvertStrToColor("white", &mFontColor);
66 ConvertStrToColor("white", &mHeaderFontColor);
Vojtech Bocek7cc278b2013-02-24 01:40:19 +010067 ConvertStrToColor("white", &mFastScrollLineColor);
68 ConvertStrToColor("white", &mFastScrollRectColor);
Dees_Troye7585ca2013-02-15 11:42:29 -060069 hasHighlightColor = false;
70 hasFontHighlightColor = false;
71 isHighlighted = false;
Dees_Troyc0583f52013-02-28 11:19:57 -060072 updateFileList = false;
Dees_Troye7585ca2013-02-15 11:42:29 -060073 startSelection = -1;
Dees_Troy51a0e822012-09-05 15:24:24 -040074
75 // Load header text
76 child = node->first_node("header");
77 if (child)
78 {
79 attr = child->first_attribute("icon");
80 if (attr)
81 mHeaderIcon = PageManager::FindResource(attr->value());
82
83 attr = child->first_attribute("background");
84 if (attr)
85 {
86 std::string color = attr->value();
87 ConvertStrToColor(color, &mHeaderBackgroundColor);
88 header_background_color_specified = -1;
89 }
90 attr = child->first_attribute("textcolor");
91 if (attr)
92 {
93 std::string color = attr->value();
94 ConvertStrToColor(color, &mHeaderFontColor);
95 header_text_color_specified = -1;
96 }
97 attr = child->first_attribute("separatorcolor");
98 if (attr)
99 {
100 std::string color = attr->value();
101 ConvertStrToColor(color, &mHeaderSeparatorColor);
102 header_separator_color_specified = -1;
103 }
104 attr = child->first_attribute("separatorheight");
105 if (attr) {
106 string parsevalue = gui_parse_text(attr->value());
107 mHeaderSeparatorH = atoi(parsevalue.c_str());
108 header_separator_height_specified = -1;
109 }
110 }
111 child = node->first_node("text");
112 if (child) mHeaderText = child->value();
113
Dees_Troye7585ca2013-02-15 11:42:29 -0600114 memset(&mHighlightColor, 0, sizeof(COLOR));
115 child = node->first_node("highlight");
116 if (child) {
117 attr = child->first_attribute("color");
118 if (attr) {
119 hasHighlightColor = true;
120 std::string color = attr->value();
121 ConvertStrToColor(color, &mHighlightColor);
122 }
123 }
124
Dees_Troy51a0e822012-09-05 15:24:24 -0400125 // Simple way to check for static state
126 mLastValue = gui_parse_text(mHeaderText);
127 if (mLastValue != mHeaderText)
128 mHeaderIsStatic = 0;
129 else
130 mHeaderIsStatic = -1;
131
132 child = node->first_node("icon");
133 if (child)
134 {
135 attr = child->first_attribute("folder");
136 if (attr)
137 mFolderIcon = PageManager::FindResource(attr->value());
138 attr = child->first_attribute("file");
139 if (attr)
140 mFileIcon = PageManager::FindResource(attr->value());
141 }
142 child = node->first_node("background");
143 if (child)
144 {
145 attr = child->first_attribute("resource");
146 if (attr)
147 mBackground = PageManager::FindResource(attr->value());
148 attr = child->first_attribute("color");
149 if (attr)
150 {
151 std::string color = attr->value();
152 ConvertStrToColor(color, &mBackgroundColor);
153 if (!header_background_color_specified)
154 ConvertStrToColor(color, &mHeaderBackgroundColor);
155 }
156 }
157
158 // Load the placement
159 LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
160 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
161
162 // Load the font, and possibly override the color
163 child = node->first_node("font");
164 if (child)
165 {
166 attr = child->first_attribute("resource");
167 if (attr)
168 mFont = PageManager::FindResource(attr->value());
169
170 attr = child->first_attribute("color");
171 if (attr)
172 {
173 std::string color = attr->value();
174 ConvertStrToColor(color, &mFontColor);
175 if (!header_text_color_specified)
176 ConvertStrToColor(color, &mHeaderFontColor);
177 }
178
179 attr = child->first_attribute("spacing");
180 if (attr) {
181 string parsevalue = gui_parse_text(attr->value());
182 mLineSpacing = atoi(parsevalue.c_str());
183 }
Dees_Troye7585ca2013-02-15 11:42:29 -0600184
185 attr = child->first_attribute("highlightcolor");
186 memset(&mFontHighlightColor, 0, sizeof(COLOR));
187 if (attr)
188 {
189 std::string color = attr->value();
190 ConvertStrToColor(color, &mFontHighlightColor);
191 hasFontHighlightColor = true;
192 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400193 }
194
195 // Load the separator if it exists
196 child = node->first_node("separator");
197 if (child)
198 {
199 attr = child->first_attribute("color");
200 if (attr)
201 {
202 std::string color = attr->value();
203 ConvertStrToColor(color, &mSeparatorColor);
204 if (!header_separator_color_specified)
205 ConvertStrToColor(color, &mHeaderSeparatorColor);
206 }
207
208 attr = child->first_attribute("height");
209 if (attr) {
210 string parsevalue = gui_parse_text(attr->value());
211 mSeparatorH = atoi(parsevalue.c_str());
212 if (!header_separator_height_specified)
213 mHeaderSeparatorH = mSeparatorH;
214 }
215 }
216
217 child = node->first_node("filter");
218 if (child)
219 {
220 attr = child->first_attribute("extn");
221 if (attr)
222 mExtn = attr->value();
223 attr = child->first_attribute("folders");
224 if (attr)
225 mShowFolders = atoi(attr->value());
226 attr = child->first_attribute("files");
227 if (attr)
228 mShowFiles = atoi(attr->value());
229 attr = child->first_attribute("nav");
230 if (attr)
231 mShowNavFolders = atoi(attr->value());
232 }
233
234 // Handle the path variable
235 child = node->first_node("path");
236 if (child)
237 {
238 attr = child->first_attribute("name");
239 if (attr)
240 mPathVar = attr->value();
241 attr = child->first_attribute("default");
242 if (attr)
243 DataManager::SetValue(mPathVar, attr->value());
244 }
245
246 // Handle the result variable
247 child = node->first_node("data");
248 if (child)
249 {
250 attr = child->first_attribute("name");
251 if (attr)
252 mVariable = attr->value();
253 attr = child->first_attribute("default");
254 if (attr)
255 DataManager::SetValue(mVariable, attr->value());
256 }
257
258 // Handle the sort variable
259 child = node->first_node("sort");
260 if (child)
261 {
262 attr = child->first_attribute("name");
263 if (attr)
264 mSortVariable = attr->value();
265 attr = child->first_attribute("default");
266 if (attr)
267 DataManager::SetValue(mSortVariable, attr->value());
268
269 DataManager::GetValue(mSortVariable, mSortOrder);
270 }
271
272 // Handle the selection variable
273 child = node->first_node("selection");
274 if (child)
275 {
276 attr = child->first_attribute("name");
277 if (attr)
278 mSelection = attr->value();
279 else
280 mSelection = "0";
281 } else
282 mSelection = "0";
283
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100284 // Fast scroll colors
285 child = node->first_node("fastscroll");
286 if (child)
287 {
288 attr = child->first_attribute("linecolor");
289 if(attr)
290 ConvertStrToColor(attr->value(), &mFastScrollLineColor);
291
292 attr = child->first_attribute("rectcolor");
293 if(attr)
294 ConvertStrToColor(attr->value(), &mFastScrollRectColor);
295
296 attr = child->first_attribute("w");
297 if (attr) {
298 string parsevalue = gui_parse_text(attr->value());
299 mFastScrollW = atoi(parsevalue.c_str());
300 }
301
302 attr = child->first_attribute("linew");
303 if (attr) {
304 string parsevalue = gui_parse_text(attr->value());
305 mFastScrollLineW = atoi(parsevalue.c_str());
306 }
307
308 attr = child->first_attribute("rectw");
309 if (attr) {
310 string parsevalue = gui_parse_text(attr->value());
311 mFastScrollRectW = atoi(parsevalue.c_str());
312 }
313
314 attr = child->first_attribute("recth");
315 if (attr) {
316 string parsevalue = gui_parse_text(attr->value());
317 mFastScrollRectH = atoi(parsevalue.c_str());
318 }
319 }
320
Dees_Troy51a0e822012-09-05 15:24:24 -0400321 // Retrieve the line height
322 gr_getFontDetails(mFont ? mFont->GetResource() : NULL, &mFontHeight, NULL);
323 mLineHeight = mFontHeight;
324 mHeaderH = mFontHeight;
325
326 if (mFolderIcon && mFolderIcon->GetResource())
327 {
328 mFolderIconWidth = gr_get_width(mFolderIcon->GetResource());
329 mFolderIconHeight = gr_get_height(mFolderIcon->GetResource());
330 if (mFolderIconHeight > (int)mLineHeight)
331 mLineHeight = mFolderIconHeight;
332 mIconWidth = mFolderIconWidth;
333 }
334
335 if (mFileIcon && mFileIcon->GetResource())
336 {
337 mFileIconWidth = gr_get_width(mFileIcon->GetResource());
338 mFileIconHeight = gr_get_height(mFileIcon->GetResource());
339 if (mFileIconHeight > (int)mLineHeight)
340 mLineHeight = mFileIconHeight;
341 if (mFileIconWidth > mIconWidth)
342 mIconWidth = mFileIconWidth;
343 }
344
345 if (mHeaderIcon && mHeaderIcon->GetResource())
346 {
347 mHeaderIconWidth = gr_get_width(mHeaderIcon->GetResource());
348 mHeaderIconHeight = gr_get_height(mHeaderIcon->GetResource());
349 if (mHeaderIconHeight > mHeaderH)
350 mHeaderH = mHeaderIconHeight;
351 if (mHeaderIconWidth > mIconWidth)
352 mIconWidth = mHeaderIconWidth;
353 }
354
355 mHeaderH += mLineSpacing + mHeaderSeparatorH;
356 actualLineHeight = mLineHeight + mLineSpacing + mSeparatorH;
357 if (mHeaderH < actualLineHeight)
358 mHeaderH = actualLineHeight;
359
360 if (actualLineHeight / 3 > 6)
361 touchDebounce = actualLineHeight / 3;
362
363 if (mBackground && mBackground->GetResource())
364 {
365 mBackgroundW = gr_get_width(mBackground->GetResource());
366 mBackgroundH = gr_get_height(mBackground->GetResource());
367 }
368
369 // Fetch the file/folder list
370 std::string value;
371 DataManager::GetValue(mPathVar, value);
Dees_Troy80a11d92013-01-25 16:36:07 +0000372 GetFileList(value);
Dees_Troy51a0e822012-09-05 15:24:24 -0400373}
374
375GUIFileSelector::~GUIFileSelector()
376{
377}
378
379int GUIFileSelector::Render(void)
380{
381 // First step, fill background
382 gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
383 gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
384
385 // Next, render the background resource (if it exists)
386 if (mBackground && mBackground->GetResource())
387 {
388 mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
389 mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
390 gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
391 }
392
Dees_Troy4622cf92013-03-01 15:29:36 -0600393 // Update the file list if needed
Dees_Troy4622cf92013-03-01 15:29:36 -0600394 if (updateFileList) {
Dees_Troy4622cf92013-03-01 15:29:36 -0600395 string value;
396 DataManager::GetValue(mPathVar, value);
397 if (GetFileList(value) == 0) {
Dees_Troy4622cf92013-03-01 15:29:36 -0600398 updateFileList = false;
Dees_Troy4622cf92013-03-01 15:29:36 -0600399 } else {
400 return 0;
401 }
Dees_Troy4622cf92013-03-01 15:29:36 -0600402 }
403
Dees_Troy51a0e822012-09-05 15:24:24 -0400404 // This tells us how many lines we can actually render
405 int lines = (mRenderH - mHeaderH) / (actualLineHeight);
406 int line;
407
408 int folderSize = mShowFolders ? mFolderList.size() : 0;
409 int fileSize = mShowFiles ? mFileList.size() : 0;
410
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100411 int listW = mRenderW;
412
Dees_Troy51a0e822012-09-05 15:24:24 -0400413 if (folderSize + fileSize < lines) {
414 lines = folderSize + fileSize;
415 scrollingY = 0;
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100416 mFastScrollRectX = mFastScrollRectY = -1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400417 } else {
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100418 listW -= mFastScrollW; // space for fast scroll
Dees_Troy51a0e822012-09-05 15:24:24 -0400419 lines++;
420 if (lines < folderSize + fileSize)
421 lines++;
422 }
423
424 void* fontResource = NULL;
425 if (mFont) fontResource = mFont->GetResource();
426
427 int yPos = mRenderY + mHeaderH + scrollingY;
428 int fontOffsetY = (int)((actualLineHeight - mFontHeight) / 2);
429 int currentIconHeight = 0, currentIconWidth = 0;
430 int currentIconOffsetY = 0, currentIconOffsetX = 0;
431 int folderIconOffsetY = (int)((actualLineHeight - mFolderIconHeight) / 2), fileIconOffsetY = (int)((actualLineHeight - mFileIconHeight) / 2);
432 int folderIconOffsetX = (mIconWidth - mFolderIconWidth) / 2, fileIconOffsetX = (mIconWidth - mFileIconWidth) / 2;
Dees_Troye7585ca2013-02-15 11:42:29 -0600433 int actualSelection = mStart;
434
435 if (isHighlighted) {
436 int selectY = scrollingY;
437
438 // Locate the correct line for highlighting
439 while (selectY + actualLineHeight < startSelection) {
440 selectY += actualLineHeight;
441 actualSelection++;
442 }
443 if (hasHighlightColor) {
444 // Highlight the area
445 gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, 255);
446 int HighlightHeight = actualLineHeight;
447 if (mRenderY + mHeaderH + selectY + actualLineHeight > mRenderH + mRenderY) {
448 HighlightHeight = actualLineHeight - (mRenderY + mHeaderH + selectY + actualLineHeight - mRenderH - mRenderY);
449 }
450 gr_fill(mRenderX, mRenderY + mHeaderH + selectY, mRenderW, HighlightHeight);
451 }
452 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400453
454 for (line = 0; line < lines; line++)
455 {
456 Resource* icon;
457 std::string label;
458
Dees_Troye7585ca2013-02-15 11:42:29 -0600459 if (isHighlighted && hasFontHighlightColor && line + mStart == actualSelection) {
460 // Use the highlight color for the font
461 gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, 255);
462 } else {
463 // Set the color for the font
464 gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, 255);
465 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400466
467 if (line + mStart < folderSize)
468 {
469 icon = mFolderIcon;
470 label = mFolderList.at(line + mStart).fileName;
471 currentIconHeight = mFolderIconHeight;
472 currentIconWidth = mFolderIconWidth;
473 currentIconOffsetY = folderIconOffsetY;
474 currentIconOffsetX = folderIconOffsetX;
475 }
476 else if (line + mStart < folderSize + fileSize)
477 {
478 icon = mFileIcon;
479 label = mFileList.at((line + mStart) - folderSize).fileName;
480 currentIconHeight = mFileIconHeight;
481 currentIconWidth = mFileIconWidth;
482 currentIconOffsetY = fileIconOffsetY;
483 currentIconOffsetX = fileIconOffsetX;
484 } else {
485 continue;
486 }
487
488 if (icon && icon->GetResource())
489 {
490 int rect_y = 0, image_y = (yPos + currentIconOffsetY);
491 if (image_y + currentIconHeight > mRenderY + mRenderH)
492 rect_y = mRenderY + mRenderH - image_y;
493 else
494 rect_y = currentIconHeight;
495 gr_blit(icon->GetResource(), 0, 0, currentIconWidth, rect_y, mRenderX + currentIconOffsetX, image_y);
496 }
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100497 gr_textExWH(mRenderX + mIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource, mRenderX + listW, mRenderY + mRenderH);
Dees_Troy51a0e822012-09-05 15:24:24 -0400498
499 // Add the separator
500 if (yPos + actualLineHeight < mRenderH + mRenderY) {
501 gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, 255);
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100502 gr_fill(mRenderX, yPos + actualLineHeight - mSeparatorH, listW, mSeparatorH);
Dees_Troy51a0e822012-09-05 15:24:24 -0400503 }
504
505 // Move the yPos
506 yPos += actualLineHeight;
507 }
508
509 // Render the Header (last so that it overwrites the top most row for per pixel scrolling)
510 // First step, fill background
511 gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, 255);
512 gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
513
514 // Now, we need the header (icon + text)
515 yPos = mRenderY;
516 {
517 Resource* headerIcon;
518 int mIconOffsetX = 0;
519
520 // render the icon if it exists
521 headerIcon = mHeaderIcon;
522 if (headerIcon && headerIcon->GetResource())
523 {
524 gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - mIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
525 mIconOffsetX = mIconWidth;
526 }
527
528 // render the text
529 gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, 255);
530 gr_textExWH(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastValue.c_str(), fontResource, mRenderX + mRenderW, mRenderY + mRenderH);
531
532 // Add the separator
533 gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, 255);
534 gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
535 }
536
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100537 // render fast scroll
538 lines = (mRenderH - mHeaderH) / (actualLineHeight);
539 if(mFastScrollW > 0 && folderSize + fileSize > lines)
540 {
541 int startX = listW + mRenderX;
542 int fWidth = mRenderW - listW;
543 int fHeight = mRenderH - mHeaderH;
544
545 // line
546 gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, 255);
547 gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
548
549 // rect
550 int pct = ((mStart*actualLineHeight - scrollingY)*100)/((folderSize + fileSize)*actualLineHeight-lines*actualLineHeight);
551 mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
552 mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
553
554 gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, 255);
555 gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
556 }
557
Dees_Troy4622cf92013-03-01 15:29:36 -0600558 // 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 -0600559 if (!updateFileList) {
560 mUpdate = 0;
561 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400562 return 0;
563}
564
565int GUIFileSelector::Update(void)
566{
567 if (!mHeaderIsStatic) {
568 std::string newValue = gui_parse_text(mHeaderText);
569 if (mLastValue != newValue) {
570 mLastValue = newValue;
571 mUpdate = 1;
572 }
573 }
574
575 if (mUpdate)
576 {
577 mUpdate = 0;
578 if (Render() == 0)
579 return 2;
580 }
581
582 // Handle kinetic scrolling
583 if (scrollingSpeed == 0) {
584 // Do nothing
585 } else if (scrollingSpeed > 0) {
586 if (scrollingSpeed < ((int) (actualLineHeight * 2.5))) {
587 scrollingY += scrollingSpeed;
588 scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
589 } else {
590 scrollingY += ((int) (actualLineHeight * 2.5));
591 scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
592 }
593 while (mStart && scrollingY > 0) {
594 mStart--;
595 scrollingY -= actualLineHeight;
596 }
597 if (mStart == 0 && scrollingY > 0) {
598 scrollingY = 0;
599 scrollingSpeed = 0;
600 } else if (scrollingSpeed < SCROLLING_FLOOR)
601 scrollingSpeed = 0;
602 mUpdate = 1;
603 } else if (scrollingSpeed < 0) {
604 int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
605 int lines = (mRenderH - mHeaderH) / (actualLineHeight);
606
607 if (totalSize > lines) {
608 int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
609
610 bottom_offset -= actualLineHeight;
611
612 if (abs(scrollingSpeed) < ((int) (actualLineHeight * 2.5))) {
613 scrollingY += scrollingSpeed;
614 scrollingSpeed += SCROLLING_SPEED_DECREMENT;
615 } else {
616 scrollingY -= ((int) (actualLineHeight * 2.5));
617 scrollingSpeed += SCROLLING_SPEED_DECREMENT;
618 }
619 while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
620 mStart++;
621 scrollingY += actualLineHeight;
622 }
623 if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
624 mStart = totalSize - lines - 1;
625 scrollingY = bottom_offset;
626 } else if (mStart + lines >= totalSize && scrollingY < 0) {
627 mStart = totalSize - lines;
628 scrollingY = 0;
629 } else if (scrollingSpeed * -1 < SCROLLING_FLOOR)
630 scrollingSpeed = 0;
631 mUpdate = 1;
632 }
633 }
634
635 return 0;
636}
637
638int GUIFileSelector::GetSelection(int x, int y)
639{
640 // We only care about y position
641 if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH) return -1;
642 return (y - mRenderY - mHeaderH);
643}
644
645int GUIFileSelector::NotifyTouch(TOUCH_STATE state, int x, int y)
646{
Dees_Troy51a0e822012-09-05 15:24:24 -0400647 static int lastY = 0, last2Y = 0;
648 int selection = 0;
649
650 switch (state)
651 {
652 case TOUCH_START:
653 if (scrollingSpeed != 0)
654 startSelection = -1;
655 else
656 startSelection = GetSelection(x,y);
Dees_Troye7585ca2013-02-15 11:42:29 -0600657 isHighlighted = (startSelection > -1);
658 if (isHighlighted)
659 mUpdate = 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400660 startY = lastY = last2Y = y;
661 scrollingSpeed = 0;
662 break;
663
664 case TOUCH_DRAG:
665 // Check if we dragged out of the selection window
666 if (GetSelection(x, y) == -1) {
667 last2Y = lastY = 0;
Dees_Troye7585ca2013-02-15 11:42:29 -0600668 if (isHighlighted) {
669 isHighlighted = false;
670 mUpdate = 1;
671 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400672 break;
673 }
674
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100675 // Fast scroll
676 if(mFastScrollRectX != -1 && x >= mRenderX + mRenderW - mFastScrollW)
677 {
678 int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
679 int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
680 int lines = (mRenderH - mHeaderH) / (actualLineHeight);
681
682 float l = float((totalSize-lines)*pct)/100;
683 if(l + lines >= totalSize)
684 {
685 mStart = totalSize - lines;
686 scrollingY = 0;
687 }
688 else
689 {
690 mStart = l;
691 scrollingY = -(l - int(l))*actualLineHeight;
692 }
693
694 startSelection = -1;
695 mUpdate = 1;
696 scrollingSpeed = 0;
697 isHighlighted = false;
698 break;
699 }
700
Dees_Troy51a0e822012-09-05 15:24:24 -0400701 // Provide some debounce on initial touches
702 if (startSelection != -1 && abs(y - startY) < touchDebounce) {
Dees_Troye7585ca2013-02-15 11:42:29 -0600703 isHighlighted = true;
704 mUpdate = 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400705 break;
706 }
707
Dees_Troye7585ca2013-02-15 11:42:29 -0600708 isHighlighted = false;
Dees_Troy51a0e822012-09-05 15:24:24 -0400709 last2Y = lastY;
710 lastY = y;
711 startSelection = -1;
712
713 // Handle scrolling
714 scrollingY += y - startY;
715 startY = y;
716 while(mStart && scrollingY > 0) {
717 mStart--;
718 scrollingY -= actualLineHeight;
719 }
720 if (mStart == 0 && scrollingY > 0)
721 scrollingY = 0;
722 {
723 int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
724 int lines = (mRenderH - mHeaderH) / (actualLineHeight);
725
726 if (totalSize > lines) {
727 int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
728
729 bottom_offset -= actualLineHeight;
730
731 while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
732 mStart++;
733 scrollingY += actualLineHeight;
734 }
735 if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
736 mStart = totalSize - lines - 1;
737 scrollingY = bottom_offset;
738 } else if (mStart + lines >= totalSize && scrollingY < 0) {
739 mStart = totalSize - lines;
740 scrollingY = 0;
741 }
742 } else
743 scrollingY = 0;
744 }
745 mUpdate = 1;
746 break;
747
748 case TOUCH_RELEASE:
Dees_Troye7585ca2013-02-15 11:42:29 -0600749 isHighlighted = false;
Dees_Troy51a0e822012-09-05 15:24:24 -0400750 if (startSelection >= 0)
751 {
752 // We've selected an item!
753 std::string str;
754
755 int folderSize = mShowFolders ? mFolderList.size() : 0;
756 int fileSize = mShowFiles ? mFileList.size() : 0;
757 int selectY = scrollingY, actualSelection = mStart;
758
759 // Move the selection to the proper place in the array
760 while (selectY + actualLineHeight < startSelection) {
761 selectY += actualLineHeight;
762 actualSelection++;
763 }
764 startSelection = actualSelection;
765
766 if (startSelection < folderSize + fileSize)
767 {
768 if (startSelection < folderSize)
769 {
770 std::string oldcwd;
771 std::string cwd;
772
773 str = mFolderList.at(startSelection).fileName;
774 if (mSelection != "0")
775 DataManager::SetValue(mSelection, str);
776 DataManager::GetValue(mPathVar, cwd);
777
778 oldcwd = cwd;
779 // Ignore requests to do nothing
780 if (str == ".") return 0;
781 if (str == TW_FILESELECTOR_UP_A_LEVEL)
782 {
783 if (cwd != "/")
784 {
785 size_t found;
786 found = cwd.find_last_of('/');
787 cwd = cwd.substr(0,found);
788
789 if (cwd.length() < 2) cwd = "/";
790 }
791 }
792 else
793 {
794 // Add a slash if we're not the root folder
795 if (cwd != "/") cwd += "/";
796 cwd += str;
797 }
798
799 if (mShowNavFolders == 0 && mShowFiles == 0)
800 {
801 // This is a "folder" selection
802 DataManager::SetValue(mVariable, cwd);
803 }
804 else
805 {
806 DataManager::SetValue(mPathVar, cwd);
Dees_Troy51a0e822012-09-05 15:24:24 -0400807 mStart = 0;
808 scrollingY = 0;
809 mUpdate = 1;
810 }
811 }
812 else if (!mVariable.empty())
813 {
814 str = mFileList.at(startSelection - folderSize).fileName;
815 if (mSelection != "0")
816 DataManager::SetValue(mSelection, str);
817
818 std::string cwd;
819 DataManager::GetValue(mPathVar, cwd);
820 if (cwd != "/") cwd += "/";
821 DataManager::SetValue(mVariable, cwd + str);
822 }
823 }
824 } else {
825 // This is for kinetic scrolling
826 scrollingSpeed = lastY - last2Y;
827 if (abs(scrollingSpeed) > SCROLLING_FLOOR)
828 scrollingSpeed *= SCROLLING_MULTIPLIER;
829 else
830 scrollingSpeed = 0;
831 }
832 case TOUCH_REPEAT:
833 case TOUCH_HOLD:
834 break;
835 }
836 return 0;
837}
838
839int GUIFileSelector::NotifyVarChange(std::string varName, std::string value)
840{
Dees_Troy146d72a2013-03-11 17:46:19 +0000841 if (varName.empty()) {
842 // Always clear the data variable so we know to use it
843 DataManager::SetValue(mVariable, "");
844 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400845 if (!mHeaderIsStatic) {
846 std::string newValue = gui_parse_text(mHeaderText);
847 if (mLastValue != newValue) {
848 mLastValue = newValue;
849 mStart = 0;
850 scrollingY = 0;
851 scrollingSpeed = 0;
852 mUpdate = 1;
853 }
854 }
855 if (varName == mPathVar || varName == mSortVariable)
856 {
Dees_Troy4622cf92013-03-01 15:29:36 -0600857 if (varName == mSortVariable) {
858 DataManager::GetValue(mSortVariable, mSortOrder);
Dees_Troyc0583f52013-02-28 11:19:57 -0600859 }
860 updateFileList = true;
Dees_Troy51a0e822012-09-05 15:24:24 -0400861 mStart = 0;
862 scrollingY = 0;
863 scrollingSpeed = 0;
Dees_Troy51a0e822012-09-05 15:24:24 -0400864 mUpdate = 1;
865 return 0;
866 }
867 return 0;
868}
869
870int GUIFileSelector::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
871{
872 mRenderX = x;
873 mRenderY = y;
874 if (w || h)
875 {
876 mRenderW = w;
877 mRenderH = h;
878 }
879 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
880 mUpdate = 1;
881 return 0;
882}
883
884bool GUIFileSelector::fileSort(FileData d1, FileData d2)
885{
886 if (d1.fileName == ".")
887 return -1;
888 if (d2.fileName == ".")
889 return 0;
890 if (d1.fileName == TW_FILESELECTOR_UP_A_LEVEL)
891 return -1;
892 if (d2.fileName == TW_FILESELECTOR_UP_A_LEVEL)
893 return 0;
894
895 switch (mSortOrder) {
896 case 3: // by size largest first
897 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
898 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600899 return d1.fileSize < d2.fileSize;
Dees_Troy51a0e822012-09-05 15:24:24 -0400900 case -3: // by size smallest first
901 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
902 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600903 return d1.fileSize > d2.fileSize;
Dees_Troy51a0e822012-09-05 15:24:24 -0400904 case 2: // by last modified date newest first
905 if (d1.lastModified == d2.lastModified)
906 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600907 return d1.lastModified < d2.lastModified;
Dees_Troy51a0e822012-09-05 15:24:24 -0400908 case -2: // by date oldest first
909 if (d1.lastModified == d2.lastModified)
910 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600911 return d1.lastModified > d2.lastModified;
Dees_Troy51a0e822012-09-05 15:24:24 -0400912 case -1: // by name descending
913 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
914 default: // should be a 1 - sort by name ascending
915 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
916 }
917}
918
919int GUIFileSelector::GetFileList(const std::string folder)
920{
921 DIR* d;
922 struct dirent* de;
923 struct stat st;
924
925 // Clear all data
926 mFolderList.clear();
927 mFileList.clear();
928
929 d = opendir(folder.c_str());
930 if (d == NULL)
931 {
932 LOGI("Unable to open '%s'\n", folder.c_str());
Dees_Troy80a11d92013-01-25 16:36:07 +0000933 if (folder != "/" && (mShowNavFolders != 0 || mShowFiles != 0)) {
934 size_t found;
935 found = folder.find_last_of('/');
936 if (found != string::npos) {
937 string new_folder = folder.substr(0, found);
938
939 if (new_folder.length() < 2)
940 new_folder = "/";
941 DataManager::SetValue(mPathVar, new_folder);
942 }
943 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400944 return -1;
945 }
946
947 while ((de = readdir(d)) != NULL)
948 {
949 FileData data;
950
951 data.fileName = de->d_name;
952 if (data.fileName == ".")
953 continue;
954 if (data.fileName == ".." && folder == "/")
955 continue;
Dees_Troy3ee47bc2013-01-25 21:47:37 +0000956 if (data.fileName == "..") {
Dees_Troy51a0e822012-09-05 15:24:24 -0400957 data.fileName = TW_FILESELECTOR_UP_A_LEVEL;
Dees_Troy3ee47bc2013-01-25 21:47:37 +0000958 data.fileType = DT_DIR;
959 } else {
960 data.fileType = de->d_type;
961 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400962
963 std::string path = folder + "/" + data.fileName;
964 stat(path.c_str(), &st);
965 data.protection = st.st_mode;
966 data.userId = st.st_uid;
967 data.groupId = st.st_gid;
968 data.fileSize = st.st_size;
969 data.lastAccess = st.st_atime;
970 data.lastModified = st.st_mtime;
971 data.lastStatChange = st.st_ctime;
972
Dees_Troy3ee47bc2013-01-25 21:47:37 +0000973 if (data.fileType == DT_UNKNOWN) {
974 data.fileType = TWFunc::Get_D_Type_From_Stat(path);
975 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400976 if (data.fileType == DT_DIR)
977 {
978 if (mShowNavFolders || (data.fileName != "." && data.fileName != TW_FILESELECTOR_UP_A_LEVEL))
979 mFolderList.push_back(data);
980 }
Dees_Troyaf4d0ce2012-09-26 20:19:06 -0400981 else if (data.fileType == DT_REG || data.fileType == DT_LNK || data.fileType == DT_BLK)
Dees_Troy51a0e822012-09-05 15:24:24 -0400982 {
983 if (mExtn.empty() || (data.fileName.length() > mExtn.length() && data.fileName.substr(data.fileName.length() - mExtn.length()) == mExtn))
984 {
985 mFileList.push_back(data);
986 }
987 }
988 }
989 closedir(d);
990
991 std::sort(mFolderList.begin(), mFolderList.end(), fileSort);
992 std::sort(mFileList.begin(), mFileList.end(), fileSort);
Dees_Troyc0583f52013-02-28 11:19:57 -0600993
Dees_Troy51a0e822012-09-05 15:24:24 -0400994 return 0;
995}
996
997void GUIFileSelector::SetPageFocus(int inFocus)
998{
999 if (inFocus)
1000 {
Dees_Troyc0583f52013-02-28 11:19:57 -06001001 updateFileList = true;
1002 scrollingY = 0;
1003 scrollingSpeed = 0;
1004 mUpdate = 1;
Dees_Troy51a0e822012-09-05 15:24:24 -04001005 }
Dees_Troy4622cf92013-03-01 15:29:36 -06001006}