1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3
4# PatchBay Canvas engine using QGraphicsView/Scene
5# Copyright (C) 2010-2019 Filipe Coelho <falktx@falktx.com>
6#
7# This program is free software; you can redistribute it and/or
8# modify it under the terms of the GNU General Public License as
9# published by the Free Software Foundation; either version 2 of
10# the License, or any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# For a full copy of the GNU General Public License see the doc/GPL.txt file.
18
19# ------------------------------------------------------------------------------------------------------------
20# Imports (Global)
21
22from PyQt5.QtCore import QPointF, QRectF
23from PyQt5.QtWidgets import QGraphicsItem
24
25# ------------------------------------------------------------------------------------------------------------
26# Imports (Theme)
27
28from .theme import getDefaultThemeName
29
30# ------------------------------------------------------------------------------------------------------------
31
32# Maximum Id for a plugin, treated as invalid/zero if above this value
33MAX_PLUGIN_ID_ALLOWED = 0x7FF
34
35# Port Mode
36PORT_MODE_NULL   = 0
37PORT_MODE_INPUT  = 1
38PORT_MODE_OUTPUT = 2
39
40# Port Type
41PORT_TYPE_NULL       = 0
42PORT_TYPE_AUDIO_JACK = 1
43PORT_TYPE_MIDI_JACK  = 2
44PORT_TYPE_MIDI_ALSA  = 3
45PORT_TYPE_PARAMETER  = 4
46
47# Callback Action
48ACTION_GROUP_INFO       =  0 # group_id, N, N
49ACTION_GROUP_RENAME     =  1 # group_id, N, N
50ACTION_GROUP_SPLIT      =  2 # group_id, N, N
51ACTION_GROUP_JOIN       =  3 # group_id, N, N
52ACTION_GROUP_POSITION   =  4 # group_id, N, N, "x1:y1:x2:y2"
53ACTION_PORT_INFO        =  5 # group_id, port_id, N
54ACTION_PORT_RENAME      =  6 # group_id, port_id, N
55ACTION_PORTS_CONNECT    =  7 # N, N, "outG:outP:inG:inP"
56ACTION_PORTS_DISCONNECT =  8 # conn_id, N, N
57ACTION_PLUGIN_CLONE     =  9 # plugin_id, N, N
58ACTION_PLUGIN_EDIT      = 10 # plugin_id, N, N
59ACTION_PLUGIN_RENAME    = 11 # plugin_id, N, N
60ACTION_PLUGIN_REPLACE   = 12 # plugin_id, N, N
61ACTION_PLUGIN_REMOVE    = 13 # plugin_id, N, N
62ACTION_PLUGIN_SHOW_UI   = 14 # plugin_id, N, N
63ACTION_BG_RIGHT_CLICK   = 15 # N, N, N
64ACTION_INLINE_DISPLAY   = 16 # plugin_id, N, N
65
66# Icon
67ICON_APPLICATION = 0
68ICON_HARDWARE    = 1
69ICON_DISTRHO     = 2
70ICON_FILE        = 3
71ICON_PLUGIN      = 4
72ICON_LADISH_ROOM = 5
73
74# Split Option
75SPLIT_UNDEF = 0
76SPLIT_NO    = 1
77SPLIT_YES   = 2
78
79# Antialiasing Option
80ANTIALIASING_NONE  = 0
81ANTIALIASING_SMALL = 1
82ANTIALIASING_FULL  = 2
83
84# Eye-Candy Option
85EYECANDY_NONE  = 0
86EYECANDY_SMALL = 1
87EYECANDY_FULL  = 2
88
89# ------------------------------------------------------------------------------------------------------------
90
91# object types
92CanvasBoxType           = QGraphicsItem.UserType + 1
93CanvasIconType          = QGraphicsItem.UserType + 2
94CanvasPortType          = QGraphicsItem.UserType + 3
95CanvasLineType          = QGraphicsItem.UserType + 4
96CanvasBezierLineType    = QGraphicsItem.UserType + 5
97CanvasLineMovType       = QGraphicsItem.UserType + 6
98CanvasBezierLineMovType = QGraphicsItem.UserType + 7
99CanvasRubberbandType    = QGraphicsItem.UserType + 8
100
101# ------------------------------------------------------------------------------------------------------------
102
103# Canvas options
104class options_t(object):
105    __slots__ = [
106        'theme_name',
107        'auto_hide_groups',
108        'auto_select_items',
109        'use_bezier_lines',
110        'antialiasing',
111        'eyecandy',
112        'inline_displays'
113    ]
114
115# Canvas features
116class features_t(object):
117    __slots__ = [
118        'group_info',
119        'group_rename',
120        'port_info',
121        'port_rename',
122        'handle_group_pos'
123    ]
124
125# Main Canvas object
126class Canvas(object):
127    def __init__(self):
128        self.qobject = None
129        self.settings = None
130        self.theme = None
131        self.initiated = False
132
133        self.group_list = []
134        self.port_list = []
135        self.connection_list = []
136        self.animation_list = []
137        self.group_plugin_map = {}
138        self.old_group_pos = {}
139
140        self.callback = self.callback
141        self.debug = False
142        self.scene = None
143        self.last_z_value = 0
144        self.last_connection_id = 0
145        self.initial_pos = QPointF(0, 0)
146        self.size_rect = QRectF()
147
148    def callback(self, action, value1, value2, value_str):
149        print("Canvas::callback({}, {}, {}, {})".format(action, value1, value2, value_str))
150
151# ------------------------------------------------------------------------------------------------------------
152
153# object lists
154class group_dict_t(object):
155    __slots__ = [
156        'group_id',
157        'group_name',
158        'split',
159        'icon',
160        'plugin_id',
161        'plugin_ui',
162        'plugin_inline',
163        'widgets'
164    ]
165
166class port_dict_t(object):
167    __slots__ = [
168        'group_id',
169        'port_id',
170        'port_name',
171        'port_mode',
172        'port_type',
173        'is_alternate',
174        'widget'
175    ]
176
177class connection_dict_t(object):
178    __slots__ = [
179        'connection_id',
180        'group_in_id',
181        'port_in_id',
182        'group_out_id',
183        'port_out_id',
184        'widget'
185    ]
186
187class animation_dict_t(object):
188    __slots__ = [
189        'animation',
190        'item'
191    ]
192
193# ------------------------------------------------------------------------------------------------------------
194
195# Internal functions
196def bool2str(check):
197    return "True" if check else "False"
198
199def port_mode2str(port_mode):
200    if port_mode == PORT_MODE_NULL:
201        return "PORT_MODE_NULL"
202    elif port_mode == PORT_MODE_INPUT:
203        return "PORT_MODE_INPUT"
204    elif port_mode == PORT_MODE_OUTPUT:
205        return "PORT_MODE_OUTPUT"
206    else:
207        return "PORT_MODE_???"
208
209def port_type2str(port_type):
210    if port_type == PORT_TYPE_NULL:
211        return "PORT_TYPE_NULL"
212    elif port_type == PORT_TYPE_AUDIO_JACK:
213        return "PORT_TYPE_AUDIO_JACK"
214    elif port_type == PORT_TYPE_MIDI_JACK:
215        return "PORT_TYPE_MIDI_JACK"
216    elif port_type == PORT_TYPE_MIDI_ALSA:
217        return "PORT_TYPE_MIDI_ALSA"
218    elif port_type == PORT_TYPE_PARAMETER:
219        return "PORT_TYPE_MIDI_PARAMETER"
220    else:
221        return "PORT_TYPE_???"
222
223def icon2str(icon):
224    if icon == ICON_APPLICATION:
225        return "ICON_APPLICATION"
226    elif icon == ICON_HARDWARE:
227        return "ICON_HARDWARE"
228    elif icon == ICON_DISTRHO:
229        return "ICON_DISTRHO"
230    elif icon == ICON_FILE:
231        return "ICON_FILE"
232    elif icon == ICON_PLUGIN:
233        return "ICON_PLUGIN"
234    elif icon == ICON_LADISH_ROOM:
235        return "ICON_LADISH_ROOM"
236    else:
237        return "ICON_???"
238
239def split2str(split):
240    if split == SPLIT_UNDEF:
241        return "SPLIT_UNDEF"
242    elif split == SPLIT_NO:
243        return "SPLIT_NO"
244    elif split == SPLIT_YES:
245        return "SPLIT_YES"
246    else:
247        return "SPLIT_???"
248
249# ------------------------------------------------------------------------------------------------------------
250
251# Global objects
252canvas = Canvas()
253
254options = options_t()
255options.theme_name = getDefaultThemeName()
256options.auto_hide_groups  = False
257options.auto_select_items = False
258options.use_bezier_lines  = True
259options.antialiasing      = ANTIALIASING_SMALL
260options.eyecandy          = EYECANDY_SMALL
261options.inline_displays   = False
262
263features = features_t()
264features.group_info   = False
265features.group_rename = False
266features.port_info    = False
267features.port_rename  = False
268features.handle_group_pos = False
269
270# PatchCanvas API
271def setOptions(new_options):
272    if canvas.initiated: return
273    options.theme_name        = new_options.theme_name
274    options.auto_hide_groups  = new_options.auto_hide_groups
275    options.auto_select_items = new_options.auto_select_items
276    options.use_bezier_lines  = new_options.use_bezier_lines
277    options.antialiasing      = new_options.antialiasing
278    options.eyecandy          = new_options.eyecandy
279    options.inline_displays   = new_options.inline_displays
280
281def setFeatures(new_features):
282    if canvas.initiated: return
283    features.group_info   = new_features.group_info
284    features.group_rename = new_features.group_rename
285    features.port_info    = new_features.port_info
286    features.port_rename  = new_features.port_rename
287    features.handle_group_pos = new_features.handle_group_pos
288