This adds the ability to select multiple devices either by a ctrl-mouse drag or by ctrl-click on individual devices title to do finer selection and deselection, and then ctrl-right-click on the canvas to produce a menu with the options to cut/copy/paste/delete selection or to merge an existing patch entirely into the patch. Pasting or merging will auto-select new devices so any old devices will lose selection. To remove selection simply click anywhere on canvas. Current problems - Multiple Select drag is buggy and may select items not in the selection area. Multiple OSS plugins are buggy so merging/pasting an OSS device can cause crashing.master
| @@ -17,6 +17,7 @@ | |||
| */ | |||
| #include "FL/fl_draw.H" | |||
| #include <FL/Fl_Scroll.H> | |||
| #include "Fl_Canvas.h" | |||
| #include "Fl_DeviceGUI.h" | |||
| #include <iostream> | |||
| @@ -34,7 +35,8 @@ cb_Connection(NULL), | |||
| cb_Unconnect(NULL), | |||
| cb_AddDevice(NULL), | |||
| m_ToolMenu(false), | |||
| m_UpdateTimer(0) | |||
| m_UpdateTimer(0), | |||
| m_Selecting(false) | |||
| { | |||
| m_IncompleteWire.OutputID=-1; | |||
| m_IncompleteWire.OutputPort=-1; | |||
| @@ -45,12 +47,15 @@ m_UpdateTimer(0) | |||
| m_BG=NULL; | |||
| m_BGData=NULL; | |||
| m_Menu=NULL; | |||
| m_CanPaste=false; | |||
| } | |||
| //////////////////////////////////////////////////////////////////////// | |||
| Fl_Canvas::~Fl_Canvas() | |||
| { | |||
| if (m_Menu) delete m_Menu; | |||
| } | |||
| //////////////////////////////////////////////////////////////////////// | |||
| @@ -58,7 +63,7 @@ Fl_Canvas::~Fl_Canvas() | |||
| void Fl_Canvas::draw() | |||
| { | |||
| Fl_Widget*const* a = array(); | |||
| if (damage() & ~FL_DAMAGE_CHILD) // redraw the entire thing: | |||
| { | |||
| if (m_BG) | |||
| @@ -96,6 +101,14 @@ void Fl_Canvas::draw() | |||
| { | |||
| draw_child(o); | |||
| draw_outside_label(o); | |||
| std::vector<int>::iterator sel = std::find( m_Selection.m_DeviceIds.begin(), m_Selection.m_DeviceIds.end(), ((Fl_DeviceGUI*)&o)->GetID() ); | |||
| if (sel != m_Selection.m_DeviceIds.end()) | |||
| { | |||
| fl_color(FL_YELLOW); | |||
| fl_rect(o.x(), o.y(), o.w(), o.h()); | |||
| } | |||
| } | |||
| } | |||
| @@ -110,6 +123,14 @@ void Fl_Canvas::draw() | |||
| { | |||
| draw_child(o); | |||
| draw_outside_label(o); | |||
| std::vector<int>::iterator sel = std::find( m_Selection.m_DeviceIds.begin(), m_Selection.m_DeviceIds.end(), ((Fl_DeviceGUI*)&o)->GetID() ); | |||
| if (sel != m_Selection.m_DeviceIds.end()) | |||
| { | |||
| fl_color(FL_YELLOW); | |||
| fl_rect(o.x(), o.y(), o.w(), o.h()); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| @@ -161,6 +182,7 @@ void Fl_Canvas::draw() | |||
| Pos+=1; | |||
| } | |||
| } | |||
| DrawSelection(); | |||
| } | |||
| //////////////////////////////////////////////////////////////////////// | |||
| @@ -179,6 +201,264 @@ void Fl_Canvas::Poll() | |||
| //////////////////////////////////////////////////////////////////////// | |||
| void Fl_Canvas::DrawSelection() | |||
| { | |||
| if (m_Selecting) | |||
| { | |||
| int X, Y, W, H; | |||
| fl_color(FL_YELLOW); | |||
| X = min(m_StartSelectX, m_EndSelectX); | |||
| Y = min(m_StartSelectY, m_EndSelectY); | |||
| W = max(m_StartSelectX, m_EndSelectX) - X; | |||
| H = max(m_StartSelectY, m_EndSelectY) - Y; | |||
| fl_rect(X-1, Y-1, W+2, H+2); | |||
| } | |||
| } | |||
| bool widget_intersects_rectangle (Fl_Widget* o, int X, int Y, int W, int H) | |||
| { | |||
| int src1_x1=o->x(), src1_y1=o->y(); | |||
| int src1_x2=o->x()+o->w(), src1_y2=o->y()+o->h(); | |||
| int src2_x1=X, src2_y1=Y; | |||
| int src2_x2=X+W, src2_y2=Y+H; | |||
| int width=0, height=0; | |||
| if (X < o->x()) | |||
| { | |||
| src1_x1=X; | |||
| src1_y1=Y; | |||
| src1_x2=X+W; | |||
| src1_y2=Y+H; | |||
| src2_x1=o->x(); | |||
| src2_y1=o->y(); | |||
| src2_x2=o->w(); | |||
| src2_y2=o->h(); | |||
| } | |||
| if (src2_x1 < src1_x2) | |||
| { | |||
| if (src1_x2 < src2_x2) | |||
| width = src1_x2 - src2_x1; | |||
| else | |||
| width = src2_x2 - src2_x1; | |||
| if (width == 0) | |||
| return false; | |||
| if (src2_y1 < src1_y1) | |||
| { | |||
| int tmp; | |||
| tmp = src2_x1; | |||
| src2_x1=src1_x1; | |||
| src1_x1=tmp; | |||
| tmp = src2_y1; | |||
| src2_y1=src1_y1; | |||
| src1_y1=tmp; | |||
| tmp = src2_x2; | |||
| src2_x2=src1_x2; | |||
| src1_x2=tmp; | |||
| tmp = src2_y2; | |||
| src2_y2=src1_y2; | |||
| src1_y2=tmp; | |||
| } | |||
| if (src2_y1 < src1_y2) | |||
| { | |||
| if (src1_y2 < src2_y2) | |||
| height = src1_y2 - src2_y1; | |||
| else | |||
| height = src2_y2 - src2_y1; | |||
| if ((height == 0)) | |||
| return false; | |||
| else | |||
| return true; | |||
| } | |||
| } | |||
| return false; | |||
| } | |||
| void Fl_Canvas::CalculateSelection() | |||
| { | |||
| Fl_Widget*const* a = array(); | |||
| int X, Y, W, H; | |||
| X = min(m_StartSelectX, m_EndSelectX); | |||
| Y = min(m_StartSelectY, m_EndSelectY); | |||
| W = max(m_StartSelectX, m_EndSelectX) - X; | |||
| H = max(m_StartSelectY, m_EndSelectY) - Y; | |||
| m_HaveSelection = false; | |||
| m_Selection.Clear(); | |||
| for (int i=0; i<children(); i++) | |||
| { | |||
| Fl_Widget& o = **a++; | |||
| if (widget_intersects_rectangle(&o, X, Y, W, H)) | |||
| { | |||
| m_HaveSelection = true; | |||
| m_Selection.m_DeviceIds.push_back(((Fl_DeviceGUI*)&o)->GetID()); | |||
| ((Fl_DeviceGUI*)&o)->SetOnDragCallback(cb_OnDrag_s, this); | |||
| } | |||
| } | |||
| } | |||
| inline void Fl_Canvas::cb_OnDrag_i(Fl_Widget* widget, int xoffset,int yoffset) | |||
| { | |||
| if ((widget) && (widget->parent())) | |||
| { | |||
| int moved_device_id = ((Fl_DeviceGUI*)(widget->parent()))->GetID(); | |||
| if (m_HaveSelection) | |||
| { | |||
| if (m_Selection.m_DeviceIds.size() <= 0) | |||
| m_HaveSelection = false; | |||
| for (unsigned int i=0; i<m_Selection.m_DeviceIds.size(); i++) | |||
| { | |||
| int ID = Selection().m_DeviceIds[i]; | |||
| Fl_Widget *o = FindDevice(ID); | |||
| if ((o) && (m_Selection.m_DeviceIds[i] != moved_device_id)) | |||
| { | |||
| o->position(o->x() + xoffset, o->y() + yoffset); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| return; | |||
| } | |||
| inline void Fl_Canvas::cb_DeleteDeviceGroup_i() | |||
| { | |||
| if (! m_HaveSelection) | |||
| return; | |||
| //show some warning here | |||
| for (unsigned int i=0; i<m_Selection.m_DeviceIds.size(); i++) | |||
| { | |||
| int ID = m_Selection.m_DeviceIds[i]; | |||
| Fl_DeviceGUI* o = FindDevice(ID); | |||
| if (o) | |||
| { | |||
| Fl_DeviceGUI::Kill(o); | |||
| } | |||
| } | |||
| m_HaveSelection=false; | |||
| m_Selection.Clear(); | |||
| redraw(); | |||
| } | |||
| void Fl_Canvas::PopupEditMenu(Fl_Group *group) | |||
| { | |||
| if (! (m_Menu)) | |||
| { | |||
| m_Menu = new Fl_Menu_Button(Fl::event_x(),Fl::event_x(),4,4,"Edit"); | |||
| m_Menu->type(Fl_Menu_Button::POPUP123); | |||
| m_Menu->textsize(10); | |||
| m_Menu->add("Cut Currently Selected Devices", 0, (Fl_Callback*)cb_CutDeviceGroup,user_data()); | |||
| m_Menu->add("Copy Currently Selected Devices", 0, (Fl_Callback*)cb_CopyDeviceGroup,user_data()); | |||
| m_Menu->add("Paste Previously Copied Devices", 0, (Fl_Callback*)cb_PasteDeviceGroup,user_data(), FL_MENU_DIVIDER); | |||
| m_Menu->add("Merge Existing Patch", 0, (Fl_Callback*)cb_MergePatch,user_data(), FL_MENU_DIVIDER); | |||
| m_Menu->add("Delete Currently Selected Devices", 0, (Fl_Callback*)cb_DeleteDeviceGroup, this); | |||
| } | |||
| m_Menu->value(0); | |||
| group->add(m_Menu); | |||
| Fl_Menu_Item *cut=(Fl_Menu_Item*)&(m_Menu->menu()[0]); | |||
| Fl_Menu_Item *copy=(Fl_Menu_Item*)&(m_Menu->menu()[1]); | |||
| Fl_Menu_Item *paste=(Fl_Menu_Item*)&(m_Menu->menu()[2]); | |||
| Fl_Menu_Item *merge=(Fl_Menu_Item*)&(m_Menu->menu()[3]); | |||
| Fl_Menu_Item *deleteitems=(Fl_Menu_Item*)&(m_Menu->menu()[4]); | |||
| if ((cb_CopyDeviceGroup) && (m_HaveSelection)) | |||
| copy->activate(); | |||
| else | |||
| copy->deactivate(); | |||
| if ((cb_CutDeviceGroup) && (m_HaveSelection)) | |||
| cut->activate(); | |||
| else | |||
| cut->deactivate(); | |||
| if ((cb_PasteDeviceGroup) && (m_CanPaste)) | |||
| paste->activate(); | |||
| else | |||
| paste->deactivate(); | |||
| if (m_HaveSelection) | |||
| deleteitems->activate(); | |||
| else | |||
| deleteitems->deactivate(); | |||
| m_Menu->popup(); | |||
| group->remove(m_Menu); | |||
| } | |||
| void Fl_Canvas::StreamSelectionWiresIn(istream &s, std::map<int,int> NewDeviceIds, bool merge, bool paste) | |||
| { | |||
| MapNewDeviceIds = NewDeviceIds; | |||
| StreamWiresIn(s, merge, paste); | |||
| } | |||
| void Fl_Canvas::StreamSelectionWiresOut(ostream &s) | |||
| { | |||
| int total_wires = 0, curpos=0; | |||
| curpos = s.tellp(); | |||
| s<<-1<<endl; | |||
| if (m_WireVec.size()>0) | |||
| for(vector<CanvasWire>::iterator i=m_WireVec.begin(); | |||
| i!=m_WireVec.end(); i++) | |||
| { | |||
| std::vector<int>::iterator output = std::find( m_Selection.m_DeviceIds.begin(), m_Selection.m_DeviceIds.end(), i->OutputID ); | |||
| std::vector<int>::iterator input = std::find( m_Selection.m_DeviceIds.begin(), m_Selection.m_DeviceIds.end(), i->InputID ); | |||
| if ((input != m_Selection.m_DeviceIds.end()) && (output != m_Selection.m_DeviceIds.end())) | |||
| { | |||
| s<<i->OutputID<<" "; | |||
| s<<0<<" "; | |||
| s<<i->OutputPort<<" "; | |||
| s<<i->OutputTerminal<<" "; | |||
| s<<i->InputID<<" "; | |||
| s<<0<<" "; | |||
| s<<i->InputPort<<" "; | |||
| s<<i->InputTerminal<<endl; | |||
| total_wires += 1; | |||
| } | |||
| } | |||
| if (total_wires >= 1) | |||
| { | |||
| s.seekp(curpos, ios::beg); | |||
| s<<total_wires<<endl; | |||
| s.seekp(0, ios::end); | |||
| } | |||
| } | |||
| //////////////////////////////////////////////////////////////////////// | |||
| void Fl_Canvas::DrawWires() | |||
| { | |||
| for(vector<CanvasWire>::iterator i=m_WireVec.begin(); | |||
| @@ -304,7 +584,7 @@ void Fl_Canvas::ClearIncompleteWire() | |||
| int Fl_Canvas::handle(int event) | |||
| { | |||
| if (Fl_Group::handle(event)) return 1; | |||
| if (event==FL_PUSH) | |||
| { | |||
| ClearIncompleteWire(); | |||
| @@ -313,12 +593,84 @@ int Fl_Canvas::handle(int event) | |||
| m_DragY=Fl::event_y(); | |||
| } | |||
| if (Fl::event_button()==1 && event==FL_DRAG) | |||
| if (Fl::event_button()==1) | |||
| { | |||
| position(x()+(Fl::event_x()-m_DragX),y()+(Fl::event_y()-m_DragY)); | |||
| m_DragX=Fl::event_x(); | |||
| m_DragY=Fl::event_y(); | |||
| redraw(); | |||
| if (event==FL_PUSH) | |||
| { | |||
| if (m_HaveSelection) | |||
| { | |||
| m_Selection.Clear(); | |||
| m_HaveSelection = false; | |||
| } | |||
| if ((Fl::event_state() & FL_CTRL) != 0) | |||
| { | |||
| m_Selecting = true; | |||
| m_StartSelectX=Fl::event_x(); | |||
| m_StartSelectY=Fl::event_y(); | |||
| m_EndSelectX=Fl::event_x(); | |||
| m_EndSelectY=Fl::event_y(); | |||
| } | |||
| ClearIncompleteWire(); | |||
| redraw(); | |||
| m_DragX=Fl::event_x(); | |||
| m_DragY=Fl::event_y(); | |||
| } | |||
| if (event==FL_DRAG) | |||
| { | |||
| if (m_Selecting) | |||
| { | |||
| m_EndSelectX=Fl::event_x(); | |||
| m_EndSelectY=Fl::event_y(); | |||
| int newx=0, newy=0, xp=((Fl_Scroll *)parent())->xposition(), yp=((Fl_Scroll *)parent())->yposition(); | |||
| if ((m_EndSelectX < m_StartSelectX) && ((m_EndSelectX - x() - xp) <= 15)) | |||
| newx=10; | |||
| if ((m_EndSelectY < m_StartSelectY) && ((m_EndSelectY - y() - yp) <= 15)) | |||
| newy=10; | |||
| if ((m_EndSelectX > m_StartSelectX) && ((((Fl_Scroll *)parent())->x() + ((Fl_Scroll *)parent())->w() - m_EndSelectX - 15) <= 15)) | |||
| newx=-10; | |||
| if ((m_EndSelectY > m_StartSelectY) && ((((Fl_Scroll *)parent())->y() + ((Fl_Scroll *)parent())->h() - m_EndSelectY - 15) <= 5)) | |||
| newy=-10; | |||
| if ((newx!=0) || (newy!=0)) { | |||
| position(x()+newx,y()+newy); | |||
| m_StartSelectX += newx; | |||
| m_StartSelectY += newy; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| position(x()+(Fl::event_x()-m_DragX),y()+(Fl::event_y()-m_DragY)); | |||
| } | |||
| m_DragX=Fl::event_x(); | |||
| m_DragY=Fl::event_y(); | |||
| redraw(); | |||
| } | |||
| if (event==FL_RELEASE) | |||
| { | |||
| if (m_Selecting) | |||
| { | |||
| m_Selecting = false; | |||
| if ((m_EndSelectX != m_StartSelectX) && (m_EndSelectY != m_StartSelectY)) | |||
| CalculateSelection(); | |||
| redraw(); | |||
| } | |||
| } | |||
| } | |||
| if (Fl::event_button()==2) | |||
| @@ -348,6 +700,11 @@ int Fl_Canvas::handle(int event) | |||
| } | |||
| } | |||
| if ((Fl::event_button()==3) && (event==FL_PUSH) && ((Fl::event_state() & FL_CTRL) != 0)) | |||
| { | |||
| PopupEditMenu(this); | |||
| } | |||
| return 1; | |||
| } | |||
| @@ -448,7 +805,7 @@ void Fl_Canvas::PortClicked(Fl_DeviceGUI* Device, int Type, int Port, bool Value | |||
| redraw(); | |||
| // Clear the current selection | |||
| // Clear the current m_Selection | |||
| m_IncompleteWire.Clear(); | |||
| } | |||
| } | |||
| @@ -603,24 +960,29 @@ void Fl_Canvas::ToBot(Fl_DeviceGUI *o) | |||
| } | |||
| ///////////////////////////////////////////////////////////////////////// | |||
| istream &operator>>(istream &s, Fl_Canvas &o) | |||
| void Fl_Canvas::StreamWiresIn(istream &s, bool merge, bool paste) | |||
| { | |||
| int NumWires; | |||
| s>>NumWires; | |||
| // my bad, didn't version this stream - remove one day... | |||
| if (NumWires==-1) | |||
| if (paste || NumWires==-1) | |||
| { | |||
| int version; | |||
| s>>version; | |||
| s>>NumWires; | |||
| if (!paste) | |||
| { | |||
| s>>version; | |||
| s>>NumWires; | |||
| } | |||
| for(int n=0; n<NumWires; n++) | |||
| { | |||
| CanvasWire NewWire; | |||
| int dummy; | |||
| s>>NewWire.OutputID; | |||
| s>>dummy; | |||
| s>>NewWire.OutputPort; | |||
| @@ -629,21 +991,30 @@ istream &operator>>(istream &s, Fl_Canvas &o) | |||
| s>>dummy; | |||
| s>>NewWire.InputPort; | |||
| s>>NewWire.InputTerminal; | |||
| // if we can turn on both ports | |||
| if (o.FindDevice(NewWire.OutputID)->AddConnection(NewWire.OutputPort+ | |||
| o.FindDevice(NewWire.OutputID)->GetInfo()->NumInputs) && | |||
| o.FindDevice(NewWire.InputID)->AddConnection(NewWire.InputPort)) | |||
| if (paste || merge) | |||
| { | |||
| o.m_WireVec.push_back(NewWire); | |||
| std::map<int, int>::iterator inputID = MapNewDeviceIds.find( NewWire.InputID); | |||
| std::map<int, int>::iterator outputID = MapNewDeviceIds.find(NewWire.OutputID); | |||
| // Notify connection by callback | |||
| o.cb_Connection(&o,(void*)&NewWire); | |||
| o.m_Graph.AddConnection(NewWire.OutputID,NewWire.OutputTerminal,NewWire.InputID,NewWire.InputTerminal); | |||
| if ((inputID != MapNewDeviceIds.end()) && (outputID != MapNewDeviceIds.end())) | |||
| { | |||
| NewWire.InputID = inputID->second; | |||
| NewWire.OutputID = outputID->second; | |||
| } | |||
| } | |||
| // if we can turn on both ports | |||
| if (FindDevice(NewWire.OutputID)->AddConnection(NewWire.OutputPort+ | |||
| FindDevice(NewWire.OutputID)->GetInfo()->NumInputs) && | |||
| FindDevice(NewWire.InputID)->AddConnection(NewWire.InputPort)) | |||
| { | |||
| m_WireVec.push_back(NewWire); | |||
| // Notify connection by callback | |||
| cb_Connection(this,(void*)&NewWire); | |||
| m_Graph.AddConnection(NewWire.OutputID,NewWire.OutputTerminal,NewWire.InputID,NewWire.InputTerminal); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| else | |||
| { | |||
| for(int n=0; n<NumWires; n++) | |||
| @@ -658,19 +1029,36 @@ istream &operator>>(istream &s, Fl_Canvas &o) | |||
| s>>dummy; | |||
| s>>NewWire.InputPort; | |||
| if (paste || merge) | |||
| { | |||
| std::map<int, int>::iterator inputID = MapNewDeviceIds.find( NewWire.InputID); | |||
| std::map<int, int>::iterator outputID = MapNewDeviceIds.find(NewWire.OutputID); | |||
| if ((inputID != MapNewDeviceIds.end()) && (outputID != MapNewDeviceIds.end())) | |||
| { | |||
| NewWire.InputID = inputID->second; | |||
| NewWire.OutputID = outputID->second; | |||
| } | |||
| } | |||
| // if we can turn on both ports | |||
| if (o.FindDevice(NewWire.OutputID)->AddConnection(NewWire.OutputPort+ | |||
| o.FindDevice(NewWire.OutputID)->GetInfo()->NumInputs) && | |||
| o.FindDevice(NewWire.InputID)->AddConnection(NewWire.InputPort)) | |||
| if (FindDevice(NewWire.OutputID)->AddConnection(NewWire.OutputPort+ | |||
| FindDevice(NewWire.OutputID)->GetInfo()->NumInputs) && | |||
| FindDevice(NewWire.InputID)->AddConnection(NewWire.InputPort)) | |||
| { | |||
| o.m_WireVec.push_back(NewWire); | |||
| m_WireVec.push_back(NewWire); | |||
| // Notify connection by callback | |||
| o.cb_Connection(&o,(void*)&NewWire); | |||
| o.m_Graph.AddConnection(NewWire.OutputID,false,NewWire.InputID,false); | |||
| cb_Connection(this,(void*)&NewWire); | |||
| m_Graph.AddConnection(NewWire.OutputID,false,NewWire.InputID,false); | |||
| } | |||
| } | |||
| } | |||
| } | |||
| istream &operator>>(istream &s, Fl_Canvas &o) | |||
| { | |||
| o.StreamWiresIn(s, false, false); | |||
| return s; | |||
| } | |||
| @@ -19,9 +19,11 @@ | |||
| #include <FL/Fl_Group.h> | |||
| #include <FL/Fl_Output.h> | |||
| #include <FL/Fl_Image.h> | |||
| #include <FL/Fl_Menu_Button.h> | |||
| #include <vector> | |||
| #include <string> | |||
| #include "../../GraphSort.h" | |||
| #include "Fl_DeviceGUI.h" | |||
| #ifndef CANVAS_WIDGET | |||
| #define CANVAS_WIDGET | |||
| @@ -55,14 +57,30 @@ public: | |||
| bool DelMe; | |||
| }; | |||
| class CanvasGroup | |||
| { | |||
| public: | |||
| CanvasGroup() { Clear(); } | |||
| void Clear() | |||
| { | |||
| m_DeviceIds.clear(); | |||
| } | |||
| vector<int> m_DeviceIds; | |||
| }; | |||
| class Fl_Canvas : public Fl_Group | |||
| { | |||
| public: | |||
| Fl_Canvas(int x, int y, int w, int h, char *name); | |||
| ~Fl_Canvas(); | |||
| Fl_Menu_Button *m_Menu; | |||
| virtual void draw(); | |||
| virtual int handle(int event); | |||
| int globalhandle(int event); | |||
| void PortClicked(Fl_DeviceGUI* Device, int Type, int Port, bool Value); | |||
| void Rename(Fl_DeviceGUI* Device); | |||
| @@ -71,6 +89,12 @@ public: | |||
| void SetAddDeviceCallback(Fl_Callback* s) { cb_AddDevice=s; } | |||
| void SetRenameCallback(Fl_Callback* s) { cb_Rename=s; } | |||
| void SetCutDeviceGroupCallback(Fl_Callback* s) { cb_CutDeviceGroup=s; } | |||
| void SetCopyDeviceGroupCallback(Fl_Callback* s) { cb_CopyDeviceGroup=s; } | |||
| void SetPasteDeviceGroupCallback(Fl_Callback* s) { cb_PasteDeviceGroup=s; } | |||
| void SetMergePatchCallback(Fl_Callback* s) { cb_MergePatch=s; } | |||
| void ClearConnections(Fl_DeviceGUI* Device); | |||
| void RemoveDevice(Fl_DeviceGUI* Device); | |||
| void Clear(); | |||
| @@ -83,7 +107,48 @@ public: | |||
| void ToTop(Fl_DeviceGUI *o); | |||
| void ToBot(Fl_DeviceGUI *o); | |||
| void StreamSelectionWiresIn(istream &s, map<int,int> NewDeviceIds, bool merge, bool paste); | |||
| void StreamSelectionWiresOut(ostream &s); | |||
| void StreamWiresIn(istream &s, bool merge, bool paste); | |||
| static void AppendSelection(int DeviceId, Fl_Canvas *data) | |||
| { | |||
| Fl_DeviceGUI *o = data->FindDevice(DeviceId); | |||
| if (o) | |||
| { | |||
| data->m_HaveSelection = true; | |||
| data->m_Selection.m_DeviceIds.push_back(DeviceId); | |||
| o->SetOnDragCallback(Fl_Canvas::cb_OnDrag_s, data); | |||
| data->redraw(); | |||
| } | |||
| } | |||
| static void ClearSelection(Fl_Canvas *data) | |||
| { | |||
| data->m_HaveSelection=false; | |||
| data->m_Selection.Clear(); | |||
| data->redraw(); | |||
| } | |||
| static void EnablePaste(Fl_Canvas *data) | |||
| { | |||
| data->m_CanPaste=true; | |||
| } | |||
| bool HaveSelection() {return m_HaveSelection; } | |||
| CanvasGroup Selection() { return m_Selection; } | |||
| static void SetDeviceCallbacks(Fl_DeviceGUI *device, Fl_Canvas *data) | |||
| { | |||
| device->SetOnDragCallback(Fl_Canvas::cb_OnDrag_s, data); | |||
| device->SetOnClickCallback(Fl_Canvas::cb_OnDragClick_s, data); | |||
| } | |||
| private: | |||
| void DrawSelection(); | |||
| void CalculateSelection(); | |||
| void DrawWires(); | |||
| void ClearIncompleteWire(); | |||
| @@ -99,6 +164,13 @@ private: | |||
| void (*cb_AddDevice)(Fl_Widget*, void*); | |||
| void (*cb_Rename)(Fl_Widget*, void*); | |||
| void (*cb_CutDeviceGroup)(Fl_Widget*, void*); | |||
| void (*cb_CopyDeviceGroup)(Fl_Widget*, void*); | |||
| void (*cb_PasteDeviceGroup)(Fl_Widget*, void*); | |||
| void (*cb_MergePatch)(Fl_Widget*, void*); | |||
| map<int,int> MapNewDeviceIds; | |||
| vector<CanvasWire> m_WireVec; | |||
| CanvasWire m_IncompleteWire; | |||
| @@ -109,9 +181,60 @@ private: | |||
| GraphSort m_Graph; | |||
| int m_UpdateTimer; | |||
| int m_DragX,m_DragY; | |||
| bool m_HaveSelection, m_Selecting; | |||
| int m_StartSelectX,m_StartSelectY; | |||
| int m_EndSelectX,m_EndSelectY; | |||
| CanvasGroup m_Selection; | |||
| bool m_CanPaste; | |||
| friend istream &operator>>(istream &s, Fl_Canvas &o); | |||
| friend ostream &operator<<(ostream &s, Fl_Canvas &o); | |||
| void PopupEditMenu(Fl_Group *group); | |||
| ///Inline Callbacks/// | |||
| inline void cb_OnDrag_i(Fl_Widget* widget, int x,int y); | |||
| inline void cb_OnDragClick_i(Fl_Widget* widget, int button,int shift_state) | |||
| { | |||
| if ((button==3) && ((shift_state & FL_CTRL) != 0)) | |||
| { | |||
| PopupEditMenu(widget->parent()); | |||
| } | |||
| if ((widget) && (button==1) && ((shift_state & FL_CTRL) != 0)) | |||
| { | |||
| int ID = ((Fl_DeviceGUI*)(widget->parent()))->GetID(); | |||
| std::vector<int>::iterator device_iter = std::find(m_Selection.m_DeviceIds.begin(), m_Selection.m_DeviceIds.end(), ID); | |||
| if (m_HaveSelection) | |||
| { | |||
| if (device_iter != m_Selection.m_DeviceIds.end()) | |||
| m_Selection.m_DeviceIds.erase(device_iter); | |||
| else | |||
| m_Selection.m_DeviceIds.push_back(ID); | |||
| } | |||
| else | |||
| { | |||
| m_Selection.Clear(); | |||
| m_HaveSelection = true; | |||
| m_Selection.m_DeviceIds.push_back(ID); | |||
| } | |||
| redraw(); | |||
| } | |||
| } | |||
| inline void cb_DeleteDeviceGroup_i(); | |||
| ///Static Callbacks/// | |||
| static void cb_OnDrag_s(Fl_Widget* widget, int x,int y, void* data) { ((Fl_Canvas *)data)->cb_OnDrag_i(widget, x, y); } | |||
| static void cb_OnDragClick_s(Fl_Widget* widget, int button,int shift_state, void* data) { ((Fl_Canvas *)data)->cb_OnDragClick_i(widget, button, shift_state); } | |||
| static void cb_DeleteDeviceGroup(Fl_Widget* widget, void* data) { ((Fl_Canvas *)data)->cb_DeleteDeviceGroup_i(); } | |||
| }; | |||
| istream &operator>>(istream &s, Fl_Canvas &o); | |||
| @@ -141,6 +141,7 @@ int Fl_DeviceGUI::handle (int event) { | |||
| Minimise(); | |||
| if (m_IconButton) m_IconButton->show(); | |||
| } | |||
| return 1; | |||
| } | |||
| @@ -104,6 +104,10 @@ class Fl_DeviceGUI : public Fl_Group { | |||
| int GetPortType (int n) { return m_Info.PortTypes[n]; } | |||
| // do we belong to a plugin that is an output? | |||
| bool IsTerminal() { return m_IsTerminal; } | |||
| void SetOnDragCallback(void (*cb)(Fl_Widget*, int x,int y, void*), void* data) { m_DragBar->cb_OnDrag = cb; m_DragBar->cb_OnDrag_Data = data; } | |||
| void SetOnClickCallback(void (*cb)(Fl_Widget*, int button, int shift_state, void*), void* data) { m_DragBar->cb_OnClick = cb; m_DragBar->cb_OnClick_Data = data; } | |||
| static void Kill(Fl_DeviceGUI *device) { if (device) device->m_DelMe = true; } | |||
| protected: | |||
| DeviceGUIInfo m_Info; | |||
| Fl_DragBar* m_DragBar; | |||
| @@ -11,9 +11,16 @@ class Fl_DragBar : public Fl_Widget { | |||
| int old_rx,old_ry; | |||
| int _type; | |||
| public: | |||
| enum DragType {WINDRAG=0,NICEWINDRAG,FLDRAG,NICEFLDRAG}; | |||
| Fl_DragBar(int x,int y,int w,int h,const char *l=0); | |||
| ~Fl_DragBar(); | |||
| enum DragType {WINDRAG=0,NICEWINDRAG,FLDRAG,NICEFLDRAG}; | |||
| void (*cb_OnClick)(Fl_Widget*, int button, int shift_state, void*); | |||
| void *cb_OnClick_Data; | |||
| void (*cb_OnDrag)(Fl_Widget*, int xoffset,int yoffset, void*); | |||
| void *cb_OnDrag_Data; | |||
| private: | |||
| void draw(); | |||
| int handle(int event); | |||
| @@ -3,7 +3,11 @@ | |||
| #include "Fl_DragBar.H" | |||
| #include <string.h> | |||
| Fl_DragBar::Fl_DragBar(int x,int y,int w,int h,const char *l): Fl_Widget(x,y,w,h,l) { | |||
| Fl_DragBar::Fl_DragBar(int x,int y,int w,int h,const char *l): | |||
| Fl_Widget(x,y,w,h,l), | |||
| cb_OnDrag(NULL), | |||
| cb_OnDrag_Data(NULL) | |||
| { | |||
| _type = Fl_DragBar::NICEFLDRAG; | |||
| } | |||
| @@ -119,6 +123,12 @@ int mx,my; | |||
| { | |||
| fl_cursor(FL_CURSOR_DEFAULT); | |||
| do_callback(); | |||
| if ((Fl::event_is_click()) && (cb_OnClick)) | |||
| { | |||
| cb_OnClick(this, Fl::event_button(), Fl::event_state(), cb_OnClick_Data); | |||
| } | |||
| return 1; | |||
| } | |||
| case FL_DRAG: | |||
| @@ -129,11 +139,30 @@ int mx,my; | |||
| yy = ry - old_ry; | |||
| if (_type < (int)Fl_DragBar::FLDRAG) | |||
| { | |||
| if (cb_OnDrag) { | |||
| int xoffset, yoffset; | |||
| xoffset = (rx - window()->x()) - old_rx; | |||
| yoffset = (ry - window()->y()) - old_ry; | |||
| cb_OnDrag(this, xoffset, yoffset, cb_OnDrag_Data); | |||
| } | |||
| window()->position(xx,yy); | |||
| } | |||
| else | |||
| { | |||
| if (cb_OnDrag) { | |||
| int xoffset, yoffset; | |||
| xoffset = (rx - parent()->x()) - old_rx; | |||
| yoffset = (ry - parent()->y()) - old_ry; | |||
| cb_OnDrag(this, xoffset, yoffset, cb_OnDrag_Data); | |||
| } | |||
| parent()->position(xx,yy); | |||
| if (parent()->parent()) | |||
| parent()->parent()->redraw(); | |||
| else | |||
| @@ -18,9 +18,16 @@ class Fl_DragBar {open : {public Fl_Widget} | |||
| } { | |||
| decl {int old_rx,old_ry;} {} | |||
| decl {int _type;} {} | |||
| decl {void (*cb_OnDrag)(Fl_Widget*, int xoffset,int yoffset, void*);} {public} | |||
| decl {void *cb_OnDrag_Data;} {public} | |||
| decl {void (*cb_OnClick)(Fl_Widget*, int button, int shift_state, void*);} {public} | |||
| decl {void *cb_OnClick_Data;} {public} | |||
| decl {enum DragType {WINDRAG=0,NICEWINDRAG,FLDRAG,NICEFLDRAG};} {public | |||
| } | |||
| Function {Fl_DragBar(int x,int y,int w,int h,const char *l=0): Fl_Widget(x,y,w,h,l)} {open | |||
| Function {Fl_DragBar(int x,int y,int w,int h,const char *l=0): Fl_Widget(x,y,w,h,l), | |||
| cb_OnDrag(NULL), | |||
| cb_OnDrag_Data(NULL) | |||
| } {open | |||
| } { | |||
| code {_type = Fl_DragBar::NICEFLDRAG;} {} | |||
| } | |||
| @@ -133,6 +140,10 @@ int mx,my; | |||
| { | |||
| fl_cursor(FL_CURSOR_DEFAULT); | |||
| do_callback(); | |||
| if (((Fl::event_is_click()) && (cb_OnClick)) | |||
| { | |||
| cb_OnClick(this, cb_OnClick_Data); | |||
| } | |||
| return 1; | |||
| } | |||
| case FL_DRAG: | |||
| @@ -141,13 +152,33 @@ int mx,my; | |||
| { | |||
| xx = rx - old_rx; | |||
| yy = ry - old_ry; | |||
| if (_type < (int)Fl_DragBar::FLDRAG) | |||
| { | |||
| if (cb_OnDrag) { | |||
| int xoffset, yoffset; | |||
| xoffset = (rx - window()->x()) - old_rx; | |||
| yoffset = (ry - window()->y()) - old_ry; | |||
| cb_OnDrag(this, xoffset, yoffset, cb_OnDrag_Data); | |||
| } | |||
| window()->position(xx,yy); | |||
| } | |||
| else | |||
| { | |||
| if (cb_OnDrag) { | |||
| int xoffset, yoffset; | |||
| xoffset = (rx - parent()->x()) - old_rx; | |||
| yoffset = (ry - parent()->y()) - old_ry; | |||
| cb_OnDrag(this, xoffset, yoffset, cb_OnDrag_Data); | |||
| } | |||
| parent()->position(xx,yy); | |||
| if (parent()->parent()) | |||
| parent()->parent()->redraw(); | |||
| else | |||
| @@ -491,14 +491,14 @@ bool PoshSamplerPlugin::SaveExternalFiles(const string &Dir) | |||
| return true; | |||
| } | |||
| void PoshSamplerPlugin::LoadExternalFiles(const string &Dir) | |||
| void PoshSamplerPlugin::LoadExternalFiles(const string &Dir, int withID) | |||
| { | |||
| for (int n=0; n<NUM_SAMPLES; n++) | |||
| { | |||
| char temp[256]; | |||
| // Andy's fix for bug 766594 | |||
| // sprintf (temp, "PoshSampler%d_%d.wav", SpiralPlugin_GetID(), n); | |||
| sprintf (temp, "PoshSampler%d_%d.wav", GetID(), n); | |||
| sprintf (temp, "PoshSampler%d_%d.wav", ((withID==-1)?GetID():withID), n); | |||
| m_SampleDescVec[n]->Pathname = temp; | |||
| } | |||
| @@ -59,7 +59,7 @@ public: | |||
| virtual void StreamIn(std::istream &s); | |||
| virtual bool SaveExternalFiles(const std::string &Dir); | |||
| virtual void LoadExternalFiles(const std::string &Dir); | |||
| virtual void LoadExternalFiles(const std::string &Dir, int withID=-1); | |||
| enum GUICommands{NONE,LOAD,SAVE,SETVOL,SETPITCH,SETLOOP,SETPING,SETNOTE,SETOCT, | |||
| SETPLAYPOINTS,SETREC,CUT,COPY,PASTE,CROP,MIX,REV,AMP,SETCURRENT, | |||
| @@ -91,7 +91,7 @@ public: | |||
| // stuff here gets saved in filename_files directory | |||
| // you must return true if this feature is used. | |||
| virtual bool SaveExternalFiles(const std::string &Dir) { return false; } | |||
| virtual void LoadExternalFiles(const std::string &Dir) {} | |||
| virtual void LoadExternalFiles(const std::string &Dir, int withID=-1) {} | |||
| const HostInfo* GetHostInfo() { return m_HostInfo; } | |||
| bool GetOutput(unsigned int n, Sample **s); | |||
| @@ -104,6 +104,7 @@ SynthModular::~SynthModular() | |||
| { | |||
| ClearUp(); | |||
| PluginManager::Get()->PackUpAndGoHome(); | |||
| system("rm -f ___temp.ssmcopytmp"); | |||
| } | |||
| ////////////////////////////////////////////////////////// | |||
| @@ -371,6 +372,13 @@ SpiralWindowType *SynthModular::CreateWindow() | |||
| m_Canvas->SetConnectionCallback((Fl_Callback*)cb_Connection); | |||
| m_Canvas->SetUnconnectCallback((Fl_Callback*)cb_Unconnect); | |||
| m_Canvas->SetAddDeviceCallback((Fl_Callback*)cb_NewDeviceFromMenu); | |||
| m_Canvas->SetCutDeviceGroupCallback((Fl_Callback*)cb_CutDeviceGroup); | |||
| m_Canvas->SetCopyDeviceGroupCallback((Fl_Callback*)cb_CopyDeviceGroup); | |||
| m_Canvas->SetPasteDeviceGroupCallback((Fl_Callback*)cb_PasteDeviceGroup); | |||
| m_Canvas->SetMergePatchCallback((Fl_Callback*)cb_MergePatch); | |||
| m_CanvasScroll->add(m_Canvas); | |||
| m_SettingsWindow = new SettingsWindow; | |||
| @@ -629,6 +637,7 @@ DeviceWin* SynthModular::NewDeviceWin(int n, int x, int y) | |||
| Info.YPos = y; //rand()%400; | |||
| nlw->m_DeviceGUI = new Fl_DeviceGUI(Info, temp, Pix, nlw->m_Device->IsTerminal()); | |||
| Fl_Canvas::SetDeviceCallbacks(nlw->m_DeviceGUI, m_Canvas); | |||
| m_Canvas->add(nlw->m_DeviceGUI); | |||
| m_Canvas->redraw(); | |||
| @@ -685,6 +694,7 @@ DeviceWin* SynthModular::NewComment(int n, int x=-1, int y=-1) | |||
| nlw->m_DeviceGUI = new Fl_CommentGUI(Info, NULL, NULL); | |||
| Fl_Canvas::SetDeviceCallbacks(nlw->m_DeviceGUI, m_Canvas); | |||
| m_Canvas->add(nlw->m_DeviceGUI); | |||
| m_Canvas->redraw(); | |||
| @@ -755,35 +765,201 @@ void SynthModular::cb_Blocking(void* o, bool mode) | |||
| ////////////////////////////////////////////////////////// | |||
| istream &operator>>(istream &s, SynthModular &o) | |||
| inline void SynthModular::cb_CutDeviceGroup_i() | |||
| { | |||
| o.PauseAudio(); | |||
| if (! m_Canvas->HaveSelection()) | |||
| return; | |||
| string dummy,dummy2; | |||
| int ver; | |||
| s>>dummy>>dummy>>dummy>>ver; | |||
| //show some warning here | |||
| if (ver>FILE_VERSION) | |||
| cb_CopyDeviceGroup_i(); | |||
| for (unsigned int i=0; i<m_Canvas->Selection().m_DeviceIds.size(); i++) | |||
| { | |||
| SpiralInfo::Alert("Bad file, or more recent version."); | |||
| return s; | |||
| int ID = m_Canvas->Selection().m_DeviceIds[i]; | |||
| Fl_DeviceGUI::Kill(m_DeviceWinMap[ID]->m_DeviceGUI); | |||
| } | |||
| Fl_Canvas::ClearSelection(m_Canvas); | |||
| } | |||
| ////////////////////////////////////////////////////////// | |||
| inline void SynthModular::cb_MergePatch_i() | |||
| { | |||
| char *fn=fl_file_chooser("Merge a patch", "*.ssm", NULL); | |||
| if (fn && fn!='\0') | |||
| { | |||
| ifstream in(fn); | |||
| if (in) | |||
| { | |||
| fstream inf; | |||
| inf.open(fn,ios::in); | |||
| m_MergeFilePath=fn; | |||
| StreamPatchIn(inf, false, true); | |||
| m_Canvas->StreamSelectionWiresIn(inf, m_Copied.m_DeviceIds, true, false); | |||
| inf.close(); | |||
| } | |||
| } | |||
| } | |||
| ////////////////////////////////////////////////////////// | |||
| inline void SynthModular::cb_CopyDeviceGroup_i() | |||
| { | |||
| if (! m_Canvas->HaveSelection()) | |||
| return; | |||
| m_Copied.devices.open("___temp.ssmcopytmp",ios::out); | |||
| if (ver>2) | |||
| m_Copied.devicecount = 0; | |||
| m_Copied.m_DeviceIds.clear(); | |||
| if (m_FilePath != "") | |||
| { | |||
| int MainWinX,MainWinY,MainWinW,MainWinH; | |||
| int EditWinX,EditWinY,EditWinW,EditWinH; | |||
| m_Copied.devices<<true<<" "; | |||
| m_Copied.devices<<m_FilePath<<endl; | |||
| } | |||
| else | |||
| m_Copied.devices<<false<<endl; | |||
| s>>MainWinX>>MainWinY>>MainWinW>>MainWinH; | |||
| s>>EditWinX>>EditWinY>>EditWinW>>EditWinH; | |||
| for (unsigned int i=0; i<m_Canvas->Selection().m_DeviceIds.size(); i++) | |||
| { | |||
| int ID = m_Canvas->Selection().m_DeviceIds[i]; | |||
| std::map<int,DeviceWin*>::iterator i = m_DeviceWinMap.find(ID); | |||
| m_Copied.m_DeviceIds[ID] = ID; | |||
| m_Copied.devicecount += 1; | |||
| m_Copied.devices<<"Device "; | |||
| m_Copied.devices<<i->first<<" "; // save the id | |||
| m_Copied.devices<<"Plugin "; | |||
| m_Copied.devices<<i->second->m_PluginID<<endl; | |||
| m_Copied.devices<<i->second->m_DeviceGUI->x()<<" "; | |||
| m_Copied.devices<<i->second->m_DeviceGUI->y()<<" "; | |||
| m_Copied.devices<<i->second->m_DeviceGUI->GetName().size()<<" "; | |||
| m_Copied.devices<<i->second->m_DeviceGUI->GetName()<<" "; | |||
| if (i->second->m_DeviceGUI->GetPluginWindow()) | |||
| { | |||
| m_Copied.devices<<i->second->m_DeviceGUI->GetPluginWindow()->visible()<<" "; | |||
| m_Copied.devices<<i->second->m_DeviceGUI->GetPluginWindow()->x()<<" "; | |||
| m_Copied.devices<<i->second->m_DeviceGUI->GetPluginWindow()->y()<<" "; | |||
| } | |||
| else | |||
| { | |||
| m_Copied.devices<<0<<" "<<0<<" "<<0; | |||
| } | |||
| m_Copied.devices<<endl; | |||
| //o.m_MainWindow->resize(MainWinX,MainWinY,MainWinW,MainWinH); | |||
| //o.m_EditorWindow->resize(EditWinX,EditWinY,EditWinW,EditWinH); | |||
| if (i->second->m_PluginID==COMMENT_ID) | |||
| { | |||
| // save the comment gui | |||
| ((Fl_CommentGUI*)(i->second->m_DeviceGUI))->StreamOut(m_Copied.devices); | |||
| } | |||
| else | |||
| { | |||
| // save the plugin | |||
| i->second->m_Device->StreamOut(m_Copied.devices); | |||
| } | |||
| m_Copied.devices<<endl; | |||
| } | |||
| m_Canvas->StreamSelectionWiresOut(m_Copied.devices); | |||
| m_Copied.devices.close(); | |||
| int Num, ID, PluginID, x,y,ps,px,py; | |||
| s>>dummy>>Num; | |||
| Fl_Canvas::EnablePaste(m_Canvas); | |||
| }; | |||
| ////////////////////////////////////////////////////////// | |||
| inline void SynthModular::cb_PasteDeviceGroup_i() | |||
| { | |||
| if (m_Copied.devicecount <= 0) | |||
| return; | |||
| m_Copied.devices.open("___temp.ssmcopytmp",ios::in); | |||
| StreamPatchIn(m_Copied.devices, true, false); | |||
| m_Canvas->StreamSelectionWiresIn(m_Copied.devices, m_Copied.m_DeviceIds, false, true); | |||
| m_Copied.devices.close(); | |||
| }; | |||
| ////////////////////////////////////////////////////////// | |||
| iostream &SynthModular::StreamPatchIn(iostream &s, bool paste, bool merge) | |||
| { | |||
| //if we are merging as opposed to loading a new patch | |||
| //we have no need to pause audio | |||
| if (!merge && !paste) | |||
| PauseAudio(); | |||
| //if we are pasting we don't have any of the file version | |||
| //or saving information. since its internal we didn't | |||
| //need it, but we do have other things we might need to load | |||
| bool has_file_path; | |||
| string m_FromFilePath; | |||
| string dummy,dummy2; | |||
| int ver; | |||
| if (paste) | |||
| { | |||
| m_Copied.devices>>has_file_path; | |||
| if (has_file_path) | |||
| m_Copied.devices>>m_FromFilePath; | |||
| } | |||
| else | |||
| { | |||
| s>>dummy>>dummy>>dummy>>ver; | |||
| if (ver>FILE_VERSION) | |||
| { | |||
| SpiralInfo::Alert("Bad file, or more recent version."); | |||
| return s; | |||
| } | |||
| if (ver>2) | |||
| { | |||
| int MainWinX,MainWinY,MainWinW,MainWinH; | |||
| int EditWinX,EditWinY,EditWinW,EditWinH; | |||
| s>>MainWinX>>MainWinY>>MainWinW>>MainWinH; | |||
| s>>EditWinX>>EditWinY>>EditWinW>>EditWinH; | |||
| //o.m_MainWindow->resize(MainWinX,MainWinY,MainWinW,MainWinH); | |||
| //o.m_EditorWindow->resize(EditWinX,EditWinY,EditWinW,EditWinH); | |||
| } | |||
| if (merge) | |||
| m_FromFilePath = m_MergeFilePath; | |||
| } | |||
| //wether pasting or merging we need to clear the current | |||
| //selection so we can replace it with the new devices | |||
| if (paste || merge) | |||
| Fl_Canvas::ClearSelection(m_Canvas); | |||
| int Num, ID, PluginID, x,y,ps,px,py; | |||
| if (paste) | |||
| { | |||
| Num = m_Copied.devicecount; | |||
| } | |||
| else | |||
| { | |||
| s>>dummy>>Num; | |||
| } | |||
| for(int n=0; n<Num; n++) | |||
| { | |||
| #ifdef DEBUG_STREAM | |||
| @@ -798,7 +974,7 @@ istream &operator>>(istream &s, SynthModular &o) | |||
| string Name; | |||
| if (ver>3) | |||
| if (paste || ver>3) | |||
| { | |||
| // load the device name | |||
| int size; | |||
| @@ -811,75 +987,107 @@ istream &operator>>(istream &s, SynthModular &o) | |||
| } else { | |||
| Name = ""; | |||
| } | |||
| } | |||
| } | |||
| #ifdef DEBUG_STREAM | |||
| cerr<<dummy<<" "<<ID<<" "<<dummy2<<" "<<PluginID<<" "<<x<<" "<<y<<endl; | |||
| #endif | |||
| if (ver>1) s>>ps>>px>>py; | |||
| // Check we're not duplicating an ID | |||
| if (o.m_DeviceWinMap.find(ID)!=o.m_DeviceWinMap.end()) | |||
| if (paste || ver>1) s>>ps>>px>>py; | |||
| //if we are merging a patch or pasting we will change duplicate ID's | |||
| if (!paste && !merge) | |||
| { | |||
| SpiralInfo::Alert("Duplicate device ID found in file - aborting load"); | |||
| return s; | |||
| // Check we're not duplicating an ID | |||
| if (m_DeviceWinMap.find(ID)!=m_DeviceWinMap.end()) | |||
| { | |||
| SpiralInfo::Alert("Duplicate device ID found in file - aborting load"); | |||
| return s; | |||
| } | |||
| } | |||
| if (PluginID==COMMENT_ID) | |||
| { | |||
| DeviceWin* temp = o.NewComment(PluginID, x, y); | |||
| DeviceWin* temp = NewComment(PluginID, x, y); | |||
| if (temp) | |||
| { | |||
| if (paste || merge) | |||
| { | |||
| m_Copied.m_DeviceIds[ID] = m_NextID++; | |||
| ID = m_Copied.m_DeviceIds[ID]; | |||
| } | |||
| temp->m_DeviceGUI->SetID(ID); | |||
| o.m_DeviceWinMap[ID]=temp; | |||
| ((Fl_CommentGUI*)(o.m_DeviceWinMap[ID]->m_DeviceGUI))->StreamIn(s); // load the plugin | |||
| if (o.m_NextID<=ID) o.m_NextID=ID+1; | |||
| m_DeviceWinMap[ID]=temp; | |||
| ((Fl_CommentGUI*)(m_DeviceWinMap[ID]->m_DeviceGUI))->StreamIn(s); // load the plugin | |||
| if (paste || merge) | |||
| Fl_Canvas::AppendSelection(ID, m_Canvas); | |||
| else | |||
| if (m_NextID<=ID) m_NextID=ID+1; | |||
| } | |||
| } | |||
| else | |||
| { | |||
| DeviceWin* temp = o.NewDeviceWin(PluginID, x, y); | |||
| DeviceWin* temp = NewDeviceWin(PluginID, x, y); | |||
| if (temp) | |||
| { | |||
| int oldID=ID; | |||
| if (paste || merge) | |||
| { | |||
| m_Copied.m_DeviceIds[ID] = m_NextID++; | |||
| ID = m_Copied.m_DeviceIds[ID]; | |||
| } | |||
| temp->m_DeviceGUI->SetID(ID); | |||
| if (ver>3) | |||
| if (paste || ver>3) | |||
| { | |||
| // set the titlebars | |||
| temp->m_DeviceGUI->SetName(Name); | |||
| } | |||
| temp->m_Device->SetUpdateInfoCallback(ID,o.cb_UpdatePluginInfo); | |||
| o.m_DeviceWinMap[ID]=temp; | |||
| o.m_DeviceWinMap[ID]->m_Device->StreamIn(s); // load the plugin | |||
| temp->m_Device->SetUpdateInfoCallback(ID,cb_UpdatePluginInfo); | |||
| m_DeviceWinMap[ID]=temp; | |||
| m_DeviceWinMap[ID]->m_Device->StreamIn(s); // load the plugin | |||
| // load external files | |||
| o.m_DeviceWinMap[ID]->m_Device->LoadExternalFiles(o.m_FilePath+"_files/"); | |||
| if (paste || merge) | |||
| m_DeviceWinMap[ID]->m_Device->LoadExternalFiles(m_FromFilePath+"_files/", oldID); | |||
| else | |||
| m_DeviceWinMap[ID]->m_Device->LoadExternalFiles(m_FilePath+"_files/"); | |||
| if (ver>1 && o.m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow()) | |||
| if ((paste || ver>1) && m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow()) | |||
| { | |||
| // set the GUI up with the loaded values | |||
| // looks messy, but if we do it here, the plugin and it's gui can remain | |||
| // totally seperated. | |||
| ((SpiralPluginGUI*)(o.m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow()))-> | |||
| UpdateValues(o.m_DeviceWinMap[ID]->m_Device); | |||
| ((SpiralPluginGUI*)(m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow()))-> | |||
| UpdateValues(m_DeviceWinMap[ID]->m_Device); | |||
| // updates the data in the channel buffers, so the values don't | |||
| // get overwritten in the next tick. (should maybe be somewhere else) | |||
| o.m_DeviceWinMap[ID]->m_Device->GetChannelHandler()->FlushChannels(); | |||
| m_DeviceWinMap[ID]->m_Device->GetChannelHandler()->FlushChannels(); | |||
| // position the plugin window in the main window | |||
| //o.m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow()->position(px,py); | |||
| //m_DeviceWinMap[ID]->m_DeviceGUI->GetPluginWindow()->position(px,py); | |||
| if (ps) | |||
| { | |||
| o.m_DeviceWinMap[ID]->m_DeviceGUI->Maximise(); | |||
| m_DeviceWinMap[ID]->m_DeviceGUI->Maximise(); | |||
| // reposition after maximise | |||
| o.m_DeviceWinMap[ID]->m_DeviceGUI->position(x,y); | |||
| m_DeviceWinMap[ID]->m_DeviceGUI->position(x,y); | |||
| } | |||
| else o.m_DeviceWinMap[ID]->m_DeviceGUI->Minimise(); | |||
| else m_DeviceWinMap[ID]->m_DeviceGUI->Minimise(); | |||
| if (paste || merge) | |||
| Fl_Canvas::AppendSelection(ID, m_Canvas); | |||
| } | |||
| if (o.m_NextID<=ID) o.m_NextID=ID+1; | |||
| if (!paste && !merge) | |||
| if (m_NextID<=ID) m_NextID=ID+1; | |||
| } | |||
| else | |||
| { | |||
| @@ -891,12 +1099,21 @@ istream &operator>>(istream &s, SynthModular &o) | |||
| } | |||
| } | |||
| s>>*o.m_Canvas; | |||
| o.ResumeAudio(); | |||
| if (!paste && !merge) | |||
| { | |||
| s>>*m_Canvas; | |||
| ResumeAudio(); | |||
| } | |||
| return s; | |||
| } | |||
| iostream &operator>>(iostream &s, SynthModular &o) | |||
| { | |||
| return o.StreamPatchIn(s, false, false); | |||
| } | |||
| ////////////////////////////////////////////////////////// | |||
| ostream &operator<<(ostream &s, SynthModular &o) | |||
| @@ -1013,15 +1230,20 @@ inline void SynthModular::cb_Load_i(Fl_Button* o, void* v) | |||
| if (fn && fn!='\0') | |||
| { | |||
| ifstream inf(fn); | |||
| ifstream in(fn); | |||
| if (inf) | |||
| if (in) | |||
| { | |||
| fstream inf; | |||
| inf.open(fn,ios::in); | |||
| m_FilePath=fn; | |||
| ClearUp(); | |||
| inf>>*this; | |||
| inf.close(); | |||
| TITLEBAR=LABEL+" "+fn; | |||
| m_TopWindow->label(TITLEBAR.c_str()); | |||
| } | |||
| @@ -1232,18 +1454,23 @@ void SynthModular::cb_UpdatePluginInfo(int ID, void *PInfo) | |||
| void SynthModular::LoadPatch(const char *fn) | |||
| { | |||
| ifstream inf(fn); | |||
| ifstream in(fn); | |||
| if (inf) | |||
| if (in) | |||
| { | |||
| m_FilePath=fn; | |||
| fstream inf; | |||
| inf.open(fn); | |||
| m_FilePath=fn; | |||
| ClearUp(); | |||
| inf>>*this; | |||
| inf>>*this; | |||
| TITLEBAR=LABEL+" "+fn; | |||
| m_TopWindow->label(TITLEBAR.c_str()); | |||
| } | |||
| inf.close(); | |||
| TITLEBAR=LABEL+" "+fn; | |||
| m_TopWindow->label(TITLEBAR.c_str()); | |||
| } | |||
| } | |||
| ////////////////////////////////////////////////////////// | |||
| @@ -32,6 +32,7 @@ | |||
| #include <FL/Fl_Tabs.H> | |||
| #include <sstream> | |||
| #include <iostream> | |||
| #include <fstream> | |||
| #include <map> | |||
| #include "GUI/Widgets/Fl_DeviceGUI.h" | |||
| #include "GUI/Widgets/Fl_CommentGUI.h" | |||
| @@ -66,6 +67,16 @@ public: | |||
| virtual void draw() { draw_label(); } | |||
| }; | |||
| class DeviceGroup | |||
| { | |||
| public: | |||
| DeviceGroup() { devicecount = 0; } | |||
| int devicecount; | |||
| fstream devices; | |||
| map<int,int> m_DeviceIds;//old ID, new ID | |||
| }; | |||
| class SynthModular | |||
| { | |||
| public: | |||
| @@ -98,6 +109,8 @@ public: | |||
| // only for audio thread | |||
| bool IsPaused() { return m_PauseAudio; } | |||
| iostream &StreamPatchIn(iostream &s, bool paste, bool merge); | |||
| private: | |||
| vector<string> BuildPluginList(const string &Path); | |||
| @@ -118,7 +131,8 @@ private: | |||
| static bool m_CallbackUpdateMode; | |||
| static bool m_BlockingOutputPluginIsReady; | |||
| string m_FilePath; | |||
| string m_MergeFilePath; | |||
| // Main GUI stuff | |||
| void CreateGUI(int xoff=0, int yoff=0, char *name=""); | |||
| @@ -146,17 +160,22 @@ private: | |||
| bool m_PauseAudio; | |||
| inline void cb_NewDevice_i(Fl_Button* o, void* v); | |||
| static void cb_NewDevice(Fl_Button* o, void* v); | |||
| static void cb_NewDevice(Fl_Button* o, void* v); | |||
| inline void cb_NewDeviceFromMenu_i(Fl_Canvas* o, void* v); | |||
| static void cb_NewDeviceFromMenu(Fl_Canvas* o, void* v); | |||
| inline void cb_NewComment_i(Fl_Button* o, void* v); | |||
| static void cb_NewComment(Fl_Button* o, void* v); | |||
| static void cb_NewComment(Fl_Button* o, void* v); | |||
| inline void cb_Load_i(Fl_Button* o, void* v); | |||
| static void cb_Load(Fl_Button* o, void* v); | |||
| static void cb_Load(Fl_Button* o, void* v); | |||
| inline void cb_Save_i(Fl_Button* o, void* v); | |||
| static void cb_Save(Fl_Button* o, void* v); | |||
| static void cb_Save(Fl_Button* o, void* v); | |||
| inline void cb_New_i(Fl_Button* o, void* v); | |||
| static void cb_New(Fl_Button* o, void* v); | |||
| static void cb_New(Fl_Button* o, void* v); | |||
| inline void cb_MergePatch_i(); | |||
| static void cb_MergePatch(Fl_Canvas* o, SynthModular*v) { v->cb_MergePatch_i(); }; | |||
| inline void cb_Connection_i(Fl_Canvas* o, void* v); | |||
| static void cb_Connection(Fl_Canvas* o, void* v); | |||
| inline void cb_Unconnect_i(Fl_Canvas* o, void* v); | |||
| @@ -168,18 +187,27 @@ private: | |||
| static void cb_GroupTab(Fl_Tabs* o, void* v); | |||
| inline void cb_Rload_i(Fl_Button* o, void* v); | |||
| static void cb_Rload(Fl_Button* o, void* v); | |||
| static void cb_Rload(Fl_Button* o, void* v); | |||
| static void cb_Update(void* o, bool Mode); | |||
| static void cb_Blocking(void* o, bool Mode); | |||
| static void cb_Update(void* o, bool Mode); | |||
| static void cb_Blocking(void* o, bool Mode); | |||
| static void cb_UpdatePluginInfo(int ID, void *PluginInfo); | |||
| DeviceGroup m_Copied; | |||
| inline void cb_CutDeviceGroup_i(); | |||
| inline void cb_CopyDeviceGroup_i(); | |||
| inline void cb_PasteDeviceGroup_i(); | |||
| static void cb_CutDeviceGroup(Fl_Canvas* o, SynthModular*v) { v->cb_CutDeviceGroup_i(); }; | |||
| static void cb_CopyDeviceGroup(Fl_Canvas* o, SynthModular*v) { v->cb_CopyDeviceGroup_i(); }; | |||
| static void cb_PasteDeviceGroup(Fl_Canvas* o, SynthModular*v) { v->cb_PasteDeviceGroup_i(); }; | |||
| friend istream &operator>>(istream &s, SynthModular &o); | |||
| friend ostream &operator<<(ostream &s, SynthModular &o); | |||
| }; | |||
| istream &operator>>(istream &s, SynthModular &o); | |||
| iostream &operator>>(iostream &s, SynthModular &o); | |||
| ostream &operator<<(ostream &s, SynthModular &o); | |||
| #endif | |||