This commit is contained in:
Philip Rosedale 2014-11-18 21:58:40 -08:00
commit 9afaf8f6d8
16 changed files with 290 additions and 86 deletions

View file

@ -21,7 +21,7 @@
#include <NodeList.h>
#include <PacketHeaders.h>
#include <SharedUtil.h>
#include <SoundCache.h>
#include "AssignmentFactory.h"
#include "AssignmentThread.h"
@ -122,7 +122,9 @@ AssignmentClient::AssignmentClient(int &argc, char **argv) :
connect(&AccountManager::getInstance(), &AccountManager::authRequired,
this, &AssignmentClient::handleAuthenticationRequest);
// Create Singleton objects on main thread
NetworkAccessManager::getInstance();
SoundCache::getInstance();
}
void AssignmentClient::sendAssignmentRequest() {

View file

@ -0,0 +1,42 @@
//
// ambiance.js
// examples
//
// Created by Clément Brisset on 11/18/14.
// Copyright 2014 High Fidelity, Inc.
//
// Distributed under the Apache License, Version 2.0.
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
var soundURL = "https://s3.amazonaws.com/hifi-public/sounds/08_Funny_Bone.wav";
var position = { x: 700, y: 25, z: 725 };
var audioOptions = {
position: position,
volume: 0.4,
loop: true,
stereo: false
};
var sound = SoundCache.getSound(soundURL, audioOptions.isStereo);
var injector = null;
var count = 100;
Script.update.connect(function() {
if (count > 0) {
count--;
return;
}
if (sound.downloaded && injector === null) {
print("Sound downloaded.");
injector = Audio.playSound(sound, audioOptions);
print("Playing: " + injector);
}
});
Script.scriptEnding.connect(function() {
if (injector !== null) {
injector.stop();
}
});

View file

@ -15,17 +15,43 @@
this.graboffset = null;
this.clickedAt = null;
this.firstHolding = true;
this.clickedX = -1;
this.clickedY = -1;
this.clicked = { x: -1, y: -1};
this.lastMovedPosition = { x: -1, y: -1};
this.lastMovedTime = 0;
this.rotateOverlayTarget = null;
this.rotateOverlayInner = null;
this.rotateOverlayOuter = null;
this.rotateOverlayCurrent = null;
this.rotateMode = false;
this.originalRotation = null;
this.sound = null;
this.moveSoundURLS = [
"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove1.wav",
"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove2.wav",
"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove3.wav"
];
this.turnSoundURLS = [
"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove1.wav",
"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove2.wav",
"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureMove3.wav"
// TODO: determine if these or other turn sounds work better than move sounds.
//"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureTurn1.wav",
//"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureTurn2.wav",
//"http://public.highfidelity.io/sounds/MovingFurniture/FurnitureTurn3.wav"
];
this.moveSounds = new Array();
this.turnSounds = new Array();
this.moveSound = null;
this.turnSound = null;
this.injector = null;
var debug = false;
var displayRotateTargets = true; // change to false if you don't want the rotate targets
var rotateOverlayTargetSize = 10000; // really big target
var innerSnapAngle = 22.5; // the angle which we snap to on the inner rotation tool
var innerRadius;
@ -34,25 +60,62 @@
var yawZero;
var rotationNormal;
var yawNormal;
var stopSoundDelay = 100; // number of msecs of not moving to have sound stop
var debug = true;
this.getRandomInt = function(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
// Download sound if needed
this.maybeDownloadSound = function() {
if (this.sound === null) {
this.sound = SoundCache.getSound("http://public.highfidelity.io/sounds/Collisions-otherorganic/whoosh2.raw");
this.downloadSounds = function() {
for (var i = 0; i < this.moveSoundURLS.length; i++) {
this.moveSounds[i] = SoundCache.getSound(this.moveSoundURLS[i]);
}
}
// Play drag sound
this.playSound = function() {
this.stopSound();
if (this.sound && this.sound.downloaded) {
this.injector = Audio.playSound(this.sound, { position: this.properties.position, loop: true, volume: 0.1 });
for (var i = 0; i < this.turnSoundURLS.length; i++) {
this.turnSounds[i] = SoundCache.getSound(this.turnSoundURLS[i]);
}
}
// stop drag sound
this.pickRandomSounds = function() {
var moveIndex = this.getRandomInt(0, this.moveSounds.length - 1);
var turnIndex = this.getRandomInt(0, this.turnSounds.length - 1);
if (debug) {
print("Random sounds -- turn:" + turnIndex + " move:" + moveIndex);
}
this.moveSound = this.moveSounds[moveIndex];
this.turnSound = this.turnSounds[turnIndex];
}
// Play move sound
this.playMoveSound = function() {
if (debug) {
print("playMoveSound()");
}
if (this.moveSound && this.moveSound.downloaded) {
if (debug) {
print("playMoveSound() --- calling this.injector = Audio.playSound(this.moveSound...)");
}
this.injector = Audio.playSound(this.moveSound, { position: this.properties.position, loop: true, volume: 0.1 });
}
}
// Play turn sound
this.playTurnSound = function() {
if (debug) {
print("playTurnSound()");
}
if (this.turnSound && this.turnSound.downloaded) {
if (debug) {
print("playTurnSound() --- calling this.injector = Audio.playSound(this.turnSound...)");
}
this.injector = Audio.playSound(this.turnSound, { position: this.properties.position, loop: true, volume: 0.1 });
}
}
// stop sound
this.stopSound = function() {
if (debug) {
print("stopSound()");
}
if (this.injector) {
Audio.stopInjector(this.injector);
this.injector = null;
@ -86,11 +149,33 @@
this.properties.position, upVector);
this.graboffset = Vec3.subtract(this.properties.position, intersection);
};
this.stopSoundIfNotMoving = function(mouseEvent) {
var nowDate = new Date();
var nowMSecs = nowDate.getTime();
if(mouseEvent.x == this.lastMovedPosition.x && mouseEvent.y == this.lastMovedPosition.y) {
var elapsedSinceLastMove = nowMSecs - this.lastMovedMSecs;
if (debug) {
print("elapsedSinceLastMove:" + elapsedSinceLastMove);
}
if (elapsedSinceLastMove > stopSoundDelay) {
if (debug) {
print("calling stopSound()...");
}
this.stopSound();
}
} else {
// if we've moved, then track our last move position and time...
this.lastMovedMSecs = nowMSecs;
this.lastMovedPosition.x = mouseEvent.x;
this.lastMovedPosition.y = mouseEvent.y;
}
}
this.move = function(mouseEvent) {
this.updatePosition(mouseEvent);
if (this.injector === null) {
this.playSound();
this.playMoveSound();
}
};
@ -146,7 +231,11 @@
majorTickMarksAngle: 45.0, minorTickMarksAngle: 5,
majorTickMarksLength: 0.25, minorTickMarksLength: 0.1, });
}
}
}
if (this.injector === null) {
this.playTurnSound();
}
};
// All callbacks start by updating the properties
this.updateProperties = function(entityID) {
@ -185,7 +274,7 @@
this.rotateOverlayTarget = Overlays.addOverlay("circle3d", {
position: this.properties.position,
size: 10000,
size: rotateOverlayTargetSize,
color: { red: 0, green: 0, blue: 0 },
alpha: 0.0,
solid: true,
@ -201,7 +290,7 @@
alpha: innerAlpha,
color: { red: 51, green: 152, blue: 203 },
solid: true,
visible: true,
visible: displayRotateTargets,
rotation: yawOverlayRotation,
hasTickMarks: true,
majorTickMarksAngle: innerSnapAngle,
@ -222,7 +311,7 @@
alpha: outerAlpha,
color: { red: 51, green: 152, blue: 203 },
solid: true,
visible: true,
visible: displayRotateTargets,
rotation: yawOverlayRotation,
hasTickMarks: true,
@ -244,7 +333,7 @@
color: { red: 224, green: 67, blue: 36},
alpha: 0.8,
solid: true,
visible: true,
visible: displayRotateTargets,
rotation: yawOverlayRotation,
ignoreRayIntersection: true, // always ignore this
hasTickMarks: true,
@ -260,19 +349,25 @@
this.preload = function(entityID) {
this.updateProperties(entityID); // All callbacks start by updating the properties
this.maybeDownloadSound();
this.downloadSounds();
};
this.clickDownOnEntity = function(entityID, mouseEvent) {
this.updateProperties(entityID); // All callbacks start by updating the properties
this.grab(mouseEvent);
var d = new Date();
this.clickedAt = d.getTime();
var nowDate = new Date();
var nowMSecs = nowDate.getTime();
this.clickedAt = nowMSecs;
this.firstHolding = true;
this.clickedX = mouseEvent.x;
this.clickedY = mouseEvent.y;
this.clicked.x = mouseEvent.x;
this.clicked.y = mouseEvent.y;
this.lastMovedPosition.x = mouseEvent.x;
this.lastMovedPosition.y = mouseEvent.y;
this.lastMovedMSecs = nowMSecs;
this.pickRandomSounds();
};
this.holdingClickOnEntity = function(entityID, mouseEvent) {
@ -281,7 +376,7 @@
if (this.firstHolding) {
// if we haven't moved yet...
if (this.clickedX == mouseEvent.x && this.clickedY == mouseEvent.y) {
if (this.clicked.x == mouseEvent.x && this.clicked.y == mouseEvent.y) {
var d = new Date();
var now = d.getTime();
@ -295,12 +390,14 @@
this.firstHolding = false;
}
}
if (this.rotateMode) {
this.rotate(mouseEvent);
} else {
this.move(mouseEvent);
}
this.stopSoundIfNotMoving(mouseEvent);
};
this.clickReleaseOnEntity = function(entityID, mouseEvent) {
this.updateProperties(entityID); // All callbacks start by updating the properties

View file

@ -12,14 +12,13 @@
{ red: 0, green: 0, blue: 255 },
];
posY = document.getElementById("horiz-y");
minorSpacing = document.getElementById("minor-spacing");
majorSpacing = document.getElementById("major-spacing");
gridOn = document.getElementById("grid-on");
snapToGrid = document.getElementById("snap-to-grid");
hGridVisible = document.getElementById("horiz-grid-visible");
bMoveToSelection = document.getElementById("move-to-selection");
bMoveToAvatar = document.getElementById("move-to-avatar");
elPosY = document.getElementById("horiz-y");
elMinorSpacing = document.getElementById("minor-spacing");
elMajorSpacing = document.getElementById("major-spacing");
elSnapToGrid = document.getElementById("snap-to-grid");
elHorizontalGridVisible = document.getElementById("horiz-grid-visible");
elMoveToSelection = document.getElementById("move-to-selection");
elMoveToAvatar = document.getElementById("move-to-avatar");
if (window.EventBridge !== undefined) {
EventBridge.scriptEventReceived.connect(function(data) {
@ -27,27 +26,27 @@
if (data.origin) {
var origin = data.origin;
posY.value = origin.y;
elPosY.value = origin.y.toFixed(2);
}
if (data.minorGridSpacing) {
minorSpacing.value = data.minorGridSpacing;
elMinorSpacing.value = data.minorGridSpacing;
}
if (data.majorGridEvery) {
majorSpacing.value = data.majorGridEvery;
elMajorSpacing.value = data.majorGridEvery;
}
if (data.gridColor) {
gridColor = data.gridColor;
}
if (data.snapToGrid !== undefined) {
snapToGrid.checked = data.snapToGrid == true;
if (data.elSnapToGrid !== undefined) {
elSnapToGrid.checked = data.snapToGrid == true;
}
if (data.visible !== undefined) {
hGridVisible.checked = data.visible == true;
elHorizontalGridVisible.checked = data.visible == true;
}
});
@ -55,29 +54,31 @@
EventBridge.emitWebEvent(JSON.stringify({
type: "update",
origin: {
y: posY.value,
y: elPosY.value,
},
minorGridSpacing: minorSpacing.value,
majorGridEvery: majorSpacing.value,
minorGridSpacing: elMinorSpacing.value,
majorGridEvery: elMajorSpacing.value,
gridColor: gridColor,
snapToGrid: snapToGrid.checked,
visible: hGridVisible.checked,
snapToGrid: elSnapToGrid.checked,
visible: elHorizontalGridVisible.checked,
}));
}
}
document.addEventListener("input", emitUpdate);
hGridVisible.addEventListener("change", emitUpdate);
snapToGrid.addEventListener("change", emitUpdate);
elPosY.addEventListener("change", emitUpdate);
elMinorSpacing.addEventListener("change", emitUpdate);
elMajorSpacing.addEventListener("change", emitUpdate);
elSnapToGrid.addEventListener("change", emitUpdate);
elHorizontalGridVisible.addEventListener("change", emitUpdate);
bMoveToAvatar.addEventListener("click", function() {
elMoveToAvatar.addEventListener("click", function() {
EventBridge.emitWebEvent(JSON.stringify({
type: "action",
action: "moveToAvatar",
}));
});
bMoveToSelection.addEventListener("click", function() {
elMoveToSelection.addEventListener("click", function() {
EventBridge.emitWebEvent(JSON.stringify({
type: "action",
action: "moveToSelection",

View file

@ -1403,6 +1403,8 @@ SelectionDisplay = (function () {
vector = vec3Mult(mask, vector);
vector = grid.snapToSpacing(vector);
var changeInDimensions = Vec3.multiply(-1, vec3Mult(signs, vector));
var newDimensions;
if (proportional) {

View file

@ -15,7 +15,7 @@ Grid = function(opts) {
var minorGridWidth = 0.5;
var majorGridWidth = 1.5;
var snapToGrid = true;
var snapToGrid = false;
var gridOverlay = Overlays.addOverlay("grid", {
position: { x: 0 , y: 0, z: 0 },
@ -69,6 +69,24 @@ Grid = function(opts) {
return Vec3.sum(position, origin);
}
that.snapToSpacing = function(delta, majorOnly) {
print('snaptogrid? ' + snapToGrid);
if (!snapToGrid) {
return delta;
}
var spacing = majorOnly ? (minorGridSpacing * majorGridEvery) : minorGridSpacing;
var snappedDelta = {
x: Math.round(delta.x / spacing) * spacing,
y: Math.round(delta.y / spacing) * spacing,
z: Math.round(delta.z / spacing) * spacing,
};
return snappedDelta;
}
that.setPosition = function(newPosition, noUpdate) {
origin = Vec3.subtract(newPosition, { x: 0, y: yOffset, z: 0 });
origin.x = 0;

View file

@ -1926,11 +1926,14 @@ void Application::init() {
// check if we have a URL in settings to load to jump back to
// we load this separate from the other settings so we don't double lookup a URL
QSettings* interfaceSettings = lockSettings();
QUrl addressURL = interfaceSettings->value(SETTINGS_ADDRESS_KEY).toUrl();
QVariant addressVariant = interfaceSettings->value(SETTINGS_ADDRESS_KEY);
AddressManager::getInstance().handleLookupString(addressURL.toString());
QString addressString = addressVariant.isNull()
? DEFAULT_HIFI_ADDRESS : addressVariant.toUrl().toString();
unlockSettings();
AddressManager::getInstance().handleLookupString(addressString);
}
qDebug() << "Loaded settings";

View file

@ -504,10 +504,12 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
(bodyOrientation * glm::quat(orientation.x, orientation.y, orientation.z, orientation.w) *
glm::vec3(_eyeRenderDesc[eye].ViewAdjust.x, _eyeRenderDesc[eye].ViewAdjust.y, _eyeRenderDesc[eye].ViewAdjust.z));
RenderArgs::RenderSide renderSide = RenderArgs::STEREO_LEFT;
if (eyeIndex == 0) {
_leftEyePosition = thisEyePosition;
} else {
_rightEyePosition = thisEyePosition;
renderSide = RenderArgs::STEREO_RIGHT;
}
_camera->update(1.0f / Application::getInstance()->getFps());
@ -522,11 +524,13 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p
glViewport(_eyeRenderViewport[eye].Pos.x, _eyeRenderViewport[eye].Pos.y,
_eyeRenderViewport[eye].Size.w, _eyeRenderViewport[eye].Size.h);
// Apply the offset for the left or right eye on the projection matrix
glTranslatef(_eyeRenderDesc[eye].ViewAdjust.x, _eyeRenderDesc[eye].ViewAdjust.y, _eyeRenderDesc[eye].ViewAdjust.z);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(_eyeRenderDesc[eye].ViewAdjust.x, _eyeRenderDesc[eye].ViewAdjust.y, _eyeRenderDesc[eye].ViewAdjust.z);
Application::getInstance()->displaySide(*_camera);
Application::getInstance()->displaySide(*_camera, false, renderSide);
applicationOverlay.displayOverlayTextureOculus(*_camera);
_activeEyeIndex = -1;

View file

@ -19,6 +19,8 @@ ToolWindow::ToolWindow(QWidget* parent) :
QMainWindow(parent),
_hasShown(false),
_lastGeometry() {
Application::getInstance()->installEventFilter(this);
}
bool ToolWindow::event(QEvent* event) {
@ -47,8 +49,37 @@ bool ToolWindow::event(QEvent* event) {
return QMainWindow::event(event);
}
bool ToolWindow::eventFilter(QObject* sender, QEvent* event) {
switch (event->type()) {
case QEvent::WindowStateChange:
if (Application::getInstance()->getWindow()->isMinimized()) {
// If we are already visible, we are self-hiding
_selfHidden = isVisible();
setVisible(false);
} else {
if (_selfHidden) {
setVisible(true);
}
}
break;
case QEvent::ApplicationDeactivate:
_selfHidden = isVisible();
setVisible(false);
break;
case QEvent::ApplicationActivate:
if (_selfHidden) {
setVisible(true);
}
break;
default:
break;
}
return false;
}
void ToolWindow::onChildVisibilityUpdated(bool visible) {
if (visible) {
if (!_selfHidden && visible) {
setVisible(true);
} else {
bool hasVisible = false;
@ -59,7 +90,10 @@ void ToolWindow::onChildVisibilityUpdated(bool visible) {
break;
}
}
setVisible(hasVisible);
// If a child was hidden and we don't have any children still visible, hide ourself.
if (!hasVisible) {
setVisible(false);
}
}
}

View file

@ -28,11 +28,15 @@ public:
virtual void addDockWidget(Qt::DockWidgetArea area, QDockWidget* dockWidget, Qt::Orientation orientation);
virtual void removeDockWidget(QDockWidget* dockWidget);
virtual bool eventFilter(QObject* sender, QEvent* event);
public slots:
void onChildVisibilityUpdated(bool visible);
private:
// Indicates whether this window was hidden by itself (because the main window lost focus).
bool _selfHidden;
bool _hasShown;
QRect _lastGeometry;
};

View file

@ -69,9 +69,10 @@ void Grid3DOverlay::render(RenderArgs* args) {
xColor color = getColor();
glm::vec3 position = getPosition();
const int GRID_DIVISIONS = 300;
const int MINOR_GRID_DIVISIONS = 100;
const int MAJOR_GRID_DIVISIONS = 50;
const float MAX_COLOR = 255.0f;
float scale = GRID_DIVISIONS * spacing;
glColor4f(color.red / MAX_COLOR, color.green / MAX_COLOR, color.blue / MAX_COLOR, alpha);
@ -80,12 +81,13 @@ void Grid3DOverlay::render(RenderArgs* args) {
// Minor grid
glPushMatrix();
{
glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2),
spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position.z);
glTranslatef(_minorGridWidth * (floorf(rotated.x / spacing) - MINOR_GRID_DIVISIONS / 2),
spacing * (floorf(rotated.y / spacing) - MINOR_GRID_DIVISIONS / 2), position.z);
float scale = MINOR_GRID_DIVISIONS * spacing;
glScalef(scale, scale, scale);
Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS);
Application::getInstance()->getGeometryCache()->renderGrid(MINOR_GRID_DIVISIONS, MINOR_GRID_DIVISIONS);
}
glPopMatrix();
@ -94,13 +96,13 @@ void Grid3DOverlay::render(RenderArgs* args) {
{
glLineWidth(4.0f);
spacing *= _majorGridEvery;
glTranslatef(spacing * (floorf(rotated.x / spacing) - GRID_DIVISIONS / 2),
spacing * (floorf(rotated.y / spacing) - GRID_DIVISIONS / 2), position.z);
glTranslatef(spacing * (floorf(rotated.x / spacing) - MAJOR_GRID_DIVISIONS / 2),
spacing * (floorf(rotated.y / spacing) - MAJOR_GRID_DIVISIONS / 2), position.z);
scale *= _majorGridEvery;
float scale = MAJOR_GRID_DIVISIONS * spacing;
glScalef(scale, scale, scale);
Application::getInstance()->getGeometryCache()->renderGrid(GRID_DIVISIONS, GRID_DIVISIONS);
Application::getInstance()->getGeometryCache()->renderGrid(MAJOR_GRID_DIVISIONS, MAJOR_GRID_DIVISIONS);
}
glPopMatrix();

View file

@ -19,7 +19,8 @@
#include "AccountManager.h"
static const QString HIFI_URL_SCHEME = "hifi";
const QString HIFI_URL_SCHEME = "hifi";
const QString DEFAULT_HIFI_ADDRESS = "hifi://sandbox";
typedef const glm::vec3& (*PositionGetter)();
typedef glm::quat (*OrientationGetter)();

View file

@ -22,8 +22,6 @@
#include "HifiSockAddr.h"
#include "NetworkPeer.h"
const QString DEFAULT_DOMAIN_HOSTNAME = "sandbox.highfidelity.io";
const unsigned short DEFAULT_DOMAIN_SERVER_PORT = 40102;
const unsigned short DEFAULT_DOMAIN_SERVER_DTLS_PORT = 40103;
const quint16 DOMAIN_SERVER_HTTP_PORT = 40100;

View file

@ -10,8 +10,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
#include <time.h>
#ifdef _WIN32
#include <process.h>
#define getpid _getpid
@ -21,6 +19,7 @@
#include <unistd.h> // for getpid() on linux
#endif
#include <qdatetime.h>
#include <qdebug.h>
#include <qtimer.h>
@ -38,6 +37,10 @@ LogHandler::LogHandler() :
QTimer* logFlushTimer = new QTimer(this);
connect(logFlushTimer, &QTimer::timeout, this, &LogHandler::flushRepeatedMessages);
logFlushTimer->start(VERBOSE_LOG_INTERVAL_SECONDS * 1000);
// when the log handler is first setup we should print our timezone
QString timezoneString = "Time zone: " + QDateTime::currentDateTime().toString("t");
printf("%s\n", qPrintable(timezoneString));
}
const char* stringForLogType(LogMsgType msgType) {
@ -57,8 +60,8 @@ const char* stringForLogType(LogMsgType msgType) {
}
}
// the following will produce 2000-10-02 13:55:36 -0700
const char DATE_STRING_FORMAT[] = "%Y-%m-%d %H:%M:%S %z";
// the following will produce 11/18 13:55:36
const QString DATE_STRING_FORMAT = "MM/dd hh:mm:ss";
void LogHandler::flushRepeatedMessages() {
QHash<QString, int>::iterator message = _repeatMessageCountHash.begin();
@ -108,18 +111,11 @@ QString LogHandler::printMessage(LogMsgType type, const QMessageLogContext& cont
}
// log prefix is in the following format
// [DEBUG] [TIMESTAMP] [PID:PARENT_PID] [TARGET] logged string
// [DEBUG] [TIMESTAMP] [PID] [TARGET] logged string
QString prefixString = QString("[%1]").arg(stringForLogType(type));
time_t rawTime;
time(&rawTime);
struct tm* localTime = localtime(&rawTime);
char dateString[100];
strftime(dateString, sizeof(dateString), DATE_STRING_FORMAT, localTime);
prefixString.append(QString(" [%1]").arg(dateString));
prefixString.append(QString(" [%1]").arg(QDateTime::currentDateTime().toString(DATE_STRING_FORMAT)));
if (_shouldOutputPID) {
prefixString.append(QString(" [%1").arg(getpid()));