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
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):
45
self._form = ui.FormLayout()
46
self._form.setParentActive()
48
self.setProvider(provider)
49
self.setFilter(filter)
54
""":return: the finder's main layout which contains all controls"""
58
""":return: current url 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
67
for elm in self._form.listChildren():
70
sel_item = elm.selectedUnformattedItem()
71
if sel_item is not None:
72
items.append(sel_item)
75
# END for each element
77
url = "/".join(items) or None
78
if absolute and url is not None:
79
url = concat_url(self.provider().root(), url)
80
# END handle absolute urls
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
91
:param index: 0 to numUrlElements()-1
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
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:
121
self._provider = provider
123
if provider is not None:
124
self._set_element_visible(0)
125
# END handle initial setup
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:
144
# END early abort if nothing changes
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
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()
162
# END ignore similar urls
164
for eid, item in enumerate(url.split("/")):
165
elm = self._form.listChildren()[eid]
166
if elm.selectedUnformattedItem() == item:
168
# END skip items which already match
170
self._set_item_by_index(elm, eid, item)
172
if not require_all_items:
174
# restore previous url
177
# END handle exceptions
178
# END for each item to set
180
self.selection_changed.send()
181
self.url_changed.send(self.selectedUrl())
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)
194
# store the currently selected item
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()):
211
# END for each child to enumerate
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."""
219
# obtain the root url
220
root_url = "/".join(c.selectedUnformattedItem() for c in elements[:start_elm_id])
223
for elm_id in range(start_elm_id, len(elements)):
225
# refill the items according to our provider
226
elm = elements[elm_id]
231
# END abort if we just disable all others
233
items = self.provider().urlItems(root_url)
234
elm.base_items = items
236
# keep one item visible, even though empty, if its the only one
237
if len(elements) > 1:
241
# END skip on first empty url
243
if elm.p_numberOfItems:
244
elm.p_removeAll = True
245
# END remove prior to re-append
248
elm.p_append = self.provider().formatItem(root_url, elm_id, item)
249
# END for each item to append
251
# try to reselect the previously selected item
252
sel_item = self.provider().storedUrlItemByIndex(elm_id)
254
# make sure next item is not being shown
257
# END handle item memorization
260
elm.selectUnformattedItem(sel_item)
261
except (RuntimeError, ValueError):
264
# END handle exception
269
# END assure / is not the first character
271
# END for each url to handle
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()
279
# create as many new scrollLists as required,
280
elms_to_create = max(0, (index+1) - len(children))
282
self._form.setActive()
283
for i in range(elms_to_create):
284
# make sure we keep our array uptodate
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
293
# they are always attached top+bottom
294
self._form.setup( attachForm=((child, t, m), (child, b, m)),
295
attachNone=(child, r) )
297
# we generally keep the right side un-attached
298
if len(children) == 1:
299
# first element goes left
300
self._form.setup(attachForm=(child, l, m))
302
# all other elements attach to the right side
303
self._form.setup(attachControl=(child, l, m, children[-2]))
304
# END handle amount of children
305
# children.append(child)
306
# END for each element to add
307
# END if elms to create
309
self._set_element_items(index, children)