소스 검색

ADD: RabbitVCS context menu integration

Alexander Koblov 11 년 전
부모
커밋
b028dcb971
4개의 변경된 파일153개의 추가작업 그리고 334개의 파일을 삭제
  1. 61 24
      src/platform/unix/upython.pas
  2. 76 265
      src/platform/unix/urabbitvcs.pas
  3. 1 1
      src/platform/unix/ushellcontextmenu.pas
  4. 15 44
      tools/rabbit-vcs.py

+ 61 - 24
src/platform/unix/upython.pas

@@ -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');

+ 76 - 265
src/platform/unix/urabbitvcs.pas

@@ -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
-  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
+  if Assigned(ShellContextMenu) 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);
-  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
+    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
-        MenuItem:= TMenuItem.Create(Menu);
-        MenuItem.OnClick:= OnClick;
-        MenuItem.Caption:= 'Commit';
-        MenuItem.Hint:= 'rabbitvcs commit' + Parameters;
-        SetBitmap(MenuItem, 'rabbitvcs-commit');
-        Menu.Items.Add(MenuItem);
+        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;
-      // (MenuRabbitVCSSvn, [
-      if Booleans['is_svn'] or not Booleans['is_in_a_or_a_working_copy'] then
+      pyObject:= PyObject_GetAttrString(pyItem, 'menu');
+      if Assigned(pyObject) and (PyList_Size(pyObject) > 0) 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;
+        BuildMenu(pyObject, MenuItem);
+        Py_DECREF(pyObject);
       end;
-    except
-      Exit;
+      BaseItem.Add(MenuItem);
     end;
-    JAnswer.Free;
-  finally
-    Free;
+  end;
+
+begin
+  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;
+    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;

+ 1 - 1
src/platform/unix/ushellcontextmenu.pas

@@ -382,7 +382,7 @@ begin
     miOpenWith.Add(mi);
 
 {$IF DEFINED(RabbitVCS)}
-    FillRabbitMenu(Self, OpenWithMenuItemSelect, FileNames);
+    FillRabbitMenu(Self, FileNames);
 {$ENDIF}
 
   finally

+ 15 - 44
tools/rabbit-vcs.py

@@ -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 GetXmlMenu(self):
-    menu = DCContextMenu(self.structure, self.conditions, self.callbacks).menu
-    return "<menu>\n" + self.BuildMenu(menu) + "</menu>"
+  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 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)