Package node_manager_fkie :: Package editor :: Module text_edit
[frames] | no frames]

Source Code for Module node_manager_fkie.editor.text_edit

  1  # Software License Agreement (BSD License) 
  2  # 
  3  # Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko 
  4  # All rights reserved. 
  5  # 
  6  # Redistribution and use in source and binary forms, with or without 
  7  # modification, are permitted provided that the following conditions 
  8  # are met: 
  9  # 
 10  #  * Redistributions of source code must retain the above copyright 
 11  #    notice, this list of conditions and the following disclaimer. 
 12  #  * Redistributions in binary form must reproduce the above 
 13  #    copyright notice, this list of conditions and the following 
 14  #    disclaimer in the documentation and/or other materials provided 
 15  #    with the distribution. 
 16  #  * Neither the name of Fraunhofer nor the names of its 
 17  #    contributors may be used to endorse or promote products derived 
 18  #    from this software without specific prior written permission. 
 19  # 
 20  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 21  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 22  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 23  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 24  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 25  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 26  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 27  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 28  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 29  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 30  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 31  # POSSIBILITY OF SUCH DAMAGE. 
 32   
 33  from python_qt_binding.QtCore import QFile, QFileInfo, QIODevice, QRegExp, Qt, Signal 
 34  from python_qt_binding.QtGui import QFont, QTextCursor 
 35  import os 
 36   
 37  from node_manager_fkie.common import package_name 
 38  from node_manager_fkie.detailed_msg_box import WarningMessageBox 
 39  import node_manager_fkie as nm 
 40   
 41  from .parser_functions import interpret_path 
 42  from .xml_highlighter import XmlHighlighter 
 43  from .yaml_highlighter import YamlHighlighter 
 44   
 45   
 46  try: 
 47      from python_qt_binding.QtGui import QApplication, QMenu, QMessageBox, QTextEdit 
 48  except: 
 49      from python_qt_binding.QtWidgets import QApplication, QMenu, QMessageBox, QTextEdit 
 50   
 51   
52 -class TextEdit(QTextEdit):
53 ''' 54 The XML editor to handle the included files. If an included file in the opened 55 launch file is detected, this can be open by STRG+(mouse click) in a new 56 editor. 57 ''' 58 59 load_request_signal = Signal(str) 60 ''' @ivar: A signal for request to open a configuration file''' 61 62 search_result_signal = Signal(str, bool, str, int) 63 ''' @ivar: A signal emitted after search_threaded was started. 64 (search text, found or not, file, position in text) 65 for each result a signal will be emitted. 66 ''' 67 68 SUBSTITUTION_ARGS = ['env', 'optenv', 'find', 'anon', 'arg'] 69 CONTEXT_FILE_EXT = ['.launch', '.test', '.xml'] 70 YAML_VALIDATION_FILES = ['.yaml', '.iface', '.sync'] 71
72 - def __init__(self, filename, parent=None):
73 self.parent = parent 74 QTextEdit.__init__(self, parent) 75 self.setObjectName(' - '.join(['Editor', filename])) 76 self.setContextMenuPolicy(Qt.CustomContextMenu) 77 self.customContextMenuRequested.connect(self.show_custom_context_menu) 78 # self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) 79 self.setAcceptRichText(False) 80 font = QFont() 81 font.setFamily("Fixed".decode("utf-8")) 82 font.setPointSize(12) 83 self.setFont(font) 84 self.setLineWrapMode(QTextEdit.NoWrap) 85 self.setTabStopWidth(25) 86 self.setAcceptRichText(False) 87 self.setCursorWidth(2) 88 self.setFontFamily("courier new") 89 self.setProperty("backgroundVisible", True) 90 self.regexp_list = [QRegExp("\\binclude\\b"), QRegExp("\\btextfile\\b"), 91 QRegExp("\\bfile\\b"), QRegExp("\\bvalue=.*pkg:\/\/\\b"), 92 QRegExp("\\bvalue=.*package:\/\/\\b"), 93 QRegExp("\\bvalue=.*\$\(find\\b"), 94 QRegExp("\\bdefault=.*\$\(find\\b")] 95 self.filename = filename 96 self.file_info = None 97 if self.filename: 98 f = QFile(filename) 99 if f.open(QIODevice.ReadOnly | QIODevice.Text): 100 self.file_info = QFileInfo(filename) 101 self.setText(unicode(f.readAll(), "utf-8")) 102 103 self.path = '.' 104 # enables drop events 105 self.setAcceptDrops(True) 106 if filename.endswith('.launch'): 107 self.hl = XmlHighlighter(self.document()) 108 self.cursorPositionChanged.connect(self._document_position_changed) 109 else: 110 self.hl = YamlHighlighter(self.document()) 111 # variables for threaded search 112 self._search_thread = None 113 self._stop = False
114
116 if isinstance(self.hl, XmlHighlighter) and nm.settings().highlight_xml_blocks: 117 # import time 118 # start_time = time.time() 119 self.hl.mark_block(self.textCursor().block(), self.textCursor().positionInBlock())
120 # print("--- mark_tag_block %.6f seconds ---" % (time.time() - start_time)) 121
122 - def save(self, force=False):
123 ''' 124 Saves changes to the file. 125 :return: saved, errors, msg 126 :rtype: bool, bool, str 127 ''' 128 if force or self.document().isModified() or not QFileInfo(self.filename).exists(): 129 f = QFile(self.filename) 130 if f.open(QIODevice.WriteOnly | QIODevice.Text): 131 f.write(self.toPlainText().encode('utf-8')) 132 self.document().setModified(False) 133 self.file_info = QFileInfo(self.filename) 134 135 ext = os.path.splitext(self.filename) 136 # validate the xml structure of the launch files 137 if ext[1] in self.CONTEXT_FILE_EXT: 138 imported = False 139 try: 140 from lxml import etree 141 imported = True 142 parser = etree.XMLParser() 143 etree.fromstring(self.toPlainText().encode('utf-8'), parser) 144 except Exception as e: 145 if imported: 146 self.markLine(e.position[0]) 147 return True, True, "%s" % e 148 # validate the yaml structure of yaml files 149 elif ext[1] in self.YAML_VALIDATION_FILES: 150 try: 151 import yaml 152 yaml.load(self.toPlainText().encode('utf-8')) 153 except yaml.MarkedYAMLError as e: 154 return True, True, "%s" % e 155 return True, False, '' 156 else: 157 return False, True, "Cannot write XML file" 158 return False, False, ''
159
160 - def markLine(self, no):
161 try: 162 cursor = self.textCursor() 163 cursor.setPosition(0, QTextCursor.MoveAnchor) 164 while (cursor.block().blockNumber() + 1 < no): 165 cursor.movePosition(QTextCursor.NextBlock, QTextCursor.MoveAnchor) 166 cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) 167 self.setTextCursor(cursor) 168 except: 169 pass
170
171 - def setCurrentPath(self, path):
172 ''' 173 Sets the current working path. This path is to open the included files, 174 which contains the relative path. 175 @param path: the path of the current opened file (without the file) 176 @type path: C{str} 177 ''' 178 self.path = path
179
180 - def index(self, text):
181 ''' 182 Searches in the given text for key indicates the including of a file and 183 return their index. 184 @param text: text to find 185 @type text: C{str} 186 @return: the index of the including key or -1 187 @rtype: C{int} 188 ''' 189 for pattern in self.regexp_list: 190 index = pattern.indexIn(text) 191 if index > -1: 192 return index 193 return -1
194
195 - def includedFiles(self):
196 ''' 197 Returns all included files in the document. 198 ''' 199 result = [] 200 b = self.document().begin() 201 while b != self.document().end(): 202 text = b.text() 203 index = self.index(text) 204 if index > -1: 205 startIndex = text.find('"', index) 206 if startIndex > -1: 207 endIndex = text.find('"', startIndex + 1) 208 fileName = text[startIndex + 1:endIndex] 209 if len(fileName) > 0: 210 try: 211 path = interpret_path(fileName) 212 f = QFile(path) 213 ext = os.path.splitext(path) 214 if f.exists() and ext[1] in nm.settings().SEARCH_IN_EXT: 215 result.append(path) 216 except: 217 import traceback 218 print traceback.format_exc(1) 219 b = b.next() 220 return result
221
222 - def focusInEvent(self, event):
223 # check for file changes 224 try: 225 if self.filename and self.file_info: 226 if self.file_info.lastModified() != QFileInfo(self.filename).lastModified(): 227 self.file_info = QFileInfo(self.filename) 228 result = QMessageBox.question(self, "File changed", "File was changed, reload?", QMessageBox.Yes | QMessageBox.No) 229 if result == QMessageBox.Yes: 230 f = QFile(self.filename) 231 if f.open(QIODevice.ReadOnly | QIODevice.Text): 232 self.setText(unicode(f.readAll(), "utf-8")) 233 self.document().setModified(False) 234 self.textChanged.emit() 235 else: 236 QMessageBox.critical(self, "Error", "Cannot open launch file%s" % self.filename) 237 except: 238 pass 239 QTextEdit.focusInEvent(self, event)
240
241 - def mouseReleaseEvent(self, event):
242 ''' 243 Opens the new editor, if the user clicked on the included file and sets the 244 default cursor. 245 ''' 246 if event.modifiers() == Qt.ControlModifier or event.modifiers() == Qt.ShiftModifier: 247 cursor = self.cursorForPosition(event.pos()) 248 index = self.index(cursor.block().text()) 249 if index > -1: 250 startIndex = cursor.block().text().find('"', index) 251 if startIndex > -1: 252 endIndex = cursor.block().text().find('"', startIndex + 1) 253 fileName = cursor.block().text()[startIndex + 1:endIndex] 254 if len(fileName) > 0: 255 try: 256 qf = QFile(interpret_path(fileName)) 257 if not qf.exists(): 258 # create a new file, if it does not exists 259 result = QMessageBox.question(self, "File not found", '\n\n'.join(["Create a new file?", qf.fileName()]), QMessageBox.Yes | QMessageBox.No) 260 if result == QMessageBox.Yes: 261 d = os.path.dirname(qf.fileName()) 262 if not os.path.exists(d): 263 os.makedirs(d) 264 with open(qf.fileName(), 'w') as f: 265 if qf.fileName().endswith('.launch'): 266 f.write('<launch>\n\n</launch>') 267 event.setAccepted(True) 268 self.load_request_signal.emit(qf.fileName()) 269 else: 270 event.setAccepted(True) 271 self.load_request_signal.emit(qf.fileName()) 272 except Exception, e: 273 WarningMessageBox(QMessageBox.Warning, "File not found %s" % fileName, str(e)).exec_() 274 QTextEdit.mouseReleaseEvent(self, event)
275
276 - def mouseMoveEvent(self, event):
277 ''' 278 Sets the X{Qt.PointingHandCursor} if the control key is pressed and 279 the mouse is over the included file. 280 ''' 281 if event.modifiers() == Qt.ControlModifier or event.modifiers() == Qt.ShiftModifier: 282 cursor = self.cursorForPosition(event.pos()) 283 index = self.index(cursor.block().text()) 284 if index > -1: 285 self.viewport().setCursor(Qt.PointingHandCursor) 286 else: 287 self.viewport().setCursor(Qt.IBeamCursor) 288 else: 289 self.viewport().setCursor(Qt.IBeamCursor) 290 QTextEdit.mouseMoveEvent(self, event)
291
292 - def keyPressEvent(self, event):
293 ''' 294 Enable the mouse tracking by X{setMouseTracking()} if the control key is pressed. 295 ''' 296 if event.key() == Qt.Key_Control or event.key() == Qt.Key_Shift: 297 self.setMouseTracking(True) 298 if event.modifiers() == Qt.ControlModifier and event.key() == Qt.Key_7: 299 self.commentText() 300 elif event.modifiers() == Qt.ControlModifier | Qt.ShiftModifier and event.key() == Qt.Key_Slash: 301 self.commentText() 302 elif event.modifiers() == Qt.AltModifier and event.key() == Qt.Key_Space: 303 ext = os.path.splitext(self.filename) 304 if ext[1] in self.CONTEXT_FILE_EXT: 305 menu = self._create_context_substitution_menu(False) 306 if menu is None: 307 menu = self._create_context_menu_for_tag() 308 if menu: 309 menu.exec_(self.mapToGlobal(self.cursorRect().bottomRight())) 310 elif event.key() != Qt.Key_Escape: 311 # handle the shifting of the block 312 if event.modifiers() == Qt.NoModifier and event.key() == Qt.Key_Tab: 313 self.shiftText() 314 elif event.modifiers() == Qt.ControlModifier and event.key() == Qt.Key_Tab: 315 self.shiftText(back=True) 316 else: 317 QTextEdit.keyPressEvent(self, event) 318 else: 319 event.accept() 320 QTextEdit.keyPressEvent(self, event)
321
322 - def keyReleaseEvent(self, event):
323 ''' 324 Disable the mouse tracking by X{setMouseTracking()} if the control key is 325 released and set the cursor back to X{Qt.IBeamCursor}. 326 ''' 327 if event.key() == Qt.Key_Control or event.key() == Qt.Key_Shift: 328 self.setMouseTracking(False) 329 self.viewport().setCursor(Qt.IBeamCursor) 330 else: 331 event.accept() 332 QTextEdit.keyReleaseEvent(self, event)
333
334 - def commentText(self):
335 cursor = self.textCursor() 336 if not cursor.isNull(): 337 cursor.beginEditBlock() 338 start = cursor.selectionStart() 339 end = cursor.selectionEnd() 340 cursor.setPosition(start) 341 block_start = cursor.blockNumber() 342 cursor.setPosition(end) 343 block_end = cursor.blockNumber() 344 if block_end - block_start > 0 and end - cursor.block().position() <= 0: 345 # skip the last block, if no characters are selected 346 block_end -= 1 347 cursor.setPosition(start, QTextCursor.MoveAnchor) 348 cursor.movePosition(QTextCursor.StartOfLine) 349 start = cursor.position() 350 while (cursor.block().blockNumber() < block_end + 1): 351 cursor.movePosition(QTextCursor.StartOfLine) 352 ext = os.path.splitext(self.filename) 353 # XML comment 354 if ext[1] in self.CONTEXT_FILE_EXT: 355 # skipt the existing spaces at the start of the line 356 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 1) 357 while cursor.selectedText() in [' ', '\t']: 358 cursor.setPosition(cursor.position(), QTextCursor.MoveAnchor) 359 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 1) 360 cursor.movePosition(QTextCursor.PreviousCharacter, 1) 361 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 4) 362 # only comments breakers at the start of the line are removed 363 if cursor.selectedText() == '<!--': 364 cursor.insertText('') 365 # remove spaces between comment and text 366 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 1) 367 while cursor.selectedText() in [' ', '\t']: 368 cursor.insertText('') 369 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 1) 370 cursor.movePosition(QTextCursor.EndOfLine) 371 cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor, 3) 372 if cursor.selectedText() == '-->': 373 cursor.insertText('') 374 # remove spaces between comment and text 375 cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor, 1) 376 while cursor.selectedText() in [' ', '\t']: 377 cursor.insertText('') 378 cursor.movePosition(QTextCursor.PreviousCharacter, QTextCursor.KeepAnchor, 1) 379 else: 380 cursor.setPosition(cursor.anchor()) 381 cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) 382 # only comment out, if no comments are found 383 if cursor.selectedText().find('<!--') < 0 and cursor.selectedText().find('-->') < 0: 384 cursor.movePosition(QTextCursor.StartOfLine) 385 # skipt the current existing spaces 386 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 1) 387 while cursor.selectedText() in [' ', '\t']: 388 cursor.setPosition(cursor.position(), QTextCursor.MoveAnchor) 389 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 1) 390 cursor.movePosition(QTextCursor.PreviousCharacter, 1) 391 cursor.insertText('<!-- ') 392 cursor.movePosition(QTextCursor.EndOfLine) 393 cursor.insertText(' -->') 394 else: # other comments 395 if cursor.block().length() < 2: 396 cursor.movePosition(QTextCursor.NextBlock) 397 continue 398 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 2) 399 # only comments breakers at the start of the line are removed 400 if cursor.selectedText() == '# ': 401 cursor.insertText('') 402 else: 403 cursor.movePosition(QTextCursor.StartOfLine) 404 cursor.insertText('# ') 405 cursor.movePosition(QTextCursor.NextBlock) 406 # Set our cursor's selection to span all of the involved lines. 407 cursor.endEditBlock() 408 cursor.setPosition(start, QTextCursor.MoveAnchor) 409 cursor.movePosition(QTextCursor.StartOfBlock, QTextCursor.MoveAnchor) 410 while (cursor.block().blockNumber() < block_end): 411 cursor.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor) 412 cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) 413 # set the cursor 414 self.setTextCursor(cursor)
415
416 - def shiftText(self, back=False):
417 ''' 418 Increase (Decrease) indentation using Tab (Ctrl+Tab). 419 ''' 420 cursor = self.textCursor() 421 if not cursor.isNull(): 422 # one undo operation 423 cursor.beginEditBlock() 424 start = cursor.selectionStart() 425 end = cursor.selectionEnd() 426 cursor.setPosition(start) 427 block_start = cursor.blockNumber() 428 cursor.setPosition(end) 429 block_end = cursor.blockNumber() 430 if block_end - block_start == 0: 431 # shift one line two spaces to the left 432 if back: 433 for _ in range(2): 434 cursor.movePosition(QTextCursor.StartOfLine) 435 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 1) 436 if cursor.selectedText() == ' ': 437 cursor.insertText('') 438 elif cursor.selectedText() == "\t": 439 cursor.insertText('') 440 break 441 cursor.movePosition(QTextCursor.StartOfLine) 442 else: 443 # shift one line two spaces to the right 444 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, end - start) 445 cursor.insertText(' ') 446 else: 447 # shift the selected block two spaces to the left 448 if back: 449 removed = 0 450 for i in reversed(range(start, end)): 451 cursor.setPosition(i) 452 if cursor.atBlockStart(): 453 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 2) 454 if cursor.selectedText() == ' ': 455 cursor.insertText('') 456 removed += 2 457 else: 458 cursor.movePosition(QTextCursor.StartOfLine) 459 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 1) 460 if cursor.selectedText() == ' ': 461 cursor.insertText('') 462 removed += 1 463 elif cursor.selectedText() == "\t": 464 cursor.insertText('') 465 removed += 1 466 cursor.setPosition(start) 467 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, end - start - removed) 468 else: 469 # shift selected block two spaces to the right 470 inserted = 0 471 for i in reversed(range(start, end)): 472 cursor.setPosition(i) 473 if cursor.atBlockStart(): 474 cursor.insertText(' ') 475 inserted += 2 476 cursor.setPosition(start) 477 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, end - start + inserted) 478 self.setTextCursor(cursor) 479 cursor.endEditBlock()
480 481 ############################################################################# 482 ########## Drag&Drop ###### 483 ############################################################################# 484
485 - def dragEnterEvent(self, e):
486 if e.mimeData().hasFormat('text/plain'): 487 e.accept() 488 else: 489 e.ignore()
490
491 - def dragMoveEvent(self, e):
492 e.accept()
493
494 - def dropEvent(self, e):
495 cursor = self.cursorForPosition(e.pos()) 496 if not cursor.isNull(): 497 text = e.mimeData().text() 498 # the files will be included 499 if text.startswith('file://'): 500 text = text[7:] 501 if os.path.exists(text) and os.path.isfile(text): 502 # find the package name containing the included file 503 (package, path) = package_name(os.path.dirname(text)) 504 if text.endswith('.launch'): 505 if package: 506 cursor.insertText('<include file="$(find %s)%s" />' % (package, text.replace(path, ''))) 507 else: 508 cursor.insertText('<include file="%s" />' % text) 509 else: 510 if package: 511 cursor.insertText('<rosparam file="$(find %s)%s" command="load" />' % (package, text.replace(path, ''))) 512 else: 513 cursor.insertText('<rosparam file="%s" command="load" />' % text) 514 else: 515 cursor.insertText(e.mimeData().text()) 516 e.accept()
517 518 ############################################################################# 519 ########## Ctrl&Space Context menu ###### 520 ############################################################################# 521
522 - def show_custom_context_menu(self, pos):
523 menu = QTextEdit.createStandardContextMenu(self) 524 # if not self.textCursor().selectedText(): 525 # self.setTextCursor(self.cursorForPosition(pos)) 526 submenu = self._create_context_menu_for_tag() 527 if submenu is not None: 528 menu.addMenu(submenu) 529 argmenu = self._create_context_substitution_menu() 530 if argmenu is not None: 531 menu.addMenu(argmenu) 532 menu.exec_(self.mapToGlobal(pos))
533
534 - def contextMenuEvent(self, event):
535 QTextEdit.contextMenuEvent(self, event)
536
538 if isinstance(self.hl, XmlHighlighter): 539 tag = self.hl.get_tag_of_current_block(self.textCursor().block(), self.textCursor().positionInBlock()) 540 if tag: 541 try: 542 menu = QMenu("ROS <%s>" % tag, self) 543 menu.triggered.connect(self._context_activated) 544 # create a menu with attributes 545 menu_attr = QMenu("attributes", menu) 546 attributes = sorted(list(set(XmlHighlighter.LAUNCH_ATTR[tag]))) 547 for attr in attributes: 548 action = menu_attr.addAction(attr.rstrip('=')) 549 action.setData('%s""' % attr) 550 menu.addMenu(menu_attr) 551 # create a menu with tags 552 tags = sorted(XmlHighlighter.LAUNCH_CHILDS[tag]) 553 if tags: 554 menu_tags = QMenu("tags", menu) 555 for tag in tags: 556 data = '<%s></%s>' % (tag, tag) if XmlHighlighter.LAUNCH_CHILDS[tag] else '<%s/>' % tag 557 action = menu_tags.addAction(tag) 558 action.setData(data) 559 menu.addMenu(menu_tags) 560 return menu 561 except: 562 import traceback 563 print traceback.format_exc(1) 564 return None 565 return None
566
567 - def _create_context_substitution_menu(self, force_all=True):
568 if isinstance(self.hl, XmlHighlighter): 569 text = self.toPlainText() 570 pos = self.textCursor().position() - 1 571 try: 572 if force_all or (text[pos] == '$' or (text[pos] == '(' and text[pos - 1] == '$')): 573 menu = QMenu("ROS substitution args", self) 574 menu.triggered.connect(self._context_activated) 575 for arg in self.SUBSTITUTION_ARGS: 576 action = menu.addAction("%s" % arg) 577 if force_all: 578 action.setData("$(%s )" % arg) 579 else: 580 action.setData("(%s" % arg if text[pos] == '$' else "%s" % arg) 581 return menu 582 except: 583 pass 584 return None
585
586 - def _context_activated(self, arg):
587 cursor = self.textCursor() 588 if not cursor.isNull(): 589 cursor.insertText(arg.data())
590