1 /*
2  * Patchbay Canvas engine using QGraphicsView/Scene
3  * Copyright (C) 2010-2012 Filipe Coelho <falktx@falktx.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * For a full copy of the GNU General Public License see the COPYING file
16  */
17 
18 #include "patchcanvas.h"
19 #include "patchscene.h"
20 
21 #include <QtCore/QSettings>
22 #include <QtCore/QTimer>
23 #include <QtGui/QAction>
24 
25 #include "canvasfadeanimation.h"
26 #include "canvasline.h"
27 #include "canvasbezierline.h"
28 #include "canvasport.h"
29 #include "canvasbox.h"
30 
CanvasObject(QObject * parent)31 CanvasObject::CanvasObject(QObject* parent) : QObject(parent) {}
32 
AnimationIdle()33 void CanvasObject::AnimationIdle()
34 {
35     PatchCanvas::CanvasFadeAnimation* animation = (PatchCanvas::CanvasFadeAnimation*)sender();
36     if (animation)
37         PatchCanvas::CanvasRemoveAnimation(animation);
38 }
39 
AnimationHide()40 void CanvasObject::AnimationHide()
41 {
42     PatchCanvas::CanvasFadeAnimation* animation = (PatchCanvas::CanvasFadeAnimation*)sender();
43     if (animation)
44     {
45         if (animation->item())
46             animation->item()->hide();
47         PatchCanvas::CanvasRemoveAnimation(animation);
48     }
49 }
50 
AnimationDestroy()51 void CanvasObject::AnimationDestroy()
52 {
53     PatchCanvas::CanvasFadeAnimation* animation = (PatchCanvas::CanvasFadeAnimation*)sender();
54     if (animation)
55     {
56         if (animation->item())
57             PatchCanvas::CanvasRemoveItemFX(animation->item());
58         PatchCanvas::CanvasRemoveAnimation(animation);
59     }
60 }
61 
CanvasPostponedGroups()62 void CanvasObject::CanvasPostponedGroups()
63 {
64     PatchCanvas::CanvasPostponedGroups();
65 }
66 
PortContextMenuDisconnect()67 void CanvasObject::PortContextMenuDisconnect()
68 {
69     bool ok;
70     int connection_id = ((QAction*)sender())->data().toInt(&ok);
71     if (ok)
72         PatchCanvas::CanvasCallback(PatchCanvas::ACTION_PORTS_DISCONNECT, connection_id, 0, "");
73 }
74 
75 START_NAMESPACE_PATCHCANVAS
76 
77 /* contructor and destructor */
Canvas()78 Canvas::Canvas()
79 {
80     qobject   = 0;
81     settings  = 0;
82     theme     = 0;
83     initiated = false;
84 }
85 
~Canvas()86 Canvas::~Canvas()
87 {
88     if (qobject)
89         delete qobject;
90     if (settings)
91         delete settings;
92     if (theme)
93         delete theme;
94 }
95 
96 /* Global objects */
97 Canvas canvas;
98 
99 options_t options = {
100     /* theme_name */       getDefaultThemeName(),
101     /* auto_hide_groups */ false,
102     /* use_bezier_lines */ true,
103     /* antialiasing */     ANTIALIASING_SMALL,
104     /* eyecandy */         EYECANDY_SMALL
105 };
106 
107 features_t features = {
108     /* group_info */       false,
109     /* group_rename */     false,
110     /* port_info */        false,
111     /* port_rename */      false,
112     /* handle_group_pos */ false
113 };
114 
115 /* Internal functions */
bool2str(bool check)116 const char* bool2str(bool check)
117 {
118     return check ? "true" : "false";
119 }
120 
port_mode2str(PortMode port_mode)121 const char* port_mode2str(PortMode port_mode)
122 {
123     if (port_mode == PORT_MODE_NULL)
124         return "PORT_MODE_NULL";
125     else if (port_mode == PORT_MODE_INPUT)
126         return "PORT_MODE_INPUT";
127     else if (port_mode == PORT_MODE_OUTPUT)
128         return "PORT_MODE_OUTPUT";
129     else
130         return "PORT_MODE_???";
131 }
132 
port_type2str(PortType port_type)133 const char* port_type2str(PortType port_type)
134 {
135     if (port_type == PORT_TYPE_NULL)
136         return "PORT_TYPE_NULL";
137     else if (port_type == PORT_TYPE_AUDIO_JACK)
138         return "PORT_TYPE_AUDIO_JACK";
139     else if (port_type == PORT_TYPE_MIDI_JACK)
140         return "PORT_TYPE_MIDI_JACK";
141     else if (port_type == PORT_TYPE_MIDI_A2J)
142         return "PORT_TYPE_MIDI_A2J";
143     else if (port_type == PORT_TYPE_MIDI_ALSA)
144         return "PORT_TYPE_MIDI_ALSA";
145     else
146         return "PORT_TYPE_???";
147 }
148 
icon2str(Icon icon)149 const char* icon2str(Icon icon)
150 {
151     if (icon == ICON_HARDWARE)
152         return "ICON_HARDWARE";
153     else if (ICON_APPLICATION)
154         return "ICON_APPLICATION";
155     else if (ICON_LADISH_ROOM)
156         return "ICON_LADISH_ROOM";
157     else
158         return "ICON_???";
159 }
160 
split2str(SplitOption split)161 const char* split2str(SplitOption split)
162 {
163     if (split == SPLIT_UNDEF)
164         return "SPLIT_UNDEF";
165     else if (split == SPLIT_NO)
166         return "SPLIT_NO";
167     else if (split == SPLIT_YES)
168         return "SPLIT_YES";
169     else
170         return "SPLIT_???";
171 }
172 
173 /* PatchCanvas API */
setOptions(options_t * new_options)174 void setOptions(options_t* new_options)
175 {
176     if (canvas.initiated) return;
177     options.theme_name        = new_options->theme_name;
178     options.auto_hide_groups  = new_options->auto_hide_groups;
179     options.use_bezier_lines  = new_options->use_bezier_lines;
180     options.antialiasing      = new_options->antialiasing;
181     options.eyecandy          = new_options->eyecandy;
182 }
183 
setFeatures(features_t * new_features)184 void setFeatures(features_t* new_features)
185 {
186     if (canvas.initiated) return;
187     features.group_info       = new_features->group_info;
188     features.group_rename     = new_features->group_rename;
189     features.port_info        = new_features->port_info;
190     features.port_rename      = new_features->port_rename;
191     features.handle_group_pos = new_features->handle_group_pos;
192 }
193 
init(PatchScene * scene,Callback callback,bool debug)194 void init(PatchScene* scene, Callback callback, bool debug)
195 {
196     if (debug)
197         qDebug("PatchCanvas::init(%p, %p, %s)", scene, callback, bool2str(debug));
198 
199     if (canvas.initiated)
200     {
201         qCritical("PatchCanvas::init() - already initiated");
202         return;
203     }
204 
205     if (!callback)
206     {
207         qFatal("PatchCanvas::init() - fatal error: callback not set");
208         return;
209     }
210 
211     canvas.scene = scene;
212     canvas.callback = callback;
213     canvas.debug = debug;
214 
215     canvas.last_z_value = 0;
216     canvas.last_connection_id = 0;
217     canvas.initial_pos = QPointF(0, 0);
218     canvas.size_rect = QRectF();
219 
220     if (!canvas.qobject) canvas.qobject = new CanvasObject();
221     if (!canvas.settings) canvas.settings = new QSettings(PATCHCANVAS_ORGANISATION_NAME, "PatchCanvas");
222 
223     if (canvas.theme)
224     {
225         delete canvas.theme;
226         canvas.theme = 0;
227     }
228 
229     for (int i=0; i<Theme::THEME_MAX; i++)
230     {
231         QString this_theme_name = getThemeName(static_cast<Theme::List>(i));
232         if (this_theme_name == options.theme_name)
233         {
234             canvas.theme = new Theme(static_cast<Theme::List>(i));
235             break;
236         }
237     }
238 
239     if (!canvas.theme)
240         canvas.theme = new Theme(getDefaultTheme());
241 
242     canvas.scene->updateTheme();
243 
244     canvas.initiated = true;
245 }
246 
clear()247 void clear()
248 {
249     if (canvas.debug)
250         qDebug("PatchCanvas::clear()");
251 
252     QList<int> group_list_ids;
253     QList<int> port_list_ids;
254     QList<int> connection_list_ids;
255 
256     foreach (const group_dict_t& group, canvas.group_list)
257         group_list_ids.append(group.group_id);
258 
259     foreach (const port_dict_t& port, canvas.port_list)
260         port_list_ids.append(port.port_id);
261 
262     foreach (const connection_dict_t& connection, canvas.connection_list)
263         connection_list_ids.append(connection.connection_id);
264 
265     foreach (const int& idx, connection_list_ids)
266         disconnectPorts(idx);
267 
268     foreach (const int& idx, port_list_ids)
269         removePort(idx);
270 
271     foreach (const int& idx, group_list_ids)
272         removeGroup(idx);
273 
274     canvas.last_z_value = 0;
275     canvas.last_connection_id = 0;
276 
277     canvas.group_list.clear();
278     canvas.port_list.clear();
279     canvas.connection_list.clear();
280 
281     canvas.initiated = false;
282 }
283 
setInitialPos(int x,int y)284 void setInitialPos(int x, int y)
285 {
286     if (canvas.debug)
287         qDebug("PatchCanvas::setInitialPos(%i, %i)", x, y);
288 
289     canvas.initial_pos.setX(x);
290     canvas.initial_pos.setY(y);
291 }
292 
setCanvasSize(int x,int y,int width,int height)293 void setCanvasSize(int x, int y, int width, int height)
294 {
295     if (canvas.debug)
296         qDebug("PatchCanvas::setCanvasSize(%i, %i, %i, %i)", x, y, width, height);
297 
298     canvas.size_rect.setX(x);
299     canvas.size_rect.setY(y);
300     canvas.size_rect.setWidth(width);
301     canvas.size_rect.setHeight(height);
302 }
303 
addGroup(int group_id,QString group_name,SplitOption split,Icon icon)304 void addGroup(int group_id, QString group_name, SplitOption split, Icon icon)
305 {
306     if (canvas.debug)
307         qDebug("PatchCanvas::addGroup(%i, %s, %s, %s)", group_id, group_name.toUtf8().constData(), split2str(split), icon2str(icon));
308 
309     foreach (const group_dict_t& group, canvas.group_list)
310     {
311         if (group.group_id == group_id)
312         {
313             qWarning("PatchCanvas::addGroup(%i, %s, %s, %s) - group already exists", group_id, group_name.toUtf8().constData(), split2str(split), icon2str(icon));
314             return;
315         }
316     }
317 
318     if (split == SPLIT_UNDEF && features.handle_group_pos)
319         split = static_cast<SplitOption>(canvas.settings->value(QString("CanvasPositions/%1_SPLIT").arg(group_name), split).toInt());
320 
321     CanvasBox* group_box = new CanvasBox(group_id, group_name, icon);
322 
323     group_dict_t group_dict;
324     group_dict.group_id   = group_id;
325     group_dict.group_name = group_name;
326     group_dict.split = (split == SPLIT_YES);
327     group_dict.icon  = icon;
328     group_dict.widgets[0] = group_box;
329     group_dict.widgets[1] = 0;
330 
331     if (split == SPLIT_YES)
332     {
333         group_box->setSplit(true, PORT_MODE_OUTPUT);
334 
335         if (features.handle_group_pos)
336             group_box->setPos(canvas.settings->value(QString("CanvasPositions/%1_OUTPUT").arg(group_name), CanvasGetNewGroupPos()).toPointF());
337         else
338             group_box->setPos(CanvasGetNewGroupPos());
339 
340         CanvasBox* group_sbox = new CanvasBox(group_id, group_name, icon);
341         group_sbox->setSplit(true, PORT_MODE_INPUT);
342 
343         group_dict.widgets[1] = group_sbox;
344 
345         if (features.handle_group_pos)
346             group_sbox->setPos(canvas.settings->value(QString("CanvasPositions/%1_INPUT").arg(group_name), CanvasGetNewGroupPos(true)).toPointF());
347         else
348             group_sbox->setPos(CanvasGetNewGroupPos(true));
349 
350         canvas.last_z_value += 1;
351         group_sbox->setZValue(canvas.last_z_value);
352 
353         if (options.auto_hide_groups == false && options.eyecandy == EYECANDY_FULL)
354             CanvasItemFX(group_sbox, true);
355     }
356     else
357     {
358         group_box->setSplit(false);
359 
360         if (features.handle_group_pos)
361             group_box->setPos(canvas.settings->value(QString("CanvasPositions/%1").arg(group_name), CanvasGetNewGroupPos()).toPointF());
362         else
363         {
364             // Special ladish fake-split groups
365             bool horizontal = (icon == ICON_HARDWARE || icon == ICON_LADISH_ROOM);
366             group_box->setPos(CanvasGetNewGroupPos(horizontal));
367         }
368     }
369 
370     canvas.last_z_value += 1;
371     group_box->setZValue(canvas.last_z_value);
372 
373     canvas.group_list.append(group_dict);
374 
375     if (options.auto_hide_groups == false && options.eyecandy == EYECANDY_FULL)
376         CanvasItemFX(group_box, true);
377 
378     QTimer::singleShot(0, canvas.scene, SLOT(update()));
379 }
380 
removeGroup(int group_id)381 void removeGroup(int group_id)
382 {
383     if (canvas.debug)
384         qDebug("PatchCanvas::removeGroup(%i)", group_id);
385 
386     foreach2 (const group_dict_t& group, canvas.group_list)
387         if (group.group_id == group_id)
388         {
389             CanvasBox* item = group.widgets[0];
390             QString group_name = group.group_name;
391 
392             if (group.split)
393             {
394                 CanvasBox* s_item = group.widgets[1];
395                 if (features.handle_group_pos)
396                 {
397                     canvas.settings->setValue(QString("CanvasPositions/%1_OUTPUT").arg(group_name), item->pos());
398                     canvas.settings->setValue(QString("CanvasPositions/%1_INPUT").arg(group_name), s_item->pos());
399                     canvas.settings->setValue(QString("CanvasPositions/%1_SPLIT").arg(group_name), SPLIT_YES);
400                 }
401 
402                 if (options.eyecandy == EYECANDY_FULL)
403                 {
404                     CanvasItemFX(s_item, false, true);
405                 }
406                 else
407                 {
408                     s_item->removeIconFromScene();
409                     canvas.scene->removeItem(s_item);
410                     delete s_item;
411                 }
412             }
413             else
414             {
415                 if (features.handle_group_pos)
416                 {
417                     canvas.settings->setValue(QString("CanvasPositions/%1").arg(group_name), item->pos());
418                     canvas.settings->setValue(QString("CanvasPositions/%1_SPLIT").arg(group_name), SPLIT_NO);
419                 }
420             }
421 
422             if (options.eyecandy == EYECANDY_FULL)
423             {
424                 CanvasItemFX(item, false, true);
425             }
426             else
427             {
428                 item->removeIconFromScene();
429                 canvas.scene->removeItem(item);
430                 delete item;
431             }
432 
433             canvas.group_list.takeAt(i);
434 
435             QTimer::singleShot(0, canvas.scene, SLOT(update()));
436             return;
437         }
438     }
439 
440     qCritical("PatchCanvas::removeGroup(%i) - unable to find group to remove", group_id);
441 }
442 
443 void renameGroup(int group_id, QString new_group_name)
444 {
445     if (canvas.debug)
446         qDebug("PatchCanvas::renameGroup(%i, %s)", group_id, new_group_name.toUtf8().constData());
447 
448     foreach2 (group_dict_t& group, canvas.group_list)
449         if (group.group_id == group_id)
450         {
451             group.group_name = new_group_name;
452             group.widgets[0]->setGroupName(new_group_name);
453 
454             if (group.split && group.widgets[1])
455                 group.widgets[1]->setGroupName(new_group_name);
456 
457             QTimer::singleShot(0, canvas.scene, SLOT(update()));
458             return;
459         }
460     }
461 
462     qCritical("PatchCanvas::renameGroup(%i, %s) - unable to find group to rename", group_id, new_group_name.toUtf8().constData());
463 }
464 
465 void splitGroup(int group_id)
466 {
467     if (canvas.debug)
468         qDebug("PatchCanvas::splitGroup(%i)", group_id);
469 
470     CanvasBox* item = 0;
471     QString group_name;
472     Icon group_icon = ICON_APPLICATION;
473     QList<port_dict_t> ports_data;
474     QList<connection_dict_t> conns_data;
475 
476     // Step 1 - Store all Item data
477     foreach (const group_dict_t& group, canvas.group_list)
478     {
479         if (group.group_id == group_id)
480         {
481             if (group.split)
482             {
483                 qCritical("PatchCanvas::splitGroup(%i) - group is already splitted", group_id);
484                 return;
485             }
486 
487             item = group.widgets[0];
488             group_name = group.group_name;
489             group_icon = group.icon;
490             break;
491         }
492     }
493 
494     if (!item)
495     {
496         qCritical("PatchCanvas::splitGroup(%i) - unable to find group to split", group_id);
497         return;
498     }
499 
500     QList<int> port_list_ids = QList<int>(item->getPortList());
501 
502     foreach (const port_dict_t& port, canvas.port_list)
503     {
504         if (port_list_ids.contains(port.port_id))
505         {
506             port_dict_t port_dict;
507             port_dict.group_id  = port.group_id;
508             port_dict.port_id   = port.port_id;
509             port_dict.port_name = port.port_name;
510             port_dict.port_mode = port.port_mode;
511             port_dict.port_type = port.port_type;
512             port_dict.widget    = 0;
513             ports_data.append(port_dict);
514         }
515     }
516 
517     foreach (const connection_dict_t& connection, canvas.connection_list)
518     {
519         if (port_list_ids.contains(connection.port_out_id) || port_list_ids.contains(connection.port_in_id))
520         {
521             connection_dict_t connection_dict;
522             connection_dict.connection_id = connection.connection_id;
523             connection_dict.port_in_id    = connection.port_in_id;
524             connection_dict.port_out_id   = connection.port_out_id;
525             connection_dict.widget        = 0;
526             conns_data.append(connection_dict);
527         }
528     }
529 
530     // Step 2 - Remove Item and Children
531     foreach (const connection_dict_t& conn, conns_data)
532         disconnectPorts(conn.connection_id);
533 
534     foreach (const int& port_id, port_list_ids)
535         removePort(port_id);
536 
537     removeGroup(group_id);
538 
539     // Step 3 - Re-create Item, now splitted
540     addGroup(group_id, group_name, SPLIT_YES, group_icon);
541 
542     foreach (const port_dict_t& port, ports_data)
543         addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type);
544 
545     foreach (const connection_dict_t& conn, conns_data)
546         connectPorts(conn.connection_id, conn.port_out_id, conn.port_in_id);
547 
548     QTimer::singleShot(0, canvas.scene, SLOT(update()));
549 }
550 
551 void joinGroup(int group_id)
552 {
553     if (canvas.debug)
554         qDebug("PatchCanvas::joinGroup(%i)", group_id);
555 
556     CanvasBox* item = 0;
557     CanvasBox* s_item = 0;
558     QString group_name;
559     Icon group_icon = ICON_APPLICATION;
560     QList<port_dict_t> ports_data;
561     QList<connection_dict_t> conns_data;
562 
563     // Step 1 - Store all Item data
564     foreach (const group_dict_t& group, canvas.group_list)
565     {
566         if (group.group_id == group_id)
567         {
568             if (group.split == false)
569             {
570                 qCritical("PatchCanvas::joinGroup(%i) - group is not splitted", group_id);
571                 return;
572             }
573 
574             item   = group.widgets[0];
575             s_item = group.widgets[1];
576             group_name = group.group_name;
577             group_icon = group.icon;
578             break;
579         }
580     }
581 
582     if (!item || !s_item)
583     {
584         qCritical("PatchCanvas::joinGroup(%i) - Unable to find groups to join", group_id);
585         return;
586     }
587 
588     QList<int> port_list_ids  = QList<int>(item->getPortList());
589     QList<int> port_list_idss = s_item->getPortList();
590 
591     foreach (const int& port_id, port_list_idss)
592     {
593         if (port_list_ids.contains(port_id) == false)
594             port_list_ids.append(port_id);
595     }
596 
597     foreach (const port_dict_t& port, canvas.port_list)
598     {
599         if (port_list_ids.contains(port.port_id))
600         {
601             port_dict_t port_dict;
602             port_dict.group_id  = port.group_id;
603             port_dict.port_id   = port.port_id;
604             port_dict.port_name = port.port_name;
605             port_dict.port_mode = port.port_mode;
606             port_dict.port_type = port.port_type;
607             port_dict.widget    = 0;
608             ports_data.append(port_dict);
609         }
610     }
611 
612     foreach (const connection_dict_t& connection, canvas.connection_list)
613     {
614         if (port_list_ids.contains(connection.port_out_id) || port_list_ids.contains(connection.port_in_id))
615         {
616             connection_dict_t connection_dict;
617             connection_dict.connection_id = connection.connection_id;
618             connection_dict.port_in_id    = connection.port_in_id;
619             connection_dict.port_out_id   = connection.port_out_id;
620             connection_dict.widget        = 0;
621             conns_data.append(connection_dict);
622         }
623     }
624 
625     // Step 2 - Remove Item and Children
626     foreach (const connection_dict_t& conn, conns_data)
627         disconnectPorts(conn.connection_id);
628 
629     foreach (const int& port_id, port_list_ids)
630         removePort(port_id);
631 
632     removeGroup(group_id);
633 
634     // Step 3 - Re-create Item, now together
635     addGroup(group_id, group_name, SPLIT_NO, group_icon);
636 
637     foreach (const port_dict_t& port, ports_data)
638         addPort(group_id, port.port_id, port.port_name, port.port_mode, port.port_type);
639 
640     foreach (const connection_dict_t& conn, conns_data)
641         connectPorts(conn.connection_id, conn.port_out_id, conn.port_in_id);
642 
643     QTimer::singleShot(0, canvas.scene, SLOT(update()));
644 }
645 
646 QPointF getGroupPos(int group_id, PortMode port_mode)
647 {
648     if (canvas.debug)
649         qDebug("PatchCanvas::getGroupPos(%i, %s)", group_id, port_mode2str(port_mode));
650 
651     foreach (const group_dict_t& group, canvas.group_list)
652     {
653         if (group.group_id == group_id)
654         {
655             if (group.split)
656             {
657                 if (port_mode == PORT_MODE_OUTPUT)
658                     return group.widgets[0]->pos();
659                 else if (port_mode == PORT_MODE_INPUT)
660                     return group.widgets[1]->pos();
661                 else
662                     return QPointF(0, 0);
663             }
664             else
665                 return group.widgets[0]->pos();
666         }
667     }
668 
669     qCritical("PatchCanvas::getGroupPos(%i, %s) - unable to find group", group_id, port_mode2str(port_mode));
670     return QPointF(0,0);
671 }
672 
673 void setGroupPos(int group_id, int group_pos_x, int group_pos_y)
674 {
675     setGroupPos(group_id, group_pos_x, group_pos_y, group_pos_x, group_pos_y);
676 }
677 
678 void setGroupPos(int group_id, int group_pos_x, int group_pos_y, int group_pos_xs, int group_pos_ys)
679 {
680     if (canvas.debug)
681         qDebug("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i)", group_id, group_pos_x, group_pos_y, group_pos_xs, group_pos_ys);
682 
683     foreach (const group_dict_t& group, canvas.group_list)
684     {
685         if (group.group_id == group_id)
686         {
687             group.widgets[0]->setPos(group_pos_x, group_pos_y);
688 
689             if (group.split && group.widgets[1])
690             {
691                 group.widgets[1]->setPos(group_pos_xs, group_pos_ys);
692             }
693 
694             QTimer::singleShot(0, canvas.scene, SLOT(update()));
695             return;
696         }
697     }
698 
699     qCritical("PatchCanvas::setGroupPos(%i, %i, %i, %i, %i) - unable to find group to reposition", group_id, group_pos_x, group_pos_y, group_pos_xs, group_pos_ys);
700 }
701 
702 void setGroupIcon(int group_id, Icon icon)
703 {
704     if (canvas.debug)
705         qDebug("PatchCanvas::setGroupIcon(%i, %s)", group_id, icon2str(icon));
706 
707     foreach2 (group_dict_t& group, canvas.group_list)
708         if (group.group_id == group_id)
709         {
710             group.icon = icon;
711             group.widgets[0]->setIcon(icon);
712 
713             if (group.split && group.widgets[1])
714                 group.widgets[1]->setIcon(icon);
715 
716             QTimer::singleShot(0, canvas.scene, SLOT(update()));
717             return;
718         }
719     }
720 
721     qCritical("PatchCanvas::setGroupIcon(%i, %s) - unable to find group to change icon", group_id, icon2str(icon));
722 }
723 
724 void addPort(int group_id, int port_id, QString port_name, PortMode port_mode, PortType port_type)
725 {
726     if (canvas.debug)
727         qDebug("PatchCanvas::addPort(%i, %i, %s, %s, %s)", group_id, port_id, port_name.toUtf8().constData(), port_mode2str(port_mode), port_type2str(port_type));
728 
729     foreach (const port_dict_t& port, canvas.port_list)
730     {
731         if (port.group_id == group_id and port.port_id == port_id)
732         {
733             qWarning("PatchCanvas::addPort(%i, %i, %s, %s, %s) - port already exists" , group_id, port_id, port_name.toUtf8().constData(), port_mode2str(port_mode), port_type2str(port_type));
734             return;
735         }
736     }
737 
738     CanvasBox* box_widget = 0;
739     CanvasPort* port_widget = 0;
740 
741     foreach (const group_dict_t& group, canvas.group_list)
742     {
743         if (group.group_id == group_id)
744         {
745             int n;
746             if (group.split && group.widgets[0]->getSplittedMode() != port_mode && group.widgets[1])
747                 n = 1;
748             else
749                 n = 0;
750             box_widget = group.widgets[n];
751             port_widget = box_widget->addPortFromGroup(port_id, port_name, port_mode, port_type);
752             break;
753         }
754     }
755 
756     if (!box_widget || !port_widget)
757     {
758         qCritical("PatchCanvas::addPort(%i, %i, %s, %s, %s) - unable to find parent group", group_id, port_id, port_name.toUtf8().constData(), port_mode2str(port_mode), port_type2str(port_type));
759         return;
760     }
761 
762     if (options.eyecandy == EYECANDY_FULL)
763         CanvasItemFX(port_widget, true);
764 
765     port_dict_t port_dict;
766     port_dict.group_id  = group_id;
767     port_dict.port_id   = port_id;
768     port_dict.port_name = port_name;
769     port_dict.port_mode = port_mode;
770     port_dict.port_type = port_type;
771     port_dict.widget    = port_widget;
772     canvas.port_list.append(port_dict);
773 
774     box_widget->updatePositions();
775 
776     QTimer::singleShot(0, canvas.scene, SLOT(update()));
777 }
778 
779 void removePort(int port_id)
780 {
781     if (canvas.debug)
782         qDebug("PatchCanvas::removePort(%i)", port_id);
783 
784     foreach2 (const port_dict_t& port, canvas.port_list)
785         if (port.port_id == port_id)
786         {
787             CanvasPort* item = port.widget;
788             ((CanvasBox*)item->parentItem())->removePortFromGroup(port_id);
789             canvas.scene->removeItem(item);
790             delete item;
791 
792             canvas.port_list.takeAt(i);
793 
794             QTimer::singleShot(0, canvas.scene, SLOT(update()));
795             return;
796         }
797     }
798 
799     qCritical("PatchCanvas::removePort(%i) - unable to find port to remove", port_id);
800 }
801 
802 void renamePort(int port_id, QString new_port_name)
803 {
804     if (canvas.debug)
805         qDebug("PatchCanvas::renamePort(%i, %s)", port_id, new_port_name.toUtf8().constData());
806 
807     foreach2 (port_dict_t& port, canvas.port_list)
808         if (port.port_id == port_id)
809         {
810             port.port_name = new_port_name;
811             port.widget->setPortName(new_port_name);
812             ((CanvasBox*)port.widget->parentItem())->updatePositions();
813 
814             QTimer::singleShot(0, canvas.scene, SLOT(update()));
815             return;
816         }
817     }
818 
819     qCritical("PatchCanvas::renamePort(%i, %s) - unable to find port to rename", port_id, new_port_name.toUtf8().constData());
820 }
821 
822 void connectPorts(int connection_id, int port_out_id, int port_in_id)
823 {
824     if (canvas.debug)
825         qDebug("PatchCanvas::connectPorts(%i, %i, %i)", connection_id, port_out_id, port_in_id);
826 
827     CanvasPort* port_out = 0;
828     CanvasPort* port_in  = 0;
829     CanvasBox* port_out_parent = 0;
830     CanvasBox* port_in_parent  = 0;
831 
832     foreach (const port_dict_t& port, canvas.port_list)
833     {
834         if (port.port_id == port_out_id)
835         {
836             port_out = port.widget;
837             port_out_parent = (CanvasBox*)port_out->parentItem();
838         }
839         else if (port.port_id == port_in_id)
840         {
841             port_in = port.widget;
842             port_in_parent = (CanvasBox*)port_in->parentItem();
843         }
844     }
845 
846     if (!port_out || !port_in)
847     {
848         qCritical("PatchCanvas::connectPorts(%i, %i, %i) - Unable to find ports to connect", connection_id, port_out_id, port_in_id);
849         return;
850     }
851 
852     connection_dict_t connection_dict;
853     connection_dict.connection_id = connection_id;
854     connection_dict.port_out_id = port_out_id;
855     connection_dict.port_in_id  = port_in_id;
856 
857     if (options.use_bezier_lines)
858         connection_dict.widget = new CanvasBezierLine(port_out, port_in, 0);
859     else
860         connection_dict.widget = new CanvasLine(port_out, port_in, 0);
861 
862     port_out_parent->addLineFromGroup(connection_dict.widget, connection_id);
863     port_in_parent->addLineFromGroup(connection_dict.widget, connection_id);
864 
865     canvas.last_z_value += 1;
866     port_out_parent->setZValue(canvas.last_z_value);
867     port_in_parent->setZValue(canvas.last_z_value);
868 
869     canvas.last_z_value += 1;
870     connection_dict.widget->setZValue(canvas.last_z_value);
871 
872     canvas.connection_list.append(connection_dict);
873 
874     if (options.eyecandy == EYECANDY_FULL)
875     {
876         QGraphicsItem* item = (options.use_bezier_lines) ? (QGraphicsItem*)(CanvasBezierLine*)connection_dict.widget : (QGraphicsItem*)(CanvasLine*)connection_dict.widget;
877         CanvasItemFX(item, true);
878     }
879 
880     QTimer::singleShot(0, canvas.scene, SLOT(update()));
881 }
882 
883 void disconnectPorts(int connection_id)
884 {
885     if (canvas.debug)
886         qDebug("PatchCanvas::disconnectPorts(%i)", connection_id);
887 
888     int port_1_id, port_2_id;
889     AbstractCanvasLine* line = 0;
890     QGraphicsItem* item1 = 0;
891     QGraphicsItem* item2 = 0;
892 
893     foreach2 (const connection_dict_t& connection, canvas.connection_list)
894         if (connection.connection_id == connection_id)
895         {
896             port_1_id = connection.port_out_id;
897             port_2_id = connection.port_in_id;
898             line = connection.widget;
899             canvas.connection_list.takeAt(i);
900             break;
901         }
902     }
903 
904     if (!line)
905     {
906         qCritical("PatchCanvas::disconnectPorts(%i) - unable to find connection ports", connection_id);
907         return;
908     }
909 
910     foreach (const port_dict_t& port, canvas.port_list)
911     {
912         if (port.port_id == port_1_id)
913         {
914             item1 = port.widget;
915             break;
916         }
917     }
918 
919     if (!item1)
920     {
921         qCritical("PatchCanvas::disconnectPorts(%i) - unable to find output port", connection_id);
922         return;
923     }
924 
925     foreach (const port_dict_t& port, canvas.port_list)
926     {
927         if (port.port_id == port_2_id)
928         {
929             item2 = port.widget;
930             break;
931         }
932     }
933 
934     if (!item2)
935     {
936         qCritical("PatchCanvas::disconnectPorts(%i) - unable to find input port", connection_id);
937         return;
938     }
939 
940     ((CanvasBox*)item1->parentItem())->removeLineFromGroup(connection_id);
941     ((CanvasBox*)item2->parentItem())->removeLineFromGroup(connection_id);
942 
943     if (options.eyecandy == EYECANDY_FULL)
944     {
945         QGraphicsItem* item = (options.use_bezier_lines) ? (QGraphicsItem*)(CanvasBezierLine*)line : (QGraphicsItem*)(CanvasLine*)line;
946         CanvasItemFX(item, false, true);
947     }
948     else
949         line->deleteFromScene();
950 
951     QTimer::singleShot(0, canvas.scene, SLOT(update()));
952 }
953 
954 void arrange()
955 {
956     if (canvas.debug)
957         qDebug("PatchCanvas::Arrange()");
958 }
959 
960 void updateZValues()
961 {
962     if (canvas.debug)
963         qDebug("PatchCanvas::updateZValues()");
964 
965 
966     foreach (const group_dict_t& group, canvas.group_list)
967     {
968         group.widgets[0]->resetLinesZValue();
969 
970         if (group.split and group.widgets[1])
971             group.widgets[1]->resetLinesZValue();
972     }
973 }
974 
975 /* Extra Internal functions */
976 
977 QString CanvasGetGroupName(int group_id)
978 {
979     if (canvas.debug)
980         qDebug("PatchCanvas::CanvasGetGroupName(%i)", group_id);
981 
982     foreach (const group_dict_t& group, canvas.group_list)
983     {
984         if (group.group_id == group_id)
985             return group.group_name;
986     }
987 
988     qCritical("PatchCanvas::CanvasGetGroupName(%i) - unable to find group", group_id);
989     return "";
990 }
991 
992 int CanvasGetGroupPortCount(int group_id)
993 {
994     if (canvas.debug)
995         qDebug("PatchCanvas::CanvasGetGroupPortCount(%i)", group_id);
996 
997     int port_count = 0;
998     foreach (const port_dict_t& port, canvas.port_list)
999     {
1000         if (port.group_id == group_id)
1001             port_count += 1;
1002     }
1003 
1004     return port_count;
1005 }
1006 
1007 QPointF CanvasGetNewGroupPos(bool horizontal)
1008 {
1009     if (canvas.debug)
1010         qDebug("PatchCanvas::CanvasGetNewGroupPos(%s)", bool2str(horizontal));
1011 
1012     QPointF new_pos(canvas.initial_pos.x(), canvas.initial_pos.y());
1013     QList<QGraphicsItem*> items = canvas.scene->items();
1014 
1015     bool break_loop = false;
1016     while (break_loop == false)
1017     {
1018         bool break_for = false;
1019         for (int i=0; i < items.count(); i++)
1020         {
1021             QGraphicsItem* item = items[i];
1022             if (item && item->type() == CanvasBoxType)
1023             {
1024                 if (item->sceneBoundingRect().contains(new_pos))
1025                 {
1026                     if (horizontal)
1027                         new_pos += QPointF(item->boundingRect().width()+15, 0);
1028                     else
1029                         new_pos += QPointF(0, item->boundingRect().height()+15);
1030                     break;
1031                 }
1032             }
1033             if (i >= items.count()-1 && break_for == false)
1034                 break_loop = true;
1035         }
1036     }
1037 
1038     return new_pos;
1039 }
1040 
1041 QString CanvasGetFullPortName(int port_id)
1042 {
1043     if (canvas.debug)
1044         qDebug("PatchCanvas::CanvasGetFullPortName(%i)", port_id);
1045 
1046     foreach (const port_dict_t& port, canvas.port_list)
1047     {
1048         if (port.port_id == port_id)
1049         {
1050             int group_id = port.group_id;
1051             foreach (const group_dict_t& group, canvas.group_list)
1052             {
1053                 if (group.group_id == group_id)
1054                     return group.group_name + ":" + port.port_name;
1055             }
1056             break;
1057         }
1058     }
1059 
1060     qCritical("PatchCanvas::CanvasGetFullPortName(%i) - unable to find port", port_id);
1061     return "";
1062 }
1063 
1064 QList<int> CanvasGetPortConnectionList(int port_id)
1065 {
1066     if (canvas.debug)
1067         qDebug("PatchCanvas::CanvasGetPortConnectionList(%i)", port_id);
1068 
1069     QList<int> port_con_list;
1070 
1071     foreach (const connection_dict_t& connection, canvas.connection_list)
1072     {
1073         if (connection.port_out_id == port_id || connection.port_in_id == port_id)
1074             port_con_list.append(connection.connection_id);
1075     }
1076 
1077     return port_con_list;
1078 }
1079 
1080 int CanvasGetConnectedPort(int connection_id, int port_id)
1081 {
1082     if (canvas.debug)
1083         qDebug("PatchCanvas::CanvasGetConnectedPort(%i, %i)", connection_id, port_id);
1084 
1085     foreach (const connection_dict_t& connection, canvas.connection_list)
1086     {
1087         if (connection.connection_id == connection_id)
1088         {
1089             if (connection.port_out_id == port_id)
1090                 return connection.port_in_id;
1091             else
1092                 return connection.port_out_id;
1093         }
1094     }
1095 
1096     qCritical("PatchCanvas::CanvasGetConnectedPort(%i, %i) - unable to find connection", connection_id, port_id);
1097     return 0;
1098 }
1099 
1100 void CanvasRemoveAnimation(CanvasFadeAnimation* f_animation)
1101 {
1102     if (canvas.debug)
1103         qDebug("PatchCanvas::CanvasRemoveAnimation(%p)", f_animation);
1104 
1105     foreach2 (const animation_dict_t& animation, canvas.animation_list)
1106         if (animation.animation == f_animation)
1107         {
1108             delete animation.animation;
1109             canvas.animation_list.takeAt(i);
1110             break;
1111         }
1112     }
1113 }
1114 
1115 void CanvasPostponedGroups()
1116 {
1117     if (canvas.debug)
1118         qDebug("PatchCanvas::CanvasPostponedGroups()");
1119 }
1120 
1121 void CanvasCallback(CallbackAction action, int value1, int value2, QString value_str)
1122 {
1123     if (canvas.debug)
1124         qDebug("PatchCanvas::CanvasCallback(%i, %i, %i, %s)", action, value1, value2, value_str.toStdString().data());
1125 
1126     canvas.callback(action, value1, value2, value_str);
1127 }
1128 
1129 void CanvasItemFX(QGraphicsItem* item, bool show, bool destroy)
1130 {
1131     if (canvas.debug)
1132         qDebug("PatchCanvas::CanvasItemFX(%p, %s, %s)", item, bool2str(show), bool2str(destroy));
1133 
1134     // Check if item already has an animationItemFX
1135     foreach2 (const animation_dict_t& animation, canvas.animation_list)
1136         if (animation.item == item)
1137         {
1138             if (animation.animation)
1139             {
1140                 animation.animation->stop();
1141                 delete animation.animation;
1142             }
1143             canvas.animation_list.takeAt(i);
1144             break;
1145         }
1146     }
1147 
1148     CanvasFadeAnimation* animation = new CanvasFadeAnimation(item, show);
1149     animation->setDuration(show ? 750 : 500);
1150 
1151     animation_dict_t animation_dict;
1152     animation_dict.animation = animation;
1153     animation_dict.item = item;
1154     canvas.animation_list.append(animation_dict);
1155 
1156     if (show)
1157     {
1158         QObject::connect(animation, SIGNAL(finished()), canvas.qobject, SLOT(AnimationIdle()));
1159     }
1160     else
1161     {
1162         if (destroy)
1163             QObject::connect(animation, SIGNAL(finished()), canvas.qobject, SLOT(AnimationDestroy()));
1164         else
1165             QObject::connect(animation, SIGNAL(finished()), canvas.qobject, SLOT(AnimationHide()));
1166     }
1167 
1168     animation->start();
1169 }
1170 
1171 void CanvasRemoveItemFX(QGraphicsItem* item)
1172 {
1173     if (canvas.debug)
1174       qDebug("PatchCanvas::CanvasRemoveItemFX(%p)", item);
1175 
1176     switch (item->type())
1177     {
1178     case CanvasBoxType:
1179     {
1180         CanvasBox* box = (CanvasBox*)item;
1181         box->removeIconFromScene();
1182         canvas.scene->removeItem(box);
1183         delete box;
1184     }
1185     case CanvasPortType:
1186     {
1187         CanvasPort* port = (CanvasPort*)item;
1188         canvas.scene->removeItem(port);
1189         delete port;
1190     }
1191     case CanvasLineType:
1192     case CanvasBezierLineType:
1193     {
1194         AbstractCanvasLine* line = (AbstractCanvasLine*)item;
1195         line->deleteFromScene();
1196     }
1197     default:
1198         break;
1199     }
1200 }
1201 
1202 END_NAMESPACE_PATCHCANVAS
1203