From ee1eb25dfab98734a4152c4321182ebbacf2c8dc Mon Sep 17 00:00:00 2001
From: Stojce Slavkovski <stojce@nextsense.com>
Date: Sun, 22 Dec 2013 18:56:53 +0100
Subject: [PATCH] replace LogDisplay with File logger

 - added abstract logger interface
---
 interface/resources/styles/log_dialog.qss | 10 ++---
 interface/src/AbstractLoggerInterface.h   | 33 +++++++++++++++
 interface/src/Application.cpp             | 17 ++++----
 interface/src/Application.h               |  6 ++-
 interface/src/FileLogger.cpp              | 38 +++++++++++++++++
 interface/src/FileLogger.h                | 28 +++++++++++++
 interface/src/Menu.cpp                    |  2 -
 interface/src/Menu.h                      |  1 -
 interface/src/VoxelHideShowThread.cpp     |  6 +--
 interface/src/VoxelPacketProcessor.cpp    |  3 +-
 interface/src/VoxelSystem.cpp             |  6 +--
 interface/src/ui/LogDialog.cpp            | 51 +++++++++--------------
 interface/src/ui/LogDialog.h              |  8 +++-
 13 files changed, 150 insertions(+), 59 deletions(-)
 create mode 100644 interface/src/AbstractLoggerInterface.h
 create mode 100644 interface/src/FileLogger.cpp
 create mode 100644 interface/src/FileLogger.h

diff --git a/interface/resources/styles/log_dialog.qss b/interface/resources/styles/log_dialog.qss
index ab74f07a33..522ed9c9d6 100644
--- a/interface/resources/styles/log_dialog.qss
+++ b/interface/resources/styles/log_dialog.qss
@@ -12,9 +12,9 @@ QPlainTextEdit {
 QLineEdit {
     padding-left: 7px;
     background-color: #CCCCCC;
-	border-width: 0;
-	border-top-right-radius: 9px;
-	border-bottom-right-radius: 9px;
+    border-width: 0;
+    border-top-right-radius: 9px;
+    border-bottom-right-radius: 9px;
     color: #333333;
     font-size: 12px;
 }
@@ -39,8 +39,8 @@ QPushButton#revealLogButton {
     padding-left: 10px;
     background-color: #333333;
     color: #BBBBBB;
-	border-width: 0;
-	border-radius: 9px;
+    border-width: 0;
+    border-radius: 9px;
     font-size: 11px;
 }
 
diff --git a/interface/src/AbstractLoggerInterface.h b/interface/src/AbstractLoggerInterface.h
new file mode 100644
index 0000000000..13e3aa0ffb
--- /dev/null
+++ b/interface/src/AbstractLoggerInterface.h
@@ -0,0 +1,33 @@
+//
+//  AbstractLoggerInterface.h
+//  interface
+//
+//  Created by Stojce Slavkovski on 12/22/13.
+//  Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
+//
+
+#ifndef __interface__AbstractLoggerInterface__
+#define __interface__AbstractLoggerInterface__
+
+#include <QtCore/QObject>
+#include <QString>
+#include <QStringList>
+
+class AbstractLoggerInterface : public QObject {
+    Q_OBJECT
+
+public:
+    inline bool extraDebugging() { return _extraDebugging; };
+    inline void setExtraDebugging(bool debugging) { _extraDebugging = debugging; };
+
+    virtual void addMessage(QString) = 0;
+    virtual QStringList getLogData(QString) = 0;
+
+signals:
+    void logReceived(QString message);
+
+private:
+    bool _extraDebugging = false;
+};
+
+#endif /* defined(__hifi__AbstractAudioInterface__) */
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 08ffa436dc..a90aaab223 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -88,7 +88,7 @@ const float MIRROR_REARVIEW_BODY_DISTANCE = 1.f;
 
 void messageHandler(QtMsgType type, const QMessageLogContext& context, const QString &message) {
     fprintf(stdout, "%s", message.toLocal8Bit().constData());
-    LogDisplay::instance.addMessage(message.toLocal8Bit().constData());
+    Application::getInstance()->getLogger()->addMessage(message.toLocal8Bit().constData());
 }
 
 Application::Application(int& argc, char** argv, timeval &startup_time) :
@@ -149,6 +149,8 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
     QFontDatabase::addApplicationFont("resources/styles/Inconsolata.otf");
     _window->setWindowTitle("Interface");
 
+    _logger = new FileLogger();
+
     qInstallMessageHandler(messageHandler);
 
     // call Menu getInstance static method to set up the menu
@@ -1346,7 +1348,7 @@ void Application::idle() {
     // Normally we check PipelineWarnings, but since idle will often take more than 10ms we only show these idle timing 
     // details if we're in ExtraDebugging mode. However, the ::update() and it's subcomponents will show their timing 
     // details normally.
-    bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
+    bool showWarnings = getLogger()->extraDebugging();
     PerformanceWarning warn(showWarnings, "Application::idle()");
     
     timeval check;
@@ -2665,7 +2667,7 @@ void Application::queryOctree(NODE_TYPE serverType, PACKET_TYPE packetType, Node
         return;
     }
     
-    bool wantExtraDebugging = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
+    bool wantExtraDebugging = getLogger()->extraDebugging();
     
     // These will be the same for all servers, so we can set them up once and then reuse for each server we send to.
     _voxelQuery.setWantLowResMoving(!Menu::getInstance()->isOptionChecked(MenuOption::DisableLowRes));
@@ -3349,11 +3351,6 @@ void Application::displayOverlay() {
     if (Menu::getInstance()->isOptionChecked(MenuOption::CoverageMap)) {
         renderCoverageMap();
     }
-    
-
-    if (Menu::getInstance()->isOptionChecked(MenuOption::Log)) {
-        LogDisplay::instance.render(_glWidget->width(), _glWidget->height());
-    }
 
     //  Show chat entry field
     if (_chatEntryOn) {
@@ -4356,7 +4353,7 @@ void* Application::networkReceive(void* args) {
                         PerformanceWarning warn(Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings), 
                             "Application::networkReceive()... _voxelProcessor.queueReceivedPacket()");
                             
-                        bool wantExtraDebugging = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
+                        bool wantExtraDebugging = app->getLogger()->extraDebugging();
                         if (wantExtraDebugging && app->_incomingPacket[0] == PACKET_TYPE_VOXEL_DATA) {
                             int numBytesPacketHeader = numBytesForPacketHeader(app->_incomingPacket);
                             unsigned char* dataAt = app->_incomingPacket + numBytesPacketHeader;
@@ -4481,7 +4478,7 @@ void Application::loadScript() {
 
 void Application::toggleLogDialog() {
     if (! _logDialog) {
-        _logDialog = new LogDialog(_glWidget);
+        _logDialog = new LogDialog(_glWidget, getLogger());
         _logDialog->show();
     } else {
         _logDialog->close();
diff --git a/interface/src/Application.h b/interface/src/Application.h
index e917f6d63d..4be944e028 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -69,6 +69,7 @@
 #include "ui/RearMirrorTools.h"
 #include "ui/LodToolsDialog.h"
 #include "ui/LogDialog.h"
+#include "FileLogger.h"
 #include "ParticleTreeRenderer.h"
 #include "ParticleEditHandle.h"
 #include "ControllerScriptingInterface.h"
@@ -201,7 +202,8 @@ public:
     
     VoxelShader& getVoxelShader() { return _voxelShader; }
     PointShader& getPointShader() { return _pointShader; }
-    
+    FileLogger* getLogger() { return _logger; }
+
     glm::vec2 getViewportDimensions() const{ return glm::vec2(_glWidget->width(),_glWidget->height()); }
     NodeToJurisdictionMap& getVoxelServerJurisdictions() { return _voxelServerJurisdictions; }
     NodeToJurisdictionMap& getParticleServerJurisdictions() { return _particleServerJurisdictions; }
@@ -509,6 +511,8 @@ private:
     std::vector<Avatar*> _avatarFades;
     ControllerScriptingInterface _controllerScriptingInterface;
     QPointer<LogDialog> _logDialog;
+
+    FileLogger* _logger;
 };
 
 #endif /* defined(__interface__Application__) */
diff --git a/interface/src/FileLogger.cpp b/interface/src/FileLogger.cpp
new file mode 100644
index 0000000000..4fb9dc64d8
--- /dev/null
+++ b/interface/src/FileLogger.cpp
@@ -0,0 +1,38 @@
+//
+//  FileLogger.cpp
+//  hifi
+//
+//  Created by Stojce Slavkovski on 12/22/13.
+//
+//
+
+#include "FileLogger.h"
+
+FileLogger::FileLogger() : _lines(NULL) {
+    setExtraDebugging(false);
+}
+
+QStringList FileLogger::getLogData(QString searchText) {
+
+    if (searchText.isEmpty()) {
+        return _lines;
+    }
+
+    // wait for adding new log data while iterating over _lines
+//    pthread_mutex_lock(& _mutex);
+    QStringList filteredList;
+    for (int i = 0; i < _lines.size(); ++i) {
+        if (_lines[i].contains(searchText, Qt::CaseInsensitive)) {
+            filteredList.append(_lines[i]);
+        }
+    }
+//    pthread_mutex_unlock(& _mutex);
+    return filteredList;
+}
+
+void FileLogger::addMessage(QString message) {
+    emit logReceived(message);
+    _lines.append(message);
+
+    // TODO: append message to file
+}
\ No newline at end of file
diff --git a/interface/src/FileLogger.h b/interface/src/FileLogger.h
new file mode 100644
index 0000000000..ba44c40005
--- /dev/null
+++ b/interface/src/FileLogger.h
@@ -0,0 +1,28 @@
+//
+//  FileLogger.h
+//  hifi
+//
+//  Created by Stojce Slavkovski on 12/22/13.
+//
+//
+
+#ifndef hifi_FileLogger_h
+#define hifi_FileLogger_h
+
+#include "AbstractLoggerInterface.h"
+
+class FileLogger : public AbstractLoggerInterface {
+    Q_OBJECT
+
+public:
+    FileLogger();
+
+    virtual void addMessage(QString);
+    virtual QStringList getLogData(QString);
+
+private:
+    QStringList _lines;
+
+};
+
+#endif
diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp
index 98b807fea2..40f0c4b415 100644
--- a/interface/src/Menu.cpp
+++ b/interface/src/Menu.cpp
@@ -497,8 +497,6 @@ Menu::Menu() :
     addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoServerAudio);
     addCheckableActionToQMenuAndActionHash(audioDebugMenu, MenuOption::EchoLocalAudio);
 
-
-    addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::ExtraDebugging);
     addActionToQMenuAndActionHash(developerMenu, MenuOption::PasteToVoxel, 
                 Qt::CTRL | Qt::SHIFT | Qt::Key_V, 
                 this,
diff --git a/interface/src/Menu.h b/interface/src/Menu.h
index 73bb0472b4..ac33fc2d0e 100644
--- a/interface/src/Menu.h
+++ b/interface/src/Menu.h
@@ -183,7 +183,6 @@ namespace MenuOption {
     const QString EchoServerAudio = "Echo Server Audio";
     const QString EchoLocalAudio = "Echo Local Audio";
     const QString ExportVoxels = "Export Voxels";
-    const QString ExtraDebugging = "Extra Debugging";
     const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes";
     const QString HeadMouse = "Head Mouse";
     const QString FaceMode = "Cycle Face Mode";
diff --git a/interface/src/VoxelHideShowThread.cpp b/interface/src/VoxelHideShowThread.cpp
index bb64dd5b08..eba4e36a86 100644
--- a/interface/src/VoxelHideShowThread.cpp
+++ b/interface/src/VoxelHideShowThread.cpp
@@ -13,7 +13,7 @@
 #include <PerfStat.h>
 #include <SharedUtil.h>
 
-#include "Menu.h"
+#include "Application.h"
 #include "VoxelHideShowThread.h"
 
 VoxelHideShowThread::VoxelHideShowThread(VoxelSystem* theSystem) :
@@ -30,8 +30,8 @@ bool VoxelHideShowThread::process() {
     _theSystem->checkForCulling();
     uint64_t end = usecTimestampNow();
     uint64_t elapsed = end - start;
-    
-    bool showExtraDebugging = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
+
+    bool showExtraDebugging = Application::getInstance()->getLogger()->extraDebugging();
     if (showExtraDebugging && elapsed > USECS_PER_FRAME) {
         printf("VoxelHideShowThread::process()... checkForCulling took %llu\n", elapsed);
     }
diff --git a/interface/src/VoxelPacketProcessor.cpp b/interface/src/VoxelPacketProcessor.cpp
index 2b78ff73d6..6e5d4ca85c 100644
--- a/interface/src/VoxelPacketProcessor.cpp
+++ b/interface/src/VoxelPacketProcessor.cpp
@@ -19,7 +19,8 @@ void VoxelPacketProcessor::processPacket(const HifiSockAddr& senderSockAddr, uns
                             "VoxelPacketProcessor::processPacket()");
                             
     const int WAY_BEHIND = 300;
-    if (packetsToProcessCount() > WAY_BEHIND && Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)) {
+
+    if (packetsToProcessCount() > WAY_BEHIND && Application::getInstance()->getLogger()->extraDebugging()) {
         qDebug("VoxelPacketProcessor::processPacket() packets to process=%d\n", packetsToProcessCount());
     }
     ssize_t messageLength = packetLength;
diff --git a/interface/src/VoxelSystem.cpp b/interface/src/VoxelSystem.cpp
index f5a2a99ffb..d7c10215f8 100644
--- a/interface/src/VoxelSystem.cpp
+++ b/interface/src/VoxelSystem.cpp
@@ -612,7 +612,7 @@ int VoxelSystem::parseData(unsigned char* sourceBuffer, int numBytes) {
                     lockTree();
                     VoxelPacketData packetData(packetIsCompressed);
                     packetData.loadFinalizedContent(dataAt, sectionLength);
-                    if (Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)) {
+                    if (Application::getInstance()->getLogger()->extraDebugging()) {
                         qDebug("VoxelSystem::parseData() ... Got Packet Section"
                                " color:%s compressed:%s sequence: %u flight:%d usec size:%d data:%d"
                                " subsection:%d sectionLength:%d uncompressed:%d\n",
@@ -973,7 +973,7 @@ int VoxelSystem::updateNodeInArrays(VoxelTreeElement* node, bool reuseIndex, boo
         // not render these Voxels. We need to think about ways to keep the entire scene intact but maybe lower quality
         // possibly shifting down to lower LOD or something. This debug message is to help identify, if/when/how this
         // state actually occurs.
-        if (Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging)) {
+        if (Application::getInstance()->getLogger()->extraDebugging()) {
             qDebug("OHHHH NOOOOOO!!!! updateNodeInArrays() BAILING (_voxelsInWriteArrays >= _maxVoxels)\n");
         }
         return 0;
@@ -1964,7 +1964,7 @@ void VoxelSystem::hideOutOfView(bool forceFullFrustum) {
         setupNewVoxelsForDrawingSingleNode(DONT_BAIL_EARLY);
     }
     
-    bool extraDebugDetails = Menu::getInstance()->isOptionChecked(MenuOption::ExtraDebugging);
+    bool extraDebugDetails = Application::getInstance()->getLogger()->extraDebugging();
     if (extraDebugDetails) {
         qDebug("hideOutOfView() scanned=%ld removed=%ld inside=%ld intersect=%ld outside=%ld\n", 
                 args.nodesScanned, args.nodesRemoved, args.nodesInside, 
diff --git a/interface/src/ui/LogDialog.cpp b/interface/src/ui/LogDialog.cpp
index 4d67e4ae00..4f211c5edc 100644
--- a/interface/src/ui/LogDialog.cpp
+++ b/interface/src/ui/LogDialog.cpp
@@ -12,7 +12,6 @@
 
 #include "SharedUtil.h"
 #include "ui/LogDialog.h"
-#include "LogDisplay.h"
 
 const int TOP_BAR_HEIGHT = 46;
 const int INITIAL_WIDTH = 720;
@@ -27,11 +26,12 @@ const int CHECKBOX_WIDTH = 140;
 const int REVEAL_BUTTON_WIDTH = 122;
 const float INITIAL_HEIGHT_RATIO = 0.6f;
 
-int cursorMeta = qRegisterMetaType<QTextCursor>("QTextCursor");
-int blockMeta = qRegisterMetaType<QTextBlock>("QTextBlock");
+int qTextCursorMeta = qRegisterMetaType<QTextCursor>("QTextCursor");
+int qTextBlockMeta = qRegisterMetaType<QTextBlock>("QTextBlock");
 
-LogDialog::LogDialog(QWidget* parent) : QDialog(parent, Qt::Dialog) {
+LogDialog::LogDialog(QWidget* parent, AbstractLoggerInterface* logger) : QDialog(parent, Qt::Dialog) {
 
+    _logger = logger;
     setWindowTitle("Log");
     setAttribute(Qt::WA_DeleteOnClose);
 
@@ -72,10 +72,13 @@ void LogDialog::initControls() {
     _searchTextBox->setGeometry(left, ELEMENT_MARGIN, SEARCH_TEXT_WIDTH, ELEMENT_HEIGHT);
     left += SEARCH_TEXT_WIDTH + CHECKBOX_MARGIN;
     _searchTextBox->show();
-    connect(_searchTextBox, SIGNAL(textChanged(QString)), SLOT(handleSeachTextChanged(QString)));
+    connect(_searchTextBox, SIGNAL(textChanged(QString)), SLOT(handleSearchTextChanged(QString)));
 
     _extraDebuggingBox = new QCheckBox("Extra debugging", this);
     _extraDebuggingBox->setGeometry(left, ELEMENT_MARGIN, CHECKBOX_WIDTH, ELEMENT_HEIGHT);
+    if (_logger->extraDebugging()) {
+        _extraDebuggingBox->setCheckState(Qt::Checked);
+    }
     _extraDebuggingBox->show();
     connect(_extraDebuggingBox, SIGNAL(stateChanged(int)), SLOT(handleExtraDebuggingCheckbox(int)));
 
@@ -91,22 +94,12 @@ void LogDialog::initControls() {
 
 }
 
-void LogDialog::showEvent(QShowEvent *e)  {
-    _searchTextBox->clear();
-    _searchTextBox->setText("");
-
-/*    pthread_mutex_lock(& _mutex);
-    QStringList _logData = LogDisplay::instance.getLogData("");
-
-    connect(&LogDisplay::instance, &LogDisplay::logReceived, this, &LogDialog::appendLogLine);
-    for(int i = 0; i < _logData.size(); ++i) {
-        appendLogLine(_logData[i]);
-    }
-
-    pthread_mutex_unlock(& _mutex);*/
+void LogDialog::showEvent(QShowEvent*)  {
+    connect(_logger, SIGNAL(logReceived(QString)), this, SLOT(appendLogLine(QString)));
+    handleSearchTextChanged("");
 }
 
-void LogDialog::resizeEvent(QResizeEvent *e) {
+void LogDialog::resizeEvent(QResizeEvent*) {
     _logTextBox->setGeometry(0, TOP_BAR_HEIGHT, width(), height() - TOP_BAR_HEIGHT);
     _revealLogButton->setGeometry(width() - ELEMENT_MARGIN - REVEAL_BUTTON_WIDTH,
                                   ELEMENT_MARGIN,
@@ -117,7 +110,9 @@ void LogDialog::resizeEvent(QResizeEvent *e) {
 void LogDialog::appendLogLine(QString logLine) {
     if (isVisible()) {
         pthread_mutex_lock(& _mutex);
-        _logTextBox->appendPlainText(logLine.simplified());
+
+        QString line = logLine.replace(QRegExp("node"), "<b>node</b>");
+        _logTextBox->appendHtml(line);
         pthread_mutex_unlock(& _mutex);
         _logTextBox->ensureCursorVisible();
     }
@@ -131,22 +126,16 @@ void LogDialog::handleRevealButton() {
 
 }
 
-void LogDialog::handleExtraDebuggingCheckbox(int state) {
-
+void LogDialog::handleExtraDebuggingCheckbox(const int state) {
+    _logger->setExtraDebugging(state != 0);
 }
 
-void LogDialog::handleSeachTextChanged(QString searchText) {
-
-    if (searchText.isEmpty()) {
-        connect(&LogDisplay::instance, &LogDisplay::logReceived, this, &LogDialog::appendLogLine);
-    } else {
-        disconnect(&LogDisplay::instance, &LogDisplay::logReceived, this, &LogDialog::appendLogLine);
-    }
+void LogDialog::handleSearchTextChanged(const QString searchText) {
 
     _logTextBox->clear();
     pthread_mutex_lock(& _mutex);
-    QStringList _logData = LogDisplay::instance.getLogData(searchText);
-    for(int i = 0; i < _logData.size(); ++i) {
+    QStringList _logData = _logger->getLogData(searchText);
+    for (int i = 0; i < _logData.size(); ++i) {
         appendLogLine(_logData[i]);
     }
 
diff --git a/interface/src/ui/LogDialog.h b/interface/src/ui/LogDialog.h
index 741d4b1845..ef3d125cb7 100644
--- a/interface/src/ui/LogDialog.h
+++ b/interface/src/ui/LogDialog.h
@@ -15,11 +15,13 @@
 #include <QPushButton>
 #include <QCheckBox>
 
+#include "AbstractLoggerInterface.h"
+
 class LogDialog : public QDialog {
     Q_OBJECT
 
 public:
-    LogDialog(QWidget* parent);
+    LogDialog(QWidget*, AbstractLoggerInterface*);
     ~LogDialog();
 
 public slots:
@@ -29,7 +31,7 @@ private slots:
     void handleSearchButton();
     void handleRevealButton();
     void handleExtraDebuggingCheckbox(const int);
-    void handleSeachTextChanged(QString);
+    void handleSearchTextChanged(const QString);
 
 protected:
     void resizeEvent(QResizeEvent*);
@@ -43,6 +45,8 @@ private:
     QPlainTextEdit* _logTextBox;
     pthread_mutex_t _mutex;
 
+    AbstractLoggerInterface* _logger;
+
     void initControls();
 };