view_build.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. /* Copyright (C) 2022-2023 Salvatore Sanfilippo -- All Rights Reserved
  2. * See the LICENSE file for information about the license. */
  3. #include "app.h"
  4. extern ProtoViewDecoder *Decoders[]; // Defined in signal.c.
  5. /* Our view private data. */
  6. typedef struct {
  7. ProtoViewDecoder *decoder; // Decoder we are using to create a message.
  8. uint32_t cur_decoder; // Decoder index when we are yet selecting
  9. // a decoder. Used when decoder is NULL.
  10. ProtoViewFieldSet *fieldset; // The fields to populate.
  11. uint32_t cur_field; // Field we are editing right now. This
  12. // is the index inside the 'fieldset'
  13. // fields.
  14. } BuildViewPrivData;
  15. /* Not all the decoders support message bulding, so we can't just
  16. * increment / decrement the cur_decoder index here. */
  17. static void select_next_decoder(ProtoViewApp *app) {
  18. BuildViewPrivData *privdata = app->view_privdata;
  19. do {
  20. privdata->cur_decoder++;
  21. if (Decoders[privdata->cur_decoder] == NULL)
  22. privdata->cur_decoder = 0;
  23. } while(Decoders[privdata->cur_decoder]->get_fields == NULL);
  24. }
  25. /* Like select_next_decoder() but goes backward. */
  26. static void select_prev_decoder(ProtoViewApp *app) {
  27. BuildViewPrivData *privdata = app->view_privdata;
  28. do {
  29. if (privdata->cur_decoder == 0) {
  30. /* Go one after the last one to wrap around. */
  31. while(Decoders[privdata->cur_decoder]) privdata->cur_decoder++;
  32. }
  33. privdata->cur_decoder--;
  34. } while(Decoders[privdata->cur_decoder]->get_fields == NULL);
  35. }
  36. /* Render the view to select the decoder, among the ones that
  37. * support message building. */
  38. static void render_view_select_decoder(Canvas *const canvas, ProtoViewApp *app) {
  39. BuildViewPrivData *privdata = app->view_privdata;
  40. canvas_set_font(canvas, FontPrimary);
  41. canvas_draw_str(canvas, 0, 9, "Signal builder");
  42. canvas_set_font(canvas, FontSecondary);
  43. canvas_draw_str(canvas, 0, 19, "up/down: select, ok: choose");
  44. // When entering the view, the current decoder is just set to zero.
  45. // Seek the next valid if needed.
  46. if (Decoders[privdata->cur_decoder]->get_fields == NULL)
  47. select_next_decoder(app);
  48. canvas_set_font(canvas, FontPrimary);
  49. canvas_draw_str(canvas, 0, 9, "Signal builder");
  50. canvas_draw_str_aligned(canvas,64,36,AlignCenter,AlignCenter,
  51. Decoders[privdata->cur_decoder]->name);
  52. }
  53. /* Render the view that allows the user to populate the fields needed
  54. * for the selected decoder to build a message. */
  55. static void render_view_set_fields(Canvas *const canvas, ProtoViewApp *app) {
  56. BuildViewPrivData *privdata = app->view_privdata;
  57. char buf[32];
  58. snprintf(buf,sizeof(buf), "%s field %d/%d",
  59. privdata->decoder->name, (int)privdata->cur_field+1,
  60. (int)privdata->fieldset->numfields);
  61. canvas_set_font(canvas, FontPrimary);
  62. canvas_draw_str(canvas, 0, 9, buf);
  63. canvas_set_font(canvas, FontSecondary);
  64. canvas_draw_str(canvas, 0, 19, "up/down: next field, ok: edit");
  65. canvas_draw_str(canvas, 0, 62, "Long ok: create, < > incr/decr");
  66. /* Write the field name, type, current content. For this part we
  67. * write white text on black screen, to visually separate the UI
  68. * description part from the UI current field editing part. */
  69. canvas_set_color(canvas,ColorBlack);
  70. canvas_draw_box(canvas,0,21,128,32);
  71. canvas_set_color(canvas,ColorWhite);
  72. ProtoViewField *field = privdata->fieldset->fields[privdata->cur_field];
  73. snprintf(buf,sizeof(buf), "%s %s:%d", field->name,
  74. field_get_type_name(field), (int)field->len);
  75. canvas_set_font(canvas, FontPrimary);
  76. canvas_draw_str_aligned(canvas,64,30,AlignCenter,AlignCenter,buf);
  77. canvas_set_font(canvas, FontSecondary);
  78. canvas_draw_str_aligned(canvas,63,45,AlignCenter,AlignCenter,"\"foobar\"");
  79. }
  80. /* Render the build message view. */
  81. void render_view_build_message(Canvas *const canvas, ProtoViewApp *app) {
  82. BuildViewPrivData *privdata = app->view_privdata;
  83. if (privdata->decoder)
  84. render_view_set_fields(canvas,app);
  85. else
  86. render_view_select_decoder(canvas,app);
  87. }
  88. /* Handle input for the decoder selection. */
  89. static void process_input_select_decoder(ProtoViewApp *app, InputEvent input) {
  90. BuildViewPrivData *privdata = app->view_privdata;
  91. if (input.type == InputTypeShort) {
  92. if (input.key == InputKeyOk) {
  93. privdata->decoder = Decoders[privdata->cur_decoder];
  94. privdata->fieldset = fieldset_new();
  95. privdata->decoder->get_fields(privdata->fieldset);
  96. // Now we use the subview system in order to protect the
  97. // message editing mode from accidental < or > presses.
  98. // Since we are technically into a subview now, we'll have
  99. // control of < and >.
  100. InputEvent ii = {.type = InputTypePress, .key = InputKeyDown};
  101. ui_process_subview_updown(app,ii,2);
  102. } else if (input.key == InputKeyDown) {
  103. select_next_decoder(app);
  104. } else if (input.key == InputKeyUp) {
  105. select_prev_decoder(app);
  106. }
  107. }
  108. }
  109. /* Handle input for fields editing mode. */
  110. static void process_input_set_fields(ProtoViewApp *app, InputEvent input) {
  111. BuildViewPrivData *privdata = app->view_privdata;
  112. ProtoViewFieldSet *fs = privdata->fieldset;
  113. if (input.type == InputTypeShort) {
  114. if (input.key == InputKeyOk) {
  115. } else if (input.key == InputKeyDown) {
  116. privdata->cur_field = (privdata->cur_field+1) % fs->numfields;
  117. } else if (input.key == InputKeyUp) {
  118. if (privdata->cur_field == 0)
  119. privdata->cur_field = fs->numfields-1;
  120. else
  121. privdata->cur_field--;
  122. }
  123. }
  124. }
  125. /* Handle input for the build message view. */
  126. void process_input_build_message(ProtoViewApp *app, InputEvent input) {
  127. BuildViewPrivData *privdata = app->view_privdata;
  128. if (privdata->decoder)
  129. process_input_set_fields(app,input);
  130. else
  131. process_input_select_decoder(app,input);
  132. }
  133. /* Called on exit for cleanup. */
  134. void view_exit_build_message(ProtoViewApp *app) {
  135. BuildViewPrivData *privdata = app->view_privdata;
  136. if (privdata->fieldset) fieldset_free(privdata->fieldset);
  137. }