Mon, 02 May 2011 19:59:41 +0200
Started implementing the Mercurial bookmarks support.
--- /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><b>Mercurial Bookmarks</b> +<p>This dialog shows a list of the projects bookmarks.</p></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><b>Bookmarks List</b> +<p>This shows a list of the projects bookmarks.</p></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>&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>&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>