Started implementing the Mercurial bookmarks support.

Mon, 02 May 2011 19:59:41 +0200

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Mon, 02 May 2011 19:59:41 +0200
changeset 1011
0b118aefae5b
parent 1010
06393b5f5330
child 1012
e3c069f40a4a

Started implementing the Mercurial bookmarks support.

Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarkDialog.py file | annotate | diff | comparison | revisions
Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarkDialog.ui file | annotate | diff | comparison | revisions
Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarkRenameDialog.py file | annotate | diff | comparison | revisions
Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarkRenameDialog.ui file | annotate | diff | comparison | revisions
Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarksListDialog.py file | annotate | diff | comparison | revisions
Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarksListDialog.ui file | annotate | diff | comparison | revisions
Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/ProjectHelper.py file | annotate | diff | comparison | revisions
Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/__init__.py file | annotate | diff | comparison | revisions
Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/bookmarks.py file | annotate | diff | comparison | revisions
Plugins/VcsPlugins/vcsMercurial/HgRevisionSelectionDialog.py file | annotate | diff | comparison | revisions
Plugins/VcsPlugins/vcsMercurial/ProjectHelper.py file | annotate | diff | comparison | revisions
Plugins/VcsPlugins/vcsMercurial/hg.py file | annotate | diff | comparison | revisions
eric5.e4p file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarkDialog.py	Mon May 02 19:59:41 2011 +0200
@@ -0,0 +1,67 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the bookmark dialog.
+"""
+
+from PyQt4.QtCore import pyqtSlot
+from PyQt4.QtGui import QDialog, QDialogButtonBox
+
+from .Ui_HgBookmarkDialog import Ui_HgBookmarkDialog
+
+
+class HgBookmarkDialog(QDialog, Ui_HgBookmarkDialog):
+    """
+    Class mplementing the bookmark dialog.
+    """
+    def __init__(self, tagsList, branchesList, bookmarksList, parent=None):
+        """
+        Constructor
+        
+        @param tagsList list of tags (list of strings)
+        @param branchesList list of branches (list of strings)
+        @param bookmarksList list of bookmarks (list of strings)
+        @param parent reference to the parent widget (QWidget)
+        """
+        QDialog.__init__(self, parent)
+        self.setupUi(self)
+        
+        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False)
+       
+        self.tagCombo.addItems(sorted(tagsList))
+        self.branchCombo.addItems(["default"] + sorted(branchesList))
+        self.bookmarkCombo.addItems(sorted(bookmarksList))
+    
+    @pyqtSlot(str)
+    def on_nameEdit_textChanged(self, txt):
+        """
+        Private slot to handle changes of the bookmark name.
+        
+        @param txt text of the edit (string)
+        """
+        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(txt != "")
+    
+    def getData(self):
+        """
+        Public method to retrieve the entered data.
+        
+        @return tuple naming the revision and the bookmark name
+            (string, string)
+        """
+        if self.numberButton.isChecked():
+            rev = str(self.numberSpinBox.value())
+        elif self.idButton.isChecked():
+            rev = self.idEdit.text()
+        elif self.tagButton.isChecked():
+            rev = self.tagCombo.currentText()
+        elif self.branchButton.isChecked():
+            rev = self.branchCombo.currentText()
+        elif self.bookmarkButton.isChecked():
+            rev = self.bookmarkCombo.currentText()
+        else:
+            rev = ""
+        
+        return rev, self.nameEdit.text()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarkDialog.ui	Mon May 02 19:59:41 2011 +0200
@@ -0,0 +1,351 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>HgBookmarkDialog</class>
+ <widget class="QDialog" name="HgBookmarkDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>276</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Define Bookmark</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout">
+     <item>
+      <widget class="QLabel" name="label">
+       <property name="text">
+        <string>Name:</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="QLineEdit" name="nameEdit">
+       <property name="toolTip">
+        <string>Enter the bookmark name</string>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Revision</string>
+     </property>
+     <layout class="QGridLayout" name="gridLayout">
+      <item row="0" column="0">
+       <widget class="QRadioButton" name="numberButton">
+        <property name="toolTip">
+         <string>Select to specify a revision by number</string>
+        </property>
+        <property name="text">
+         <string>Number</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="QSpinBox" name="numberSpinBox">
+        <property name="enabled">
+         <bool>false</bool>
+        </property>
+        <property name="toolTip">
+         <string>Enter a revision number</string>
+        </property>
+        <property name="alignment">
+         <set>Qt::AlignRight</set>
+        </property>
+        <property name="minimum">
+         <number>1</number>
+        </property>
+        <property name="maximum">
+         <number>999999999</number>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="2">
+       <spacer name="spacer">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>158</width>
+          <height>20</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item row="1" column="0">
+       <widget class="QRadioButton" name="idButton">
+        <property name="toolTip">
+         <string>Select to specify a revision by changeset id</string>
+        </property>
+        <property name="text">
+         <string>Id:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1" colspan="2">
+       <widget class="QLineEdit" name="idEdit">
+        <property name="enabled">
+         <bool>false</bool>
+        </property>
+        <property name="toolTip">
+         <string>Enter a changeset id</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="0">
+       <widget class="QRadioButton" name="tagButton">
+        <property name="toolTip">
+         <string>Select to specify a revision by a tag</string>
+        </property>
+        <property name="text">
+         <string>Tag:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1" colspan="2">
+       <widget class="QComboBox" name="tagCombo">
+        <property name="enabled">
+         <bool>false</bool>
+        </property>
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Enter a tag name</string>
+        </property>
+        <property name="editable">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0">
+       <widget class="QRadioButton" name="branchButton">
+        <property name="toolTip">
+         <string>Select to specify a revision by a branch</string>
+        </property>
+        <property name="text">
+         <string>Branch:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="1" colspan="2">
+       <widget class="QComboBox" name="branchCombo">
+        <property name="enabled">
+         <bool>false</bool>
+        </property>
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Enter a branch name</string>
+        </property>
+        <property name="editable">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="0">
+       <widget class="QRadioButton" name="bookmarkButton">
+        <property name="toolTip">
+         <string>Select to specify a revision by a bookmark</string>
+        </property>
+        <property name="text">
+         <string>Bookmark:</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="1" colspan="2">
+       <widget class="QComboBox" name="bookmarkCombo">
+        <property name="enabled">
+         <bool>false</bool>
+        </property>
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <property name="toolTip">
+         <string>Enter a bookmark name</string>
+        </property>
+        <property name="editable">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+      <item row="5" column="0" colspan="3">
+       <widget class="QRadioButton" name="tipButton">
+        <property name="toolTip">
+         <string>Select tip revision of repository</string>
+        </property>
+        <property name="text">
+         <string>TIP</string>
+        </property>
+        <property name="checked">
+         <bool>true</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>nameEdit</tabstop>
+  <tabstop>numberButton</tabstop>
+  <tabstop>numberSpinBox</tabstop>
+  <tabstop>idButton</tabstop>
+  <tabstop>idEdit</tabstop>
+  <tabstop>tagButton</tabstop>
+  <tabstop>tagCombo</tabstop>
+  <tabstop>branchButton</tabstop>
+  <tabstop>branchCombo</tabstop>
+  <tabstop>bookmarkButton</tabstop>
+  <tabstop>bookmarkCombo</tabstop>
+  <tabstop>tipButton</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>HgBookmarkDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>274</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>HgBookmarkDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>numberButton</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>numberSpinBox</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>48</x>
+     <y>89</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>118</x>
+     <y>87</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>idButton</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>idEdit</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>36</x>
+     <y>109</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>105</x>
+     <y>116</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>tagButton</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>tagCombo</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>43</x>
+     <y>142</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>102</x>
+     <y>141</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>branchButton</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>branchCombo</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>42</x>
+     <y>170</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>115</x>
+     <y>171</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>bookmarkButton</sender>
+   <signal>toggled(bool)</signal>
+   <receiver>bookmarkCombo</receiver>
+   <slot>setEnabled(bool)</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>51</x>
+     <y>196</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>108</x>
+     <y>201</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarkRenameDialog.py	Mon May 02 19:59:41 2011 +0200
@@ -0,0 +1,68 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to get the data to rename a bookmark.
+"""
+
+from PyQt4.QtCore import pyqtSlot
+from PyQt4.QtGui import QDialog, QDialogButtonBox
+
+from .Ui_HgBookmarkRenameDialog import Ui_HgBookmarkRenameDialog
+
+
+class HgBookmarkRenameDialog(QDialog, Ui_HgBookmarkRenameDialog):
+    """
+    Class implementing a dialog to get the data to rename a bookmark.
+    """
+    def __init__(self, bookmarksList, parent=None):
+        """
+        Constructor
+        
+        @param bookmarksList list of bookmarks (list of strings)
+        @param parent reference to the parent widget (QWidget)
+        """
+        QDialog.__init__(self, parent)
+        self.setupUi(self)
+        
+        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(False)
+       
+        self.bookmarkCombo.addItems(sorted(bookmarksList))
+    
+    def __updateUI(self):
+        """
+        Private slot to update the UI.
+        """
+        self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(
+            self.nameEdit.text() != "" and \
+            self.bookmarkCombo.currentText() != ""
+        )
+    
+    @pyqtSlot(str)
+    def on_nameEdit_textChanged(self, txt):
+        """
+        Private slot to handle changes of the bookmark name.
+        
+        @param txt text of the edit (string)
+        """
+        self.__updateUI()
+    
+    @pyqtSlot(str)
+    def on_bookmarkCombo_editTextChanged(self, txt):
+        """
+        Private slot to handle changes of the selected bookmark.
+        
+        @param txt name of the selected bookmark (string)
+        """
+        self.__updateUI()
+    
+    def getData(self):
+        """
+        Public method to retrieve the entered data.
+        
+        @return tuple naming the new and old bookmark names
+            (string, string)
+        """
+        return self.nameEdit.text(), self.bookmarkCombo.currentText()
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarkRenameDialog.ui	Mon May 02 19:59:41 2011 +0200
@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>HgBookmarkRenameDialog</class>
+ <widget class="QDialog" name="HgBookmarkRenameDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>400</width>
+    <height>102</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Rename Bookmark</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QGridLayout" name="gridLayout">
+   <item row="0" column="0">
+    <widget class="QLabel" name="label">
+     <property name="text">
+      <string>New Name:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="0" column="1">
+    <widget class="QLineEdit" name="nameEdit">
+     <property name="toolTip">
+      <string>Enter the bookmark name</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="0">
+    <widget class="QLabel" name="label_2">
+     <property name="text">
+      <string>Bookmark:</string>
+     </property>
+    </widget>
+   </item>
+   <item row="1" column="1">
+    <widget class="QComboBox" name="bookmarkCombo">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="toolTip">
+      <string>Enter the bookmark name to be renamed</string>
+     </property>
+     <property name="editable">
+      <bool>true</bool>
+     </property>
+    </widget>
+   </item>
+   <item row="2" column="0" colspan="2">
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>nameEdit</tabstop>
+  <tabstop>bookmarkCombo</tabstop>
+  <tabstop>buttonBox</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>accepted()</signal>
+   <receiver>HgBookmarkRenameDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>buttonBox</sender>
+   <signal>rejected()</signal>
+   <receiver>HgBookmarkRenameDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarksListDialog.py	Mon May 02 19:59:41 2011 +0200
@@ -0,0 +1,279 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a dialog to show a list of bookmarks.
+"""
+
+import os
+
+from PyQt4.QtCore import pyqtSlot, QProcess, Qt, QTimer
+from PyQt4.QtGui import QDialog, QDialogButtonBox, QHeaderView, QTreeWidgetItem, \
+    QLineEdit
+
+from E5Gui import E5MessageBox
+
+from .Ui_HgBookmarksListDialog import Ui_HgBookmarksListDialog
+
+import Preferences
+
+
+class HgBookmarksListDialog(QDialog, Ui_HgBookmarksListDialog):
+    """
+    Class implementing a dialog to show a list of bookmarks.
+    """
+    def __init__(self, vcs, parent=None):
+        """
+        Constructor
+        
+        @param vcs reference to the vcs object
+        @param parent parent widget (QWidget)
+        """
+        QDialog.__init__(self, parent)
+        self.setupUi(self)
+        
+        self.buttonBox.button(QDialogButtonBox.Close).setEnabled(False)
+        self.buttonBox.button(QDialogButtonBox.Cancel).setDefault(True)
+        
+        self.process = QProcess()
+        self.vcs = vcs
+        self.__bookmarksList = None
+        
+        self.bookmarksList.headerItem().setText(self.bookmarksList.columnCount(), "")
+        self.bookmarksList.header().setSortIndicator(3, Qt.AscendingOrder)
+        
+        self.process.finished.connect(self.__procFinished)
+        self.process.readyReadStandardOutput.connect(self.__readStdout)
+        self.process.readyReadStandardError.connect(self.__readStderr)
+    
+    def closeEvent(self, e):
+        """
+        Private slot implementing a close event handler.
+        
+        @param e close event (QCloseEvent)
+        """
+        if self.process is not None and \
+           self.process.state() != QProcess.NotRunning:
+            self.process.terminate()
+            QTimer.singleShot(2000, self.process.kill)
+            self.process.waitForFinished(3000)
+        
+        e.accept()
+    
+    def start(self, path, bookmarksList):
+        """
+        Public slot to start the bookmarks command.
+        
+        @param path name of directory to be listed (string)
+        @param bookmarksList reference to string list receiving the bookmarks
+            (list of strings)
+        """
+        self.errorGroup.hide()
+        
+        self.intercept = False
+        self.activateWindow()
+        
+        self.__bookmarksList = bookmarksList
+        dname, fname = self.vcs.splitPath(path)
+        
+        # find the root of the repo
+        repodir = dname
+        while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)):
+            repodir = os.path.dirname(repodir)
+            if repodir == os.sep:
+                return
+        
+        args = []
+        args.append('bookmarks')
+        
+        self.process.kill()
+        self.process.setWorkingDirectory(repodir)
+        
+        self.process.start('hg', args)
+        procStarted = self.process.waitForStarted()
+        if not procStarted:
+            self.inputGroup.setEnabled(False)
+            self.inputGroup.hide()
+            E5MessageBox.critical(self,
+                self.trUtf8('Process Generation Error'),
+                self.trUtf8(
+                    'The process {0} could not be started. '
+                    'Ensure, that it is in the search path.'
+                ).format('hg'))
+        else:
+            self.inputGroup.setEnabled(True)
+            self.inputGroup.show()
+    
+    def __finish(self):
+        """
+        Private slot called when the process finished or the user pressed the button.
+        """
+        if self.process is not None and \
+           self.process.state() != QProcess.NotRunning:
+            self.process.terminate()
+            QTimer.singleShot(2000, self.process.kill)
+            self.process.waitForFinished(3000)
+        
+        self.inputGroup.setEnabled(False)
+        self.inputGroup.hide()
+        
+        self.buttonBox.button(QDialogButtonBox.Close).setEnabled(True)
+        self.buttonBox.button(QDialogButtonBox.Cancel).setEnabled(False)
+        self.buttonBox.button(QDialogButtonBox.Close).setDefault(True)
+        self.buttonBox.button(QDialogButtonBox.Close).setFocus(Qt.OtherFocusReason)
+        
+        self.process = None
+        
+        if self.bookmarksList.topLevelItemCount() == 0:
+            # no bookmarks defined
+            self.__generateItem(self.trUtf8("no bookmarks defined"), "", "", "")
+        self.bookmarksList.doItemsLayout()
+        self.__resizeColumns()
+        self.__resort()
+    
+    def on_buttonBox_clicked(self, button):
+        """
+        Private slot called by a button of the button box clicked.
+        
+        @param button button that was clicked (QAbstractButton)
+        """
+        if button == self.buttonBox.button(QDialogButtonBox.Close):
+            self.close()
+        elif button == self.buttonBox.button(QDialogButtonBox.Cancel):
+            self.__finish()
+    
+    def __procFinished(self, exitCode, exitStatus):
+        """
+        Private slot connected to the finished signal.
+        
+        @param exitCode exit code of the process (integer)
+        @param exitStatus exit status of the process (QProcess.ExitStatus)
+        """
+        self.__finish()
+    
+    def __resort(self):
+        """
+        Private method to resort the tree.
+        """
+        self.bookmarksList.sortItems(self.bookmarksList.sortColumn(),
+            self.bookmarksList.header().sortIndicatorOrder())
+    
+    def __resizeColumns(self):
+        """
+        Private method to resize the list columns.
+        """
+        self.bookmarksList.header().resizeSections(QHeaderView.ResizeToContents)
+        self.bookmarksList.header().setStretchLastSection(True)
+    
+    def __generateItem(self, revision, changeset, status, name):
+        """
+        Private method to generate a bookmark item in the bookmarks list.
+        
+        @param revision revision of the bookmark (string)
+        @param changeset changeset of the bookmark (string)
+        @param status of the bookmark (string)
+        @param name name of the bookmark (string)
+        """
+        itm = QTreeWidgetItem(self.bookmarksList, [
+            "{0:>7}".format(revision),
+            changeset,
+            status,
+            name])
+        itm.setTextAlignment(0, Qt.AlignRight)
+        itm.setTextAlignment(1, Qt.AlignRight)
+        itm.setTextAlignment(2, Qt.AlignHCenter)
+    
+    def __readStdout(self):
+        """
+        Private slot to handle the readyReadStdout signal.
+        
+        It reads the output of the process, formats it and inserts it into
+        the contents pane.
+        """
+        self.process.setReadChannel(QProcess.StandardOutput)
+        
+        while self.process.canReadLine():
+            s = str(self.process.readLine(),
+                    Preferences.getSystem("IOEncoding"),
+                    'replace').strip()
+            l = s.split()
+            if l[-1][0] in "1234567890":
+                # last element is a rev:changeset
+                rev, changeset = l[-1].split(":", 1)
+                del l[-1]
+                if l[0] == "*":
+                    status = "current"
+                    del l[0]
+                else:
+                    status = ""
+                name = " ".join(l)
+                self.__generateItem(rev, changeset, status, name)
+                if self.__bookmarksList is not None:
+                    self.__bookmarksList.append(name)
+    
+    def __readStderr(self):
+        """
+        Private slot to handle the readyReadStderr signal.
+        
+        It reads the error output of the process and inserts it into the
+        error pane.
+        """
+        if self.process is not None:
+            self.errorGroup.show()
+            s = str(self.process.readAllStandardError(),
+                    Preferences.getSystem("IOEncoding"),
+                    'replace')
+            self.errors.insertPlainText(s)
+            self.errors.ensureCursorVisible()
+    
+    def on_passwordCheckBox_toggled(self, isOn):
+        """
+        Private slot to handle the password checkbox toggled.
+        
+        @param isOn flag indicating the status of the check box (boolean)
+        """
+        if isOn:
+            self.input.setEchoMode(QLineEdit.Password)
+        else:
+            self.input.setEchoMode(QLineEdit.Normal)
+    
+    @pyqtSlot()
+    def on_sendButton_clicked(self):
+        """
+        Private slot to send the input to the subversion process.
+        """
+        input = self.input.text()
+        input += os.linesep
+        
+        if self.passwordCheckBox.isChecked():
+            self.errors.insertPlainText(os.linesep)
+            self.errors.ensureCursorVisible()
+        else:
+            self.errors.insertPlainText(input)
+            self.errors.ensureCursorVisible()
+        
+        self.process.write(input)
+        
+        self.passwordCheckBox.setChecked(False)
+        self.input.clear()
+    
+    def on_input_returnPressed(self):
+        """
+        Private slot to handle the press of the return key in the input field.
+        """
+        self.intercept = True
+        self.on_sendButton_clicked()
+    
+    def keyPressEvent(self, evt):
+        """
+        Protected slot to handle a key press event.
+        
+        @param evt the key press event (QKeyEvent)
+        """
+        if self.intercept:
+            self.intercept = False
+            evt.accept()
+            return
+        QDialog.keyPressEvent(self, evt)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarksListDialog.ui	Mon May 02 19:59:41 2011 +0200
@@ -0,0 +1,180 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>HgBookmarksListDialog</class>
+ <widget class="QDialog" name="HgBookmarksListDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>634</width>
+    <height>494</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Mercurial Bookmarks</string>
+  </property>
+  <property name="whatsThis">
+   <string>&lt;b&gt;Mercurial Bookmarks&lt;/b&gt;
+&lt;p&gt;This dialog shows a list of the projects bookmarks.&lt;/p&gt;</string>
+  </property>
+  <property name="sizeGripEnabled">
+   <bool>true</bool>
+  </property>
+  <layout class="QVBoxLayout">
+   <item>
+    <widget class="QTreeWidget" name="bookmarksList">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>2</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="focusPolicy">
+      <enum>Qt::NoFocus</enum>
+     </property>
+     <property name="whatsThis">
+      <string>&lt;b&gt;Bookmarks List&lt;/b&gt;
+&lt;p&gt;This shows a list of the projects bookmarks.&lt;/p&gt;</string>
+     </property>
+     <property name="alternatingRowColors">
+      <bool>true</bool>
+     </property>
+     <property name="rootIsDecorated">
+      <bool>false</bool>
+     </property>
+     <property name="itemsExpandable">
+      <bool>false</bool>
+     </property>
+     <property name="sortingEnabled">
+      <bool>true</bool>
+     </property>
+     <column>
+      <property name="text">
+       <string>Revision</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>Changeset</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>Status</string>
+      </property>
+     </column>
+     <column>
+      <property name="text">
+       <string>Name</string>
+      </property>
+     </column>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="errorGroup">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>1</verstretch>
+      </sizepolicy>
+     </property>
+     <property name="title">
+      <string>Errors</string>
+     </property>
+     <layout class="QVBoxLayout">
+      <item>
+       <widget class="QTextEdit" name="errors">
+        <property name="focusPolicy">
+         <enum>Qt::NoFocus</enum>
+        </property>
+        <property name="readOnly">
+         <bool>true</bool>
+        </property>
+        <property name="acceptRichText">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="inputGroup">
+     <property name="title">
+      <string>Input</string>
+     </property>
+     <layout class="QGridLayout">
+      <item row="1" column="1">
+       <spacer>
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="sizeType">
+         <enum>QSizePolicy::Expanding</enum>
+        </property>
+        <property name="sizeHint" stdset="0">
+         <size>
+          <width>327</width>
+          <height>29</height>
+         </size>
+        </property>
+       </spacer>
+      </item>
+      <item row="1" column="2">
+       <widget class="QPushButton" name="sendButton">
+        <property name="toolTip">
+         <string>Press to send the input to the hg process</string>
+        </property>
+        <property name="text">
+         <string>&amp;Send</string>
+        </property>
+        <property name="shortcut">
+         <string>Alt+S</string>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="0" colspan="3">
+       <widget class="QLineEdit" name="input">
+        <property name="toolTip">
+         <string>Enter data to be sent to the hg process</string>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QCheckBox" name="passwordCheckBox">
+        <property name="toolTip">
+         <string>Select to switch the input field to password mode</string>
+        </property>
+        <property name="text">
+         <string>&amp;Password Mode</string>
+        </property>
+        <property name="shortcut">
+         <string>Alt+P</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QDialogButtonBox" name="buttonBox">
+     <property name="orientation">
+      <enum>Qt::Horizontal</enum>
+     </property>
+     <property name="standardButtons">
+      <set>QDialogButtonBox::Cancel|QDialogButtonBox::Close</set>
+     </property>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <layoutdefault spacing="6" margin="11"/>
+ <pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
+ <tabstops>
+  <tabstop>input</tabstop>
+  <tabstop>passwordCheckBox</tabstop>
+  <tabstop>sendButton</tabstop>
+ </tabstops>
+ <resources/>
+ <connections/>
+</ui>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/ProjectHelper.py	Mon May 02 19:59:41 2011 +0200
@@ -0,0 +1,147 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the bookmarks extension project helper.
+"""
+
+from PyQt4.QtCore import QObject
+from PyQt4.QtGui import QMenu
+
+from E5Gui.E5Action import E5Action
+
+
+class BookmarksProjectHelper(QObject):
+    """
+    Class implementing the bookmarks extension project helper.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        QObject.__init__(self)
+        
+        self.actions = []
+        
+        self.initActions()
+    
+    def setObjects(self, vcsObject, projectObject):
+        """
+        Public method to set references to the vcs and project objects.
+        
+        @param vcsObject reference to the vcs object
+        @param projectObject reference to the project object
+        """
+        self.vcs = vcsObject
+        self.project = projectObject
+    
+    def getActions(self):
+        """
+        Public method to get a list of all actions.
+        
+        @return list of all actions (list of E5Action)
+        """
+        return self.actions[:]
+    
+    def initActions(self):
+        """
+        Public method to generate the action objects.
+        """
+        self.hgBookmarksListAct = E5Action(self.trUtf8('List bookmarks'),
+                self.trUtf8('List bookmarks...'),
+                0, 0, self, 'mercurial_list_bookmarks')
+        self.hgBookmarksListAct.setStatusTip(self.trUtf8(
+            'List bookmarks of the project'
+        ))
+        self.hgBookmarksListAct.setWhatsThis(self.trUtf8(
+            """<b>List bookmarks</b>"""
+            """<p>This lists the bookmarks of the project.</p>"""
+        ))
+        self.hgBookmarksListAct.triggered[()].connect(self.__hgBookmarksList)
+        self.actions.append(self.hgBookmarksListAct)
+    
+        self.hgBookmarkDefineAct = E5Action(self.trUtf8('Define bookmark'),
+                self.trUtf8('Define bookmark...'),
+                0, 0, self, 'mercurial_define_bookmark')
+        self.hgBookmarkDefineAct.setStatusTip(self.trUtf8(
+            'Define a bookmark for the project'
+        ))
+        self.hgBookmarkDefineAct.setWhatsThis(self.trUtf8(
+            """<b>Define bookmark</b>"""
+            """<p>This defines a bookmark for the project.</p>"""
+        ))
+        self.hgBookmarkDefineAct.triggered[()].connect(self.__hgBookmarkDefine)
+        self.actions.append(self.hgBookmarkDefineAct)
+    
+        self.hgBookmarkDeleteAct = E5Action(self.trUtf8('Delete bookmark'),
+                self.trUtf8('Delete bookmark...'),
+                0, 0, self, 'mercurial_delete_bookmark')
+        self.hgBookmarkDeleteAct.setStatusTip(self.trUtf8(
+            'Delete a bookmark of the project'
+        ))
+        self.hgBookmarkDeleteAct.setWhatsThis(self.trUtf8(
+            """<b>Delete bookmark</b>"""
+            """<p>This deletes a bookmark of the project.</p>"""
+        ))
+        self.hgBookmarkDeleteAct.triggered[()].connect(self.__hgBookmarkDelete)
+        self.actions.append(self.hgBookmarkDeleteAct)
+    
+        self.hgBookmarkRenameAct = E5Action(self.trUtf8('Rename bookmark'),
+                self.trUtf8('Rename bookmark...'),
+                0, 0, self, 'mercurial_rename_bookmark')
+        self.hgBookmarkRenameAct.setStatusTip(self.trUtf8(
+            'Rename a bookmark of the project'
+        ))
+        self.hgBookmarkRenameAct.setWhatsThis(self.trUtf8(
+            """<b>Rename bookmark</b>"""
+            """<p>This renames a bookmark of the project.</p>"""
+        ))
+        self.hgBookmarkRenameAct.triggered[()].connect(self.__hgBookmarkRename)
+        self.actions.append(self.hgBookmarkRenameAct)
+    
+    def initMenu(self, mainMenu):
+        """
+        Public method to generate the VCS menu.
+        
+        @param mainMenu reference to the main menu (QMenu)
+        @return populated menu (QMenu)
+        """
+        menu = QMenu(self.trUtf8("Bookmarks"), mainMenu)
+        
+        menu.addAction(self.hgBookmarkDefineAct)
+        menu.addAction(self.hgBookmarkDeleteAct)
+        menu.addAction(self.hgBookmarkRenameAct)
+        menu.addSeparator()
+        menu.addAction(self.hgBookmarksListAct)
+        
+        return menu
+    
+    def __hgBookmarksList(self):
+        """
+        Private slot used to list the bookmarks. 
+        """
+        self.vcs.getExtensionObject("bookmarks")\
+            .hgListBookmarks(self.project.getProjectPath())
+    
+    def __hgBookmarkDefine(self):
+        """
+        Private slot used to define a bookmark.
+        """
+        self.vcs.getExtensionObject("bookmarks")\
+            .hgBookmarkDefine(self.project.getProjectPath())
+    
+    def __hgBookmarkDelete(self):
+        """
+        Private slot used to delete a bookmark.
+        """
+        self.vcs.getExtensionObject("bookmarks")\
+            .hgBookmarkDelete(self.project.getProjectPath())
+    
+    def __hgBookmarkRename(self):
+        """
+        Private slot used to delete a bookmark.
+        """
+        self.vcs.getExtensionObject("bookmarks")\
+            .hgBookmarkRename(self.project.getProjectPath())
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/__init__.py	Mon May 02 19:59:41 2011 +0200
@@ -0,0 +1,8 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Package implementing the bookmarks extension support interface.
+"""
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/bookmarks.py	Mon May 02 19:59:41 2011 +0200
@@ -0,0 +1,156 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2011 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the bookmarks extension interface.
+"""
+
+import os
+
+from PyQt4.QtCore import QObject
+from PyQt4.QtGui import QDialog, QInputDialog
+
+from ..HgDialog import HgDialog
+
+from .HgBookmarksListDialog import HgBookmarksListDialog
+from .HgBookmarkDialog import HgBookmarkDialog
+from .HgBookmarkRenameDialog import HgBookmarkRenameDialog
+
+
+class Bookmarks(QObject):
+    """
+    Class implementing the bookmarks extension interface.
+    """
+    def __init__(self, vcs):
+        """
+        Constructor
+        """
+        QObject.__init__(self, vcs)
+        
+        self.vcs = vcs
+        
+        self.bookmarksListDlg = None
+        self.bookmarksList = []
+    
+    def shutdown(self):
+        """
+        Public method used to shutdown the bookmarks interface.
+        """
+        if self.bookmarksListDlg is not None:
+            self.bookmarksListDlg.close()
+    
+    def hgListBookmarks(self, path):
+        """
+        Public method used to list the available bookmarks.
+        
+        @param path directory name of the project (string)
+        """
+        self.bookmarksList = []
+        
+        self.bookmarksListDlg = HgBookmarksListDialog(self.vcs)
+        self.bookmarksListDlg.show()
+        self.bookmarksListDlg.start(path, self.bookmarksList)
+    
+    def hgGetLoadedBookmarksList(self):
+        """
+        Public method to get the list of loaded bookmarks.
+        
+        @return list of loaded bookmarks (list of string)
+        """
+        return self.bookmarksList[:]
+    
+    def hgBookmarkDefine(self, name):
+        """
+        Public method to define a bookmark.
+        
+        @param name file/directory name (string)
+        """
+        dname, fname = self.vcs.splitPath(name)
+        
+        # find the root of the repo
+        repodir = str(dname)
+        while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)):
+            repodir = os.path.dirname(repodir)
+            if repodir == os.sep:
+                return
+        
+        dlg = HgBookmarkDialog(self.vcs.tagsList, self.vcs.branchesList, 
+            self.bookmarksList)
+        if dlg.exec_() == QDialog.Accepted:
+            rev, bookmark = dlg.getData()
+            
+            args = []
+            args.append("bookmarks")
+            if rev:
+                args.append("--rev")
+                args.append(rev)
+            args.append(bookmark)
+            
+            dia = HgDialog(self.trUtf8('Mercurial Bookmark'))
+            res = dia.startProcess(args, repodir)
+            if res:
+                dia.exec_()
+    
+    def hgBookmarkDelete(self, name):
+        """
+        Public method to delete a bookmark.
+        
+        @param name file/directory name (string)
+        """
+        dname, fname = self.vcs.splitPath(name)
+        
+        # find the root of the repo
+        repodir = str(dname)
+        while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)):
+            repodir = os.path.dirname(repodir)
+            if repodir == os.sep:
+                return
+        
+        bookmark, ok = QInputDialog.getItem(
+            None,
+            self.trUtf8("Delete Bookmark"),
+            self.trUtf8("Select the bookmark to be deleted:"),
+            [""] + self.bookmarksList,
+            0, True)
+        if ok and bookmark:
+            args = []
+            args.append("bookmarks")
+            args.append("--delete")
+            args.append(bookmark)
+            
+            dia = HgDialog(self.trUtf8('Delete Mercurial Bookmark'))
+            res = dia.startProcess(args, repodir)
+            if res:
+                dia.exec_()
+    
+    def hgBookmarkRename(self, name):
+        """
+        Public method to rename a bookmark.
+        
+        @param name file/directory name (string)
+        """
+        dname, fname = self.vcs.splitPath(name)
+        
+        # find the root of the repo
+        repodir = str(dname)
+        while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)):
+            repodir = os.path.dirname(repodir)
+            if repodir == os.sep:
+                return
+        
+        dlg = HgBookmarkRenameDialog(self.bookmarksList)
+        if dlg.exec_() == QDialog.Accepted:
+            newName, oldName = dlg.getData()
+            
+            args = []
+            args.append("bookmarks")
+            args.append("--rename")
+            args.append(oldName)
+            args.append(newName)
+            
+            dia = HgDialog(self.trUtf8('Delete Mercurial Bookmark'))
+            res = dia.startProcess(args, repodir)
+            if res:
+                dia.exec_()
--- a/Plugins/VcsPlugins/vcsMercurial/HgRevisionSelectionDialog.py	Sun May 01 19:01:24 2011 +0200
+++ b/Plugins/VcsPlugins/vcsMercurial/HgRevisionSelectionDialog.py	Mon May 02 19:59:41 2011 +0200
@@ -40,8 +40,7 @@
         """
         Public method to retrieve the selected revision.
         
-        @return tuple naming the revision and a flag indicating a
-            forced merge (string, boolean)
+        @return selected revision (string)
         """
         if self.numberButton.isChecked():
             rev = str(self.numberSpinBox.value())
--- a/Plugins/VcsPlugins/vcsMercurial/ProjectHelper.py	Sun May 01 19:01:24 2011 +0200
+++ b/Plugins/VcsPlugins/vcsMercurial/ProjectHelper.py	Mon May 02 19:59:41 2011 +0200
@@ -16,6 +16,8 @@
 
 from VCS.ProjectHelper import VcsProjectHelper
 
+from .BookmarksExtension.ProjectHelper import BookmarksProjectHelper
+
 from E5Gui.E5Action import E5Action
 
 import UI.PixmapCache
@@ -36,6 +38,24 @@
         @param name name of this object (string)
         """
         VcsProjectHelper.__init__(self, vcsObject, projectObject, parent, name)
+        
+        # instantiate the extensions
+        self.__extensions = {
+            "bookmarks" : BookmarksProjectHelper(),
+        }
+    
+    def setObjects(self, vcsObject, projectObject):
+        """
+        Public method to set references to the vcs and project objects.
+        
+        @param vcsObject reference to the vcs object
+        @param projectObject reference to the project object
+        """
+        self.vcs = vcsObject
+        self.project = projectObject
+        
+        for extension in self.__extensions.values():
+            extension.setObjects(vcsObject, projectObject)
     
     def getActions(self):
         """
@@ -43,7 +63,10 @@
         
         @return list of all actions (list of E5Action)
         """
-        return self.actions[:]
+        actions = self.actions[:]
+        for extension in self.__extensions.values():
+            actions.extend(extension.getActions())
+        return actions
     
     def initActions(self):
         """
@@ -829,6 +852,13 @@
         bisectMenu.addAction(self.hgBisectSkipAct)
         bisectMenu.addAction(self.hgBisectResetAct)
         
+        extensionsMenu = QMenu(self.trUtf8("Extensions"), menu)
+        extensionsMenu.aboutToShow.connect(self.__showExtensionMenu)
+        self.extensionMenus = {}
+        for extensionName in self.__extensions:
+            self.extensionMenus[extensionName] = extensionsMenu.addMenu(
+                self.__extensions[extensionName].initMenu(extensionsMenu))
+        
         act = menu.addAction(
             UI.PixmapCache.getIcon(
                 os.path.join("VcsPlugins", "vcsMercurial", "icons", "mercurial.png")),
@@ -848,6 +878,8 @@
         menu.addSeparator()
         menu.addMenu(bundleMenu)
         menu.addSeparator()
+        menu.addMenu(extensionsMenu)
+        menu.addSeparator()
         menu.addAction(self.vcsNewAct)
         menu.addAction(self.vcsExportAct)
         menu.addSeparator()
@@ -890,6 +922,14 @@
         menu.addSeparator()
         menu.addAction(self.hgConfigAct)
     
+    def __showExtensionMenu(self):
+        """
+        Private slot showing the extensions menu.
+        """
+        for extensionName in self.extensionMenus:
+            self.extensionMenus[extensionName].setEnabled(
+                self.vcs.isExtensionActive(extensionName))
+    
     def __hgExtendedDiff(self):
         """
         Private slot used to perform a hg diff with the selection of revisions.
--- a/Plugins/VcsPlugins/vcsMercurial/hg.py	Sun May 01 19:01:24 2011 +0200
+++ b/Plugins/VcsPlugins/vcsMercurial/hg.py	Mon May 02 19:59:41 2011 +0200
@@ -13,7 +13,7 @@
 import urllib.parse
 import urllib.error
 
-from PyQt4.QtCore import QProcess, pyqtSignal, QFileInfo
+from PyQt4.QtCore import QProcess, pyqtSignal, QFileInfo, QFileSystemWatcher
 from PyQt4.QtGui import QApplication, QDialog, QInputDialog
 
 from E5Gui.E5Application import e5App
@@ -44,6 +44,9 @@
 from .HgBundleDialog import HgBundleDialog
 from .HgBackoutDialog import HgBackoutDialog
 from .HgServeDialog import HgServeDialog
+from .HgUtilities import getConfigPath
+
+from .BookmarksExtension.bookmarks import Bookmarks
 
 from .ProjectBrowserHelper import HgProjectBrowserHelper
 
@@ -120,6 +123,17 @@
         self.__commitDialog = None
         
         self.__forgotNames = []
+        
+        self.__activeExtensions = []
+        
+        self.__iniWatcher = QFileSystemWatcher(self)
+        self.__iniWatcher.fileChanged.connect(self.__iniFileChanged)
+        self.__iniWatcher.addPath(getConfigPath())
+        
+        # instantiate the extensions
+        self.__extensions = {
+            "bookmarks" : Bookmarks(self),
+        }
     
     def getPlugin(self):
         """
@@ -148,6 +162,10 @@
         
         if self.bundleFile and os.path.exists(self.bundleFile):
             os.remove(self.bundleFile)
+        
+        # shut down the extensions
+        for extension in self.__extensions.values():
+            extension.shutdown()
     
     def vcsExists(self):
         """
@@ -168,6 +186,7 @@
                 output = \
                     str(process.readAllStandardOutput(), ioEncoding, 'replace')
                 self.versionStr = output.splitlines()[0].split()[-1][0:-1]
+                self.__getExtensionsInfo()
                 return True, errMsg
             else:
                 if finished:
@@ -2009,7 +2028,7 @@
         res = E5MessageBox.yesNo(None,
             self.trUtf8("Rollback last transaction"),
             self.trUtf8("""Are you sure you want to rollback the last transaction?"""),
-            icon = E5MessageBox.Warning)
+            icon=E5MessageBox.Warning)
         if res:
             dia = HgDialog(self.trUtf8('Rollback last transaction'))
             res = dia.startProcess(["rollback"], repodir)
@@ -2035,12 +2054,85 @@
         self.serveDlg.show()
     
     ############################################################################
+    ## Methods to handle extensions are below.
+    ############################################################################
+    
+    def __iniFileChanged(self, path):
+        """
+        Private slot to handle a change of the Mercurial config file.
+        
+        @path path name of the changed file (string)
+        """
+        self.__getExtensionsInfo()
+    
+    def __monitorRepoIniFile(self, name):
+        """
+        Private slot to add a repository config file to the list of monitored files.
+        
+        @param name directory name pointing into the repository (string)
+        """
+        dname, fname = self.splitPath(name)
+        
+        # find the root of the repo
+        repodir = str(dname)
+        while not os.path.isdir(os.path.join(repodir, self.adminDir)):
+            repodir = os.path.dirname(repodir)
+            if repodir == os.sep:
+                return
+        
+        cfgFile = os.path.join(repodir, self.adminDir, "hgrc")
+        self.__iniWatcher.addPath(cfgFile)
+    
+    def __getExtensionsInfo(self):
+        """
+        Private method to get the active extensions from Mercurial.
+        """
+        self.__activeExtensions = []
+        
+        process = QProcess()
+        args = []
+        args.append('showconfig')
+        args.append('extensions')
+        process.start('hg', args)
+        procStarted = process.waitForStarted()
+        if procStarted:
+            finished = process.waitForFinished(30000)
+            if finished and process.exitCode() == 0:
+                output = str(process.readAllStandardOutput(),
+                    Preferences.getSystem("IOEncoding"), 'replace')
+                for line in output.splitlines():
+                    extensionName = line.split("=", 1)[0].strip().split(".")[-1].strip()
+                    self.__activeExtensions.append(extensionName)
+        
+        if self.versionStr >= "1.8":
+            if "bookmarks" not in self.__activeExtensions:
+                self.__activeExtensions.append("bookmarks")
+    
+    def isExtensionActive(self, extensionName):
+        """
+        Public method to check, if an extension is active.
+        
+        @param extensionName name of the extension to check for (string)
+        @return flag indicating an active extension (boolean)
+        """
+        return extensionName.strip() in self.__activeExtensions
+    
+    def getExtensionObject(self, extensionName):
+        """
+        Public method to get a reference to an extension object. 
+        
+        @param extensionName name of the extension (string)
+        @return reference to the extension object (boolean)
+        """
+        return self.__extensions[extensionName]
+    
+    ############################################################################
     ## Methods to get the helper objects are below.
     ############################################################################
     
     def vcsGetProjectBrowserHelper(self, browser, project, isTranslationsBrowser=False):
         """
-        Public method to instanciate a helper object for the different project browsers.
+        Public method to instantiate a helper object for the different project browsers.
         
         @param browser reference to the project browser object
         @param project reference to the project object
@@ -2052,13 +2144,14 @@
         
     def vcsGetProjectHelper(self, project):
         """
-        Public method to instanciate a helper object for the project.
+        Public method to instantiate a helper object for the project.
         
         @param project reference to the project object
         @return the project helper object
         """
         helper = self.__plugin.getProjectHelper()
         helper.setObjects(self, project)
+        self.__monitorRepoIniFile(project.ppath)
         return helper
 
     ############################################################################
--- a/eric5.e4p	Sun May 01 19:01:24 2011 +0200
+++ b/eric5.e4p	Mon May 02 19:59:41 2011 +0200
@@ -876,6 +876,12 @@
     <Source>Helpviewer/Download/DownloadAskActionDialog.py</Source>
     <Source>QScintilla/Lexers/LexerMatlab.py</Source>
     <Source>QScintilla/Lexers/LexerOctave.py</Source>
+    <Source>Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/__init__.py</Source>
+    <Source>Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/ProjectHelper.py</Source>
+    <Source>Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/bookmarks.py</Source>
+    <Source>Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarksListDialog.py</Source>
+    <Source>Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarkDialog.py</Source>
+    <Source>Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarkRenameDialog.py</Source>
   </Sources>
   <Forms>
     <Form>PyUnit/UnittestDialog.ui</Form>
@@ -1115,6 +1121,9 @@
     <Form>Plugins/CheckerPlugins/Pep8/Pep8StatisticsDialog.ui</Form>
     <Form>Preferences/ConfigurationPages/HelpVirusTotalPage.ui</Form>
     <Form>Helpviewer/Download/DownloadAskActionDialog.ui</Form>
+    <Form>Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarksListDialog.ui</Form>
+    <Form>Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarkDialog.ui</Form>
+    <Form>Plugins/VcsPlugins/vcsMercurial/BookmarksExtension/HgBookmarkRenameDialog.ui</Form>
   </Forms>
   <Translations>
     <Translation>i18n/eric5_cs.qm</Translation>
@@ -1563,7 +1572,7 @@
               <string>FixIssues</string>
             </key>
             <value>
-              <bool>False</bool>
+              <bool>True</bool>
             </value>
             <key>
               <string>IncludeMessages</string>

eric ide

mercurial