34 """ |
34 """ |
35 Class implementing a customized tab bar supporting drag & drop. |
35 Class implementing a customized tab bar supporting drag & drop. |
36 |
36 |
37 @signal tabMoveRequested(int, int) emitted to signal a tab move request giving |
37 @signal tabMoveRequested(int, int) emitted to signal a tab move request giving |
38 the old and new index position |
38 the old and new index position |
39 @signal tabRelocateRequested(long, int, int) emitted to signal a tab relocation |
39 @signal tabRelocateRequested(str, int, int) emitted to signal a tab relocation |
40 request giving the id of the old tab widget, the index in the old tab widget |
40 request giving the string encoded id of the old tab widget, the index in |
41 and the new index position |
41 the old tab widget and the new index position |
42 @signal tabCopyRequested(long, int, int) emitted to signal a clone request |
42 @signal tabCopyRequested(str, int, int) emitted to signal a clone request |
43 giving the id of the source tab widget, the index in the source tab widget |
43 giving the string encoded id of the source tab widget, the index in the |
44 and the new index position |
44 source tab widget and the new index position |
45 @signal tabCopyRequested(int, int) emitted to signal a clone request giving |
45 @signal tabCopyRequested(int, int) emitted to signal a clone request giving |
46 the old and new index position |
46 the old and new index position |
47 """ |
47 """ |
48 tabMoveRequested = pyqtSignal(int, int) |
48 tabMoveRequested = pyqtSignal(int, int) |
49 tabRelocateRequested = pyqtSignal(int, int, int) |
49 tabRelocateRequested = pyqtSignal(str, int, int) |
50 tabCopyRequested = pyqtSignal(int, int, int) |
50 tabCopyRequested = pyqtSignal((str, int, int), (int, int)) |
51 tabCopyRequested = pyqtSignal(int, int) |
|
52 |
51 |
53 def __init__(self, parent=None): |
52 def __init__(self, parent=None): |
54 """ |
53 """ |
55 Constructor |
54 Constructor |
56 |
55 |
83 drag = QDrag(self) |
82 drag = QDrag(self) |
84 mimeData = QMimeData() |
83 mimeData = QMimeData() |
85 index = self.tabAt(event.pos()) |
84 index = self.tabAt(event.pos()) |
86 mimeData.setText(self.tabText(index)) |
85 mimeData.setText(self.tabText(index)) |
87 mimeData.setData("action", "tab-reordering") |
86 mimeData.setData("action", "tab-reordering") |
88 mimeData.setData("tabbar-id", QByteArray.number(id(self))) |
87 mimeData.setData("tabbar-id", str(id(self))) |
89 mimeData.setData("source-index", |
88 mimeData.setData("source-index", |
90 QByteArray.number(self.tabAt(self.__dragStartPos))) |
89 QByteArray.number(self.tabAt(self.__dragStartPos))) |
91 mimeData.setData("tabwidget-id", QByteArray.number(id(self.parentWidget()))) |
90 mimeData.setData("tabwidget-id", str(id(self.parentWidget()))) |
92 drag.setMimeData(mimeData) |
91 drag.setMimeData(mimeData) |
93 if event.modifiers() == Qt.KeyboardModifiers(Qt.ShiftModifier): |
92 if event.modifiers() == Qt.KeyboardModifiers(Qt.ShiftModifier): |
94 drag.exec_(Qt.DropActions(Qt.CopyAction)) |
93 drag.exec_(Qt.DropActions(Qt.CopyAction)) |
95 elif event.modifiers() == Qt.KeyboardModifiers(Qt.NoModifier): |
94 elif event.modifiers() == Qt.KeyboardModifiers(Qt.NoModifier): |
96 drag.exec_(Qt.DropActions(Qt.MoveAction)) |
95 drag.exec_(Qt.DropActions(Qt.MoveAction)) |
117 Protected method to handle drop events. |
116 Protected method to handle drop events. |
118 |
117 |
119 @param event reference to the drop event (QDropEvent) |
118 @param event reference to the drop event (QDropEvent) |
120 """ |
119 """ |
121 mimeData = event.mimeData() |
120 mimeData = event.mimeData() |
122 oldID = mimeData.data("tabbar-id").toLong()[0] |
121 oldID = int(mimeData.data("tabbar-id")) |
123 fromIndex = mimeData.data("source-index").toInt()[0] |
122 fromIndex = mimeData.data("source-index").toInt()[0] |
124 toIndex = self.tabAt(event.pos()) |
123 toIndex = self.tabAt(event.pos()) |
125 if oldID != id(self): |
124 if oldID != id(self): |
126 parentID = mimeData.data("tabwidget-id").toLong()[0] |
125 parentID = int(mimeData.data("tabwidget-id")) |
127 if event.proposedAction() == Qt.MoveAction: |
126 if event.proposedAction() == Qt.MoveAction: |
128 self.tabRelocateRequested.emit(parentID, fromIndex, toIndex) |
127 self.tabRelocateRequested.emit(str(parentID), fromIndex, toIndex) |
129 event.acceptProposedAction() |
128 event.acceptProposedAction() |
130 elif event.proposedAction() == Qt.CopyAction: |
129 elif event.proposedAction() == Qt.CopyAction: |
131 self.tabCopyRequested.emit(parentID, fromIndex, toIndex) |
130 self.tabCopyRequested[str, int, int].emit( |
|
131 str(parentID), fromIndex, toIndex) |
132 event.acceptProposedAction() |
132 event.acceptProposedAction() |
133 else: |
133 else: |
134 if fromIndex != toIndex: |
134 if fromIndex != toIndex: |
135 if event.proposedAction() == Qt.MoveAction: |
135 if event.proposedAction() == Qt.MoveAction: |
136 self.tabMoveRequested.emit(fromIndex, toIndex) |
136 self.tabMoveRequested.emit(fromIndex, toIndex) |
137 event.acceptProposedAction() |
137 event.acceptProposedAction() |
138 elif event.proposedAction() == Qt.CopyAction: |
138 elif event.proposedAction() == Qt.CopyAction: |
139 self.tabCopyRequested.emit(fromIndex, toIndex) |
139 self.tabCopyRequested[int, int].emit(fromIndex, toIndex) |
140 event.acceptProposedAction() |
140 event.acceptProposedAction() |
141 super().dropEvent(event) |
141 super().dropEvent(event) |
142 |
142 |
143 |
143 |
144 class TabWidget(E5TabWidget): |
144 class TabWidget(E5TabWidget): |
314 if on: |
314 if on: |
315 self.indicator.setColor(QColor("green")) |
315 self.indicator.setColor(QColor("green")) |
316 else: |
316 else: |
317 self.indicator.setColor(QColor("red")) |
317 self.indicator.setColor(QColor("red")) |
318 |
318 |
319 def addTab(self, editor, title): |
319 def addTab(self, assembly, title): |
320 """ |
320 """ |
321 Overwritten method to add a new tab. |
321 Overwritten method to add a new tab. |
322 |
322 |
323 @param editor the editor object to be added (QScintilla.Editor.Editor) |
323 @param assembly editor assembly object to be added |
|
324 (QScintilla.EditorAssembly.EditorAssembly) |
324 @param title title for the new tab (string) |
325 @param title title for the new tab (string) |
325 """ |
326 """ |
326 assembly = editor.parent() |
327 editor = assembly.getEditor() |
327 super().addTab(assembly, UI.PixmapCache.getIcon("empty.png"), title) |
328 super().addTab(assembly, UI.PixmapCache.getIcon("empty.png"), title) |
328 if self.closeButton: |
329 if self.closeButton: |
329 self.closeButton.setEnabled(True) |
330 self.closeButton.setEnabled(True) |
330 else: |
331 else: |
331 self.setTabsClosable(True) |
332 self.setTabsClosable(True) |
337 |
338 |
338 emptyIndex = self.indexOf(self.emptyLabel) |
339 emptyIndex = self.indexOf(self.emptyLabel) |
339 if emptyIndex > -1: |
340 if emptyIndex > -1: |
340 self.removeTab(emptyIndex) |
341 self.removeTab(emptyIndex) |
341 |
342 |
342 def insertWidget(self, index, editor, title): |
343 def insertWidget(self, index, assembly, title): |
343 """ |
344 """ |
344 Overwritten method to insert a new tab. |
345 Overwritten method to insert a new tab. |
345 |
346 |
346 @param index index position for the new tab (integer) |
347 @param index index position for the new tab (integer) |
347 @param editor the editor object to be added (QScintilla.Editor.Editor) |
348 @param assembly editor assembly object to be added |
|
349 (QScintilla.EditorAssembly.EditorAssembly) |
348 @param title title for the new tab (string) |
350 @param title title for the new tab (string) |
349 @return index of the inserted tab (integer) |
351 @return index of the inserted tab (integer) |
350 """ |
352 """ |
351 assembly = editor.parent() |
353 editor = assembly.getEditor() |
352 newIndex = super().insertTab(index, assembly, |
354 newIndex = super().insertTab(index, assembly, |
353 UI.PixmapCache.getIcon("empty.png"), |
355 UI.PixmapCache.getIcon("empty.png"), |
354 title) |
356 title) |
355 if self.closeButton: |
357 if self.closeButton: |
356 self.closeButton.setEnabled(True) |
358 self.closeButton.setEnabled(True) |
418 self.closeButton.setEnabled(False) |
420 self.closeButton.setEnabled(False) |
419 else: |
421 else: |
420 self.setTabsClosable(False) |
422 self.setTabsClosable(False) |
421 self.navigationButton.setEnabled(False) |
423 self.navigationButton.setEnabled(False) |
422 |
424 |
423 def relocateTab(self, sourceId, sourceIndex, targetIndex): |
425 def __relocateTab(self, sourceId, sourceIndex, targetIndex): |
424 """ |
426 """ |
425 Public method to relocate an editor from another TabWidget. |
427 Private method to relocate an editor from another TabWidget. |
426 |
428 |
427 @param sourceId id of the TabWidget to get the editor from (long) |
429 @param sourceId id of the TabWidget to get the editor from (string) |
428 @param sourceIndex index of the tab in the old tab widget (integer) |
430 @param sourceIndex index of the tab in the old tab widget (integer) |
429 @param targetIndex index position to place it to (integer) |
431 @param targetIndex index position to place it to (integer) |
430 """ |
432 """ |
431 tw = self.vm.getTabWidgetById(sourceId) |
433 tw = self.vm.getTabWidgetById(int(sourceId)) |
432 if tw is not None: |
434 if tw is not None: |
433 # step 1: get data of the tab of the source |
435 # step 1: get data of the tab of the source |
434 toolTip = tw.tabToolTip(sourceIndex) |
436 toolTip = tw.tabToolTip(sourceIndex) |
435 text = tw.tabText(sourceIndex) |
437 text = tw.tabText(sourceIndex) |
436 icon = tw.tabIcon(sourceIndex) |
438 icon = tw.tabIcon(sourceIndex) |
437 whatsThis = tw.tabWhatsThis(sourceIndex) |
439 whatsThis = tw.tabWhatsThis(sourceIndex) |
438 editor = tw.widget(sourceIndex).getEditor() |
440 assembly = tw.widget(sourceIndex) |
439 |
441 |
440 # step 2: relocate the tab |
442 # step 2: relocate the tab |
441 tw.removeWidget(editor) |
443 tw.removeWidget(assembly.getEditor()) |
442 self.insertWidget(targetIndex, editor, text) |
444 self.insertWidget(targetIndex, assembly, text) |
443 |
445 |
444 # step 3: set the tab data again |
446 # step 3: set the tab data again |
445 self.setTabIcon(targetIndex, icon) |
447 self.setTabIcon(targetIndex, icon) |
446 self.setTabToolTip(targetIndex, toolTip) |
448 self.setTabToolTip(targetIndex, toolTip) |
447 self.setTabWhatsThis(targetIndex, whatsThis) |
449 self.setTabWhatsThis(targetIndex, whatsThis) |
448 |
450 |
449 # step 4: set current widget |
451 # step 4: set current widget |
450 self.setCurrentIndex(targetIndex) |
452 self.setCurrentIndex(targetIndex) |
451 |
453 |
452 def copyTabOther(self, sourceId, sourceIndex, targetIndex): |
454 def __copyTabOther(self, sourceId, sourceIndex, targetIndex): |
453 """ |
455 """ |
454 Public method to copy an editor from another TabWidget. |
456 Private method to copy an editor from another TabWidget. |
455 |
457 |
456 @param sourceId id of the TabWidget to get the editor from (long) |
458 @param sourceId id of the TabWidget to get the editor from (string) |
457 @param sourceIndex index of the tab in the old tab widget (integer) |
459 @param sourceIndex index of the tab in the old tab widget (integer) |
458 @param targetIndex index position to place it to (integer) |
460 @param targetIndex index position to place it to (integer) |
459 """ |
461 """ |
460 tw = self.vm.getTabWidgetById(sourceId) |
462 tw = self.vm.getTabWidgetById(int(sourceId)) |
461 if tw is not None: |
463 if tw is not None: |
462 editor = tw.widget(sourceIndex).getEditor() |
464 editor = tw.widget(sourceIndex).getEditor() |
463 newEditor = self.vm.cloneEditor(editor, editor.getFileType(), |
465 newEditor, assembly = self.vm.cloneEditor(editor, editor.getFileType(), |
464 editor.getFileName()) |
466 editor.getFileName()) |
465 self.vm.insertView(newEditor, self, targetIndex, |
467 self.vm.insertView(assembly, self, targetIndex, |
466 editor.getFileName(), editor.getNoName()) |
468 editor.getFileName(), editor.getNoName()) |
467 |
469 |
468 def copyTab(self, sourceIndex, targetIndex): |
470 def __copyTab(self, sourceIndex, targetIndex): |
469 """ |
471 """ |
470 Public method to copy an editor. |
472 Private method to copy an editor. |
471 |
473 |
472 @param sourceIndex index of the tab (integer) |
474 @param sourceIndex index of the tab (integer) |
473 @param targetIndex index position to place it to (integer) |
475 @param targetIndex index position to place it to (integer) |
474 """ |
476 """ |
475 editor = self.widget(sourceIndex).getEditor() |
477 editor = self.widget(sourceIndex).getEditor() |
476 newEditor = self.vm.cloneEditor(editor, editor.getFileType(), |
478 newEditor, assembly = self.vm.cloneEditor(editor, editor.getFileType(), |
477 editor.getFileName()) |
479 editor.getFileName()) |
478 self.vm.insertView(newEditor, self, targetIndex, |
480 self.vm.insertView(assembly, self, targetIndex, |
479 editor.getFileName(), editor.getNoName()) |
481 editor.getFileName(), editor.getNoName()) |
480 |
482 |
481 def currentWidget(self): |
483 def currentWidget(self): |
482 """ |
484 """ |
483 Overridden method to return a reference to the current page. |
485 Overridden method to return a reference to the current page. |
788 |
790 |
789 def _addView(self, win, fn=None, noName=""): |
791 def _addView(self, win, fn=None, noName=""): |
790 """ |
792 """ |
791 Protected method to add a view (i.e. window) |
793 Protected method to add a view (i.e. window) |
792 |
794 |
793 @param win editor window to be added |
795 @param win editor assembly to be added |
794 @param fn filename of this editor (string) |
796 @param fn filename of this editor (string) |
795 @param noName name to be used for an unnamed editor (string) |
797 @param noName name to be used for an unnamed editor (string) |
796 """ |
798 """ |
|
799 editor = win.getEditor() |
797 if fn is None: |
800 if fn is None: |
798 if not noName: |
801 if not noName: |
799 self.untitledCount += 1 |
802 self.untitledCount += 1 |
800 noName = self.trUtf8("Untitled {0}").format(self.untitledCount) |
803 noName = self.trUtf8("Untitled {0}").format(self.untitledCount) |
801 self.currentTabWidget.addTab(win, noName) |
804 self.currentTabWidget.addTab(win, noName) |
802 win.setNoName(noName) |
805 editor.setNoName(noName) |
803 else: |
806 else: |
804 if self.filenameOnly: |
807 if self.filenameOnly: |
805 txt = os.path.basename(fn) |
808 txt = os.path.basename(fn) |
806 else: |
809 else: |
807 txt = e5App().getObject("Project").getRelativePath(fn) |
810 txt = e5App().getObject("Project").getRelativePath(fn) |
812 self.currentTabWidget.addTab(win, txt) |
815 self.currentTabWidget.addTab(win, txt) |
813 index = self.currentTabWidget.indexOf(win) |
816 index = self.currentTabWidget.indexOf(win) |
814 self.currentTabWidget.setTabToolTip(index, fn) |
817 self.currentTabWidget.setTabToolTip(index, fn) |
815 self.currentTabWidget.setCurrentWidget(win) |
818 self.currentTabWidget.setCurrentWidget(win) |
816 win.show() |
819 win.show() |
817 win.setFocus() |
820 editor.setFocus() |
818 if fn: |
821 if fn: |
819 self.changeCaption.emit(fn) |
822 self.changeCaption.emit(fn) |
820 self.editorChanged.emit(fn) |
823 self.editorChanged.emit(fn) |
821 else: |
824 else: |
822 self.changeCaption.emit("") |
825 self.changeCaption.emit("") |
823 |
826 |
824 def insertView(self, win, tabWidget, index, fn=None, noName=""): |
827 def insertView(self, win, tabWidget, index, fn=None, noName=""): |
825 """ |
828 """ |
826 Protected method to add a view (i.e. window) |
829 Protected method to add a view (i.e. window) |
827 |
830 |
828 @param win editor window to be added |
831 @param win editor assembly to be inserted |
829 @param tabWidget reference to the tab widget to insert the editor into (TabWidget) |
832 @param tabWidget reference to the tab widget to insert the editor into (TabWidget) |
830 @param index index position to insert at (integer) |
833 @param index index position to insert at (integer) |
831 @param fn filename of this editor (string) |
834 @param fn filename of this editor (string) |
832 @param noName name to be used for an unnamed editor (string) |
835 @param noName name to be used for an unnamed editor (string) |
833 """ |
836 """ |
|
837 editor = win.getEditor() |
834 if fn is None: |
838 if fn is None: |
835 if not noName: |
839 if not noName: |
836 self.untitledCount += 1 |
840 self.untitledCount += 1 |
837 noName = self.trUtf8("Untitled {0}").format(self.untitledCount) |
841 noName = self.trUtf8("Untitled {0}").format(self.untitledCount) |
838 tabWidget.insertWidget(index, win, noName) |
842 tabWidget.insertWidget(index, win, noName) |
839 win.setNoName(noName) |
843 editor.setNoName(noName) |
840 else: |
844 else: |
841 if self.filenameOnly: |
845 if self.filenameOnly: |
842 txt = os.path.basename(fn) |
846 txt = os.path.basename(fn) |
843 else: |
847 else: |
844 txt = e5App().getObject("Project").getRelativePath(fn) |
848 txt = e5App().getObject("Project").getRelativePath(fn) |
848 txt = self.trUtf8("{0} (ro)").format(txt) |
852 txt = self.trUtf8("{0} (ro)").format(txt) |
849 nindex = tabWidget.insertWidget(index, win, txt) |
853 nindex = tabWidget.insertWidget(index, win, txt) |
850 tabWidget.setTabToolTip(nindex, fn) |
854 tabWidget.setTabToolTip(nindex, fn) |
851 tabWidget.setCurrentWidget(win) |
855 tabWidget.setCurrentWidget(win) |
852 win.show() |
856 win.show() |
853 win.setFocus() |
857 editor.setFocus() |
854 if fn: |
858 if fn: |
855 self.changeCaption.emit(fn) |
859 self.changeCaption.emit(fn) |
856 self.editorChanged.emit(fn) |
860 self.editorChanged.emit(fn) |
857 else: |
861 else: |
858 self.changeCaption.emit("") |
862 self.changeCaption.emit("") |
859 |
863 |
860 self._modificationStatusChanged(win.isModified(), win) |
864 self._modificationStatusChanged(editor.isModified(), editor) |
861 self._checkActions(win) |
865 self._checkActions(editor) |
862 |
866 |
863 def _showView(self, win, fn=None): |
867 def _showView(self, win, fn=None): |
864 """ |
868 """ |
865 Protected method to show a view (i.e. window) |
869 Protected method to show a view (i.e. window) |
866 |
870 |