You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

676 lines
16KB

  1. /* Canvas Widget
  2. * Copyleft (C) 2002 David Griffiths <dave@pawfal.org>
  3. *
  4. * This program is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  17. */
  18. #include "Fl/fl_draw.H"
  19. #include "Fl_Canvas.h"
  20. #include "Fl_DeviceGUI.h"
  21. #include <iostream>
  22. #include "../../SpiralSynthModularInfo.h"
  23. // no of calls to handle when dragged, before the widget is redrawn
  24. // to allow the wire (connection currently being made) to be redrawn
  25. static const int UPDATE_TICKS = 5;
  26. ////////////////////////////////////////////////////////////////////////
  27. Fl_Canvas::Fl_Canvas(int x, int y, int w, int h, char *name) :
  28. Fl_Group(x,y,w,h,name),
  29. cb_Connection(NULL),
  30. cb_Unconnect(NULL),
  31. cb_AddDevice(NULL),
  32. m_ToolMenu(false),
  33. m_UpdateTimer(0)
  34. {
  35. m_IncompleteWire.OutputChild=-1;
  36. m_IncompleteWire.OutputPort=-1;
  37. m_IncompleteWire.OutputTerminal=false;
  38. m_IncompleteWire.InputChild=-1;
  39. m_IncompleteWire.InputPort=-1;
  40. m_IncompleteWire.InputTerminal=false;
  41. m_BG=NULL;
  42. m_BGData=NULL;
  43. }
  44. ////////////////////////////////////////////////////////////////////////
  45. Fl_Canvas::~Fl_Canvas()
  46. {
  47. }
  48. ////////////////////////////////////////////////////////////////////////
  49. void Fl_Canvas::draw()
  50. {
  51. Fl_Widget*const* a = array();
  52. if (damage() & ~FL_DAMAGE_CHILD) // redraw the entire thing:
  53. {
  54. if (m_BG)
  55. {
  56. int X=0,Y=0;
  57. while (Y<w())
  58. {
  59. while (X<h())
  60. {
  61. m_BG->draw(parent()->x()+X,parent()->y()+Y);
  62. #if FL_MAJOR_VERSION == 1 && FL_MINOR_VERSION == 0
  63. X+=m_BG->w;
  64. #else
  65. X+=m_BG->w();
  66. #endif
  67. }
  68. #if FL_MAJOR_VERSION == 1 && FL_MINOR_VERSION == 0
  69. Y+=m_BG->h;
  70. #else
  71. Y+=m_BG->h();
  72. #endif
  73. X=0;
  74. }
  75. }
  76. else
  77. {
  78. draw_box();
  79. }
  80. // draw minimised modules first
  81. for (int i=children(); i--;)
  82. {
  83. Fl_Widget& o = **a++;
  84. if (((Fl_DeviceGUI*)&o)->IsMinimised())
  85. {
  86. draw_child(o);
  87. draw_outside_label(o);
  88. }
  89. }
  90. DrawWires();
  91. // draw maximised modules on top of everything else
  92. Fl_Widget*const* a = array();
  93. for (int i=children(); i--;)
  94. {
  95. Fl_Widget& o = **a++;
  96. if (!((Fl_DeviceGUI*)&o)->IsMinimised())
  97. {
  98. draw_child(o);
  99. draw_outside_label(o);
  100. }
  101. }
  102. }
  103. else // only redraw the children that need it:
  104. {
  105. for (int i=children(); i--;) update_child(**a++);
  106. }
  107. if (m_ToolMenu)
  108. {
  109. int Pos=0,X=0,Y=0,textw,texth;
  110. int DegreesPerItem=30;
  111. float conv=3.151/180.0f;
  112. bool selected=false;
  113. m_Selected=-1;
  114. fl_font(fl_font(),10);
  115. for (vector< pair<string,int> >::iterator i=m_PluginNameList.begin();
  116. i!=m_PluginNameList.end(); ++i)
  117. {
  118. textw=0;
  119. fl_font(fl_font(),10);
  120. fl_measure(i->first.c_str(), textw, texth);
  121. X=m_x-(textw/2);
  122. Y=m_y-(m_PluginNameList.size()*5)+Pos*10;
  123. if (Fl::event_y()>Y-10 && Fl::event_y()<Y &&
  124. Fl::event_x()>X && Fl::event_x()<X+textw)
  125. {
  126. fl_font(fl_font(),15);
  127. fl_measure(i->first.c_str(), textw, texth);
  128. X=m_x-(textw/2);
  129. m_Selected=i->second;
  130. selected=true;
  131. }
  132. else selected=false;
  133. fl_color(FL_GRAY);
  134. fl_color(FL_WHITE);
  135. fl_draw(i->first.c_str(),X-1,Y+1);
  136. if (selected) fl_color(FL_BLUE);
  137. else fl_color(FL_BLACK);
  138. fl_draw(i->first.c_str(),X,Y);
  139. Pos+=1;
  140. }
  141. }
  142. }
  143. ////////////////////////////////////////////////////////////////////////
  144. void Fl_Canvas::Poll()
  145. {
  146. // bit of a workaround...
  147. if (UserMakingConnection()) m_UpdateTimer++;
  148. if (m_UpdateTimer>UPDATE_TICKS)
  149. {
  150. m_UpdateTimer=0;
  151. redraw();
  152. }
  153. }
  154. ////////////////////////////////////////////////////////////////////////
  155. void Fl_Canvas::DrawWires()
  156. {
  157. for(vector<CanvasWire>::iterator i=m_WireVec.begin();
  158. i!=m_WireVec.end(); i++)
  159. {
  160. if (i->OutputChild>children() || i->InputChild>children())
  161. {
  162. cerr<<"wire output child = "<<i->OutputChild<<endl;
  163. cerr<<"wire input child = "<<i->InputChild<<endl;
  164. SpiralInfo::Alert("Wire drawing mismatch!");
  165. return;
  166. }
  167. Fl_DeviceGUI* SourceDevice = (Fl_DeviceGUI*)(child(i->OutputChild));
  168. Fl_DeviceGUI* DestDevice = (Fl_DeviceGUI*)(child(i->InputChild));
  169. if (!SourceDevice || !DestDevice)
  170. {
  171. SpiralInfo::Alert("Cant find source or dest device while drawing wires");
  172. return;
  173. }
  174. Fl_Color col = (Fl_Color) WIRE_COL0;
  175. switch (SourceDevice->GetPortType(i->OutputPort+SourceDevice->GetInfo()->NumInputs)) {
  176. case 0: col = (Fl_Color) WIRE_COL0; break;
  177. case 1: col = (Fl_Color) WIRE_COL1; break;
  178. case 2: col = (Fl_Color) WIRE_COL2; break;
  179. case 3: col = (Fl_Color) WIRE_COL3; break;
  180. case 4: col = (Fl_Color) WIRE_COL4; break;
  181. default: col = (Fl_Color) WIRE_COL0;
  182. }
  183. fl_color(col);
  184. fl_line(SourceDevice->GetPortX(i->OutputPort+SourceDevice->GetInfo()->NumInputs),
  185. SourceDevice->GetPortY(i->OutputPort+SourceDevice->GetInfo()->NumInputs),
  186. DestDevice->GetPortX(i->InputPort),
  187. DestDevice->GetPortY(i->InputPort));
  188. }
  189. DrawIncompleteWire();
  190. }
  191. ////////////////////////////////////////////////////////////////////////
  192. bool Fl_Canvas::UserMakingConnection()
  193. {
  194. return (m_IncompleteWire.InputChild!=-1 || m_IncompleteWire.OutputChild!=-1);
  195. }
  196. ////////////////////////////////////////////////////////////////////////
  197. void Fl_Canvas::DrawIncompleteWire()
  198. {
  199. // draw the wire we are currently connecting
  200. if(m_IncompleteWire.InputChild!=-1)
  201. {
  202. Fl_DeviceGUI* Device = (Fl_DeviceGUI*)(child(m_IncompleteWire.InputChild));
  203. if (!Device)
  204. {
  205. SpiralInfo::Alert("Cant find source or dest device while drawing wires");
  206. return;
  207. }
  208. Fl_Color col = (Fl_Color) WIRE_COL0;
  209. switch (Device->GetPortType(m_IncompleteWire.InputPort)) {
  210. case 0: col = (Fl_Color) WIRE_COL0; break;
  211. case 1: col = (Fl_Color) WIRE_COL1; break;
  212. case 2: col = (Fl_Color) WIRE_COL2; break;
  213. case 3: col = (Fl_Color) WIRE_COL3; break;
  214. case 4: col = (Fl_Color) WIRE_COL4; break;
  215. default: col = (Fl_Color) WIRE_COL0;
  216. }
  217. fl_color(col);
  218. fl_line(Device->GetPortX(m_IncompleteWire.InputPort),
  219. Device->GetPortY(m_IncompleteWire.InputPort),
  220. Fl::event_x(),
  221. Fl::event_y());
  222. }
  223. if(m_IncompleteWire.OutputChild!=-1)
  224. {
  225. Fl_DeviceGUI* Device = (Fl_DeviceGUI*)(child(m_IncompleteWire.OutputChild));
  226. if (!Device)
  227. {
  228. SpiralInfo::Alert("Cant find source or dest device while drawing wires");
  229. return;
  230. }
  231. Fl_Color col = (Fl_Color) WIRE_COL0;
  232. switch (Device->GetPortType(m_IncompleteWire.OutputPort+Device->GetInfo()->NumInputs)) {
  233. case 0: col = (Fl_Color) WIRE_COL0; break;
  234. case 1: col = (Fl_Color) WIRE_COL1; break;
  235. case 2: col = (Fl_Color) WIRE_COL2; break;
  236. case 3: col = (Fl_Color) WIRE_COL3; break;
  237. case 4: col = (Fl_Color) WIRE_COL4; break;
  238. default: col = (Fl_Color) WIRE_COL0;
  239. }
  240. fl_color(col);
  241. fl_line(Device->GetPortX(m_IncompleteWire.OutputPort+Device->GetInfo()->NumInputs),
  242. Device->GetPortY(m_IncompleteWire.OutputPort+Device->GetInfo()->NumInputs),
  243. Fl::event_x(),
  244. Fl::event_y());
  245. }
  246. }
  247. ////////////////////////////////////////////////////////////////////////
  248. void Fl_Canvas::ClearIncompleteWire()
  249. {
  250. // Turn off both ports
  251. if (m_IncompleteWire.OutputChild!=-1)
  252. {
  253. ((Fl_DeviceGUI*)(child(m_IncompleteWire.OutputChild)))->RemoveConnection(m_IncompleteWire.OutputPort+
  254. ((Fl_DeviceGUI*)(child(m_IncompleteWire.OutputChild)))->GetInfo()->NumInputs);
  255. }
  256. if (m_IncompleteWire.InputChild!=-1)
  257. {
  258. ((Fl_DeviceGUI*)(child(m_IncompleteWire.InputChild)))->RemoveConnection(m_IncompleteWire.InputPort);
  259. }
  260. m_IncompleteWire.Clear();
  261. }
  262. ////////////////////////////////////////////////////////////////////////
  263. int Fl_Canvas::handle(int event)
  264. {
  265. if (Fl_Group::handle(event)) return 1;
  266. if (event==FL_PUSH)
  267. {
  268. ClearIncompleteWire();
  269. redraw();
  270. }
  271. if (Fl::event_button()==3)
  272. {
  273. if (event==FL_PUSH)
  274. {
  275. m_ToolMenu=true;
  276. m_x=Fl::event_x();
  277. m_y=Fl::event_y();
  278. redraw();
  279. }
  280. if (event==FL_DRAG) redraw();
  281. if (event==FL_RELEASE && Fl::event_button()==3)
  282. {
  283. m_ToolMenu=false;
  284. if (m_Selected!=-1 && cb_AddDevice)
  285. {
  286. int args[3];
  287. args[0]=m_Selected;
  288. args[1]=m_x;
  289. args[2]=m_y;
  290. cb_AddDevice(this,args);
  291. }
  292. redraw();
  293. }
  294. }
  295. return 1;
  296. }
  297. ////////////////////////////////////////////////////////////////////////
  298. void Fl_Canvas::PortClicked(Fl_DeviceGUI* Device, int Type, int Port, bool Value)
  299. {
  300. // find out which child this comes from.
  301. int ChildNum=-1;
  302. for(int n=0; n<children(); n++)
  303. {
  304. if(child(n)==Device)
  305. {
  306. ChildNum=n;
  307. }
  308. }
  309. if (ChildNum==-1)
  310. {
  311. SpiralInfo::Alert("Port clicked callback can't find source child.");
  312. return;
  313. }
  314. if(Value) // Turned on the port
  315. {
  316. if(m_IncompleteWire.InputChild==-1 || m_IncompleteWire.OutputChild==-1)
  317. {
  318. if (Type==Fl_DeviceGUI::OUTPUT)
  319. {
  320. // make sure we don't make a output->output connection
  321. if (m_IncompleteWire.OutputChild==-1)
  322. {
  323. m_IncompleteWire.OutputChild=ChildNum;
  324. m_IncompleteWire.OutputPort=Port;
  325. m_IncompleteWire.OutputID=Device->GetID();
  326. m_IncompleteWire.OutputTerminal=Device->IsTerminal();
  327. }
  328. else
  329. {
  330. ClearIncompleteWire();
  331. }
  332. }
  333. else
  334. {
  335. // make sure we don't make a input->input connection
  336. if (m_IncompleteWire.InputChild==-1)
  337. {
  338. m_IncompleteWire.InputChild=ChildNum;
  339. m_IncompleteWire.InputPort=Port;
  340. m_IncompleteWire.InputID=Device->GetID();
  341. m_IncompleteWire.InputTerminal=Device->IsTerminal();
  342. }
  343. else
  344. {
  345. ClearIncompleteWire();
  346. }
  347. }
  348. // if both have now been set...
  349. if (m_IncompleteWire.InputChild!=-1 && m_IncompleteWire.OutputChild!=-1)
  350. {
  351. m_WireVec.push_back(m_IncompleteWire);
  352. // send the connect callback
  353. cb_Connection(this,(void*)&m_IncompleteWire);
  354. m_Graph.AddConnection(m_IncompleteWire.OutputID,m_IncompleteWire.OutputTerminal,
  355. m_IncompleteWire.InputID,m_IncompleteWire.InputTerminal);
  356. // Turn on both ports
  357. Fl_DeviceGUI* ODGUI = (Fl_DeviceGUI*)(child(m_IncompleteWire.OutputChild));
  358. ODGUI->AddConnection(m_IncompleteWire.OutputPort+ODGUI->GetInfo()->NumInputs);
  359. Fl_DeviceGUI* IDGUI = (Fl_DeviceGUI*)(child(m_IncompleteWire.InputChild));
  360. IDGUI->AddConnection(m_IncompleteWire.InputPort);
  361. m_IncompleteWire.Clear();
  362. redraw();
  363. }
  364. }
  365. }
  366. else // Turned off the port
  367. {
  368. // Find connections using this port
  369. bool Found=true;
  370. while (Found)
  371. {
  372. Found=false;
  373. for(vector<CanvasWire>::iterator i=m_WireVec.begin();
  374. i!=m_WireVec.end(); i++)
  375. {
  376. if ((Type==Fl_DeviceGUI::OUTPUT && i->OutputChild==ChildNum && i->OutputPort==Port) ||
  377. (Type==Fl_DeviceGUI::INPUT && i->InputChild==ChildNum && i->InputPort==Port))
  378. {
  379. // Turn off both ports
  380. Fl_DeviceGUI* ODGUI = (Fl_DeviceGUI*)(child(i->OutputChild));
  381. ODGUI->RemoveConnection(i->OutputPort+ODGUI->GetInfo()->NumInputs);
  382. Fl_DeviceGUI* IDGUI = (Fl_DeviceGUI*)(child(i->InputChild));
  383. IDGUI->RemoveConnection(i->InputPort);
  384. // send the unconnect callback
  385. cb_Unconnect(this,(void*)&(*i));
  386. m_Graph.RemoveConnection(i->OutputID,i->InputID);
  387. // Remove the wire
  388. m_WireVec.erase(i);
  389. Found=true;
  390. break;
  391. }
  392. }
  393. }
  394. redraw();
  395. // Clear the current selection
  396. m_IncompleteWire.Clear();
  397. }
  398. }
  399. ////////////////////////////////////////////////////////////////////////
  400. void Fl_Canvas::ClearConnections(Fl_DeviceGUI* Device)
  401. {
  402. // find out which child this comes from.
  403. int ChildNum=-1;
  404. for(int n=0; n<children(); n++)
  405. {
  406. if(child(n)==Device)
  407. {
  408. ChildNum=n;
  409. }
  410. }
  411. if (ChildNum==-1)
  412. {
  413. SpiralInfo::Alert("Clear connections callback can't find source child.");
  414. return;
  415. }
  416. bool removedall=false;
  417. while (!removedall)
  418. {
  419. removedall=true;
  420. for (vector<CanvasWire>::iterator i=m_WireVec.begin();
  421. i!=m_WireVec.end(); i++)
  422. {
  423. if (i->OutputChild==ChildNum ||
  424. i->InputChild==ChildNum)
  425. {
  426. // Turn off both ports
  427. ((Fl_DeviceGUI*)(child(i->OutputChild)))->RemoveConnection(i->OutputPort+
  428. ((Fl_DeviceGUI*)(child(i->OutputChild)))->GetInfo()->NumInputs);
  429. ((Fl_DeviceGUI*)(child(i->InputChild)))->RemoveConnection(i->InputPort);
  430. // send the unconnect callback
  431. cb_Unconnect(this,(void*)&(*i));
  432. m_Graph.RemoveConnection(i->OutputID,i->InputID);
  433. m_WireVec.erase(i);
  434. removedall=false;
  435. break;
  436. }
  437. }
  438. }
  439. }
  440. ////////////////////////////////////////////////////////////////////////
  441. void Fl_Canvas::RemoveDevice(Fl_DeviceGUI* Device)
  442. {
  443. // find out which child this comes from.
  444. int ChildNum=-1;
  445. for(int n=0; n<children(); n++)
  446. {
  447. if(child(n)==Device)
  448. {
  449. ChildNum=n;
  450. }
  451. }
  452. if (ChildNum==-1)
  453. {
  454. SpiralInfo::Alert("Remove device callback can't find source child.");
  455. return;
  456. }
  457. ClearConnections(Device);
  458. for (vector<CanvasWire>::iterator i=m_WireVec.begin();
  459. i!=m_WireVec.end(); i++)
  460. {
  461. if (i->OutputChild>ChildNum) i->OutputChild--;
  462. if (i->InputChild>ChildNum) i->InputChild--;
  463. }
  464. remove(child(ChildNum));
  465. redraw();
  466. }
  467. ////////////////////////////////////////////////////////////////////////
  468. void Fl_Canvas::Clear()
  469. {
  470. m_Graph.Clear();
  471. int kids=children();
  472. for(int n=0; n<kids; n++)
  473. {
  474. remove(child(0));
  475. }
  476. m_WireVec.clear();
  477. redraw();
  478. }
  479. ////////////////////////////////////////////////////////////////////////
  480. void Fl_Canvas::Rename(Fl_DeviceGUI* Device)
  481. {
  482. if (cb_Rename) cb_Rename(this,Device);
  483. }
  484. ////////////////////////////////////////////////////////////////////////
  485. istream &operator>>(istream &s, Fl_Canvas &o)
  486. {
  487. int NumWires;
  488. s>>NumWires;
  489. // my bad, didn't version this stream - remove one day...
  490. if (NumWires==-1)
  491. {
  492. int version;
  493. s>>version;
  494. s>>NumWires;
  495. for(int n=0; n<NumWires; n++)
  496. {
  497. CanvasWire NewWire;
  498. s>>NewWire.OutputID;
  499. s>>NewWire.OutputChild;
  500. s>>NewWire.OutputPort;
  501. s>>NewWire.OutputTerminal;
  502. s>>NewWire.InputID;
  503. s>>NewWire.InputChild;
  504. s>>NewWire.InputPort;
  505. s>>NewWire.InputTerminal;
  506. // if we can turn on both ports
  507. if (((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->AddConnection(NewWire.OutputPort+
  508. ((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->GetInfo()->NumInputs) &&
  509. ((Fl_DeviceGUI*)(o.child(NewWire.InputChild)))->AddConnection(NewWire.InputPort))
  510. {
  511. o.m_WireVec.push_back(NewWire);
  512. // Notify connection by callback
  513. o.cb_Connection(&o,(void*)&NewWire);
  514. o.m_Graph.AddConnection(NewWire.OutputID,NewWire.OutputTerminal,NewWire.InputID,NewWire.InputTerminal);
  515. }
  516. }
  517. }
  518. else
  519. {
  520. for(int n=0; n<NumWires; n++)
  521. {
  522. CanvasWire NewWire;
  523. s>>NewWire.OutputID;
  524. s>>NewWire.OutputChild;
  525. s>>NewWire.OutputPort;
  526. s>>NewWire.InputID;
  527. s>>NewWire.InputChild;
  528. s>>NewWire.InputPort;
  529. // if we can turn on both ports
  530. if (((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->AddConnection(NewWire.OutputPort+
  531. ((Fl_DeviceGUI*)(o.child(NewWire.OutputChild)))->GetInfo()->NumInputs) &&
  532. ((Fl_DeviceGUI*)(o.child(NewWire.InputChild)))->AddConnection(NewWire.InputPort))
  533. {
  534. o.m_WireVec.push_back(NewWire);
  535. // Notify connection by callback
  536. o.cb_Connection(&o,(void*)&NewWire);
  537. o.m_Graph.AddConnection(NewWire.OutputID,false,NewWire.InputID,false);
  538. }
  539. }
  540. }
  541. return s;
  542. }
  543. ////////////////////////////////////////////////////////////////////////
  544. ostream &operator<<(ostream &s, Fl_Canvas &o)
  545. {
  546. int version=0;
  547. s<<-1<<" "<<version<<" ";
  548. s<<o.m_WireVec.size()<<endl;
  549. for(vector<CanvasWire>::iterator i=o.m_WireVec.begin();
  550. i!=o.m_WireVec.end(); i++)
  551. {
  552. s<<i->OutputID<<" ";
  553. s<<i->OutputChild<<" ";
  554. s<<i->OutputPort<<" ";
  555. s<<i->OutputTerminal<<" ";
  556. s<<i->InputID<<" ";
  557. s<<i->InputChild<<" ";
  558. s<<i->InputPort<<" ";
  559. s<<i->InputTerminal<<endl;
  560. }
  561. return s;
  562. }