/* ============================================================================== This file is part of the JUCE 6 technical preview. Copyright (c) 2020 - Raw Material Software Limited You may use this code under the terms of the GPL v3 (see www.gnu.org/licenses). For this technical preview, this file is not subject to commercial licensing. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE DISCLAIMED. ============================================================================== */ #pragma once //============================================================================== struct MessageHandler { virtual ~MessageHandler() {} virtual bool sendMessage (const ValueTree&) = 0; static MemoryBlock convertMessage (const ValueTree& tree) { MemoryOutputStream out; tree.writeToStream (out); return out.getMemoryBlock(); } static ValueTree convertMessage (const MemoryBlock& rawData) { return ValueTree::readFromData (rawData.getData(), rawData.getSize()); } }; //============================================================================== static inline Rectangle varToRect (const var& v) { if (const Array* obj = v.getArray()) { if (obj->size() == 4) { int intArray[4]; for (int i = 0; i < 4; ++i) { const var& p = obj->getReference (i); if (p.isInt() || p.isDouble() || p.isInt64()) intArray[i] = static_cast (p); else return Rectangle(); } return Rectangle (intArray[0], intArray[1], intArray[2], intArray[3]); } } return Rectangle(); } static inline var rectToVar (const Rectangle& rect) { Array retval; retval.add (var (rect.getX())); retval.add (var (rect.getY())); retval.add (var (rect.getWidth())); retval.add (var (rect.getHeight())); return var (retval); } //============================================================================== namespace MessageTypes { inline bool send (MessageHandler& target, const ValueTree& v) { bool result = target.sendMessage (v); if (! result) Logger::outputDebugString ("*** Message failed: " + v.getType().toString()); return result; } inline bool sendPing (MessageHandler& target) { return send (target, ValueTree (PING)); } //============================================================================== // client -> server inline void sendOpenPreview (MessageHandler& target, const ClassDatabase::Class& comp, Rectangle mainWindowRect) { ValueTree v (OPEN_PREVIEW); v.setProperty (Ids::name, comp.getName(), nullptr); v.setProperty (Ids::bounds, rectToVar (mainWindowRect), nullptr); send (target, v); } inline void sendReinstantiate (MessageHandler& target) { send (target, ValueTree (RELOAD)); } inline void sendFileChanges (MessageHandler& target, const Array& changes, const File& file) { ValueTree changesMessage (MessageTypes::LIVE_FILE_CHANGES); changesMessage.setProperty (Ids::file, file.getFullPathName(), nullptr); for (const CodeChange& change : changes) { ValueTree v (CHANGE); v.setProperty (Ids::start, change.range.getStart(), nullptr); v.setProperty (Ids::end, change.range.getEnd(), nullptr); v.setProperty (Ids::text, change.text, nullptr); changesMessage.appendChild (v, nullptr); } send (target, changesMessage); } inline Array getChangeArray (const ValueTree& changes) { Array result; for (int i = 0; i < changes.getNumChildren(); ++i) { const ValueTree& v = changes.getChild (i); result.add (CodeChange (Range (v[Ids::start], v[Ids::end]), v[Ids::text])); } return result; } inline void sendFileContentFullUpdate (MessageHandler& target, const File& file, const String& text) { ValueTree v (LIVE_FILE_UPDATE); v.setProperty (Ids::file, file.getFullPathName(), nullptr); v.setProperty (Ids::text, text, nullptr); send (target, v); } inline void sendHandleFileReset (MessageHandler& target, const File& file) { ValueTree v (LIVE_FILE_RESET); v.setProperty (Ids::file, file.getFullPathName(), nullptr); send (target, v); } inline void sendNewBuild (MessageHandler& target, const ProjectBuildInfo& build) { send (target, build.tree); } inline void sendCleanAll (MessageHandler& target) { send (target, ValueTree (CLEAN_ALL)); } inline void sendNewDiagnosticList (MessageHandler& target, const ValueTree& list) { send (target, list); } inline void sendEmptyDiagnosticList (MessageHandler& target) { send (target, ValueTree (MessageTypes::DIAGNOSTIC_LIST)); } inline void sendProcessActivationState (MessageHandler& target, bool isNowActive) { ValueTree v (FOREGROUND); v.setProperty (Ids::parentActive, isNowActive, nullptr); send (target, v); } inline void sendLaunchApp (MessageHandler& target) { send (target, ValueTree (LAUNCH_APP)); } inline void sendQuit (MessageHandler& target) { send (target, ValueTree (QUIT_SERVER)); } inline void sendShouldCloseIDE (MessageHandler& target) { send (target, ValueTree (QUIT_IDE)); } //============================================================================== // server -> client inline void sendNewClassList (MessageHandler& target, const ClassDatabase::ClassList& classes) { send (target, classes.toValueTree()); } inline void sendCrash (MessageHandler& target, const String& message) { ValueTree v (CRASH); v.setProperty (Ids::message, message, nullptr); send (target, v); } inline void sendSystemHeadersMissing (MessageHandler& target) { send (target, ValueTree (MISSING_SYSTEM_HEADERS)); } inline void sendBuildFailed (MessageHandler& target) { send (target, ValueTree (BUILD_FAILED)); } inline void sendNewActivityList (MessageHandler& target, const StringArray& list) { ValueTree v (ACTIVITY_LIST); v.setProperty (Ids::list, concatenateListOfStrings (list), nullptr); send (target, v); } inline void sendChangeCode (MessageHandler& target, const String& location, const String& newText) { if (location.isNotEmpty()) { ValueTree v (CHANGE_CODE); v.setProperty (Ids::position, location, nullptr); v.setProperty (Ids::text, newText, nullptr); send (target, v); } } inline void sendHighlightCode (MessageHandler& target, const String& location) { if (location.isNotEmpty()) { ValueTree v (HIGHLIGHT_CODE); v.setProperty (Ids::position, location, nullptr); send (target, v); } } inline void sendAppLaunched (MessageHandler& target) { send (target, ValueTree (LAUNCHED)); } inline void sendAppQuit (MessageHandler& target) { send (target, ValueTree (APPQUIT)); } inline void sendKeyPress (MessageHandler& target, const String& className, const String& keyDesc) { ValueTree v (KEY); v.setProperty (Ids::class_, className, nullptr); v.setProperty (Ids::key, keyDesc, nullptr); send (target, v); } //============================================================================== template static void dispatchToClient (MessageHandlerType& target, const ValueTree& v) { if (v.hasType (DIAGNOSTIC_LIST)) target.handleNewDiagnosticList (v); else if (v.hasType (ACTIVITY_LIST)) target.handleActivityListChanged (separateJoinedStrings (v [Ids::list])); else if (v.hasType (Ids::CLASSLIST)) target.handleClassListChanged (v); else if (v.hasType (BUILD_FAILED)) target.handleBuildFailed(); else if (v.hasType (CHANGE_CODE)) target.handleChangeCode (v [Ids::position].toString(), v [Ids::text]); else if (v.hasType (HIGHLIGHT_CODE)) target.handleHighlightCode (v [Ids::position].toString()); else if (v.hasType (LAUNCHED)) target.handleAppLaunched(); else if (v.hasType (APPQUIT)) target.handleAppQuit(); else if (v.hasType (PING)) target.handlePing(); else if (v.hasType (CRASH)) target.handleCrash (v [Ids::message]); else if (v.hasType (KEY)) target.handleKeyPress (v[Ids::class_], KeyPress::createFromDescription (v[Ids::key])); else if (v.hasType (QUIT_IDE)) target.handleCloseIDE(); else if (v.hasType (MISSING_SYSTEM_HEADERS)) target.handleMissingSystemHeaders(); else jassertfalse; } template static void dispatchToServer (MessageHandlerType& target, const ValueTree& v) { if (v.hasType (CLEAN_ALL)) target.handleCleanAll(); else if (v.hasType (BUILDINFO)) target.handleNewBuildSettings (ProjectBuildInfo (v)); else if (v.hasType (OPEN_PREVIEW)) target.handleOpenPreview (v[Ids::name], varToRect (v[Ids::bounds])); else if (v.hasType (RELOAD)) target.handleReinstantiatePreviews(); else if (v.hasType (LAUNCH_APP)) target.handleLaunchApp(); else if (v.hasType (LIVE_FILE_CHANGES)) target.handleLiveFileChanges (v[Ids::file].toString(), getChangeArray (v)); else if (v.hasType (LIVE_FILE_UPDATE)) target.handleLiveFileFullUpdate (v[Ids::file].toString(), v[Ids::text]); else if (v.hasType (LIVE_FILE_RESET)) target.handleResetLiveFileContent (v[Ids::file].toString()); else if (v.hasType (FOREGROUND)) target.handleProcessActivationState (v[Ids::parentActive]); else if (v.hasType (PING)) target.handlePing(); else jassertfalse; } }