Package node_manager_fkie :: Module xml_editor
[frames] | no frames]

Source Code for Module node_manager_fkie.xml_editor

  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  import os 
 34  from PySide import QtGui 
 35  from PySide import QtCore 
 36  from PySide import QtUiTools 
 37   
 38  import roslib 
 39  import rospy 
 40  from xml_highlighter import XmlHighlighter 
 41   
42 -class Editor(QtGui.QTextEdit):
43 ''' 44 The XML editor to handle the included files. If an included file in the opened 45 launch file is detected, this can be open by STRG+(mouse click) in a new 46 editor. 47 ''' 48 49 load_request_signal = QtCore.Signal(str) 50 ''' @ivar: A signal for request to open a configuration file''' 51
52 - def __init__(self, filename, parent=None):
53 self.parent = parent 54 QtGui.QTextEdit.__init__(self, parent) 55 self.setObjectName(' - '.join(['Editor', filename])) 56 font = QtGui.QFont() 57 font.setFamily("Fixed".decode("utf-8")) 58 font.setPointSize(12) 59 self.setFont(font) 60 self.setLineWrapMode(QtGui.QTextEdit.NoWrap) 61 self.setTabStopWidth(25) 62 self.setAcceptRichText(False) 63 self.setCursorWidth(2) 64 self.setFontFamily("courier new") 65 self.setProperty("backgroundVisible", True) 66 self.regexp_list = [QtCore.QRegExp("\\binclude\\b"), QtCore.QRegExp("\\btextfile\\b"), 67 QtCore.QRegExp("\\bfile\\b")] 68 self.filename = filename 69 file = QtCore.QFile(filename); 70 if file.open(QtCore.QIODevice.ReadOnly | QtCore.QIODevice.Text): 71 self.setText(unicode(file.readAll(), "utf-8")) 72 73 self.path = '.'
74 75 # def __del__(self): 76 # print "********** desctroy:", self.objectName() 77
78 - def save(self):
79 ''' 80 Saves changes to the file. 81 ''' 82 if self.document().isModified(): 83 file = QtCore.QFile(self.filename) 84 if file.open(QtCore.QIODevice.WriteOnly | QtCore.QIODevice.Text): 85 file.write(self.toPlainText().encode('utf-8')) 86 self.document().setModified(False) 87 return True 88 else: 89 QtGui.QMessageBox.critical(self, "Error", "Cannot write XML file") 90 return False 91 return False
92
93 - def setCurrentPath(self, path):
94 ''' 95 Sets the current working path. This path is to open the included files, which 96 contains the relative path. 97 @param path: the path of the current opened file (without the file) 98 @type path: C{str} 99 ''' 100 self.path = path
101
102 - def interpretPath(self, path):
103 ''' 104 Tries to determine the path of the included file. The statement of 105 C{$(find 'package')} will be resolved. 106 @param path: the sting which contains the included path 107 @type path: C{str} 108 @return: if no leading C{os.sep} is detected, the path setted by L{setCurrentPath()} 109 will be prepend. C{$(find 'package')} will be resolved. Otherwise the parameter 110 itself will be returned 111 @rtype: C{str} 112 ''' 113 path = path.strip() 114 index = path.find('$') 115 if index > -1: 116 startIndex = path.find('(', index) 117 if startIndex > -1: 118 endIndex = path.find(')', startIndex+1) 119 script = path[startIndex+1:endIndex].split() 120 if len(script) == 2 and (script[0] == 'find'): 121 pkg = roslib.packages.get_pkg_dir(script[1]) 122 return os.path.normpath(''.join([pkg, '/', path[endIndex+1:]])) 123 elif len(path) > 0 and path[0] != '/': 124 return os.path.normpath(''.join([self.path, '/', path])) 125 return os.path.normpath(path)
126
127 - def index(self, text):
128 ''' 129 Searches in the given text for key indicates the including of a file and 130 return their index. 131 @param text: text to find 132 @type text: C{str} 133 @return: the index of the including key or -1 134 @rtype: C{int} 135 ''' 136 for pattern in self.regexp_list: 137 index = pattern.indexIn(text) 138 if index > -1: 139 return index 140 return -1
141
142 - def includedFiles(self):
143 ''' 144 Returns all included files in the document. 145 ''' 146 result = [] 147 b = self.document().begin() 148 while b != self.document().end(): 149 text = b.text() 150 index = self.index(text) 151 if index > -1: 152 startIndex = text.find('"', index) 153 if startIndex > -1: 154 endIndex = text.find('"', startIndex+1) 155 fileName = text[startIndex+1:endIndex] 156 if len(fileName) > 0: 157 path = self.interpretPath(fileName) 158 file = QtCore.QFile(path) 159 if file.exists(): 160 result.append(path) 161 b = b.next() 162 return result
163
164 - def fileWithText(self, search_text):
165 ''' 166 Searches for given text in this document and all included files. 167 @param search_text: text to find 168 @type search_text: C{str} 169 @return: the list with all files contain the text 170 @rtype: C{[str, ...]} 171 ''' 172 result = [] 173 start_pos = QtGui.QTextCursor() 174 search_result = self.document().find(search_text, start_pos.position()+1) 175 if not search_result.isNull(): 176 result.append(self.filename) 177 inc_files = self.includedFiles() 178 for f in inc_files: 179 editor = Editor(f, None) 180 result[len(result):] = editor.fileWithText(search_text) 181 return result
182
183 - def mouseReleaseEvent(self, event):
184 ''' 185 Opens the new editor, if the user clicked on the included file and sets the 186 default cursor. 187 ''' 188 if event.modifiers() == QtCore.Qt.ControlModifier or event.modifiers() == QtCore.Qt.ShiftModifier: 189 cursor = self.cursorForPosition(event.pos()) 190 index = self.index(cursor.block().text()) 191 if index > -1: 192 startIndex = cursor.block().text().find('"', index) 193 if startIndex > -1: 194 endIndex = cursor.block().text().find('"', startIndex+1) 195 fileName = cursor.block().text()[startIndex+1:endIndex] 196 if len(fileName) > 0: 197 file = QtCore.QFile(self.interpretPath(fileName)) 198 if not file.exists(): 199 # create a new file, if it does not exists 200 result = QtGui.QMessageBox.question(self, "File not found", '\n\n'.join(["Create a new file?", file.fileName()]), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) 201 if result == QtGui.QMessageBox.Yes: 202 dir = os.path.dirname(file.fileName()) 203 if not os.path.exists(dir): 204 os.makedirs(dir) 205 with open(file.fileName(),'w') as f: 206 if file.fileName().endswith('.launch'): 207 f.write('<launch>\n\n</launch>') 208 self.load_request_signal.emit(file.fileName()) 209 else: 210 self.load_request_signal.emit(file.fileName()) 211 QtGui.QTextEdit.mouseReleaseEvent(self, event)
212
213 - def mouseMoveEvent(self, event):
214 ''' 215 Sets the X{QtCore.Qt.PointingHandCursor} if the control key is pressed and 216 the mouse is over the included file. 217 ''' 218 if event.modifiers() == QtCore.Qt.ControlModifier or event.modifiers() == QtCore.Qt.ShiftModifier: 219 cursor = self.cursorForPosition(event.pos()) 220 index = self.index(cursor.block().text()) 221 if index > -1: 222 self.viewport().setCursor(QtCore.Qt.PointingHandCursor) 223 else: 224 self.viewport().setCursor(QtCore.Qt.IBeamCursor) 225 else: 226 self.viewport().setCursor(QtCore.Qt.IBeamCursor) 227 QtGui.QTextEdit.mouseMoveEvent(self, event)
228
229 - def keyPressEvent(self, event):
230 ''' 231 Enable the mouse tracking by X{setMouseTracking()} if the control key is pressed. 232 ''' 233 if event.key() == QtCore.Qt.Key_Control or event.key() == QtCore.Qt.Key_Shift: 234 self.setMouseTracking(True) 235 if event.key() != QtCore.Qt.Key_Escape: 236 # handle the shifting of the block 237 if event.key() == QtCore.Qt.Key_Tab: 238 self.shiftText() 239 else: 240 QtGui.QTextEdit.keyPressEvent(self, event) 241 else: 242 event.accept() 243 QtGui.QTextEdit.keyPressEvent(self, event)
244
245 - def keyReleaseEvent(self, event):
246 ''' 247 Disable the mouse tracking by X{setMouseTracking()} if the control key is 248 released and set the cursor back to X{QtCore.Qt.IBeamCursor}. 249 ''' 250 if event.key() == QtCore.Qt.Key_Control or event.key() == QtCore.Qt.Key_Shift: 251 self.setMouseTracking(False) 252 self.viewport().setCursor(QtCore.Qt.IBeamCursor) 253 QtGui.QTextEdit.keyReleaseEvent(self, event)
254
255 - def shiftText(self):
256 ''' 257 Increase (Decrease) indentation using Tab (Ctrl+Tab). 258 ''' 259 cursor = self.textCursor() 260 if not cursor.isNull(): 261 key_mod = QtGui.QApplication.keyboardModifiers() 262 # one undo operation 263 cursor.beginEditBlock() 264 start = cursor.selectionStart() 265 end = cursor.selectionEnd() 266 cursor.setPosition(start) 267 block_start = cursor.blockNumber() 268 cursor.setPosition(end) 269 block_end = cursor.blockNumber() 270 if block_end-block_start == 0: 271 # shift one line two spaces to the left 272 if key_mod & QtCore.Qt.ControlModifier: 273 for s in range(2): 274 cursor.movePosition(QtGui.QTextCursor.StartOfLine) 275 cursor.movePosition(QtGui.QTextCursor.NextCharacter, QtGui.QTextCursor.KeepAnchor, 1) 276 if cursor.selectedText() == ' ': 277 cursor.insertText('') 278 elif cursor.selectedText() == "\t": 279 cursor.insertText('') 280 break 281 cursor.movePosition(QtGui.QTextCursor.StartOfLine) 282 else: 283 # shift one line two spaces to the right 284 cursor.movePosition(QtGui.QTextCursor.NextCharacter, QtGui.QTextCursor.KeepAnchor, end-start) 285 cursor.insertText(' ') 286 else: 287 # shift the selected block two spaces to the left 288 if key_mod & QtCore.Qt.ControlModifier: 289 removed = 0 290 for i in reversed(range(start, end)): 291 cursor.setPosition(i) 292 if cursor.atBlockStart(): 293 cursor.movePosition(QtGui.QTextCursor.NextCharacter, QtGui.QTextCursor.KeepAnchor, 2) 294 if cursor.selectedText() == ' ': 295 cursor.insertText('') 296 removed += 2 297 else: 298 cursor.movePosition(QtGui.QTextCursor.StartOfLine) 299 cursor.movePosition(QtGui.QTextCursor.NextCharacter, QtGui.QTextCursor.KeepAnchor, 1) 300 if cursor.selectedText() == ' ': 301 cursor.insertText('') 302 removed += 1 303 elif cursor.selectedText() == "\t": 304 cursor.insertText('') 305 removed += 1 306 cursor.setPosition(start) 307 cursor.movePosition(QtGui.QTextCursor.NextCharacter, QtGui.QTextCursor.KeepAnchor, end-start-removed) 308 else: 309 # shift selected block two spaces to the right 310 inserted = 0 311 for i in reversed(range(start, end)): 312 cursor.setPosition(i) 313 if cursor.atBlockStart(): 314 cursor.insertText(' ') 315 inserted += 2 316 cursor.setPosition(start) 317 cursor.movePosition(QtGui.QTextCursor.NextCharacter, QtGui.QTextCursor.KeepAnchor, end-start+inserted) 318 self.setTextCursor(cursor) 319 cursor.endEditBlock()
320 321
322 -class FindDialog(QtGui.QDialog):
323 ''' 324 A dialog to find text in the Editor. 325 ''' 326
327 - def __init__(self, parent=None):
328 QtGui.QDialog.__init__(self, parent) 329 self.setObjectName('FindDialog') 330 self.setWindowTitle('Search') 331 self.verticalLayout = QtGui.QVBoxLayout(self) 332 self.verticalLayout.setObjectName("verticalLayout") 333 334 self.content = QtGui.QWidget(self) 335 self.contentLayout = QtGui.QFormLayout(self.content) 336 # self.contentLayout.setFieldGrowthPolicy(QtGui.QFormLayout.AllNonFixedFieldsGrow) 337 # self.contentLayout.setVerticalSpacing(0) 338 self.contentLayout.setContentsMargins(0, 0, 0, 0) 339 self.verticalLayout.addWidget(self.content) 340 341 label = QtGui.QLabel("Find:", self.content) 342 self.search_field = QtGui.QLineEdit(self.content) 343 self.contentLayout.addRow(label, self.search_field) 344 replace_label = QtGui.QLabel("Replace:", self.content) 345 self.replace_field = QtGui.QLineEdit(self.content) 346 self.contentLayout.addRow(replace_label, self.replace_field) 347 self.recursive = QtGui.QCheckBox("recursive search") 348 self.contentLayout.addRow(self.recursive) 349 self.result_label = QtGui.QLabel("") 350 self.verticalLayout.addWidget(self.result_label) 351 self.found_files = QtGui.QListWidget() 352 self.found_files.setVisible(False) 353 self.found_files.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) 354 self.verticalLayout.addWidget(self.found_files) 355 356 self.buttonBox = QtGui.QDialogButtonBox(self) 357 self.find_button = QtGui.QPushButton(self.tr("&Find")) 358 self.find_button.setDefault(True) 359 self.buttonBox.addButton(self.find_button, QtGui.QDialogButtonBox.ActionRole) 360 self.replace_button = QtGui.QPushButton(self.tr("&Replace/Find")) 361 self.buttonBox.addButton(self.replace_button, QtGui.QDialogButtonBox.ActionRole) 362 self.buttonBox.addButton(QtGui.QDialogButtonBox.Close) 363 self.buttonBox.setOrientation(QtCore.Qt.Horizontal) 364 self.buttonBox.setObjectName("buttonBox") 365 self.verticalLayout.addWidget(self.buttonBox) 366 367 # QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("accepted()"), self.accept) 368 QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL("rejected()"), self.reject) 369 QtCore.QMetaObject.connectSlotsByName(self) 370 371 self.search_text = '' 372 self.search_pos = QtGui.QTextCursor()
373 374 # def __del__(self): 375 # print "********** desctroy:", self.objectName() 376 377
378 -class XmlEditor(QtGui.QDialog):
379 ''' 380 Creates a dialog to edit a launch file. 381 ''' 382 383 finished_signal = QtCore.Signal(list) 384 ''' 385 finished_signal has as parameter the filenames of the initialization and is emitted, if this 386 dialog was closed. 387 ''' 388
389 - def __init__(self, filenames, search_text='', parent=None):
390 ''' 391 @param filenames: a list with filenames. The last one will be activated. 392 @type filenames: C{[str, ...]} 393 @param search_text: if not empty, searches in new document for first occurrence of the given text 394 @type search_text: C{str} (Default: C{Empty String}) 395 ''' 396 QtGui.QDialog.__init__(self, parent) 397 self.setObjectName(' - '.join(['xmlEditor', str(filenames)])) 398 self.setAttribute(QtCore.Qt.WA_DeleteOnClose, True) 399 self.setWindowFlags(QtCore.Qt.Window) 400 self.resize(800,640) 401 self.mIcon = QtGui.QIcon(":/icons/crystal_clear_edit_launch.png") 402 self.setWindowIcon(self.mIcon) 403 self.setWindowTitle("ROSLaunch Editor"); 404 # self.finished.connect(self.closeEvent) 405 self.init_filenames = list(filenames) 406 407 self.files = [] 408 '''@ivar: list with all open files ''' 409 410 # create tabs for files 411 self.verticalLayout = QtGui.QVBoxLayout(self) 412 self.verticalLayout.setContentsMargins(0, 0, 0, 0) 413 self.verticalLayout.setObjectName("verticalLayout") 414 self.tabWidget = QtGui.QTabWidget(self) 415 self.tabWidget.setTabPosition(QtGui.QTabWidget.North) 416 self.tabWidget.setDocumentMode(True) 417 self.tabWidget.setTabsClosable(True) 418 self.tabWidget.setMovable(False) 419 self.tabWidget.setObjectName("tabWidget") 420 self.tabWidget.tabCloseRequested.connect(self.on_close_tab) 421 self.verticalLayout.addWidget(self.tabWidget) 422 423 # create the buttons line 424 self.buttons = QtGui.QWidget(self) 425 self.horizontalLayout = QtGui.QHBoxLayout(self.buttons) 426 self.horizontalLayout.setContentsMargins(4, 0, 4, 0) 427 self.horizontalLayout.setObjectName("horizontalLayout") 428 # add the search button 429 self.searchButton = QtGui.QPushButton(self) 430 self.searchButton.setObjectName("searchButton") 431 self.searchButton.clicked.connect(self.on_shortcut_find) 432 self.searchButton.setText(QtGui.QApplication.translate("XmlEditor", "Search", None, QtGui.QApplication.UnicodeUTF8)) 433 self.searchButton.setShortcut(QtGui.QApplication.translate("XmlEditor", "Ctrl+F", None, QtGui.QApplication.UnicodeUTF8)) 434 self.searchButton.setToolTip('Open a search dialog (Ctrl+F)') 435 self.horizontalLayout.addWidget(self.searchButton) 436 # add the got button 437 self.gotoButton = QtGui.QPushButton(self) 438 self.gotoButton.setObjectName("gotoButton") 439 self.gotoButton.clicked.connect(self.on_shortcut_goto) 440 self.gotoButton.setText(QtGui.QApplication.translate("XmlEditor", "Goto line", None, QtGui.QApplication.UnicodeUTF8)) 441 self.gotoButton.setShortcut(QtGui.QApplication.translate("XmlEditor", "Ctrl+L", None, QtGui.QApplication.UnicodeUTF8)) 442 self.gotoButton.setToolTip('Open a goto dialog (Ctrl+L)') 443 self.horizontalLayout.addWidget(self.gotoButton) 444 # add spacer 445 spacerItem = QtGui.QSpacerItem(515, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) 446 self.horizontalLayout.addItem(spacerItem) 447 # add line number label 448 self.pos_label = QtGui.QLabel() 449 self.horizontalLayout.addWidget(self.pos_label) 450 # add spacer 451 spacerItem = QtGui.QSpacerItem(515, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) 452 self.horizontalLayout.addItem(spacerItem) 453 # add save button 454 self.saveButton = QtGui.QPushButton(self) 455 self.saveButton.setObjectName("saveButton") 456 self.saveButton.clicked.connect(self.on_saveButton_clicked) 457 self.saveButton.setText(QtGui.QApplication.translate("XmlEditor", "Save", None, QtGui.QApplication.UnicodeUTF8)) 458 self.saveButton.setShortcut(QtGui.QApplication.translate("XmlEditor", "Ctrl+S", None, QtGui.QApplication.UnicodeUTF8)) 459 self.saveButton.setToolTip('Save the changes to the file (Ctrl+S)') 460 self.horizontalLayout.addWidget(self.saveButton) 461 self.verticalLayout.addWidget(self.buttons) 462 463 #create the find dialog 464 self.find_dialog = FindDialog(self) 465 self.find_dialog.buttonBox.clicked.connect(self.on_find_dialog_clicked) 466 self.find_dialog.found_files.itemActivated.connect(self.find_dialog_itemActivated) 467 468 # self._shortcut_find = QtGui.QShortcut(QtGui.QKeySequence(self.tr("Ctrl+F", "find text")), self) 469 # self._shortcut_find.activated.connect(self.on_shortcut_find) 470 471 #open the files 472 for f in filenames: 473 if f: 474 self.on_load_request(os.path.normpath(f), search_text)
475 476 # print "================ create", self.objectName() 477 # 478 # def __del__(self): 479 # print "******** destroy", self.objectName() 480
481 - def on_load_request(self, filename, search_text=''):
482 ''' 483 Loads a file in a new tab or focus the tab, if the file is already open. 484 @param filename: the path to file 485 @type filename: C{str} 486 @param search_text: if not empty, searches in new document for first occurrence of the given text 487 @type search_text: C{str} (Default: C{Empty String}) 488 ''' 489 if not filename: 490 return 491 492 self.tabWidget.setUpdatesEnabled(False) 493 try: 494 if not filename in self.files: 495 tab_name = self.__getTabName(filename) 496 editor = Editor(filename, self.tabWidget) 497 tab_index = self.tabWidget.addTab(editor, tab_name) 498 self.files.append(filename) 499 editor.setCurrentPath(os.path.basename(filename)) 500 editor.load_request_signal.connect(self.on_load_request) 501 502 hl = XmlHighlighter(editor.document()) 503 editor.textChanged.connect(self.on_editor_textChanged) 504 editor.cursorPositionChanged.connect(self.on_editor_positionChanged) 505 editor.setFocus(QtCore.Qt.OtherFocusReason) 506 self.tabWidget.setCurrentIndex(tab_index) 507 else: 508 for i in range(self.tabWidget.count()): 509 if self.tabWidget.widget(i).filename == filename: 510 self.tabWidget.setCurrentIndex(i) 511 break 512 except: 513 import traceback 514 rospy.logwarn("Error while open %s: %s", filename, traceback.format_exc()) 515 516 self.tabWidget.setUpdatesEnabled(True) 517 if search_text: 518 if self.find(search_text, False): 519 if not self.find_dialog.search_pos.isNull(): 520 self.tabWidget.currentWidget().moveCursor(QtGui.QTextCursor.StartOfLine)
521
522 - def on_close_tab(self, tab_index):
523 ''' 524 Signal handling to close single tabs. 525 @param tab_index: tab index to close 526 @type tab_index: C{int} 527 ''' 528 try: 529 doremove = True 530 w = self.tabWidget.widget(tab_index) 531 if w.document().isModified(): 532 name = self.__getTabName(w.filename) 533 result = QtGui.QMessageBox.question(self, "Unsaved Changes", '\n\n'.join(["Save the file before closing?", name]), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No | QtGui.QMessageBox.Cancel) 534 if result == QtGui.QMessageBox.Yes: 535 self.tabWidget.currentWidget().save() 536 elif result == QtGui.QMessageBox.No: 537 pass 538 else: 539 doremove = False 540 if doremove: 541 # remove the indexed files 542 if w.filename in self.files: 543 self.files.remove(w.filename) 544 # close tab 545 self.tabWidget.removeTab(tab_index) 546 # close editor, if no tabs are open 547 if not self.tabWidget.count(): 548 self.close() 549 except: 550 import traceback 551 rospy.logwarn("Error while close tab %s: %s", str(tab_index), traceback.format_exc())
552 553 # def hideEvent(self, event): 554 # self.close() 555
556 - def closeEvent (self, event):
557 ''' 558 Test the open files for changes and save this if needed. 559 ''' 560 changed = [] 561 #get the names of all changed files 562 for i in range(self.tabWidget.count()): 563 w = self.tabWidget.widget(i) 564 if w.document().isModified(): 565 changed.append(self.__getTabName(w.filename)) 566 if changed: 567 # ask the user for save changes 568 if self.isHidden(): 569 buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No 570 else: 571 buttons = QtGui.QMessageBox.Yes | QtGui.QMessageBox.No | QtGui.QMessageBox.Cancel 572 result = QtGui.QMessageBox.question(self, "Unsaved Changes", '\n\n'.join(["Save the file before closing?", '\n'.join(changed)]), buttons) 573 if result == QtGui.QMessageBox.Yes: 574 for i in range(self.tabWidget.count()): 575 w = self.tabWidget.widget(i).save() 576 event.accept() 577 elif result == QtGui.QMessageBox.No: 578 event.accept() 579 else: 580 event.ignore() 581 else: 582 event.accept() 583 if event.isAccepted(): 584 self.finished_signal.emit(self.init_filenames)
585
586 - def on_saveButton_clicked(self):
587 ''' 588 Saves the current document. This method is called if the C{save button} 589 was clicked. 590 ''' 591 if self.tabWidget.currentWidget().save(): 592 self.on_editor_textChanged()
593
594 - def on_editor_textChanged(self):
595 ''' 596 If the content was changed, a '*' will be shown in the tab name. 597 ''' 598 tab_name = self.__getTabName(self.tabWidget.currentWidget().filename) 599 if (self.tabWidget.currentWidget().document().isModified()): 600 tab_name = ''.join(['*', tab_name]) 601 self.tabWidget.setTabText(self.tabWidget.currentIndex(), tab_name)
602
604 ''' 605 Shows the number of the line and column in a label. 606 ''' 607 cursor = self.tabWidget.currentWidget().textCursor() 608 self.pos_label.setText(''.join([str(cursor.blockNumber()+1), ':', str(cursor.columnNumber()+1)]))
609
610 - def on_shortcut_find(self):
611 ''' 612 Opens a find dialog. 613 ''' 614 self.find_dialog.show() 615 self.find_dialog.raise_() 616 self.find_dialog.activateWindow()
617
618 - def on_shortcut_goto(self):
619 ''' 620 Opens a C{goto} dialog. 621 ''' 622 value, ok = QtGui.QInputDialog.getInt(self, "Goto", 623 self.tr("Line number:"), QtGui.QLineEdit.Normal, 624 minValue=1, step=1) 625 if ok: 626 if value > self.tabWidget.currentWidget().document().blockCount(): 627 value = self.tabWidget.currentWidget().document().blockCount() 628 curpos = self.tabWidget.currentWidget().textCursor().blockNumber()+1 629 while curpos != value: 630 mov = QtGui.QTextCursor.NextBlock if curpos < value else QtGui.QTextCursor.PreviousBlock 631 self.tabWidget.currentWidget().moveCursor(mov) 632 curpos = self.tabWidget.currentWidget().textCursor().blockNumber()+1
633
634 - def __getTabName(self, file):
635 base = os.path.basename(file).replace('.launch', '') 636 package = self.__getPackageName(os.path.dirname(file)) 637 return ''.join([str(base), ' [', str(package),']'])
638
639 - def __getPackageName(self, dir):
640 if not (dir is None) and dir and dir != '/' and os.path.isdir(dir): 641 package = os.path.basename(dir) 642 fileList = os.listdir(dir) 643 for file in fileList: 644 if file == 'manifest.xml': 645 return package 646 return self.__getPackageName(os.path.dirname(dir)) 647 return None
648 649
650 - def on_find_dialog_clicked(self, button):
651 ''' 652 Method to handle the button actions of the C{find dialog}. 653 ''' 654 if button == self.find_dialog.find_button: 655 self.find(self.find_dialog.search_field.text(), self.find_dialog.recursive.isChecked()) 656 elif button == self.find_dialog.replace_button: 657 self.find_dialog.recursive.setChecked(False) 658 cursor = self.tabWidget.currentWidget().textCursor() 659 if self.find_dialog.search_field.text() and cursor.selectedText() == self.find_dialog.search_field.text(): 660 cursor.insertText(self.find_dialog.replace_field.text()) 661 currentLine = str(cursor.blockNumber()+1) 662 self.find_dialog.result_label.setText(''.join(["'", self.find_dialog.search_text, "'", ' replaced at line: ', currentLine, ' by ', "'", self.find_dialog.replace_field.text(),"'"])) 663 self.tabWidget.currentWidget().setTextCursor(cursor) 664 self.find(self.find_dialog.search_field.text(), self.find_dialog.recursive.isChecked())
665
666 - def find(self, search_text, recursive):
667 ''' 668 Searches for text in the current text editor. If `recursive` is C{True}, 669 the included files will be searched. 670 @param search_text: text to find 671 @type search_text: C{str} 672 @param recursive: search in included files if this is C{True} 673 @type recursive: C{bool} 674 ''' 675 found = False 676 if self.find_dialog.search_text != search_text: 677 self.find_dialog.search_pos = QtGui.QTextCursor() 678 self.find_dialog.found_files.clear() 679 self.find_dialog.found_files.setVisible(False) 680 self.find_dialog.result_label.setText(''.join(["'", search_text, "'", ' not found!'])) 681 self.find_dialog.search_text = search_text 682 if search_text: 683 if recursive: 684 files = self.tabWidget.currentWidget().fileWithText(search_text) 685 items = list(set(files)) 686 self.find_dialog.result_label.setText(''.join(["'", search_text, "'", ' found in ', str(len(items)), ' files:'])) 687 self.find_dialog.found_files.clear() 688 self.find_dialog.found_files.addItems(items) 689 self.find_dialog.found_files.setVisible(True) 690 self.find_dialog.resize(self.find_dialog.found_files.contentsSize()) 691 found = True 692 else: 693 tmp_pos = self.find_dialog.search_pos 694 self.find_dialog.search_pos = self.tabWidget.currentWidget().document().find(search_text, self.find_dialog.search_pos.position()+1) 695 if self.find_dialog.search_pos.isNull() and not tmp_pos.isNull(): 696 self.find_dialog.search_pos = self.tabWidget.currentWidget().document().find(search_text, self.find_dialog.search_pos.position()+1) 697 # do recursive search 698 if not self.find_dialog.search_pos.isNull(): 699 self.tabWidget.currentWidget().setTextCursor(self.find_dialog.search_pos) 700 currentTabName = self.tabWidget.tabText(self.tabWidget.currentIndex()) 701 currentLine = str(self.tabWidget.currentWidget().textCursor().blockNumber()+1) 702 self.find_dialog.result_label.setText(''.join(["'", search_text, "'", ' found at line: ', currentLine, ' in ', "'", currentTabName,"'"])) 703 found = True 704 else: 705 self.find_dialog.result_label.setText(''.join(["'", search_text, "'", ' not found!'])) 706 return found
707
708 - def find_dialog_itemActivated(self, item):
709 ''' 710 On recursive search all files contained the search text are listed. If one of 711 this file is activated, it will be open in a new tab and the cursor moved to 712 the C{search text} position. 713 @param item: The activated item of the C{QListWidget} 714 @type item: L{PySide.QtGui.QListWidgetItem} 715 ''' 716 self.find_dialog.recursive.setChecked(False) 717 self.on_load_request(item.text(), self.find_dialog.search_text) 718 self.find(self.find_dialog.search_text, False) 719 currentTabName = self.tabWidget.tabText(self.tabWidget.currentIndex()) 720 currentLine = str(self.tabWidget.currentWidget().textCursor().blockNumber()+1) 721 self.find_dialog.result_label.setText(''.join(["'", self.find_dialog.search_text, "'", ' found at line: ', currentLine, ' in ', "'", currentTabName,"'"]))
722