mirror of
https://github.com/doublecmd/doublecmd.git
synced 2026-06-21 09:58:13 +00:00
ADD: RabbitVCS context menu integration
This commit is contained in:
parent
cb592c06a3
commit
b028dcb971
4 changed files with 153 additions and 334 deletions
|
|
@ -49,17 +49,6 @@ type
|
|||
ob_type: PPyTypeObject;
|
||||
end;
|
||||
|
||||
function PythonLoadModule(const ModuleName: UTF8String): PPyObject;
|
||||
function PythonRunFunction(Module: PPyObject; const FunctionName: UTF8String; FileList: TStrings): UTF8String;
|
||||
|
||||
var
|
||||
HasPython: Boolean = False;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
dynlibs, dl;
|
||||
|
||||
var
|
||||
// pythonrun.h
|
||||
Py_Initialize: procedure; cdecl;
|
||||
|
|
@ -74,16 +63,37 @@ var
|
|||
// abstract.h
|
||||
PyObject_CallObject: function(callable_object, args: PPyObject): PPyObject; cdecl;
|
||||
PyObject_CallFunctionObjArgs: function(callable: PPyObject): PPyObject; cdecl; varargs;
|
||||
PyObject_CallMethodObjArgs: function(o, name: PPyObject): PPyObject; cdecl; varargs;
|
||||
// stringobject.h
|
||||
PyString_AsString: function(ob: PPyObject): PAnsiChar; cdecl;
|
||||
PyString_FromString: function(s: PAnsiChar): PPyObject; cdecl;
|
||||
// listobject.h
|
||||
PyList_New: function(size: csize_t): PPyObject; cdecl;
|
||||
PyList_Size: function (ob: PPyObject): csize_t; cdecl;
|
||||
PyList_GetItem: function(ob: PPyObject; index: csize_t): PPyObject; cdecl;
|
||||
PyList_SetItem: function(ob: PPyObject; index: csize_t; item: PPyObject): cint; cdecl;
|
||||
// tupleobject.h
|
||||
PyTuple_New: function(size: csize_t): PPyObject; cdecl;
|
||||
PyTuple_SetItem: function(ob: PPyObject; index: csize_t; item: PPyObject): cint; cdecl;
|
||||
|
||||
procedure Py_DECREF(op: PPyObject);
|
||||
procedure Py_XDECREF(op: PPyObject);
|
||||
function PyStringToString(S: PPyObject): String;
|
||||
|
||||
|
||||
procedure PythonAddModulePath(const Path: UTF8String);
|
||||
function PythonLoadModule(const ModuleName: UTF8String): PPyObject;
|
||||
function PythonRunFunction(Module: PPyObject; const FunctionName, FunctionArg: UTF8String): PPyObject; overload;
|
||||
function PythonRunFunction(Module: PPyObject; const FunctionName: UTF8String; FileList: TStrings): PPyObject; overload;
|
||||
|
||||
var
|
||||
HasPython: Boolean = False;
|
||||
|
||||
implementation
|
||||
|
||||
uses
|
||||
dynlibs, dl;
|
||||
|
||||
procedure Py_DECREF(op: PPyObject);
|
||||
begin
|
||||
with op^ do begin
|
||||
|
|
@ -99,6 +109,16 @@ begin
|
|||
if Assigned(op) then Py_DECREF(op);
|
||||
end;
|
||||
|
||||
function PyStringToString(S: PPyObject): String;
|
||||
begin
|
||||
if not Assigned(S) then
|
||||
Result:= EmptyStr
|
||||
else begin
|
||||
Result:= StrPas(PyString_AsString(S));
|
||||
Py_DECREF(S);
|
||||
end;
|
||||
end;
|
||||
|
||||
function StringsToPyList(Strings: TStrings): PPyObject;
|
||||
var
|
||||
I: LongInt;
|
||||
|
|
@ -123,43 +143,57 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
procedure PythonAddModulePath(const Path: UTF8String);
|
||||
begin
|
||||
PyRun_SimpleString('import sys');
|
||||
PyRun_SimpleString(PAnsiChar('sys.path.append("' + Path + '")'));
|
||||
end;
|
||||
|
||||
function PythonLoadModule(const ModuleName: UTF8String): PPyObject;
|
||||
var
|
||||
pyName: PPyObject;
|
||||
begin
|
||||
PyRun_SimpleString('import sys');
|
||||
PyRun_SimpleString('sys.path.append("")');
|
||||
pyName:= PyString_FromString(PAnsiChar(ModuleName));
|
||||
Result:= PyImport_Import(pyName);
|
||||
Py_DECREF(pyName);
|
||||
end;
|
||||
|
||||
function PythonRunFunction(Module: PPyObject; const FunctionName: UTF8String; FileList: TStrings): UTF8String;
|
||||
function PythonCallFunction(Module: PPyObject; const FunctionName: UTF8String; FunctionArg: PPyObject): PPyObject; overload;
|
||||
var
|
||||
pyFunc, pyList: PPyObject;
|
||||
pyArgs, pyValue: PPyObject;
|
||||
pyFunc, pyArgs: PPyObject;
|
||||
begin
|
||||
if Assigned(Module) then
|
||||
begin
|
||||
pyFunc:= PyObject_GetAttrString(Module, PAnsiChar(FunctionName));
|
||||
if (Assigned(pyFunc) and (PyCallable_Check(pyFunc) <> 0)) then
|
||||
begin
|
||||
pyList:= StringsToPyList(FileList);
|
||||
pyArgs:= PyObjectsToPyTuple([pyList]);
|
||||
pyValue:= PyObject_CallObject(pyFunc, pyArgs);
|
||||
Py_XDECREF(pyList);
|
||||
pyArgs:= PyObjectsToPyTuple([FunctionArg]);
|
||||
Result:= PyObject_CallObject(pyFunc, pyArgs);
|
||||
Py_XDECREF(pyArgs);
|
||||
if (pyValue = nil) then
|
||||
if (Result = nil) then begin
|
||||
PyErr_Print()
|
||||
else begin
|
||||
Result:= StrPas(PyString_AsString(pyValue));
|
||||
Py_DECREF(pyValue);
|
||||
end;
|
||||
Py_DECREF(pyFunc);
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
|
||||
function PythonRunFunction(Module: PPyObject; const FunctionName, FunctionArg: UTF8String): PPyObject;
|
||||
var
|
||||
pyArgs: PPyObject;
|
||||
begin
|
||||
pyArgs:= PyString_FromString(PAnsiChar(FunctionArg));
|
||||
Result:= PythonCallFunction(Module, FunctionName, pyArgs);
|
||||
end;
|
||||
|
||||
function PythonRunFunction(Module: PPyObject; const FunctionName: UTF8String; FileList: TStrings): PPyObject;
|
||||
var
|
||||
pyArgs: PPyObject;
|
||||
begin
|
||||
pyArgs:= StringsToPyList(FileList);
|
||||
Result:= PythonCallFunction(Module, FunctionName, pyArgs);
|
||||
end;
|
||||
|
||||
var
|
||||
libpython: TLibHandle;
|
||||
|
||||
|
|
@ -177,10 +211,13 @@ begin
|
|||
@PyCallable_Check:= SafeGetProcAddress(libpython, 'PyCallable_Check');
|
||||
@PyObject_GetAttrString:= SafeGetProcAddress(libpython, 'PyObject_GetAttrString');
|
||||
@PyObject_CallObject:= SafeGetProcAddress(libpython, 'PyObject_CallObject');
|
||||
@PyObject_CallMethodObjArgs:= SafeGetProcAddress(libpython, 'PyObject_CallMethodObjArgs');
|
||||
@PyObject_CallFunctionObjArgs:= SafeGetProcAddress(libpython, 'PyObject_CallFunctionObjArgs');
|
||||
@PyString_AsString:= SafeGetProcAddress(libpython, 'PyString_AsString');
|
||||
@PyString_FromString:= SafeGetProcAddress(libpython, 'PyString_FromString');
|
||||
@PyList_New:= SafeGetProcAddress(libpython, 'PyList_New');
|
||||
@PyList_Size:= SafeGetProcAddress(libpython, 'PyList_Size');
|
||||
@PyList_GetItem:= SafeGetProcAddress(libpython, 'PyList_GetItem');
|
||||
@PyList_SetItem:= SafeGetProcAddress(libpython, 'PyList_SetItem');
|
||||
@PyTuple_New:= SafeGetProcAddress(libpython, 'PyTuple_New');
|
||||
@PyTuple_SetItem:= SafeGetProcAddress(libpython, 'PyTuple_SetItem');
|
||||
|
|
|
|||
|
|
@ -60,9 +60,7 @@ const
|
|||
function CheckStatus(Path: UTF8String; Recurse: Boolean32 = False;
|
||||
Invalidate: Boolean32 = True; Summary: Boolean32 = False): string;
|
||||
|
||||
function GenerateMenuConditions(Paths: TStringList): string;
|
||||
|
||||
procedure FillRabbitMenu(Menu: TPopupMenu; OnClick: TNotifyEvent; Paths: TStringList);
|
||||
procedure FillRabbitMenu(Menu: TPopupMenu; Paths: TStringList);
|
||||
|
||||
var
|
||||
RabbitVCS: Boolean = False;
|
||||
|
|
@ -70,11 +68,17 @@ var
|
|||
implementation
|
||||
|
||||
uses
|
||||
dbus, unixtype, fpjson, jsonparser, unix, uDCUtils;
|
||||
dbus, unixtype, fpjson, jsonparser, unix,
|
||||
uGlobs, uGlobsPaths, uPython;
|
||||
|
||||
const
|
||||
MODULE_NAME = 'rabbit-vcs';
|
||||
|
||||
var
|
||||
error: DBusError;
|
||||
conn: PDBusConnection;
|
||||
conn: PDBusConnection = nil;
|
||||
PythonModule: PPyObject = nil;
|
||||
ShellContextMenu: PPyObject = nil;
|
||||
|
||||
procedure Print(const sMessage: String);
|
||||
begin
|
||||
|
|
@ -93,10 +97,7 @@ begin
|
|||
Result := False;
|
||||
end;
|
||||
|
||||
function CheckService: Boolean;
|
||||
const
|
||||
RunStatusChecker = 'echo "from rabbitvcs.services.checkerservice import StatusCheckerStub' +
|
||||
#13 + 'status_checker = StatusCheckerStub()' + #13 + '" | python';
|
||||
function CheckService(const PythonScript: UTF8String): Boolean;
|
||||
var
|
||||
service_exists: dbus_bool_t;
|
||||
begin
|
||||
|
|
@ -111,7 +112,7 @@ begin
|
|||
Print('Service found running.')
|
||||
else
|
||||
begin
|
||||
Result:= fpSystem(RunStatusChecker) = 0;
|
||||
Result:= fpSystem(PythonScript) = 0;
|
||||
if Result then
|
||||
Print('Service successfully started.');
|
||||
end;
|
||||
|
|
@ -225,107 +226,25 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function GenerateMenuConditions(Paths: TStringList): string;
|
||||
procedure MenuClickHandler(Self, Sender: TObject);
|
||||
var
|
||||
I: Integer;
|
||||
Return: Boolean;
|
||||
StringPtr: PAnsiChar;
|
||||
optsPChar: PAnsiChar;
|
||||
message: PDBusMessage;
|
||||
pending: PDBusPendingCall;
|
||||
argsIter, arrayIter: DBusMessageIter;
|
||||
pyMethod, pyArgs: PPyObject;
|
||||
MenuItem: TMenuItem absolute Sender;
|
||||
begin
|
||||
if not RabbitVCS then Exit;
|
||||
|
||||
// Create a new method call and check for errors
|
||||
message := dbus_message_new_method_call(RabbitVCSAddress, // target for the method call
|
||||
RabbitVCSObject, // object to call on
|
||||
RabbitVCSInterface, // interface to call on
|
||||
'GenerateMenuConditions'); // method name
|
||||
if (message = nil) then
|
||||
if Assigned(ShellContextMenu) then
|
||||
begin
|
||||
Print('Cannot create message "GenerateMenuConditions"');
|
||||
Exit;
|
||||
end;
|
||||
|
||||
try
|
||||
dbus_message_iter_init_append(message, @argsIter);
|
||||
Return := dbus_message_iter_open_container(@argsIter, DBUS_TYPE_ARRAY, PChar(DBUS_TYPE_STRING_AS_STRING), @arrayIter) <> 0;
|
||||
if Return then
|
||||
begin
|
||||
for I := 0 to Paths.Count - 1 do
|
||||
begin
|
||||
optsPChar := PAnsiChar(Paths[I]);
|
||||
if dbus_message_iter_append_basic(@arrayIter, DBUS_TYPE_STRING, @optsPChar) = 0 then
|
||||
begin
|
||||
Print('Cannot append arguments');
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
if dbus_message_iter_close_container(@argsIter, @arrayIter) = 0 then
|
||||
begin
|
||||
Print('Cannot append arguments');
|
||||
Exit;
|
||||
end;
|
||||
end;
|
||||
|
||||
// Send message and get a handle for a reply
|
||||
if (dbus_connection_send_with_reply(conn, message, @pending, -1) = 0) then // -1 is default timeout
|
||||
begin
|
||||
Print('Error sending message');
|
||||
Exit;
|
||||
end;
|
||||
|
||||
if (pending = nil) then
|
||||
begin
|
||||
Print('Pending call is null');
|
||||
Exit;
|
||||
end;
|
||||
|
||||
dbus_connection_flush(conn);
|
||||
|
||||
finally
|
||||
dbus_message_unref(message);
|
||||
end;
|
||||
|
||||
// Block until we recieve a reply
|
||||
dbus_pending_call_block(pending);
|
||||
// Get the reply message
|
||||
message := dbus_pending_call_steal_reply(pending);
|
||||
// Free the pending message handle
|
||||
dbus_pending_call_unref(pending);
|
||||
|
||||
if (message = nil) then
|
||||
begin
|
||||
Print('Reply is null');
|
||||
Exit;
|
||||
end;
|
||||
|
||||
try
|
||||
// Read the parameters
|
||||
if (dbus_message_iter_init(message, @argsIter) <> 0) then
|
||||
begin
|
||||
if (dbus_message_iter_get_arg_type(@argsIter) = DBUS_TYPE_STRING) then
|
||||
begin
|
||||
dbus_message_iter_get_basic(@argsIter, @StringPtr);
|
||||
|
||||
Result:= StrPas(StringPtr);
|
||||
end;
|
||||
end;
|
||||
finally
|
||||
dbus_message_unref(message);
|
||||
pyMethod:= PyString_FromString('Execute');
|
||||
pyArgs:= PyString_FromString(PAnsiChar(MenuItem.Hint));
|
||||
PyObject_CallMethodObjArgs(ShellContextMenu, pyMethod, pyArgs, nil);
|
||||
Py_XDECREF(pyArgs);
|
||||
Py_XDECREF(pyMethod);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure FillRabbitMenu(Menu: TPopupMenu; OnClick: TNotifyEvent; Paths: TStringList);
|
||||
procedure FillRabbitMenu(Menu: TPopupMenu; Paths: TStringList);
|
||||
var
|
||||
I: Integer;
|
||||
Parameters,
|
||||
Conditions: String;
|
||||
MenuItem,
|
||||
SubMenu: TMenuItem;
|
||||
JAnswer : TJSONObject;
|
||||
Handler: TMethod;
|
||||
pyMethod, pyValue, pyArgs: PPyObject;
|
||||
|
||||
procedure SetBitmap(Item: TMenuItem; const IconName: String);
|
||||
var
|
||||
|
|
@ -344,189 +263,81 @@ var
|
|||
end;
|
||||
end;
|
||||
|
||||
procedure AddSeparator(MenuTarget: TMenuItem);
|
||||
procedure BuildMenu(pyMenu: PPyObject; BaseItem: TMenuItem);
|
||||
var
|
||||
Index: Integer;
|
||||
IconName: String;
|
||||
MenuItem: TMenuItem;
|
||||
pyItem, pyObject: PPyObject;
|
||||
begin
|
||||
MenuItem:= TMenuItem.Create(Menu);
|
||||
MenuItem.Caption:= '-';
|
||||
MenuTarget.Add(MenuItem);
|
||||
for Index:= 0 to PyList_Size(pyMenu) - 1 do
|
||||
begin
|
||||
pyItem:= PyList_GetItem(pyMenu, Index);
|
||||
MenuItem:= TMenuItem.Create(BaseItem);
|
||||
pyObject:= PyObject_GetAttrString(pyItem, 'label');
|
||||
MenuItem.Caption:= PyStringToString(pyObject);
|
||||
if MenuItem.Caption <> '-' then
|
||||
begin
|
||||
pyObject:= PyObject_GetAttrString(pyItem, 'identifier');
|
||||
MenuItem.Hint:= PyStringToString(pyObject);
|
||||
if Length(MenuItem.Hint) > 0 then begin
|
||||
MenuItem.OnClick:= TNotifyEvent(Handler);
|
||||
end;
|
||||
pyObject:= PyObject_GetAttrString(pyItem, 'icon');
|
||||
IconName:= PyStringToString(pyObject);
|
||||
if Length(IconName) > 0 then SetBitmap(MenuItem, IconName);
|
||||
end;
|
||||
pyObject:= PyObject_GetAttrString(pyItem, 'menu');
|
||||
if Assigned(pyObject) and (PyList_Size(pyObject) > 0) then
|
||||
begin
|
||||
BuildMenu(pyObject, MenuItem);
|
||||
Py_DECREF(pyObject);
|
||||
end;
|
||||
BaseItem.Add(MenuItem);
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
Conditions:= GenerateMenuConditions(Paths);
|
||||
for I := 0 to Paths.Count - 1 do
|
||||
Parameters := Parameters + ' ' + QuoteStr(Paths[I]);
|
||||
with TJSONParser.Create(Conditions) do
|
||||
try
|
||||
JAnswer:= Parse as TJSONObject;
|
||||
with JAnswer do
|
||||
try
|
||||
// (MenuUpdate, None),
|
||||
if (Booleans['is_in_a_or_a_working_copy'] and
|
||||
Booleans['is_versioned'] and
|
||||
not Booleans['is_added']) then
|
||||
begin
|
||||
MenuItem:= TMenuItem.Create(Menu);
|
||||
MenuItem.OnClick:= OnClick;
|
||||
MenuItem.Caption:= 'Update';
|
||||
MenuItem.Hint:= 'rabbitvcs update' + Parameters;
|
||||
SetBitmap(MenuItem, 'rabbitvcs-update');
|
||||
Menu.Items.Add(MenuItem);
|
||||
end;
|
||||
// (MenuCommit, None),
|
||||
if Booleans['is_svn'] and Booleans['is_in_a_or_a_working_copy'] and
|
||||
(Booleans['is_dir'] or Booleans['is_added'] or Booleans['is_modified'] or
|
||||
Booleans['is_deleted'] or not Booleans['is_versioned']) then
|
||||
begin
|
||||
MenuItem:= TMenuItem.Create(Menu);
|
||||
MenuItem.OnClick:= OnClick;
|
||||
MenuItem.Caption:= 'Commit';
|
||||
MenuItem.Hint:= 'rabbitvcs commit' + Parameters;
|
||||
SetBitmap(MenuItem, 'rabbitvcs-commit');
|
||||
Menu.Items.Add(MenuItem);
|
||||
end;
|
||||
// (MenuRabbitVCSSvn, [
|
||||
if Booleans['is_svn'] or not Booleans['is_in_a_or_a_working_copy'] then
|
||||
begin
|
||||
SubMenu:= TMenuItem.Create(Menu);
|
||||
SubMenu.Caption:= 'RabbitVCS SVN';
|
||||
SetBitmap(SubMenu, 'rabbitvcs');
|
||||
Menu.Items.Add(SubMenu);
|
||||
|
||||
// (MenuCheckout, None),
|
||||
if (Integers['length'] = 1) and Booleans['is_dir'] and
|
||||
not Booleans['is_working_copy'] then
|
||||
begin
|
||||
MenuItem:= TMenuItem.Create(Menu);
|
||||
MenuItem.OnClick:= OnClick;
|
||||
MenuItem.Caption:= 'Checkout...';
|
||||
MenuItem.Hint:= 'rabbitvcs checkout' + Parameters;
|
||||
SetBitmap(MenuItem, 'rabbitvcs-checkout');
|
||||
SubMenu.Add(MenuItem);
|
||||
end;
|
||||
// (MenuCompareTool, None),
|
||||
if ((Integers['length'] = 1) and
|
||||
Booleans['is_in_a_or_a_working_copy'] and
|
||||
(Booleans['is_modified'] or Booleans['has_modified'] or
|
||||
Booleans['is_conflicted'] or Booleans['has_conflicted'])) then
|
||||
begin
|
||||
MenuItem:= TMenuItem.Create(Menu);
|
||||
MenuItem.OnClick:= OnClick;
|
||||
MenuItem.Caption:= 'Compare with base';
|
||||
MenuItem.Hint:= 'rabbitvcs diff -s' + Parameters;
|
||||
SetBitmap(MenuItem, 'rabbitvcs-compare');
|
||||
SubMenu.Add(MenuItem);
|
||||
end;
|
||||
// (MenuShowLog, None),
|
||||
if ((Integers['length'] = 1) and
|
||||
Booleans['is_in_a_or_a_working_copy'] and
|
||||
Booleans['is_versioned'] and
|
||||
not Booleans['is_added']) then
|
||||
begin
|
||||
MenuItem:= TMenuItem.Create(Menu);
|
||||
MenuItem.OnClick:= OnClick;
|
||||
MenuItem.Caption:= 'Show Log';
|
||||
MenuItem.Hint:= 'rabbitvcs log' + Parameters;
|
||||
SetBitmap(MenuItem, 'rabbitvcs-show_log');
|
||||
SubMenu.Add(MenuItem);
|
||||
end;
|
||||
// (MenuRepoBrowser, None),
|
||||
begin
|
||||
MenuItem:= TMenuItem.Create(Menu);
|
||||
MenuItem.OnClick:= OnClick;
|
||||
MenuItem.Caption:= 'Repository Browser';
|
||||
MenuItem.Hint:= 'rabbitvcs browser' + Parameters;
|
||||
SetBitmap(MenuItem, 'system-search');
|
||||
SubMenu.Add(MenuItem);
|
||||
end;
|
||||
// (MenuCheckForModifications, None),
|
||||
if (Booleans['is_working_copy'] or
|
||||
Booleans['is_versioned']) then
|
||||
begin
|
||||
MenuItem:= TMenuItem.Create(Menu);
|
||||
MenuItem.OnClick:= OnClick;
|
||||
MenuItem.Caption:= 'Check for Modifications...';
|
||||
MenuItem.Hint:= 'rabbitvcs checkmods' + Parameters;
|
||||
SetBitmap(MenuItem, 'rabbitvcs-checkmods');
|
||||
SubMenu.Add(MenuItem);
|
||||
end;
|
||||
// (MenuSeparator, None),
|
||||
AddSeparator(SubMenu);
|
||||
// (MenuAdd, None),
|
||||
if (Booleans['is_svn'] and
|
||||
((Booleans['is_dir'] and Booleans['is_in_a_or_a_working_copy']) or
|
||||
((not Booleans['is_dir']) and Booleans['is_in_a_or_a_working_copy'] and
|
||||
not Booleans['is_versioned']) )) then
|
||||
begin
|
||||
MenuItem:= TMenuItem.Create(Menu);
|
||||
MenuItem.OnClick:= OnClick;
|
||||
MenuItem.Caption:= 'Add';
|
||||
MenuItem.Hint:= 'rabbitvcs add' + Parameters;
|
||||
SetBitmap(MenuItem, 'rabbitvcs-add');
|
||||
SubMenu.Add(MenuItem);
|
||||
end;
|
||||
// (MenuSeparator, None),
|
||||
AddSeparator(SubMenu);
|
||||
// (MenuUpdateToRevision, None),
|
||||
if ((Integers['length'] = 1) and
|
||||
Booleans['is_versioned'] and
|
||||
Booleans['is_in_a_or_a_working_copy']) then
|
||||
begin
|
||||
MenuItem:= TMenuItem.Create(Menu);
|
||||
MenuItem.OnClick:= OnClick;
|
||||
MenuItem.Caption:= 'Update to revision...';
|
||||
MenuItem.Hint:= 'rabbitvcs updateto' + Parameters;
|
||||
SetBitmap(MenuItem, 'rabbitvcs-update');
|
||||
SubMenu.Add(MenuItem);
|
||||
end;
|
||||
// (MenuRename, None),
|
||||
if ((Integers['length'] = 1) and
|
||||
Booleans['is_in_a_or_a_working_copy'] and
|
||||
Booleans['is_versioned']) then
|
||||
begin
|
||||
MenuItem:= TMenuItem.Create(Menu);
|
||||
MenuItem.OnClick:= OnClick;
|
||||
MenuItem.Caption:= 'Rename...';
|
||||
MenuItem.Hint:= 'rabbitvcs rename' + Parameters;
|
||||
SetBitmap(MenuItem, 'rabbitvcs-rename');
|
||||
SubMenu.Add(MenuItem);
|
||||
end;
|
||||
// (MenuDelete, None),
|
||||
if (Booleans['exists'] or Booleans['is_versioned']) and
|
||||
not Booleans['is_deleted'] then
|
||||
begin
|
||||
MenuItem:= TMenuItem.Create(Menu);
|
||||
MenuItem.OnClick:= OnClick;
|
||||
MenuItem.Caption:= 'Delete';
|
||||
MenuItem.Hint:= 'rabbitvcs delete' + Parameters;
|
||||
SetBitmap(MenuItem, 'rabbitvcs-delete');
|
||||
SubMenu.Add(MenuItem);
|
||||
end;
|
||||
end;
|
||||
except
|
||||
Exit;
|
||||
Py_XDECREF(ShellContextMenu);
|
||||
ShellContextMenu:= PythonRunFunction(PythonModule, 'GetContextMenu', Paths);
|
||||
if Assigned(ShellContextMenu) then
|
||||
begin
|
||||
Handler.Data:= Menu;
|
||||
Handler.Code:= @MenuClickHandler;
|
||||
pyMethod:= PyString_FromString('GetMenu');
|
||||
pyValue:= PyObject_CallMethodObjArgs(ShellContextMenu, pyMethod, nil);
|
||||
if Assigned(pyValue) then
|
||||
begin
|
||||
BuildMenu(pyValue, Menu.Items);
|
||||
Py_DECREF(pyValue);
|
||||
end;
|
||||
JAnswer.Free;
|
||||
finally
|
||||
Free;
|
||||
Py_XDECREF(pyMethod);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure Initialize;
|
||||
var
|
||||
PythonPath: UTF8String;
|
||||
begin
|
||||
dbus_error_init(@error);
|
||||
conn := dbus_bus_get(DBUS_BUS_SESSION, @error);
|
||||
if CheckError('Cannot acquire connection to DBUS session bus', @error) then
|
||||
Exit;
|
||||
RabbitVCS:= CheckService;
|
||||
PythonPath:= gpExePath + 'tools';
|
||||
RabbitVCS:= CheckService(PythonPath + PathDelim + MODULE_NAME + '.py');
|
||||
if RabbitVCS then begin
|
||||
PythonAddModulePath(PythonPath);
|
||||
PythonModule:= PythonLoadModule(MODULE_NAME);
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure Finalize;
|
||||
begin
|
||||
dbus_connection_close(conn);
|
||||
if Assigned(conn) then dbus_connection_unref(conn);
|
||||
end;
|
||||
|
||||
initialization
|
||||
Initialize;
|
||||
RegisterInitialization(@Initialize);
|
||||
|
||||
finalization
|
||||
Finalize;
|
||||
|
|
|
|||
|
|
@ -382,7 +382,7 @@ begin
|
|||
miOpenWith.Add(mi);
|
||||
|
||||
{$IF DEFINED(RabbitVCS)}
|
||||
FillRabbitMenu(Self, OpenWithMenuItemSelect, FileNames);
|
||||
FillRabbitMenu(Self, FileNames);
|
||||
{$ENDIF}
|
||||
|
||||
finally
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class DCMenuItem:
|
|||
identifier = None
|
||||
label = None
|
||||
icon = None
|
||||
menu = None
|
||||
menu = []
|
||||
|
||||
def connect(self, signal, *callback):
|
||||
return
|
||||
|
|
@ -59,18 +59,15 @@ class DCContextMenu(MenuBuilder):
|
|||
if type(item) is MenuSeparator:
|
||||
menuitem.label = "-"
|
||||
else:
|
||||
if item.callback_name != None:
|
||||
menuitem.identifier = "RabbitVCS::" + item.callback_name
|
||||
else:
|
||||
menuitem.identifier = item.identifier
|
||||
menuitem.label = item.make_label()
|
||||
menuitem.icon = item.icon
|
||||
menuitem.label = item.make_label()
|
||||
menuitem.identifier = item.callback_name
|
||||
|
||||
return menuitem
|
||||
|
||||
def attach_submenu(self, menu_node, submenu_list):
|
||||
menu_node.menu = []
|
||||
menu_node.identifier = None
|
||||
menu_node.identifier = ""
|
||||
for item in submenu_list:
|
||||
menu_node.menu.append(item)
|
||||
|
||||
|
|
@ -80,22 +77,15 @@ class DCContextMenu(MenuBuilder):
|
|||
class DCMainContextMenu(MainContextMenu):
|
||||
"""Double Commander main context menu class"""
|
||||
|
||||
def BuildMenu(self, menu):
|
||||
result = ""
|
||||
for item in menu:
|
||||
result += "<item caption=\"%s\">\n" % item.label
|
||||
if item.identifier != None:
|
||||
result += "<command>%s</command>\n" % item.identifier
|
||||
if item.icon != None:
|
||||
result += "<icon>" + item.icon + "</icon>\n"
|
||||
if item.menu != None:
|
||||
result += self.BuildMenu(item.menu)
|
||||
result += "</item>\n"
|
||||
return result
|
||||
def Execute(self, identifier):
|
||||
# Try to find and execute callback function
|
||||
if hasattr(self.callbacks, identifier):
|
||||
function = getattr(self.callbacks, identifier)
|
||||
if callable(function):
|
||||
function(self, None)
|
||||
|
||||
def GetXmlMenu(self):
|
||||
menu = DCContextMenu(self.structure, self.conditions, self.callbacks).menu
|
||||
return "<menu>\n" + self.BuildMenu(menu) + "</menu>"
|
||||
def GetMenu(self):
|
||||
return DCContextMenu(self.structure, self.conditions, self.callbacks).menu
|
||||
|
||||
def GetContextMenu(paths):
|
||||
upaths = []
|
||||
|
|
@ -104,28 +94,9 @@ def GetContextMenu(paths):
|
|||
|
||||
sender = DCSender()
|
||||
base_dir = os.path.dirname(upaths[0])
|
||||
return DCMainContextMenu(sender, base_dir, upaths, None).GetXmlMenu()
|
||||
|
||||
def Execute(identifier, paths):
|
||||
sender = DCSender()
|
||||
base_dir = os.path.dirname(paths[0])
|
||||
vcs_client = rabbitvcs.vcs.create_vcs_instance()
|
||||
action = MenuItem.make_default_name(identifier)
|
||||
callbacks = MainContextMenuCallbacks(sender, base_dir, vcs_client, paths)
|
||||
# Try to find and execute callback function
|
||||
if hasattr(callbacks, action):
|
||||
function = getattr(callbacks, action)
|
||||
if callable(function):
|
||||
function(sender, None)
|
||||
return DCMainContextMenu(sender, base_dir, upaths, None)
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
args = sys.argv
|
||||
argc = len(args)
|
||||
status_checker = StatusCheckerStub()
|
||||
|
||||
if argc < 2:
|
||||
status_checker = StatusCheckerStub()
|
||||
elif (argc > 2):
|
||||
args.pop(0)
|
||||
identifier = args.pop(0)
|
||||
Execute(identifier, args)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue