create_view.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. #include "../barcode_app.h"
  2. #include "create_view.h"
  3. #include <math.h>
  4. #define LINE_HEIGHT 16
  5. #define TEXT_PADDING 4
  6. #define TOTAL_MENU_ITEMS 5
  7. typedef enum {
  8. TypeMenuItem,
  9. FileNameMenuItem,
  10. BarcodeDataMenuItem,
  11. SaveMenuButton,
  12. DeleteMenuButton
  13. } MenuItems;
  14. /**
  15. * Took this function from blackjack
  16. * @author @teeebor
  17. */
  18. void draw_menu_item(
  19. Canvas* const canvas,
  20. const char* text,
  21. const char* value,
  22. int y,
  23. bool left_caret,
  24. bool right_caret,
  25. bool selected) {
  26. UNUSED(selected);
  27. if(y < 0 || y >= 64) {
  28. return;
  29. }
  30. if(selected) {
  31. canvas_set_color(canvas, ColorBlack);
  32. canvas_draw_box(canvas, 0, y, 123, LINE_HEIGHT);
  33. canvas_set_color(canvas, ColorWhite);
  34. }
  35. canvas_draw_str_aligned(canvas, 4, y + TEXT_PADDING, AlignLeft, AlignTop, text);
  36. if(left_caret) {
  37. canvas_draw_str_aligned(canvas, 60, y + TEXT_PADDING, AlignLeft, AlignTop, "<");
  38. }
  39. canvas_draw_str_aligned(canvas, 90, y + TEXT_PADDING, AlignCenter, AlignTop, value);
  40. if(right_caret) {
  41. canvas_draw_str_aligned(canvas, 120, y + TEXT_PADDING, AlignRight, AlignTop, ">");
  42. }
  43. canvas_set_color(canvas, ColorBlack);
  44. }
  45. void draw_button(Canvas* const canvas, const char* text, int y, bool selected) {
  46. if(selected) {
  47. canvas_set_color(canvas, ColorBlack);
  48. canvas_draw_box(canvas, 0, y, 123, LINE_HEIGHT);
  49. canvas_set_color(canvas, ColorWhite);
  50. }
  51. canvas_draw_str_aligned(canvas, 64, y + TEXT_PADDING, AlignCenter, AlignTop, text);
  52. canvas_set_color(canvas, ColorBlack);
  53. }
  54. static void app_draw_callback(Canvas* canvas, void* ctx) {
  55. furi_assert(ctx);
  56. CreateViewModel* create_view_model = ctx;
  57. BarcodeTypeObj* type_obj = create_view_model->barcode_type;
  58. if(create_view_model->barcode_type == NULL) {
  59. return;
  60. }
  61. BarcodeType selected_type = type_obj->type;
  62. int selected_menu_item = create_view_model->selected_menu_item;
  63. int total_menu_items = create_view_model->mode == EditMode ? TOTAL_MENU_ITEMS :
  64. TOTAL_MENU_ITEMS - 1;
  65. int startY = 0;
  66. //the menu items index that is/would be in view
  67. //int current_last_menu_item = selected_menu_item + 3;
  68. if(selected_menu_item > 1) {
  69. int offset = 2;
  70. if(selected_menu_item + offset > total_menu_items) {
  71. offset = 3;
  72. }
  73. startY -= (LINE_HEIGHT * (selected_menu_item - offset));
  74. }
  75. //ensure that the scroll height is atleast 1
  76. int scrollHeight = ceil(64.0 / total_menu_items);
  77. int scrollPos = scrollHeight * selected_menu_item;
  78. canvas_set_color(canvas, ColorBlack);
  79. //draw the scroll bar box
  80. canvas_draw_box(canvas, 125, scrollPos, 3, scrollHeight);
  81. //draw the scroll bar track
  82. canvas_draw_box(canvas, 126, 0, 1, 64);
  83. draw_menu_item(
  84. canvas,
  85. "Type",
  86. type_obj->name,
  87. TypeMenuItem * LINE_HEIGHT + startY,
  88. selected_type > 0,
  89. selected_type < NUMBER_OF_BARCODE_TYPES - 2,
  90. selected_menu_item == TypeMenuItem);
  91. draw_menu_item(
  92. canvas,
  93. "Name",
  94. furi_string_empty(create_view_model->file_name) ?
  95. "--" :
  96. furi_string_get_cstr(create_view_model->file_name),
  97. FileNameMenuItem * LINE_HEIGHT + startY,
  98. false,
  99. false,
  100. selected_menu_item == FileNameMenuItem);
  101. draw_menu_item(
  102. canvas,
  103. "Data",
  104. furi_string_empty(create_view_model->barcode_data) ?
  105. "--" :
  106. furi_string_get_cstr(create_view_model->barcode_data),
  107. BarcodeDataMenuItem * LINE_HEIGHT + startY,
  108. false,
  109. false,
  110. selected_menu_item == BarcodeDataMenuItem);
  111. draw_button(
  112. canvas,
  113. "Save",
  114. SaveMenuButton * LINE_HEIGHT + startY,
  115. selected_menu_item == SaveMenuButton);
  116. if(create_view_model->mode == EditMode) {
  117. draw_button(
  118. canvas,
  119. "Delete",
  120. DeleteMenuButton * LINE_HEIGHT + startY,
  121. selected_menu_item == DeleteMenuButton);
  122. }
  123. }
  124. void text_input_callback(void* ctx) {
  125. CreateView* create_view_object = ctx;
  126. with_view_model(
  127. create_view_object->view,
  128. CreateViewModel * model,
  129. {
  130. if(create_view_object->setter == FileNameSetter) {
  131. furi_string_set_str(model->file_name, create_view_object->input);
  132. }
  133. if(create_view_object->setter == BarcodeDataSetter) {
  134. furi_string_set_str(model->barcode_data, create_view_object->input);
  135. }
  136. },
  137. true);
  138. view_dispatcher_switch_to_view(
  139. create_view_object->barcode_app->view_dispatcher, CreateBarcodeView);
  140. }
  141. static bool app_input_callback(InputEvent* input_event, void* ctx) {
  142. furi_assert(ctx);
  143. if(input_event->key == InputKeyBack) {
  144. return false;
  145. }
  146. CreateView* create_view_object = ctx;
  147. //get the currently selected menu item from the model
  148. int selected_menu_item = 0;
  149. BarcodeTypeObj* barcode_type = NULL;
  150. FuriString* file_name;
  151. FuriString* barcode_data;
  152. CreateMode mode;
  153. with_view_model(
  154. create_view_object->view,
  155. CreateViewModel * model,
  156. {
  157. selected_menu_item = model->selected_menu_item;
  158. barcode_type = model->barcode_type;
  159. file_name = model->file_name;
  160. barcode_data = model->barcode_data;
  161. mode = model->mode;
  162. },
  163. true);
  164. int total_menu_items = mode == EditMode ? TOTAL_MENU_ITEMS : TOTAL_MENU_ITEMS - 1;
  165. if(input_event->type == InputTypePress) {
  166. if(input_event->key == InputKeyUp && selected_menu_item > 0) {
  167. selected_menu_item--;
  168. } else if(input_event->key == InputKeyDown && selected_menu_item < total_menu_items - 1) {
  169. selected_menu_item++;
  170. } else if(input_event->key == InputKeyLeft) {
  171. if(selected_menu_item == TypeMenuItem && barcode_type != NULL) { //Select Barcode Type
  172. if(barcode_type->type > 0) {
  173. barcode_type = barcode_type_objs[barcode_type->type - 1];
  174. }
  175. }
  176. } else if(input_event->key == InputKeyRight) {
  177. if(selected_menu_item == TypeMenuItem && barcode_type != NULL) { //Select Barcode Type
  178. if(barcode_type->type < NUMBER_OF_BARCODE_TYPES - 2) {
  179. barcode_type = barcode_type_objs[barcode_type->type + 1];
  180. }
  181. }
  182. } else if(input_event->key == InputKeyOk) {
  183. if(selected_menu_item == FileNameMenuItem && barcode_type != NULL) {
  184. create_view_object->setter = FileNameSetter;
  185. snprintf(
  186. create_view_object->input,
  187. sizeof(create_view_object->input),
  188. "%s",
  189. furi_string_get_cstr(file_name));
  190. text_input_set_result_callback(
  191. create_view_object->barcode_app->text_input,
  192. text_input_callback,
  193. create_view_object,
  194. create_view_object->input,
  195. TEXT_BUFFER_SIZE - BARCODE_EXTENSION_LENGTH, //remove the barcode length
  196. //clear default text
  197. false);
  198. text_input_set_header_text(
  199. create_view_object->barcode_app->text_input, "File Name");
  200. view_dispatcher_switch_to_view(
  201. create_view_object->barcode_app->view_dispatcher, TextInputView);
  202. }
  203. if(selected_menu_item == BarcodeDataMenuItem && barcode_type != NULL) {
  204. create_view_object->setter = BarcodeDataSetter;
  205. snprintf(
  206. create_view_object->input,
  207. sizeof(create_view_object->input),
  208. "%s",
  209. furi_string_get_cstr(barcode_data));
  210. text_input_set_result_callback(
  211. create_view_object->barcode_app->text_input,
  212. text_input_callback,
  213. create_view_object,
  214. create_view_object->input,
  215. TEXT_BUFFER_SIZE,
  216. //clear default text
  217. false);
  218. text_input_set_header_text(
  219. create_view_object->barcode_app->text_input, "Barcode Data");
  220. view_dispatcher_switch_to_view(
  221. create_view_object->barcode_app->view_dispatcher, TextInputView);
  222. }
  223. if(selected_menu_item == SaveMenuButton && barcode_type != NULL) {
  224. save_barcode(create_view_object);
  225. }
  226. if(selected_menu_item == DeleteMenuButton && barcode_type != NULL) {
  227. if(mode == EditMode) {
  228. remove_barcode(create_view_object);
  229. } else if(mode == NewMode) {
  230. view_dispatcher_switch_to_view(
  231. create_view_object->barcode_app->view_dispatcher, MainMenuView);
  232. }
  233. }
  234. }
  235. }
  236. //change the currently selected menu item
  237. with_view_model(
  238. create_view_object->view,
  239. CreateViewModel * model,
  240. {
  241. model->selected_menu_item = selected_menu_item;
  242. model->barcode_type = barcode_type;
  243. },
  244. true);
  245. return true;
  246. }
  247. CreateView* create_view_allocate(BarcodeApp* barcode_app) {
  248. furi_assert(barcode_app);
  249. CreateView* create_view_object = malloc(sizeof(CreateView));
  250. create_view_object->view = view_alloc();
  251. create_view_object->barcode_app = barcode_app;
  252. view_set_context(create_view_object->view, create_view_object);
  253. view_allocate_model(create_view_object->view, ViewModelTypeLocking, sizeof(CreateViewModel));
  254. view_set_draw_callback(create_view_object->view, app_draw_callback);
  255. view_set_input_callback(create_view_object->view, app_input_callback);
  256. return create_view_object;
  257. }
  258. void create_view_free_model(CreateView* create_view_object) {
  259. with_view_model(
  260. create_view_object->view,
  261. CreateViewModel * model,
  262. {
  263. if(model->file_path != NULL) {
  264. furi_string_free(model->file_path);
  265. }
  266. if(model->file_name != NULL) {
  267. furi_string_free(model->file_name);
  268. }
  269. if(model->barcode_data != NULL) {
  270. furi_string_free(model->barcode_data);
  271. }
  272. },
  273. true);
  274. }
  275. void remove_barcode(CreateView* create_view_object) {
  276. Storage* storage = furi_record_open(RECORD_STORAGE);
  277. bool success = false;
  278. with_view_model(
  279. create_view_object->view,
  280. CreateViewModel * model,
  281. {
  282. FURI_LOG_I(TAG, "Attempting to remove file");
  283. if(model->file_path != NULL) {
  284. FURI_LOG_I(TAG, "Removing File: %s", furi_string_get_cstr(model->file_path));
  285. if(storage_simply_remove(storage, furi_string_get_cstr(model->file_path))) {
  286. FURI_LOG_I(
  287. TAG,
  288. "File: \"%s\" was successfully removed",
  289. furi_string_get_cstr(model->file_path));
  290. success = true;
  291. } else {
  292. FURI_LOG_E(TAG, "Unable to remove file!");
  293. success = false;
  294. }
  295. } else {
  296. FURI_LOG_E(TAG, "Could not remove barcode file");
  297. success = false;
  298. }
  299. },
  300. true);
  301. furi_record_close(RECORD_STORAGE);
  302. with_view_model(
  303. create_view_object->barcode_app->message_view->view,
  304. MessageViewModel * model,
  305. {
  306. if(success) {
  307. model->message = "File Deleted";
  308. } else {
  309. model->message = "Could not delete file";
  310. }
  311. },
  312. true);
  313. view_dispatcher_switch_to_view(
  314. create_view_object->barcode_app->view_dispatcher, MessageErrorView);
  315. }
  316. void save_barcode(CreateView* create_view_object) {
  317. BarcodeTypeObj* barcode_type = NULL;
  318. FuriString* file_path; //this may be empty
  319. FuriString* file_name;
  320. FuriString* barcode_data;
  321. CreateMode mode;
  322. with_view_model(
  323. create_view_object->view,
  324. CreateViewModel * model,
  325. {
  326. file_path = model->file_path;
  327. file_name = model->file_name;
  328. barcode_data = model->barcode_data;
  329. barcode_type = model->barcode_type;
  330. mode = model->mode;
  331. },
  332. true);
  333. if(file_name == NULL || furi_string_empty(file_name)) {
  334. FURI_LOG_E(TAG, "File Name cannot be empty");
  335. return;
  336. }
  337. if(barcode_data == NULL || furi_string_empty(barcode_data)) {
  338. FURI_LOG_E(TAG, "Barcode Data cannot be empty");
  339. return;
  340. }
  341. if(barcode_type == NULL) {
  342. FURI_LOG_E(TAG, "Type not defined");
  343. return;
  344. }
  345. bool success = false;
  346. FuriString* full_file_path = furi_string_alloc_set(DEFAULT_USER_BARCODES);
  347. furi_string_push_back(full_file_path, '/');
  348. furi_string_cat(full_file_path, file_name);
  349. furi_string_cat_str(full_file_path, BARCODE_EXTENSION);
  350. Storage* storage = furi_record_open(RECORD_STORAGE);
  351. if(mode == EditMode) {
  352. if(!furi_string_empty(file_path)) {
  353. if(!furi_string_equal(file_path, full_file_path)) {
  354. FS_Error error = storage_common_rename(
  355. storage,
  356. furi_string_get_cstr(file_path),
  357. furi_string_get_cstr(full_file_path));
  358. if(error != FSE_OK) {
  359. FURI_LOG_E(TAG, "Rename error: %s", storage_error_get_desc(error));
  360. } else {
  361. FURI_LOG_I(TAG, "Rename Success");
  362. }
  363. }
  364. }
  365. }
  366. FlipperFormat* ff = flipper_format_file_alloc(storage);
  367. FURI_LOG_I(TAG, "Saving Barcode to: %s", furi_string_get_cstr(full_file_path));
  368. bool file_opened_status = false;
  369. if(mode == NewMode) {
  370. file_opened_status =
  371. flipper_format_file_open_new(ff, furi_string_get_cstr(full_file_path));
  372. } else if(mode == EditMode) {
  373. file_opened_status =
  374. flipper_format_file_open_always(ff, furi_string_get_cstr(full_file_path));
  375. }
  376. if(file_opened_status) {
  377. // Filetype: Barcode
  378. // Version: 1
  379. // # Types - UPC-A, EAN-8, EAN-13, CODE-39
  380. // Type: CODE-39
  381. // Data: AB
  382. flipper_format_write_string_cstr(ff, "Filetype", "Barcode");
  383. flipper_format_write_string_cstr(ff, "Version", FILE_VERSION);
  384. flipper_format_write_comment_cstr(
  385. ff, "Types - UPC-A, EAN-8, EAN-13, CODE-39, CODE-128, Codabar");
  386. flipper_format_write_string_cstr(ff, "Type", barcode_type->name);
  387. flipper_format_write_string_cstr(ff, "Data", furi_string_get_cstr(barcode_data));
  388. success = true;
  389. } else {
  390. FURI_LOG_E(TAG, "Save error");
  391. success = false;
  392. }
  393. furi_string_free(full_file_path);
  394. flipper_format_free(ff);
  395. furi_record_close(RECORD_STORAGE);
  396. with_view_model(
  397. create_view_object->barcode_app->message_view->view,
  398. MessageViewModel * model,
  399. {
  400. if(success) {
  401. model->message = "File Saved!";
  402. } else {
  403. model->message = "A saving error has occurred";
  404. }
  405. },
  406. true);
  407. view_dispatcher_switch_to_view(
  408. create_view_object->barcode_app->view_dispatcher, MessageErrorView);
  409. }
  410. void create_view_free(CreateView* create_view_object) {
  411. furi_assert(create_view_object);
  412. create_view_free_model(create_view_object);
  413. view_free(create_view_object->view);
  414. free(create_view_object);
  415. }
  416. View* create_get_view(CreateView* create_view_object) {
  417. furi_assert(create_view_object);
  418. return create_view_object->view;
  419. }