1#!/usr/local/bin/python3.8
2# -*- coding: utf-8 -*-
3
4# Common/Shared code
5# Copyright (C) 2010-2018 Filipe Coelho <falktx@falktx.com>
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# 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 COPYING file
18
19# ------------------------------------------------------------------------------------------------------------
20# Imports (Global)
21
22import os
23import sys
24from codecs import open as codecopen
25from unicodedata import normalize
26
27if True:
28    from PyQt5.QtCore import pyqtSignal, qWarning
29    from PyQt5.QtGui import QIcon
30    from PyQt5.QtWidgets import QApplication, QFileDialog, QMessageBox
31else:
32    from PyQt4.QtCore import pyqtSignal, qWarning
33    from PyQt4.QtGui import QIcon
34    from PyQt4.QtGui import QApplication, QFileDialog, QMessageBox
35
36# ------------------------------------------------------------------------------------------------------------
37# Set Platform
38
39if sys.platform == "darwin":
40    from PyQt5.QtGui import qt_mac_set_menubar_icons
41    qt_mac_set_menubar_icons(False)
42    HAIKU   = False
43    LINUX   = False
44    MACOS   = True
45    WINDOWS = False
46elif "haiku" in sys.platform:
47    HAIKU   = True
48    LINUX   = False
49    MACOS   = False
50    WINDOWS = False
51elif "linux" in sys.platform:
52    HAIKU   = False
53    LINUX   = True
54    MACOS   = False
55    WINDOWS = False
56elif sys.platform in ("win32", "win64", "cygwin"):
57    WINDIR  = os.getenv("WINDIR")
58    HAIKU   = False
59    LINUX   = False
60    MACOS   = False
61    WINDOWS = True
62else:
63    HAIKU   = False
64    LINUX   = False
65    MACOS   = False
66    WINDOWS = False
67
68# ------------------------------------------------------------------------------------------------------------
69# Try Import Signal
70
71try:
72    from signal import signal, SIGINT, SIGTERM, SIGUSR1, SIGUSR2
73    haveSignal = True
74except:
75    haveSignal = False
76
77# ------------------------------------------------------------------------------------------------------------
78# Safe exception hook, needed for PyQt5
79
80def sys_excepthook(typ, value, tback):
81    return sys.__excepthook__(typ, value, tback)
82
83sys.excepthook = sys_excepthook
84
85# ------------------------------------------------------------------------------------------------------------
86# Set Version
87
88VERSION = "0.9.0"
89
90# ------------------------------------------------------------------------------------------------------------
91# Set Debug mode
92
93DEBUG = bool("-d" in sys.argv or "-debug" in sys.argv or "--debug" in sys.argv)
94
95# ------------------------------------------------------------------------------------------------------------
96# Global variables
97
98global gGui
99gGui = None
100
101# ------------------------------------------------------------------------------------------------------------
102# Set TMP
103
104TMP = os.getenv("TMP")
105
106if TMP is None:
107    if WINDOWS:
108        qWarning("TMP variable not set")
109        TMP = os.path.join(WINDIR, "temp")
110    else:
111        TMP = "/tmp"
112
113# ------------------------------------------------------------------------------------------------------------
114# Set HOME
115
116HOME = os.getenv("HOME")
117
118if HOME is None:
119    HOME = os.path.expanduser("~")
120
121    if not WINDOWS:
122        qWarning("HOME variable not set")
123
124if not os.path.exists(HOME):
125    qWarning("HOME does not exist")
126    HOME = TMP
127
128# ------------------------------------------------------------------------------------------------------------
129# Set PATH
130
131PATH = os.getenv("PATH")
132
133if PATH is None:
134    qWarning("PATH variable not set")
135
136    if MACOS:
137        PATH = ("/opt/local/bin", "/usr/local/bin", "/usr/bin", "/bin")
138    elif WINDOWS:
139        PATH = (os.path.join(WINDIR, "system32"), WINDIR)
140    else:
141        PATH = ("/usr/local/bin", "/usr/bin", "/bin")
142
143else:
144    PATH = PATH.split(os.pathsep)
145
146# ------------------------------------------------------------------------------------------------------------
147# Remove/convert non-ascii chars from a string
148
149def asciiString(string):
150    return normalize("NFKD", string).encode("ascii", "ignore").decode("utf-8")
151
152# ------------------------------------------------------------------------------------------------------------
153# Convert a ctypes c_char_p into a python string
154
155def cString(value):
156    if not value:
157        return ""
158    if isinstance(value, str):
159        return value
160    return value.decode("utf-8", errors="ignore")
161
162# ------------------------------------------------------------------------------------------------------------
163# Check if a value is a number (float support)
164
165def isNumber(value):
166    try:
167        float(value)
168        return True
169    except:
170        return False
171
172# ------------------------------------------------------------------------------------------------------------
173# Convert a value to a list
174
175def toList(value):
176    if value is None:
177        return []
178    elif not isinstance(value, list):
179        return [value]
180    else:
181        return value
182
183# ------------------------------------------------------------------------------------------------------------
184# Unicode open
185
186def uopen(filename, mode="r"):
187    return codecopen(filename, encoding="utf-8", mode=mode)
188
189# ------------------------------------------------------------------------------------------------------------
190# QLineEdit and QPushButton combo
191
192def getAndSetPath(self_, currentPath, lineEdit):
193    newPath = QFileDialog.getExistingDirectory(self_, self_.tr("Set Path"), currentPath, QFileDialog.ShowDirsOnly)
194    if newPath:
195        lineEdit.setText(newPath)
196    return newPath
197
198# ------------------------------------------------------------------------------------------------------------
199# Get Icon from user theme, using our own as backup (Oxygen)
200
201def getIcon(icon, size=16):
202    return QIcon.fromTheme(icon, QIcon(":/%ix%i/%s.png" % (size, size, icon)))
203
204# ------------------------------------------------------------------------------------------------------------
205# Custom MessageBox
206
207def CustomMessageBox(self_, icon, title, text, extraText="", buttons=QMessageBox.Yes|QMessageBox.No, defButton=QMessageBox.No):
208    msgBox = QMessageBox(self_)
209    msgBox.setIcon(icon)
210    msgBox.setWindowTitle(title)
211    msgBox.setText(text)
212    msgBox.setInformativeText(extraText)
213    msgBox.setStandardButtons(buttons)
214    msgBox.setDefaultButton(defButton)
215    return msgBox.exec_()
216
217# ------------------------------------------------------------------------------------------------------------
218# Signal handler
219
220def setUpSignals(self_):
221    global gGui
222
223    if gGui is None:
224        gGui = self_
225
226    if not haveSignal:
227        return
228
229    signal(SIGINT,  signalHandler)
230    signal(SIGTERM, signalHandler)
231    signal(SIGUSR1, signalHandler)
232    signal(SIGUSR2, signalHandler)
233
234    gGui.SIGTERM.connect(closeWindowHandler)
235    gGui.SIGUSR2.connect(showWindowHandler)
236
237def signalHandler(sig, frame):
238    global gGui
239
240    if gGui is None:
241        return
242
243    if sig in (SIGINT, SIGTERM):
244        gGui.SIGTERM.emit()
245    elif sig == SIGUSR1:
246        gGui.SIGUSR1.emit()
247    elif sig == SIGUSR2:
248        gGui.SIGUSR2.emit()
249
250def closeWindowHandler():
251    global gGui
252
253    if gGui is None:
254        return
255
256    gGui.hide()
257    gGui.close()
258    QApplication.instance().quit()
259
260    gGui = None
261
262def showWindowHandler():
263    global gGui
264
265    if gGui is None:
266        return
267
268    if gGui.isMaximized():
269        gGui.showMaximized()
270    else:
271        gGui.showNormal()
272
273# ------------------------------------------------------------------------------------------------------------
274# Shared Icons
275
276def setIcons(self_, modes):
277    global gGui
278
279    if gGui is None:
280        gGui = self_
281
282    if "canvas" in modes:
283        gGui.ui.act_canvas_arrange.setIcon(getIcon("view-sort-ascending"))
284        gGui.ui.act_canvas_refresh.setIcon(getIcon("view-refresh"))
285        gGui.ui.act_canvas_zoom_fit.setIcon(getIcon("zoom-fit-best"))
286        gGui.ui.act_canvas_zoom_in.setIcon(getIcon("zoom-in"))
287        gGui.ui.act_canvas_zoom_out.setIcon(getIcon("zoom-out"))
288        gGui.ui.act_canvas_zoom_100.setIcon(getIcon("zoom-original"))
289        gGui.ui.b_canvas_zoom_fit.setIcon(getIcon("zoom-fit-best"))
290        gGui.ui.b_canvas_zoom_in.setIcon(getIcon("zoom-in"))
291        gGui.ui.b_canvas_zoom_out.setIcon(getIcon("zoom-out"))
292        gGui.ui.b_canvas_zoom_100.setIcon(getIcon("zoom-original"))
293
294    if "jack" in modes:
295        gGui.ui.act_jack_clear_xruns.setIcon(getIcon("edit-clear"))
296        gGui.ui.act_jack_configure.setIcon(getIcon("configure"))
297        gGui.ui.act_jack_render.setIcon(getIcon("media-record"))
298        gGui.ui.b_jack_clear_xruns.setIcon(getIcon("edit-clear"))
299        gGui.ui.b_jack_configure.setIcon(getIcon("configure"))
300        gGui.ui.b_jack_render.setIcon(getIcon("media-record"))
301
302    if "transport" in modes:
303        gGui.ui.act_transport_play.setIcon(getIcon("media-playback-start"))
304        gGui.ui.act_transport_stop.setIcon(getIcon("media-playback-stop"))
305        gGui.ui.act_transport_backwards.setIcon(getIcon("media-seek-backward"))
306        gGui.ui.act_transport_forwards.setIcon(getIcon("media-seek-forward"))
307        gGui.ui.b_transport_play.setIcon(getIcon("media-playback-start"))
308        gGui.ui.b_transport_stop.setIcon(getIcon("media-playback-stop"))
309        gGui.ui.b_transport_backwards.setIcon(getIcon("media-seek-backward"))
310        gGui.ui.b_transport_forwards.setIcon(getIcon("media-seek-forward"))
311
312    if "misc" in modes:
313        gGui.ui.act_quit.setIcon(getIcon("application-exit"))
314        gGui.ui.act_configure.setIcon(getIcon("configure"))
315