diff --git a/GUI/Widgets/Fl_Canvas.C b/GUI/Widgets/Fl_Canvas.C index df7e75b..1668499 100644 --- a/GUI/Widgets/Fl_Canvas.C +++ b/GUI/Widgets/Fl_Canvas.C @@ -17,6 +17,7 @@ */ #include "FL/fl_draw.H" +#include #include "Fl_Canvas.h" #include "Fl_DeviceGUI.h" #include @@ -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::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::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; iGetID()); + ((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; iposition(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; itype(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 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<0) + for(vector::iterator i=m_WireVec.begin(); + i!=m_WireVec.end(); i++) + { + std::vector::iterator output = std::find( m_Selection.m_DeviceIds.begin(), m_Selection.m_DeviceIds.end(), i->OutputID ); + std::vector::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<OutputID<<" "; + s<<0<<" "; + s<OutputPort<<" "; + s<OutputTerminal<<" "; + s<InputID<<" "; + s<<0<<" "; + s<InputPort<<" "; + s<InputTerminal<= 1) + { + s.seekp(curpos, ios::beg); + s<::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>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::iterator inputID = MapNewDeviceIds.find( NewWire.InputID); + std::map::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>(istream &s, Fl_Canvas &o) s>>dummy; s>>NewWire.InputPort; + if (paste || merge) + { + std::map::iterator inputID = MapNewDeviceIds.find( NewWire.InputID); + std::map::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; } diff --git a/GUI/Widgets/Fl_Canvas.h b/GUI/Widgets/Fl_Canvas.h index c86cbb1..750eae4 100644 --- a/GUI/Widgets/Fl_Canvas.h +++ b/GUI/Widgets/Fl_Canvas.h @@ -19,9 +19,11 @@ #include #include #include +#include #include #include #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 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 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 MapNewDeviceIds; + vector 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::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); diff --git a/GUI/Widgets/Fl_DeviceGUI.C b/GUI/Widgets/Fl_DeviceGUI.C index 3096ac0..4330078 100644 --- a/GUI/Widgets/Fl_DeviceGUI.C +++ b/GUI/Widgets/Fl_DeviceGUI.C @@ -141,6 +141,7 @@ int Fl_DeviceGUI::handle (int event) { Minimise(); if (m_IconButton) m_IconButton->show(); } + return 1; } diff --git a/GUI/Widgets/Fl_DeviceGUI.h b/GUI/Widgets/Fl_DeviceGUI.h index 5063352..eb0ac7e 100644 --- a/GUI/Widgets/Fl_DeviceGUI.h +++ b/GUI/Widgets/Fl_DeviceGUI.h @@ -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; diff --git a/GUI/Widgets/Fl_DragBar.H b/GUI/Widgets/Fl_DragBar.H index 6a71588..24d1cfa 100644 --- a/GUI/Widgets/Fl_DragBar.H +++ b/GUI/Widgets/Fl_DragBar.H @@ -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); diff --git a/GUI/Widgets/Fl_DragBar.cxx b/GUI/Widgets/Fl_DragBar.cxx index ea84825..82cabfa 100644 --- a/GUI/Widgets/Fl_DragBar.cxx +++ b/GUI/Widgets/Fl_DragBar.cxx @@ -3,7 +3,11 @@ #include "Fl_DragBar.H" #include -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 diff --git a/GUI/Widgets/Fl_DragBar.fld b/GUI/Widgets/Fl_DragBar.fld index c008883..34bb599 100644 --- a/GUI/Widgets/Fl_DragBar.fld +++ b/GUI/Widgets/Fl_DragBar.fld @@ -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 diff --git a/SpiralSound/Plugins/PoshSamplerPlugin/PoshSamplerPlugin.C b/SpiralSound/Plugins/PoshSamplerPlugin/PoshSamplerPlugin.C index 32111e7..8f9ede1 100644 --- a/SpiralSound/Plugins/PoshSamplerPlugin/PoshSamplerPlugin.C +++ b/SpiralSound/Plugins/PoshSamplerPlugin/PoshSamplerPlugin.C @@ -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; nPathname = temp; } diff --git a/SpiralSound/Plugins/PoshSamplerPlugin/PoshSamplerPlugin.h b/SpiralSound/Plugins/PoshSamplerPlugin/PoshSamplerPlugin.h index 2e1d1da..eaa8045 100644 --- a/SpiralSound/Plugins/PoshSamplerPlugin/PoshSamplerPlugin.h +++ b/SpiralSound/Plugins/PoshSamplerPlugin/PoshSamplerPlugin.h @@ -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, diff --git a/SpiralSound/Plugins/SpiralPlugin.h b/SpiralSound/Plugins/SpiralPlugin.h index dffc8f8..1a6cffb 100644 --- a/SpiralSound/Plugins/SpiralPlugin.h +++ b/SpiralSound/Plugins/SpiralPlugin.h @@ -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); diff --git a/SpiralSynthModular.C b/SpiralSynthModular.C index 9c9d240..6494fd3 100644 --- a/SpiralSynthModular.C +++ b/SpiralSynthModular.C @@ -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; iSelection().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<>MainWinX>>MainWinY>>MainWinW>>MainWinH; - s>>EditWinX>>EditWinY>>EditWinW>>EditWinH; + for (unsigned int i=0; iSelection().m_DeviceIds.size(); i++) + { + int ID = m_Canvas->Selection().m_DeviceIds[i]; + std::map::iterator i = m_DeviceWinMap.find(ID); + + m_Copied.m_DeviceIds[ID] = ID; + + m_Copied.devicecount += 1; + + m_Copied.devices<<"Device "; + m_Copied.devices<first<<" "; // save the id + m_Copied.devices<<"Plugin "; + m_Copied.devices<second->m_PluginID<second->m_DeviceGUI->x()<<" "; + m_Copied.devices<second->m_DeviceGUI->y()<<" "; + m_Copied.devices<second->m_DeviceGUI->GetName().size()<<" "; + m_Copied.devices<second->m_DeviceGUI->GetName()<<" "; + + if (i->second->m_DeviceGUI->GetPluginWindow()) + { + m_Copied.devices<second->m_DeviceGUI->GetPluginWindow()->visible()<<" "; + m_Copied.devices<second->m_DeviceGUI->GetPluginWindow()->x()<<" "; + m_Copied.devices<second->m_DeviceGUI->GetPluginWindow()->y()<<" "; + } + else + { + m_Copied.devices<<0<<" "<<0<<" "<<0; + } + m_Copied.devices<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<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>(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<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()); + } } ////////////////////////////////////////////////////////// diff --git a/SpiralSynthModular.h b/SpiralSynthModular.h index f4b6649..1957685 100644 --- a/SpiralSynthModular.h +++ b/SpiralSynthModular.h @@ -32,6 +32,7 @@ #include #include #include +#include #include #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 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 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