diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..1323064
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,34 @@
+#!/usr/bin/make -f
+# Makefile for Cadence #
+# ---------------------- #
+# Created by falkTX
+#
+
+PYUIC = pyuic4
+PYRCC = pyrcc4
+
+all: build
+
+build: UI RES LANG
+
+UI: tools
+
+tools: \
+ src/ui_logs.py
+
+src/ui_logs.py: src/ui/logs.ui
+ $(PYUIC) -w -o src/ui_logs.py $<
+
+RES: src/icons_rc.py
+
+src/icons_rc.py: src/icons/icons.qrc
+ $(PYRCC) -py3 -o src/icons_rc.py $<
+
+LANG:
+# pylupdate4 -verbose src/lang/lang.pro
+# lrelease src/lang/lang.pro
+
+clean:
+ rm -f *~ src/*~ src/*.pyc src/*.so src/ui_*.py src/icons_rc.py
+
+distclean: clean
diff --git a/README b/README
index 717a90b..0dabe6c 100644
--- a/README
+++ b/README
@@ -3,4 +3,4 @@
-----------------------
Cadence is a set of tools useful for audio production.
-It's being developed by falkTX, using Python and Qt (and some C++ when needed).
+It's being developed by falkTX, using Python and Qt (and some C++ where needed).
diff --git a/src/icons/16x16/edit-delete.png b/src/icons/16x16/edit-delete.png
new file mode 100644
index 0000000..4933cfa
Binary files /dev/null and b/src/icons/16x16/edit-delete.png differ
diff --git a/src/icons/16x16/window-close.png b/src/icons/16x16/window-close.png
new file mode 100644
index 0000000..1f0d0e2
Binary files /dev/null and b/src/icons/16x16/window-close.png differ
diff --git a/src/icons/icons.qrc b/src/icons/icons.qrc
new file mode 100644
index 0000000..fe86639
--- /dev/null
+++ b/src/icons/icons.qrc
@@ -0,0 +1,6 @@
+
+
+ 16x16/edit-delete.png
+ 16x16/window-close.png
+
+
diff --git a/src/logs.py b/src/logs.py
new file mode 100644
index 0000000..7955e1d
--- /dev/null
+++ b/src/logs.py
@@ -0,0 +1,366 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+# JACK, A2J, LASH and LADISH Logs Viewer
+# Copyright (C) 2012 Filipe Coelho
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For a full copy of the GNU General Public License see the COPYING file
+
+# Imports (Global)
+from PyQt4.QtCore import pyqtSlot, Qt, QFile, QIODevice, QTextStream, QThread, SIGNAL, SLOT
+from PyQt4.QtGui import QDialog, QPalette, QSyntaxHighlighter
+
+# Imports (Custom Stuff)
+import ui_logs
+from shared import *
+
+# Fix log text output (get rid of terminal colors stuff)
+def fixLogText(text):
+ return text.replace("[1m[31m","").replace("[1m[33m","").replace("[31m","").replace("[33m","").replace("[0m","")
+
+# Syntax Highlighter for JACK
+class SyntaxHighligher_JACK(QSyntaxHighlighter):
+ def __init__(self, parent):
+ QSyntaxHighlighter.__init__(self, parent)
+
+ self.m_palette = self.parent().palette()
+
+ def highlightBlock(self, text):
+ if (": ERROR: " in text):
+ self.setFormat(text.find(" ERROR: "), len(text), Qt.red)
+ elif (": WARNING: " in text):
+ self.setFormat(text.find(" WARNING: "), len(text), Qt.darkRed)
+ elif (": ------------------" in text):
+ self.setFormat(text.find(" ------------------"), len(text), self.m_palette.color(QPalette.Active, QPalette.Mid))
+ elif (": Connecting " in text):
+ self.setFormat(text.find(" Connecting "), len(text), self.m_palette.color(QPalette.Active, QPalette.Link))
+ elif (": Disconnecting " in text):
+ self.setFormat(text.find(" Disconnecting "), len(text), self.m_palette.color(QPalette.Active, QPalette.LinkVisited))
+ #elif (": New client " in text):
+ #self.setFormat(text.find(" New client "), len(text), self.m_palette.color(QPalette.Active, QPalette.Link))
+
+# Syntax Highlighter for A2J
+class SyntaxHighligher_A2J(QSyntaxHighlighter):
+ def __init__(self, parent):
+ QSyntaxHighlighter.__init__(self, parent)
+
+ self.m_palette = self.parent().palette()
+
+ def highlightBlock(self, text):
+ if (": error: " in text):
+ self.setFormat(text.find(" error: "), len(text), Qt.red)
+ elif (": WARNING: " in text):
+ self.setFormat(text.find(" WARNING: "), len(text), Qt.darkRed)
+ elif (": ----------------------------" in text):
+ self.setFormat(text.find("----------------------------"), len(text), self.m_palette.color(QPalette.Active, QPalette.Mid))
+ elif (": port created: " in text):
+ self.setFormat(text.find(" port created: "), len(text), self.m_palette.color(QPalette.Active, QPalette.Link))
+ elif (": port deleted: " in text):
+ self.setFormat(text.find(" port deleted: "), len(text), self.m_palette.color(QPalette.Active, QPalette.LinkVisited))
+
+# Syntax Highlighter for LASH
+class SyntaxHighligher_LASH(QSyntaxHighlighter):
+ def __init__(self, parent):
+ QSyntaxHighlighter.__init__(self, parent)
+
+ self.m_palette = self.parent().palette()
+
+ def highlightBlock(self, text):
+ if (": ERROR: " in text):
+ self.setFormat(text.find(" ERROR: "), len(text), Qt.red)
+ elif (": WARNING: " in text):
+ self.setFormat(text.find(" WARNING: "), len(text), Qt.darkRed)
+ elif (": ------------------" in text):
+ self.setFormat(text.find(" ------------------"), len(text), self.m_palette.color(QPalette.Active, QPalette.Mid))
+
+# Syntax Highlighter for LADISH
+class SyntaxHighligher_LADISH(QSyntaxHighlighter):
+ def __init__(self, parent):
+ QSyntaxHighlighter.__init__(self, parent)
+
+ self.m_palette = self.parent().palette()
+
+ def highlightBlock(self, text):
+ if (": ERROR: " in text):
+ self.setFormat(text.find(" ERROR: "), len(text), Qt.red)
+ elif (": WARNING: " in text):
+ self.setFormat(text.find(" WARNING: "), len(text), Qt.darkRed)
+ elif (": -------" in text):
+ self.setFormat(text.find(" -------"), len(text), self.m_palette.color(QPalette.Active, QPalette.Mid))
+
+# Lockless file read thread
+class LogsReadThread(QThread):
+ def __init__(self, parent):
+ QThread.__init__(self, parent)
+
+ self.m_purgeLogs = False
+
+ # -------------------------------------------------------------
+ # Take some values from parent
+
+ self.LOG_FILE_JACK = self.parent().LOG_FILE_JACK
+ self.LOG_FILE_A2J = self.parent().LOG_FILE_A2J
+ self.LOG_FILE_LASH = self.parent().LOG_FILE_LASH
+ self.LOG_FILE_LADISH = self.parent().LOG_FILE_LADISH
+
+ # -------------------------------------------------------------
+ # Init logs
+
+ if (self.LOG_FILE_JACK):
+ self.log_jack_file = QFile(self.LOG_FILE_JACK)
+ self.log_jack_file.open(QIODevice.ReadOnly)
+ self.log_jack_stream = QTextStream(self.log_jack_file)
+ self.log_jack_stream.setCodec("UTF-8")
+
+ if (self.LOG_FILE_A2J):
+ self.log_a2j_file = QFile(self.LOG_FILE_A2J)
+ self.log_a2j_file.open(QIODevice.ReadOnly)
+ self.log_a2j_stream = QTextStream(self.log_a2j_file)
+ self.log_a2j_stream.setCodec("UTF-8")
+
+ if (self.LOG_FILE_LASH):
+ self.log_lash_file = QFile(self.LOG_FILE_LASH)
+ self.log_lash_file.open(QIODevice.ReadOnly)
+ self.log_lash_stream = QTextStream(self.log_lash_file)
+ self.log_lash_stream.setCodec("UTF-8")
+
+ if (self.LOG_FILE_LADISH):
+ self.log_ladish_file = QFile(self.LOG_FILE_LADISH)
+ self.log_ladish_file.open(QIODevice.ReadOnly)
+ self.log_ladish_stream = QTextStream(self.log_ladish_file)
+ self.log_ladish_stream.setCodec("UTF-8")
+
+ def purgeLogs(self):
+ self.m_purgeLogs = True
+
+ def run(self):
+ # -------------------------------------------------------------
+ # Read logs and set text in main thread
+
+ while (self.isRunning()):
+ if (self.m_purgeLogs):
+ if (self.LOG_FILE_JACK):
+ self.log_jack_stream.flush()
+ self.log_jack_file.close()
+ self.log_jack_file.open(QIODevice.WriteOnly)
+ self.log_jack_file.close()
+ self.log_jack_file.open(QIODevice.ReadOnly)
+
+ if (self.LOG_FILE_A2J):
+ self.log_a2j_stream.flush()
+ self.log_a2j_file.close()
+ self.log_a2j_file.open(QIODevice.WriteOnly)
+ self.log_a2j_file.close()
+ self.log_a2j_file.open(QIODevice.ReadOnly)
+
+ if (self.LOG_FILE_LASH):
+ self.log_lash_stream.flush()
+ self.log_lash_file.close()
+ self.log_lash_file.open(QIODevice.WriteOnly)
+ self.log_lash_file.close()
+ self.log_lash_file.open(QIODevice.ReadOnly)
+
+ if (self.LOG_FILE_LADISH):
+ self.log_ladish_stream.flush()
+ self.log_ladish_file.close()
+ self.log_ladish_file.open(QIODevice.WriteOnly)
+ self.log_ladish_file.close()
+ self.log_ladish_file.open(QIODevice.ReadOnly)
+
+ else:
+ text_jack = ""
+ text_a2j = ""
+ text_lash = ""
+ text_ladish = ""
+
+ if (self.LOG_FILE_JACK):
+ text_jack = fixLogText(self.log_jack_stream.readAll()).strip()
+
+ if (self.LOG_FILE_A2J):
+ text_a2j = fixLogText(self.log_a2j_stream.readAll()).strip()
+
+ if (self.LOG_FILE_LASH):
+ text_lash = fixLogText(self.log_lash_stream.readAll()).strip()
+
+ if (self.LOG_FILE_LADISH):
+ text_ladish = fixLogText(self.log_ladish_stream.readAll()).strip()
+
+ self.parent().setLogsText(text_jack, text_a2j, text_lash, text_ladish)
+ self.emit(SIGNAL("updateLogs()"))
+
+ self.sleep(1)
+
+ # -------------------------------------------------------------
+ # Close logs before closing thread
+
+ if (self.LOG_FILE_JACK):
+ self.log_jack_file.close()
+
+ if (self.LOG_FILE_A2J):
+ self.log_a2j_file.close()
+
+ if (self.LOG_FILE_LASH):
+ self.log_lash_file.close()
+
+ if (self.LOG_FILE_LADISH):
+ self.log_ladish_file.close()
+
+# Class Window
+class LogsW(QDialog, ui_logs.Ui_LogsW):
+
+ LOG_PATH = os.path.join(HOME, ".log")
+
+ LOG_FILE_JACK = os.path.join(LOG_PATH, "jack", "jackdbus.log")
+ LOG_FILE_A2J = os.path.join(LOG_PATH, "a2j", "a2j.log")
+ LOG_FILE_LASH = os.path.join(LOG_PATH, "lash", "lash.log")
+ LOG_FILE_LADISH = os.path.join(LOG_PATH, "ladish", "ladish.log")
+
+ def __init__(self, parent, flags):
+ QDialog.__init__(self, parent, flags)
+ self.setupUi(self)
+
+ self.b_close.setIcon(getIcon("dialog-close"))
+ self.b_purge.setIcon(getIcon("user-trash"))
+
+ self.m_firstRun = True
+ self.m_text_jack = ""
+ self.m_text_a2j = ""
+ self.m_text_lash = ""
+ self.m_text_ladish = ""
+
+ # -------------------------------------------------------------
+ # Check for unexisting logs and remove tabs for those
+
+ tab_index = 0
+
+ if (os.path.exists(self.LOG_FILE_JACK) == False):
+ self.LOG_FILE_JACK = None
+ self.tabWidget.removeTab(0-tab_index)
+ tab_index += 1
+
+ if (os.path.exists(self.LOG_FILE_A2J) == False):
+ self.LOG_FILE_A2J = None
+ self.tabWidget.removeTab(1-tab_index)
+ tab_index += 1
+
+ if (os.path.exists(self.LOG_FILE_LASH) == False):
+ self.LOG_FILE_LASH = None
+ self.tabWidget.removeTab(2-tab_index)
+ tab_index += 1
+
+ if (os.path.exists(self.LOG_FILE_LADISH) == False):
+ self.LOG_FILE_LADISH = None
+ self.tabWidget.removeTab(3-tab_index)
+ tab_index += 1
+
+ # -------------------------------------------------------------
+ # Init logs viewers
+
+ if (self.LOG_FILE_JACK):
+ syntax_jack = SyntaxHighligher_JACK(self.pte_jack)
+ syntax_jack.setDocument(self.pte_jack.document())
+
+ if (self.LOG_FILE_A2J):
+ syntax_a2j = SyntaxHighligher_A2J(self.pte_a2j)
+ syntax_a2j.setDocument(self.pte_a2j.document())
+
+ if (self.LOG_FILE_LASH):
+ syntax_lash = SyntaxHighligher_LASH(self.pte_lash)
+ syntax_lash.setDocument(self.pte_lash.document())
+
+ if (self.LOG_FILE_LADISH):
+ syntax_ladish = SyntaxHighligher_LADISH(self.pte_ladish)
+ syntax_ladish.setDocument(self.pte_ladish.document())
+
+ # -------------------------------------------------------------
+ # Init file read thread
+
+ self.m_readThread = LogsReadThread(self)
+ self.m_readThread.start()
+
+ # -------------------------------------------------------------
+ # Set-up connections
+
+ self.connect(self.b_purge, SIGNAL("clicked()"), SLOT("slot_purgeLogs()"))
+ self.connect(self.m_readThread, SIGNAL("updateLogs()"), SLOT("slot_updateLogs()"))
+
+ def setLogsText(self, text_jack, text_a2j, text_lash, text_ladish):
+ self.m_text_jack = text_jack
+ self.m_text_a2j = text_a2j
+ self.m_text_lash = text_lash
+ self.m_text_ladish = text_ladish
+
+ @pyqtSlot()
+ def slot_updateLogs(self):
+ if (self.m_firstRun):
+ self.pte_jack.clear()
+ self.pte_a2j.clear()
+ self.pte_lash.clear()
+ self.pte_ladish.clear()
+
+ if (self.LOG_FILE_JACK):
+ if (self.m_text_jack):
+ self.pte_jack.appendPlainText(self.m_text_jack)
+
+ if (self.LOG_FILE_A2J):
+ if (self.m_text_a2j):
+ self.pte_a2j.appendPlainText(self.m_text_a2j)
+
+ if (self.LOG_FILE_LASH):
+ if (self.m_text_lash):
+ self.pte_lash.appendPlainText(self.m_text_lash)
+
+ if (self.LOG_FILE_LADISH):
+ if (self.m_text_ladish):
+ self.pte_ladish.appendPlainText(self.m_text_ladish)
+
+ if (self.m_firstRun):
+ self.pte_jack.horizontalScrollBar().setValue(0)
+ self.pte_jack.verticalScrollBar().setValue(self.pte_jack.verticalScrollBar().maximum())
+ self.pte_a2j.horizontalScrollBar().setValue(0)
+ self.pte_a2j.verticalScrollBar().setValue(self.pte_a2j.verticalScrollBar().maximum())
+ self.pte_lash.horizontalScrollBar().setValue(0)
+ self.pte_lash.verticalScrollBar().setValue(self.pte_lash.verticalScrollBar().maximum())
+ self.pte_ladish.horizontalScrollBar().setValue(0)
+ self.pte_ladish.verticalScrollBar().setValue(self.pte_ladish.verticalScrollBar().maximum())
+ self.m_firstRun = False
+
+ @pyqtSlot()
+ def slot_purgeLogs(self):
+ self.m_readThread.purgeLogs()
+ self.pte_jack.clear()
+ self.pte_a2j.clear()
+ self.pte_lash.clear()
+ self.pte_ladish.clear()
+
+ def closeEvent(self, event):
+ self.m_readThread.quit()
+ return QDialog.closeEvent(self, event)
+
+# -------------------------------------------------------------
+# Allow to use this as a standalone app
+if __name__ == '__main__':
+
+ # Additional imports
+ import sys
+ from PyQt4.QtGui import QApplication
+
+ # App initialization
+ app = QApplication(sys.argv)
+
+ # Show GUI
+ gui = LogsW(None, Qt.WindowFlags())
+ gui.show()
+
+ # App-Loop
+ sys.exit(app.exec_())
diff --git a/src/shared.py b/src/shared.py
index fdeaa42..d9c9fc9 100644
--- a/src/shared.py
+++ b/src/shared.py
@@ -15,3 +15,29 @@
# GNU General Public License for more details.
#
# For a full copy of the GNU General Public License see the COPYING file
+
+# Imports (Global)
+import os
+from PyQt4.QtCore import qDebug, qWarning
+from PyQt4.QtGui import QIcon
+
+# Small integrity tests
+HOME = os.getenv("HOME")
+if (HOME == None):
+ qWarning("HOME variable not set")
+ HOME = "/tmp"
+elif (os.path.exists(HOME) == False):
+ qWarning("HOME variable set but not valid")
+ HOME = "/tmp"
+
+PATH_env = os.getenv("PATH")
+if (PATH_env == None):
+ qWarning("PATH variable not set")
+ PATH = ("/bin", "/sbin", "/usr/local/bin", "/usr/local/sbin", "/usr/bin", "/usr/sbin", "/usr/games")
+else:
+ PATH = PATH_env.split(os.pathsep)
+ del PATH_env
+
+# Get Icon from user theme, using our own as backup (Oxygen)
+def getIcon(icon, size=16):
+ return QIcon.fromTheme(icon, QIcon(":/%ix%i/%s.png" % (size, size, icon)))
diff --git a/src/ui/logs.ui b/src/ui/logs.ui
new file mode 100644
index 0000000..5d4aa5b
--- /dev/null
+++ b/src/ui/logs.ui
@@ -0,0 +1,181 @@
+
+
+ LogsW
+
+
+
+ 0
+ 0
+ 712
+ 414
+
+
+
+ Logs
+
+
+ -
+
+
+ 0
+
+
+
+ JACK
+
+
+
-
+
+
+ Qt::ScrollBarAlwaysOn
+
+
+ QPlainTextEdit::NoWrap
+
+
+ Loading...
+
+
+ Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
+
+
+
+
+
+
+
+ A2J
+
+
+ -
+
+
+ Qt::ScrollBarAlwaysOn
+
+
+ QPlainTextEdit::NoWrap
+
+
+ Loading...
+
+
+ Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
+
+
+
+
+
+
+
+ LASH
+
+
+ -
+
+
+ Qt::ScrollBarAlwaysOn
+
+
+ QPlainTextEdit::NoWrap
+
+
+ Loading...
+
+
+ Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
+
+
+
+
+
+
+
+ LADISH
+
+
+ -
+
+
+ Qt::ScrollBarAlwaysOn
+
+
+ QPlainTextEdit::NoWrap
+
+
+ Loading...
+
+
+ Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse
+
+
+
+
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ -
+
+
+ Purge all logs
+
+
+
+ :/16x16/edit-delete.png:/16x16/edit-delete.png
+
+
+
+ -
+
+
+ Close
+
+
+
+ :/16x16/window-close.png:/16x16/window-close.png
+
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+
+
+
+
+
+
+
+ b_close
+ clicked()
+ LogsW
+ accept()
+
+
+ 665
+ 395
+
+
+ 355
+ 206
+
+
+
+
+