blob: edf3279f4d3580125f7e7a777b281aee364e126e [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;
72 startSelection = -1;
Dees_Troy51a0e822012-09-05 15:24:24 -040073
74 // Load header text
75 child = node->first_node("header");
76 if (child)
77 {
78 attr = child->first_attribute("icon");
79 if (attr)
80 mHeaderIcon = PageManager::FindResource(attr->value());
81
82 attr = child->first_attribute("background");
83 if (attr)
84 {
85 std::string color = attr->value();
86 ConvertStrToColor(color, &mHeaderBackgroundColor);
87 header_background_color_specified = -1;
88 }
89 attr = child->first_attribute("textcolor");
90 if (attr)
91 {
92 std::string color = attr->value();
93 ConvertStrToColor(color, &mHeaderFontColor);
94 header_text_color_specified = -1;
95 }
96 attr = child->first_attribute("separatorcolor");
97 if (attr)
98 {
99 std::string color = attr->value();
100 ConvertStrToColor(color, &mHeaderSeparatorColor);
101 header_separator_color_specified = -1;
102 }
103 attr = child->first_attribute("separatorheight");
104 if (attr) {
105 string parsevalue = gui_parse_text(attr->value());
106 mHeaderSeparatorH = atoi(parsevalue.c_str());
107 header_separator_height_specified = -1;
108 }
109 }
110 child = node->first_node("text");
111 if (child) mHeaderText = child->value();
112
Dees_Troye7585ca2013-02-15 11:42:29 -0600113 memset(&mHighlightColor, 0, sizeof(COLOR));
114 child = node->first_node("highlight");
115 if (child) {
116 attr = child->first_attribute("color");
117 if (attr) {
118 hasHighlightColor = true;
119 std::string color = attr->value();
120 ConvertStrToColor(color, &mHighlightColor);
121 }
122 }
123
Dees_Troy51a0e822012-09-05 15:24:24 -0400124 // Simple way to check for static state
125 mLastValue = gui_parse_text(mHeaderText);
126 if (mLastValue != mHeaderText)
127 mHeaderIsStatic = 0;
128 else
129 mHeaderIsStatic = -1;
130
131 child = node->first_node("icon");
132 if (child)
133 {
134 attr = child->first_attribute("folder");
135 if (attr)
136 mFolderIcon = PageManager::FindResource(attr->value());
137 attr = child->first_attribute("file");
138 if (attr)
139 mFileIcon = PageManager::FindResource(attr->value());
140 }
141 child = node->first_node("background");
142 if (child)
143 {
144 attr = child->first_attribute("resource");
145 if (attr)
146 mBackground = PageManager::FindResource(attr->value());
147 attr = child->first_attribute("color");
148 if (attr)
149 {
150 std::string color = attr->value();
151 ConvertStrToColor(color, &mBackgroundColor);
152 if (!header_background_color_specified)
153 ConvertStrToColor(color, &mHeaderBackgroundColor);
154 }
155 }
156
157 // Load the placement
158 LoadPlacement(node->first_node("placement"), &mRenderX, &mRenderY, &mRenderW, &mRenderH);
159 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
160
161 // Load the font, and possibly override the color
162 child = node->first_node("font");
163 if (child)
164 {
165 attr = child->first_attribute("resource");
166 if (attr)
167 mFont = PageManager::FindResource(attr->value());
168
169 attr = child->first_attribute("color");
170 if (attr)
171 {
172 std::string color = attr->value();
173 ConvertStrToColor(color, &mFontColor);
174 if (!header_text_color_specified)
175 ConvertStrToColor(color, &mHeaderFontColor);
176 }
177
178 attr = child->first_attribute("spacing");
179 if (attr) {
180 string parsevalue = gui_parse_text(attr->value());
181 mLineSpacing = atoi(parsevalue.c_str());
182 }
Dees_Troye7585ca2013-02-15 11:42:29 -0600183
184 attr = child->first_attribute("highlightcolor");
185 memset(&mFontHighlightColor, 0, sizeof(COLOR));
186 if (attr)
187 {
188 std::string color = attr->value();
189 ConvertStrToColor(color, &mFontHighlightColor);
190 hasFontHighlightColor = true;
191 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400192 }
193
194 // Load the separator if it exists
195 child = node->first_node("separator");
196 if (child)
197 {
198 attr = child->first_attribute("color");
199 if (attr)
200 {
201 std::string color = attr->value();
202 ConvertStrToColor(color, &mSeparatorColor);
203 if (!header_separator_color_specified)
204 ConvertStrToColor(color, &mHeaderSeparatorColor);
205 }
206
207 attr = child->first_attribute("height");
208 if (attr) {
209 string parsevalue = gui_parse_text(attr->value());
210 mSeparatorH = atoi(parsevalue.c_str());
211 if (!header_separator_height_specified)
212 mHeaderSeparatorH = mSeparatorH;
213 }
214 }
215
216 child = node->first_node("filter");
217 if (child)
218 {
219 attr = child->first_attribute("extn");
220 if (attr)
221 mExtn = attr->value();
222 attr = child->first_attribute("folders");
223 if (attr)
224 mShowFolders = atoi(attr->value());
225 attr = child->first_attribute("files");
226 if (attr)
227 mShowFiles = atoi(attr->value());
228 attr = child->first_attribute("nav");
229 if (attr)
230 mShowNavFolders = atoi(attr->value());
231 }
232
233 // Handle the path variable
234 child = node->first_node("path");
235 if (child)
236 {
237 attr = child->first_attribute("name");
238 if (attr)
239 mPathVar = attr->value();
240 attr = child->first_attribute("default");
241 if (attr)
242 DataManager::SetValue(mPathVar, attr->value());
243 }
244
245 // Handle the result variable
246 child = node->first_node("data");
247 if (child)
248 {
249 attr = child->first_attribute("name");
250 if (attr)
251 mVariable = attr->value();
252 attr = child->first_attribute("default");
253 if (attr)
254 DataManager::SetValue(mVariable, attr->value());
255 }
256
257 // Handle the sort variable
258 child = node->first_node("sort");
259 if (child)
260 {
261 attr = child->first_attribute("name");
262 if (attr)
263 mSortVariable = attr->value();
264 attr = child->first_attribute("default");
265 if (attr)
266 DataManager::SetValue(mSortVariable, attr->value());
267
268 DataManager::GetValue(mSortVariable, mSortOrder);
269 }
270
271 // Handle the selection variable
272 child = node->first_node("selection");
273 if (child)
274 {
275 attr = child->first_attribute("name");
276 if (attr)
277 mSelection = attr->value();
278 else
279 mSelection = "0";
280 } else
281 mSelection = "0";
282
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100283 // Fast scroll colors
284 child = node->first_node("fastscroll");
285 if (child)
286 {
287 attr = child->first_attribute("linecolor");
288 if(attr)
289 ConvertStrToColor(attr->value(), &mFastScrollLineColor);
290
291 attr = child->first_attribute("rectcolor");
292 if(attr)
293 ConvertStrToColor(attr->value(), &mFastScrollRectColor);
294
295 attr = child->first_attribute("w");
296 if (attr) {
297 string parsevalue = gui_parse_text(attr->value());
298 mFastScrollW = atoi(parsevalue.c_str());
299 }
300
301 attr = child->first_attribute("linew");
302 if (attr) {
303 string parsevalue = gui_parse_text(attr->value());
304 mFastScrollLineW = atoi(parsevalue.c_str());
305 }
306
307 attr = child->first_attribute("rectw");
308 if (attr) {
309 string parsevalue = gui_parse_text(attr->value());
310 mFastScrollRectW = atoi(parsevalue.c_str());
311 }
312
313 attr = child->first_attribute("recth");
314 if (attr) {
315 string parsevalue = gui_parse_text(attr->value());
316 mFastScrollRectH = atoi(parsevalue.c_str());
317 }
318 }
319
Dees_Troy51a0e822012-09-05 15:24:24 -0400320 // Retrieve the line height
321 gr_getFontDetails(mFont ? mFont->GetResource() : NULL, &mFontHeight, NULL);
322 mLineHeight = mFontHeight;
323 mHeaderH = mFontHeight;
324
325 if (mFolderIcon && mFolderIcon->GetResource())
326 {
327 mFolderIconWidth = gr_get_width(mFolderIcon->GetResource());
328 mFolderIconHeight = gr_get_height(mFolderIcon->GetResource());
329 if (mFolderIconHeight > (int)mLineHeight)
330 mLineHeight = mFolderIconHeight;
331 mIconWidth = mFolderIconWidth;
332 }
333
334 if (mFileIcon && mFileIcon->GetResource())
335 {
336 mFileIconWidth = gr_get_width(mFileIcon->GetResource());
337 mFileIconHeight = gr_get_height(mFileIcon->GetResource());
338 if (mFileIconHeight > (int)mLineHeight)
339 mLineHeight = mFileIconHeight;
340 if (mFileIconWidth > mIconWidth)
341 mIconWidth = mFileIconWidth;
342 }
343
344 if (mHeaderIcon && mHeaderIcon->GetResource())
345 {
346 mHeaderIconWidth = gr_get_width(mHeaderIcon->GetResource());
347 mHeaderIconHeight = gr_get_height(mHeaderIcon->GetResource());
348 if (mHeaderIconHeight > mHeaderH)
349 mHeaderH = mHeaderIconHeight;
350 if (mHeaderIconWidth > mIconWidth)
351 mIconWidth = mHeaderIconWidth;
352 }
353
354 mHeaderH += mLineSpacing + mHeaderSeparatorH;
355 actualLineHeight = mLineHeight + mLineSpacing + mSeparatorH;
356 if (mHeaderH < actualLineHeight)
357 mHeaderH = actualLineHeight;
358
359 if (actualLineHeight / 3 > 6)
360 touchDebounce = actualLineHeight / 3;
361
362 if (mBackground && mBackground->GetResource())
363 {
364 mBackgroundW = gr_get_width(mBackground->GetResource());
365 mBackgroundH = gr_get_height(mBackground->GetResource());
366 }
367
368 // Fetch the file/folder list
369 std::string value;
370 DataManager::GetValue(mPathVar, value);
Dees_Troy80a11d92013-01-25 16:36:07 +0000371 GetFileList(value);
Dees_Troy51a0e822012-09-05 15:24:24 -0400372}
373
374GUIFileSelector::~GUIFileSelector()
375{
376}
377
378int GUIFileSelector::Render(void)
379{
380 // First step, fill background
381 gr_color(mBackgroundColor.red, mBackgroundColor.green, mBackgroundColor.blue, 255);
382 gr_fill(mRenderX, mRenderY + mHeaderH, mRenderW, mRenderH - mHeaderH);
383
384 // Next, render the background resource (if it exists)
385 if (mBackground && mBackground->GetResource())
386 {
387 mBackgroundX = mRenderX + ((mRenderW - mBackgroundW) / 2);
388 mBackgroundY = mRenderY + ((mRenderH - mBackgroundH) / 2);
389 gr_blit(mBackground->GetResource(), 0, 0, mBackgroundW, mBackgroundH, mBackgroundX, mBackgroundY);
390 }
391
392 // This tells us how many lines we can actually render
393 int lines = (mRenderH - mHeaderH) / (actualLineHeight);
394 int line;
395
396 int folderSize = mShowFolders ? mFolderList.size() : 0;
397 int fileSize = mShowFiles ? mFileList.size() : 0;
398
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100399 int listW = mRenderW;
400
Dees_Troy51a0e822012-09-05 15:24:24 -0400401 if (folderSize + fileSize < lines) {
402 lines = folderSize + fileSize;
403 scrollingY = 0;
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100404 mFastScrollRectX = mFastScrollRectY = -1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400405 } else {
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100406 listW -= mFastScrollW; // space for fast scroll
Dees_Troy51a0e822012-09-05 15:24:24 -0400407 lines++;
408 if (lines < folderSize + fileSize)
409 lines++;
410 }
411
412 void* fontResource = NULL;
413 if (mFont) fontResource = mFont->GetResource();
414
415 int yPos = mRenderY + mHeaderH + scrollingY;
416 int fontOffsetY = (int)((actualLineHeight - mFontHeight) / 2);
417 int currentIconHeight = 0, currentIconWidth = 0;
418 int currentIconOffsetY = 0, currentIconOffsetX = 0;
419 int folderIconOffsetY = (int)((actualLineHeight - mFolderIconHeight) / 2), fileIconOffsetY = (int)((actualLineHeight - mFileIconHeight) / 2);
420 int folderIconOffsetX = (mIconWidth - mFolderIconWidth) / 2, fileIconOffsetX = (mIconWidth - mFileIconWidth) / 2;
Dees_Troye7585ca2013-02-15 11:42:29 -0600421 int actualSelection = mStart;
422
423 if (isHighlighted) {
424 int selectY = scrollingY;
425
426 // Locate the correct line for highlighting
427 while (selectY + actualLineHeight < startSelection) {
428 selectY += actualLineHeight;
429 actualSelection++;
430 }
431 if (hasHighlightColor) {
432 // Highlight the area
433 gr_color(mHighlightColor.red, mHighlightColor.green, mHighlightColor.blue, 255);
434 int HighlightHeight = actualLineHeight;
435 if (mRenderY + mHeaderH + selectY + actualLineHeight > mRenderH + mRenderY) {
436 HighlightHeight = actualLineHeight - (mRenderY + mHeaderH + selectY + actualLineHeight - mRenderH - mRenderY);
437 }
438 gr_fill(mRenderX, mRenderY + mHeaderH + selectY, mRenderW, HighlightHeight);
439 }
440 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400441
442 for (line = 0; line < lines; line++)
443 {
444 Resource* icon;
445 std::string label;
446
Dees_Troye7585ca2013-02-15 11:42:29 -0600447 if (isHighlighted && hasFontHighlightColor && line + mStart == actualSelection) {
448 // Use the highlight color for the font
449 gr_color(mFontHighlightColor.red, mFontHighlightColor.green, mFontHighlightColor.blue, 255);
450 } else {
451 // Set the color for the font
452 gr_color(mFontColor.red, mFontColor.green, mFontColor.blue, 255);
453 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400454
455 if (line + mStart < folderSize)
456 {
457 icon = mFolderIcon;
458 label = mFolderList.at(line + mStart).fileName;
459 currentIconHeight = mFolderIconHeight;
460 currentIconWidth = mFolderIconWidth;
461 currentIconOffsetY = folderIconOffsetY;
462 currentIconOffsetX = folderIconOffsetX;
463 }
464 else if (line + mStart < folderSize + fileSize)
465 {
466 icon = mFileIcon;
467 label = mFileList.at((line + mStart) - folderSize).fileName;
468 currentIconHeight = mFileIconHeight;
469 currentIconWidth = mFileIconWidth;
470 currentIconOffsetY = fileIconOffsetY;
471 currentIconOffsetX = fileIconOffsetX;
472 } else {
473 continue;
474 }
475
476 if (icon && icon->GetResource())
477 {
478 int rect_y = 0, image_y = (yPos + currentIconOffsetY);
479 if (image_y + currentIconHeight > mRenderY + mRenderH)
480 rect_y = mRenderY + mRenderH - image_y;
481 else
482 rect_y = currentIconHeight;
483 gr_blit(icon->GetResource(), 0, 0, currentIconWidth, rect_y, mRenderX + currentIconOffsetX, image_y);
484 }
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100485 gr_textExWH(mRenderX + mIconWidth + 5, yPos + fontOffsetY, label.c_str(), fontResource, mRenderX + listW, mRenderY + mRenderH);
Dees_Troy51a0e822012-09-05 15:24:24 -0400486
487 // Add the separator
488 if (yPos + actualLineHeight < mRenderH + mRenderY) {
489 gr_color(mSeparatorColor.red, mSeparatorColor.green, mSeparatorColor.blue, 255);
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100490 gr_fill(mRenderX, yPos + actualLineHeight - mSeparatorH, listW, mSeparatorH);
Dees_Troy51a0e822012-09-05 15:24:24 -0400491 }
492
493 // Move the yPos
494 yPos += actualLineHeight;
495 }
496
497 // Render the Header (last so that it overwrites the top most row for per pixel scrolling)
498 // First step, fill background
499 gr_color(mHeaderBackgroundColor.red, mHeaderBackgroundColor.green, mHeaderBackgroundColor.blue, 255);
500 gr_fill(mRenderX, mRenderY, mRenderW, mHeaderH);
501
502 // Now, we need the header (icon + text)
503 yPos = mRenderY;
504 {
505 Resource* headerIcon;
506 int mIconOffsetX = 0;
507
508 // render the icon if it exists
509 headerIcon = mHeaderIcon;
510 if (headerIcon && headerIcon->GetResource())
511 {
512 gr_blit(headerIcon->GetResource(), 0, 0, mHeaderIconWidth, mHeaderIconHeight, mRenderX + ((mHeaderIconWidth - mIconWidth) / 2), (yPos + (int)((mHeaderH - mHeaderIconHeight) / 2)));
513 mIconOffsetX = mIconWidth;
514 }
515
516 // render the text
517 gr_color(mHeaderFontColor.red, mHeaderFontColor.green, mHeaderFontColor.blue, 255);
518 gr_textExWH(mRenderX + mIconOffsetX + 5, yPos + (int)((mHeaderH - mFontHeight) / 2), mLastValue.c_str(), fontResource, mRenderX + mRenderW, mRenderY + mRenderH);
519
520 // Add the separator
521 gr_color(mHeaderSeparatorColor.red, mHeaderSeparatorColor.green, mHeaderSeparatorColor.blue, 255);
522 gr_fill(mRenderX, yPos + mHeaderH - mHeaderSeparatorH, mRenderW, mHeaderSeparatorH);
523 }
524
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100525 // render fast scroll
526 lines = (mRenderH - mHeaderH) / (actualLineHeight);
527 if(mFastScrollW > 0 && folderSize + fileSize > lines)
528 {
529 int startX = listW + mRenderX;
530 int fWidth = mRenderW - listW;
531 int fHeight = mRenderH - mHeaderH;
532
533 // line
534 gr_color(mFastScrollLineColor.red, mFastScrollLineColor.green, mFastScrollLineColor.blue, 255);
535 gr_fill(startX + fWidth/2, mRenderY + mHeaderH, mFastScrollLineW, mRenderH - mHeaderH);
536
537 // rect
538 int pct = ((mStart*actualLineHeight - scrollingY)*100)/((folderSize + fileSize)*actualLineHeight-lines*actualLineHeight);
539 mFastScrollRectX = startX + (fWidth - mFastScrollRectW)/2;
540 mFastScrollRectY = mRenderY+mHeaderH + ((fHeight - mFastScrollRectH)*pct)/100;
541
542 gr_color(mFastScrollRectColor.red, mFastScrollRectColor.green, mFastScrollRectColor.blue, 255);
543 gr_fill(mFastScrollRectX, mFastScrollRectY, mFastScrollRectW, mFastScrollRectH);
544 }
545
Dees_Troy51a0e822012-09-05 15:24:24 -0400546 mUpdate = 0;
547 return 0;
548}
549
550int GUIFileSelector::Update(void)
551{
552 if (!mHeaderIsStatic) {
553 std::string newValue = gui_parse_text(mHeaderText);
554 if (mLastValue != newValue) {
555 mLastValue = newValue;
556 mUpdate = 1;
557 }
558 }
559
560 if (mUpdate)
561 {
562 mUpdate = 0;
563 if (Render() == 0)
564 return 2;
565 }
566
567 // Handle kinetic scrolling
568 if (scrollingSpeed == 0) {
569 // Do nothing
570 } else if (scrollingSpeed > 0) {
571 if (scrollingSpeed < ((int) (actualLineHeight * 2.5))) {
572 scrollingY += scrollingSpeed;
573 scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
574 } else {
575 scrollingY += ((int) (actualLineHeight * 2.5));
576 scrollingSpeed -= SCROLLING_SPEED_DECREMENT;
577 }
578 while (mStart && scrollingY > 0) {
579 mStart--;
580 scrollingY -= actualLineHeight;
581 }
582 if (mStart == 0 && scrollingY > 0) {
583 scrollingY = 0;
584 scrollingSpeed = 0;
585 } else if (scrollingSpeed < SCROLLING_FLOOR)
586 scrollingSpeed = 0;
587 mUpdate = 1;
588 } else if (scrollingSpeed < 0) {
589 int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
590 int lines = (mRenderH - mHeaderH) / (actualLineHeight);
591
592 if (totalSize > lines) {
593 int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
594
595 bottom_offset -= actualLineHeight;
596
597 if (abs(scrollingSpeed) < ((int) (actualLineHeight * 2.5))) {
598 scrollingY += scrollingSpeed;
599 scrollingSpeed += SCROLLING_SPEED_DECREMENT;
600 } else {
601 scrollingY -= ((int) (actualLineHeight * 2.5));
602 scrollingSpeed += SCROLLING_SPEED_DECREMENT;
603 }
604 while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
605 mStart++;
606 scrollingY += actualLineHeight;
607 }
608 if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
609 mStart = totalSize - lines - 1;
610 scrollingY = bottom_offset;
611 } else if (mStart + lines >= totalSize && scrollingY < 0) {
612 mStart = totalSize - lines;
613 scrollingY = 0;
614 } else if (scrollingSpeed * -1 < SCROLLING_FLOOR)
615 scrollingSpeed = 0;
616 mUpdate = 1;
617 }
618 }
619
620 return 0;
621}
622
623int GUIFileSelector::GetSelection(int x, int y)
624{
625 // We only care about y position
626 if (y < mRenderY || y - mRenderY <= mHeaderH || y - mRenderY > mRenderH) return -1;
627 return (y - mRenderY - mHeaderH);
628}
629
630int GUIFileSelector::NotifyTouch(TOUCH_STATE state, int x, int y)
631{
Dees_Troy51a0e822012-09-05 15:24:24 -0400632 static int lastY = 0, last2Y = 0;
633 int selection = 0;
634
635 switch (state)
636 {
637 case TOUCH_START:
638 if (scrollingSpeed != 0)
639 startSelection = -1;
640 else
641 startSelection = GetSelection(x,y);
Dees_Troye7585ca2013-02-15 11:42:29 -0600642 isHighlighted = (startSelection > -1);
643 if (isHighlighted)
644 mUpdate = 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400645 startY = lastY = last2Y = y;
646 scrollingSpeed = 0;
647 break;
648
649 case TOUCH_DRAG:
650 // Check if we dragged out of the selection window
651 if (GetSelection(x, y) == -1) {
652 last2Y = lastY = 0;
Dees_Troye7585ca2013-02-15 11:42:29 -0600653 if (isHighlighted) {
654 isHighlighted = false;
655 mUpdate = 1;
656 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400657 break;
658 }
659
Vojtech Bocek7cc278b2013-02-24 01:40:19 +0100660 // Fast scroll
661 if(mFastScrollRectX != -1 && x >= mRenderX + mRenderW - mFastScrollW)
662 {
663 int pct = ((y-mRenderY-mHeaderH)*100)/(mRenderH-mHeaderH);
664 int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
665 int lines = (mRenderH - mHeaderH) / (actualLineHeight);
666
667 float l = float((totalSize-lines)*pct)/100;
668 if(l + lines >= totalSize)
669 {
670 mStart = totalSize - lines;
671 scrollingY = 0;
672 }
673 else
674 {
675 mStart = l;
676 scrollingY = -(l - int(l))*actualLineHeight;
677 }
678
679 startSelection = -1;
680 mUpdate = 1;
681 scrollingSpeed = 0;
682 isHighlighted = false;
683 break;
684 }
685
Dees_Troy51a0e822012-09-05 15:24:24 -0400686 // Provide some debounce on initial touches
687 if (startSelection != -1 && abs(y - startY) < touchDebounce) {
Dees_Troye7585ca2013-02-15 11:42:29 -0600688 isHighlighted = true;
689 mUpdate = 1;
Dees_Troy51a0e822012-09-05 15:24:24 -0400690 break;
691 }
692
Dees_Troye7585ca2013-02-15 11:42:29 -0600693 isHighlighted = false;
Dees_Troy51a0e822012-09-05 15:24:24 -0400694 last2Y = lastY;
695 lastY = y;
696 startSelection = -1;
697
698 // Handle scrolling
699 scrollingY += y - startY;
700 startY = y;
701 while(mStart && scrollingY > 0) {
702 mStart--;
703 scrollingY -= actualLineHeight;
704 }
705 if (mStart == 0 && scrollingY > 0)
706 scrollingY = 0;
707 {
708 int totalSize = (mShowFolders ? mFolderList.size() : 0) + (mShowFiles ? mFileList.size() : 0);
709 int lines = (mRenderH - mHeaderH) / (actualLineHeight);
710
711 if (totalSize > lines) {
712 int bottom_offset = ((int)(mRenderH) - mHeaderH) - (lines * actualLineHeight);
713
714 bottom_offset -= actualLineHeight;
715
716 while (mStart + lines + (bottom_offset ? 1 : 0) < totalSize && abs(scrollingY) > actualLineHeight) {
717 mStart++;
718 scrollingY += actualLineHeight;
719 }
720 if (bottom_offset != 0 && mStart + lines + 1 >= totalSize && scrollingY <= bottom_offset) {
721 mStart = totalSize - lines - 1;
722 scrollingY = bottom_offset;
723 } else if (mStart + lines >= totalSize && scrollingY < 0) {
724 mStart = totalSize - lines;
725 scrollingY = 0;
726 }
727 } else
728 scrollingY = 0;
729 }
730 mUpdate = 1;
731 break;
732
733 case TOUCH_RELEASE:
Dees_Troye7585ca2013-02-15 11:42:29 -0600734 isHighlighted = false;
Dees_Troy51a0e822012-09-05 15:24:24 -0400735 if (startSelection >= 0)
736 {
737 // We've selected an item!
738 std::string str;
739
740 int folderSize = mShowFolders ? mFolderList.size() : 0;
741 int fileSize = mShowFiles ? mFileList.size() : 0;
742 int selectY = scrollingY, actualSelection = mStart;
743
744 // Move the selection to the proper place in the array
745 while (selectY + actualLineHeight < startSelection) {
746 selectY += actualLineHeight;
747 actualSelection++;
748 }
749 startSelection = actualSelection;
750
751 if (startSelection < folderSize + fileSize)
752 {
753 if (startSelection < folderSize)
754 {
755 std::string oldcwd;
756 std::string cwd;
757
758 str = mFolderList.at(startSelection).fileName;
759 if (mSelection != "0")
760 DataManager::SetValue(mSelection, str);
761 DataManager::GetValue(mPathVar, cwd);
762
763 oldcwd = cwd;
764 // Ignore requests to do nothing
765 if (str == ".") return 0;
766 if (str == TW_FILESELECTOR_UP_A_LEVEL)
767 {
768 if (cwd != "/")
769 {
770 size_t found;
771 found = cwd.find_last_of('/');
772 cwd = cwd.substr(0,found);
773
774 if (cwd.length() < 2) cwd = "/";
775 }
776 }
777 else
778 {
779 // Add a slash if we're not the root folder
780 if (cwd != "/") cwd += "/";
781 cwd += str;
782 }
783
784 if (mShowNavFolders == 0 && mShowFiles == 0)
785 {
786 // This is a "folder" selection
787 DataManager::SetValue(mVariable, cwd);
788 }
789 else
790 {
791 DataManager::SetValue(mPathVar, cwd);
Dees_Troy80a11d92013-01-25 16:36:07 +0000792 GetFileList(cwd);
Dees_Troy51a0e822012-09-05 15:24:24 -0400793 mStart = 0;
794 scrollingY = 0;
795 mUpdate = 1;
796 }
797 }
798 else if (!mVariable.empty())
799 {
800 str = mFileList.at(startSelection - folderSize).fileName;
801 if (mSelection != "0")
802 DataManager::SetValue(mSelection, str);
803
804 std::string cwd;
805 DataManager::GetValue(mPathVar, cwd);
806 if (cwd != "/") cwd += "/";
807 DataManager::SetValue(mVariable, cwd + str);
808 }
809 }
810 } else {
811 // This is for kinetic scrolling
812 scrollingSpeed = lastY - last2Y;
813 if (abs(scrollingSpeed) > SCROLLING_FLOOR)
814 scrollingSpeed *= SCROLLING_MULTIPLIER;
815 else
816 scrollingSpeed = 0;
817 }
818 case TOUCH_REPEAT:
819 case TOUCH_HOLD:
820 break;
821 }
822 return 0;
823}
824
825int GUIFileSelector::NotifyVarChange(std::string varName, std::string value)
826{
827 if (varName.empty())
828 {
829 // Always clear the data variable so we know to use it
830 DataManager::SetValue(mVariable, "");
831 }
832 if (!mHeaderIsStatic) {
833 std::string newValue = gui_parse_text(mHeaderText);
834 if (mLastValue != newValue) {
835 mLastValue = newValue;
836 mStart = 0;
837 scrollingY = 0;
838 scrollingSpeed = 0;
839 mUpdate = 1;
840 }
841 }
842 if (varName == mPathVar || varName == mSortVariable)
843 {
844 DataManager::GetValue(mPathVar, value); // sometimes the value will be the sort order instead of the path, so we read the path everytime
845 DataManager::GetValue(mSortVariable, mSortOrder);
846 mStart = 0;
847 scrollingY = 0;
848 scrollingSpeed = 0;
849 GetFileList(value);
850 mUpdate = 1;
851 return 0;
852 }
853 return 0;
854}
855
856int GUIFileSelector::SetRenderPos(int x, int y, int w /* = 0 */, int h /* = 0 */)
857{
858 mRenderX = x;
859 mRenderY = y;
860 if (w || h)
861 {
862 mRenderW = w;
863 mRenderH = h;
864 }
865 SetActionPos(mRenderX, mRenderY, mRenderW, mRenderH);
866 mUpdate = 1;
867 return 0;
868}
869
870bool GUIFileSelector::fileSort(FileData d1, FileData d2)
871{
872 if (d1.fileName == ".")
873 return -1;
874 if (d2.fileName == ".")
875 return 0;
876 if (d1.fileName == TW_FILESELECTOR_UP_A_LEVEL)
877 return -1;
878 if (d2.fileName == TW_FILESELECTOR_UP_A_LEVEL)
879 return 0;
880
881 switch (mSortOrder) {
882 case 3: // by size largest first
883 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
884 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600885 return d1.fileSize < d2.fileSize;
Dees_Troy51a0e822012-09-05 15:24:24 -0400886 case -3: // by size smallest first
887 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
888 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600889 return d1.fileSize > d2.fileSize;
Dees_Troy51a0e822012-09-05 15:24:24 -0400890 case 2: // by last modified date newest first
891 if (d1.lastModified == d2.lastModified)
892 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600893 return d1.lastModified < d2.lastModified;
Dees_Troy51a0e822012-09-05 15:24:24 -0400894 case -2: // by date oldest first
895 if (d1.lastModified == d2.lastModified)
896 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
Dees_Troy6ef66352013-02-21 08:26:57 -0600897 return d1.lastModified > d2.lastModified;
Dees_Troy51a0e822012-09-05 15:24:24 -0400898 case -1: // by name descending
899 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) > 0);
900 default: // should be a 1 - sort by name ascending
901 return (strcasecmp(d1.fileName.c_str(), d2.fileName.c_str()) < 0);
902 }
903}
904
905int GUIFileSelector::GetFileList(const std::string folder)
906{
907 DIR* d;
908 struct dirent* de;
909 struct stat st;
910
911 // Clear all data
912 mFolderList.clear();
913 mFileList.clear();
914
915 d = opendir(folder.c_str());
916 if (d == NULL)
917 {
918 LOGI("Unable to open '%s'\n", folder.c_str());
Dees_Troy80a11d92013-01-25 16:36:07 +0000919 if (folder != "/" && (mShowNavFolders != 0 || mShowFiles != 0)) {
920 size_t found;
921 found = folder.find_last_of('/');
922 if (found != string::npos) {
923 string new_folder = folder.substr(0, found);
924
925 if (new_folder.length() < 2)
926 new_folder = "/";
927 DataManager::SetValue(mPathVar, new_folder);
928 }
929 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400930 return -1;
931 }
932
933 while ((de = readdir(d)) != NULL)
934 {
935 FileData data;
936
937 data.fileName = de->d_name;
938 if (data.fileName == ".")
939 continue;
940 if (data.fileName == ".." && folder == "/")
941 continue;
Dees_Troy3ee47bc2013-01-25 21:47:37 +0000942 if (data.fileName == "..") {
Dees_Troy51a0e822012-09-05 15:24:24 -0400943 data.fileName = TW_FILESELECTOR_UP_A_LEVEL;
Dees_Troy3ee47bc2013-01-25 21:47:37 +0000944 data.fileType = DT_DIR;
945 } else {
946 data.fileType = de->d_type;
947 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400948
949 std::string path = folder + "/" + data.fileName;
950 stat(path.c_str(), &st);
951 data.protection = st.st_mode;
952 data.userId = st.st_uid;
953 data.groupId = st.st_gid;
954 data.fileSize = st.st_size;
955 data.lastAccess = st.st_atime;
956 data.lastModified = st.st_mtime;
957 data.lastStatChange = st.st_ctime;
958
Dees_Troy3ee47bc2013-01-25 21:47:37 +0000959 if (data.fileType == DT_UNKNOWN) {
960 data.fileType = TWFunc::Get_D_Type_From_Stat(path);
961 }
Dees_Troy51a0e822012-09-05 15:24:24 -0400962 if (data.fileType == DT_DIR)
963 {
964 if (mShowNavFolders || (data.fileName != "." && data.fileName != TW_FILESELECTOR_UP_A_LEVEL))
965 mFolderList.push_back(data);
966 }
Dees_Troyaf4d0ce2012-09-26 20:19:06 -0400967 else if (data.fileType == DT_REG || data.fileType == DT_LNK || data.fileType == DT_BLK)
Dees_Troy51a0e822012-09-05 15:24:24 -0400968 {
969 if (mExtn.empty() || (data.fileName.length() > mExtn.length() && data.fileName.substr(data.fileName.length() - mExtn.length()) == mExtn))
970 {
971 mFileList.push_back(data);
972 }
973 }
974 }
975 closedir(d);
976
977 std::sort(mFolderList.begin(), mFolderList.end(), fileSort);
978 std::sort(mFileList.begin(), mFileList.end(), fileSort);
979 return 0;
980}
981
982void GUIFileSelector::SetPageFocus(int inFocus)
983{
984 if (inFocus)
985 {
986 std::string value;
987 DataManager::GetValue(mPathVar, value);
Dees_Troy80a11d92013-01-25 16:36:07 +0000988 GetFileList(value);
Dees_Troy51a0e822012-09-05 15:24:24 -0400989 }
990}
991