Sun, 21 Jul 2013 18:14:50 +0200
Added forgotten files for the last change.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Documentation/Source/eric5.Plugins.VcsPlugins.vcsMercurial.HgSummaryDialog.html Sun Jul 21 18:14:50 2013 +0200 @@ -0,0 +1,216 @@ +<!DOCTYPE html> +<html><head> +<title>eric5.Plugins.VcsPlugins.vcsMercurial.HgSummaryDialog</title> +<meta charset="UTF-8"> +<style> +body { + background: #EDECE6; + margin: 0em 1em 10em 1em; + color: black; +} + +h1 { color: white; background: #85774A; } +h2 { color: white; background: #85774A; } +h3 { color: white; background: #9D936E; } +h4 { color: white; background: #9D936E; } + +a { color: #BA6D36; } + +</style> +</head> +<body><a NAME="top" ID="top"></a> +<h1>eric5.Plugins.VcsPlugins.vcsMercurial.HgSummaryDialog</h1> +<p> +Module implementing a dialog to show some summary information of the working +directory state. +</p> +<h3>Global Attributes</h3> +<table> +<tr><td>None</td></tr> +</table> +<h3>Classes</h3> +<table> +<tr> +<td><a href="#HgSummaryDialog">HgSummaryDialog</a></td> +<td>Class implementing a dialog to show some summary information of the working directory state.</td> +</tr> +</table> +<h3>Functions</h3> +<table> +<tr><td>None</td></tr> +</table> +<hr /><hr /> +<a NAME="HgSummaryDialog" ID="HgSummaryDialog"></a> +<h2>HgSummaryDialog</h2> +<p> + Class implementing a dialog to show some summary information of the working + directory state. +</p> +<h3>Derived from</h3> +QDialog, Ui_HgSummaryDialog +<h3>Class Attributes</h3> +<table> +<tr><td>None</td></tr> +</table> +<h3>Class Methods</h3> +<table> +<tr><td>None</td></tr> +</table> +<h3>Methods</h3> +<table> +<tr> +<td><a href="#HgSummaryDialog.__init__">HgSummaryDialog</a></td> +<td>Constructor</td> +</tr><tr> +<td><a href="#HgSummaryDialog.__committed">__committed</a></td> +<td>Private slot called after the commit has finished.</td> +</tr><tr> +<td><a href="#HgSummaryDialog.__finish">__finish</a></td> +<td>Private slot called when the process finished or the user pressed the button.</td> +</tr><tr> +<td><a href="#HgSummaryDialog.__procFinished">__procFinished</a></td> +<td>Private slot connected to the finished signal.</td> +</tr><tr> +<td><a href="#HgSummaryDialog.__processOutput">__processOutput</a></td> +<td>Private method to process the output into nice readable text.</td> +</tr><tr> +<td><a href="#HgSummaryDialog.__readStderr">__readStderr</a></td> +<td>Private slot to handle the readyReadStandardError signal.</td> +</tr><tr> +<td><a href="#HgSummaryDialog.__readStdout">__readStdout</a></td> +<td>Private slot to handle the readyReadStandardOutput signal.</td> +</tr><tr> +<td><a href="#HgSummaryDialog.__showError">__showError</a></td> +<td>Private slot to show some error.</td> +</tr><tr> +<td><a href="#HgSummaryDialog.closeEvent">closeEvent</a></td> +<td>Private slot implementing a close event handler.</td> +</tr><tr> +<td><a href="#HgSummaryDialog.on_buttonBox_clicked">on_buttonBox_clicked</a></td> +<td>Private slot called by a button of the button box clicked.</td> +</tr><tr> +<td><a href="#HgSummaryDialog.on_refreshButton_clicked">on_refreshButton_clicked</a></td> +<td>Private slot to refresh the status display.</td> +</tr><tr> +<td><a href="#HgSummaryDialog.start">start</a></td> +<td>Public slot to start the hg summary command.</td> +</tr> +</table> +<h3>Static Methods</h3> +<table> +<tr><td>None</td></tr> +</table> +<a NAME="HgSummaryDialog.__init__" ID="HgSummaryDialog.__init__"></a> +<h4>HgSummaryDialog (Constructor)</h4> +<b>HgSummaryDialog</b>(<i>vcs, parent=None</i>) +<p> + Constructor +</p><dl> +<dt><i>vcs</i></dt> +<dd> +reference to the vcs object +</dd><dt><i>parent</i></dt> +<dd> +parent widget (QWidget) +</dd> +</dl><a NAME="HgSummaryDialog.__committed" ID="HgSummaryDialog.__committed"></a> +<h4>HgSummaryDialog.__committed</h4> +<b>__committed</b>(<i></i>) +<p> + Private slot called after the commit has finished. +</p><a NAME="HgSummaryDialog.__finish" ID="HgSummaryDialog.__finish"></a> +<h4>HgSummaryDialog.__finish</h4> +<b>__finish</b>(<i></i>) +<p> + Private slot called when the process finished or the user pressed the button. +</p><a NAME="HgSummaryDialog.__procFinished" ID="HgSummaryDialog.__procFinished"></a> +<h4>HgSummaryDialog.__procFinished</h4> +<b>__procFinished</b>(<i>exitCode, exitStatus</i>) +<p> + Private slot connected to the finished signal. +</p><dl> +<dt><i>exitCode</i></dt> +<dd> +exit code of the process (integer) +</dd><dt><i>exitStatus</i></dt> +<dd> +exit status of the process (QProcess.ExitStatus) +</dd> +</dl><a NAME="HgSummaryDialog.__processOutput" ID="HgSummaryDialog.__processOutput"></a> +<h4>HgSummaryDialog.__processOutput</h4> +<b>__processOutput</b>(<i>output</i>) +<p> + Private method to process the output into nice readable text. +</p><dl> +<dt><i>output</i></dt> +<dd> +output from the summary command (string) +</dd> +</dl><a NAME="HgSummaryDialog.__readStderr" ID="HgSummaryDialog.__readStderr"></a> +<h4>HgSummaryDialog.__readStderr</h4> +<b>__readStderr</b>(<i></i>) +<p> + Private slot to handle the readyReadStandardError signal. +</p><p> + It reads the error output of the process and inserts it into the + error pane. +</p><a NAME="HgSummaryDialog.__readStdout" ID="HgSummaryDialog.__readStdout"></a> +<h4>HgSummaryDialog.__readStdout</h4> +<b>__readStdout</b>(<i></i>) +<p> + Private slot to handle the readyReadStandardOutput signal. +</p><p> + It reads the output of the process, formats it and inserts it into + the contents pane. +</p><a NAME="HgSummaryDialog.__showError" ID="HgSummaryDialog.__showError"></a> +<h4>HgSummaryDialog.__showError</h4> +<b>__showError</b>(<i>out</i>) +<p> + Private slot to show some error. +</p><dl> +<dt><i>out</i></dt> +<dd> +error to be shown (string) +</dd> +</dl><a NAME="HgSummaryDialog.closeEvent" ID="HgSummaryDialog.closeEvent"></a> +<h4>HgSummaryDialog.closeEvent</h4> +<b>closeEvent</b>(<i>e</i>) +<p> + Private slot implementing a close event handler. +</p><dl> +<dt><i>e</i></dt> +<dd> +close event (QCloseEvent) +</dd> +</dl><a NAME="HgSummaryDialog.on_buttonBox_clicked" ID="HgSummaryDialog.on_buttonBox_clicked"></a> +<h4>HgSummaryDialog.on_buttonBox_clicked</h4> +<b>on_buttonBox_clicked</b>(<i>button</i>) +<p> + Private slot called by a button of the button box clicked. +</p><dl> +<dt><i>button</i></dt> +<dd> +button that was clicked (QAbstractButton) +</dd> +</dl><a NAME="HgSummaryDialog.on_refreshButton_clicked" ID="HgSummaryDialog.on_refreshButton_clicked"></a> +<h4>HgSummaryDialog.on_refreshButton_clicked</h4> +<b>on_refreshButton_clicked</b>(<i></i>) +<p> + Private slot to refresh the status display. +</p><a NAME="HgSummaryDialog.start" ID="HgSummaryDialog.start"></a> +<h4>HgSummaryDialog.start</h4> +<b>start</b>(<i>path, mq=False</i>) +<p> + Public slot to start the hg summary command. +</p><dl> +<dt><i>path</i></dt> +<dd> +path name of the working directory (string) +</dd><dt><i>mq</i></dt> +<dd> +flag indicating to show the queue status as well (boolean) +</dd> +</dl> +<div align="right"><a href="#top">Up</a></div> +<hr /> +</body></html> \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/VcsPlugins/vcsMercurial/HgSummaryDialog.py Sun Jul 21 18:14:50 2013 +0200 @@ -0,0 +1,438 @@ +# -*- coding: utf-8 -*- + +# Copyright (c) 2013 Detlev Offenbach <detlev@die-offenbachs.de> +# + +""" +Module implementing a dialog to show some summary information of the working +directory state. +""" + +import os + +from PyQt4.QtCore import pyqtSlot, QProcess, QProcessEnvironment, QTimer +from PyQt4.QtGui import QDialog, QDialogButtonBox + +from E5Gui import E5MessageBox + +from .Ui_HgSummaryDialog import Ui_HgSummaryDialog + +import Preferences + + +class HgSummaryDialog(QDialog, Ui_HgSummaryDialog): + """ + Class implementing a dialog to show some summary information of the working + directory state. + """ + def __init__(self, vcs, parent=None): + """ + Constructor + + @param vcs reference to the vcs object + @param parent parent widget (QWidget) + """ + super().__init__(parent) + self.setupUi(self) + + self.refreshButton = \ + self.buttonBox.addButton(self.trUtf8("Refresh"), QDialogButtonBox.ActionRole) + self.refreshButton.setToolTip(self.trUtf8("Press to refresh the summary display")) + self.refreshButton.setEnabled(False) + + self.process = None + self.vcs = vcs + self.vcs.committed.connect(self.__committed) + + 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, mq=False): + """ + Public slot to start the hg summary command. + + @param path path name of the working directory (string) + @param mq flag indicating to show the queue status as well (boolean) + """ + self.errorGroup.hide() + self.__path = path + self.__mq = mq + + args = [] + args.append('summary') + self.vcs.addArguments(args, self.vcs.options['global']) + args.append("--remote") + if self.__mq: + args.append("--mq") + + # find the root of the repo + repodir = self.__path + while not os.path.isdir(os.path.join(repodir, self.vcs.adminDir)): + repodir = os.path.dirname(repodir) + if os.path.splitdrive(repodir)[1] == os.sep: + return + + if self.process: + self.process.kill() + else: + self.process = QProcess() + env = QProcessEnvironment.systemEnvironment() + env.insert("LANG", "C") + self.process.setProcessEnvironment(env) + self.process.finished.connect(self.__procFinished) + self.process.readyReadStandardOutput.connect(self.__readStdout) + self.process.readyReadStandardError.connect(self.__readStderr) + + self.process.setWorkingDirectory(repodir) + + self.__buffer = [] + + self.process.start('hg', args) + procStarted = self.process.waitForStarted(5000) + if not procStarted: + 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')) + + 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.refreshButton.setEnabled(True) + self.process = None + + 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.refreshButton: + self.on_refreshButton_clicked() + + 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.__processOutput(self.__buffer) + self.__finish() + + def __readStdout(self): + """ + Private slot to handle the readyReadStandardOutput signal. + + It reads the output of the process, formats it and inserts it into + the contents pane. + """ + if self.process is not None: + self.process.setReadChannel(QProcess.StandardOutput) + + while self.process.canReadLine(): + line = str(self.process.readLine(), + Preferences.getSystem("IOEncoding"), + 'replace') + self.__buffer.append(line) + + def __readStderr(self): + """ + Private slot to handle the readyReadStandardError signal. + + It reads the error output of the process and inserts it into the + error pane. + """ + if self.process is not None: + s = str(self.process.readAllStandardError(), + Preferences.getSystem("IOEncoding"), + 'replace') + self.__showError(s) + + def __showError(self, out): + """ + Private slot to show some error. + + @param out error to be shown (string) + """ + self.errorGroup.show() + self.errors.insertPlainText(out) + self.errors.ensureCursorVisible() + + @pyqtSlot() + def on_refreshButton_clicked(self): + """ + Private slot to refresh the status display. + """ + self.refreshButton.setEnabled(False) + self.summary.clear() + + self.start(self.__path, mq=self.__mq) + + def __committed(self): + """ + Private slot called after the commit has finished. + """ + if self.isVisible(): + self.on_refreshButton_clicked() + + def __processOutput(self, output): + """ + Private method to process the output into nice readable text. + + @param output output from the summary command (string) + """ + infoDict = {} + + # step 1: parse the output + while output: + line = output.pop(0) + name, value = line.split(": ", 1) + value = value.strip() + + if name == "parent": + if " " in value: + parent, tags = value.split(" ", 1) + else: + parent = value + tags = "" + rev, node = parent.split(":") + + remarks = [] + if tags: + if " (empty repository)" in tags: + remarks.append("@EMPTY@") + tags = tags.replace(" (empty repository)", "") + if " (no revision checked out)" in tags: + remarks.append("@NO_REVISION@") + tags = tags.replace(" (no revision checked out)", "") + else: + tags = None + + value = infoDict.get(name, []) + + if rev == "-1": + value.append((int(rev), node, tags, None, remarks)) + else: + message = output.pop(0).strip() + value.append((int(rev), node, tags, message, remarks)) + elif name == "branch": + pass + elif name == "bookmarks": + pass + elif name == "commit": + stateDict = {} + if "(" in value: + if value.startswith("("): + states = "" + remark = value[1:-1] + else: + states, remark = value.rsplit(" (", 1) + remark = remark[:-1] + else: + states = value + remark = "" + states = states.split(", ") + for state in states: + if state: + count, category = state.split(" ") + stateDict[category] = count + value = (stateDict, remark) + elif name == "update": + if value.endswith("(current)"): + value = ("@CURRENT@", 0, 0) + elif value.endswith("(update)"): + value = ("@UPDATE@", value.split(" ", 1)[0], 0) + elif value.endswith("(merge)"): + parts = value.split(", ") + value = ("@MERGE@", parts[0].split(" ", 1)[0], + parts[1].split(" ", 1)[0]) + else: + value = ("@UNKNOWN@", 0, 0) + elif name == "remote": + if value == "(synced)": + value = (0, 0, 0, 0) + else: + inc = incb = outg = outgb = 0 + for val in value.split(", "): + count, category = val.split(" ", 1) + if category == "outgoing": + outg = int(count) + elif category.endswith("incoming"): + inc = int(count) + elif category == "incoming bookmarks": + incb = int(count) + elif category == "outgoing bookmarks": + outgb = int(count) + value = (inc, outg, incb, outgb) + elif name == "mq": + if value == "(empty queue)": + value = (0, 0) + else: + applied = unapplied = 0 + for val in value.split(", "): + count, category = val.split(" ", 1) + if category == "applied": + applied = int(count) + elif category == "unapplied": + unapplied = int(count) + value = (applied, unapplied) + else: + # ignore unknown entries + continue + + infoDict[name] = value + + # step 2: build the output + if infoDict: + info = ["<table>"] + pindex = 0 + for rev, node, tags, message, remarks in infoDict["parent"]: + pindex += 1 + changeset = "{0}:{1}".format(rev, node) + if len(infoDict["parent"]) > 1: + info.append( + self.trUtf8("<tr><td><b>Parent #{0}</b></td><td>{1}</td></tr>") + .format(pindex, changeset)) + else: + info.append( + self.trUtf8("<tr><td><b>Parent</b></td><td>{0}</td></tr>") + .format(changeset)) + if tags: + info.append(self.trUtf8("<tr><td><b>Tags</b></td><td>{0}</td></tr>") + .format('<br/>'.join(tags.split()))) + if message: + info.append( + self.trUtf8("<tr><td><b>Commit Message</b></td><td>{0}</td></tr>") + .format(message)) + if remarks: + rem = [] + if "@EMPTY@" in remarks: + rem.append(self.trUtf8("empty repository")) + if "@NO_REVISION@" in remarks: + rem.append(self.trUtf8("no revision checked out")) + info.append( + self.trUtf8("<tr><td><b>Remarks</b></td><td>{0}</td></tr>") + .format(", ".join(rem))) + if "branch" in infoDict: + info.append(self.trUtf8("<tr><td><b>Branch</b></td><td>{0}</td></tr>") + .format(infoDict["branch"])) + if "bookmarks" in infoDict: + bookmarks = infoDict["bookmarks"].split() + for i in range(len(bookmarks)): + if bookmarks[i].startswith("*"): + bookmarks[i] = "<b>{0}</b>".format(bookmarks[i]) + info.append(self.trUtf8("<tr><td><b>Bookmarks</b></td><td>{0}</td></tr>") + .format('<br/>'.join(bookmarks))) + if "commit" in infoDict: + cinfo = [] + for category, count in infoDict["commit"][0].items(): + if category == "modified": + cinfo.append(self.trUtf8("{0} modified").format(count)) + elif category == "added": + cinfo.append(self.trUtf8("{0} added").format(count)) + elif category == "removed": + cinfo.append(self.trUtf8("{0} removed").format(count)) + elif category == "renamed": + cinfo.append(self.trUtf8("{0} renamed").format(count)) + elif category == "copied": + cinfo.append(self.trUtf8("{0} copied").format(count)) + elif category == "deleted": + cinfo.append(self.trUtf8("{0} deleted").format(count)) + elif category == "unknown": + cinfo.append(self.trUtf8("{0} unknown").format(count)) + elif category == "ignored": + cinfo.append(self.trUtf8("{0} ignored").format(count)) + elif category == "unresolved": + cinfo.append(self.trUtf8("{0} unresolved").format(count)) + elif category == "subrepos": + cinfo.append(self.trUtf8("{0} subrepos").format(count)) + remark = infoDict["commit"][1] + if remark == "merge": + cinfo.append(self.trUtf8("Merge needed")) + elif remark == "new branch": + cinfo.append(self.trUtf8("New Branch")) + elif remark == "head closed": + cinfo.append(self.trUtf8("Head is closed")) + elif remark == "clean": + cinfo.append(self.trUtf8("No commit required")) + elif remark == "new branch head": + cinfo.append(self.trUtf8("New Branch Head")) + info.append( + self.trUtf8("<tr><td><b>Commit Status</b></td><td>{0}</td></tr>") + .format("<br/>".join(cinfo))) + if "update" in infoDict: + if infoDict["update"][0] == "@CURRENT@": + uinfo = self.trUtf8("current") + elif infoDict["update"][0] == "@UPDATE@": + uinfo = self.trUtf8("{0} new changesets<br/>Update required")\ + .format(infoDict["update"][1]) + elif infoDict["update"][0] == "@MERGE@": + uinfo = self.trUtf8( + "{0} new changesets<br/>{1} branch heads<br/>Merge required")\ + .format(infoDict["update"][1], infoDict["update"][2]) + else: + uinfo = self.trUtf8("unknown status") + info.append( + self.trUtf8("<tr><td><b>Update Status</b></td><td>{0}</td></tr>") + .format(uinfo)) + if "remote" in infoDict: + if infoDict["remote"] == (0, 0, 0, 0): + rinfo = self.trUtf8("synched") + else: + l = [] + if infoDict["remote"][0]: + l.append(self.trUtf8("1 or more incoming")) + if infoDict["remote"][1]: + l.append(self.trUtf8("{0} outgoing")\ + .format(infoDict["remote"][1])) + if infoDict["remote"][2]: + l.append(self.trUtf8("{0} incoming bookmarks") + .format(infoDict["remote"][2])) + if infoDict["remote"][3]: + l.append(self.trUtf8("{0} outgoing bookmarks") + .format(infoDict["remote"][3])) + rinfo = "<br/>".join(l) + info.append( + self.trUtf8("<tr><td><b>Remote Status</b></td><td>{0}</td></tr>") + .format(rinfo)) + if "mq" in infoDict: + if infoDict["mq"] == (0, 0): + qinfo = self.trUtf8("empty queue") + else: + l = [] + if infoDict["mq"][0]: + l.append(self.trUtf8("{0} applied").format(infoDict["mq"][0])) + if infoDict["mq"][1]: + l.append(self.trUtf8("{0} unapplied").format(infoDict["mq"][1])) + qinfo = "<br/>".join(l) + info.append( + self.trUtf8("<tr><td><b>Queues Status</b></td><td>{0}</td></tr>") + .format(qinfo)) + info.append("</table>") + else: + info = [self.trUtf8("<p>No status information available.</p>")] + + self.summary.insertHtml("\n".join(info))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Plugins/VcsPlugins/vcsMercurial/HgSummaryDialog.ui Sun Jul 21 18:14:50 2013 +0200 @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>HgSummaryDialog</class> + <widget class="QDialog" name="HgSummaryDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>600</width> + <height>500</height> + </rect> + </property> + <property name="windowTitle"> + <string>Summary Information</string> + </property> + <property name="sizeGripEnabled"> + <bool>true</bool> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QTextEdit" name="summary"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>2</verstretch> + </sizepolicy> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + </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="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="readOnly"> + <bool>true</bool> + </property> + <property name="acceptRichText"> + <bool>false</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::Close</set> + </property> + </widget> + </item> + </layout> + </widget> + <tabstops> + <tabstop>summary</tabstop> + <tabstop>errors</tabstop> + <tabstop>buttonBox</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>HgSummaryDialog</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>HgSummaryDialog</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>