5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
24 #define REFRESH_FILES() do { reusableBuffer.sdmanager.offset = 65535; currentBitmapIndex = -1; } while (0)
25 #define NODE_TYPE(fname) fname[SD_SCREEN_FILE_LENGTH+1]
26 #define IS_DIRECTORY(fname) ((bool)(!NODE_TYPE(fname)))
27 #define IS_FILE(fname) ((bool)(NODE_TYPE(fname)))
29 int currentBitmapIndex
= 0;
30 BitmapBuffer
* currentBitmap
= NULL
;
32 bool menuRadioSdManagerInfo(event_t event
)
34 SIMPLE_SUBMENU(STR_SD_INFO_TITLE
, ICON_RADIO_SD_BROWSER
, 1);
36 lcdDrawText(MENUS_MARGIN_LEFT
, 2*FH
, STR_SD_TYPE
);
37 lcdDrawText(100, 2*FH
, SD_IS_HC() ? STR_SDHC_CARD
: STR_SD_CARD
);
39 lcdDrawText(MENUS_MARGIN_LEFT
, 3*FH
, STR_SD_SIZE
);
40 lcdDrawNumber(100, 3*FH
, sdGetSize(), LEFT
, 0, NULL
, "M");
42 lcdDrawText(MENUS_MARGIN_LEFT
, 4*FH
, STR_SD_SECTORS
);
43 #if defined(SD_GET_FREE_BLOCKNR)
44 lcdDrawNumber(100, 4*FH
, SD_GET_FREE_BLOCKNR()/1000, LEFT
, 0, NULL
, "/");
45 lcdDrawNumber(150, 4*FH
, sdGetNoSectors()/1000, LEFT
);
47 lcdDrawNumber(100, 4*FH
, sdGetNoSectors()/1000, LEFT
, 0, NULL
, "k");
50 lcdDrawText(MENUS_MARGIN_LEFT
, 5*FH
, STR_SD_SPEED
);
51 lcdDrawNumber(100, 5*FH
, SD_GET_SPEED()/1000, LEFT
, 0, NULL
, "kb/s");
56 inline bool isFilenameGreater(bool isfile
, const char * fn
, const char * line
)
58 return (isfile
&& IS_DIRECTORY(line
)) || (isfile
==IS_FILE(line
) && strcasecmp(fn
, line
) > 0);
61 inline bool isFilenameLower(bool isfile
, const char * fn
, const char * line
)
63 return (!isfile
&& IS_FILE(line
)) || (isfile
==IS_FILE(line
) && strcasecmp(fn
, line
) < 0);
66 void getSelectionFullPath(char * lfn
)
68 f_getcwd(lfn
, _MAX_LFN
);
69 strcat(lfn
, PSTR("/"));
70 strcat(lfn
, reusableBuffer
.sdmanager
.lines
[menuVerticalPosition
- menuVerticalOffset
]);
73 void onSdManagerMenu(const char * result
)
75 TCHAR lfn
[_MAX_LFN
+1];
77 // TODO possible buffer overflows here!
79 uint8_t index
= menuVerticalPosition
-menuVerticalOffset
;
80 char *line
= reusableBuffer
.sdmanager
.lines
[index
];
82 if (result
== STR_SD_INFO
) {
83 pushMenu(menuRadioSdManagerInfo
);
85 else if (result
== STR_SD_FORMAT
) {
86 POPUP_CONFIRMATION(STR_CONFIRM_FORMAT
);
88 else if (result
== STR_COPY_FILE
) {
89 clipboard
.type
= CLIPBOARD_TYPE_SD_FILE
;
90 f_getcwd(clipboard
.data
.sd
.directory
, CLIPBOARD_PATH_LEN
);
91 strncpy(clipboard
.data
.sd
.filename
, line
, CLIPBOARD_PATH_LEN
-1);
93 else if (result
== STR_PASTE
) {
94 f_getcwd(lfn
, _MAX_LFN
);
95 // if destination is dir, copy into that dir
96 if (IS_DIRECTORY(line
)) {
97 strcat(lfn
, PSTR("/"));
100 if (strcmp(clipboard
.data
.sd
.directory
, lfn
)) { // prevent copying to the same directory
101 POPUP_WARNING(sdCopyFile(clipboard
.data
.sd
.filename
, clipboard
.data
.sd
.directory
, clipboard
.data
.sd
.filename
, lfn
));
105 else if (result
== STR_RENAME_FILE
) {
106 memcpy(reusableBuffer
.sdmanager
.originalName
, line
, sizeof(reusableBuffer
.sdmanager
.originalName
));
107 uint8_t fnlen
= 0, extlen
= 0;
108 getFileExtension(line
, 0, LEN_FILE_EXTENSION_MAX
, &fnlen
, &extlen
);
109 // write spaces to allow extending the length of a filename
110 memset(line
+ fnlen
- extlen
, ' ', SD_SCREEN_FILE_LENGTH
- fnlen
+ extlen
);
111 line
[SD_SCREEN_FILE_LENGTH
-extlen
] = '\0';
112 s_editMode
= EDIT_MODIFY_STRING
;
113 editNameCursorPos
= 0;
115 else if (result
== STR_DELETE_FILE
) {
116 getSelectionFullPath(lfn
);
118 menuVerticalOffset
= 0;
119 menuVerticalPosition
= 0;
122 else if (result
== STR_PLAY_FILE
) {
123 getSelectionFullPath(lfn
);
124 audioQueue
.stopAll();
125 audioQueue
.playFile(lfn
, 0, ID_PLAY_FROM_SD_MANAGER
);
127 else if (result
== STR_ASSIGN_BITMAP
) {
128 memcpy(g_model
.header
.bitmap
, line
, sizeof(g_model
.header
.bitmap
));
129 storageDirty(EE_MODEL
);
131 else if (result
== STR_ASSIGN_SPLASH
) {
132 f_getcwd(lfn
, _MAX_LFN
);
133 sdCopyFile(line
, lfn
, SPLASH_FILE
, BITMAPS_PATH
);
135 else if (result
== STR_VIEW_TEXT
) {
136 getSelectionFullPath(lfn
);
137 pushMenuTextView(lfn
);
139 else if (result
== STR_FLASH_EXTERNAL_DEVICE
) {
140 getSelectionFullPath(lfn
);
141 sportFlashDevice(EXTERNAL_MODULE
, lfn
);
144 else if (result
== STR_EXECUTE_FILE
) {
145 getSelectionFullPath(lfn
);
151 bool menuRadioSdManager(event_t _event
)
155 showMessageBox(STR_FORMATTING
);
164 event_t event
= (EVT_KEY_MASK(_event
) == KEY_ENTER
? 0 : _event
);
165 SIMPLE_MENU(SD_IS_HC() ? STR_SDHC_CARD
: STR_SD_CARD
, RADIO_ICONS
, menuTabGeneral
, MENU_RADIO_SD_MANAGER
, reusableBuffer
.sdmanager
.count
);
167 int index
= menuVerticalPosition
-menuVerticalOffset
;
176 // TODO: Implement it
177 case EVT_KEY_LONG(KEY_MENU
):
178 if (!READ_ONLY() && s_editMode
== 0) {
180 POPUP_MENU_ADD_ITEM(STR_SD_INFO
);
181 POPUP_MENU_ADD_ITEM(STR_SD_FORMAT
);
182 POPUP_MENU_START(onSdManagerMenu
);
187 case EVT_KEY_BREAK(KEY_EXIT
):
191 case EVT_KEY_BREAK(KEY_ENTER
):
192 if (s_editMode
> 0) {
196 if (IS_DIRECTORY(reusableBuffer
.sdmanager
.lines
[index
])) {
197 f_chdir(reusableBuffer
.sdmanager
.lines
[index
]);
198 menuVerticalOffset
= 0;
199 menuVerticalPosition
= 1;
208 case EVT_KEY_LONG(KEY_ENTER
):
209 if (s_editMode
== 0) {
211 char * line
= reusableBuffer
.sdmanager
.lines
[index
];
212 if (!strcmp(line
, "..")) {
213 break; // no menu for parent dir
215 const char * ext
= getFileExtension(line
);
217 if (!strcasecmp(ext
, SOUNDS_EXT
)) {
218 POPUP_MENU_ADD_ITEM(STR_PLAY_FILE
);
220 else if (isExtensionMatching(ext
, BITMAPS_EXT
)) {
221 TCHAR lfn
[_MAX_LFN
+1];
222 f_getcwd(lfn
, _MAX_LFN
);
223 if (!READ_ONLY() && unsigned(strlen(ext
)+ext
-line
) <= sizeof(g_model
.header
.bitmap
) && !strcmp(lfn
, BITMAPS_PATH
)) {
224 POPUP_MENU_ADD_ITEM(STR_ASSIGN_BITMAP
);
226 if (!strcmp(ext
, PNG_EXT
)) {
227 POPUP_MENU_ADD_ITEM(STR_ASSIGN_SPLASH
);
230 else if (!strcasecmp(ext
, TEXT_EXT
)) {
231 POPUP_MENU_ADD_ITEM(STR_VIEW_TEXT
);
233 else if (!READ_ONLY() && !strcasecmp(ext
, SPORT_FIRMWARE_EXT
)) {
234 POPUP_MENU_ADD_ITEM(STR_FLASH_EXTERNAL_DEVICE
);
236 else if (isExtensionMatching(ext
, SCRIPTS_EXT
)) {
237 POPUP_MENU_ADD_ITEM(STR_EXECUTE_FILE
);
242 POPUP_MENU_ADD_ITEM(STR_COPY_FILE
);
243 if (clipboard
.type
== CLIPBOARD_TYPE_SD_FILE
)
244 POPUP_MENU_ADD_ITEM(STR_PASTE
);
245 POPUP_MENU_ADD_ITEM(STR_RENAME_FILE
);
247 POPUP_MENU_ADD_ITEM(STR_DELETE_FILE
);
249 POPUP_MENU_START(onSdManagerMenu
);
254 if (reusableBuffer
.sdmanager
.offset
!= menuVerticalOffset
) {
258 if (menuVerticalOffset
== 0) {
259 reusableBuffer
.sdmanager
.offset
= 0;
260 memset(reusableBuffer
.sdmanager
.lines
, 0, sizeof(reusableBuffer
.sdmanager
.lines
));
262 else if (menuVerticalOffset
== reusableBuffer
.sdmanager
.count
-NUM_BODY_LINES
) {
263 reusableBuffer
.sdmanager
.offset
= menuVerticalOffset
;
264 memset(reusableBuffer
.sdmanager
.lines
, 0, sizeof(reusableBuffer
.sdmanager
.lines
));
266 else if (menuVerticalOffset
> reusableBuffer
.sdmanager
.offset
) {
267 memmove(reusableBuffer
.sdmanager
.lines
[0], reusableBuffer
.sdmanager
.lines
[1], (NUM_BODY_LINES
-1)*sizeof(reusableBuffer
.sdmanager
.lines
[0]));
268 memset(reusableBuffer
.sdmanager
.lines
[NUM_BODY_LINES
-1], 0xff, SD_SCREEN_FILE_LENGTH
);
269 NODE_TYPE(reusableBuffer
.sdmanager
.lines
[NUM_BODY_LINES
-1]) = 1;
272 memmove(reusableBuffer
.sdmanager
.lines
[1], reusableBuffer
.sdmanager
.lines
[0], (NUM_BODY_LINES
-1)*sizeof(reusableBuffer
.sdmanager
.lines
[0]));
273 memset(reusableBuffer
.sdmanager
.lines
[0], 0, sizeof(reusableBuffer
.sdmanager
.lines
[0]));
276 reusableBuffer
.sdmanager
.count
= 0;
278 FRESULT res
= f_opendir(&dir
, "."); // Open the directory
280 bool firstTime
= true;
282 res
= sdReadDir(&dir
, &fno
, firstTime
);
283 if (res
!= FR_OK
|| fno
.fname
[0] == 0) break; /* Break on error or end of dir */
284 if (strlen(fno
.fname
) > SD_SCREEN_FILE_LENGTH
) continue;
285 if (fno
.fname
[0] == '.' && fno
.fname
[1] != '.') continue; /* Ignore hidden files under UNIX, but not .. */
287 reusableBuffer
.sdmanager
.count
++;
289 bool isfile
= !(fno
.fattrib
& AM_DIR
);
291 if (menuVerticalOffset
== 0) {
292 for (uint8_t i
=0; i
<NUM_BODY_LINES
; i
++) {
293 char * line
= reusableBuffer
.sdmanager
.lines
[i
];
294 if (line
[0] == '\0' || isFilenameLower(isfile
, fno
.fname
, line
)) {
295 if (i
< NUM_BODY_LINES
-1) memmove(reusableBuffer
.sdmanager
.lines
[i
+1], line
, sizeof(reusableBuffer
.sdmanager
.lines
[i
]) * (NUM_BODY_LINES
-1-i
));
296 memset(line
, 0, sizeof(reusableBuffer
.sdmanager
.lines
[0]));
297 strcpy(line
, fno
.fname
);
298 NODE_TYPE(line
) = isfile
;
303 else if (reusableBuffer
.sdmanager
.offset
== menuVerticalOffset
) {
304 for (int8_t i
=NUM_BODY_LINES
-1; i
>=0; i
--) {
305 char *line
= reusableBuffer
.sdmanager
.lines
[i
];
306 if (line
[0] == '\0' || isFilenameGreater(isfile
, fno
.fname
, line
)) {
307 if (i
> 0) memmove(reusableBuffer
.sdmanager
.lines
[0], reusableBuffer
.sdmanager
.lines
[1], sizeof(reusableBuffer
.sdmanager
.lines
[0]) * i
);
308 memset(line
, 0, sizeof(reusableBuffer
.sdmanager
.lines
[0]));
309 strcpy(line
, fno
.fname
);
310 NODE_TYPE(line
) = isfile
;
315 else if (menuVerticalOffset
> reusableBuffer
.sdmanager
.offset
) {
316 if (isFilenameGreater(isfile
, fno
.fname
, reusableBuffer
.sdmanager
.lines
[NUM_BODY_LINES
-2]) && isFilenameLower(isfile
, fno
.fname
, reusableBuffer
.sdmanager
.lines
[NUM_BODY_LINES
-1])) {
317 memset(reusableBuffer
.sdmanager
.lines
[NUM_BODY_LINES
-1], 0, sizeof(reusableBuffer
.sdmanager
.lines
[0]));
318 strcpy(reusableBuffer
.sdmanager
.lines
[NUM_BODY_LINES
-1], fno
.fname
);
319 NODE_TYPE(reusableBuffer
.sdmanager
.lines
[NUM_BODY_LINES
-1]) = isfile
;
323 if (isFilenameLower(isfile
, fno
.fname
, reusableBuffer
.sdmanager
.lines
[1]) && isFilenameGreater(isfile
, fno
.fname
, reusableBuffer
.sdmanager
.lines
[0])) {
324 memset(reusableBuffer
.sdmanager
.lines
[0], 0, sizeof(reusableBuffer
.sdmanager
.lines
[0]));
325 strcpy(reusableBuffer
.sdmanager
.lines
[0], fno
.fname
);
326 NODE_TYPE(reusableBuffer
.sdmanager
.lines
[0]) = isfile
;
334 reusableBuffer
.sdmanager
.offset
= menuVerticalOffset
;
336 for (uint8_t i
=0; i
<NUM_BODY_LINES
; i
++) {
337 coord_t y
= MENU_CONTENT_TOP
+ i
*FH
;
338 LcdFlags attr
= (index
== i
? INVERS
: 0);
339 if (reusableBuffer
.sdmanager
.lines
[i
][0]) {
340 if (s_editMode
== EDIT_MODIFY_STRING
&& attr
) {
341 uint8_t extlen
, efflen
;
342 const char * ext
= getFileExtension(reusableBuffer
.sdmanager
.originalName
, 0, 0, NULL
, &extlen
);
343 editName(MENUS_MARGIN_LEFT
, y
, reusableBuffer
.sdmanager
.lines
[i
], SD_SCREEN_FILE_LENGTH
- extlen
, _event
, attr
, 0);
344 efflen
= effectiveLen(reusableBuffer
.sdmanager
.lines
[i
], SD_SCREEN_FILE_LENGTH
- extlen
);
345 if (s_editMode
== 0) {
347 strAppend(&reusableBuffer
.sdmanager
.lines
[i
][efflen
], ext
);
350 reusableBuffer
.sdmanager
.lines
[i
][efflen
] = 0;
352 f_rename(reusableBuffer
.sdmanager
.originalName
, reusableBuffer
.sdmanager
.lines
[i
]);
356 else if (IS_DIRECTORY(reusableBuffer
.sdmanager
.lines
[i
])) {
357 char s
[sizeof(reusableBuffer
.sdmanager
.lines
[0])+2];
360 ptr
= strAppend(ptr
, reusableBuffer
.sdmanager
.lines
[i
]);
363 lcdDrawText(MENUS_MARGIN_LEFT
, y
, s
, attr
);
366 lcdDrawText(MENUS_MARGIN_LEFT
, y
, reusableBuffer
.sdmanager
.lines
[i
], attr
);
371 const char * ext
= getFileExtension(reusableBuffer
.sdmanager
.lines
[index
]);
372 if (ext
&& isExtensionMatching(ext
, BITMAPS_EXT
)) {
373 if (currentBitmapIndex
!= menuVerticalPosition
) {
374 currentBitmapIndex
= menuVerticalPosition
;
375 delete currentBitmap
;
376 currentBitmap
= BitmapBuffer::load(reusableBuffer
.sdmanager
.lines
[index
]);
379 uint16_t height
= currentBitmap
->getHeight();
380 uint16_t width
= currentBitmap
->getWidth();
381 if (height
> MENU_BODY_HEIGHT
-10) {
382 height
= MENU_BODY_HEIGHT
- 10;
384 if (width
> LCD_W
/2) {
387 lcd
->drawScaledBitmap(currentBitmap
, LCD_W
/ 2 - 20 + LCD_W
/4 - width
/2, MENU_BODY_TOP
+ MENU_BODY_HEIGHT
/2 - height
/2, width
, height
);