archive_browser.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. #include "archive_files.h"
  2. #include "archive_apps.h"
  3. #include "archive_browser.h"
  4. #include <math.h>
  5. bool archive_is_item_in_array(ArchiveBrowserViewModel* model, uint32_t idx) {
  6. size_t array_size = files_array_size(model->files);
  7. if((idx >= (uint32_t)model->array_offset + array_size) ||
  8. (idx < (uint32_t)model->array_offset)) {
  9. return false;
  10. }
  11. return true;
  12. }
  13. void archive_update_offset(ArchiveBrowserView* browser) {
  14. furi_assert(browser);
  15. with_view_model(
  16. browser->view, (ArchiveBrowserViewModel * model) {
  17. uint16_t bounds = model->item_cnt > 3 ? 2 : model->item_cnt;
  18. if((model->item_cnt > 3u) && (model->item_idx >= ((int32_t)model->item_cnt - 1))) {
  19. model->list_offset = model->item_idx - 3;
  20. } else if(model->list_offset < model->item_idx - bounds) {
  21. model->list_offset =
  22. CLAMP(model->item_idx - 2, (int32_t)model->item_cnt - bounds, 0);
  23. } else if(model->list_offset > model->item_idx - bounds) {
  24. model->list_offset =
  25. CLAMP(model->item_idx - 1, (int32_t)model->item_cnt - bounds, 0);
  26. }
  27. return true;
  28. });
  29. }
  30. void archive_update_focus(ArchiveBrowserView* browser, const char* target) {
  31. furi_assert(browser);
  32. furi_assert(target);
  33. archive_get_filenames(browser, string_get_cstr(browser->path));
  34. if(!archive_file_get_array_size(browser) && !archive_get_depth(browser)) {
  35. archive_switch_tab(browser, TAB_RIGHT);
  36. } else {
  37. with_view_model(
  38. browser->view, (ArchiveBrowserViewModel * model) {
  39. uint16_t idx = 0;
  40. while(idx < files_array_size(model->files)) {
  41. ArchiveFile_t* current = files_array_get(model->files, idx);
  42. if(!string_search(current->name, target)) {
  43. model->item_idx = idx + model->array_offset;
  44. break;
  45. }
  46. ++idx;
  47. }
  48. return false;
  49. });
  50. archive_update_offset(browser);
  51. }
  52. }
  53. size_t archive_file_get_array_size(ArchiveBrowserView* browser) {
  54. furi_assert(browser);
  55. uint16_t size = 0;
  56. with_view_model(
  57. browser->view, (ArchiveBrowserViewModel * model) {
  58. size = files_array_size(model->files);
  59. return false;
  60. });
  61. return size;
  62. }
  63. void archive_set_item_count(ArchiveBrowserView* browser, uint32_t count) {
  64. furi_assert(browser);
  65. with_view_model(
  66. browser->view, (ArchiveBrowserViewModel * model) {
  67. model->item_cnt = count;
  68. model->item_idx = CLAMP(model->item_idx, (int32_t)model->item_cnt - 1, 0);
  69. return false;
  70. });
  71. archive_update_offset(browser);
  72. }
  73. void archive_file_array_rm_selected(ArchiveBrowserView* browser) {
  74. furi_assert(browser);
  75. uint32_t items_cnt = 0;
  76. with_view_model(
  77. browser->view, (ArchiveBrowserViewModel * model) {
  78. files_array_remove_v(
  79. model->files,
  80. model->item_idx - model->array_offset,
  81. model->item_idx - model->array_offset + 1);
  82. model->item_cnt--;
  83. model->item_idx = CLAMP(model->item_idx, (int32_t)model->item_cnt - 1, 0);
  84. items_cnt = model->item_cnt;
  85. return false;
  86. });
  87. if((items_cnt == 0) && (archive_get_depth(browser) == 0)) {
  88. archive_switch_tab(browser, TAB_RIGHT);
  89. }
  90. archive_update_offset(browser);
  91. }
  92. void archive_file_array_swap(ArchiveBrowserView* browser, int8_t dir) {
  93. furi_assert(browser);
  94. with_view_model(
  95. browser->view, (ArchiveBrowserViewModel * model) {
  96. ArchiveFile_t temp;
  97. size_t array_size = files_array_size(model->files) - 1;
  98. uint8_t swap_idx = CLAMP((size_t)(model->item_idx + dir), array_size, 0u);
  99. if(model->item_idx == 0 && dir < 0) {
  100. ArchiveFile_t_init(&temp);
  101. files_array_pop_at(&temp, model->files, array_size);
  102. files_array_push_at(model->files, model->item_idx, temp);
  103. ArchiveFile_t_clear(&temp);
  104. } else if(((uint32_t)model->item_idx == array_size) && (dir > 0)) {
  105. ArchiveFile_t_init(&temp);
  106. files_array_pop_at(&temp, model->files, 0);
  107. files_array_push_at(model->files, array_size, temp);
  108. ArchiveFile_t_clear(&temp);
  109. } else {
  110. files_array_swap_at(model->files, model->item_idx, swap_idx);
  111. }
  112. return false;
  113. });
  114. }
  115. void archive_file_array_rm_all(ArchiveBrowserView* browser) {
  116. furi_assert(browser);
  117. with_view_model(
  118. browser->view, (ArchiveBrowserViewModel * model) {
  119. files_array_reset(model->files);
  120. return false;
  121. });
  122. }
  123. bool archive_file_array_load(ArchiveBrowserView* browser, int8_t dir) {
  124. furi_assert(browser);
  125. int32_t offset_new = 0;
  126. with_view_model(
  127. browser->view, (ArchiveBrowserViewModel * model) {
  128. if(model->item_cnt > FILE_LIST_BUF_LEN) {
  129. if(dir < 0) {
  130. offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 3;
  131. } else if(dir == 0) {
  132. offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 2;
  133. } else {
  134. offset_new = model->item_idx - FILE_LIST_BUF_LEN / 4 * 1;
  135. }
  136. offset_new = CLAMP(offset_new, (int32_t)model->item_cnt - FILE_LIST_BUF_LEN, 0);
  137. }
  138. return false;
  139. });
  140. bool res = archive_dir_read_items(
  141. browser, string_get_cstr(browser->path), offset_new, FILE_LIST_BUF_LEN);
  142. with_view_model(
  143. browser->view, (ArchiveBrowserViewModel * model) {
  144. model->array_offset = offset_new;
  145. model->list_loading = false;
  146. return true;
  147. });
  148. return res;
  149. }
  150. ArchiveFile_t* archive_get_current_file(ArchiveBrowserView* browser) {
  151. furi_assert(browser);
  152. ArchiveFile_t* selected;
  153. with_view_model(
  154. browser->view, (ArchiveBrowserViewModel * model) {
  155. selected = files_array_size(model->files) ?
  156. files_array_get(model->files, model->item_idx - model->array_offset) :
  157. NULL;
  158. return false;
  159. });
  160. return selected;
  161. }
  162. ArchiveFile_t* archive_get_file_at(ArchiveBrowserView* browser, size_t idx) {
  163. furi_assert(browser);
  164. ArchiveFile_t* selected;
  165. with_view_model(
  166. browser->view, (ArchiveBrowserViewModel * model) {
  167. idx = CLAMP(idx - model->array_offset, files_array_size(model->files), 0u);
  168. selected = files_array_size(model->files) ? files_array_get(model->files, idx) : NULL;
  169. return false;
  170. });
  171. return selected;
  172. }
  173. ArchiveTabEnum archive_get_tab(ArchiveBrowserView* browser) {
  174. furi_assert(browser);
  175. ArchiveTabEnum tab_id;
  176. with_view_model(
  177. browser->view, (ArchiveBrowserViewModel * model) {
  178. tab_id = model->tab_idx;
  179. return false;
  180. });
  181. return tab_id;
  182. }
  183. uint8_t archive_get_depth(ArchiveBrowserView* browser) {
  184. furi_assert(browser);
  185. uint8_t depth;
  186. with_view_model(
  187. browser->view, (ArchiveBrowserViewModel * model) {
  188. depth = idx_last_array_size(model->idx_last);
  189. return false;
  190. });
  191. return depth;
  192. }
  193. const char* archive_get_path(ArchiveBrowserView* browser) {
  194. return string_get_cstr(browser->path);
  195. }
  196. const char* archive_get_name(ArchiveBrowserView* browser) {
  197. ArchiveFile_t* selected = archive_get_current_file(browser);
  198. return string_get_cstr(selected->name);
  199. }
  200. void archive_set_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) {
  201. furi_assert(browser);
  202. with_view_model(
  203. browser->view, (ArchiveBrowserViewModel * model) {
  204. model->tab_idx = tab;
  205. return false;
  206. });
  207. }
  208. void archive_set_last_tab(ArchiveBrowserView* browser, ArchiveTabEnum tab) {
  209. UNUSED(tab); // FIXME?
  210. furi_assert(browser);
  211. with_view_model(
  212. browser->view, (ArchiveBrowserViewModel * model) {
  213. model->last_tab = model->tab_idx;
  214. return false;
  215. });
  216. }
  217. void archive_add_app_item(ArchiveBrowserView* browser, const char* name) {
  218. furi_assert(browser);
  219. furi_assert(name);
  220. ArchiveFile_t item;
  221. string_t full_name;
  222. string_init_set(full_name, browser->path);
  223. string_cat_printf(full_name, "/%s", name);
  224. char* app_name = strchr(string_get_cstr(full_name), ':');
  225. if(app_name == NULL) {
  226. string_clear(full_name);
  227. return;
  228. }
  229. ArchiveFile_t_init(&item);
  230. string_init_set_str(item.name, name);
  231. archive_set_file_type(&item, NULL, app_name + 1, true);
  232. with_view_model(
  233. browser->view, (ArchiveBrowserViewModel * model) {
  234. files_array_push_back(model->files, item);
  235. model->item_cnt = files_array_size(model->files);
  236. return false;
  237. });
  238. ArchiveFile_t_clear(&item);
  239. string_clear(full_name);
  240. }
  241. void archive_add_file_item(ArchiveBrowserView* browser, FileInfo* file_info, const char* name) {
  242. furi_assert(browser);
  243. furi_assert(file_info);
  244. furi_assert(name);
  245. ArchiveFile_t item;
  246. if(archive_filter_by_extension(
  247. file_info, archive_get_tab_ext(archive_get_tab(browser)), name)) {
  248. ArchiveFile_t_init(&item);
  249. string_init_set_str(item.name, name);
  250. archive_set_file_type(&item, file_info, archive_get_path(browser), false);
  251. with_view_model(
  252. browser->view, (ArchiveBrowserViewModel * model) {
  253. files_array_push_back(model->files, item);
  254. return false;
  255. });
  256. ArchiveFile_t_clear(&item);
  257. }
  258. }
  259. void archive_show_file_menu(ArchiveBrowserView* browser, bool show) {
  260. furi_assert(browser);
  261. with_view_model(
  262. browser->view, (ArchiveBrowserViewModel * model) {
  263. if(show) {
  264. if(archive_is_item_in_array(model, model->item_idx)) {
  265. model->menu = true;
  266. model->menu_idx = 0;
  267. ArchiveFile_t* selected =
  268. files_array_get(model->files, model->item_idx - model->array_offset);
  269. selected->fav = archive_is_favorite("%s", string_get_cstr(selected->name));
  270. }
  271. } else {
  272. model->menu = false;
  273. model->menu_idx = 0;
  274. }
  275. return true;
  276. });
  277. }
  278. void archive_favorites_move_mode(ArchiveBrowserView* browser, bool active) {
  279. furi_assert(browser);
  280. with_view_model(
  281. browser->view, (ArchiveBrowserViewModel * model) {
  282. model->move_fav = active;
  283. return true;
  284. });
  285. }
  286. void archive_switch_dir(ArchiveBrowserView* browser, const char* path) {
  287. furi_assert(browser);
  288. furi_assert(path);
  289. string_set(browser->path, path);
  290. archive_get_filenames(browser, string_get_cstr(browser->path));
  291. archive_update_offset(browser);
  292. }
  293. void archive_switch_tab(ArchiveBrowserView* browser, InputKey key) {
  294. furi_assert(browser);
  295. ArchiveTabEnum tab = archive_get_tab(browser);
  296. if(key == InputKeyLeft) {
  297. tab = ((tab - 1) + ArchiveTabTotal) % ArchiveTabTotal;
  298. } else if(key == InputKeyRight) {
  299. tab = (tab + 1) % ArchiveTabTotal;
  300. }
  301. archive_set_tab(browser, tab);
  302. const char* path = archive_get_default_path(tab);
  303. bool tab_empty = true;
  304. if(tab == ArchiveTabFavorites) {
  305. if(archive_favorites_count(browser) > 0) tab_empty = false;
  306. } else if(strncmp(path, "/app:", 5) == 0) {
  307. if(archive_app_is_available(browser, path)) tab_empty = false;
  308. } else {
  309. uint32_t files_cnt = archive_dir_count_items(browser, archive_get_default_path(tab));
  310. if(files_cnt > 0) tab_empty = false;
  311. }
  312. if((tab_empty) && (tab != ArchiveTabBrowser)) {
  313. archive_switch_tab(browser, key);
  314. } else {
  315. with_view_model(
  316. browser->view, (ArchiveBrowserViewModel * model) {
  317. if(model->last_tab != model->tab_idx) {
  318. model->item_idx = 0;
  319. model->array_offset = 0;
  320. idx_last_array_reset(model->idx_last);
  321. }
  322. return false;
  323. });
  324. archive_switch_dir(browser, archive_get_default_path(tab));
  325. }
  326. archive_set_last_tab(browser, tab);
  327. }
  328. void archive_enter_dir(ArchiveBrowserView* browser, string_t name) {
  329. furi_assert(browser);
  330. furi_assert(name);
  331. if(string_size(name) >= (MAX_NAME_LEN - 1)) {
  332. return;
  333. }
  334. if(string_cmp(browser->path, name) != 0) {
  335. with_view_model(
  336. browser->view, (ArchiveBrowserViewModel * model) {
  337. idx_last_array_push_back(model->idx_last, model->item_idx);
  338. model->array_offset = 0;
  339. model->item_idx = 0;
  340. return false;
  341. });
  342. string_set(browser->path, name);
  343. }
  344. archive_dir_count_items(browser, string_get_cstr(name));
  345. archive_switch_dir(browser, string_get_cstr(browser->path));
  346. }
  347. void archive_leave_dir(ArchiveBrowserView* browser) {
  348. furi_assert(browser);
  349. const char* path = archive_get_path(browser);
  350. char* last_char_ptr = strrchr(path, '/');
  351. if(last_char_ptr) {
  352. size_t pos = last_char_ptr - path;
  353. string_left(browser->path, pos);
  354. }
  355. archive_dir_count_items(browser, path);
  356. with_view_model(
  357. browser->view, (ArchiveBrowserViewModel * model) {
  358. idx_last_array_pop_back(&model->item_idx, model->idx_last);
  359. return false;
  360. });
  361. archive_switch_dir(browser, path);
  362. }