class SearchDialog(Dialog):#{{{ title = "SearchDialog" subtitle = "GenericWindow" _items = [] def __init__(self, **kwargs):#{{{ self.search_box = SearchBox() self.items_count = Text("", align='right') self.search_items = SimpleListWalker(self._null_list_item()) connect_signal(self.search_items, "modified", self._update_items_count) self.search_list = ListBox(self.search_items) connect_signal(self.search_box, "edit-done", self.on_edit_done) connect_signal(self.search_box, "edit-cancel", lambda w: self.quit()) connect_signal(self.search_box, "change", lambda sb, term: self.set_search_term(term)) self._constr = self.get_item_constructor() self.multiple_selection = kwargs.pop('multiple_selection', False) self._selected_items = OrderedSet([]) opts = { 'height': kwargs.get('height', None), 'width': kwargs.get('width', ('relative', 90)), 'title': kwargs.get('title', self.title), 'subtitle': kwargs.get('subtitle', self.subtitle), 'compact_header': kwargs.get('compact_header', True), } kwargs.update(opts) self.pile = Pile([ ('fixed', 15, AttrMap(self.search_list, 'dialog.search.item')), Columns([ AttrMap(self.search_box, 'dialog.search.input'), ('fixed', 1, AttrMap(Divider(), 'dialog.search.input')), ('fixed', 4, AttrMap(self.items_count, 'dialog.search.input')), ]), ], focus_item=0) self.__super.__init__(self.pile, **kwargs) self.attr_style = "dialog.search" self.title_attr_style = "dialog.search.title" self.subtitle_attr_style = "dialog.search.subtitle" #}}} def keypress(self, key):#{{{ if key == 'insert' and self.multiple_selection: if self.pile.get_focus().original_widget is self.search_list: wid, pos = self.search_list.get_focus() else: pos = 0 current_item = self.search_items[pos] article = self.get_data_for(pos) current_item.original_widget.selected = not current_item.original_widget.selected if current_item.original_widget.selected: current_item.attr_map = {None: 'dialog.search.item.selected'} current_item.focus_map = {None: 'dialog.search.item.focus.selected'} self._selected_items.add(article) else: current_item.attr_map = {None: 'dialog.search.item'} current_item.focus_map = {None: 'dialog.search.item.focus'} self._selected_items.discard(article) self.search_list.set_focus(pos+1) self._update_items_count() #}}} def on_edit_done(self, widget, text):#{{{ result = [] if self.pile.get_focus().original_widget is self.search_list: wid, pos = self.search_list.get_focus() else: pos = 0 if self.multiple_selection: result = list(self._selected_items) if len(result) < 1: if self.get_data_for(pos): result = [self.get_data_for(pos)] self.dialog_result = result self.quit() #}}} def set_search_term(self, term):#{{{ self._clear_search_items() query = self.get_query(term) if query is not None: self._items = tuple(query[:150]) if len(self._items) > 0: l_items = map(self._constr, self._items) for i in l_items: i.set_search_box(self.search_box) self.search_items.extend([AttrMap(i, 'dialog.search.item',\ 'dialog.search.item.focus') for i in l_items]) if self.multiple_selection: for a in (self._selected_items & set(self._items)): idx = self._items.index(a) self.search_items[idx].attr_map = {None: 'dialog.search.item.selected'} self.search_items[idx].focus_map = {None: 'dialog.search.item.focus.selected'} self.search_items[idx].original_widget.selected = True return self.search_items.extend(self._null_list_item()) #}}} def _clear_search_items(self):#{{{ self.search_items[:] = [] self._update_items_count() #}}} def _null_list_item(self):#{{{ null = SearchListItem([Text("")]) null.set_search_box(self.search_box) return [null] #}}} def _update_items_count(self):#{{{ if len(self.search_items) > 149: self.items_count.set_text("+150") else: self.items_count.set_text("") selected_count = len(self._selected_items) if selected_count > 0: self._title_widget.set_text(self.title + (" (+%d)" % selected_count)) else: self._title_widget.set_text(self.title) #}}} def get_data_for(self, index):#{{{ try: return self._items[index] except IndexError as e: # index out of range return None #}}} def get_query(self, term):#{{{ raise NotImplementedError("This must be implemented by subclass") #}}} def get_item_constructor(self):#{{{ return None
class TreePile(WidgetWrap): _selectable = True def __init__(self, walker, **kwargs): if not isinstance(walker, TreeListWalker): walker = TreeListWalker(walker) self._walker = walker self._lines = [] self.loadlines() logging.debug('lines:\n\n%s' % str(self._lines)) self._pile = Pile(self._lines) self.__super.__init__(self._pile) def loadlines(self): widget, pos = self._walker.get_focus() while pos is not None: self._lines.append(widget) widget, pos = self._walker.get_next(pos) # Widget API def get_focus(self): return self._pile.get_focus() def keypress(self, size, key): key = self._pile.keypress(size, key) if key in ['left', 'right', '[', ']', '-', '+', 'C', 'E']: if key == 'left': self.focus_parent() elif key == 'right': self.focus_first_child() elif key == '[': self.focus_prev_sibling() elif key == ']': self.focus_next_sibling() if isinstance(self._walker, CollapseMixin): if key == '-': w, focuspos = self._walker.get_focus() self._walker.collapse(focuspos) elif key == '+': w, focuspos = self._walker.get_focus() self._walker.expand(focuspos) elif key == 'C': self._walker.collapse_all() elif key == 'E': self._walker.expand_all() # This is a hack around ListBox misbehaving: # it seems impossible to set the focus without calling keypress as # otherwise the change becomes visible only after the next render() return self._pile.keypress(size, None) else: return self._pile.keypress(size, key) # Tree based focus movement def focus_parent(self): w, focuspos = self._walker.get_focus() parent = self._walker.parent_position(focuspos) if parent is not None: self._pile.set_focus(parent) def focus_first_child(self): w, focuspos = self._walker.get_focus() child = self._walker.first_child_position(focuspos) if child is not None: self._outer_list.set_focus(child) def focus_next_sibling(self): w, focuspos = self._walker.get_focus() sib = self._walker.next_sibling_position(focuspos) if sib is not None: self._outer_list.set_focus(sib) def focus_prev_sibling(self): w, focuspos = self._walker.get_focus() sib = self._walker.prev_sibling_position(focuspos) if sib is not None: self._outer_list.set_focus(sib)