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_draw.H" | ||||
#include <FL/Fl_Scroll.H> | |||||
#include "Fl_Canvas.h" | #include "Fl_Canvas.h" | ||||
#include "Fl_DeviceGUI.h" | #include "Fl_DeviceGUI.h" | ||||
#include <iostream> | #include <iostream> | ||||
@@ -34,7 +35,8 @@ cb_Connection(NULL), | |||||
cb_Unconnect(NULL), | cb_Unconnect(NULL), | ||||
cb_AddDevice(NULL), | cb_AddDevice(NULL), | ||||
m_ToolMenu(false), | m_ToolMenu(false), | ||||
m_UpdateTimer(0) | |||||
m_UpdateTimer(0), | |||||
m_Selecting(false) | |||||
{ | { | ||||
m_IncompleteWire.OutputID=-1; | m_IncompleteWire.OutputID=-1; | ||||
m_IncompleteWire.OutputPort=-1; | m_IncompleteWire.OutputPort=-1; | ||||
@@ -45,12 +47,15 @@ m_UpdateTimer(0) | |||||
m_BG=NULL; | m_BG=NULL; | ||||
m_BGData=NULL; | m_BGData=NULL; | ||||
m_Menu=NULL; | |||||
m_CanPaste=false; | |||||
} | } | ||||
//////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////// | ||||
Fl_Canvas::~Fl_Canvas() | Fl_Canvas::~Fl_Canvas() | ||||
{ | { | ||||
if (m_Menu) delete m_Menu; | |||||
} | } | ||||
//////////////////////////////////////////////////////////////////////// | //////////////////////////////////////////////////////////////////////// | ||||
@@ -58,7 +63,7 @@ Fl_Canvas::~Fl_Canvas() | |||||
void Fl_Canvas::draw() | void Fl_Canvas::draw() | ||||
{ | { | ||||
Fl_Widget*const* a = array(); | Fl_Widget*const* a = array(); | ||||
if (damage() & ~FL_DAMAGE_CHILD) // redraw the entire thing: | if (damage() & ~FL_DAMAGE_CHILD) // redraw the entire thing: | ||||
{ | { | ||||
if (m_BG) | if (m_BG) | ||||
@@ -96,6 +101,14 @@ void Fl_Canvas::draw() | |||||
{ | { | ||||
draw_child(o); | draw_child(o); | ||||
draw_outside_label(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_child(o); | ||||
draw_outside_label(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; | 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() | void Fl_Canvas::DrawWires() | ||||
{ | { | ||||
for(vector<CanvasWire>::iterator i=m_WireVec.begin(); | for(vector<CanvasWire>::iterator i=m_WireVec.begin(); | ||||
@@ -304,7 +584,7 @@ void Fl_Canvas::ClearIncompleteWire() | |||||
int Fl_Canvas::handle(int event) | int Fl_Canvas::handle(int event) | ||||
{ | { | ||||
if (Fl_Group::handle(event)) return 1; | if (Fl_Group::handle(event)) return 1; | ||||
if (event==FL_PUSH) | if (event==FL_PUSH) | ||||
{ | { | ||||
ClearIncompleteWire(); | ClearIncompleteWire(); | ||||
@@ -313,12 +593,84 @@ int Fl_Canvas::handle(int event) | |||||
m_DragY=Fl::event_y(); | 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) | 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; | return 1; | ||||
} | } | ||||
@@ -448,7 +805,7 @@ void Fl_Canvas::PortClicked(Fl_DeviceGUI* Device, int Type, int Port, bool Value | |||||
redraw(); | redraw(); | ||||
// Clear the current selection | |||||
// Clear the current m_Selection | |||||
m_IncompleteWire.Clear(); | 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; | int NumWires; | ||||
s>>NumWires; | s>>NumWires; | ||||
// my bad, didn't version this stream - remove one day... | // my bad, didn't version this stream - remove one day... | ||||
if (NumWires==-1) | |||||
if (paste || NumWires==-1) | |||||
{ | { | ||||
int version; | int version; | ||||
s>>version; | |||||
s>>NumWires; | |||||
if (!paste) | |||||
{ | |||||
s>>version; | |||||
s>>NumWires; | |||||
} | |||||
for(int n=0; n<NumWires; n++) | for(int n=0; n<NumWires; n++) | ||||
{ | { | ||||
CanvasWire NewWire; | CanvasWire NewWire; | ||||
int dummy; | int dummy; | ||||
s>>NewWire.OutputID; | s>>NewWire.OutputID; | ||||
s>>dummy; | s>>dummy; | ||||
s>>NewWire.OutputPort; | s>>NewWire.OutputPort; | ||||
@@ -629,21 +991,30 @@ istream &operator>>(istream &s, Fl_Canvas &o) | |||||
s>>dummy; | s>>dummy; | ||||
s>>NewWire.InputPort; | s>>NewWire.InputPort; | ||||
s>>NewWire.InputTerminal; | 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 | else | ||||
{ | { | ||||
for(int n=0; n<NumWires; n++) | for(int n=0; n<NumWires; n++) | ||||
@@ -658,19 +1029,36 @@ istream &operator>>(istream &s, Fl_Canvas &o) | |||||
s>>dummy; | s>>dummy; | ||||
s>>NewWire.InputPort; | 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 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 | // 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; | return s; | ||||
} | } | ||||
@@ -19,9 +19,11 @@ | |||||
#include <FL/Fl_Group.h> | #include <FL/Fl_Group.h> | ||||
#include <FL/Fl_Output.h> | #include <FL/Fl_Output.h> | ||||
#include <FL/Fl_Image.h> | #include <FL/Fl_Image.h> | ||||
#include <FL/Fl_Menu_Button.h> | |||||
#include <vector> | #include <vector> | ||||
#include <string> | #include <string> | ||||
#include "../../GraphSort.h" | #include "../../GraphSort.h" | ||||
#include "Fl_DeviceGUI.h" | |||||
#ifndef CANVAS_WIDGET | #ifndef CANVAS_WIDGET | ||||
#define CANVAS_WIDGET | #define CANVAS_WIDGET | ||||
@@ -55,14 +57,30 @@ public: | |||||
bool DelMe; | bool DelMe; | ||||
}; | }; | ||||
class CanvasGroup | |||||
{ | |||||
public: | |||||
CanvasGroup() { Clear(); } | |||||
void Clear() | |||||
{ | |||||
m_DeviceIds.clear(); | |||||
} | |||||
vector<int> m_DeviceIds; | |||||
}; | |||||
class Fl_Canvas : public Fl_Group | class Fl_Canvas : public Fl_Group | ||||
{ | { | ||||
public: | public: | ||||
Fl_Canvas(int x, int y, int w, int h, char *name); | Fl_Canvas(int x, int y, int w, int h, char *name); | ||||
~Fl_Canvas(); | ~Fl_Canvas(); | ||||
Fl_Menu_Button *m_Menu; | |||||
virtual void draw(); | virtual void draw(); | ||||
virtual int handle(int event); | virtual int handle(int event); | ||||
int globalhandle(int event); | |||||
void PortClicked(Fl_DeviceGUI* Device, int Type, int Port, bool Value); | void PortClicked(Fl_DeviceGUI* Device, int Type, int Port, bool Value); | ||||
void Rename(Fl_DeviceGUI* Device); | void Rename(Fl_DeviceGUI* Device); | ||||
@@ -71,6 +89,12 @@ public: | |||||
void SetAddDeviceCallback(Fl_Callback* s) { cb_AddDevice=s; } | void SetAddDeviceCallback(Fl_Callback* s) { cb_AddDevice=s; } | ||||
void SetRenameCallback(Fl_Callback* s) { cb_Rename=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 ClearConnections(Fl_DeviceGUI* Device); | ||||
void RemoveDevice(Fl_DeviceGUI* Device); | void RemoveDevice(Fl_DeviceGUI* Device); | ||||
void Clear(); | void Clear(); | ||||
@@ -83,7 +107,48 @@ public: | |||||
void ToTop(Fl_DeviceGUI *o); | void ToTop(Fl_DeviceGUI *o); | ||||
void ToBot(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: | private: | ||||
void DrawSelection(); | |||||
void CalculateSelection(); | |||||
void DrawWires(); | void DrawWires(); | ||||
void ClearIncompleteWire(); | void ClearIncompleteWire(); | ||||
@@ -99,6 +164,13 @@ private: | |||||
void (*cb_AddDevice)(Fl_Widget*, void*); | void (*cb_AddDevice)(Fl_Widget*, void*); | ||||
void (*cb_Rename)(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; | vector<CanvasWire> m_WireVec; | ||||
CanvasWire m_IncompleteWire; | CanvasWire m_IncompleteWire; | ||||
@@ -109,9 +181,60 @@ private: | |||||
GraphSort m_Graph; | GraphSort m_Graph; | ||||
int m_UpdateTimer; | int m_UpdateTimer; | ||||
int m_DragX,m_DragY; | 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 istream &operator>>(istream &s, Fl_Canvas &o); | ||||
friend ostream &operator<<(ostream &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); | istream &operator>>(istream &s, Fl_Canvas &o); | ||||
@@ -141,6 +141,7 @@ int Fl_DeviceGUI::handle (int event) { | |||||
Minimise(); | Minimise(); | ||||
if (m_IconButton) m_IconButton->show(); | if (m_IconButton) m_IconButton->show(); | ||||
} | } | ||||
return 1; | return 1; | ||||
} | } | ||||
@@ -104,6 +104,10 @@ class Fl_DeviceGUI : public Fl_Group { | |||||
int GetPortType (int n) { return m_Info.PortTypes[n]; } | int GetPortType (int n) { return m_Info.PortTypes[n]; } | ||||
// do we belong to a plugin that is an output? | // do we belong to a plugin that is an output? | ||||
bool IsTerminal() { return m_IsTerminal; } | 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: | protected: | ||||
DeviceGUIInfo m_Info; | DeviceGUIInfo m_Info; | ||||
Fl_DragBar* m_DragBar; | Fl_DragBar* m_DragBar; | ||||
@@ -11,9 +11,16 @@ class Fl_DragBar : public Fl_Widget { | |||||
int old_rx,old_ry; | int old_rx,old_ry; | ||||
int _type; | int _type; | ||||
public: | public: | ||||
enum DragType {WINDRAG=0,NICEWINDRAG,FLDRAG,NICEFLDRAG}; | |||||
Fl_DragBar(int x,int y,int w,int h,const char *l=0); | Fl_DragBar(int x,int y,int w,int h,const char *l=0); | ||||
~Fl_DragBar(); | ~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: | private: | ||||
void draw(); | void draw(); | ||||
int handle(int event); | int handle(int event); | ||||
@@ -3,7 +3,11 @@ | |||||
#include "Fl_DragBar.H" | #include "Fl_DragBar.H" | ||||
#include <string.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; | _type = Fl_DragBar::NICEFLDRAG; | ||||
} | } | ||||
@@ -119,6 +123,12 @@ int mx,my; | |||||
{ | { | ||||
fl_cursor(FL_CURSOR_DEFAULT); | fl_cursor(FL_CURSOR_DEFAULT); | ||||
do_callback(); | do_callback(); | ||||
if ((Fl::event_is_click()) && (cb_OnClick)) | |||||
{ | |||||
cb_OnClick(this, Fl::event_button(), Fl::event_state(), cb_OnClick_Data); | |||||
} | |||||
return 1; | return 1; | ||||
} | } | ||||
case FL_DRAG: | case FL_DRAG: | ||||
@@ -129,11 +139,30 @@ int mx,my; | |||||
yy = ry - old_ry; | yy = ry - old_ry; | ||||
if (_type < (int)Fl_DragBar::FLDRAG) | 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); | window()->position(xx,yy); | ||||
} | } | ||||
else | 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); | parent()->position(xx,yy); | ||||
if (parent()->parent()) | if (parent()->parent()) | ||||
parent()->parent()->redraw(); | parent()->parent()->redraw(); | ||||
else | else | ||||
@@ -18,9 +18,16 @@ class Fl_DragBar {open : {public Fl_Widget} | |||||
} { | } { | ||||
decl {int old_rx,old_ry;} {} | decl {int old_rx,old_ry;} {} | ||||
decl {int _type;} {} | 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 | 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;} {} | code {_type = Fl_DragBar::NICEFLDRAG;} {} | ||||
} | } | ||||
@@ -133,6 +140,10 @@ int mx,my; | |||||
{ | { | ||||
fl_cursor(FL_CURSOR_DEFAULT); | fl_cursor(FL_CURSOR_DEFAULT); | ||||
do_callback(); | do_callback(); | ||||
if (((Fl::event_is_click()) && (cb_OnClick)) | |||||
{ | |||||
cb_OnClick(this, cb_OnClick_Data); | |||||
} | |||||
return 1; | return 1; | ||||
} | } | ||||
case FL_DRAG: | case FL_DRAG: | ||||
@@ -141,13 +152,33 @@ int mx,my; | |||||
{ | { | ||||
xx = rx - old_rx; | xx = rx - old_rx; | ||||
yy = ry - old_ry; | yy = ry - old_ry; | ||||
if (_type < (int)Fl_DragBar::FLDRAG) | 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); | window()->position(xx,yy); | ||||
} | } | ||||
else | 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); | parent()->position(xx,yy); | ||||
if (parent()->parent()) | if (parent()->parent()) | ||||
parent()->parent()->redraw(); | parent()->parent()->redraw(); | ||||
else | else | ||||
@@ -491,14 +491,14 @@ bool PoshSamplerPlugin::SaveExternalFiles(const string &Dir) | |||||
return true; | return true; | ||||
} | } | ||||
void PoshSamplerPlugin::LoadExternalFiles(const string &Dir) | |||||
void PoshSamplerPlugin::LoadExternalFiles(const string &Dir, int withID) | |||||
{ | { | ||||
for (int n=0; n<NUM_SAMPLES; n++) | for (int n=0; n<NUM_SAMPLES; n++) | ||||
{ | { | ||||
char temp[256]; | char temp[256]; | ||||
// Andy's fix for bug 766594 | // Andy's fix for bug 766594 | ||||
// sprintf (temp, "PoshSampler%d_%d.wav", SpiralPlugin_GetID(), n); | // 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; | m_SampleDescVec[n]->Pathname = temp; | ||||
} | } | ||||
@@ -59,7 +59,7 @@ public: | |||||
virtual void StreamIn(std::istream &s); | virtual void StreamIn(std::istream &s); | ||||
virtual bool SaveExternalFiles(const std::string &Dir); | 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, | enum GUICommands{NONE,LOAD,SAVE,SETVOL,SETPITCH,SETLOOP,SETPING,SETNOTE,SETOCT, | ||||
SETPLAYPOINTS,SETREC,CUT,COPY,PASTE,CROP,MIX,REV,AMP,SETCURRENT, | SETPLAYPOINTS,SETREC,CUT,COPY,PASTE,CROP,MIX,REV,AMP,SETCURRENT, | ||||
@@ -91,7 +91,7 @@ public: | |||||
// stuff here gets saved in filename_files directory | // stuff here gets saved in filename_files directory | ||||
// you must return true if this feature is used. | // you must return true if this feature is used. | ||||
virtual bool SaveExternalFiles(const std::string &Dir) { return false; } | 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; } | const HostInfo* GetHostInfo() { return m_HostInfo; } | ||||
bool GetOutput(unsigned int n, Sample **s); | bool GetOutput(unsigned int n, Sample **s); | ||||
@@ -104,6 +104,7 @@ SynthModular::~SynthModular() | |||||
{ | { | ||||
ClearUp(); | ClearUp(); | ||||
PluginManager::Get()->PackUpAndGoHome(); | PluginManager::Get()->PackUpAndGoHome(); | ||||
system("rm -f ___temp.ssmcopytmp"); | |||||
} | } | ||||
////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////// | ||||
@@ -371,6 +372,13 @@ SpiralWindowType *SynthModular::CreateWindow() | |||||
m_Canvas->SetConnectionCallback((Fl_Callback*)cb_Connection); | m_Canvas->SetConnectionCallback((Fl_Callback*)cb_Connection); | ||||
m_Canvas->SetUnconnectCallback((Fl_Callback*)cb_Unconnect); | m_Canvas->SetUnconnectCallback((Fl_Callback*)cb_Unconnect); | ||||
m_Canvas->SetAddDeviceCallback((Fl_Callback*)cb_NewDeviceFromMenu); | 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_CanvasScroll->add(m_Canvas); | ||||
m_SettingsWindow = new SettingsWindow; | m_SettingsWindow = new SettingsWindow; | ||||
@@ -629,6 +637,7 @@ DeviceWin* SynthModular::NewDeviceWin(int n, int x, int y) | |||||
Info.YPos = y; //rand()%400; | Info.YPos = y; //rand()%400; | ||||
nlw->m_DeviceGUI = new Fl_DeviceGUI(Info, temp, Pix, nlw->m_Device->IsTerminal()); | 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->add(nlw->m_DeviceGUI); | ||||
m_Canvas->redraw(); | 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); | 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->add(nlw->m_DeviceGUI); | ||||
m_Canvas->redraw(); | 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++) | for(int n=0; n<Num; n++) | ||||
{ | { | ||||
#ifdef DEBUG_STREAM | #ifdef DEBUG_STREAM | ||||
@@ -798,7 +974,7 @@ istream &operator>>(istream &s, SynthModular &o) | |||||
string Name; | string Name; | ||||
if (ver>3) | |||||
if (paste || ver>3) | |||||
{ | { | ||||
// load the device name | // load the device name | ||||
int size; | int size; | ||||
@@ -811,75 +987,107 @@ istream &operator>>(istream &s, SynthModular &o) | |||||
} else { | } else { | ||||
Name = ""; | Name = ""; | ||||
} | } | ||||
} | |||||
} | |||||
#ifdef DEBUG_STREAM | #ifdef DEBUG_STREAM | ||||
cerr<<dummy<<" "<<ID<<" "<<dummy2<<" "<<PluginID<<" "<<x<<" "<<y<<endl; | cerr<<dummy<<" "<<ID<<" "<<dummy2<<" "<<PluginID<<" "<<x<<" "<<y<<endl; | ||||
#endif | #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) | if (PluginID==COMMENT_ID) | ||||
{ | { | ||||
DeviceWin* temp = o.NewComment(PluginID, x, y); | |||||
DeviceWin* temp = NewComment(PluginID, x, y); | |||||
if (temp) | if (temp) | ||||
{ | { | ||||
if (paste || merge) | |||||
{ | |||||
m_Copied.m_DeviceIds[ID] = m_NextID++; | |||||
ID = m_Copied.m_DeviceIds[ID]; | |||||
} | |||||
temp->m_DeviceGUI->SetID(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 | else | ||||
{ | { | ||||
DeviceWin* temp = o.NewDeviceWin(PluginID, x, y); | |||||
DeviceWin* temp = NewDeviceWin(PluginID, x, y); | |||||
if (temp) | 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); | temp->m_DeviceGUI->SetID(ID); | ||||
if (ver>3) | |||||
if (paste || ver>3) | |||||
{ | { | ||||
// set the titlebars | // set the titlebars | ||||
temp->m_DeviceGUI->SetName(Name); | 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 | // 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 | // set the GUI up with the loaded values | ||||
// looks messy, but if we do it here, the plugin and it's gui can remain | // looks messy, but if we do it here, the plugin and it's gui can remain | ||||
// totally seperated. | // 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 | // updates the data in the channel buffers, so the values don't | ||||
// get overwritten in the next tick. (should maybe be somewhere else) | // 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 | // 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) | if (ps) | ||||
{ | { | ||||
o.m_DeviceWinMap[ID]->m_DeviceGUI->Maximise(); | |||||
m_DeviceWinMap[ID]->m_DeviceGUI->Maximise(); | |||||
// reposition after 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 | 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; | return s; | ||||
} | } | ||||
iostream &operator>>(iostream &s, SynthModular &o) | |||||
{ | |||||
return o.StreamPatchIn(s, false, false); | |||||
} | |||||
////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////// | ||||
ostream &operator<<(ostream &s, SynthModular &o) | 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') | if (fn && fn!='\0') | ||||
{ | { | ||||
ifstream inf(fn); | |||||
ifstream in(fn); | |||||
if (inf) | |||||
if (in) | |||||
{ | { | ||||
fstream inf; | |||||
inf.open(fn,ios::in); | |||||
m_FilePath=fn; | m_FilePath=fn; | ||||
ClearUp(); | ClearUp(); | ||||
inf>>*this; | inf>>*this; | ||||
inf.close(); | |||||
TITLEBAR=LABEL+" "+fn; | TITLEBAR=LABEL+" "+fn; | ||||
m_TopWindow->label(TITLEBAR.c_str()); | m_TopWindow->label(TITLEBAR.c_str()); | ||||
} | } | ||||
@@ -1232,18 +1454,23 @@ void SynthModular::cb_UpdatePluginInfo(int ID, void *PInfo) | |||||
void SynthModular::LoadPatch(const char *fn) | 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(); | 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 <FL/Fl_Tabs.H> | ||||
#include <sstream> | #include <sstream> | ||||
#include <iostream> | #include <iostream> | ||||
#include <fstream> | |||||
#include <map> | #include <map> | ||||
#include "GUI/Widgets/Fl_DeviceGUI.h" | #include "GUI/Widgets/Fl_DeviceGUI.h" | ||||
#include "GUI/Widgets/Fl_CommentGUI.h" | #include "GUI/Widgets/Fl_CommentGUI.h" | ||||
@@ -66,6 +67,16 @@ public: | |||||
virtual void draw() { draw_label(); } | 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 | class SynthModular | ||||
{ | { | ||||
public: | public: | ||||
@@ -98,6 +109,8 @@ public: | |||||
// only for audio thread | // only for audio thread | ||||
bool IsPaused() { return m_PauseAudio; } | bool IsPaused() { return m_PauseAudio; } | ||||
iostream &StreamPatchIn(iostream &s, bool paste, bool merge); | |||||
private: | private: | ||||
vector<string> BuildPluginList(const string &Path); | vector<string> BuildPluginList(const string &Path); | ||||
@@ -118,7 +131,8 @@ private: | |||||
static bool m_CallbackUpdateMode; | static bool m_CallbackUpdateMode; | ||||
static bool m_BlockingOutputPluginIsReady; | static bool m_BlockingOutputPluginIsReady; | ||||
string m_FilePath; | string m_FilePath; | ||||
string m_MergeFilePath; | |||||
// Main GUI stuff | // Main GUI stuff | ||||
void CreateGUI(int xoff=0, int yoff=0, char *name=""); | void CreateGUI(int xoff=0, int yoff=0, char *name=""); | ||||
@@ -146,17 +160,22 @@ private: | |||||
bool m_PauseAudio; | bool m_PauseAudio; | ||||
inline void cb_NewDevice_i(Fl_Button* o, void* v); | 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); | inline void cb_NewDeviceFromMenu_i(Fl_Canvas* o, void* v); | ||||
static void cb_NewDeviceFromMenu(Fl_Canvas* o, void* v); | static void cb_NewDeviceFromMenu(Fl_Canvas* o, void* v); | ||||
inline void cb_NewComment_i(Fl_Button* 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); | 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); | 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); | 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); | inline void cb_Connection_i(Fl_Canvas* o, void* v); | ||||
static void cb_Connection(Fl_Canvas* o, void* v); | static void cb_Connection(Fl_Canvas* o, void* v); | ||||
inline void cb_Unconnect_i(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); | static void cb_GroupTab(Fl_Tabs* o, void* v); | ||||
inline void cb_Rload_i(Fl_Button* 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); | 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 istream &operator>>(istream &s, SynthModular &o); | ||||
friend ostream &operator<<(ostream &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); | ostream &operator<<(ostream &s, SynthModular &o); | ||||
#endif | #endif |