mrv.maya.ui.browse.finder
Covered: 78 lines
Missed: 117 lines
Skipped 118 lines
Percent: 40 %
  2
__docformat__ = "restructuredtext"
  3
import mrv.maya.ui as ui
  4
from mrv.maya.util import (logException,)
  6
from control import FinderElement
  8
from util import concat_url
 10
__all__ = ('Finder', )
 12
class Finder(ui.EventSenderUI):
 13
	"""The Finder control implements a finder-like browser, which displays URLs.
 14
	URLs consist of items separated by the "/" character. Whenever a item is selected, 
 15
	an iProvider compatible instance will be asked for the subitems of the corresponding URL. 
 16
	Using these, a new field will be set up for presentation.
 17
	A filter can be installed to prevent items from being shown.
 19
	An added benefit is the ability to automatically match previously selected path
 20
	items on a certain level of the URL with the available ones, allowing to quickly
 21
	parse through URLs with a similar structure.
 23
	A limitation of the current implementation is, that you can only keep one
 24
	item selected at once in each url item area."""
 27
	t_element = FinderElement
 33
	selection_changed = ui.Signal()
 36
	url_changed = ui.Signal() 
 40
	def __init__(self, provider=None, filter=None):
 41
		self._provider = None
 42
		self._filter = None
 45
		self._form = ui.FormLayout()
 46
		self._form.setParentActive()
 48
		self.setProvider(provider)
 49
		self.setFilter(filter)
 53
	def layout(self):
 54
		""":return: the finder's main layout which contains all controls"""
 55
		return self._form
 57
	def provider(self):
 58
		""":return: current url provider"""
 59
		return self._provider
 61
	def selectedUrl(self, absolute=False):
 62
		""":return: string representing the currently selected, / separated URL, or
 63
			None if there is no url selected
 64
		:param absolute: if True, an absolute URL will be provided using the provider's
 65
			root"""
 66
		items = list()
 67
		for elm in self._form.listChildren():
 68
			if not elm.p_manage:
 69
				break
 70
			sel_item = elm.selectedUnformattedItem()
 71
			if sel_item is not None:
 72
				items.append(sel_item)
 73
			else:
 74
				break
 77
		url = "/".join(items) or None
 78
		if absolute and url is not None:
 79
			url = concat_url(self.provider().root(), url)
 81
		return url
 83
	def numUrlElements(self):
 84
		""":return: number of url elements that are currently shown. A url of 1/2 would
 85
		have two url elements"""
 86
		return len(tuple(c for c in self._form.listChildren() if c.p_manage))
 88
	def selectedUrlItemByIndex(self, index):
 89
		""":return: The selected url item at the given element index or None if nothing 
 90
			is selected
 91
		:param index: 0 to numUrlElements()-1
 92
		:raies IndexError:"""
 93
		return self._form.listChildren()[index].selectedUnformattedItem()
 95
	def urlItemsByIndex(self, index):
 96
		""":return: list of item ids which are currently being shown
 97
		:param index: 0 based element index to numUrlElements()-1
 98
		:raise IndexError:"""
 99
		return list(self._form.listChildren()[index].base_items) 
106
	def setFilter(self, filter=None):
107
		"""Set or unset a filter. All items will be sent through the filter, and will
108
		be shown only if they pass.
109
		:param filter: Functor called f(url,t) and returns True for each item which may
110
			be shown in the Finder. The url is the full relative url leading to, but 
111
			excluding the item t, whose visibility is being decided upon"""
112
		self._filter = filter
114
	def setProvider(self, provider=None):
115
		"""Set the provider to use
116
		:param provider: ``iFinderProvider`` compatible instance, or None
117
			If no provider is set, the instance will be blank"""
118
		if self._provider is provider:
119
			return
121
		self._provider = provider
123
		if provider is not None:
124
			self._set_element_visible(0)
127
		self.selection_changed.send()
128
		self.url_changed.send(self.selectedUrl())
130
	def _set_item_by_index(self, elm, index, item):
131
		self._set_element_visible(index)
132
		elm.selectUnformattedItem(item)
133
		self.provider().storeUrlItem(index, item)
134
		self._set_element_visible(index+1)
136
	def setItemByIndex(self, item, index):
137
		"""Set the given string item, which sits at the given index of a url
138
		:raise ValueError: if item does not exist at given index
139
		:raise IndexError: if index is not currently shown"""
140
		assert self.provider() is not None, "Provider is not set"
141
		elm = self._form.listChildren()[index]
142
		if elm.selectedUnformattedItem() == item:
143
			return
145
		self._set_item_by_index(elm, index, item)
147
		self.selection_changed.send()
148
		self.url_changed.send(self.selectedUrl())
150
	def setUrl(self, url, require_all_items=True, allow_memory=False):
151
		"""Set the given url to be selected
152
		:param url: / separated relative url. The individual items must be available
153
			in the provider.
154
		:parm require_all_items: if False, the control will display as many items as possible.
155
			Otherwise it must display all given items, or raise ValueError
156
		:param allow_memory: if true, provider memory may be used to show the longest chosen url, 
157
			being possibly more than you specify. Currently not implemented"""
158
		assert self.provider() is not None, "Provider is not set"
159
		cur_url = self.selectedUrl()
160
		if cur_url == url:
161
			return
164
		for eid, item in enumerate(url.split("/")):
165
			elm = self._form.listChildren()[eid]
166
			if elm.selectedUnformattedItem() == item:
167
				continue
169
			try:
170
				self._set_item_by_index(elm, eid, item)
171
			except ValueError:
172
				if not require_all_items:
173
					break
175
				self.setUrl(cur_url)
176
				raise
180
		self.selection_changed.send()
181
		self.url_changed.send(self.selectedUrl())
189
	@logException
190
	def _element_selection_changed(self, element, *args):
191
		"""Called whenever any element changes its value, which forces the following 
192
		elements to refresh"""
193
		index = self._index_by_item_element(element)
195
		self.provider().storeUrlItem(index, element.selectedUnformattedItem())
196
		self._set_element_visible(index+1)
198
		self.selection_changed.send()
199
		self.url_changed.send(self.selectedUrl())
205
	def _index_by_item_element(self, element):
206
		""":return: index matching the given item element, which must be one of our children"""
207
		assert '|' in element
208
		for cid, c in enumerate(self._form.listChildren()):
209
			if c == element:
210
				return cid
212
		raise ValueError("Didn't find element: %s" % element)
214
	def _set_element_items(self, start_elm_id, elements ):
215
		"""Fill the items from the start_elm_id throughout to all elements, until
216
		one url does not yield any items, or the item cannot be selected 
217
		:param elements: a full list of all available child elements."""
220
		root_url = "/".join(c.selectedUnformattedItem() for c in elements[:start_elm_id])
222
		manage = True
223
		for elm_id in range(start_elm_id, len(elements)):
226
			elm = elements[elm_id]
227
			elm.p_manage=manage
229
			if not manage:
230
				continue
233
			items = self.provider().urlItems(root_url)
234
			elm.base_items = items
235
			if not items:
237
				if len(elements) > 1:
238
					elm.p_manage=False
239
				manage=False
240
				continue
243
			if elm.p_numberOfItems:
244
				elm.p_removeAll = True
247
			for item in items:
248
				elm.p_append = self.provider().formatItem(root_url, elm_id, item)
252
			sel_item = self.provider().storedUrlItemByIndex(elm_id)
253
			if sel_item is None:
255
				manage=False
256
				continue
259
			try:
260
				elm.selectUnformattedItem(sel_item)
261
			except (RuntimeError, ValueError):
262
				manage=False
263
				continue
267
			if root_url:
268
				root_url += "/"
270
			root_url += sel_item
274
	def _set_element_visible(self, index):
275
		"""Possibly create and fill the given element index, all following elements
276
		are set invivisble"""
277
		children = self._form.listChildren()
280
		elms_to_create = max(0, (index+1) - len(children))
281
		if elms_to_create:
282
			self._form.setActive()
283
			for i in range(elms_to_create):
285
				child = self._form.add(self.t_element(allowMultiSelection=False, font="smallFixedWidthFont"))
286
				children.append(child)
288
				child.e_selectCommand = self._element_selection_changed
290
				t, b, l, r = self._form.kSides
291
				m = 2
294
				self._form.setup(	attachForm=((child, t, m), (child, b, m)),
295
									attachNone=(child, r)	)
298
				if len(children) == 1:
300
					self._form.setup(attachForm=(child, l, m))
301
				else:
303
					self._form.setup(attachControl=(child, l, m, children[-2]))
309
		self._set_element_items(index, children)