archive_browser.c 15 KB

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