comparison src/browser/sat_browser/list_manager.py @ 739:4545d48dee60

browser_side: improve ListPanel and TitlePanel
author souliane <souliane@mailoo.org>
date Thu, 19 Nov 2015 16:42:39 +0100
parents fe3c2357a8c9
children b6510fd9ae15
comparison
equal deleted inserted replaced
738:caad07bdb659 739:4545d48dee60
16 16
17 # You should have received a copy of the GNU Affero General Public License 17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. 18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 19
20 from sat.core.log import getLogger 20 from sat.core.log import getLogger
21 from pyjamas.ui.DragHandler import DragHandler
22 log = getLogger(__name__) 21 log = getLogger(__name__)
22 from sat.core.i18n import _
23 23
24 from pyjamas.ui.ClickListener import ClickHandler 24 from pyjamas.ui.ClickListener import ClickHandler
25 from pyjamas.ui.FocusListener import FocusHandler 25 from pyjamas.ui.FocusListener import FocusHandler
26 from pyjamas.ui.ChangeListener import ChangeHandler 26 from pyjamas.ui.ChangeListener import ChangeHandler
27 from pyjamas.ui.DragHandler import DragHandler
27 from pyjamas.ui.KeyboardListener import KeyboardHandler, KEY_ENTER 28 from pyjamas.ui.KeyboardListener import KeyboardHandler, KEY_ENTER
28 from pyjamas.ui.DragWidget import DragWidget 29 from pyjamas.ui.DragWidget import DragWidget
29 from pyjamas.ui.ListBox import ListBox 30 from pyjamas.ui.ListBox import ListBox
30 from pyjamas.ui.Button import Button 31 from pyjamas.ui.Button import Button
31 from pyjamas.ui.FlowPanel import FlowPanel 32 from pyjamas.ui.FlowPanel import FlowPanel
44 45
45 46
46 class ListItem(HorizontalPanel): 47 class ListItem(HorizontalPanel):
47 """This class implements a list item with auto-completion and a delete button.""" 48 """This class implements a list item with auto-completion and a delete button."""
48 49
49 STYLE = {"listItem-box": "listItem-box", 50 STYLE = {"listItem": "listItem",
51 "listItem-box": "listItem-box",
50 "listItem-box-invalid": "listItem-box-invalid", 52 "listItem-box-invalid": "listItem-box-invalid",
51 "listItem-button": "listItem-button", 53 "listItem-button": "listItem-button",
52 } 54 }
53 55
54 VALID = 1 56 VALID = 1
61 @param listener (ListItemHandler): handler for the UI events 63 @param listener (ListItemHandler): handler for the UI events
62 @param taglist (quick_list_manager.QuickTagList): list manager 64 @param taglist (quick_list_manager.QuickTagList): list manager
63 @param validate (callable): method returning a bool to validate the entry 65 @param validate (callable): method returning a bool to validate the entry
64 """ 66 """
65 HorizontalPanel.__init__(self) 67 HorizontalPanel.__init__(self)
68 self.addStyleName(self.STYLE["listItem"])
66 69
67 self.box = AutoCompleteTextBox(StyleName=self.STYLE["listItem-box"]) 70 self.box = AutoCompleteTextBox(StyleName=self.STYLE["listItem-box"])
68 self.remove_btn = Button('<span>x</span>', Visible=False) 71 self.remove_btn = Button('<span>x</span>', Visible=False)
69 self.remove_btn.setStyleName(self.STYLE["listItem-button"]) 72 self.remove_btn.setStyleName(self.STYLE["listItem-button"])
70 self.add(self.box) 73 self.add(self.box)
105 if self.validate: # if None, the state is always valid 108 if self.validate: # if None, the state is always valid
106 self.last_validity = self.validate(self.text) 109 self.last_validity = self.validate(self.text)
107 110
108 if self.last_validity == self.VALID: 111 if self.last_validity == self.VALID:
109 self.box.removeStyleName(self.STYLE["listItem-box-invalid"]) 112 self.box.removeStyleName(self.STYLE["listItem-box-invalid"])
113 self.box.setVisibleLength(max(len(self.text), 10))
110 elif self.last_validity == self.INVALID: 114 elif self.last_validity == self.INVALID:
111 self.box.addStyleName(self.STYLE["listItem-box-invalid"]) 115 self.box.addStyleName(self.STYLE["listItem-box-invalid"])
112 elif self.last_validity == self.DUPLICATE: 116 elif self.last_validity == self.DUPLICATE:
113 self.remove_btn.click() # this may do more stuff then self.remove() 117 self.remove_btn.click() # this may do more stuff then self.remove()
114 return 118 return
115 119
116 if self.taglist and self.text: 120 if self.taglist and self.text:
117 self.taglist.tag([self.text]) 121 self.taglist.tag([self.text])
118 self.last_checked_value = self.text 122 self.last_checked_value = self.text
123 self.box.setSelectionRange(len(self.text), 0)
119 self.remove_btn.setVisible(len(self.text) > 0) 124 self.remove_btn.setVisible(len(self.text) > 0)
120 125
121 def setFocus(self, focused): 126 def setFocus(self, focused):
122 self.box.setFocus(focused) 127 self.box.setFocus(focused)
123 128
144 self.addDragListener(listener) 149 self.addDragListener(listener)
145 150
146 151
147 def onDragStart(self, event): 152 def onDragStart(self, event):
148 """The user starts dragging the item.""" 153 """The user starts dragging the item."""
149 self.box.setSelectionRange(len(self.text), 0)
150
151 dt = event.dataTransfer 154 dt = event.dataTransfer
152 dt.setData('text/plain', "%s\n%s" % (self.text, "CONTACT_TEXTBOX")) 155 dt.setData('text/plain', "%s\n%s" % (self.text, "CONTACT_TEXTBOX"))
153 dt.setDragImage(self.box.getElement(), 15, 15) 156 dt.setDragImage(self.box.getElement(), 15, 15)
154 157
155 158
156 class ListItemHandler(ClickHandler, FocusHandler, KeyboardHandler, ChangeHandler): 159 class ListItemHandler(ClickHandler, FocusHandler, KeyboardHandler, ChangeHandler):
157 """Implements basic handlers for the ListItem events.""" 160 """Implements basic handlers for the ListItem events."""
158 161
159 last_item = None # the last item is an empty text box for user input 162 last_item = None # the last item is an empty text box for user input
160 163
161 def __init__(self, manager, key): 164 def __init__(self, taglist):
165 """
166
167 @param taglist (quick_list_manager.QuickTagList): list manager
168 """
162 ClickHandler.__init__(self) 169 ClickHandler.__init__(self)
163 FocusHandler.__init__(self) 170 FocusHandler.__init__(self)
164 ChangeHandler.__init__(self) 171 ChangeHandler.__init__(self)
165 KeyboardHandler.__init__(self) 172 KeyboardHandler.__init__(self)
166 self.manager = manager 173 self.taglist = taglist
167 self.key = key
168 174
169 def addItem(self, item): 175 def addItem(self, item):
170 raise NotImplementedError 176 raise NotImplementedError
171 177
172 def removeItem(self, item): 178 def removeItem(self, item):
187 193
188 def onFocus(self, sender): 194 def onFocus(self, sender):
189 """The text box has the focus.""" 195 """The text box has the focus."""
190 #log.debug("onFocus sender type: %s" % type(sender)) 196 #log.debug("onFocus sender type: %s" % type(sender))
191 assert isinstance(sender, AutoCompleteTextBox) 197 assert isinstance(sender, AutoCompleteTextBox)
192 sender.setCompletionItems(self.manager.untagged) 198 sender.setCompletionItems(self.taglist.untagged)
193 199
194 def onKeyUp(self, sender, keycode, modifiers): 200 def onKeyUp(self, sender, keycode, modifiers):
195 """The text box is being modified - or ENTER key has been pressed.""" 201 """The text box is being modified - or ENTER key has been pressed."""
196 # this is called after onChange when you press ENTER, and now we get the final value 202 # this is called after onChange when you press ENTER, and now we get the final value
197 #log.debug("onKeyUp sender type: %s" % type(sender)) 203 #log.debug("onKeyUp sender type: %s" % type(sender))
211 item = textbox.getParent() 217 item = textbox.getParent()
212 if item.text == item.last_checked_value: 218 if item.text == item.last_checked_value:
213 # this method has already been called (by self.onChange) and there's nothing new 219 # this method has already been called (by self.onChange) and there's nothing new
214 return 220 return
215 item.refresh() 221 item.refresh()
216 item.box.setSelectionRange(len(item.text), 0)
217 if item == self.last_item and item.last_validity == ListItem.VALID and item.text: 222 if item == self.last_item and item.last_validity == ListItem.VALID and item.text:
218 self.addItem() 223 self.addItem()
219 224
220 class DraggableListItemHandler(ListItemHandler, DragHandler): 225 class DraggableListItemHandler(ListItemHandler, DragHandler):
221 """Implements basic handlers for the DraggableListItem events.""" 226 """Implements basic handlers for the DraggableListItem events."""
222 227
223 def __init__(self, manager, key): 228 def __init__(self, manager):
224 ListItemHandler.__init__(self, manager, key) 229 """
230
231 @param manager (ListManager): list manager
232 """
233 ListItemHandler.__init__(self, manager)
225 DragHandler.__init__(self) 234 DragHandler.__init__(self)
235
236 @property
237 def manager(self):
238 return self.taglist
226 239
227 def onDragStart(self, event): 240 def onDragStart(self, event):
228 """The user starts dragging the item.""" 241 """The user starts dragging the item."""
229 self.manager.drop_target = None 242 self.manager.drop_target = None
230 243
244 # XXX: beware that pyjamas.ui.FlowPanel is not fully implemented: 257 # XXX: beware that pyjamas.ui.FlowPanel is not fully implemented:
245 # - it can not be used with pyjamas.ui.Label 258 # - it can not be used with pyjamas.ui.Label
246 # - FlowPanel.insert doesn't work 259 # - FlowPanel.insert doesn't work
247 260
248 STYLE = {"listPanel": "listPanel"} 261 STYLE = {"listPanel": "listPanel"}
249 262 ACCEPT_NEW_ENTRY = False
250 def __init__(self, manager, key, items): 263
264 def __init__(self, manager, items=None):
251 """Initialization with a button for the list name (key) and a DraggableListItem. 265 """Initialization with a button for the list name (key) and a DraggableListItem.
252 266
253 @param manager (ListManager) 267 @param manager (ListManager): list manager
254 @param key (unicode): list name 268 @param items (list): items to be set
255 @param items (list): items to append
256 """ 269 """
257 FlowPanel.__init__(self) 270 FlowPanel.__init__(self)
258 DraggableListItemHandler.__init__(self, manager, key) 271 DraggableListItemHandler.__init__(self, manager)
259 libervia_widget.DropCell.__init__(self, None) 272 libervia_widget.DropCell.__init__(self, None)
260 self.addStyleName(self.STYLE["listPanel"]) 273 self.addStyleName(self.STYLE["listPanel"])
261 self.manager = manager 274 self.manager = manager
262 items.sort() 275 self.resetItems(items)
263 self.addItem()
264 for item in items:
265 self.addItem(unicode(item))
266 276
267 # FIXME: dirty magic strings '@' and '@@' 277 # FIXME: dirty magic strings '@' and '@@'
268 self.drop_keys = {"GROUP": lambda host, item_s: self.addItem("@%s" % item_s), 278 self.drop_keys = {"GROUP": lambda host, item_s: self.addItem("@%s" % item_s),
269 "CONTACT": lambda host, item_s: self.addItem(item_s), 279 "CONTACT": lambda host, item_s: self.addItem(item_s),
270 "CONTACT_TITLE": lambda host, item_s: self.addItem('@@'), 280 "CONTACT_TITLE": lambda host, item_s: self.addItem('@@'),
307 def count(list_, item): # XXX: list.count in not implemented by pyjamas 317 def count(list_, item): # XXX: list.count in not implemented by pyjamas
308 return len([elt for elt in list_ if elt == item]) 318 return len([elt for elt in list_ if elt == item])
309 319
310 if count(self.getItems(), text) > 1: 320 if count(self.getItems(), text) > 1:
311 return ListItem.DUPLICATE # item already exists in this list so we suggest its deletion 321 return ListItem.DUPLICATE # item already exists in this list so we suggest its deletion
322 if self.ACCEPT_NEW_ENTRY:
323 return ListItem.VALID
312 return ListItem.VALID if text in self.manager.items or not text else ListItem.INVALID 324 return ListItem.VALID if text in self.manager.items or not text else ListItem.INVALID
313 325
314 def addItem(self, text=""): 326 def addItem(self, text=""):
315 """Add an item. 327 """Add an item.
316 328
344 """ 356 """
345 if item == self.last_item: 357 if item == self.last_item:
346 self.addItem("") 358 self.addItem("")
347 item.remove() # this also updates the taglist 359 item.remove() # this also updates the taglist
348 360
361 def resetItems(self, items):
362 """Reset the items.
363
364 @param items (list): items to be set
365 """
366 for child in self.getChildren():
367 child.remove()
368
369 self.addItem()
370 if not items:
371 return
372
373 items.sort()
374 for item in items:
375 self.addItem(unicode(item))
376
349 377
350 class ListManager(FlexTable, quick_list_manager.QuickTagList): 378 class ListManager(FlexTable, quick_list_manager.QuickTagList):
351 """Implements a table to manage one or several lists of items.""" 379 """Implements a table to manage one or several lists of items."""
352 380
353 STYLE = {"listManager-button": "group", 381 STYLE = {"listManager-button": "group",
378 406
379 if items is None: 407 if items is None:
380 items = [] 408 items = []
381 409
382 self.lists[key] = {"button": Button(key, Title=key, StyleName=self.STYLE["listManager-button"]), 410 self.lists[key] = {"button": Button(key, Title=key, StyleName=self.STYLE["listManager-button"]),
383 "panel": ListPanel(self, key, items)} 411 "panel": ListPanel(self, items)}
384 412
385 y, x = len(self.lists), 0 413 y, x = len(self.lists), 0
386 self.insertRow(y) 414 self.insertRow(y)
387 self.setWidget(y, x, self.lists[key]["button"]) 415 self.setWidget(y, x, self.lists[key]["button"])
388 self.setWidget(y, x + 1, self.lists[key]["panel"]) 416 self.setWidget(y, x + 1, self.lists[key]["panel"])