Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Lots of ipc improvements

  • Loading branch information...
commit 161f9363a341c39a9b7b13fe4d8ddbdd4af6c68c 1 parent ffe3c85
Alexander Stigsen authored
7 src/Document.cpp
View
@@ -1847,6 +1847,13 @@ void Document::Freeze() {
pState(rRevision) = STATE_FROZEN;
}
+bool Document::IsFrozen() const {
+ if (m_docId.IsDocument()) return true; // Only Drafts can be editable
+
+ c4_RowRef rRevision = vHistory[m_docId.version_id];
+ return pState(rRevision) == STATE_FROZEN;
+}
+
wxDateTime Document::GetDate() const {
wxASSERT(IsOk());
1  src/Document.h
View
@@ -43,6 +43,7 @@ class Document {
bool IsEmpty() const;
bool IsDraft() const {return m_docId.IsDraft();};
bool IsDocument() const {return m_docId.IsDocument();};
+ bool IsFrozen() const;
void Clear(bool initialRevision=false);
void Close();
void Commit(const wxString& label, const wxString& desc);
47 src/EditorCtrl.cpp
View
@@ -597,6 +597,15 @@ doc_id EditorCtrl::GetDocID() const {
cxENDLOCK
}
+doc_id EditorCtrl::GetLastStableDocID() const {
+ cxLOCKDOC_READ(m_doc)
+ doc_id di = doc.GetDocument();
+ if (doc.IsFrozen()) return di;
+ else return doc.GetDraftParent(di.version_id);
+ cxENDLOCK
+}
+
+
wxString EditorCtrl::GetName() const {
cxLOCKDOC_READ(m_doc)
return doc.GetPropertyName();
@@ -1327,6 +1336,14 @@ void EditorCtrl::GetTextPart(unsigned int start, unsigned int end, vector<char>&
cxENDLOCK
}
+void EditorCtrl::GetLine(unsigned int lineid, vector<char>& text) const {
+ unsigned int start;
+ unsigned int end;
+ m_lines.GetLineExtent(lineid, start, end);
+
+ GetTextPart(start, end, text);
+}
+
void EditorCtrl::Tab() {
const bool shiftDown = wxGetKeyState(WXK_SHIFT);
const bool isMultiSel = m_lines.IsSelectionMultiline() && !m_lines.IsSelectionShadow();
@@ -3123,6 +3140,32 @@ bool EditorCtrl::SetDocument(const doc_id& di, const wxString& path, const Remot
// Only derived controls are embedded in a parent panel; see BundleItemEditorCtrl.
void EditorCtrl::UpdateParentPanels() {}
+void EditorCtrl::GetLinesChangedSince(const doc_id& di, vector<size_t>& lines) {
+ vector<cxChange> changes;
+ cxLOCKDOC_READ(m_doc)
+ const doc_id current = doc.GetDocument();
+ doc_id prev = di;
+ if (prev == current) {
+ if (prev.IsDraft()) prev = doc.GetDraftParent(di.version_id);
+ else return;
+ }
+ changes = doc.GetChanges(prev, current);
+ cxENDLOCK
+
+ const vector<cxLineChange> linechanges = m_lines.ChangesToFullLines(changes);
+ const size_t end = m_lines.GetLength();
+
+ for (vector<cxLineChange>::const_iterator p = linechanges.begin(); p != linechanges.end(); ++p) {
+ const size_t firstline = m_lines.GetLineFromStartPos(p->start);
+ const size_t lastline = p->end == end ? m_lines.GetLineCount() : m_lines.GetLineFromStartPos(p->end);
+ lines.push_back(firstline);
+
+ for (size_t i = firstline+1; i < lastline; ++i) {
+ lines.push_back(i);
+ }
+ }
+}
+
void EditorCtrl::ApplyDiff(const doc_id& oldDoc, bool moveToFirstChange) {
vector<cxChange> changes;
cxLOCKDOC_READ(m_doc)
@@ -7577,6 +7620,10 @@ void EditorCtrl::SetEnv(cxEnv& env, bool isUnix, const tmBundle* bundle) {
// Add document/editor keys
+ // TM_EDITOR_ID (used for scripting via ipc)
+ const int id = -GetId(); // internally id is negative, but pass to user as positive
+ env.SetEnv(wxT("TM_EDITOR_ID"), wxString::Format(wxT("%u"), id));
+
// TM_FILENAME
// note: in case of remote files, this is of the buffer file
const wxString name = m_path.GetFullName();
3  src/EditorCtrl.h
View
@@ -98,6 +98,7 @@ class EditorCtrl : public KeyHookable<wxControl>,
wxString GetText(unsigned int start, unsigned int end) const;
void GetText(vector<char>& text) const;
void GetTextPart(unsigned int start, unsigned int end, vector<char>& text) const;
+ void GetLine(unsigned int lineId, vector<char>& text) const;
wxString GetCurrentWord() const;
void WriteText(wxOutputStream& stream) const;
@@ -165,6 +166,7 @@ class EditorCtrl : public KeyHookable<wxControl>,
virtual const DocumentWrapper& GetDocument() const {return m_doc;};
bool SetDocument(const doc_id& di, const wxString& path=wxEmptyString, const RemoteProfile* rp=NULL);
doc_id GetDocID() const;
+ doc_id GetLastStableDocID() const;
virtual wxString GetName() const;
virtual const vector<unsigned int>& GetOffsets() const {return m_lines.GetOffsets();};
@@ -349,6 +351,7 @@ class EditorCtrl : public KeyHookable<wxControl>,
void MarkAsModified() {++m_changeToken;};
unsigned int GetChangeToken() const {return m_changeToken;};
virtual EditorChangeState GetChangeState() const;
+ void GetLinesChangedSince(const doc_id& di, vector<size_t>& lines);
// Callbacks
void SetBeforeRedrawCallback(void(*callback)(void*), void* data) {m_beforeRedrawCallback = callback; if (data) m_callbackData = data;};
43 src/EditorFrame.cpp
View
@@ -69,6 +69,7 @@
#include "CurrentTabsPopup.h"
#include "eauibook.h"
#include "DiffDirPane.h"
+#include "InputPanel.h"
#ifdef __WXMSW__
// For multi-monitor-aware position restore on Windows, include WinUser.h
@@ -358,6 +359,11 @@ EditorFrame::EditorFrame(CatalystWrapper cat, unsigned int frameId, const wxStr
box->Add(m_commandPanel, 0, wxEXPAND);
box->Show(m_commandPanel, false);
+ // Create and add the inputpanel
+ m_inputPanel = new InputPanel(*this, panel);
+ box->Add(m_inputPanel, 0, wxEXPAND);
+ box->Show(m_inputPanel, false);
+
// Layout main components
box->Layout();
panel->SetSizer(box);
@@ -1333,7 +1339,14 @@ void EditorFrame::UpdateTabs() {
}
// May be NULL, always check in reciever
-EditorCtrl* EditorFrame::GetEditorCtrl() { return editorCtrl; }
+EditorCtrl* EditorFrame::GetEditorCtrl() const { return editorCtrl; }
+
+// May be NULL, always check in reciever
+EditorCtrl* EditorFrame::GetEditorCtrl(int winId) const {
+ wxWindow* editor = FindWindowById(winId, this);
+ return (EditorCtrl*)editor;
+}
+
// May be NULL, always check in reciever. Downcast.
IEditorSearch* EditorFrame::GetSearch() { return editorCtrl; }
@@ -2039,6 +2052,9 @@ void EditorFrame::SaveAllFilesInProject() {
void EditorFrame::ShowSearch(bool show, bool replace) {
if (show) {
+ if (box->IsShown(m_commandPanel)) ShowCommandMode(false);
+ else if (box->IsShown(m_inputPanel)) HideInputPanel();
+
m_searchPanel->InitSearch(editorCtrl->GetSelFirstLine(), replace);
box->Show(m_searchPanel);
}
@@ -2053,7 +2069,8 @@ bool EditorFrame::IsSearching() const { return m_searchPanel->IsShown(); }
void EditorFrame::ShowCommandMode(bool show) {
if (show) {
- if (box->IsShown(m_searchPanel)) box->Hide(m_searchPanel);
+ if (box->IsShown(m_searchPanel)) box->Hide(m_searchPanel);
+ else if (box->IsShown(m_inputPanel)) HideInputPanel();
box->Show(m_commandPanel);
}
else {
@@ -2072,6 +2089,28 @@ bool EditorFrame::IsCommandMode() const {
return m_commandPanel->IsShown();
}
+void EditorFrame::ShowInputPanel(unsigned int notifier_id, const wxString& caption) {
+ m_inputPanel->Set(notifier_id, caption);
+
+ if (box->IsShown(m_searchPanel)) box->Hide(m_searchPanel);
+ else if (box->IsShown(m_commandPanel)) box->Hide(m_commandPanel);
+ box->Show(m_inputPanel);
+ box->Layout();
+
+ m_inputPanel->SetFocus();
+}
+
+void EditorFrame::HideInputPanel() {
+ wxGetApp().OnInputLineClosed(m_inputPanel->GetNotifierId());
+
+ box->Hide(m_inputPanel);
+ box->Layout();
+}
+
+void EditorFrame::OnInputPanelChanged(unsigned int notifier_id, const wxString& text) {
+ wxGetApp().OnInputLineChanged(notifier_id, text);
+}
+
bool EditorFrame::GetSetting(const wxString& name) const {
if (name == wxT("search/highlight")) return m_searchHighlight;
if (name == wxT("editor/linenumbers")) return m_showGutter;
10 src/EditorFrame.h
View
@@ -60,6 +60,7 @@ class DiffDirPane;
class DiffPanel;
class IEditorSearch;
class RemoteThread;
+class InputPanel;
class EditorFrame : public KeyHookable<wxFrame>,
@@ -193,7 +194,8 @@ class EditorFrame : public KeyHookable<wxFrame>,
void UpdateTabs();
void GotoPos(int line, int column);
bool CloseTab(unsigned int tab_id, bool removetab=true);
- EditorCtrl* GetEditorCtrl();
+ EditorCtrl* GetEditorCtrl() const; // Gets currently active
+ EditorCtrl* GetEditorCtrl(int winId) const;
virtual IEditorSearch* GetSearch();
// Editor Service methods.
@@ -239,6 +241,11 @@ class EditorFrame : public KeyHookable<wxFrame>,
void ShowCommand(const wxString& cmd=wxEmptyString);
bool IsCommandMode() const;
+ // Input Panel
+ void ShowInputPanel(unsigned int notifier_id, const wxString& caption);
+ void HideInputPanel();
+ void OnInputPanelChanged(unsigned int notifier_id, const wxString& text);
+
// Settings
bool GetSetting(const wxString& name) const;
void SetSetting(const wxString& name, bool value);
@@ -507,6 +514,7 @@ class EditorFrame : public KeyHookable<wxFrame>,
wxBoxSizer* editorbox;
SearchPanel* m_searchPanel;
CommandPanel* m_commandPanel;
+ InputPanel* m_inputPanel;
eAuiNotebook* m_tabBar;
// Menus
4 src/Lines.cpp
View
@@ -458,6 +458,10 @@ unsigned int Lines::GetLineEndFromPos(unsigned int pos) const {
unsigned int Lines::GetLineStartFromPos(unsigned int pos) const {
wxASSERT(pos <= GetLength());
+ if (!NewlineTerminated && pos == GetLength()) {
+ return IsEmpty() ? 0 : ll->offset(GetLineCount()-1);
+ }
+
return ll->StartFromPos(pos);
}
14 src/e-exe/e-exe.vcproj
View
@@ -41,8 +41,8 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
- AdditionalIncludeDirectories="..\;..\..\ecore;..\..\external\wxwidgets\lib\vc_lib\mswud;..\..\external\wxwidgets\include;..\..\external\pcre;..\..\external\libtomcrypt\src\headers;..\..\external\tinyxml;..\..\external\curl\include;..\..\external\metakit\include"
- PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"
+ AdditionalIncludeDirectories="..\;..\..\ecore;..\..\external\wxwidgets\lib\vc_lib\mswud;..\..\external\wxwidgets\include;..\..\external\pcre;..\..\external\libtomcrypt\src\headers;..\..\external\tinyxml;..\..\external\curl\include;..\..\external\metakit\include;..\..\external\boost"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;BOOST_DATE_TIME_NO_LIB;BOOST_REGEX_NO_LIB"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="3"
@@ -66,7 +66,7 @@
AdditionalDependencies="odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib wxbase28ud.lib wxmsw28ud_core.lib wxbase28ud_net.lib wxmsw28ud_adv.lib wxmsw28ud_aui.lib wxpngd.lib wxjpegd.lib wxzlibd.lib wxregexud.lib mk4vc60s_d.lib libtommathd.lib libtomcryptd.lib pcred.lib tinyxmld.lib dbghelp.lib libcurld.lib winmm.lib ecored.lib e.lib"
OutputFile="$(OutDir)\e.exe"
LinkIncremental="2"
- AdditionalLibraryDirectories="..\..\external\wxwidgets\lib\vc_lib;..\..\external\pcre;..\..\external\libtomcrypt;..\..\external\libtommath;..\..\external\tinyxml\Debug;..\..\external\curl\lib\Debug;..\..\external\metakit\builds;..\Debug;..\..\ecore"
+ AdditionalLibraryDirectories="..\..\external\wxwidgets\lib\vc_lib;..\..\external\pcre;..\..\external\libtomcrypt;..\..\external\libtommath;..\..\external\tinyxml\Debug;..\..\external\curl\lib\Debug;..\..\external\metakit\builds;..\Debug;..\..\external\boost\stage\lib;..\..\ecore"
GenerateDebugInformation="true"
SubSystem="2"
TargetMachine="1"
@@ -120,8 +120,8 @@
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
- AdditionalIncludeDirectories="..\;..\..\ecore;..\..\external\wxwidgets\lib\vc_lib\mswud;..\..\external\wxwidgets\include;..\..\external\pcre;..\..\external\libtomcrypt\src\headers;..\..\external\tinyxml;..\..\external\curl\include;..\..\external\metakit\include"
- PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
+ AdditionalIncludeDirectories="..\;..\..\external\wxwidgets\lib\vc_lib\mswud;..\..\external\wxwidgets\include;..\..\external\pcre;..\..\external\libtomcrypt\src\headers;..\..\external\tinyxml;..\..\external\curl\include;..\..\external\metakit\include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;BOOST_DATE_TIME_NO_LIB;BOOST_REGEX_NO_LIB"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="0"
@@ -139,10 +139,10 @@
/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="libcmt.lib libcpmt.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib wxbase28u.lib wxbase28u_net.lib wxmsw28u_core.lib wxmsw28u_adv.lib wxmsw28u_aui.lib wxpng.lib wxjpeg.lib wxzlib.lib wxregexu.lib mk4vc60s.lib libtommath.lib libtomcrypt.lib pcre.lib tinyxml.lib dbghelp.lib libcurl.lib winmm.lib e.lib ecore.lib"
+ AdditionalDependencies="libcmt.lib libcpmt.lib odbc32.lib odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib wxbase28u.lib wxbase28u_net.lib wxmsw28u_core.lib wxmsw28u_adv.lib wxmsw28u_aui.lib wxpng.lib wxjpeg.lib wxzlib.lib wxregexu.lib mk4vc60s.lib libtommath.lib libtomcrypt.lib pcre.lib tinyxml.lib dbghelp.lib libcurl.lib winmm.lib e.lib"
OutputFile="$(OutDir)\e.exe"
LinkIncremental="1"
- AdditionalLibraryDirectories="..\..\external\wxwidgets\lib\vc_lib;..\..\external\pcre;..\..\external\libtomcrypt;..\..\external\libtommath;..\..\external\tinyxml\Release;..\..\external\curl\lib\Release;..\..\external\metakit\builds;..\Release;..\..\ecore"
+ AdditionalLibraryDirectories="..\..\external\wxwidgets\lib\vc_lib;..\..\external\pcre;..\..\external\libtomcrypt;..\..\external\libtommath;..\..\external\tinyxml\Release;..\..\external\curl\lib\Release;..\..\external\metakit\builds;..\Release;..\..\external\boost\stage\lib"
IgnoreAllDefaultLibraries="false"
IgnoreDefaultLibraryNames="libc.lib,MSVCRTD.lib,msvcrt.lib"
GenerateDebugInformation="true"
230 src/e.vcproj
View
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
- Version="9.00"
+ Version="9,00"
Name="e"
ProjectGUID="{34A9B5C4-22D3-4F14-A896-0F12FDF194C5}"
RootNamespace="e"
@@ -48,8 +48,8 @@
<Tool
Name="VCCLCompilerTool"
Optimization="0"
- AdditionalIncludeDirectories="..\ecore;..\external\wxwidgets\lib\vc_lib\mswud;..\external\wxwidgets\include;..\external\pcre;..\external\libtomcrypt\src\headers;..\external\tinyxml;..\external\curl\include;..\external\metakit\include"
- PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;WINVER=0x500;_MT;wxUSE_GUI=1;__WXDEBUG__;wxUSE_UNICODE=1;WXDEBUG=1;PCRE_STATIC;SUPPORT_UTF8;CURL_STATICLIB;HAVE_CONFIG_H;FEAT_BROWSER"
+ AdditionalIncludeDirectories="..\ecore;..\external\wxwidgets\lib\vc_lib\mswud;..\external\wxwidgets\include;..\external\pcre;..\external\libtomcrypt\src\headers;..\external\tinyxml;..\external\curl\include;..\external\metakit\include;..\external\boost"
+ PreprocessorDefinitions="_DEBUG;WIN32;_WINDOWS;WINVER=0x500;_MT;wxUSE_GUI=1;__WXDEBUG__;wxUSE_UNICODE=1;WXDEBUG=1;PCRE_STATIC;SUPPORT_UTF8;CURL_STATICLIB;HAVE_CONFIG_H;FEAT_BROWSER;_WIN32_WINNT=0x0501;BOOST_DATE_TIME_NO_LIB;BOOST_REGEX_NO_LIB"
BasicRuntimeChecks="3"
SmallerTypeCheck="true"
RuntimeLibrary="3"
@@ -212,8 +212,8 @@
Optimization="1"
InlineFunctionExpansion="2"
WholeProgramOptimization="true"
- AdditionalIncludeDirectories="..\ecore;..\external\wxwidgets\lib\vc_lib\mswu;..\external\wxwidgets\include;..\external\pcre;..\external\libtomcrypt\src\headers;..\external\tinyxml;..\external\curl\include;..\external\metakit\include"
- PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;WINVER=0x500;_MT;wxUSE_GUI=1;wxUSE_UNICODE=1;PCRE_STATIC;SUPPORT_UTF8;CURL_STATICLIB;HAVE_CONFIG_H;FEAT_BROWSER"
+ AdditionalIncludeDirectories="..\ecore;..\external\wxwidgets\lib\vc_lib\mswu;..\external\wxwidgets\include;..\external\pcre;..\external\libtomcrypt\src\headers;..\external\tinyxml;..\external\curl\include;..\external\metakit\include;..\external\boost"
+ PreprocessorDefinitions="NDEBUG;WIN32;_WINDOWS;WINVER=0x500;_MT;wxUSE_GUI=1;wxUSE_UNICODE=1;PCRE_STATIC;SUPPORT_UTF8;CURL_STATICLIB;HAVE_CONFIG_H;FEAT_BROWSER;_WIN32_WINNT=0x0501;BOOST_DATE_TIME_NO_LIB;BOOST_REGEX_NO_LIB"
StringPooling="true"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
@@ -353,6 +353,10 @@
>
</File>
<File
+ RelativePath=".\IConnection.h"
+ >
+ </File>
+ <File
RelativePath="IEditorDoAction.h"
>
</File>
@@ -401,6 +405,14 @@
>
</File>
<File
+ RelativePath=".\IIpcHandler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\IIpcServer.h"
+ >
+ </File>
+ <File
RelativePath="IOpenTextmateURL.h"
>
</File>
@@ -1101,6 +1113,86 @@
>
</File>
</Filter>
+ <Filter
+ Name="hessian_ipc"
+ >
+ <File
+ RelativePath=".\hessian_ipc\connection.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\hessian_ipc\connection.h"
+ >
+ </File>
+ <File
+ RelativePath=".\hessian_ipc\connection_manager.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\hessian_ipc\connection_manager.h"
+ >
+ </File>
+ <File
+ RelativePath=".\eConnection.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\eConnection.h"
+ >
+ </File>
+ <File
+ RelativePath=".\eIpcThread.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\eIpcThread.h"
+ >
+ </File>
+ <File
+ RelativePath=".\eServer.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\eServer.h"
+ >
+ </File>
+ <File
+ RelativePath=".\hessian_ipc\hessian_reader.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\hessian_ipc\hessian_reader.h"
+ >
+ </File>
+ <File
+ RelativePath=".\hessian_ipc\hessian_values.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\hessian_ipc\hessian_values.h"
+ >
+ </File>
+ <File
+ RelativePath=".\hessian_ipc\hessian_writers.h"
+ >
+ </File>
+ <File
+ RelativePath=".\hessian_ipc\proxy.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\hessian_ipc\proxy.h"
+ >
+ </File>
+ <File
+ RelativePath=".\hessian_ipc\server.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\hessian_ipc\server.h"
+ >
+ </File>
+ </Filter>
<File
RelativePath="application.ico"
>
@@ -1154,6 +1246,46 @@
>
</File>
<File
+ RelativePath=".\Catalyst.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\Catalyst.h"
+ >
+ </File>
+ <File
+ RelativePath=".\cData.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\cData.h"
+ >
+ </File>
+ <File
+ RelativePath=".\cDataBool.h"
+ >
+ </File>
+ <File
+ RelativePath=".\cDataList.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\cDataList.h"
+ >
+ </File>
+ <File
+ RelativePath=".\cDataNum.h"
+ >
+ </File>
+ <File
+ RelativePath=".\cDataText.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\cDataText.h"
+ >
+ </File>
+ <File
RelativePath="Cell.cpp"
>
</File>
@@ -1178,10 +1310,58 @@
>
</File>
<File
+ RelativePath=".\CommandHandler.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\CommandHandler.h"
+ >
+ </File>
+ <File
+ RelativePath=".\CommandPanel.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\CommandPanel.h"
+ >
+ </File>
+ <File
RelativePath="CrashFileNames.h"
>
</File>
<File
+ RelativePath=".\cxInternal.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\cxInternal.h"
+ >
+ </File>
+ <File
+ RelativePath=".\DataList.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\DataList.h"
+ >
+ </File>
+ <File
+ RelativePath=".\DataText.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\DataText.h"
+ >
+ </File>
+ <File
+ RelativePath=".\diff.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\diff.h"
+ >
+ </File>
+ <File
RelativePath="diff.xpm"
>
</File>
@@ -1202,6 +1382,14 @@
>
</File>
<File
+ RelativePath=".\doc_byte_iter.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\doc_byte_iter.h"
+ >
+ </File>
+ <File
RelativePath="DocHistory.cpp"
>
</File>
@@ -1350,6 +1538,10 @@
>
</File>
<File
+ RelativePath=".\Fletcher.h"
+ >
+ </File>
+ <File
RelativePath="Fold.cpp"
>
</File>
@@ -1382,6 +1574,14 @@
>
</File>
<File
+ RelativePath=".\HashStream.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\HashStream.h"
+ >
+ </File>
+ <File
RelativePath="HtmlOutputPane.cpp"
>
</File>
@@ -1390,6 +1590,14 @@
>
</File>
<File
+ RelativePath=".\InputPanel.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\InputPanel.h"
+ >
+ </File>
+ <File
RelativePath="Interval.h"
>
</File>
@@ -1454,6 +1662,10 @@
>
</File>
<File
+ RelativePath=".\public_key.h"
+ >
+ </File>
+ <File
RelativePath=".\RankedItem.h"
>
</File>
@@ -1462,6 +1674,14 @@
>
</File>
<File
+ RelativePath=".\RegisterDlg.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\RegisterDlg.h"
+ >
+ </File>
+ <File
RelativePath="RemoteThread.cpp"
>
</File>
388 src/eApp.cpp
View
@@ -38,6 +38,7 @@
#include "IIpcServer.h"
#include "IConnection.h"
#include "eIpcThread.h"
+#include "hessian_ipc/hessian_values.h"
#ifdef __WXMSW__
#include <wx/msw/registry.h>
@@ -115,6 +116,7 @@ BEGIN_EVENT_TABLE(eApp, wxApp)
EVT_MENU(ID_UPDATES_CHECKED, eApp::OnUpdatesChecked)
EVT_IDLE(eApp::OnIdle)
EVT_COMMAND(wxID_ANY, wxEVT_IPC_CALL, eApp::OnIpcCall)
+ EVT_COMMAND(wxID_ANY, wxEVT_IPC_CLOSE, eApp::OnIpcClosed)
END_EVENT_TABLE()
bool eApp::OnInit() {
@@ -288,7 +290,7 @@ bool eApp::OnInit() {
}
// Start the scripting server
- m_ipcThread = new eIpcThread(*this);
+ InitIpc();
return true;
}
@@ -334,7 +336,7 @@ void eApp::CloseAllFrames() {
}
}
-EditorFrame* eApp::GetTopFrame() {
+EditorFrame* eApp::GetTopFrame() const {
EditorFrame* win = NULL;
wxWindowList::const_iterator i;
const wxWindowList::const_iterator end = wxTopLevelWindows.end();
@@ -379,6 +381,30 @@ bool eApp::IsLastFrame() const {
return (frameCount == 1);
}
+EditorCtrl* eApp::GetActiveEditorCtrl() const {
+ const EditorFrame* frame = GetTopFrame();
+ if (!frame) return NULL;
+
+ return frame->GetEditorCtrl();
+}
+
+EditorCtrl* eApp::GetEditorCtrl(int winId) const {
+ wxWindowList::const_iterator i;
+ const wxWindowList::const_iterator end = wxTopLevelWindows.end();
+
+ // Search all frames for an editor with the given id
+ for ( i = wxTopLevelWindows.begin(); i != end; ++i )
+ {
+ if (!(*i)->IsKindOf(CLASSINFO(EditorFrame))) continue;
+
+ EditorFrame* win = wx_static_cast(EditorFrame*, *i);
+ EditorCtrl* editor = win->GetEditorCtrl(winId);
+ if (editor) return editor;
+ }
+
+ return NULL; // not found
+}
+
void eApp::ClearState() {
m_settings.DeleteAllFrameSettings(0);
m_settings.Save();
@@ -541,6 +567,28 @@ void eApp::OnIdle(wxIdleEvent& event) {
event.RequestMore();
}
+ // Do we have ipc connections watching for editor changes
+ vector<EditorWatch>::iterator p = m_editorWatchers.begin();
+ while (p != m_editorWatchers.end()) {
+ EditorCtrl* editor = GetEditorCtrl(p->editorId);
+ if (!editor){
+ // the editor has been closed
+ OnEditorChanged(p->notifierId, false); // Send notification
+ m_notifiers.erase(p->notifierId);
+ p = m_editorWatchers.erase(p); // remove entry
+ continue;
+ }
+
+ const unsigned int token = editor->GetChangeToken();
+ if (token != p->changeToken) {
+ wxLogDebug(wxT("token: %d"), token);
+ OnEditorChanged(p->notifierId, true); // Send notification
+ p->changeToken = token;
+ }
+
+ ++p;
+ }
+
// Important: wxApp needs to do its idle processing as well
event.Skip();
}
@@ -822,6 +870,27 @@ int eApp::TotalDays() const {return m_pCatalyst->DaysLeftOfTrial();}
const wxString& eApp::RegisteredUserName() const {return m_pCatalyst->RegisteredUserName();}
const wxString& eApp::RegisteredUserEmail() const {return m_pCatalyst->RegisteredUserEmail();}
+void eApp::InitIpc() {
+ m_ipcNextNotifierId = 0;
+
+ // Register method handlers
+ m_ipcFunctions["get_active_editor"] = (Pmemfun)&eApp::IpcGetActiveEditor;
+ m_ipcEditorFunctions["insert_at"] = (Pmemfun)&eApp::IpcEditorInsertAt;
+ m_ipcEditorFunctions["delete_range"] = (Pmemfun)&eApp::IpcEditorDeleteRange;
+ m_ipcEditorFunctions["gettext"] = (Pmemfun)&eApp::IpcEditorGetText;
+ m_ipcEditorFunctions["getline"] = (Pmemfun)&eApp::IpcEditorGetLine;
+ m_ipcEditorFunctions["getline_offset"] = (Pmemfun)&eApp::IpcEditorGetLineOffset;
+ m_ipcEditorFunctions["getpos"] = (Pmemfun)&eApp::IpcEditorGetPos;
+ m_ipcEditorFunctions["get_versionid"] = (Pmemfun)&eApp::IpcEditorGetVersionId;
+ m_ipcEditorFunctions["select"] = (Pmemfun)&eApp::IpcEditorSelect;
+ m_ipcEditorFunctions["get_changes_since"] = (Pmemfun)&eApp::IpcEditorGetChangesSince;
+ m_ipcEditorFunctions["show_input_line"] = (Pmemfun)&eApp::IpcEditorShowInputLine;
+ m_ipcEditorFunctions["watch_changes"] = (Pmemfun)&eApp::IpcEditorWatchChanges;
+
+ // Start the ipc server
+ m_ipcThread = new eIpcThread(*this);
+}
+
void eApp::OnIpcCall(wxCommandEvent& event) {
IConnection* conn = (IConnection*)event.GetClientData();
if (!conn) return;
@@ -834,10 +903,317 @@ void eApp::OnIpcCall(wxCommandEvent& event) {
wxLogDebug(wxT("IPC: %s"), method);
- // Write the reply
- hessian_ipc::Writer& writer = conn->get_reply_writer();
- writer.write_reply(true);
+ // Check if the call is for a specific object
+ map<string, Pmemfun>& funcs = call->IsObjectCall() ? m_ipcEditorFunctions : m_ipcFunctions;
+
+ // Call the function (if it exists)
+ map<string, Pmemfun>::const_iterator p = funcs.find(m.c_str());
+ if (p != funcs.end()) {
+ (this->*p->second)(*conn);
+ }
+ else {
+ hessian_ipc::Writer& writer = conn->get_reply_writer();
+ writer.write_fault(hessian_ipc::NoSuchMethodException, "Unknown method");
+ }
// Notify connection that it can send the reply (threadsafe)
conn->reply_done();
-}
+}
+
+eApp::ConnectionState& eApp::GetConnState(IConnection& conn) {
+ boost::ptr_map<IConnection*,ConnectionState>::iterator p = m_connStates.find(&conn);
+ if (p == m_connStates.end()) {
+ IConnection* c = &conn;
+ m_connStates.insert(c, new ConnectionState);
+ p = m_connStates.find(&conn);
+ }
+
+ return *p->second;
+}
+
+void eApp::OnIpcClosed(wxCommandEvent& event) {
+ IConnection* conn = (IConnection*)event.GetClientData();
+ if (!conn) return;
+
+ // Remove all notifiers from closed connection
+ map<unsigned int, IConnection*>::iterator p = m_notifiers.begin();
+ while (p != m_notifiers.end()) {
+ if (p->second == conn) p = m_notifiers.erase(p);
+ else ++p;
+ }
+
+ // Clear any associated state
+ m_connStates.erase(conn);
+}
+
+
+void eApp::IpcGetActiveEditor(IConnection& conn) {
+ EditorCtrl* editor = GetActiveEditorCtrl();
+ const int id = -editor->GetId();
+
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ writer.write_reply_handle(id);
+}
+
+void eApp::IpcEditorGetVersionId(IConnection& conn) {
+ const hessian_ipc::Call& call = *conn.get_call();
+ const hessian_ipc::Value& v1 = call.GetParameter(0);
+ const int editorId = -v1.GetInt();
+
+ EditorCtrl* editor = GetEditorCtrl(editorId);
+ if (!editor) return; // fault: object does not exist
+
+ const doc_id id = editor->GetLastStableDocID();
+
+ // create handle for doc_id
+ ConnectionState& connState = GetConnState(conn);
+ const size_t handle = connState.docHandles.size();
+ connState.docHandles.push_back(id);
+
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ writer.write_reply(handle);
+}
+
+void eApp::IpcEditorGetText(IConnection& conn) {
+ const hessian_ipc::Call& call = *conn.get_call();
+ const hessian_ipc::Value& v1 = call.GetParameter(0);
+ const int editorId = -v1.GetInt();
+ EditorCtrl* editor = GetEditorCtrl(editorId);
+ if (!editor) return; // fault: object does not exist
+
+ vector<char> text;
+ editor->GetText(text);
+
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ writer.write_reply(text);
+}
+
+void eApp::IpcEditorGetLine(IConnection& conn) {
+ const hessian_ipc::Call& call = *conn.get_call();
+ const hessian_ipc::Value& v1 = call.GetParameter(0);
+ const int editorId = -v1.GetInt();
+ EditorCtrl* editor = GetEditorCtrl(editorId);
+ if (!editor) return; // fault: object does not exist
+
+ const unsigned int lineid = call.GetParameter(1).GetInt();
+ if (lineid > editor->GetLineCount()) return; // fault
+
+ vector<char> text;
+ editor->GetLine(lineid, text);
+
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ writer.write_reply(text);
+}
+
+void eApp::IpcEditorGetLineOffset(IConnection& conn) {
+ const hessian_ipc::Call& call = *conn.get_call();
+ const hessian_ipc::Value& v1 = call.GetParameter(0);
+ const int editorId = -v1.GetInt();
+ EditorCtrl* editor = GetEditorCtrl(editorId);
+ if (!editor) return; // fault: object does not exist
+
+ const unsigned int lineid = call.GetParameter(1).GetInt();
+ if (lineid > editor->GetLineCount()) return; // fault
+
+ const interval iv = editor->GetLineExtent(lineid);
+
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ writer.write_reply(iv.start);
+}
+
+void eApp::IpcEditorGetPos(IConnection& conn) {
+ const hessian_ipc::Call& call = *conn.get_call();
+ const hessian_ipc::Value& v1 = call.GetParameter(0);
+ const int editorId = -v1.GetInt();
+
+ EditorCtrl* editor = GetEditorCtrl(editorId);
+ if (!editor) return; // fault: object does not exist
+
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ writer.write_reply(editor->GetPos());
+}
+
+void eApp::IpcEditorSelect(IConnection& conn) {
+ // Get the editor id
+ const hessian_ipc::Call& call = *conn.get_call();
+ const hessian_ipc::Value& v1 = call.GetParameter(0);
+ const int editorId = -v1.GetInt();
+ EditorCtrl* editor = GetEditorCtrl(editorId);
+ if (!editor) return; // fault: object does not exist
+
+ const int v2 = call.GetParameter(1).GetInt();
+ const int v3 = call.GetParameter(2).GetInt();
+
+ editor->Select(v2, v3);
+ editor->SetPos(v3);
+ editor->MakeSelectionVisible();
+ editor->ReDraw();
+
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ writer.write_reply(true);
+}
+
+void eApp::IpcEditorInsertAt(IConnection& conn) {
+ // Get the editor id
+ const hessian_ipc::Call& call = *conn.get_call();
+ const hessian_ipc::Value& v1 = call.GetParameter(0);
+ const int editorId = -v1.GetInt();
+ EditorCtrl* editor = GetEditorCtrl(editorId);
+ if (!editor) return; // fault: object does not exist
+
+ // Get the insert position
+ const size_t pos = call.GetParameter(1).GetInt();
+ if (pos > editor->GetLength()) return; // fault: invalid position
+
+ // Get the text to insert
+ const hessian_ipc::Value& v3 = call.GetParameter(2);
+ const string& t = v3.GetString();
+ const wxString text(t.c_str(), wxConvUTF8, t.size());
+
+ // Insert the text
+ // TODO: adjust selections
+ const unsigned int cpos = editor->GetPos();
+ const size_t byte_len = editor->RawInsert(pos, text);
+ if (cpos >= pos) editor->SetPos(cpos + byte_len);
+ editor->ReDraw();
+
+ // Write the reply
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ writer.write_reply(byte_len);
+}
+
+void eApp::IpcEditorDeleteRange(IConnection& conn) {
+ // Get the editor id
+ const hessian_ipc::Call& call = *conn.get_call();
+ const hessian_ipc::Value& v1 = call.GetParameter(0);
+ const int editorId = -v1.GetInt();
+ EditorCtrl* editor = GetEditorCtrl(editorId);
+ if (!editor) return; // fault: object does not exist
+
+ // Get the inset position
+ const size_t start = call.GetParameter(1).GetInt();
+ const size_t end = call.GetParameter(2).GetInt();
+ if (end > editor->GetLength() || start > end) return; // fault: invalid positions
+
+ // Delete the range
+ const size_t byte_len = editor->RawDelete(start, end);
+
+ // Write the reply
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ writer.write_reply(byte_len);
+}
+
+
+void eApp::IpcEditorShowInputLine(IConnection& conn) {
+ // Get the editor id
+ const hessian_ipc::Call& call = *conn.get_call();
+ const hessian_ipc::Value& v1 = call.GetParameter(0);
+ const int editorId = -v1.GetInt();
+ EditorCtrl* editor = GetEditorCtrl(editorId);
+ if (!editor) return; // fault: object does not exist
+
+ // Get caption
+ const hessian_ipc::Value& v2 = call.GetParameter(1);
+ const string& t = v2.GetString();
+ const wxString caption(t.c_str(), wxConvUTF8, t.size());
+
+ // Register notifier
+ const unsigned int notifier_id = GetNextNotifierId();
+ m_notifiers[notifier_id] = &conn;
+
+ // Show input line
+ EditorFrame* frame = GetTopFrame();
+ if (!frame) return;
+ frame->ShowInputPanel(notifier_id, caption);
+
+ // Return notifier id
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ writer.write_reply(notifier_id);
+}
+
+void eApp::IpcEditorWatchChanges(IConnection& conn) {
+ // Get the editor id
+ const hessian_ipc::Call& call = *conn.get_call();
+ const hessian_ipc::Value& v1 = call.GetParameter(0);
+ const int editorId = -v1.GetInt();
+ EditorCtrl* editor = GetEditorCtrl(editorId);
+ if (!editor) return; // fault: object does not exist
+
+ // Register notifier
+ const unsigned int notifier_id = GetNextNotifierId();
+ m_notifiers[notifier_id] = &conn;
+
+ // Add to watch list
+ EditorWatch ew = {editorId, editor->GetChangeToken(), notifier_id};
+ m_editorWatchers.push_back(ew);
+
+ // Return notifier id
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ writer.write_reply(notifier_id);
+}
+
+void eApp::IpcEditorGetChangesSince(IConnection& conn) {
+ // Get the editor id
+ const hessian_ipc::Call& call = *conn.get_call();
+ const hessian_ipc::Value& v1 = call.GetParameter(0);
+ const int editorId = -v1.GetInt();
+ EditorCtrl* editor = GetEditorCtrl(editorId);
+ if (!editor) return; // fault: object does not exist
+
+ // Get the version handle
+ const size_t versionHandle = call.GetParameter(1).GetInt();
+ ConnectionState& connState = GetConnState(conn);
+ if (versionHandle >= connState.docHandles.size()) return; // fault: invalid handle
+ const doc_id& di = connState.docHandles[versionHandle];
+
+ // Get diff
+ vector<size_t> changedlines;
+ editor->GetLinesChangedSince(di, changedlines);
+
+ // Return changed lines
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ writer.write_reply(changedlines);
+}
+
+void eApp::OnInputLineChanged(unsigned int nid, const wxString& text) {
+ // Look up notifier id
+ map<unsigned int, IConnection*>::const_iterator p = m_notifiers.find(nid);
+ if (p == m_notifiers.end()) return;
+ IConnection& conn = *p->second;
+
+ // Send notifier
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ const wxCharBuffer str = text.ToUTF8();
+ writer.write_notifier(nid, str.data());
+
+ conn.notifier_done();
+}
+
+void eApp::OnInputLineClosed(unsigned int nid) {
+ // Look up notifier id
+ map<unsigned int, IConnection*>::const_iterator p = m_notifiers.find(nid);
+ if (p == m_notifiers.end()) return;
+ IConnection& conn = *p->second;
+
+ // Remove notifier
+ m_notifiers.erase(nid);
+
+ // Notifier listener that the notifier has ended
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ writer.write_notifier_ended(nid);
+ conn.notifier_done();
+}
+
+void eApp::OnEditorChanged(unsigned int nid, bool state) {
+ // Look up notifier id
+ map<unsigned int, IConnection*>::const_iterator p = m_notifiers.find(nid);
+ if (p == m_notifiers.end()) return;
+ IConnection& conn = *p->second;
+
+ // Send notifier
+ hessian_ipc::Writer& writer = conn.get_reply_writer();
+ if (state) writer.write_notifier(nid, true); // true for change, false for close
+ else writer.write_notifier_ended(nid);
+
+ conn.notifier_done();
+}
53 src/eApp.h
View
@@ -30,13 +30,22 @@
#include "IAppPaths.h"
#include "IExecuteAppCommand.h"
+#include <map>
+#include <boost/ptr_container/ptr_map.hpp>
+// Pre-definitions
class wxSingleInstanceChecker;
class TmSyntaxHandler;
class PListHandler;
class EditorFrame;
class AppVersion;
class eIpcThread;
+class IConnection;
+class EditorCtrl;
+namespace hessian_ipc {
+ class Call;
+ class Writer;
+};
class eApp : public wxApp,
public IAppPaths,
@@ -86,11 +95,38 @@ class eApp : public wxApp,
void OnAssertFailure(const wxChar *file, int line, const wxChar *cond, const wxChar *msg);
#endif //__WXDEBUG__
+ // Ipc notifications
+ void OnInputLineChanged(unsigned int nid, const wxString& text);
+ void OnInputLineClosed(unsigned int nid);
+
private:
// Frames
EditorFrame* OpenFrame(size_t frameId);
- EditorFrame* GetTopFrame();
+ EditorFrame* GetTopFrame() const;
void CheckForModifiedFiles();
+ EditorCtrl* GetActiveEditorCtrl() const;
+ EditorCtrl* GetEditorCtrl(int winId) const;
+
+ // Ipc handling
+ struct ConnectionState {
+ vector<doc_id> docHandles;
+ };
+ void InitIpc();
+ ConnectionState& GetConnState(IConnection& conn);
+ unsigned int GetNextNotifierId() {return m_ipcNextNotifierId++;};
+ void IpcGetActiveEditor(IConnection& conn);
+ void IpcEditorGetVersionId(IConnection& conn);
+ void IpcEditorGetPos(IConnection& conn);
+ void IpcEditorGetText(IConnection& conn);
+ void IpcEditorGetLine(IConnection& conn);
+ void IpcEditorGetLineOffset(IConnection& conn);
+ void IpcEditorSelect(IConnection& conn);
+ void IpcEditorInsertAt(IConnection& conn);
+ void IpcEditorDeleteRange(IConnection& conn);
+ void IpcEditorShowInputLine(IConnection& conn);
+ void IpcEditorWatchChanges(IConnection& conn);
+ void IpcEditorGetChangesSince(IConnection& conn);
+ void OnEditorChanged(unsigned int nid, bool state);
// Member variables
wxString m_version_name;
@@ -112,6 +148,7 @@ class eApp : public wxApp,
void OnUpdatesChecked(wxCommandEvent& event);
void OnIdle(wxIdleEvent& event);
void OnIpcCall(wxCommandEvent& event);
+ void OnIpcClosed(wxCommandEvent& event);
DECLARE_EVENT_TABLE();
// Member variables
@@ -126,6 +163,20 @@ class eApp : public wxApp,
wxArrayString m_openStack;
eIpcThread* m_ipcThread;
+ // Ipc variables
+ struct EditorWatch {
+ int editorId;
+ unsigned int changeToken;
+ unsigned int notifierId;
+ };
+ typedef void (eApp::* Pmemfun)(IConnection& conn);
+ map<string, Pmemfun> m_ipcFunctions;
+ map<string, Pmemfun> m_ipcEditorFunctions;
+ map<unsigned int, IConnection*> m_notifiers;
+ unsigned int m_ipcNextNotifierId;
+ boost::ptr_map<IConnection*, ConnectionState> m_connStates;
+ vector<EditorWatch> m_editorWatchers;
+
#ifndef __WXMSW__
eServer* m_server;
#endif
8 src/eConnection.cpp
View
@@ -13,6 +13,10 @@ void eConnection::invoke_method() {
m_handler.handle_call(*this);
}
+void eConnection::on_close() {
+ m_handler.handle_close(*this);
+}
+
const hessian_ipc::Call* eConnection::get_call() {
return request_;
}
@@ -23,4 +27,8 @@ hessian_ipc::Writer& eConnection::get_reply_writer() {
void eConnection::reply_done() {
connection::reply_done();
+}
+
+void eConnection::notifier_done() {
+ connection::notifier_done();
}
2  src/eConnection.h
View
@@ -27,11 +27,13 @@ class eConnection : public hessian_ipc::connection, public IConnection {
// Method handling
void invoke_method();
+ void on_close();
// Interface methods
virtual const hessian_ipc::Call* get_call(); // The request recieved (may be NULL)
virtual hessian_ipc::Writer& get_reply_writer();
virtual void reply_done(); // notify connection that it can send the reply (threadsafe)
+ virtual void notifier_done(); // notify connection that it can send the notifier (threadsafe)
private:
// Member variables
11 src/eIpcThread.cpp
View
@@ -4,6 +4,7 @@
#include "IIpcServer.h"
DEFINE_EVENT_TYPE(wxEVT_IPC_CALL)
+DEFINE_EVENT_TYPE(wxEVT_IPC_CLOSE)
eIpcThread::eIpcThread(eApp& app) : m_ipcServer(NULL), m_app(app) {
Create();
@@ -14,7 +15,7 @@ void* eIpcThread::Entry() {
m_ipcServer = NewIpcServer(*this);
m_ipcServer->run();
- delete m_ipcServer;
+ m_ipcServer->destroy(); // will clean up after itself
return NULL;
}
@@ -30,3 +31,11 @@ void eIpcThread::handle_call(IConnection& conn) {
// Notify app that there is a new call (threadsafe)
m_app.AddPendingEvent(event);
}
+
+void eIpcThread::handle_close(IConnection& conn) {
+ wxCommandEvent event(wxEVT_IPC_CLOSE, wxID_ANY);
+ event.SetClientData(&conn);
+
+ // Notify app that there is a new call (threadsafe)
+ m_app.AddPendingEvent(event);
+}
2  src/eIpcThread.h
View
@@ -27,6 +27,7 @@ class IConnections;
class IIpcServer;
DECLARE_EVENT_TYPE(wxEVT_IPC_CALL, -1)
+DECLARE_EVENT_TYPE(wxEVT_IPC_CLOSE, -1)
class eIpcThread : public wxThread, public IIpcHandler {
public:
@@ -36,6 +37,7 @@ class eIpcThread : public wxThread, public IIpcHandler {
void stop(); // Threadsafe stop of server
void handle_call(IConnection& conn);
+ void handle_close(IConnection& conn);
private:
IIpcServer* m_ipcServer;
4 src/eServer.cpp
View
@@ -14,6 +14,10 @@ void eServer::stop() {
server::stop();
}
+void eServer::destroy() {
+ delete this;
+}
+
hessian_ipc::connection* eServer::new_connection() {
return new eConnection(io_service_, connection_manager_, m_handler);
}
1  src/eServer.h
View
@@ -14,6 +14,7 @@ class eServer : public hessian_ipc::server, public IIpcServer {
virtual void run();
virtual void stop();
+ virtual void destroy();
hessian_ipc::connection* new_connection();
152 src/hessian_ipc/connection.cpp
View
@@ -6,7 +6,8 @@
namespace hessian_ipc {
connection::connection(boost::asio::io_service& io_service, connection_manager& manager)
- : io_service_(io_service), socket_(io_service), connection_manager_(manager), request_(NULL)
+ : io_service_(io_service), socket_(io_service), connection_manager_(manager), request_(NULL),
+ write_in_progress_(false)
{
}
@@ -35,6 +36,9 @@ void connection::invoke_method() {
throw hessian_ipc::value_exception("Unknown method");
}
+void connection::on_close() {
+}
+
void connection::handle_read(const boost::system::error_code& e, size_t bytes_transferred) {
if (!e) {
try {
@@ -43,15 +47,23 @@ void connection::handle_read(const boost::system::error_code& e, size_t bytes_tr
// Parse the request
if (reader_.Parse(begin, end)) {
+ // If the socket is closed while the request is
+ // being handled we could end up with writes to
+ // a dangling pointer. So we make sure it is kept
+ // alive until the request is complete
+ keep_alive_ = shared_from_this();
+
// Invoke handler
request_ = reader_.GetResultCall();
invoke_method();
}
-
- socket_.async_read_some(boost::asio::buffer(buffer_),
- boost::bind(&connection::handle_read, shared_from_this(),
- boost::asio::placeholders::error,
- boost::asio::placeholders::bytes_transferred));
+ else {
+ // More input needed
+ socket_.async_read_some(boost::asio::buffer(buffer_),
+ boost::bind(&connection::handle_read, shared_from_this(),
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred));
+ }
}
catch (hessian_ipc::value_exception& e) {
protocol_error(e.what()); // Send fault
@@ -62,26 +74,107 @@ void connection::handle_read(const boost::system::error_code& e, size_t bytes_tr
}
else if (e != boost::asio::error::operation_aborted) {
connection_manager_.stop(shared_from_this());
+ on_close();
}
}
void connection::reply_done() {
- // notify connection that it can send the reply (threadsafe)
- io_service_.post(boost::bind(&connection::send_reply, this));
+ queue_lock_.lock();
+ queue_.push_back(new vector<unsigned char>(writer_.GetOutput()));
+ reply_ptr_ = &queue_.back(); // marks only reply in queue
+ queue_lock_.unlock();
+ writer_.Reset();
+
+ // notify connection that there are new items on queue (threadsafe)
+ io_service_.post(boost::bind(&connection::send, this));
+ keep_alive_.reset();
+}
+
+void connection::notifier_done() {
+ queue_lock_.lock();
+ queue_.push_back(new vector<unsigned char>(writer_.GetOutput()));
+ queue_lock_.unlock();
+ writer_.Reset();
+
+ // notify connection that there are new items on queue (threadsafe)
+ io_service_.post(boost::bind(&connection::send, this));
+ keep_alive_.reset();
}
-void connection::send_reply() {
- // Prepare for reading next request
- reader_.Reset();
- request_ = NULL;
+void connection::send() {
+ // If there already is a write in progress, it will
+ // pick up the new items on the queue
+ if (write_in_progress_) return;
+
+ queue_lock_.lock();
+ const vector<unsigned char>* reply = !queue_.empty() ? &queue_.front() : NULL;
+ queue_lock_.unlock();
+ if (!reply) return;
// Send the reply
- const vector<unsigned char>& reply = writer_.GetOutput();
- boost::asio::async_write(socket_, boost::asio::buffer(reply),
+ write_in_progress_ = true;
+ boost::asio::async_write(socket_, boost::asio::buffer(*reply),
boost::bind(&connection::handle_write, shared_from_this(),
boost::asio::placeholders::error));
}
+void connection::handle_write(const boost::system::error_code& e) {
+ if (!e) {
+ queue_lock_.lock();
+ // Check if the item we just wrote was a reply
+ const bool was_reply = (&queue_.front() == reply_ptr_);
+ if (was_reply) reply_ptr_ = NULL;
+
+ // Are there more to send
+ queue_.pop_front();
+ const vector<unsigned char>* msg = !queue_.empty() ? &queue_.front() : NULL;
+ queue_lock_.unlock();
+
+ if (was_reply) {
+ // Prepare for reading next request
+ reader_.Reset();
+ request_ = NULL;
+
+ // Read next request
+ socket_.async_read_some(boost::asio::buffer(buffer_),
+ boost::bind(&connection::handle_read, shared_from_this(),
+ boost::asio::placeholders::error,
+ boost::asio::placeholders::bytes_transferred));
+ }
+
+ if (msg) {
+ // Send next item in queue
+ boost::asio::async_write(socket_, boost::asio::buffer(*msg),
+ boost::bind(&connection::handle_write, shared_from_this(),
+ boost::asio::placeholders::error));
+ }
+ else write_in_progress_ = false;
+ }
+ else if (e != boost::asio::error::operation_aborted) {
+ connection_manager_.stop(shared_from_this());
+ on_close();
+ }
+}
+
+void connection::handle_write_and_close(const boost::system::error_code& e) {
+ if (!e) {
+ // Initiate graceful connection closure.
+ boost::system::error_code ignored_ec;
+ socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
+ }
+
+ if (e != boost::asio::error::operation_aborted) {
+ connection_manager_.stop(shared_from_this());
+ on_close();
+ }
+}
+
+int connection::get_parameter_int(size_t pos) {
+ if (pos >= request_->GetParameterCount()) throw hessian_ipc::value_exception("Wrong number of arguments.");
+ const hessian_ipc::Value& arg = request_->GetParameter(pos);
+ return arg.GetInt();
+}
+
void connection::protocol_error(const string& msg) {
writer_.write_fault(hessian_ipc::ProtocolException, msg);
const vector<unsigned char>& reply = writer_.GetOutput();
@@ -112,35 +205,4 @@ void connection::service_error(const string& msg) {
boost::asio::placeholders::error));
}
-void connection::handle_write(const boost::system::error_code& e) {
- if (!e) {
- // Read next request
- socket_.async_read_some(boost::asio::buffer(buffer_),
- boost::bind(&connection::handle_read, shared_from_this(),
- boost::asio::placeholders::error,
- boost::asio::placeholders::bytes_transferred));
- }
- else if (e != boost::asio::error::operation_aborted) {
- connection_manager_.stop(shared_from_this());
- }
-}
-
-void connection::handle_write_and_close(const boost::system::error_code& e) {
- if (!e) {
- // Initiate graceful connection closure.
- boost::system::error_code ignored_ec;
- socket_.shutdown(boost::asio::ip::tcp::socket::shutdown_both, ignored_ec);
- }
-
- if (e != boost::asio::error::operation_aborted) {
- connection_manager_.stop(shared_from_this());
- }
-}
-
-int connection::get_parameter_int(size_t pos) {
- if (pos >= request_->GetParameterCount()) throw hessian_ipc::value_exception("Wrong number of arguments.");
- const hessian_ipc::Value& arg = request_->GetParameter(pos);
- return arg.GetInt();
-}
-
} // namespace hessian_ipc
19 src/hessian_ipc/connection.h
View
@@ -4,7 +4,9 @@
#include <boost/asio.hpp>
#include <boost/noncopyable.hpp>
#include <boost/enable_shared_from_this.hpp>
-#include <vector>
+#include <boost/thread/mutex.hpp>
+#include <boost/ptr_container/ptr_deque.hpp>
+#include <deque>
#include "hessian_reader.h"
namespace hessian_ipc {
@@ -28,10 +30,12 @@ class connection
void stop(); // Stop all asynchronous operations associated with the connection.
void reply_done();
+ void notifier_done();
protected:
// Method handling
virtual void invoke_method();
+ virtual void on_close();
// Functions used by method handlers
int get_parameter_int(size_t pos);
@@ -47,7 +51,7 @@ class connection
private:
// Handle completion of read operations
void handle_read(const boost::system::error_code& e, size_t bytes_transferred);
- void send_reply();
+ void send();
// Handle completion of write operations
void handle_write(const boost::system::error_code& e);
@@ -60,9 +64,14 @@ class connection
// Member variables
boost::asio::io_service& io_service_;
- boost::asio::ip::tcp::socket socket_; // Socket for the connection.
- connection_manager& connection_manager_; // The manager for this connection.
- std::vector<unsigned char> buffer_; // Buffer for incoming data.
+ boost::asio::ip::tcp::socket socket_; // Socket for the connection.
+ connection_manager& connection_manager_; // The manager for this connection.
+ std::vector<unsigned char> buffer_; // Buffer for incoming data.
+ boost::shared_ptr<connection> keep_alive_; // Ensure that conn is not deleted during calls
+ boost::mutex queue_lock_;
+ std::vector<unsigned char>* reply_ptr_; // Only one item in queue can be a reply
+ boost::ptr_deque<std::vector<unsigned char>> queue_;
+ bool write_in_progress_;
};
typedef boost::shared_ptr<connection> connection_ptr;
72 src/hessian_ipc/hessian_reader.cpp
View
@@ -23,37 +23,56 @@ bool Reader::Parse(vector<unsigned char>::const_iterator begin, vector<unsigned
const bool result = ParseValue();
if (!result) return false; // need more input
- if (m_result->IsList()) {
- m_stateStack.push_back(new savedstate(call_method, 0, m_result));
+ if (m_result->IsList() || m_result->IsHandle()) {
+ m_stateStack.push_back(new savedstate(m_state, 0, m_result));
m_state = value_start;
continue;
}
- if (m_stateStack.empty()) return true;
- savedstate& s = m_stateStack.back();
+ while (!m_stateStack.empty()) {
+ savedstate& s = m_stateStack.back();
+ bool needmore = true;
- switch (s.state) {
- case call_method:
- s.value->AsCall().SetMethod(m_result);
- s.state = call_len;
- m_state = value_start;
- continue; //ParseValue();
- case call_len:
- s.len_left = m_result->GetInt();
- s.state = call_args;
- m_state = value_start;
- continue; //ParseValue();
- case call_args:
- s.value->AsCall().AddParameter(m_result);
- m_state = value_start;
- if (--s.len_left) break; //ParseValue();
- else {
+ switch (s.state) {
+ case call_method:
+ s.value->AsCall().SetMethod(m_result);
+ s.state = call_len;
+ m_state = value_start;
+ break; //ParseValue();
+ case call_len:
+ s.len_left = m_result->GetInt();
+ if (s.len_left == 0) {
+ m_result = s.value;
+ return true; // no args
+ }
+ s.state = call_args;
+ m_state = value_start;
+ break; //ParseValue();
+ case call_args:
+ s.value->AsCall().AddParameter(m_result);
+ m_state = value_start;
+ if (--s.len_left) break; //ParseValue();
+ else {
+ m_result = s.value;
+ return true; // call parsed
+ }
+ case proxy_handle:
+ s.value->AsInteger() = m_result->GetInt();
m_result = s.value;
- return true; // call parsed
+
+ needmore = false;
+ break;
+ default:
+ throw value_exception("invalid state");
+ }
+
+ if (needmore) break; // Parse more values;
+ else {
+ m_state = value_start;
+ m_stateStack.pop_back();
}
- default:
- throw value_exception("invalid state");
}
+ continue;
}
switch (m_state) {
@@ -294,8 +313,11 @@ bool Reader::ParseValue() {
case 0x7c: case 0x7d: case 0x7e: case 0x7f:
break;
-
-
+ // Remote Proxy Handle
+ case 'P':
+ m_result.reset(new ProxyHandle());
+ m_state = proxy_handle;
+ return true;
default:
throw value_exception("invalid tag");
1  src/hessian_ipc/hessian_reader.h
View
@@ -60,6 +60,7 @@ namespace hessian_ipc {
string_len2,
string_data,
string_next,
+ proxy_handle,
value_last
};
61 src/hessian_ipc/hessian_values.cpp
View
@@ -47,6 +47,28 @@ void Writer::Reset() {
objectMap.clear();
}
+void Writer::write_reply(const unsigned char* value, size_t len) {
+ Reset();
+
+ out.push_back('R');
+ write_binary(value, len);
+}
+
+void Writer::write_reply_handle(int handle) {
+ Reset();
+
+ out.push_back('R');
+ write_handle(handle);
+}
+
+void Writer::write_notifier_ended(unsigned int notifier_id) {
+ Reset();
+
+ out.push_back('N');
+ write(notifier_id);
+ write_null();
+}
+
void Writer::write_null() {
out.push_back('N');
}
@@ -205,6 +227,11 @@ void Writer::write_date(time_t value) {
out.push_back(b8);
}
+void Writer::write(const char* value) {
+ const string str(value);
+ write(str);
+}
+
// write string
void Writer::write(const string& value) {
size_t len = utf8_len(value);
@@ -241,7 +268,8 @@ void Writer::write(const string& value) {
if (len <= 0x1f) {
out.push_back((unsigned char)len); // single octet length
- out.insert(out.end(), str + offset, str + offset + len);
+ const size_t bytelen = value.size() - offset;
+ out.insert(out.end(), str + offset, str + offset + bytelen);
}
else if (len <= 0x3ff) {
// pack in two octets
@@ -249,7 +277,8 @@ void Writer::write(const string& value) {
const unsigned char b8 = len & 0x000000FF;
out.push_back(b16);
out.push_back(b8);
- out.insert(out.end(), str + offset, str + offset + len);
+ const size_t bytelen = value.size() - offset;
+ out.insert(out.end(), str + offset, str + offset + bytelen);
}
else {
// tag + double octets
@@ -258,18 +287,29 @@ void Writer::write(const string& value) {
out.push_back('S');
out.push_back(b16);
out.push_back(b8);
- out.insert(out.end(), str + offset, str + offset + len);
+ const size_t bytelen = value.size() - offset;
+ out.insert(out.end(), str + offset, str + offset + bytelen);
}
}
}
+void Writer::write(const vector<unsigned char>& binary) {
+ if (binary.empty()) write_binary(NULL, 0);
+ else write_binary(&*binary.begin(), binary.size());
+}
+
+void Writer::write(const vector<char>& binary) {
+ if (binary.empty()) write_binary(NULL, 0);
+ else write_binary((const unsigned char*)&*binary.begin(), binary.size());
+}
+
// write binary
void Writer::write_binary(const unsigned char* value, size_t len) {
size_t offset = 0;
// split in chunks
while (len > 0x8000) {
- out.push_back('A');
+ out.push_back('b');
out.push_back(0x80);
out.push_back(0x00);
out.insert(out.end(), value + offset, value + offset + 0x8000);
@@ -281,13 +321,6 @@ void Writer::write_binary(const unsigned char* value, size_t len) {
if (len <= 0x0f) {
out.push_back(0x20 + (unsigned char)len); // single octet length
}
- else if (len <= 0x3ff) {
- // pack in two octets
- const unsigned char b16 = 0x34 + ((len >> 8) & 0xFF);
- const unsigned char b8 = len & 0xFF;
- out.push_back(b16);
- out.push_back(b8);
- }
else {
// tag + double octets
const unsigned char b16 = (len >> 8) & 0xFF;
@@ -299,6 +332,11 @@ void Writer::write_binary(const unsigned char* value, size_t len) {
out.insert(out.end(), value + offset, value + offset + len);
}
+void Writer::write_handle(int handle) {
+ out.push_back('P'); // proxy object handle
+ write(handle);
+}
+
void Writer::write_direct(unsigned char c) {
out.push_back(c);
}
@@ -353,6 +391,7 @@ void Writer::write_fault(fault_type type, const string& msg) {
Reset();
out.push_back('F');
+ out.push_back('H');
write("code");
switch (type) {
20 src/hessian_ipc/hessian_values.h
View
@@ -52,6 +52,7 @@ namespace hessian_ipc {
Writer() {};
void Reset();
+ bool IsEmpty() const {return out.empty();};
const vector<unsigned char>& GetOutput() const {return out;};
// Calls
@@ -60,6 +61,10 @@ namespace hessian_ipc {
template<class T1, class T2> void call(const string& method, const T1& arg1, const T2& arg2);
template<class T> void write_reply(const T& value);
+ void write_reply(const unsigned char* value, size_t len);
+ void write_reply_handle(int handle);
+ template<class T> void write_notifier(unsigned int notifier_id, const T& value);
+ void write_notifier_ended(unsigned int notifier_id);
void write_fault(fault_type type, const string& msg);
// Variable writers
@@ -67,15 +72,19 @@ namespace hessian_ipc {
void write(int value);
void write(unsigned int value);
void write(long long value);
+ void write(const char* value);
void write(const string& value);
void write(const Value& value);
void write(const ObjectMixin& value);
+ void write(const vector<unsigned char>& binary);
+ void write(const vector<char>& binary);
template<class T> void write(const vector<T>& value);
template<class T1, class T2> void write(const map<T1,T2>& value);
void write_null();
void write_date(time_t value);
void write_binary(const unsigned char* value, size_t len);
+ void write_handle(int handle);
void write_direct(unsigned char c);
private:
@@ -100,6 +109,7 @@ namespace hessian_ipc {
virtual bool IsObject() const {return false;};
virtual bool IsCall() const {return false;};
virtual bool IsStringChunk() const {return false;};
+ virtual bool IsHandle() const {return false;};
// get the value
virtual int GetInt() const {throw value_exception("invalid value access");};
@@ -173,6 +183,14 @@ namespace hessian_ipc {
int m_value;
};
+ class ProxyHandle : public Integer {
+ public:
+ ProxyHandle() {};
+ ProxyHandle(int value) : Integer(value) {};
+
+ bool IsHandle() const {return true;};
+ };
+
class Long : public Value {
public:
Long() : m_value(0) {};
@@ -273,6 +291,8 @@ namespace hessian_ipc {
// get the value
const string& GetMethod() const {return m_method;};
+ bool IsObjectCall() const {return !m_parameters.empty() && m_parameters[0].IsHandle();};
+ bool HasParameters() const {return !m_parameters.empty();};
size_t GetParameterCount() const {return m_parameters.size();};
const Value& GetParameter(size_t index) const {return m_parameters[index];};
12 src/hessian_ipc/hessian_writers.h
View
@@ -37,10 +37,16 @@ template<class T> void Writer::write_reply(const T& value) {
write(value);
}
-template<class T> void Writer::write(const vector<T>& value) {
- const size_t len = value.size();
+template<class T> void Writer::write_notifier(unsigned int notifier_id, const T& value) {
+ Reset();
- out += x57; // tag for variable-length untyped list
+ out.push_back('N');
+ write(notifier_id);
+ write(value);
+}
+
+template<class T> void Writer::write(const vector<T>& value) {
+ out.push_back(0x57); // tag for variable-length untyped list
for (vector<T>::const_iterator p = value.begin(); p != value.end(); ++p) {
write(*p);
}
2  src/hessian_ipc/server.cpp
View
@@ -11,7 +11,7 @@ server::server(const std::string& address, const std::string& port)
{
// Open the acceptor with the option to reuse the address (i.e. SO_REUSEADDR).
boost::asio::ip::tcp::resolver resolver(io_service_);
- boost::asio::ip::tcp::resolver::query query(address, port);
+ boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), address, port);
boost::asio::ip::tcp::endpoint endpoint = *resolver.resolve(query);
acceptor_.open(endpoint.protocol());
acceptor_.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true));
Please sign in to comment.
Something went wrong with that request. Please try again.