1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
|
'''
Task Coach - Your friendly task manager
Copyright (C) 2004-2014 Task Coach developers <developers@taskcoach.org>
Task Coach is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Task Coach is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
from taskcoachlib import operating_system
from taskcoachlib.gui import menu
from wx.lib.pubsub import pub
import wx.lib.agw.aui as aui
import wx
class ViewerContainer(object):
''' ViewerContainer is a container of viewers. It has a containerWidget
that displays the viewers. The containerWidget is assumed to be
an AUI managed frame. The ViewerContainer knows which of its viewers
is active and dispatches method calls to the active viewer or to the
first viewer that can handle the method. This allows other GUI
components, e.g. menu's, to talk to the ViewerContainer as were
it a regular viewer. '''
def __init__(self, containerWidget, settings, *args, **kwargs):
self.containerWidget = containerWidget
self._notifyActiveViewer = False
self.__bind_event_handlers()
self._settings = settings
self.viewers = []
super(ViewerContainer, self).__init__(*args, **kwargs)
def componentsCreated(self):
self._notifyActiveViewer = True
def advanceSelection(self, forward):
''' Activate the next viewer if forward is true else the previous
viewer. '''
if len(self.viewers) <= 1:
return # Not enough viewers to advance selection
active_viewer = self.activeViewer()
current_index = self.viewers.index(active_viewer) if active_viewer else 0
minimum_index, maximum_index = 0, len(self.viewers) - 1
if forward:
new_index = current_index + 1 if minimum_index <= current_index < maximum_index else minimum_index
else:
new_index = current_index - 1 if minimum_index < current_index <= maximum_index else maximum_index
self.activateViewer(self.viewers[new_index])
def isViewerContainer(self):
''' Return whether this is a viewer container or an actual viewer. '''
return True
def __bind_event_handlers(self):
''' Register for pane closing, activating and floating events. '''
self.containerWidget.Bind(aui.EVT_AUI_PANE_CLOSE, self.onPageClosed)
self.containerWidget.Bind(aui.EVT_AUI_PANE_ACTIVATED,
self.onPageChanged)
self.containerWidget.Bind(aui.EVT_AUI_PANE_FLOATED, self.onPageFloated)
def __getitem__(self, index):
return self.viewers[index]
def __len__(self):
return len(self.viewers)
def addViewer(self, viewer, floating=False):
''' Add a new pane with the specified viewer. '''
self.containerWidget.addPane(viewer, viewer.title(), floating=floating)
self.viewers.append(viewer)
if len(self.viewers) == 1:
self.activateViewer(viewer)
pub.subscribe(self.onStatusChanged, viewer.viewerStatusEventType())
def closeViewer(self, viewer):
''' Close the specified viewer. '''
if viewer == self.activeViewer():
self.advanceSelection(False)
pane = self.containerWidget.manager.GetPane(viewer)
self.containerWidget.manager.ClosePane(pane)
def __getattr__(self, attribute):
''' Forward unknown attributes to the active viewer or the first
viewer if there is no active viewer. '''
return getattr(self.activeViewer() or self.viewers[0], attribute)
def activeViewer(self):
''' Return the active (selected) viewer. '''
all_panes = self.containerWidget.manager.GetAllPanes()
for pane in all_panes:
if pane.IsToolbar():
continue
if pane.HasFlag(pane.optionActive):
if pane.IsNotebookControl():
notebook = aui.GetNotebookRoot(all_panes, pane.notebook_id)
return notebook.window.GetCurrentPage()
else:
return pane.window
return None
def activateViewer(self, viewer_to_activate):
''' Activate (select) the specified viewer. '''
self.containerWidget.manager.ActivatePane(viewer_to_activate)
paneInfo = self.containerWidget.manager.GetPane(viewer_to_activate)
if paneInfo.IsNotebookPage():
self.containerWidget.manager.ShowPane(viewer_to_activate, True)
self.sendViewerStatusEvent()
def __del__(self):
pass # Don't forward del to one of the viewers.
def onStatusChanged(self, viewer):
if self.activeViewer() == viewer:
self.sendViewerStatusEvent()
pub.sendMessage('all.viewer.status', viewer=viewer)
def onPageChanged(self, event):
self.__ensure_active_viewer_has_focus()
self.sendViewerStatusEvent()
if self._notifyActiveViewer and self.activeViewer() is not None:
self.activeViewer().activate()
event.Skip()
def sendViewerStatusEvent(self):
pub.sendMessage('viewer.status')
def __ensure_active_viewer_has_focus(self):
if not self.activeViewer():
return
window = wx.Window.FindFocus()
if operating_system.isMacOsXTiger_OrOlder() and window is None:
# If the SearchCtrl has focus on Mac OS X Tiger,
# wx.Window.FindFocus returns None. If we would continue,
# the focus would be set to the active viewer right away,
# making it impossible for the user to type in the search
# control.
return
while window:
if window == self.activeViewer():
break
window = window.GetParent()
else:
wx.CallAfter(self.activeViewer().SetFocus)
def onPageClosed(self, event):
if event.GetPane().IsToolbar():
return
window = event.GetPane().window
if hasattr(window, 'GetPage'):
# Window is a notebook, close each of its pages
for pageIndex in range(window.GetPageCount()):
self.__close_viewer(window.GetPage(pageIndex))
else:
# Window is a viewer, close it
self.__close_viewer(window)
# Make sure we have an active viewer
if not self.activeViewer():
self.activateViewer(self.viewers[0])
event.Skip()
def __close_viewer(self, viewer):
''' Close the specified viewer and unsubscribe all its event
handlers. '''
# When closing an AUI managed frame, we get two close events,
# be prepared:
if viewer in self.viewers:
self.viewers.remove(viewer)
viewer.detach()
@staticmethod
def onPageFloated(event):
''' Give floating pane accelerator keys for activating next and previous
viewer. '''
viewer = event.GetPane().window
table = wx.AcceleratorTable([(wx.ACCEL_CTRL, wx.WXK_PAGEDOWN,
menu.activateNextViewerId),
(wx.ACCEL_CTRL, wx.WXK_PAGEUP,
menu.activatePreviousViewerId)])
viewer.SetAcceleratorTable(table)
|