|
1 # -*- coding: utf-8 -*- |
|
2 |
|
3 # Copyright (c) 2002 - 2009 Detlev Offenbach <detlev@die-offenbachs.de> |
|
4 # |
|
5 |
|
6 """ |
|
7 Module implementing a tabbed viewmanager class. |
|
8 """ |
|
9 |
|
10 import os |
|
11 |
|
12 from PyQt4.QtCore import * |
|
13 from PyQt4.QtGui import * |
|
14 |
|
15 from E4Gui.E4Application import e4App |
|
16 |
|
17 from ViewManager.ViewManager import ViewManager |
|
18 |
|
19 import QScintilla.Editor |
|
20 |
|
21 import UI.PixmapCache |
|
22 |
|
23 from E4Gui.E4TabWidget import E4TabWidget, E4WheelTabBar |
|
24 from E4Gui.E4Led import E4Led |
|
25 |
|
26 import Preferences |
|
27 |
|
28 from eric4config import getConfig |
|
29 |
|
30 class TabBar(E4WheelTabBar): |
|
31 """ |
|
32 Class implementing a customized tab bar supporting drag & drop. |
|
33 |
|
34 @signal tabMoveRequested(int, int) emitted to signal a tab move request giving |
|
35 the old and new index position |
|
36 @signal tabRelocateRequested(long, int, int) emitted to signal a tab relocation |
|
37 request giving the id of the old tab widget, the index in the old tab widget |
|
38 and the new index position |
|
39 @signal tabCopyRequested(long, int, int) emitted to signal a clone request |
|
40 giving the id of the source tab widget, the index in the source tab widget |
|
41 and the new index position |
|
42 @signal tabCopyRequested(int, int) emitted to signal a clone request giving |
|
43 the old and new index position |
|
44 """ |
|
45 def __init__(self, parent = None): |
|
46 """ |
|
47 Constructor |
|
48 |
|
49 @param parent reference to the parent widget (QWidget) |
|
50 """ |
|
51 E4WheelTabBar.__init__(self, parent) |
|
52 self.setAcceptDrops(True) |
|
53 |
|
54 self.__dragStartPos = QPoint() |
|
55 |
|
56 def mousePressEvent(self, event): |
|
57 """ |
|
58 Protected method to handle mouse press events. |
|
59 |
|
60 @param event reference to the mouse press event (QMouseEvent) |
|
61 """ |
|
62 if event.button() == Qt.LeftButton: |
|
63 self.__dragStartPos = QPoint(event.pos()) |
|
64 E4WheelTabBar.mousePressEvent(self, event) |
|
65 |
|
66 def mouseMoveEvent(self, event): |
|
67 """ |
|
68 Protected method to handle mouse move events. |
|
69 |
|
70 @param event reference to the mouse move event (QMouseEvent) |
|
71 """ |
|
72 if event.buttons() == Qt.MouseButtons(Qt.LeftButton) and \ |
|
73 (event.pos() - self.__dragStartPos).manhattanLength() > \ |
|
74 QApplication.startDragDistance(): |
|
75 drag = QDrag(self) |
|
76 mimeData = QMimeData() |
|
77 index = self.tabAt(event.pos()) |
|
78 mimeData.setText(self.tabText(index)) |
|
79 mimeData.setData("action", "tab-reordering") |
|
80 mimeData.setData("tabbar-id", QByteArray.number(id(self))) |
|
81 mimeData.setData("source-index", |
|
82 QByteArray.number(self.tabAt(self.__dragStartPos))) |
|
83 mimeData.setData("tabwidget-id", QByteArray.number(id(self.parentWidget()))) |
|
84 drag.setMimeData(mimeData) |
|
85 if event.modifiers() == Qt.KeyboardModifiers(Qt.ShiftModifier): |
|
86 drag.exec_(Qt.DropActions(Qt.CopyAction)) |
|
87 elif event.modifiers() == Qt.KeyboardModifiers(Qt.NoModifier): |
|
88 drag.exec_(Qt.DropActions(Qt.MoveAction)) |
|
89 E4WheelTabBar.mouseMoveEvent(self, event) |
|
90 |
|
91 def dragEnterEvent(self, event): |
|
92 """ |
|
93 Protected method to handle drag enter events. |
|
94 |
|
95 @param event reference to the drag enter event (QDragEnterEvent) |
|
96 """ |
|
97 mimeData = event.mimeData() |
|
98 formats = mimeData.formats() |
|
99 if formats.contains("action") and \ |
|
100 mimeData.data("action") == "tab-reordering" and \ |
|
101 formats.contains("tabbar-id") and \ |
|
102 formats.contains("source-index") and \ |
|
103 formats.contains("tabwidget-id"): |
|
104 event.acceptProposedAction() |
|
105 E4WheelTabBar.dragEnterEvent(self, event) |
|
106 |
|
107 def dropEvent(self, event): |
|
108 """ |
|
109 Protected method to handle drop events. |
|
110 |
|
111 @param event reference to the drop event (QDropEvent) |
|
112 """ |
|
113 mimeData = event.mimeData() |
|
114 oldID = mimeData.data("tabbar-id").toLong()[0] |
|
115 fromIndex = mimeData.data("source-index").toInt()[0] |
|
116 toIndex = self.tabAt(event.pos()) |
|
117 if oldID != id(self): |
|
118 parentID = mimeData.data("tabwidget-id").toLong()[0] |
|
119 if event.proposedAction() == Qt.MoveAction: |
|
120 self.emit(SIGNAL("tabRelocateRequested(long, int, int)"), |
|
121 parentID, fromIndex, toIndex) |
|
122 event.acceptProposedAction() |
|
123 elif event.proposedAction() == Qt.CopyAction: |
|
124 self.emit(SIGNAL("tabCopyRequested(long, int, int)"), |
|
125 parentID, fromIndex, toIndex) |
|
126 event.acceptProposedAction() |
|
127 else: |
|
128 if fromIndex != toIndex: |
|
129 if event.proposedAction() == Qt.MoveAction: |
|
130 self.emit(SIGNAL("tabMoveRequested(int, int)"), fromIndex, toIndex) |
|
131 event.acceptProposedAction() |
|
132 elif event.proposedAction() == Qt.CopyAction: |
|
133 self.emit(SIGNAL("tabCopyRequested(int, int)"), fromIndex, toIndex) |
|
134 event.acceptProposedAction() |
|
135 E4WheelTabBar.dropEvent(self, event) |
|
136 |
|
137 class TabWidget(E4TabWidget): |
|
138 """ |
|
139 Class implementing a custimized tab widget. |
|
140 """ |
|
141 def __init__(self, vm): |
|
142 """ |
|
143 Constructor |
|
144 |
|
145 @param vm view manager widget (Tabview) |
|
146 """ |
|
147 E4TabWidget.__init__(self) |
|
148 self.setAttribute(Qt.WA_DeleteOnClose, True) |
|
149 |
|
150 self.__tabBar = TabBar(self) |
|
151 self.setTabBar(self.__tabBar) |
|
152 |
|
153 self.connect(self.__tabBar, SIGNAL("tabMoveRequested(int, int)"), |
|
154 self.moveTab) |
|
155 self.connect(self.__tabBar, SIGNAL("tabRelocateRequested(long, int, int)"), |
|
156 self.relocateTab) |
|
157 self.connect(self.__tabBar, SIGNAL("tabCopyRequested(long, int, int)"), |
|
158 self.copyTabOther) |
|
159 self.connect(self.__tabBar, SIGNAL("tabCopyRequested(int, int)"), |
|
160 self.copyTab) |
|
161 |
|
162 self.vm = vm |
|
163 self.editors = [] |
|
164 |
|
165 self.indicator = E4Led(self) |
|
166 self.setCornerWidget(self.indicator, Qt.TopLeftCorner) |
|
167 |
|
168 self.rightCornerWidget = QWidget(self) |
|
169 self.rightCornerWidgetLayout = QHBoxLayout(self.rightCornerWidget) |
|
170 self.rightCornerWidgetLayout.setMargin(0) |
|
171 self.rightCornerWidgetLayout.setSpacing(0) |
|
172 |
|
173 self.__navigationMenu = QMenu(self) |
|
174 self.connect(self.__navigationMenu, SIGNAL("aboutToShow()"), |
|
175 self.__showNavigationMenu) |
|
176 self.connect(self.__navigationMenu, SIGNAL("triggered(QAction*)"), |
|
177 self.__navigationMenuTriggered) |
|
178 |
|
179 self.navigationButton = QToolButton(self) |
|
180 self.navigationButton.setIcon(UI.PixmapCache.getIcon("1downarrow.png")) |
|
181 self.navigationButton.setToolTip(self.trUtf8("Show a navigation menu")) |
|
182 self.navigationButton.setPopupMode(QToolButton.InstantPopup) |
|
183 self.navigationButton.setMenu(self.__navigationMenu) |
|
184 self.navigationButton.setEnabled(False) |
|
185 self.rightCornerWidgetLayout.addWidget(self.navigationButton) |
|
186 |
|
187 if Preferences.getUI("SingleCloseButton") or \ |
|
188 not hasattr(self, 'setTabsClosable'): |
|
189 self.closeButton = QToolButton(self) |
|
190 self.closeButton.setIcon(UI.PixmapCache.getIcon("close.png")) |
|
191 self.closeButton.setToolTip(self.trUtf8("Close the current editor")) |
|
192 self.closeButton.setEnabled(False) |
|
193 self.connect(self.closeButton, SIGNAL("clicked(bool)"), |
|
194 self.__closeButtonClicked) |
|
195 self.rightCornerWidgetLayout.addWidget(self.closeButton) |
|
196 else: |
|
197 self.connect(self, SIGNAL("tabCloseRequested(int)"), |
|
198 self.__closeRequested) |
|
199 self.closeButton = None |
|
200 |
|
201 self.setCornerWidget(self.rightCornerWidget, Qt.TopRightCorner) |
|
202 |
|
203 self.__initMenu() |
|
204 self.contextMenuEditor = None |
|
205 self.contextMenuIndex = -1 |
|
206 |
|
207 self.setTabContextMenuPolicy(Qt.CustomContextMenu) |
|
208 self.connect(self, SIGNAL('customTabContextMenuRequested(const QPoint &, int)'), |
|
209 self.__showContextMenu) |
|
210 |
|
211 ericPic = QPixmap(os.path.join(getConfig('ericPixDir'), 'eric_small.png')) |
|
212 self.emptyLabel = QLabel() |
|
213 self.emptyLabel.setPixmap(ericPic) |
|
214 self.emptyLabel.setAlignment(Qt.AlignVCenter | Qt.AlignHCenter) |
|
215 E4TabWidget.addTab(self, self.emptyLabel, UI.PixmapCache.getIcon("empty.png"), "") |
|
216 |
|
217 def __initMenu(self): |
|
218 """ |
|
219 Private method to initialize the tab context menu. |
|
220 """ |
|
221 self.__menu = QMenu(self) |
|
222 self.leftMenuAct = \ |
|
223 self.__menu.addAction(UI.PixmapCache.getIcon("1leftarrow.png"), |
|
224 self.trUtf8('Move Left'), self.__contextMenuMoveLeft) |
|
225 self.rightMenuAct = \ |
|
226 self.__menu.addAction(UI.PixmapCache.getIcon("1rightarrow.png"), |
|
227 self.trUtf8('Move Right'), self.__contextMenuMoveRight) |
|
228 self.firstMenuAct = \ |
|
229 self.__menu.addAction(UI.PixmapCache.getIcon("2leftarrow.png"), |
|
230 self.trUtf8('Move First'), self.__contextMenuMoveFirst) |
|
231 self.lastMenuAct = \ |
|
232 self.__menu.addAction(UI.PixmapCache.getIcon("2rightarrow.png"), |
|
233 self.trUtf8('Move Last'), self.__contextMenuMoveLast) |
|
234 self.__menu.addSeparator() |
|
235 self.__menu.addAction(UI.PixmapCache.getIcon("close.png"), |
|
236 self.trUtf8('Close'), self.__contextMenuClose) |
|
237 self.closeOthersMenuAct = self.__menu.addAction(self.trUtf8("Close Others"), |
|
238 self.__contextMenuCloseOthers) |
|
239 self.__menu.addAction(self.trUtf8('Close All'), self.__contextMenuCloseAll) |
|
240 self.__menu.addSeparator() |
|
241 self.saveMenuAct = \ |
|
242 self.__menu.addAction(UI.PixmapCache.getIcon("fileSave.png"), |
|
243 self.trUtf8('Save'), self.__contextMenuSave) |
|
244 self.__menu.addAction(UI.PixmapCache.getIcon("fileSaveAs.png"), |
|
245 self.trUtf8('Save As...'), self.__contextMenuSaveAs) |
|
246 self.__menu.addAction(UI.PixmapCache.getIcon("fileSaveAll.png"), |
|
247 self.trUtf8('Save All'), self.__contextMenuSaveAll) |
|
248 self.projectMenuAct = \ |
|
249 self.__menu.addAction(UI.PixmapCache.getIcon("fileSaveProject.png"), |
|
250 self.trUtf8('Save to Project'), self.__contextMenuSaveToProject) |
|
251 self.__menu.addSeparator() |
|
252 self.__menu.addAction(UI.PixmapCache.getIcon("print.png"), |
|
253 self.trUtf8('Print'), self.__contextMenuPrintFile) |
|
254 |
|
255 def __showContextMenu(self, coord, index): |
|
256 """ |
|
257 Private slot to show the tab context menu. |
|
258 |
|
259 @param coord the position of the mouse pointer (QPoint) |
|
260 @param index index of the tab the menu is requested for (integer) |
|
261 """ |
|
262 if self.editors: |
|
263 self.contextMenuEditor = self.widget(index) |
|
264 if self.contextMenuEditor: |
|
265 self.saveMenuAct.setEnabled(self.contextMenuEditor.isModified()) |
|
266 self.projectMenuAct.setEnabled(e4App().getObject("Project").isOpen()) |
|
267 |
|
268 self.contextMenuIndex = index |
|
269 self.leftMenuAct.setEnabled(index > 0) |
|
270 self.rightMenuAct.setEnabled(index < self.count() - 1) |
|
271 self.firstMenuAct.setEnabled(index > 0) |
|
272 self.lastMenuAct.setEnabled(index < self.count() - 1) |
|
273 |
|
274 self.closeOthersMenuAct.setEnabled(self.count() > 1) |
|
275 |
|
276 coord = self.mapToGlobal(coord) |
|
277 self.__menu.popup(coord) |
|
278 |
|
279 def __showNavigationMenu(self): |
|
280 """ |
|
281 Private slot to show the navigation button menu. |
|
282 """ |
|
283 self.__navigationMenu.clear() |
|
284 for index in range(self.count()): |
|
285 act = self.__navigationMenu.addAction(self.tabIcon(index), |
|
286 self.tabText(index)) |
|
287 act.setData(QVariant(index)) |
|
288 |
|
289 def __navigationMenuTriggered(self, act): |
|
290 """ |
|
291 Private slot called to handle the navigation button menu selection. |
|
292 |
|
293 @param act reference to the selected action (QAction) |
|
294 """ |
|
295 index, ok = act.data().toInt() |
|
296 if ok: |
|
297 self.setCurrentIndex(index) |
|
298 |
|
299 def showIndicator(self, on): |
|
300 """ |
|
301 Public slot to set the indicator on or off. |
|
302 |
|
303 @param on flag indicating the dtate of the indicator (boolean) |
|
304 """ |
|
305 if on: |
|
306 self.indicator.setColor(QColor("green")) |
|
307 else: |
|
308 self.indicator.setColor(QColor("red")) |
|
309 |
|
310 def addTab(self, editor, title): |
|
311 """ |
|
312 Overwritten method to add a new tab. |
|
313 |
|
314 @param editor the editor object to be added (QScintilla.Editor.Editor) |
|
315 @param title title for the new tab (string) |
|
316 """ |
|
317 E4TabWidget.addTab(self, editor, UI.PixmapCache.getIcon("empty.png"), title) |
|
318 if self.closeButton: |
|
319 self.closeButton.setEnabled(True) |
|
320 else: |
|
321 self.setTabsClosable(True) |
|
322 self.navigationButton.setEnabled(True) |
|
323 |
|
324 if not editor in self.editors: |
|
325 self.editors.append(editor) |
|
326 self.connect(editor, SIGNAL('captionChanged'), |
|
327 self.__captionChange) |
|
328 |
|
329 emptyIndex = self.indexOf(self.emptyLabel) |
|
330 if emptyIndex > -1: |
|
331 self.removeTab(emptyIndex) |
|
332 |
|
333 def insertWidget(self, index, editor, title): |
|
334 """ |
|
335 Overwritten method to insert a new tab. |
|
336 |
|
337 @param index index position for the new tab (integer) |
|
338 @param editor the editor object to be added (QScintilla.Editor.Editor) |
|
339 @param title title for the new tab (string) |
|
340 @return index of the inserted tab (integer) |
|
341 """ |
|
342 newIndex = E4TabWidget.insertTab(self, index, editor, |
|
343 UI.PixmapCache.getIcon("empty.png"), |
|
344 title) |
|
345 if self.closeButton: |
|
346 self.closeButton.setEnabled(True) |
|
347 else: |
|
348 self.setTabsClosable(True) |
|
349 self.navigationButton.setEnabled(True) |
|
350 |
|
351 if not editor in self.editors: |
|
352 self.editors.append(editor) |
|
353 self.connect(editor, SIGNAL('captionChanged'), |
|
354 self.__captionChange) |
|
355 |
|
356 emptyIndex = self.indexOf(self.emptyLabel) |
|
357 if emptyIndex > -1: |
|
358 self.removeTab(emptyIndex) |
|
359 |
|
360 return newIndex |
|
361 |
|
362 def __captionChange(self, cap, editor): |
|
363 """ |
|
364 Private method to handle Caption change signals from the editor. |
|
365 |
|
366 Updates the tab text and tooltip text to reflect the new caption information. |
|
367 |
|
368 @param cap Caption for the editor |
|
369 @param editor Editor to update the caption for |
|
370 """ |
|
371 fn = editor.getFileName() |
|
372 if fn: |
|
373 if Preferences.getUI("TabViewManagerFilenameOnly"): |
|
374 txt = os.path.basename(fn) |
|
375 else: |
|
376 txt = fn |
|
377 ppath = e4App().getObject("Project").getProjectPath() |
|
378 if ppath: |
|
379 txt = txt.replace(ppath + os.sep, "") |
|
380 |
|
381 maxFileNameChars = Preferences.getUI("TabViewManagerFilenameLength") |
|
382 if len(txt) > maxFileNameChars: |
|
383 txt = "...%s" % txt[-maxFileNameChars:] |
|
384 if editor.isReadOnly(): |
|
385 txt = self.trUtf8("{0} (ro)").format(txt) |
|
386 |
|
387 index = self.indexOf(editor) |
|
388 if index > -1: |
|
389 self.setTabText(index, txt) |
|
390 self.setTabToolTip(index, fn) |
|
391 |
|
392 def removeWidget(self, object): |
|
393 """ |
|
394 Public method to remove a widget. |
|
395 |
|
396 @param object object to be removed (QWidget) |
|
397 """ |
|
398 index = self.indexOf(object) |
|
399 if index > -1: |
|
400 self.removeTab(index) |
|
401 |
|
402 if isinstance(object, QScintilla.Editor.Editor): |
|
403 self.disconnect(object, SIGNAL('captionChanged'), |
|
404 self.__captionChange) |
|
405 self.editors.remove(object) |
|
406 |
|
407 if not self.editors: |
|
408 E4TabWidget.addTab(self, self.emptyLabel, |
|
409 UI.PixmapCache.getIcon("empty.png"), "") |
|
410 self.emptyLabel.show() |
|
411 if self.closeButton: |
|
412 self.closeButton.setEnabled(False) |
|
413 else: |
|
414 self.setTabsClosable(False) |
|
415 self.navigationButton.setEnabled(False) |
|
416 |
|
417 def relocateTab(self, sourceId, sourceIndex, targetIndex): |
|
418 """ |
|
419 Public method to relocate an editor from another TabWidget. |
|
420 |
|
421 @param sourceId id of the TabWidget to get the editor from (long) |
|
422 @param sourceIndex index of the tab in the old tab widget (integer) |
|
423 @param targetIndex index position to place it to (integer) |
|
424 """ |
|
425 tw = self.vm.getTabWidgetById(sourceId) |
|
426 if tw is not None: |
|
427 # step 1: get data of the tab of the source |
|
428 toolTip = tw.tabToolTip(sourceIndex) |
|
429 text = tw.tabText(sourceIndex) |
|
430 icon = tw.tabIcon(sourceIndex) |
|
431 whatsThis = tw.tabWhatsThis(sourceIndex) |
|
432 editor = tw.widget(sourceIndex) |
|
433 |
|
434 # step 2: relocate the tab |
|
435 tw.removeWidget(editor) |
|
436 self.insertWidget(targetIndex, editor, text) |
|
437 |
|
438 # step 3: set the tab data again |
|
439 self.setTabIcon(targetIndex, icon) |
|
440 self.setTabToolTip(targetIndex, toolTip) |
|
441 self.setTabWhatsThis(targetIndex, whatsThis) |
|
442 |
|
443 # step 4: set current widget |
|
444 self.setCurrentIndex(targetIndex) |
|
445 |
|
446 def copyTabOther(self, sourceId, sourceIndex, targetIndex): |
|
447 """ |
|
448 Public method to copy an editor from another TabWidget. |
|
449 |
|
450 @param sourceId id of the TabWidget to get the editor from (long) |
|
451 @param sourceIndex index of the tab in the old tab widget (integer) |
|
452 @param targetIndex index position to place it to (integer) |
|
453 """ |
|
454 tw = self.vm.getTabWidgetById(sourceId) |
|
455 if tw is not None: |
|
456 editor = tw.widget(sourceIndex) |
|
457 newEditor = self.vm.cloneEditor(editor, editor.getFileType(), |
|
458 editor.getFileName()) |
|
459 self.vm.insertView(newEditor, self, targetIndex, |
|
460 editor.getFileName(), editor.getNoName()) |
|
461 |
|
462 def copyTab(self, sourceIndex, targetIndex): |
|
463 """ |
|
464 Public method to copy an editor. |
|
465 |
|
466 @param sourceIndex index of the tab (integer) |
|
467 @param targetIndex index position to place it to (integer) |
|
468 """ |
|
469 editor = self.widget(sourceIndex) |
|
470 newEditor = self.vm.cloneEditor(editor, editor.getFileType(), |
|
471 editor.getFileName()) |
|
472 self.vm.insertView(newEditor, self, targetIndex, |
|
473 editor.getFileName(), editor.getNoName()) |
|
474 |
|
475 def currentWidget(self): |
|
476 """ |
|
477 Overridden method to return a reference to the current page. |
|
478 |
|
479 @return reference to the current page (QWidget) |
|
480 """ |
|
481 if not self.editors: |
|
482 return None |
|
483 else: |
|
484 return E4TabWidget.currentWidget(self) |
|
485 |
|
486 def hasEditor(self, editor): |
|
487 """ |
|
488 Public method to check for an editor. |
|
489 |
|
490 @param editor editor object to check for |
|
491 @return flag indicating, whether the editor to be checked belongs |
|
492 to the list of editors managed by this tab widget. |
|
493 """ |
|
494 return editor in self.editors |
|
495 |
|
496 def hasEditors(self): |
|
497 """ |
|
498 Public method to test, if any editor is managed. |
|
499 |
|
500 @return flag indicating editors are managed |
|
501 """ |
|
502 return len(self.editors) > 0 |
|
503 |
|
504 def __contextMenuClose(self): |
|
505 """ |
|
506 Private method to close the selected tab. |
|
507 """ |
|
508 if self.contextMenuEditor: |
|
509 self.vm.closeEditorWindow(self.contextMenuEditor) |
|
510 |
|
511 def __contextMenuCloseOthers(self): |
|
512 """ |
|
513 Private method to close the other tabs. |
|
514 """ |
|
515 index = self.contextMenuIndex |
|
516 for i in range(self.count() - 1, index, -1) + range(index - 1, -1, -1): |
|
517 editor = self.widget(i) |
|
518 self.vm.closeEditorWindow(editor) |
|
519 |
|
520 def __contextMenuCloseAll(self): |
|
521 """ |
|
522 Private method to close all tabs. |
|
523 """ |
|
524 savedEditors = self.editors[:] |
|
525 for editor in savedEditors: |
|
526 self.vm.closeEditorWindow(editor) |
|
527 |
|
528 def __contextMenuSave(self): |
|
529 """ |
|
530 Private method to save the selected tab. |
|
531 """ |
|
532 if self.contextMenuEditor: |
|
533 self.vm.saveEditorEd(self.contextMenuEditor) |
|
534 |
|
535 def __contextMenuSaveAs(self): |
|
536 """ |
|
537 Private method to save the selected tab to a new file. |
|
538 """ |
|
539 if self.contextMenuEditor: |
|
540 self.vm.saveAsEditorEd(self.contextMenuEditor) |
|
541 |
|
542 def __contextMenuSaveAll(self): |
|
543 """ |
|
544 Private method to save all tabs. |
|
545 """ |
|
546 self.vm.saveEditorsList(self.editors) |
|
547 |
|
548 def __contextMenuSaveToProject(self): |
|
549 """ |
|
550 Private method to save the selected tab to the current project. |
|
551 """ |
|
552 if self.contextMenuEditor: |
|
553 self.vm.saveEditorToProjectEd(self.contextMenuEditor) |
|
554 |
|
555 def __contextMenuPrintFile(self): |
|
556 """ |
|
557 Private method to print the selected tab. |
|
558 """ |
|
559 if self.contextMenuEditor: |
|
560 self.vm.printEditor(self.contextMenuEditor) |
|
561 |
|
562 def __contextMenuMoveLeft(self): |
|
563 """ |
|
564 Private method to move a tab one position to the left. |
|
565 """ |
|
566 self.moveTab(self.contextMenuIndex, self.contextMenuIndex - 1) |
|
567 |
|
568 def __contextMenuMoveRight(self): |
|
569 """ |
|
570 Private method to move a tab one position to the right. |
|
571 """ |
|
572 self.moveTab(self.contextMenuIndex, self.contextMenuIndex + 1) |
|
573 |
|
574 def __contextMenuMoveFirst(self): |
|
575 """ |
|
576 Private method to move a tab to the first position. |
|
577 """ |
|
578 self.moveTab(self.contextMenuIndex, 0) |
|
579 |
|
580 def __contextMenuMoveLast(self): |
|
581 """ |
|
582 Private method to move a tab to the last position. |
|
583 """ |
|
584 self.moveTab(self.contextMenuIndex, self.count() - 1) |
|
585 |
|
586 def __closeButtonClicked(self): |
|
587 """ |
|
588 Private method to handle the press of the close button. |
|
589 """ |
|
590 self.vm.closeEditorWindow(self.currentWidget()) |
|
591 |
|
592 def __closeRequested(self, index): |
|
593 """ |
|
594 Private method to handle the press of the individual tab close button. |
|
595 |
|
596 @param index index of the tab (integer) |
|
597 """ |
|
598 if index >= 0: |
|
599 self.vm.closeEditorWindow(self.widget(index)) |
|
600 |
|
601 def mouseDoubleClickEvent(self, event): |
|
602 """ |
|
603 Protected method handling double click events. |
|
604 |
|
605 @param event reference to the event object (QMouseEvent) |
|
606 """ |
|
607 self.vm.newEditor() |
|
608 |
|
609 class Tabview(QSplitter, ViewManager): |
|
610 """ |
|
611 Class implementing a tabbed viewmanager class embedded in a splitter. |
|
612 |
|
613 @signal changeCaption(string) emitted if a change of the caption is necessary |
|
614 @signal editorChanged(string) emitted when the current editor has changed |
|
615 """ |
|
616 def __init__(self, parent): |
|
617 """ |
|
618 Constructor |
|
619 |
|
620 @param parent parent widget (QWidget) |
|
621 @param ui reference to the main user interface |
|
622 @param dbs reference to the debug server object |
|
623 """ |
|
624 self.tabWidgets = [] |
|
625 |
|
626 QSplitter.__init__(self, parent) |
|
627 ViewManager.__init__(self) |
|
628 tw = TabWidget(self) |
|
629 self.addWidget(tw) |
|
630 self.tabWidgets.append(tw) |
|
631 self.currentTabWidget = tw |
|
632 self.currentTabWidget.showIndicator(True) |
|
633 self.connect(tw, SIGNAL('currentChanged(int)'), |
|
634 self.__currentChanged) |
|
635 tw.installEventFilter(self) |
|
636 tw.tabBar().installEventFilter(self) |
|
637 self.setOrientation(Qt.Vertical) |
|
638 self.__inRemoveView = False |
|
639 |
|
640 self.maxFileNameChars = Preferences.getUI("TabViewManagerFilenameLength") |
|
641 self.filenameOnly = Preferences.getUI("TabViewManagerFilenameOnly") |
|
642 |
|
643 def canCascade(self): |
|
644 """ |
|
645 Public method to signal if cascading of managed windows is available. |
|
646 |
|
647 @return flag indicating cascading of windows is available |
|
648 """ |
|
649 return False |
|
650 |
|
651 def canTile(self): |
|
652 """ |
|
653 Public method to signal if tiling of managed windows is available. |
|
654 |
|
655 @return flag indicating tiling of windows is available |
|
656 """ |
|
657 return False |
|
658 |
|
659 def canSplit(self): |
|
660 """ |
|
661 public method to signal if splitting of the view is available. |
|
662 |
|
663 @return flag indicating splitting of the view is available. |
|
664 """ |
|
665 return True |
|
666 |
|
667 def tile(self): |
|
668 """ |
|
669 Public method to tile the managed windows. |
|
670 """ |
|
671 pass |
|
672 |
|
673 def cascade(self): |
|
674 """ |
|
675 Public method to cascade the managed windows. |
|
676 """ |
|
677 pass |
|
678 |
|
679 def _removeAllViews(self): |
|
680 """ |
|
681 Protected method to remove all views (i.e. windows) |
|
682 """ |
|
683 for win in self.editors: |
|
684 self._removeView(win) |
|
685 |
|
686 def _removeView(self, win): |
|
687 """ |
|
688 Protected method to remove a view (i.e. window) |
|
689 |
|
690 @param win editor window to be removed |
|
691 """ |
|
692 self.__inRemoveView = True |
|
693 for tw in self.tabWidgets: |
|
694 if tw.hasEditor(win): |
|
695 tw.removeWidget(win) |
|
696 break |
|
697 win.closeIt() |
|
698 self.__inRemoveView = False |
|
699 |
|
700 # if this was the last editor in this view, switch to the next, that |
|
701 # still has open editors |
|
702 for i in range(self.tabWidgets.index(tw), -1, -1) + \ |
|
703 range(self.tabWidgets.index(tw) + 1, len(self.tabWidgets)): |
|
704 if self.tabWidgets[i].hasEditors(): |
|
705 self.currentTabWidget.showIndicator(False) |
|
706 self.currentTabWidget = self.tabWidgets[i] |
|
707 self.currentTabWidget.showIndicator(True) |
|
708 self.activeWindow().setFocus() |
|
709 break |
|
710 |
|
711 aw = self.activeWindow() |
|
712 fn = aw and aw.getFileName() or None |
|
713 if fn: |
|
714 self.emit(SIGNAL('changeCaption'), fn) |
|
715 self.emit(SIGNAL('editorChanged'), fn) |
|
716 else: |
|
717 self.emit(SIGNAL('changeCaption'), "") |
|
718 |
|
719 def _addView(self, win, fn = None, noName = ""): |
|
720 """ |
|
721 Protected method to add a view (i.e. window) |
|
722 |
|
723 @param win editor window to be added |
|
724 @param fn filename of this editor (string) |
|
725 @param noName name to be used for an unnamed editor (string) |
|
726 """ |
|
727 if fn is None: |
|
728 if not noName: |
|
729 self.untitledCount += 1 |
|
730 noName = self.trUtf8("Untitled {0}").format(self.untitledCount) |
|
731 self.currentTabWidget.addTab(win, noName) |
|
732 win.setNoName(noName) |
|
733 else: |
|
734 if self.filenameOnly: |
|
735 txt = os.path.basename(fn) |
|
736 else: |
|
737 txt = fn |
|
738 ppath = e4App().getObject("Project").getProjectPath() |
|
739 if ppath: |
|
740 txt = txt.replace(ppath + os.sep, "") |
|
741 if len(txt) > self.maxFileNameChars: |
|
742 txt = "...%s" % txt[-self.maxFileNameChars:] |
|
743 if not QFileInfo(fn).isWritable(): |
|
744 txt = self.trUtf8("{0} (ro)").format(txt) |
|
745 self.currentTabWidget.addTab(win, txt) |
|
746 index = self.currentTabWidget.indexOf(win) |
|
747 self.currentTabWidget.setTabToolTip(index, fn) |
|
748 self.currentTabWidget.setCurrentWidget(win) |
|
749 win.show() |
|
750 win.setFocus() |
|
751 if fn: |
|
752 self.emit(SIGNAL('changeCaption'), fn) |
|
753 self.emit(SIGNAL('editorChanged'), fn) |
|
754 else: |
|
755 self.emit(SIGNAL('changeCaption'), "") |
|
756 |
|
757 def insertView(self, win, tabWidget, index, fn = None, noName = ""): |
|
758 """ |
|
759 Protected method to add a view (i.e. window) |
|
760 |
|
761 @param win editor window to be added |
|
762 @param tabWidget reference to the tab widget to insert the editor into (TabWidget) |
|
763 @param index index position to insert at (integer) |
|
764 @param fn filename of this editor (string) |
|
765 @param noName name to be used for an unnamed editor (string) |
|
766 """ |
|
767 if fn is None: |
|
768 if not noName: |
|
769 self.untitledCount += 1 |
|
770 noName = self.trUtf8("Untitled {0}").format(self.untitledCount) |
|
771 tabWidget.insertWidget(index, win, noName) |
|
772 win.setNoName(noName) |
|
773 else: |
|
774 if self.filenameOnly: |
|
775 txt = os.path.basename(fn) |
|
776 else: |
|
777 txt = fn |
|
778 ppath = e4App().getObject("Project").getProjectPath() |
|
779 if ppath: |
|
780 txt = txt.replace(ppath + os.sep, "") |
|
781 if len(txt) > self.maxFileNameChars: |
|
782 txt = "...%s" % txt[-self.maxFileNameChars:] |
|
783 if not QFileInfo(fn).isWritable(): |
|
784 txt = self.trUtf8("{0} (ro)").format(txt) |
|
785 nindex = tabWidget.insertWidget(index, win, txt) |
|
786 tabWidget.setTabToolTip(nindex, fn) |
|
787 tabWidget.setCurrentWidget(win) |
|
788 win.show() |
|
789 win.setFocus() |
|
790 if fn: |
|
791 self.emit(SIGNAL('changeCaption'), fn) |
|
792 self.emit(SIGNAL('editorChanged'), fn) |
|
793 else: |
|
794 self.emit(SIGNAL('changeCaption'), "") |
|
795 |
|
796 self._modificationStatusChanged(win.isModified(), win) |
|
797 self._checkActions(win) |
|
798 |
|
799 def _showView(self, win, fn = None): |
|
800 """ |
|
801 Protected method to show a view (i.e. window) |
|
802 |
|
803 @param win editor window to be shown |
|
804 @param fn filename of this editor (string) |
|
805 """ |
|
806 win.show() |
|
807 for tw in self.tabWidgets: |
|
808 if tw.hasEditor(win): |
|
809 tw.setCurrentWidget(win) |
|
810 self.currentTabWidget.showIndicator(False) |
|
811 self.currentTabWidget = tw |
|
812 self.currentTabWidget.showIndicator(True) |
|
813 break |
|
814 win.setFocus() |
|
815 |
|
816 def activeWindow(self): |
|
817 """ |
|
818 Public method to return the active (i.e. current) window. |
|
819 |
|
820 @return reference to the active editor |
|
821 """ |
|
822 return self.currentTabWidget.currentWidget() |
|
823 |
|
824 def showWindowMenu(self, windowMenu): |
|
825 """ |
|
826 Public method to set up the viewmanager part of the Window menu. |
|
827 |
|
828 @param windowMenu reference to the window menu |
|
829 """ |
|
830 pass |
|
831 |
|
832 def _initWindowActions(self): |
|
833 """ |
|
834 Protected method to define the user interface actions for window handling. |
|
835 """ |
|
836 pass |
|
837 |
|
838 def setEditorName(self, editor, newName): |
|
839 """ |
|
840 Public method to change the displayed name of the editor. |
|
841 |
|
842 @param editor editor window to be changed |
|
843 @param newName new name to be shown (string) |
|
844 """ |
|
845 if self.filenameOnly: |
|
846 tabName = os.path.basename(newName) |
|
847 else: |
|
848 tabName = newName |
|
849 ppath = e4App().getObject("Project").getProjectPath() |
|
850 if ppath: |
|
851 tabName = tabName.replace(ppath + os.sep, "") |
|
852 if len(tabName) > self.maxFileNameChars: |
|
853 tabName = "...%s" % tabName[-self.maxFileNameChars:] |
|
854 index = self.currentTabWidget.indexOf(editor) |
|
855 self.currentTabWidget.setTabText(index, tabName) |
|
856 self.currentTabWidget.setTabToolTip(index, newName) |
|
857 self.emit(SIGNAL('changeCaption'), newName) |
|
858 |
|
859 def _modificationStatusChanged(self, m, editor): |
|
860 """ |
|
861 Protected slot to handle the modificationStatusChanged signal. |
|
862 |
|
863 @param m flag indicating the modification status (boolean) |
|
864 @param editor editor window changed |
|
865 """ |
|
866 for tw in self.tabWidgets: |
|
867 if tw.hasEditor(editor): |
|
868 break |
|
869 index = tw.indexOf(editor) |
|
870 if m: |
|
871 tw.setTabIcon(index, UI.PixmapCache.getIcon("fileModified.png")) |
|
872 elif editor.hasSyntaxErrors(): |
|
873 tw.setTabIcon(index, UI.PixmapCache.getIcon("syntaxError.png")) |
|
874 else: |
|
875 tw.setTabIcon(index, UI.PixmapCache.getIcon("empty.png")) |
|
876 self._checkActions(editor) |
|
877 |
|
878 def _syntaxErrorToggled(self, editor): |
|
879 """ |
|
880 Protected slot to handle the syntaxerrorToggled signal. |
|
881 |
|
882 @param editor editor that sent the signal |
|
883 """ |
|
884 for tw in self.tabWidgets: |
|
885 if tw.hasEditor(editor): |
|
886 break |
|
887 index = tw.indexOf(editor) |
|
888 if editor.hasSyntaxErrors(): |
|
889 tw.setTabIcon(index, UI.PixmapCache.getIcon("syntaxError.png")) |
|
890 else: |
|
891 tw.setTabIcon(index, UI.PixmapCache.getIcon("empty.png")) |
|
892 |
|
893 ViewManager._syntaxErrorToggled(self, editor) |
|
894 |
|
895 def addSplit(self): |
|
896 """ |
|
897 Public method used to split the current view. |
|
898 """ |
|
899 tw = TabWidget(self) |
|
900 tw.show() |
|
901 self.addWidget(tw) |
|
902 self.tabWidgets.append(tw) |
|
903 self.currentTabWidget.showIndicator(False) |
|
904 self.currentTabWidget = self.tabWidgets[-1] |
|
905 self.currentTabWidget.showIndicator(True) |
|
906 self.connect(tw, SIGNAL('currentChanged(int)'), |
|
907 self.__currentChanged) |
|
908 tw.installEventFilter(self) |
|
909 tw.tabBar().installEventFilter(self) |
|
910 if self.orientation() == Qt.Horizontal: |
|
911 size = self.width() |
|
912 else: |
|
913 size = self.height() |
|
914 self.setSizes([int(size/len(self.tabWidgets))] * len(self.tabWidgets)) |
|
915 self.splitRemoveAct.setEnabled(True) |
|
916 self.nextSplitAct.setEnabled(True) |
|
917 self.prevSplitAct.setEnabled(True) |
|
918 |
|
919 def removeSplit(self): |
|
920 """ |
|
921 Public method used to remove the current split view. |
|
922 |
|
923 @return flag indicating successfull removal |
|
924 """ |
|
925 if len(self.tabWidgets) > 1: |
|
926 tw = self.currentTabWidget |
|
927 res = True |
|
928 savedEditors = tw.editors[:] |
|
929 for editor in savedEditors: |
|
930 res &= self.closeEditor(editor) |
|
931 if res: |
|
932 try: |
|
933 i = self.tabWidgets.index(tw) |
|
934 except ValueError: |
|
935 return True |
|
936 if i == len(self.tabWidgets) - 1: |
|
937 i -= 1 |
|
938 self.tabWidgets.remove(tw) |
|
939 tw.close() |
|
940 self.currentTabWidget = self.tabWidgets[i] |
|
941 self.currentTabWidget.showIndicator(True) |
|
942 if len(self.tabWidgets) == 1: |
|
943 self.splitRemoveAct.setEnabled(False) |
|
944 self.nextSplitAct.setEnabled(False) |
|
945 self.prevSplitAct.setEnabled(False) |
|
946 return True |
|
947 |
|
948 return False |
|
949 |
|
950 def setSplitOrientation(self, orientation): |
|
951 """ |
|
952 Public method used to set the orientation of the split view. |
|
953 |
|
954 @param orientation orientation of the split |
|
955 (Qt.Horizontal or Qt.Vertical) |
|
956 """ |
|
957 self.setOrientation(orientation) |
|
958 |
|
959 def nextSplit(self): |
|
960 """ |
|
961 Public slot used to move to the next split. |
|
962 """ |
|
963 aw = self.activeWindow() |
|
964 _hasFocus = aw and aw.hasFocus() |
|
965 ind = self.tabWidgets.index(self.currentTabWidget) + 1 |
|
966 if ind == len(self.tabWidgets): |
|
967 ind = 0 |
|
968 |
|
969 self.currentTabWidget.showIndicator(False) |
|
970 self.currentTabWidget = self.tabWidgets[ind] |
|
971 self.currentTabWidget.showIndicator(True) |
|
972 if _hasFocus: |
|
973 aw = self.activeWindow() |
|
974 if aw: |
|
975 aw.setFocus() |
|
976 |
|
977 def prevSplit(self): |
|
978 """ |
|
979 Public slot used to move to the previous split. |
|
980 """ |
|
981 aw = self.activeWindow() |
|
982 _hasFocus = aw and aw.hasFocus() |
|
983 ind = self.tabWidgets.index(self.currentTabWidget) - 1 |
|
984 if ind == -1: |
|
985 ind = len(self.tabWidgets) - 1 |
|
986 |
|
987 self.currentTabWidget.showIndicator(False) |
|
988 self.currentTabWidget = self.tabWidgets[ind] |
|
989 self.currentTabWidget.showIndicator(True) |
|
990 if _hasFocus: |
|
991 aw = self.activeWindow() |
|
992 if aw: |
|
993 aw.setFocus() |
|
994 |
|
995 def __currentChanged(self, index): |
|
996 """ |
|
997 Private slot to handle the currentChanged signal. |
|
998 |
|
999 @param index index of the current tab (integer) |
|
1000 """ |
|
1001 if index == -1 or not self.editors: |
|
1002 return |
|
1003 |
|
1004 editor = self.activeWindow() |
|
1005 if editor is None: |
|
1006 return |
|
1007 |
|
1008 self._checkActions(editor) |
|
1009 editor.setFocus() |
|
1010 fn = editor.getFileName() |
|
1011 if fn: |
|
1012 self.emit(SIGNAL('changeCaption'), fn) |
|
1013 if not self.__inRemoveView: |
|
1014 self.emit(SIGNAL('editorChanged'), fn) |
|
1015 else: |
|
1016 self.emit(SIGNAL('changeCaption'), "") |
|
1017 |
|
1018 def eventFilter(self, watched, event): |
|
1019 """ |
|
1020 Public method called to filter the event queue. |
|
1021 |
|
1022 @param watched the QObject being watched (QObject) |
|
1023 @param event the event that occurred (QEvent) |
|
1024 @return always False |
|
1025 """ |
|
1026 if event.type() == QEvent.MouseButtonPress and \ |
|
1027 not event.button() == Qt.RightButton: |
|
1028 self.currentTabWidget.showIndicator(False) |
|
1029 if isinstance(watched, E4TabWidget): |
|
1030 switched = watched is not self.currentTabWidget |
|
1031 self.currentTabWidget = watched |
|
1032 elif isinstance(watched, QTabBar): |
|
1033 switched = watched.parent() is not self.currentTabWidget |
|
1034 self.currentTabWidget = watched.parent() |
|
1035 if switched: |
|
1036 index = self.currentTabWidget.selectTab(event.pos()) |
|
1037 switched = self.currentTabWidget.widget(index) is self.activeWindow() |
|
1038 elif isinstance(watched, QScintilla.Editor.Editor): |
|
1039 for tw in self.tabWidgets: |
|
1040 if tw.hasEditor(watched): |
|
1041 switched = tw is not self.currentTabWidget |
|
1042 self.currentTabWidget = tw |
|
1043 break |
|
1044 self.currentTabWidget.showIndicator(True) |
|
1045 |
|
1046 aw = self.activeWindow() |
|
1047 if aw is not None: |
|
1048 self._checkActions(aw) |
|
1049 aw.setFocus() |
|
1050 fn = aw.getFileName() |
|
1051 if fn: |
|
1052 self.emit(SIGNAL('changeCaption'), fn) |
|
1053 if switched: |
|
1054 self.emit(SIGNAL('editorChanged'), fn) |
|
1055 else: |
|
1056 self.emit(SIGNAL('changeCaption'), "") |
|
1057 |
|
1058 return False |
|
1059 |
|
1060 def preferencesChanged(self): |
|
1061 """ |
|
1062 Public slot to handle the preferencesChanged signal. |
|
1063 """ |
|
1064 ViewManager.preferencesChanged(self) |
|
1065 |
|
1066 self.maxFileNameChars = Preferences.getUI("TabViewManagerFilenameLength") |
|
1067 self.filenameOnly = Preferences.getUI("TabViewManagerFilenameOnly") |
|
1068 |
|
1069 for tabWidget in self.tabWidgets: |
|
1070 for index in range(tabWidget.count()): |
|
1071 editor = tabWidget.widget(index) |
|
1072 if isinstance(editor, QScintilla.Editor.Editor): |
|
1073 fn = editor.getFileName() |
|
1074 if fn: |
|
1075 if self.filenameOnly: |
|
1076 txt = os.path.basename(fn) |
|
1077 else: |
|
1078 txt = fn |
|
1079 ppath = e4App().getObject("Project").getProjectPath() |
|
1080 if ppath: |
|
1081 txt = txt.replace(ppath + os.sep, "") |
|
1082 if len(txt) > self.maxFileNameChars: |
|
1083 txt = "...%s" % txt[-self.maxFileNameChars:] |
|
1084 if not QFileInfo(fn).isWritable(): |
|
1085 txt = self.trUtf8("{0} (ro)").format(txt) |
|
1086 tabWidget.setTabText(index, txt) |
|
1087 |
|
1088 def getTabWidgetById(self, id_): |
|
1089 """ |
|
1090 Public method to get a reference to a tab widget knowing it's ID. |
|
1091 |
|
1092 @param id_ id of the tab widget (long) |
|
1093 @return reference to the tab widget (TabWidget) |
|
1094 """ |
|
1095 for tw in self.tabWidgets: |
|
1096 if id(tw) == id_: |
|
1097 return tw |
|
1098 return None |