mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-04 20:57:49 +00:00
561 lines
13 KiB
C++
561 lines
13 KiB
C++
/**
|
|
* RobloxDocManager.cpp
|
|
* Copyright (c) 2013 ROBLOX Corp. All Rights Reserved.
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "RobloxDocManager.h"
|
|
|
|
// Qt Headers
|
|
#include <QString>
|
|
#include <QMessageBox>
|
|
|
|
// Roblox Headers
|
|
#include "Script/LuaSourceContainer.h"
|
|
#include "Util/standardout.h"
|
|
#include "Util/ContentId.h"
|
|
#include "V8DataModel/PluginManager.h"
|
|
|
|
// Roblox Studio Headers
|
|
#include "DocDockManager.h"
|
|
#include "DocTabManager.h"
|
|
#include "LuaSourceBuffer.h"
|
|
#include "RobloxMainWindow.h"
|
|
#include "RobloxGameExplorer.h"
|
|
#include "RobloxIDEDoc.h"
|
|
#include "RobloxScriptDoc.h"
|
|
#include "RobloxWebDoc.h"
|
|
#include "RobloxObjectBrowserDoc.h"
|
|
#include "UpdateUIManager.h"
|
|
#include "ScriptTextEditor.h"
|
|
|
|
RobloxDocManager& RobloxDocManager::Instance()
|
|
{
|
|
static RobloxDocManager singleInstance;
|
|
return singleInstance;
|
|
}
|
|
|
|
RobloxDocManager::RobloxDocManager()
|
|
: m_pMainWindow(NULL)
|
|
, m_pCurrentDoc(NULL)
|
|
, m_pPlayDoc(NULL)
|
|
, m_pObjectBrowser(NULL)
|
|
, m_pWebDoc(NULL)
|
|
, m_DocTabManager(NULL)
|
|
, m_DocDockManager(NULL)
|
|
{
|
|
}
|
|
|
|
RobloxDocManager::~RobloxDocManager()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Only call initialize once.
|
|
*/
|
|
void RobloxDocManager::initialize(RobloxMainWindow& mainWindow)
|
|
{
|
|
RBXASSERT(!m_pMainWindow);
|
|
|
|
m_pMainWindow = &mainWindow;
|
|
m_DocTabManager = new DocTabManager(mainWindow);
|
|
m_DocDockManager = new DocDockManager(mainWindow);
|
|
}
|
|
|
|
void RobloxDocManager::shutDown()
|
|
{
|
|
m_pCurrentDoc = NULL;
|
|
m_pPlayDoc = NULL;
|
|
m_KeyDocMap.clear();
|
|
|
|
delete m_DocTabManager;
|
|
delete m_DocDockManager;
|
|
|
|
if(m_pObjectBrowser)
|
|
delete m_pObjectBrowser;
|
|
|
|
if(m_pWebDoc)
|
|
delete m_pWebDoc;
|
|
|
|
m_DocTabManager = NULL;
|
|
m_DocDockManager = NULL;
|
|
m_pMainWindow = NULL;
|
|
}
|
|
|
|
/**
|
|
* Attempt to close all docs.
|
|
* Any failure to close aborts the operation.
|
|
*
|
|
* @return true if all docs closed successfully
|
|
*/
|
|
bool RobloxDocManager::requestCloseAllDocs()
|
|
{
|
|
while ( !m_KeyDocMap.empty() )
|
|
{
|
|
RBX::BaldPtr<IRobloxDoc> doc = m_KeyDocMap.begin().value();
|
|
if ( !m_pMainWindow->requestDocClose(doc) )
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Attempts to save and close all docs.
|
|
* Any failure to save will abort the process.
|
|
*
|
|
* @return true if all docs were saved successfully
|
|
*/
|
|
bool RobloxDocManager::requestCloseAndSaveAllDocs()
|
|
{
|
|
while ( !m_KeyDocMap.empty() )
|
|
{
|
|
RBX::BaldPtr<IRobloxDoc> doc = m_KeyDocMap.begin().value();
|
|
if ( !m_pMainWindow->fileSave(doc) )
|
|
return false;
|
|
removeDoc(*doc);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Closes all docs without saving.
|
|
*/
|
|
void RobloxDocManager::closeAllDocs()
|
|
{
|
|
while ( !m_KeyDocMap.empty() )
|
|
{
|
|
RBX::BaldPtr<IRobloxDoc> doc = m_KeyDocMap.begin().value();
|
|
removeDoc(*doc);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers a script doc.
|
|
* Used for attempting to reopen scripts.
|
|
*/
|
|
void RobloxDocManager::registerOpenedScriptDoc(RobloxScriptDoc& doc, LuaSourceBuffer script)
|
|
{
|
|
RBXASSERT(!m_OpenScriptDocs.contains(&doc));
|
|
m_OpenScriptDocs.push_back(&doc);
|
|
}
|
|
|
|
/**
|
|
* Unregisters a script doc.
|
|
* Used for attempting to reopen scripts.
|
|
*/
|
|
void RobloxDocManager::deregisterScriptDoc(RobloxScriptDoc& doc)
|
|
{
|
|
RBXASSERT(m_OpenScriptDocs.contains(&doc));
|
|
m_OpenScriptDocs.removeAll(&doc);
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a handle to the ObjectBrowser, or NULL if it hasnt been created.
|
|
*/
|
|
RobloxObjectBrowserDoc* RobloxDocManager::getObjectBrowserDoc()
|
|
{
|
|
return m_pObjectBrowser;
|
|
}
|
|
|
|
RobloxScriptDoc* RobloxDocManager::findOpenScriptDoc(LuaSourceBuffer script)
|
|
{
|
|
for (tScriptDocs::iterator itr = m_OpenScriptDocs.begin(); itr != m_OpenScriptDocs.end(); ++itr)
|
|
{
|
|
if ((*itr)->getCurrentScript() == script)
|
|
{
|
|
return *itr;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* Creates a doc or returns an existing doc of the given type.
|
|
* The Object Browser and Start Page get created only once.
|
|
*/
|
|
IRobloxDoc* RobloxDocManager::getOrCreateDoc(IRobloxDoc::RBXDocType docType)
|
|
{
|
|
RBX::BaldPtr<IRobloxDoc> pDoc = NULL;
|
|
|
|
switch ( docType )
|
|
{
|
|
case IRobloxDoc::IDE:
|
|
{
|
|
m_pPlayDoc = new RobloxIDEDoc(m_pMainWindow);
|
|
pDoc = m_pPlayDoc;
|
|
break;
|
|
}
|
|
case IRobloxDoc::SCRIPT:
|
|
{
|
|
pDoc = new RobloxScriptDoc();
|
|
break;
|
|
}
|
|
case IRobloxDoc::BROWSER:
|
|
{
|
|
if ( !m_pWebDoc )
|
|
m_pWebDoc = new RobloxWebDoc(tr("Start Page"), "StartPage");
|
|
pDoc = m_pWebDoc;
|
|
break;
|
|
}
|
|
case IRobloxDoc::OBJECTBROWSER:
|
|
{
|
|
if ( !m_pObjectBrowser )
|
|
m_pObjectBrowser = new RobloxObjectBrowserDoc();
|
|
pDoc = m_pObjectBrowser;
|
|
break;
|
|
}
|
|
default:
|
|
RBXASSERT(false);
|
|
}
|
|
|
|
return pDoc;
|
|
}
|
|
|
|
/**
|
|
* Attempt to open a script document.
|
|
* If the script is already open, the doc will be returned.
|
|
* If the script is not already open, a new doc will be returned.
|
|
*/
|
|
RobloxScriptDoc* RobloxDocManager::openDoc(LuaSourceBuffer script)
|
|
{
|
|
if (script.empty())
|
|
return NULL;
|
|
|
|
boost::scoped_ptr<RBX::DataModel::LegacyLock> lock;
|
|
|
|
// Double click scripts in game explorer will attempt to open doc, only open
|
|
// script doc if we have a play doc.
|
|
if (m_pPlayDoc == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// RobloxScriptDoc::setScript needs a write lock
|
|
lock.reset(new RBX::DataModel::LegacyLock(m_pPlayDoc->getDataModel(), RBX::DataModelJob::Write));
|
|
|
|
// check if it is a linked script, and if so substitute opening linked source
|
|
if (shared_ptr<RBX::LuaSourceContainer> lsc = script.toInstance())
|
|
{
|
|
if (!lsc->getScriptId().isNull())
|
|
{
|
|
script = LuaSourceBuffer::fromContentId(lsc->getScriptId());
|
|
}
|
|
}
|
|
|
|
if (!script.sourceAvailable())
|
|
{
|
|
// release the lock before entering message box event loop
|
|
lock.reset();
|
|
QMessageBox mb;
|
|
mb.setText("Unable to load script. If the script uses LinkedSource, the "
|
|
"LinkedSource may have been renamed or removed.");
|
|
mb.exec();
|
|
return NULL;
|
|
}
|
|
|
|
// If the doc for this script already exists, then return the same
|
|
if (RobloxScriptDoc* doc = findOpenScriptDoc(script))
|
|
{
|
|
setCurrentDoc(doc);
|
|
return doc;
|
|
}
|
|
|
|
// Create new script document
|
|
RobloxScriptDoc* pDoc = new RobloxScriptDoc();
|
|
if ( !pDoc->open(m_pMainWindow,"") )
|
|
{
|
|
RBX::StandardOut::singleton()->printf(RBX::MESSAGE_INFO, "Error in opening script");
|
|
pDoc->close();
|
|
delete pDoc;
|
|
return NULL;
|
|
}
|
|
|
|
configureDocWidget(*pDoc);
|
|
pDoc->setScript(shared_from(getPlayDoc()->getDataModel()), script);
|
|
|
|
return pDoc;
|
|
}
|
|
|
|
void RobloxDocManager::openDoc(shared_ptr<RBX::Instance> script, int lineNumber)
|
|
{
|
|
if (RobloxScriptDoc* scriptDoc = openDoc(LuaSourceBuffer::fromInstance(script)))
|
|
scriptDoc->getTextEditor()->moveCursorToLine(lineNumber);
|
|
}
|
|
|
|
/**
|
|
* Requests a script doc to be closed.
|
|
*/
|
|
void RobloxDocManager::closeDoc(const LuaSourceBuffer& script)
|
|
{
|
|
RBXASSERT(!script.empty());
|
|
|
|
if (RobloxScriptDoc* pDoc = findOpenScriptDoc(script))
|
|
{
|
|
m_pMainWindow->requestDocClose(*pDoc);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Adds the doc to the doc tab manager.
|
|
*/
|
|
void RobloxDocManager::configureDocWidget(IRobloxDoc& doc)
|
|
{
|
|
const RBX::BaldPtr<QWidget> widget = doc.getViewer();
|
|
RBXASSERT(widget);
|
|
|
|
const QString keyName = doc.keyName();
|
|
RBXASSERT(!keyName.isEmpty());
|
|
|
|
if (!m_KeyDocMap.contains(keyName))
|
|
{
|
|
m_KeyDocMap[keyName] = &doc;
|
|
m_DocTabManager->addDoc(doc);
|
|
}
|
|
setCurrentDoc(&doc);
|
|
}
|
|
|
|
/**
|
|
* Removes a doc from the tab or dock manager.
|
|
*/
|
|
void RobloxDocManager::removeDoc(IRobloxDoc& doc)
|
|
{
|
|
m_KeyDocMap.remove(doc.keyName());
|
|
|
|
if ( &doc == m_pCurrentDoc )
|
|
{
|
|
setCurrentDoc(NULL);
|
|
m_pCurrentDoc = NULL;
|
|
}
|
|
|
|
if ( &doc == m_pPlayDoc )
|
|
{
|
|
restoreCentralWidget();
|
|
|
|
tScriptDocs copyOfScriptDocs = m_OpenScriptDocs;
|
|
for (tScriptDocs::iterator itr = copyOfScriptDocs.begin();
|
|
itr != copyOfScriptDocs.end(); ++itr)
|
|
{
|
|
m_pMainWindow->requestDocClose(*itr);
|
|
}
|
|
RBXASSERT(m_OpenScriptDocs.empty());
|
|
|
|
m_pPlayDoc = NULL;
|
|
}
|
|
|
|
if (m_DocTabManager && !m_DocTabManager->removeDoc(doc) )
|
|
{
|
|
m_DocDockManager->removeDoc(doc);
|
|
setCurrentDoc(m_DocTabManager->getCurrentDoc());
|
|
}
|
|
|
|
doc.close();
|
|
updateWindowMenu();
|
|
}
|
|
|
|
/**
|
|
* Sets the current doc.
|
|
* The doc may be NULL. The old doc is deactivated.
|
|
*/
|
|
void RobloxDocManager::setCurrentDoc(IRobloxDoc* doc)
|
|
{
|
|
if ( doc == m_pCurrentDoc )
|
|
{
|
|
if (m_pCurrentDoc)
|
|
m_pCurrentDoc->activate();
|
|
return;
|
|
}
|
|
|
|
bool docTypeChanged = (m_pCurrentDoc ? m_pCurrentDoc->docType() : -1) != (doc ? doc->docType() : -1);
|
|
|
|
if ( m_pCurrentDoc )
|
|
{
|
|
m_pCurrentDoc->deActivate();
|
|
if (docTypeChanged)
|
|
m_pCurrentDoc->updateUI(false);
|
|
}
|
|
|
|
m_pCurrentDoc = doc;
|
|
|
|
if ( m_pCurrentDoc )
|
|
{
|
|
m_pCurrentDoc->activate();
|
|
if (docTypeChanged)
|
|
m_pCurrentDoc->updateUI(true);
|
|
}
|
|
else
|
|
{
|
|
RBX::PluginManager::singleton()->setCurrentDataModel(NULL);
|
|
}
|
|
|
|
UpdateUIManager::Instance().updateToolBars();
|
|
m_pMainWindow->updateWindowTitle();
|
|
|
|
if ( !m_DocTabManager->setCurrentDoc(*doc) )
|
|
{
|
|
m_pMainWindow->setFocus();
|
|
m_DocDockManager->setCurrentDoc(*doc);
|
|
}
|
|
else if (m_pCurrentDoc)
|
|
{
|
|
m_pCurrentDoc->getViewer()->setFocus();
|
|
}
|
|
|
|
updateWindowMenu();
|
|
Q_EMIT currentDocChanged();
|
|
}
|
|
|
|
/**
|
|
* Updates the list of open documents in the Window menu.
|
|
* The current open doc is marked with a check.
|
|
*/
|
|
void RobloxDocManager::updateWindowMenu()
|
|
{
|
|
if (!m_pMainWindow)
|
|
return;
|
|
|
|
int count = 0;
|
|
tKeyDocMap::iterator iter = m_KeyDocMap.begin();
|
|
while ( iter != m_KeyDocMap.end() && count < RobloxMainWindow::MAX_DOC_WINDOWS )
|
|
{
|
|
RBX::BaldPtr<IRobloxDoc> doc = iter.value();
|
|
QString text = QObject::tr("&%1 %2").arg(count + 1).arg(doc->displayName());
|
|
m_pMainWindow->currentOpenedfiles[count]->setText(text);
|
|
m_pMainWindow->currentOpenedfiles[count]->setData(count);
|
|
m_pMainWindow->currentOpenedfiles[count]->setVisible(true);
|
|
|
|
bool selected = (doc == m_pCurrentDoc);
|
|
m_pMainWindow->currentOpenedfiles[count]->setChecked(selected);
|
|
|
|
++iter;
|
|
++count;
|
|
}
|
|
|
|
while ( count < RobloxMainWindow::MAX_DOC_WINDOWS )
|
|
{
|
|
m_pMainWindow->currentOpenedfiles[count]->setVisible(false);
|
|
++count;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback for user clicking on a doc in the Window menu.
|
|
*/
|
|
void RobloxDocManager::onActivateWindow()
|
|
{
|
|
const RBX::BaldPtr<QAction> action = qobject_cast<QAction*>(sender());
|
|
int index = action->data().toInt();
|
|
activateDoc(index);
|
|
updateWindowMenu();
|
|
}
|
|
|
|
/**
|
|
* Activate a doc at the given index.
|
|
* Used by onActivateWindow.
|
|
*/
|
|
void RobloxDocManager::activateDoc(int index)
|
|
{
|
|
tKeyDocMap::iterator iter = m_KeyDocMap.begin();
|
|
iter = iter + index;
|
|
if ( iter != m_KeyDocMap.end() )
|
|
setCurrentDoc(iter.value());
|
|
}
|
|
|
|
/**
|
|
* Changes the title of a doc.
|
|
* If the doc is a tab, the tab name is changed. If the doc is a dock widget, the window
|
|
* title is changed.
|
|
*/
|
|
void RobloxDocManager::setDocTitle(IRobloxDoc& doc,const QString& displayName,const QString& fileName,const QIcon& icon)
|
|
{
|
|
const QString oldKey = m_KeyDocMap.key(&doc);
|
|
RBXASSERT(!oldKey.isEmpty());
|
|
|
|
m_KeyDocMap.remove(oldKey);
|
|
m_KeyDocMap[doc.keyName()] = &doc;
|
|
|
|
if ( !m_DocTabManager->renameDoc(doc,displayName,fileName,icon) )
|
|
m_DocDockManager->renameDoc(doc,displayName,fileName);
|
|
|
|
updateWindowMenu();
|
|
m_pMainWindow->updateWindowTitle();
|
|
}
|
|
|
|
/**
|
|
* Detaches the given doc from the tab manager.
|
|
* Optionally begins dragging the title bar to move the window.
|
|
*/
|
|
void RobloxDocManager::detachDoc(IRobloxDoc& doc,bool dragging)
|
|
{
|
|
if ( m_DocTabManager->removeDoc(doc) )
|
|
{
|
|
m_DocDockManager->addDoc(doc);
|
|
if ( dragging )
|
|
m_DocDockManager->startDrag(doc);
|
|
m_DocDockManager->setCurrentDoc(doc);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Attaches a dock widget to the tab manager.
|
|
*/
|
|
void RobloxDocManager::attachDoc(IRobloxDoc& doc, int index)
|
|
{
|
|
if ( m_DocDockManager->removeDoc(doc) )
|
|
m_DocTabManager->addDoc(doc, index);
|
|
}
|
|
|
|
/**
|
|
* Sets the current state of the mouse hover over.
|
|
* When a dock widget is hovered over the tab widget while dragging,
|
|
* the tab widget shows if the dock can be reattached.
|
|
*/
|
|
void RobloxDocManager::setDockHoverOverPos(const QPoint& globalPos)
|
|
{
|
|
m_DocTabManager->setDockHoverOverPos(globalPos);
|
|
}
|
|
|
|
/**
|
|
* Attempts to attach a dock widget.
|
|
* If the mouse position is inside the region allowable for reattachment,
|
|
* the doc is reattached to the tab bar.
|
|
*
|
|
* @param doc doc to attach
|
|
* @param worldPos position the mouse button was released
|
|
*/
|
|
bool RobloxDocManager::attemptAttach(IRobloxDoc& doc,const QPoint& globalPos)
|
|
{
|
|
return m_DocTabManager->attemptAttach(doc,globalPos);
|
|
}
|
|
|
|
/**
|
|
* Sets the central widget for the main window to just show the IDE 3D view.
|
|
*/
|
|
void RobloxDocManager::setPlayDocCentralWidget()
|
|
{
|
|
RBXASSERT(m_pPlayDoc);
|
|
|
|
QWidget* centralWidget = m_pMainWindow->centralWidget();
|
|
if ( centralWidget != m_pPlayDoc->getViewer() )
|
|
{
|
|
if ( !m_DocDockManager->removeDoc(*m_pPlayDoc) )
|
|
m_DocTabManager->removeDoc(*m_pPlayDoc);
|
|
|
|
m_pMainWindow->setCentralWidget(m_pPlayDoc->getViewer());
|
|
QApplication::removePostedEvents(centralWidget); // don't allow to be deleted
|
|
m_pPlayDoc->getViewer()->show();
|
|
|
|
setCurrentDoc(m_pPlayDoc);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Resets the central widget for the main window back to the tab manager.
|
|
*/
|
|
void RobloxDocManager::restoreCentralWidget()
|
|
{
|
|
m_DocTabManager->restoreAsCentralWidget();
|
|
}
|