# data file for the Fltk User Interface Designer (fluid)
version 1.0304
header_name {.h}
code_name {.cc}
comment {MidiLearnUI.h} {not_in_source in_header
}

comment {MidiLearnUI.cc} {in_source not_in_header
}

comment {Copyright (C) 2016-2020, Will Godfrey

This file is part of yoshimi, which is free software: you can redistribute
it and/or modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.

yoshimi is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.   See the GNU General Public License (version 2 or
later) for more details.

You should have received a copy of the GNU General Public License along with
yoshimi; if not, write to the Free Software Foundation, Inc., 51 Franklin
Street, Fifth Floor, Boston, MA  02110-1301, USA.

} {selected in_source in_header
}

decl {\#include <FL/fl_ask.H>
    \#include "Misc/Config.h"
    \#include "Misc/SynthEngine.h"} {private global
}

decl {using namespace std;} {public local
}

decl {\#include "MasterUI.h"} {private global
}

decl {\#include "Misc/SynthEngine.h"} {private local
}

decl {\#include "UI/MiscGui.h"
    \#include "EffUI.h"
    \#include "BankUI.h"
    \#include "ADnoteUI.h"
    \#include "SUBnoteUI.h"
    \#include "PADnoteUI.h"
    \#include "UI/WidgetMWSlider.h"
    \#include "UI/WidgetSpinner.h"
    \#include "UI/WidgetCheckButton.h"
    \#include "Params/Controller.h"} {public global
}

decl {\#include "Misc/FileMgrFuncs.h"
    using file::findLeafName;} {private local
}

decl {\#include "Misc/FormatFuncs.h"
    using func::asHexString;} {private local
}

decl {\#include "Misc/TextMsgBuffer.h"

    namespace { // Implementation details...
        TextMsgBuffer& textMsgBuffer = TextMsgBuffer::instance();
    }} {private local
}

class MidiLearnKitItem {: {public Fl_Group}
} {
  Function {make_window()} {} {
    Fl_Window midilearnkititem {
      xywh {8 649 825 20} type Double hide
      class Fl_Group
    } {
      Fl_Group midilearnkititemgroup {
        xywh {56 -4 745 23} box FLAT_BOX
      } {
        Fl_Spinner CCcounter {
          class WidgetSpinner
          callback {send_data(0, MIDILEARN::control::CCorChannel);}
          tooltip {Continuous Controller to recognise} xywh {80 0 43 15} labelsize 12 minimum 0 maximum 129 value 14 textsize 12
        }
        Fl_Button {} {
          callback {// does nothing - hides spinner arrows}
          xywh {114 0 15 16} box FLAT_BOX down_box FLAT_BOX
        }
        Fl_Button nrpn {
          label {8888 h}
          tooltip {NRPN value} xywh {76 0 49 15} box THIN_DOWN_BOX down_box THIN_DOWN_BOX color 247 selection_color 7 labelsize 12 hide
        }
        Fl_Light_Button sevenbit {
          callback {send_data(0, MIDILEARN::control::sevenBit);}
          tooltip {Set for 7bit NRPN} xywh {59 0 13 15} box NO_BOX color 28 selection_color 1 hide
        }
        Fl_Choice channelchoice {
          callback {send_data(0, 48);}
          tooltip {Incoming channel} xywh {137 0 39 15} down_box BORDER_BOX selection_color 49 labelsize 12 textsize 12
        } {
          MenuItem chan1 {
            label 1
            xywh {0 0 26 15} labelsize 11
          }
          MenuItem chan2 {
            label 2
            xywh {10 10 26 15} labelsize 11
          }
          MenuItem chan3 {
            label 3
            xywh {20 20 26 15} labelsize 11
          }
          MenuItem chan4 {
            label 4
            xywh {30 30 26 15} labelsize 11
          }
          MenuItem chan5 {
            label 5
            xywh {40 40 26 15} labelsize 11
          }
          MenuItem chan6 {
            label 6
            xywh {50 50 26 15} labelsize 11
          }
          MenuItem chan7 {
            label 7
            xywh {60 60 26 15} labelsize 11
          }
          MenuItem chan8 {
            label 8
            xywh {70 70 26 15} labelsize 11
          }
          MenuItem chan9 {
            label 9
            xywh {80 80 26 15} labelsize 11
          }
          MenuItem chan10 {
            label 10
            xywh {90 90 26 15} labelsize 11
          }
          MenuItem chan11 {
            label 11
            xywh {100 100 26 15} labelsize 11
          }
          MenuItem chan12 {
            label 12
            xywh {110 110 26 15} labelsize 11
          }
          MenuItem chan13 {
            label 13
            xywh {120 120 26 15} labelsize 11
          }
          MenuItem chan14 {
            label 14
            xywh {130 130 26 15} labelsize 11
          }
          MenuItem chan15 {
            label 15
            xywh {140 140 26 15} labelsize 11
          }
          MenuItem chan16 {
            label 16
            xywh {150 150 26 15} labelsize 11
          }
          MenuItem chanAll {
            label All
            xywh {160 160 26 15} labelsize 11
          }
        }
        Fl_Check_Button compresscheck {
          class Fl_Check_Button2
          callback {//
          send_data(0, MIDILEARN::control::limit);}
          tooltip {Limit or compress incoming value} xywh {324 0 15 15} down_box DOWN_BOX labelfont 1 align 4
        }
        Fl_Check_Button blockcheck {
          class Fl_Check_Button2
          callback {//
          send_data(0, MIDILEARN::control::block);}
          tooltip {Stop any later lines (or system controls) responding to this CC/Channel pair} xywh {360 0 20 15} down_box DOWN_BOX labelfont 1 align 4
        }
        Fl_Light_Button activity {
          callback {//
          if (o->value() != 0)
              o->value(0);
          else
              o->value(1);}
          xywh {180 0 13 15} box NO_BOX color 15 selection_color 2
        }
        Fl_Spinner minval {
          class WidgetSpinner
          callback {send_data(0, MIDILEARN::control::minimum);}
          tooltip {Minimum % to pass on} xywh {197 0 52 15} type Float labelsize 12 minimum 0 step 0.5 value 0 textsize 12
        }
        Fl_Button {} {
          callback {// does nothing - hides spinner arrows}
          xywh {240 0 15 16} box FLAT_BOX down_box FLAT_BOX
        }
        Fl_Spinner maxval {
          class WidgetSpinner
          callback {//
          send_data(0, MIDILEARN::control::maximum);}
          tooltip {Maximim % to pass on} xywh {261 0 52 15} type Float labelsize 12 minimum 0 step 0.5 value 100 textsize 12
        }
        Fl_Button {} {
          callback {// does nothing - hides spinner arrows}
          xywh {303 0 15 16} box FLAT_BOX down_box FLAT_BOX
        }
        Fl_Button commandName {
          label text
          callback {//
          send_data(0, MIDILEARN::control::deleteLine);}
          tooltip {To delete, hold CTRL and click here.} xywh {395 0 400 15} box THIN_DOWN_BOX color 247 selection_color 247 labelfont 1 labelsize 10 align 64
        }
      }
      Fl_Check_Button mutecheck {
        class Fl_Check_Button2
        callback {//
        if (o->value())
            midilearnkititemgroup->deactivate();
        else
            midilearnkititemgroup->activate();
        send_data(0, MIDILEARN::control::mute);}
        tooltip {Completely ignore this line} xywh {39 0 21 15} down_box DOWN_BOX labelfont 1 align 4
        code0 {o->copy_label(to_string(n + 1).c_str());}
      }
    }
  }
  Function {send_data(int action, int control)} {} {
    code {//
    bool doit = true;
    int type = 0;
    int CC = UNUSED;
    int chan = UNUSED;
    int min = UNUSED;
    int max = UNUSED;
    switch (control)
    {
        case MIDILEARN::control::block:
            if (blockcheck->value() != 0)
                type = 1;
            break;
        case MIDILEARN::control::limit:
            if (compresscheck->value() != 0)
                type = 2;
            break;
        case MIDILEARN::control::mute:
            if (mutecheck->value() != 0)
                type = 4;
            break;
        case MIDILEARN::control::sevenBit:
            if (sevenbit->value() != 0)
               type = 16;
            break;
        case MIDILEARN::control::minimum:
            min = lrint(minval->value() * 2.0f);
            break;
        case MIDILEARN::control::maximum:
            max = lrint(maxval->value() * 2.0f);
            break;
        case MIDILEARN::control::deleteLine:
            doit = (Fl::event_state(FL_CTRL) != 0);
            if (doit)
                doit = (choice(synth, "", "Yes", "No", "Remove line " + to_string( n + 1) + " " + commandName->label() + "?") > 1);
            break;
        case MIDILEARN::control::CCorChannel:
            CC = lrint(CCcounter->value());
            break;
        case 48:
            chan = lrint(channelchoice->value());
            control = MIDILEARN::control::CCorChannel;
            break;
    }
    if (doit)
        collect_data(synth, n, action, type, control, TOPLEVEL::section::midiLearn, CC, chan, min, max, UNUSED, 0);} {}
  }
  Function {MidiLearnKitItem(int x,int y, int w, int h, const char *label=0):Fl_Group(x,y,w,h,label)} {} {
    code {n=0;} {}
  }
  Function {~MidiLearnKitItem()} {} {
    code {//
    midilearnkititem->hide();
    delete(midilearnkititem);} {}
  }
  Function {init(SynthEngine *synth_, int n_)} {} {
    code {//
    synth = synth_;
    n = n_;
    make_window();
    show();
    end();} {}
  }
  decl {int n;} {private local
  }
  decl {char label[50];} {private local
  }
  decl {SynthEngine *synth;} {private local
  }
  decl {unsigned char stat;} {public local
  }
  Function {kitRscale(float dScale)} {} {
    code {//
    int size11 = int(11 * dScale);
    int size12 = int(12 * dScale);
    int size14 = int(14 * dScale);

    chan1->labelsize(size11);
    chan2->labelsize(size11);
    chan3->labelsize(size11);
    chan4->labelsize(size11);
    chan5->labelsize(size11);
    chan6->labelsize(size11);
    chan7->labelsize(size11);
    chan8->labelsize(size11);
    chan9->labelsize(size11);
    chan10->labelsize(size11);
    chan11->labelsize(size11);
    chan12->labelsize(size11);
    chan13->labelsize(size11);
    chan14->labelsize(size11);
    chan15->labelsize(size11);
    chan16->labelsize(size11);
    chanAll->labelsize(size11);

    CCcounter->labelsize(size12);
        CCcounter->textsize(size12);
    nrpn->labelsize(size12);
    sevenbit->labelsize(size14);
    channelchoice->labelsize(size12);
        channelchoice->textsize(size12);
    compresscheck->labelsize(size14);
    blockcheck->labelsize(size14);
    activity->labelsize(size14);
    minval->labelsize(size12);
        minval->textsize(size12);
    maxval->labelsize(size12);
        maxval->textsize(size12);
    mutecheck->labelsize(size12);
    commandName->labelsize(int(10 * dScale));

     midilearnkititem->redraw();} {}
  }
}

class MidiLearnUI {} {
  Function {make_window()} {} {
    Fl_Window midilearnwindow {
      label {Midi Learn}
      callback {close->do_callback();}
      tooltip {Editor for all learned controllers.
CTRL-right click on any knob/slider/button to learn.
Note: Adding/deleting entries or changing CC/Chan will renumber the lines.} xywh {7 694 820 285} type Double hide resizable
      code0 {send_data(TOPLEVEL::action::lowPrio, MIDILEARN::control::sendRefreshRequest, 0, 3);}
      code1 {learnDW = o->w(); learnDH = o->h();}
      code2 {o->size_range(learnDW, learnDH, 0, 0, 0, 0, 1);}
    } {
      Fl_Scroll kitlist {
        tooltip {Editor for all learned controllers.
CTRL-Right click on any knob/slider/button to learn.
Note: Adding/deleting entries or changing CC/Chan will renumber the lines.} xywh {0 15 818 245} type VERTICAL box DOWN_FRAME
      } {}
      Fl_Box none {
        label {No Entries}
        xywh {290 131 206 44} labelsize 32
      }
      Fl_Box mutebox {
        label Mute
        xywh {18 0 41 15} labelfont 1 labelsize 11 align 18
      }
      Fl_Box ccbox {
        label CC
        xywh {80 0 40 15} labelfont 1 labelsize 11 align 18
      }
      Fl_Box chanbox {
        label Chan
        xywh {139 0 40 15} labelfont 1 labelsize 11 align 18
      }
      Fl_Box blockbox {
        label Block
        xywh {355 0 50 15} labelfont 1 labelsize 11 align 18
      }
      Fl_Box limitbox {
        label Limit
        xywh {310 0 48 15} labelfont 1 labelsize 11 align 18
      }
      Fl_Box controlbox {
        label {Control Function}
        xywh {530 0 130 15} labelfont 1 labelsize 11 align 18
      }
      Fl_Box minbox {
        label {Min %}
        xywh {200 0 40 15} labelfont 1 labelsize 11 align 18
      }
      Fl_Box maxbox {
        label {Max %}
        xywh {263 0 40 15} labelfont 1 labelsize 11 align 18
      }
      Fl_Button close {
        label Close
        callback {//
    saveWin(synth, midilearnwindow->w(), midilearnwindow->h(), midilearnwindow->x(), midilearnwindow->y(), false, "Midi-learn");
    midilearnwindow->hide();
    learnSeen = false;}
        xywh {730 262 63 20} box THIN_UP_BOX
      }
      Fl_Button load {
        label Load
        callback {//
        string filename = setfiler(synth,"", "", false, TOPLEVEL::XML::MLearn);
        if (filename.empty())
            return;
        loadMidi(filename);}
        tooltip {Load complete learned list} xywh {30 264 70 18} down_box DOWN_BOX labelfont 1 labelsize 12
      }
      Fl_Button save {
        label Save
        callback {//
        string filename = setfiler(synth,"", "", true, TOPLEVEL::XML::MLearn);
        if (filename.empty())
            return;
        send_data(0, MIDILEARN::control::saveList, 0, 0, 0, 0, 0, 0, textMsgBuffer.push((string) filename));
        recent->activate();
        setWindowTitle(findLeafName(filename));}
        tooltip {Save complete learned list} xywh {130 264 70 18} down_box DOWN_BOX labelfont 1 labelsize 12 deactivate
      }
      Fl_Button clear {
        label Clear
        callback {//
        if (choice(synth, "", "Yes", "No", "Remove all entries") < 2)
            return;

        send_data(0, MIDILEARN::control::clearAll,0,0);
        o->deactivate();
        setWindowTitle();}
        tooltip {Remove all entries} xywh {330 264 70 18} down_box DOWN_BOX labelfont 1 labelsize 12 deactivate
      }
      Fl_Button recent {
        label Recent
        callback {//
        synth->getGuiMaster()->paramsui->Recent->position(midilearnwindow->x() + recent->x() - 80, midilearnwindow->y() + recent->y() - 187);
        synth->getGuiMaster()->paramsui->Show(TOPLEVEL::XML::MLearn);}
        tooltip {Load from recently seen list} xywh {230 264 70 18} down_box DOWN_BOX labelfont 1 labelsize 12
        code0 {vector<string> &listType = *synth->getHistory(TOPLEVEL::XML::MLearn);}
        code1 {if (listType.size() == 0) o->deactivate(); else o->activate();}
      }
    }
  }
  Function {showLearn()} {} {
    code {//
    int fetchW, fetchH, fetchX, fetchY, fetchO;
    loadWin(synth, fetchW, fetchH, fetchX, fetchY, fetchO, "Midi-learn");
    if (fetchW < learnDW || fetchH < learnDH)
    {
        fetchW = learnDW;
        fetchH = learnDH;
    }
    checkSane(fetchX, fetchY, fetchW, fetchH, learnDW, learnDH);

    midilearnwindow->resize(fetchX, fetchY, fetchW, fetchH);
    midilearnwindow->show();
    learnW = 0;
    learnSeen = true;} {}
  }
  Function {send_data(int action, int control, float value, int type, int kititem = UNUSED, int engine = UNUSED, int insert = UNUSED, int parameter = UNUSED, int miscmsg = UNUSED)} {} {
    code {//
    collect_data(synth, value, action, type, control, TOPLEVEL::section::midiLearn, kititem, engine, insert, parameter, UNUSED, miscmsg);} {}
  }
  Function {returns_update(CommandBlock *getData)} {} {
    code {//
    int value = lrint(getData->data.value);
    unsigned char type = getData->data.type;
    unsigned char control = getData->data.control;
    unsigned char kititem = getData->data.kit;
    unsigned char engine = getData->data.engine;
    unsigned char insert = getData->data.insert;
    unsigned char parameter = getData->data.parameter;
    unsigned char miscmsg = getData->data.miscmsg;
    string hex;
    int nrpnTot;
    unsigned int IDold;
    unsigned int IDnew;
    switch(control)
    {
        case MIDILEARN::control::block:
            break;
        case MIDILEARN::control::ignoreMove:
            if (type != UNUSED) // edit line in place
            {
                midilearnkititem[value]->blockcheck->value((type & 1) != 0);
                midilearnkititem[value]->compresscheck->value((type & 2) != 0);
                midilearnkititem[value]->mutecheck->value((type & 4) != 0);
                midilearnkititem[value]->sevenbit->value((type & 16) != 0);
                if (type & 4) // it's muted
                    midilearnkititem[value]->midilearnkititemgroup->deactivate();
                else
                    midilearnkititem[value]->midilearnkititemgroup->activate();
            }
            if (kititem != UNUSED)
                midilearnkititem[value]->CCcounter->value(kititem);
            if (engine != UNUSED)
                midilearnkititem[value]->channelchoice->value(engine);
            if (insert != UNUSED)
                midilearnkititem[value]->minval->value(insert / 2.0f);
            if (parameter != UNUSED)
                midilearnkititem[value]->maxval->value(parameter / 2.0f);
            break;
        case MIDILEARN::control::nrpnDetected: // set NRPN fixed value in place
            nrpnTot = (int(engine) << 8) + (int(kititem));
            /* These were stored 8 bit values so must be joined then
             * split as two 7 bit values to give NRPN msb and lsb
             */
            hex = (asHexString(nrpnTot >> 7)) + " " + (asHexString(nrpnTot & 0x7f));
            midilearnkititem[value]->stat |= 0x10;
            midilearnkititem[value]->nrpn->copy_label(hex.c_str());
            midilearnkititem[value]->CCcounter->hide();
            midilearnkititem[value]->nrpn->show();
            midilearnkititem[value]->sevenbit->show();
            break;
        case MIDILEARN::control::CCorChannel:
            addLine(getData);
            break;
        case MIDILEARN::control::findSize: // not using this yet
            break;
        case MIDILEARN::control::sendLearnMessage:
            synth->getGuiMaster()->setmessage(UNUSED, false, textMsgBuffer.fetch(miscmsg), "Close", "Cancel");
            break;
        case MIDILEARN::control::sendRefreshRequest:
            //midilearnwindow->show();
            showLearn();
            break;
        case MIDILEARN::control::reportActivity: // flash LED
            IDold = kititem | (engine << 8);
            for (int i = 0; i < MIDI_LEARN_BLOCK; ++ i)
            {
                if (midilearnkititem[i] != NULL && midilearnkititem[i]->mutecheck->value() == 0)
                {
                    unsigned char newchan = midilearnkititem[i]->channelchoice->value();
                    if (newchan == NUM_MIDI_CHANNELS) // all of them
                        newchan = engine; // force it to match
                    IDnew = (unsigned int)midilearnkititem[i]->CCcounter->value() | (newchan << 8);
                    if (IDold == IDnew && (type & 0x10) == (midilearnkititem[i]->stat & 0x10))
                    {
                        midilearnkititem[i]->activity->do_callback();
                        if (midilearnkititem[i]->blockcheck->value() != 0)
                            IDold = 0xffffff; // block following lines
                    }
                }
            }
            break;
        case MIDILEARN::control::clearAll:
            clearAll(value == 0);
            break;
        case MIDILEARN::control::cancelLearn:
            synth->getGuiMaster()->setmessage(UNUSED, false, "Learn cancelled", "Close");
            break;
    }} {}
  }
  Function {addLine(CommandBlock *getData)} {} {
    code {//
    int lineNo = ((int)getData->data.value);
    int status = getData->data.type;
    string name = textMsgBuffer.fetch(getData->data.miscmsg);
    none->hide();
    clear->activate();
    save->activate();
    midilearnkititem[lineNo] = new MidiLearnKitItem(0, 0,818,20,"");
    midilearnkititem[lineNo]->init(synth, lineNo);
    kitlist->add(midilearnkititem[lineNo]);

    midilearnkititem[lineNo]->position(2, 21 + lineNo*20);
    midilearnkititem[lineNo]->stat = (status & 0x10);
    midilearnkititem[lineNo]->mutecheck->value(status & 4);
    midilearnkititem[lineNo]->CCcounter->value(getData->data.kit);
    midilearnkititem[lineNo]->channelchoice->value(getData->data.engine);
    midilearnkititem[lineNo]->minval->value(getData->data.insert / 2.0f);
    midilearnkititem[lineNo]->maxval->value(getData->data.parameter / 2.0f);
    midilearnkititem[lineNo]->compresscheck->value(status & 2);
    midilearnkititem[lineNo]->blockcheck->value(status & 1);
    midilearnkititem[lineNo]->commandName->copy_label(name.c_str());
    if (status & 4)
        midilearnkititem[lineNo]->midilearnkititemgroup->deactivate();
    learnW = 0;
    midilearnwindow->redraw();} {}
  }
  Function {MidiLearnUI(SynthEngine *_synth)} {} {
    code {//
    synth = _synth;
    for (int i = 0; i < MIDI_LEARN_BLOCK; ++i)
        midilearnkititem[i] = NULL;
    make_window();
    setWindowTitle();
    learnW = 0;
    learnSeen = false;} {}
  }
  Function {~MidiLearnUI()} {} {
    code {//
    if (learnSeen)
        saveWin(synth, midilearnwindow->w(), midilearnwindow->h(), midilearnwindow->x(), midilearnwindow->y(), true, "Midi-learn");
    midilearnwindow->hide();
    delete midilearnwindow;} {}
  }
  Function {clearAll(bool empty)} {} {
    code {//
    kitlist->clear();
    for (int i = 0; i < MIDI_LEARN_BLOCK; ++i)
        midilearnkititem[i] = NULL;
    if (empty)
    {
        none->show();
        clear->deactivate();
        save->deactivate();
    }
    kitlist->redraw();
    kitlist->show();} {}
  }
  Function {loadMidi(string file)} {} {
    code {//
    send_data(TOPLEVEL::action::forceUpdate, MIDILEARN::control::loadList, 0, 0, 0, 0, 0, 0, textMsgBuffer.push((string) file));
    recent->activate();
    setWindowTitle(findLeafName(file));} {}
  }
  Function {setWindowTitle(string name = "")} {} {
    code {//
    if (name > "")
        name = " - " + name;
    midilearnwindow->copy_label(synth->makeUniqueName("MIDI Learn" + name).c_str());} {}
  }
  Function {learnRtext()} {} {
    code {//
    if (learnW == midilearnwindow->w())
        return;
    learnW = midilearnwindow->w();

    float dScale = midilearnwindow->w() / float(learnDW);

    int size11 = int(11 * dScale);
    int size12 = int(12 * dScale);

    close->labelsize(size12);
    load->labelsize(size12);
    save->labelsize(size12);
    clear->labelsize(size12);
    recent->labelsize(size12);

    mutebox->labelsize(size11);
    ccbox->labelsize(size11);
    chanbox->labelsize(size11);
    blockbox->labelsize(size11);
    limitbox->labelsize(size11);
    controlbox->labelsize(size11);
    minbox->labelsize(size11);
    maxbox->labelsize(size11);

    none->labelsize(int(32 * dScale));
    for (int i = 0; i < MIDI_LEARN_BLOCK; ++i)
    {
        if (midilearnkititem[i] == NULL)
            continue;
        midilearnkititem[i]->kitRscale(dScale);
        midilearnkititem[i]->resize(2 * dScale, (21 + i * 20) * dScale, 818 * dScale, 20 * dScale);
    }
    midilearnwindow->redraw();} {}
  }
  decl {int lastkititem;} {public local
  }
  decl {SynthEngine *synth;} {private local
  }
  decl {MidiLearnKitItem *midilearnkititem[MIDI_LEARN_BLOCK];} {private local
  }
  decl {int learnDW;} {private local
  }
  decl {int learnDH;} {private local
  }
  decl {int learnW;} {private local
  }
  decl {bool learnSeen;} {private local
  }
}
