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  import re 
 37   
 38  from node_manager_fkie.common import package_name, utf8 
 39  from node_manager_fkie.detailed_msg_box import MessageBox 
 40  from node_manager_fkie.launch_config import LaunchConfig 
 41  import node_manager_fkie as nm 
 42   
 43  from .parser_functions import interpret_path 
 44  from .xml_highlighter import XmlHighlighter 
 45  from .yaml_highlighter import YamlHighlighter 
 46   
 47   
 48  try: 
 49      from python_qt_binding.QtGui import QApplication, QMenu, QTextEdit 
 50  except: 
 51      from python_qt_binding.QtWidgets import QApplication, QMenu, QTextEdit 
 52   
 53   
54 -class TextEdit(QTextEdit):
55 ''' 56 The XML editor to handle the included files. If an included file in the opened 57 launch file is detected, this can be open by STRG+(mouse click) in a new 58 editor. 59 ''' 60 61 load_request_signal = Signal(str) 62 ''' @ivar: A signal for request to open a configuration file''' 63 64 search_result_signal = Signal(str, bool, str, int) 65 ''' @ivar: A signal emitted after search_threaded was started. 66 (search text, found or not, file, position in text) 67 for each result a signal will be emitted. 68 ''' 69 70 SUBSTITUTION_ARGS = ['env', 'optenv', 'find', 'anon', 'arg'] 71 CONTEXT_FILE_EXT = ['.launch', '.test', '.xml'] 72 YAML_VALIDATION_FILES = ['.yaml', '.iface', '.sync'] 73
74 - def __init__(self, filename, parent=None):
75 self.parent = parent 76 QTextEdit.__init__(self, parent) 77 self.setObjectName(' - '.join(['Editor', filename])) 78 self.setContextMenuPolicy(Qt.CustomContextMenu) 79 self.customContextMenuRequested.connect(self.show_custom_context_menu) 80 # self.setFrameStyle(QFrame.StyledPanel | QFrame.Sunken) 81 self.setAcceptRichText(False) 82 font = QFont() 83 font.setFamily("Fixed".decode("utf-8")) 84 font.setPointSize(12) 85 self.setFont(font) 86 self.setLineWrapMode(QTextEdit.NoWrap) 87 self.setTabStopWidth(25) 88 self.setAcceptRichText(False) 89 self.setCursorWidth(2) 90 self.setFontFamily("courier new") 91 self.setProperty("backgroundVisible", True) 92 self.regexp_list = [QRegExp("\\binclude\\b"), QRegExp("\\btextfile\\b"), 93 QRegExp("\\bfile\\b"), QRegExp("\\bvalue=.*pkg:\/\/\\b"), 94 QRegExp("\\bvalue=.*package:\/\/\\b"), 95 QRegExp("\\bvalue=.*\$\(find\\b"), 96 QRegExp("\\bargs=.*\$\(find\\b"), 97 QRegExp("\\bdefault=.*\$\(find\\b")] 98 self.filename = filename 99 self.file_info = None 100 if self.filename: 101 f = QFile(filename) 102 if f.open(QIODevice.ReadOnly | QIODevice.Text): 103 self.file_info = QFileInfo(filename) 104 self.setText(unicode(f.readAll(), "utf-8")) 105 106 self.path = '.' 107 # enables drop events 108 self.setAcceptDrops(True) 109 if filename.endswith('.launch'): 110 self.hl = XmlHighlighter(self.document()) 111 self.cursorPositionChanged.connect(self._document_position_changed) 112 else: 113 self.hl = YamlHighlighter(self.document()) 114 # variables for threaded search 115 self._search_thread = None 116 self._stop = False
117
119 if isinstance(self.hl, XmlHighlighter) and nm.settings().highlight_xml_blocks: 120 # import time 121 # start_time = time.time() 122 self.hl.mark_block(self.textCursor().block(), self.textCursor().positionInBlock())
123 # print("--- mark_tag_block %.6f seconds ---" % (time.time() - start_time)) 124
125 - def save(self, force=False):
126 ''' 127 Saves changes to the file. 128 :return: saved, errors, msg 129 :rtype: bool, bool, str 130 ''' 131 if force or self.document().isModified() or not QFileInfo(self.filename).exists(): 132 f = QFile(self.filename) 133 if f.open(QIODevice.WriteOnly | QIODevice.Text): 134 f.write(self.toPlainText().encode('utf-8')) 135 self.document().setModified(False) 136 self.file_info = QFileInfo(self.filename) 137 138 ext = os.path.splitext(self.filename) 139 # validate the xml structure of the launch files 140 if ext[1] in self.CONTEXT_FILE_EXT: 141 imported = False 142 try: 143 from lxml import etree 144 imported = True 145 parser = etree.XMLParser() 146 etree.fromstring(self.toPlainText().encode('utf-8'), parser) 147 except Exception as e: 148 if imported: 149 self.markLine(e.position[0]) 150 return True, True, "%s" % e 151 # validate the yaml structure of yaml files 152 elif ext[1] in self.YAML_VALIDATION_FILES: 153 try: 154 import yaml 155 yaml.load(self.toPlainText().encode('utf-8')) 156 except yaml.MarkedYAMLError as e: 157 return True, True, "%s" % e 158 return True, False, '' 159 else: 160 return False, True, "Cannot write XML file" 161 return False, False, ''
162
163 - def markLine(self, no):
164 try: 165 cursor = self.textCursor() 166 cursor.setPosition(0, QTextCursor.MoveAnchor) 167 while (cursor.block().blockNumber() + 1 < no): 168 cursor.movePosition(QTextCursor.NextBlock, QTextCursor.MoveAnchor) 169 cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) 170 self.setTextCursor(cursor) 171 except: 172 pass
173
174 - def setCurrentPath(self, path):
175 ''' 176 Sets the current working path. This path is to open the included files, 177 which contains the relative path. 178 @param path: the path of the current opened file (without the file) 179 @type path: C{str} 180 ''' 181 self.path = path
182
183 - def index(self, text):
184 ''' 185 Searches in the given text for key indicates the including of a file and 186 return their index. 187 @param text: text to find 188 @type text: C{str} 189 @return: the index of the including key or -1 190 @rtype: C{int} 191 ''' 192 for pattern in self.regexp_list: 193 index = pattern.indexIn(text) 194 if index > -1: 195 return index 196 return -1
197
198 - def includedFiles(self):
199 ''' 200 Returns all included files in the document. 201 ''' 202 result = [] 203 b = self.document().begin() 204 while b != self.document().end(): 205 text = b.text() 206 index = self.index(text) 207 if index > -1: 208 startIndex = text.find('"', index) 209 if startIndex > -1: 210 endIndex = text.find('"', startIndex + 1) 211 fileName = text[startIndex + 1:endIndex] 212 if len(fileName) > 0: 213 try: 214 path = interpret_path(fileName) 215 f = QFile(path) 216 ext = os.path.splitext(path) 217 if f.exists() and ext[1] in nm.settings().SEARCH_IN_EXT: 218 result.append(path) 219 except: 220 import traceback 221 print traceback.format_exc(1) 222 b = b.next() 223 return result
224
225 - def focusInEvent(self, event):
226 # check for file changes 227 try: 228 if self.filename and self.file_info: 229 if self.file_info.lastModified() != QFileInfo(self.filename).lastModified(): 230 self.file_info = QFileInfo(self.filename) 231 result = MessageBox.question(self, "File changed", "File was changed, reload?", buttons=MessageBox.Yes | MessageBox.No) 232 if result == MessageBox.Yes: 233 f = QFile(self.filename) 234 if f.open(QIODevice.ReadOnly | QIODevice.Text): 235 self.setText(unicode(f.readAll(), "utf-8")) 236 self.document().setModified(False) 237 self.textChanged.emit() 238 else: 239 MessageBox.critical(self, "Error", "Cannot open launch file%s" % self.filename) 240 except: 241 pass 242 QTextEdit.focusInEvent(self, event)
243
244 - def mouseReleaseEvent(self, event):
245 ''' 246 Opens the new editor, if the user clicked on the included file and sets the 247 default cursor. 248 ''' 249 if event.modifiers() == Qt.ControlModifier or event.modifiers() == Qt.ShiftModifier: 250 cursor = self.cursorForPosition(event.pos()) 251 inc_files = LaunchConfig.included_files(cursor.block().text(), recursive=False) 252 if inc_files: 253 try: 254 qf = QFile(inc_files[0]) 255 if not qf.exists(): 256 # create a new file, if it does not exists 257 result = MessageBox.question(self, "File not found", '\n\n'.join(["Create a new file?", qf.fileName()]), buttons=MessageBox.Yes | MessageBox.No) 258 if result == MessageBox.Yes: 259 d = os.path.dirname(qf.fileName()) 260 if not os.path.exists(d): 261 os.makedirs(d) 262 with open(qf.fileName(), 'w') as f: 263 if qf.fileName().endswith('.launch'): 264 f.write('<launch>\n\n</launch>') 265 event.setAccepted(True) 266 self.load_request_signal.emit(qf.fileName()) 267 else: 268 event.setAccepted(True) 269 self.load_request_signal.emit(qf.fileName()) 270 except Exception, e: 271 MessageBox.critical(self, "Error", "File not found %s" % inc_files[0], detailed_text=utf8(e)) 272 QTextEdit.mouseReleaseEvent(self, event)
273
274 - def mouseMoveEvent(self, event):
275 ''' 276 Sets the X{Qt.PointingHandCursor} if the control key is pressed and 277 the mouse is over the included file. 278 ''' 279 if event.modifiers() == Qt.ControlModifier or event.modifiers() == Qt.ShiftModifier: 280 cursor = self.cursorForPosition(event.pos()) 281 index = self.index(cursor.block().text()) 282 if index > -1: 283 self.viewport().setCursor(Qt.PointingHandCursor) 284 else: 285 self.viewport().setCursor(Qt.IBeamCursor) 286 else: 287 self.viewport().setCursor(Qt.IBeamCursor) 288 QTextEdit.mouseMoveEvent(self, event)
289
290 - def keyPressEvent(self, event):
291 ''' 292 Enable the mouse tracking by X{setMouseTracking()} if the control key is pressed. 293 ''' 294 if event.key() == Qt.Key_Control or event.key() == Qt.Key_Shift: 295 self.setMouseTracking(True) 296 if event.modifiers() == Qt.ControlModifier and event.key() == Qt.Key_7: 297 self.commentText() 298 elif event.modifiers() == Qt.ControlModifier | Qt.ShiftModifier and event.key() == Qt.Key_Slash: 299 self.commentText() 300 elif event.modifiers() == Qt.AltModifier and event.key() == Qt.Key_Space: 301 ext = os.path.splitext(self.filename) 302 if ext[1] in self.CONTEXT_FILE_EXT: 303 menu = self._create_context_substitution_menu(False) 304 if menu is None: 305 menu = self._create_context_menu_for_tag() 306 if menu: 307 menu.exec_(self.mapToGlobal(self.cursorRect().bottomRight())) 308 elif event.key() != Qt.Key_Escape: 309 # handle the shifting of the block 310 if event.modifiers() == Qt.NoModifier and event.key() == Qt.Key_Tab: 311 self.shiftText() 312 elif event.modifiers() == Qt.ShiftModifier and event.key() == Qt.Key_Backtab: 313 self.shiftText(back=True) 314 else: 315 event.accept() 316 if event.key() in [Qt.Key_Enter, Qt.Key_Return]: 317 ident = self.getIdentOfCurretLine() 318 QTextEdit.keyPressEvent(self, event) 319 if event.key() in [Qt.Key_Enter, Qt.Key_Return]: 320 self.indentCurrentLine(ident) 321 else: 322 event.accept() 323 QTextEdit.keyPressEvent(self, event)
324
325 - def keyReleaseEvent(self, event):
326 ''' 327 Disable the mouse tracking by X{setMouseTracking()} if the control key is 328 released and set the cursor back to X{Qt.IBeamCursor}. 329 ''' 330 if event.key() == Qt.Key_Control or event.key() == Qt.Key_Shift: 331 self.setMouseTracking(False) 332 self.viewport().setCursor(Qt.IBeamCursor) 333 else: 334 event.accept() 335 QTextEdit.keyReleaseEvent(self, event)
336
337 - def _has_uncommented(self):
338 cursor = QTextCursor(self.textCursor()) 339 if not cursor.isNull(): 340 start = cursor.selectionStart() 341 end = cursor.selectionEnd() 342 cursor.setPosition(start) 343 block_start = cursor.blockNumber() 344 cursor.setPosition(end) 345 block_end = cursor.blockNumber() 346 if block_end - block_start > 0 and end - cursor.block().position() <= 0: 347 # skip the last block, if no characters are selected 348 block_end -= 1 349 cursor.setPosition(start, QTextCursor.MoveAnchor) 350 cursor.movePosition(QTextCursor.StartOfLine) 351 start = cursor.position() 352 xmlre = re.compile(r"\A\s*<!--") 353 otherre = re.compile(r"\A\s*#") 354 ext = os.path.splitext(self.filename) 355 # XML comment 356 xml_file = ext[1] in self.CONTEXT_FILE_EXT 357 while (cursor.block().blockNumber() < block_end + 1): 358 cursor.movePosition(QTextCursor.StartOfLine) 359 cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) 360 if xml_file: 361 if not xmlre.match(cursor.selectedText()): 362 return True 363 else: 364 if not otherre.match(cursor.selectedText()): 365 return True 366 cursor.movePosition(QTextCursor.NextBlock) 367 return False
368
369 - def commentText(self):
370 do_comment = self._has_uncommented() 371 cursor = self.textCursor() 372 if not cursor.isNull(): 373 cursor.beginEditBlock() 374 start = cursor.selectionStart() 375 end = cursor.selectionEnd() 376 cursor.setPosition(start) 377 block_start = cursor.blockNumber() 378 cursor.setPosition(end) 379 block_end = cursor.blockNumber() 380 if block_end - block_start > 0 and end - cursor.block().position() <= 0: 381 # skip the last block, if no characters are selected 382 block_end -= 1 383 cursor.setPosition(start, QTextCursor.MoveAnchor) 384 cursor.movePosition(QTextCursor.StartOfLine) 385 start = cursor.position() 386 ext = os.path.splitext(self.filename) 387 # XML comment 388 xml_file = ext[1] in self.CONTEXT_FILE_EXT 389 while (cursor.block().blockNumber() < block_end + 1): 390 cursor.movePosition(QTextCursor.StartOfLine) 391 # XML comment 392 if xml_file: 393 xmlre_start = re.compile(r"<!-- ?") 394 xmlre_end = re.compile(r" ?-->") 395 cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) 396 if do_comment: 397 cursor.insertText("<!-- %s -->" % cursor.selectedText().replace("--", "- - ")) 398 else: 399 res = cursor.selectedText() 400 mstart = xmlre_start.search(res) 401 if mstart: 402 res = res.replace(mstart.group(), "", 1) 403 res = res.replace("<!- -", "<!--", 1) 404 mend = xmlre_end.search(res) 405 if mend: 406 res = res.replace(mend.group(), "", 1) 407 last_pos = res.rfind("- ->") 408 if last_pos > -1: 409 res = "%s-->" % res[0:last_pos] 410 cursor.insertText(res.replace("- - ", "--")) 411 else: # other comments 412 hash_re = re.compile(r"# ?") 413 if do_comment: 414 cursor.insertText('# ') 415 else: 416 cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) 417 res = cursor.selectedText() 418 hres = hash_re.search(res) 419 if hres: 420 res = res.replace(hres.group(), "", 1) 421 cursor.insertText(res) 422 cursor.movePosition(QTextCursor.NextBlock) 423 # Set our cursor's selection to span all of the involved lines. 424 cursor.endEditBlock() 425 cursor.setPosition(start, QTextCursor.MoveAnchor) 426 cursor.movePosition(QTextCursor.StartOfBlock, QTextCursor.MoveAnchor) 427 while (cursor.block().blockNumber() < block_end): 428 cursor.movePosition(QTextCursor.NextBlock, QTextCursor.KeepAnchor) 429 cursor.movePosition(QTextCursor.EndOfBlock, QTextCursor.KeepAnchor) 430 # set the cursor 431 self.setTextCursor(cursor)
432
433 - def shiftText(self, back=False):
434 ''' 435 Increase (Decrease) indentation using Tab (Ctrl+Tab). 436 ''' 437 cursor = self.textCursor() 438 if not cursor.isNull(): 439 # one undo operation 440 cursor.beginEditBlock() 441 start = cursor.selectionStart() 442 end = cursor.selectionEnd() 443 cursor.setPosition(start) 444 block_start = cursor.blockNumber() 445 cursor.setPosition(end) 446 block_end = cursor.blockNumber() 447 if block_end - block_start == 0: 448 # shift one line two spaces to the left 449 if back: 450 for _ in range(2): 451 cursor.movePosition(QTextCursor.StartOfLine) 452 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 1) 453 if cursor.selectedText() == ' ': 454 cursor.insertText('') 455 elif cursor.selectedText() == "\t": 456 cursor.insertText('') 457 break 458 cursor.movePosition(QTextCursor.StartOfLine) 459 else: 460 # shift one line two spaces to the right 461 indent_prev = self.getIndentOfPreviewsBlock() 462 if self.textCursor().positionInBlock() >= indent_prev: 463 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, end - start) 464 cursor.insertText(' ') 465 else: 466 # move to the position of previous indent 467 cursor.movePosition(QTextCursor.StartOfLine) 468 pose_of_line = cursor.position() 469 cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) 470 cursor.insertText("%s%s" % (' ' * indent_prev, cursor.selectedText().lstrip())) 471 cursor.setPosition(pose_of_line + indent_prev, QTextCursor.MoveAnchor) 472 else: 473 # shift the selected block two spaces to the left 474 if back: 475 removed = 0 476 for i in reversed(range(start, end)): 477 cursor.setPosition(i) 478 if cursor.atBlockStart(): 479 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 2) 480 if cursor.selectedText() == ' ': 481 cursor.insertText('') 482 removed += 2 483 else: 484 cursor.movePosition(QTextCursor.StartOfLine) 485 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, 1) 486 if cursor.selectedText() == ' ': 487 cursor.insertText('') 488 removed += 1 489 elif cursor.selectedText() == "\t": 490 cursor.insertText('') 491 removed += 1 492 cursor.setPosition(start) 493 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, end - start - removed) 494 else: 495 # shift selected block two spaces to the right 496 inserted = 0 497 for i in reversed(range(start, end)): 498 cursor.setPosition(i) 499 if cursor.atBlockStart(): 500 cursor.insertText(' ') 501 inserted += 2 502 cursor.setPosition(start) 503 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, end - start + inserted) 504 self.setTextCursor(cursor) 505 cursor.endEditBlock()
506
507 - def indentCurrentLine(self, count=0):
508 ''' 509 Increase indentation of current line according to the preview line. 510 ''' 511 cursor = self.textCursor() 512 if not cursor.isNull(): 513 # one undo operation 514 cursor.beginEditBlock() 515 start = cursor.selectionStart() 516 end = cursor.selectionEnd() 517 cursor.setPosition(start) 518 block_start = cursor.blockNumber() 519 cursor.setPosition(end) 520 block_end = cursor.blockNumber() 521 ident = '' 522 for _ in range(count): 523 ident += ' ' 524 if block_end - block_start == 0: 525 # shift one line of count spaces to the right 526 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, end - start) 527 cursor.insertText(ident) 528 else: 529 # shift selected block two spaces to the right 530 inserted = 0 531 for i in reversed(range(start, end)): 532 cursor.setPosition(i) 533 if cursor.atBlockStart(): 534 cursor.insertText(ident) 535 inserted += count 536 cursor.setPosition(start) 537 cursor.movePosition(QTextCursor.NextCharacter, QTextCursor.KeepAnchor, end - start + inserted) 538 self.setTextCursor(cursor) 539 cursor.endEditBlock()
540
541 - def getIdentOfCurretLine(self):
542 cursor = self.textCursor() 543 if not cursor.isNull(): 544 cursor.movePosition(QTextCursor.StartOfLine) 545 cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) 546 line = cursor.selectedText() 547 return len(line) - len(line.lstrip(' ')) 548 return 0
549
550 - def getIndentOfPreviewsBlock(self):
551 cursor = self.textCursor() 552 if not cursor.isNull(): 553 cursor.movePosition(QTextCursor.PreviousBlock) 554 cursor.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor) 555 line = cursor.selectedText() 556 return len(line) - len(line.lstrip(' ')) 557 return 0
558 559 ############################################################################# 560 ########## Drag&Drop ###### 561 ############################################################################# 562
563 - def dragEnterEvent(self, e):
564 if e.mimeData().hasFormat('text/plain'): 565 e.accept() 566 else: 567 e.ignore()
568
569 - def dragMoveEvent(self, e):
570 e.accept()
571
572 - def dropEvent(self, e):
573 cursor = self.cursorForPosition(e.pos()) 574 if not cursor.isNull(): 575 text = e.mimeData().text() 576 # the files will be included 577 if text.startswith('file://'): 578 text = text[7:] 579 if os.path.exists(text) and os.path.isfile(text): 580 # find the package name containing the included file 581 (package, path) = package_name(os.path.dirname(text)) 582 if text.endswith('.launch'): 583 if package: 584 cursor.insertText('<include file="$(find %s)%s" />' % (package, text.replace(path, ''))) 585 else: 586 cursor.insertText('<include file="%s" />' % text) 587 else: 588 if package: 589 cursor.insertText('<rosparam file="$(find %s)%s" command="load" />' % (package, text.replace(path, ''))) 590 else: 591 cursor.insertText('<rosparam file="%s" command="load" />' % text) 592 else: 593 cursor.insertText(e.mimeData().text()) 594 e.accept()
595 596 ############################################################################# 597 ########## Ctrl&Space Context menu ###### 598 ############################################################################# 599
600 - def show_custom_context_menu(self, pos):
601 menu = QTextEdit.createStandardContextMenu(self) 602 # if not self.textCursor().selectedText(): 603 # self.setTextCursor(self.cursorForPosition(pos)) 604 submenu = self._create_context_menu_for_tag() 605 if submenu is not None: 606 menu.addMenu(submenu) 607 argmenu = self._create_context_substitution_menu() 608 if argmenu is not None: 609 menu.addMenu(argmenu) 610 menu.exec_(self.mapToGlobal(pos))
611
612 - def contextMenuEvent(self, event):
613 QTextEdit.contextMenuEvent(self, event)
614
616 if isinstance(self.hl, XmlHighlighter): 617 tag = self.hl.get_tag_of_current_block(self.textCursor().block(), self.textCursor().positionInBlock()) 618 if tag: 619 try: 620 menu = QMenu("ROS <%s>" % tag, self) 621 menu.triggered.connect(self._context_activated) 622 # create a menu with attributes 623 menu_attr = QMenu("attributes", menu) 624 attributes = sorted(list(set(XmlHighlighter.LAUNCH_ATTR[tag]))) 625 for attr in attributes: 626 action = menu_attr.addAction(attr.rstrip('=')) 627 action.setData('%s""' % attr) 628 menu.addMenu(menu_attr) 629 # create a menu with tags 630 tags = sorted(XmlHighlighter.LAUNCH_CHILDS[tag]) 631 if tags: 632 menu_tags = QMenu("tags", menu) 633 for tag in tags: 634 data = '<%s></%s>' % (tag, tag) if XmlHighlighter.LAUNCH_CHILDS[tag] else '<%s/>' % tag 635 action = menu_tags.addAction(tag) 636 action.setData(data) 637 menu.addMenu(menu_tags) 638 return menu 639 except: 640 import traceback 641 print traceback.format_exc(1) 642 return None 643 return None
644
645 - def _create_context_substitution_menu(self, force_all=True):
646 if isinstance(self.hl, XmlHighlighter): 647 text = self.toPlainText() 648 pos = self.textCursor().position() - 1 649 try: 650 if force_all or (text[pos] == '$' or (text[pos] == '(' and text[pos - 1] == '$')): 651 menu = QMenu("ROS substitution args", self) 652 menu.triggered.connect(self._context_activated) 653 for arg in self.SUBSTITUTION_ARGS: 654 action = menu.addAction("%s" % arg) 655 if force_all: 656 action.setData("$(%s )" % arg) 657 else: 658 action.setData("(%s" % arg if text[pos] == '$' else "%s" % arg) 659 return menu 660 except: 661 pass 662 return None
663
664 - def _context_activated(self, arg):
665 cursor = self.textCursor() 666 if not cursor.isNull(): 667 cursor.insertText(arg.data())
668