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