LTVA1 2 лет назад
Родитель
Сommit
139d8483af
12 измененных файлов с 257 добавлено и 55 удалено
  1. 1 1
      application.fam
  2. 5 7
      diskop.c
  3. 8 2
      flizzer_tracker.c
  4. 13 0
      flizzer_tracker.h
  5. 20 20
      font.h
  6. 47 2
      init_deinit.c
  7. 1 1
      input/pattern.c
  8. 22 0
      input/sequence.c
  9. 22 0
      input/songinfo.c
  10. 97 13
      input_event.c
  11. 9 2
      input_event.h
  12. 12 7
      view/pattern_editor.c

+ 1 - 1
application.fam

@@ -6,7 +6,7 @@ App(
     cdefines=["APP_FLIZZER_TRACKER"],
     stack_size=2 * 1024,
     order=90,
-	fap_version=(0, 1),
+	fap_version=(0, 2),
 	fap_description="An advanced Flipper Zero chiptune tracker with 4 channels",
 	fap_author="LTVA",
     fap_weburl="https://github.com/LTVA1/flizzer_tracker",

+ 5 - 7
diskop.c

@@ -64,8 +64,7 @@ void save_instrument_inner(Stream* stream, Instrument* inst) {
     UNUSED(rwops);
 }
 
-bool save_instrument(FlizzerTrackerApp* tracker, FuriString* filepath)
-{
+bool save_instrument(FlizzerTrackerApp* tracker, FuriString* filepath) {
     bool file_removed =
         storage_simply_remove(tracker->storage, furi_string_get_cstr(filepath)); // just in case
     bool open_file = file_stream_open(
@@ -177,8 +176,7 @@ bool load_instrument_disk(TrackerSong* song, uint8_t inst, Stream* stream) {
     if(strcmp(header, INST_FILE_SIG) == 0) {
         rwops = stream_read(stream, (uint8_t*)&version, sizeof(version));
 
-        if(version <= TRACKER_ENGINE_VERSION)
-        {
+        if(version <= TRACKER_ENGINE_VERSION) {
             load_instrument_inner(stream, song->instrument[inst], version);
         }
     }
@@ -187,12 +185,12 @@ bool load_instrument_disk(TrackerSong* song, uint8_t inst, Stream* stream) {
     return false;
 }
 
-bool load_instrument_util(FlizzerTrackerApp* tracker, FuriString* filepath)
-{
+bool load_instrument_util(FlizzerTrackerApp* tracker, FuriString* filepath) {
     bool open_file = file_stream_open(
         tracker->stream, furi_string_get_cstr(filepath), FSAM_READ, FSOM_OPEN_ALWAYS);
 
-    bool result = load_instrument_disk(&tracker->song, tracker->current_instrument, tracker->stream);
+    bool result =
+        load_instrument_disk(&tracker->song, tracker->current_instrument, tracker->stream);
 
     tracker->is_loading_instrument = false;
     file_stream_close(tracker->stream);

+ 8 - 2
flizzer_tracker.c

@@ -39,8 +39,14 @@ void draw_callback(Canvas* canvas, void* ctx) {
             tracker_engine_set_song(&tracker->tracker_engine, &tracker->song);
         }
 
-        draw_songinfo_view(canvas, tracker);
-        draw_sequence_view(canvas, tracker);
+        if(tracker->focus != EDIT_PATTERN) {
+            draw_songinfo_view(canvas, tracker);
+        }
+
+        if(tracker->focus != EDIT_PATTERN) {
+            draw_sequence_view(canvas, tracker);
+        }
+
         draw_pattern_view(canvas, tracker);
         break;
     }

+ 13 - 0
flizzer_tracker.h

@@ -133,6 +133,7 @@ typedef enum {
     VIEW_TRACKER,
     VIEW_KEYBOARD,
     VIEW_SUBMENU_PATTERN,
+    VIEW_SUBMENU_PATTERN_COPYPASTE,
     VIEW_SUBMENU_INSTRUMENT,
     VIEW_FILE_OVERWRITE,
     VIEW_INSTRUMENT_FILE_OVERWRITE,
@@ -147,6 +148,13 @@ typedef enum {
     SUBMENU_PATTERN_EXIT,
 } PatternSubmenuParams;
 
+typedef enum {
+    SUBMENU_PATTERN_COPYPASTE_COPY,
+    SUBMENU_PATTERN_COPYPASTE_PASTE,
+    SUBMENU_PATTERN_COPYPASTE_CUT,
+    SUBMENU_PATTERN_COPYPASTE_CLEAR,
+} PatternCopypasteSubmenuParams;
+
 typedef enum {
     SUBMENU_INSTRUMENT_LOAD,
     SUBMENU_INSTRUMENT_SAVE,
@@ -165,6 +173,7 @@ typedef struct {
     FuriString* filepath;
     DialogsApp* dialogs;
     Submenu* pattern_submenu;
+    Submenu* pattern_copypaste_submenu;
     Submenu* instrument_submenu;
     VariableItemList* settings_list;
     Widget* overwrite_file_widget;
@@ -189,6 +198,8 @@ typedef struct {
 
     uint8_t inst_editor_shift;
 
+    int16_t source_pattern_index;
+
     bool editing;
     bool was_editing;
 
@@ -198,6 +209,8 @@ typedef struct {
     bool is_saving_instrument;
     bool showing_help;
 
+    bool cut_pattern; //if we need to clear the pattern we pasted from
+
     bool quit;
 
     char eq[2];

+ 20 - 20
font.h

@@ -9,23 +9,23 @@ BBX Build Mode: 0
 // this is a modified version with dot and semicolon moved 1 pixel to the left; lowercase symbols removed to save space
 // changed "G", "N" and "V" glyphs
 const uint8_t u8g2_font_tom_thumb_4x6_tr[610] =
-  "a\0\2\2\2\3\2\3\4\3\5\0\0\5\0\5\0\1`\0\0\2E\0\4@\62\1\4@\62\2"
-  "\4@\62\3\4@\62\4\4@\62\5\4@\62\6\4@\62\7\4@\62\10\4@\62\11\4@\62\12"
-  "\4@\62\13\4@\62\14\4@\62\15\4@\62\16\4@\62\17\4@\62\20\4@\62\21\4@\62\22"
-  "\4@\62\23\4@\62\24\4@\62\25\4@\62\26\4@\62\27\4@\62\30\4@\62\31\4@\62\32"
-  "\4@\62\33\4@\62\34\4@\62\35\4@\62\36\4@\62\37\4@\62 \4@\62!\5u\62+"
-  "\42\6\313\63I\5#\10W\62i\250\241\2$\10Wr#\216\230\0%\10W\62\31\265Q\0&\10"
-  "W\62J\215\224\4'\5\351\63\2(\6vr\252\14)\7V\62\61%\5*\6O\63\251\3+\7"
-  "\317ri%\0,\5Jr\12-\5G\63\3.\5E\62\1/\7W\262U\31\1\60\7Wr\313"
-  "Z\0\61\6Vr\253\1\62\7W\62\32\244r\63\11W\62\32\244\14\26\0\64\7W\62I\215X\65"
-  "\10W\62#j\260\0\66\7Wrs\244\21\67\7W\62\63\225\21\70\10W\62#\15\65\2\71\10W"
-  "\62#\215\270\0:\5\315\62);\7Rr\31(\0<\10W\262\251\6\31\4=\6\317\62\33\14>"
-  "\11W\62\31d\220J\0\77\10W\62\63e\230\0@\7Wr\325\320@A\7Wr\325P*B\10"
-  "W\62*\255\264\0C\7Wr\263\6\2D\7W\62*Y\13E\7W\62#\216\70F\10W\62#"
-  "\216\30\1G\7Wr\63\251$H\10W\62I\15\245\2I\7W\62+V\3J\7W\262\245\252\0"
-  "K\10W\62I\255\244\2L\6W\62\261\71M\10W\62i\14\245\2N\7W\62*\271\2O\7W"
-  "r\225U\1P\10W\62*\255\30\1Q\7Wr\225\32IR\7W\62*\215US\10Wr\33d"
-  "\260\0T\7W\62+\266\0U\7W\62\311\225\4V\7W\62\311U\1W\10W\62I\215\241\2X"
-  "\10W\62I\265T\0Y\10W\62I\225\25\0Z\7W\62\63\225\3[\7W\62#\226\3\134\7\317"
-  "\62\31d\20]\7W\62\263\34\1^\5\313s\15_\5G\62\3`\5\312\63\61\0\0\0\4\377\377"
-  "\0";
+    "a\0\2\2\2\3\2\3\4\3\5\0\0\5\0\5\0\1`\0\0\2E\0\4@\62\1\4@\62\2"
+    "\4@\62\3\4@\62\4\4@\62\5\4@\62\6\4@\62\7\4@\62\10\4@\62\11\4@\62\12"
+    "\4@\62\13\4@\62\14\4@\62\15\4@\62\16\4@\62\17\4@\62\20\4@\62\21\4@\62\22"
+    "\4@\62\23\4@\62\24\4@\62\25\4@\62\26\4@\62\27\4@\62\30\4@\62\31\4@\62\32"
+    "\4@\62\33\4@\62\34\4@\62\35\4@\62\36\4@\62\37\4@\62 \4@\62!\5u\62+"
+    "\42\6\313\63I\5#\10W\62i\250\241\2$\10Wr#\216\230\0%\10W\62\31\265Q\0&\10"
+    "W\62J\215\224\4'\5\351\63\2(\6vr\252\14)\7V\62\61%\5*\6O\63\251\3+\7"
+    "\317ri%\0,\5Jr\12-\5G\63\3.\5E\62\1/\7W\262U\31\1\60\7Wr\313"
+    "Z\0\61\6Vr\253\1\62\7W\62\32\244r\63\11W\62\32\244\14\26\0\64\7W\62I\215X\65"
+    "\10W\62#j\260\0\66\7Wrs\244\21\67\7W\62\63\225\21\70\10W\62#\15\65\2\71\10W"
+    "\62#\215\270\0:\5\315\62);\7Rr\31(\0<\10W\262\251\6\31\4=\6\317\62\33\14>"
+    "\11W\62\31d\220J\0\77\10W\62\63e\230\0@\7Wr\325\320@A\7Wr\325P*B\10"
+    "W\62*\255\264\0C\7Wr\263\6\2D\7W\62*Y\13E\7W\62#\216\70F\10W\62#"
+    "\216\30\1G\7Wr\63\251$H\10W\62I\15\245\2I\7W\62+V\3J\7W\262\245\252\0"
+    "K\10W\62I\255\244\2L\6W\62\261\71M\10W\62i\14\245\2N\7W\62*\271\2O\7W"
+    "r\225U\1P\10W\62*\255\30\1Q\7Wr\225\32IR\7W\62*\215US\10Wr\33d"
+    "\260\0T\7W\62+\266\0U\7W\62\311\225\4V\7W\62\311U\1W\10W\62I\215\241\2X"
+    "\10W\62I\265T\0Y\10W\62I\225\25\0Z\7W\62\63\225\3[\7W\62#\226\3\134\7\317"
+    "\62\31d\20]\7W\62\263\34\1^\5\313s\15_\5G\62\3`\5\312\63\61\0\0\0\4\377\377"
+    "\0";

+ 47 - 2
init_deinit.c

@@ -82,9 +82,12 @@ FlizzerTrackerApp* init_tracker(
         tracker->view_dispatcher, VIEW_KEYBOARD, text_input_get_view(tracker->text_input));
 
     tracker->pattern_submenu = submenu_alloc();
+    tracker->pattern_copypaste_submenu = submenu_alloc();
     tracker->instrument_submenu = submenu_alloc();
 
     view_set_previous_callback(submenu_get_view(tracker->pattern_submenu), submenu_exit_callback);
+    view_set_previous_callback(
+        submenu_get_view(tracker->pattern_copypaste_submenu), submenu_exit_callback);
     view_set_previous_callback(
         submenu_get_view(tracker->instrument_submenu), submenu_exit_callback);
 
@@ -108,16 +111,53 @@ FlizzerTrackerApp* init_tracker(
         tracker->pattern_submenu, "Exit", SUBMENU_PATTERN_EXIT, submenu_callback, tracker);
 
     submenu_add_item(
-        tracker->instrument_submenu, "Load instrument", SUBMENU_INSTRUMENT_LOAD, submenu_callback, tracker);
+        tracker->instrument_submenu,
+        "Load instrument",
+        SUBMENU_INSTRUMENT_LOAD,
+        submenu_callback,
+        tracker);
     submenu_add_item(
-        tracker->instrument_submenu, "Save instrument", SUBMENU_INSTRUMENT_SAVE, submenu_callback, tracker);
+        tracker->instrument_submenu,
+        "Save instrument",
+        SUBMENU_INSTRUMENT_SAVE,
+        submenu_callback,
+        tracker);
     submenu_add_item(
         tracker->instrument_submenu, "Exit", SUBMENU_INSTRUMENT_EXIT, submenu_callback, tracker);
 
+    submenu_add_item(
+        tracker->pattern_copypaste_submenu,
+        "Copy",
+        SUBMENU_PATTERN_COPYPASTE_COPY,
+        submenu_copypaste_callback,
+        tracker);
+    submenu_add_item(
+        tracker->pattern_copypaste_submenu,
+        "Paste",
+        SUBMENU_PATTERN_COPYPASTE_PASTE,
+        submenu_copypaste_callback,
+        tracker);
+    submenu_add_item(
+        tracker->pattern_copypaste_submenu,
+        "Cut",
+        SUBMENU_PATTERN_COPYPASTE_CUT,
+        submenu_copypaste_callback,
+        tracker);
+    submenu_add_item(
+        tracker->pattern_copypaste_submenu,
+        "Clear",
+        SUBMENU_PATTERN_COPYPASTE_CLEAR,
+        submenu_copypaste_callback,
+        tracker);
+
     view_dispatcher_add_view(
         tracker->view_dispatcher,
         VIEW_SUBMENU_PATTERN,
         submenu_get_view(tracker->pattern_submenu));
+    view_dispatcher_add_view(
+        tracker->view_dispatcher,
+        VIEW_SUBMENU_PATTERN_COPYPASTE,
+        submenu_get_view(tracker->pattern_copypaste_submenu));
     view_dispatcher_add_view(
         tracker->view_dispatcher,
         VIEW_SUBMENU_INSTRUMENT,
@@ -206,6 +246,9 @@ FlizzerTrackerApp* init_tracker(
 
     set_default_song(tracker);
 
+    tracker->focus = EDIT_SONGINFO;
+    tracker->source_pattern_index = -1;
+
     return tracker;
 }
 
@@ -219,6 +262,7 @@ void deinit_tracker(FlizzerTrackerApp* tracker) {
     view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SETTINGS);
     view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_FILE_OVERWRITE);
     view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SUBMENU_INSTRUMENT);
+    view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SUBMENU_PATTERN_COPYPASTE);
     view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_SUBMENU_PATTERN);
     view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_KEYBOARD);
     view_dispatcher_remove_view(tracker->view_dispatcher, VIEW_TRACKER);
@@ -228,6 +272,7 @@ void deinit_tracker(FlizzerTrackerApp* tracker) {
     variable_item_list_free(tracker->settings_list);
 
     submenu_free(tracker->pattern_submenu);
+    submenu_free(tracker->pattern_copypaste_submenu);
     submenu_free(tracker->instrument_submenu);
 
     widget_free(tracker->overwrite_file_widget);

+ 1 - 1
input/pattern.c

@@ -259,7 +259,7 @@ void pattern_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event)
     if(event->input.key == InputKeyDown && event->input.type == InputTypeLong &&
        !(tracker->editing)) {
         tracker->tracker_engine.pattern_position =
-            tracker->tracker_engine.song->pattern_length - 1; // return to pattern 1st row
+            tracker->tracker_engine.song->pattern_length - 1; // go to pattern last row
         return;
     }
 

+ 22 - 0
input/sequence.c

@@ -75,6 +75,28 @@ void sequence_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event)
         tracker->editing = !tracker->editing;
     }
 
+    if(event->input.key == InputKeyOk && event->input.type == InputTypeLong) {
+        if(!(tracker->editing)) {
+            if(tracker->tracker_engine.playing) {
+                stop_song(tracker);
+            }
+
+            else {
+                if(tracker->tracker_engine.pattern_position == tracker->song.pattern_length - 1 &&
+                   tracker->tracker_engine.sequence_position ==
+                       tracker->song.num_sequence_steps -
+                           1) // if we are at the very end of the song
+                {
+                    stop_song(tracker);
+                }
+
+                else {
+                    play_song(tracker, true);
+                }
+            }
+        }
+    }
+
     if(event->input.key == InputKeyRight && event->input.type == InputTypeShort) {
         tracker->current_digit++;
 

+ 22 - 0
input/songinfo.c

@@ -117,6 +117,28 @@ void songinfo_edit_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event)
         tracker->editing = !tracker->editing;
     }
 
+    if(event->input.key == InputKeyOk && event->input.type == InputTypeLong) {
+        if(!(tracker->editing)) {
+            if(tracker->tracker_engine.playing) {
+                stop_song(tracker);
+            }
+
+            else {
+                if(tracker->tracker_engine.pattern_position == tracker->song.pattern_length - 1 &&
+                   tracker->tracker_engine.sequence_position ==
+                       tracker->song.num_sequence_steps -
+                           1) // if we are at the very end of the song
+                {
+                    stop_song(tracker);
+                }
+
+                else {
+                    play_song(tracker, true);
+                }
+            }
+        }
+    }
+
     if(event->input.key == InputKeyRight && event->input.type == InputTypeShort) {
         switch(tracker->selected_param) {
         default: {

+ 97 - 13
input_event.c

@@ -7,7 +7,8 @@
 void return_from_keyboard_callback(void* ctx) {
     FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx;
 
-    if(!tracker->is_loading && !tracker->is_saving && !tracker->is_loading_instrument && !tracker->is_saving_instrument) {
+    if(!tracker->is_loading && !tracker->is_saving && !tracker->is_loading_instrument &&
+       !tracker->is_saving_instrument) {
         uint8_t string_length = 0;
         char* string = NULL;
 
@@ -71,15 +72,21 @@ void return_from_keyboard_callback(void* ctx) {
 
         tracker->filepath = furi_string_alloc();
         furi_string_cat_printf(
-            tracker->filepath, "%s/%s%s", FLIZZER_TRACKER_INSTRUMENTS_FOLDER, tracker->filename, INST_FILE_EXT);
+            tracker->filepath,
+            "%s/%s%s",
+            FLIZZER_TRACKER_INSTRUMENTS_FOLDER,
+            tracker->filename,
+            INST_FILE_EXT);
 
         if(storage_file_exists(tracker->storage, furi_string_get_cstr(tracker->filepath))) {
-            view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_INSTRUMENT_FILE_OVERWRITE);
+            view_dispatcher_switch_to_view(
+                tracker->view_dispatcher, VIEW_INSTRUMENT_FILE_OVERWRITE);
             return;
         }
 
         else {
-            FlizzerTrackerEvent event = {.type = EventTypeSaveInstrument, .input = {{0}}, .period = 0};
+            FlizzerTrackerEvent event = {
+                .type = EventTypeSaveInstrument, .input = {{0}}, .period = 0};
             furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever);
         }
     }
@@ -94,7 +101,8 @@ void overwrite_file_widget_yes_input_callback(GuiButtonType result, InputType ty
         tracker->is_saving = true;
         view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER);
         // save_song(tracker, tracker->filepath);
-        static FlizzerTrackerEvent event = {.type = EventTypeSaveSong, .input = {{0}}, .period = 0};
+        static FlizzerTrackerEvent event = {
+            .type = EventTypeSaveSong, .input = {{0}}, .period = 0};
         furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever);
     }
 }
@@ -111,7 +119,10 @@ void overwrite_file_widget_no_input_callback(GuiButtonType result, InputType typ
     }
 }
 
-void overwrite_instrument_file_widget_yes_input_callback(GuiButtonType result, InputType type, void* ctx) {
+void overwrite_instrument_file_widget_yes_input_callback(
+    GuiButtonType result,
+    InputType type,
+    void* ctx) {
     UNUSED(result);
 
     FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx;
@@ -120,12 +131,16 @@ void overwrite_instrument_file_widget_yes_input_callback(GuiButtonType result, I
         tracker->is_saving_instrument = true;
         view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER);
         // save_song(tracker, tracker->filepath);
-        static FlizzerTrackerEvent event = {.type = EventTypeSaveInstrument, .input = {{0}}, .period = 0};
+        static FlizzerTrackerEvent event = {
+            .type = EventTypeSaveInstrument, .input = {{0}}, .period = 0};
         furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever);
     }
 }
 
-void overwrite_instrument_file_widget_no_input_callback(GuiButtonType result, InputType type, void* ctx) {
+void overwrite_instrument_file_widget_no_input_callback(
+    GuiButtonType result,
+    InputType type,
+    void* ctx) {
     UNUSED(result);
 
     FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)ctx;
@@ -243,7 +258,8 @@ void submenu_callback(void* context, uint32_t index) {
         }
 
         case SUBMENU_INSTRUMENT_LOAD: {
-            FlizzerTrackerEvent event = {.type = EventTypeLoadInstrument, .input = {{0}}, .period = 0};
+            FlizzerTrackerEvent event = {
+                .type = EventTypeLoadInstrument, .input = {{0}}, .period = 0};
             furi_message_queue_put(tracker->event_queue, &event, FuriWaitForever);
             view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER);
             break;
@@ -261,6 +277,64 @@ void submenu_callback(void* context, uint32_t index) {
     }
 }
 
+void submenu_copypaste_callback(void* context, uint32_t index) {
+    FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)context;
+
+    uint8_t sequence_position = tracker->tracker_engine.sequence_position;
+    uint8_t current_pattern_index =
+        tracker->tracker_engine.song->sequence.sequence_step[sequence_position]
+            .pattern_indices[tracker->current_channel];
+
+    TrackerSongPattern* source_pattern;
+
+    if(tracker->source_pattern_index >= 0) {
+        source_pattern = &tracker->song.pattern[tracker->source_pattern_index];
+    }
+
+    TrackerSongPattern* current_pattern = &tracker->song.pattern[current_pattern_index];
+
+    uint16_t pattern_length = tracker->tracker_engine.song->pattern_length;
+
+    switch(index) {
+    case SUBMENU_PATTERN_COPYPASTE_COPY: {
+        tracker->source_pattern_index = current_pattern_index;
+        tracker->cut_pattern = false;
+        break;
+    }
+
+    case SUBMENU_PATTERN_COPYPASTE_PASTE: {
+        if(tracker->source_pattern_index >= 0) {
+            memcpy(
+                current_pattern->step,
+                source_pattern->step,
+                sizeof(TrackerSongPatternStep) * pattern_length);
+
+            if(tracker->cut_pattern) {
+                set_empty_pattern(source_pattern, pattern_length);
+                tracker->cut_pattern = false;
+            }
+        }
+        break;
+    }
+
+    case SUBMENU_PATTERN_COPYPASTE_CUT: {
+        tracker->source_pattern_index = current_pattern_index;
+        tracker->cut_pattern = true;
+        break;
+    }
+
+    case SUBMENU_PATTERN_COPYPASTE_CLEAR: {
+        set_empty_pattern(current_pattern, pattern_length);
+        break;
+    }
+
+    default:
+        break;
+    }
+
+    view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_TRACKER);
+}
+
 void audio_output_changed_callback(VariableItem* item) {
     FlizzerTrackerApp* tracker = (FlizzerTrackerApp*)variable_item_get_context(item);
     uint8_t index = variable_item_get_current_value_index(item);
@@ -346,7 +420,8 @@ void process_input_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event)
         return;
     }
 
-    if(tracker->showing_help || tracker->is_loading || tracker->is_saving)
+    if(tracker->showing_help || tracker->is_loading || tracker->is_saving ||
+       tracker->is_loading_instrument || tracker->is_saving_instrument)
         return; //do not react until these are finished
 
     if(event->input.key == InputKeyBack && event->input.type == InputTypeShort &&
@@ -360,15 +435,24 @@ void process_input_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event)
         event->input.key == InputKeyBack && event->input.type == InputTypeShort &&
         !(tracker->editing)) {
         cycle_focus(tracker);
-        stop_song(tracker);
+        //stop_song(tracker);
         return;
     }
 
     if(event->input.key == InputKeyBack && event->input.type == InputTypeLong) {
         switch(tracker->mode) {
         case PATTERN_VIEW: {
-            submenu_set_selected_item(tracker->pattern_submenu, SUBMENU_PATTERN_LOAD_SONG);
-            view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_SUBMENU_PATTERN);
+            if(tracker->focus == EDIT_PATTERN) {
+                submenu_set_selected_item(
+                    tracker->pattern_copypaste_submenu, SUBMENU_PATTERN_COPYPASTE_COPY);
+                view_dispatcher_switch_to_view(
+                    tracker->view_dispatcher, VIEW_SUBMENU_PATTERN_COPYPASTE);
+            }
+
+            else {
+                submenu_set_selected_item(tracker->pattern_submenu, SUBMENU_PATTERN_LOAD_SONG);
+                view_dispatcher_switch_to_view(tracker->view_dispatcher, VIEW_SUBMENU_PATTERN);
+            }
             break;
         }
 

+ 9 - 2
input_event.h

@@ -23,11 +23,18 @@ void return_from_keyboard_callback(void* ctx);
 void overwrite_file_widget_yes_input_callback(GuiButtonType result, InputType type, void* ctx);
 void overwrite_file_widget_no_input_callback(GuiButtonType result, InputType type, void* ctx);
 
-void overwrite_instrument_file_widget_yes_input_callback(GuiButtonType result, InputType type, void* ctx);
-void overwrite_instrument_file_widget_no_input_callback(GuiButtonType result, InputType type, void* ctx);
+void overwrite_instrument_file_widget_yes_input_callback(
+    GuiButtonType result,
+    InputType type,
+    void* ctx);
+void overwrite_instrument_file_widget_no_input_callback(
+    GuiButtonType result,
+    InputType type,
+    void* ctx);
 
 uint32_t submenu_exit_callback(void* context);
 uint32_t submenu_settings_exit_callback(void* context);
 void submenu_callback(void* context, uint32_t index);
+void submenu_copypaste_callback(void* context, uint32_t index);
 void audio_output_changed_callback(VariableItem* item);
 void process_input_event(FlizzerTrackerApp* tracker, FlizzerTrackerEvent* event);

+ 12 - 7
view/pattern_editor.c

@@ -3,7 +3,7 @@
 
 #include <flizzer_tracker_icons.h>
 
-#define PATTERN_EDITOR_Y (64 - (6 * 5) - 1)
+#define PATTERN_EDITOR_Y ((tracker->focus == EDIT_PATTERN) ? 4 : (64 - (6 * 5) - 1))
 
 static const char* notenames[] = {
     "C-",
@@ -73,15 +73,18 @@ void draw_pattern_view(Canvas* canvas, FlizzerTrackerApp* tracker) {
 
         TrackerSongPattern* pattern = &tracker->tracker_engine.song->pattern[current_pattern];
 
-        for(uint8_t pos = 0; pos < 5; ++pos) {
+        for(uint8_t pos = 0; pos < ((tracker->focus == EDIT_PATTERN) ? 9 : 5); ++pos) {
             TrackerSongPatternStep* step = NULL;
 
-            if(pattern_step - 2 + pos >= 0 && pattern_step - 2 + pos < pattern_length) {
-                step = &pattern->step[pattern_step + pos - 2];
+            if(pattern_step - ((tracker->focus == EDIT_PATTERN) ? 4 : 2) + pos >= 0 &&
+               pattern_step - ((tracker->focus == EDIT_PATTERN) ? 4 : 2) + pos < pattern_length) {
+                step =
+                    &pattern->step[pattern_step + pos - ((tracker->focus == EDIT_PATTERN) ? 4 : 2)];
             }
 
             uint8_t string_x = i * 32;
-            uint8_t string_y = PATTERN_EDITOR_Y + 6 * pos + 6 + 1;
+            uint8_t string_y =
+                PATTERN_EDITOR_Y + 6 * pos + 6 + ((tracker->focus == EDIT_PATTERN) ? 3 : 1);
 
             if(step) {
                 uint8_t note = tracker_engine_get_note(step);
@@ -135,7 +138,8 @@ void draw_pattern_view(Canvas* canvas, FlizzerTrackerApp* tracker) {
     if(tracker->editing && tracker->focus == EDIT_PATTERN) {
         uint16_t x = tracker->current_channel * 32 + tracker->patternx * 4 +
                      (tracker->patternx > 0 ? 4 : 0) - 1;
-        uint16_t y = PATTERN_EDITOR_Y + 6 * 2 + 1;
+        uint16_t y = PATTERN_EDITOR_Y + 6 * ((tracker->focus == EDIT_PATTERN) ? 4 : 2) +
+                     ((tracker->focus == EDIT_PATTERN) ? 3 : 1);
 
         canvas_draw_box(canvas, x, y, (tracker->patternx > 0 ? 5 : 9), 7);
     }
@@ -143,7 +147,8 @@ void draw_pattern_view(Canvas* canvas, FlizzerTrackerApp* tracker) {
     if(!(tracker->editing) && tracker->focus == EDIT_PATTERN) {
         uint16_t x = tracker->current_channel * 32 + tracker->patternx * 4 +
                      (tracker->patternx > 0 ? 4 : 0) - 1;
-        uint16_t y = PATTERN_EDITOR_Y + 6 * 2 + 1;
+        uint16_t y = PATTERN_EDITOR_Y + 6 * ((tracker->focus == EDIT_PATTERN) ? 4 : 2) +
+                     ((tracker->focus == EDIT_PATTERN) ? 3 : 1);
 
         canvas_draw_frame(canvas, x, y, (tracker->patternx > 0 ? 5 : 9), 7);
     }