Package muntjac :: Package ui :: Module abstract_select
[hide private]
[frames] | no frames]

Source Code for Module muntjac.ui.abstract_select

   1  # Copyright (C) 2012 Vaadin Ltd.  
   2  # Copyright (C) 2012 Richard Lincoln 
   3  #  
   4  # Licensed under the Apache License, Version 2.0 (the "License");  
   5  # you may not use this file except in compliance with the License.  
   6  # You may obtain a copy of the License at  
   7  #  
   8  #     http://www.apache.org/licenses/LICENSE-2.0  
   9  #  
  10  # Unless required by applicable law or agreed to in writing, software  
  11  # distributed under the License is distributed on an "AS IS" BASIS,  
  12  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  
  13  # See the License for the specific language governing permissions and  
  14  # limitations under the License. 
  15   
  16  """Defines a class representing a selection of items the user has selected 
  17  in a UI.""" 
  18   
  19  from muntjac.util import OrderedSet 
  20   
  21  from muntjac.ui.abstract_field import AbstractField 
  22  from muntjac.terminal.resource import IResource 
  23  from muntjac.terminal.key_mapper import KeyMapper 
  24   
  25  from muntjac.data import property as prop 
  26  from muntjac.data import container 
  27  from muntjac.data import item 
  28   
  29  from muntjac.data.util.indexed_container import IndexedContainer 
  30   
  31  from muntjac.event.dd.acceptcriteria.target_detail_is import TargetDetailIs 
  32  from muntjac.event.dd.target_details_impl import TargetDetailsImpl 
  33   
  34  from muntjac.event.dd.acceptcriteria.contains_data_flavor import \ 
  35      ContainsDataFlavor 
  36   
  37  from muntjac.event.dd.acceptcriteria.client_side_criterion import \ 
  38      ClientSideCriterion 
  39   
  40  from muntjac.terminal.gwt.client.ui.dd.vertical_drop_location import \ 
  41      VerticalDropLocation 
42 43 44 -class AbstractSelect(AbstractField, container.IContainer, container.IViewer, 45 container.IPropertySetChangeListener, 46 container.IPropertySetChangeNotifier, 47 container.IItemSetChangeNotifier, 48 container.IItemSetChangeListener):
49 """A class representing a selection of items the user has selected 50 in a UI. The set of choices is presented as a set of L{IItem}s in a 51 L{IContainer}. 52 53 A C{Select} component may be in single- or multiselect mode. Multiselect 54 mode means that more than one item can be selected simultaneously. 55 56 @author: Vaadin Ltd. 57 @author: Richard Lincoln 58 @version: 1.1.2 59 """ 60 61 #: IItem caption mode: IItem's ID's C{String} representation 62 # is used as caption. 63 ITEM_CAPTION_MODE_ID = 0 64 65 #: IItem caption mode: IItem's C{String} representation is 66 # used as caption. 67 ITEM_CAPTION_MODE_ITEM = 1 68 69 #: IItem caption mode: Index of the item is used as caption. The index 70 # mode can only be used with the containers implementing the 71 # L{muntjac.data.container.IIndexed} interface. 72 ITEM_CAPTION_MODE_INDEX = 2 73 74 #: IItem caption mode: If an IItem has a caption it's used, if not, 75 # IItem's ID's string representation is used as caption. 76 # B{This is the default}. 77 ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID = 3 78 79 #: IItem caption mode: Captions must be explicitly specified. 80 ITEM_CAPTION_MODE_EXPLICIT = 4 81 82 #: IItem caption mode: Only icons are shown, captions are hidden. 83 ITEM_CAPTION_MODE_ICON_ONLY = 5 84 85 #: IItem caption mode: IItem captions are read from property specified 86 # with C{setItemCaptionPropertyId}. 87 ITEM_CAPTION_MODE_PROPERTY = 6 88 89
90 - def __init__(self, *args):
91 """Creates an empty Select with caption, that is connected to a 92 data-source or is filled from a collection of option values. 93 94 @param args: tuple of the form 95 - () 96 - (caption) 97 1. the caption of the component. 98 - (caption, dataSource) 99 1. the caption of the component. 100 2. the IContainer datasource to be selected from by 101 this select. 102 - (caption, options) 103 1. the caption of the component. 104 2. the Collection containing the options. 105 """ 106 #: Is the select in multiselect mode? 107 self._multiSelect = False 108 109 #: Select options. 110 self.items = None 111 112 #: Is the user allowed to add new options? 113 self._allowNewOptions = None 114 115 #: Keymapper used to map key values. 116 self.itemIdMapper = KeyMapper() 117 118 #: IItem icons. 119 self._itemIcons = dict() 120 121 #: IItem captions. 122 self._itemCaptions = dict() 123 124 #: IItem caption mode. 125 self._itemCaptionMode = self.ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID 126 127 #: IItem caption source property id. 128 self._itemCaptionPropertyId = None 129 130 #: IItem icon source property id. 131 self._itemIconPropertyId = None 132 133 #: List of property set change event listeners. 134 self._propertySetEventListeners = OrderedSet() 135 136 #: List of item set change event listeners. 137 self._itemSetEventListeners = OrderedSet() 138 139 self._propertySetEventCallbacks = dict() 140 141 self._itemSetEventCallbacks = dict() 142 143 #: IItem id that represents null selection of this select. 144 # 145 # Data interface does not support nulls as item ids. Selecting the 146 # item identified by this id is the same as selecting no items at 147 # all. This setting only affects the single select mode. 148 self._nullSelectionItemId = None 149 150 # Null (empty) selection is enabled by default 151 self._nullSelectionAllowed = True 152 153 self._newItemHandler = None 154 155 #: Caption (IItem / IProperty) change listeners 156 self._captionChangeListener = None 157 158 super(AbstractSelect, self).__init__() 159 160 nargs = len(args) 161 if nargs == 0: 162 self.setContainerDataSource(IndexedContainer()) 163 elif nargs == 1: 164 caption, = args 165 self.setContainerDataSource(IndexedContainer()) 166 self.setCaption(caption) 167 elif nargs == 2: 168 if isinstance(args[1], list): 169 caption, options = args 170 # Create the options container and add given options to it 171 c = IndexedContainer() 172 if options is not None: 173 for o in options: 174 c.addItem(o) 175 176 self.setCaption(caption) 177 self.setContainerDataSource(c) 178 else: 179 caption, dataSource = args 180 self.setCaption(caption) 181 self.setContainerDataSource(dataSource) 182 else: 183 raise ValueError, 'too many arguments'
184 185
186 - def paintContent(self, target):
187 """Paints the content of this component. 188 189 @param target: 190 the Paint Event. 191 @raise PaintException: 192 if the paint operation failed. 193 """ 194 # Paints field properties 195 super(AbstractSelect, self).paintContent(target) 196 197 # Paints select attributes 198 if self.isMultiSelect(): 199 target.addAttribute('selectmode', 'multi') 200 201 if self.isNewItemsAllowed(): 202 target.addAttribute('allownewitem', True) 203 204 if self.isNullSelectionAllowed(): 205 target.addAttribute('nullselect', True) 206 if self.getNullSelectionItemId() is not None: 207 target.addAttribute('nullselectitem', True) 208 209 # Constructs selected keys array 210 if self.isMultiSelect(): 211 selectedKeys = [None] * len(self.getValue()) 212 elif (self.getValue() is None 213 and self.getNullSelectionItemId() is None): 214 selectedKeys = [None] * 0 215 else: 216 selectedKeys = [None] * 1 217 218 # first remove all previous item/property listeners 219 self.getCaptionChangeListener().clear() 220 # Paints the options and create array of selected id keys 221 222 target.startTag('options') 223 keyIndex = 0 224 # Support for external null selection item id 225 ids = self.getItemIds() 226 if (self.isNullSelectionAllowed() 227 and (self.getNullSelectionItemId() is not None) 228 and (self.getNullSelectionItemId() not in ids)): 229 idd = self.getNullSelectionItemId() 230 # Paints option 231 target.startTag('so') 232 self.paintItem(target, idd) 233 if self.isSelected(idd): 234 selectedKeys[keyIndex] = self.itemIdMapper.key(idd) 235 keyIndex += 1 # post increment 236 target.endTag('so') 237 238 i = self.getItemIds() 239 # Paints the available selection options from data source 240 for idd in i: 241 if (not self.isNullSelectionAllowed() 242 and (idd is not None) 243 and (idd == self.getNullSelectionItemId())): 244 # Remove item if it's the null selection item but null 245 # selection is not allowed 246 continue 247 248 key = self.itemIdMapper.key(idd) 249 # add listener for each item, to cause repaint 250 # if an item changes 251 self.getCaptionChangeListener().addNotifierForItem(idd) 252 target.startTag('so') 253 self.paintItem(target, idd) 254 if self.isSelected(idd) and (keyIndex < len(selectedKeys)): 255 selectedKeys[keyIndex] = key 256 keyIndex += 1 # post increment 257 target.endTag('so') 258 target.endTag('options') 259 260 # Paint variables 261 target.addVariable(self, 'selected', selectedKeys) 262 if self.isNewItemsAllowed(): 263 target.addVariable(self, 'newitem', '')
264 265
266 - def paintItem(self, target, itemId):
267 key = self.itemIdMapper.key(itemId) 268 caption = self.getItemCaption(itemId) 269 icon = self.getItemIcon(itemId) 270 if icon is not None: 271 target.addAttribute('icon', icon) 272 273 target.addAttribute('caption', caption) 274 if (itemId is not None) and (itemId == self.getNullSelectionItemId()): 275 target.addAttribute('nullselection', True) 276 277 target.addAttribute('key', key) 278 if self.isSelected(itemId): 279 target.addAttribute('selected', True)
280 281
282 - def changeVariables(self, source, variables):
283 """Invoked when the value of a variable has changed. 284 285 @see: L{AbstractComponent.changeVariables} 286 """ 287 super(AbstractSelect, self).changeVariables(source, variables) 288 289 # New option entered (and it is allowed) 290 if self.isNewItemsAllowed(): 291 newitem = variables.get('newitem') 292 if (newitem is not None) and (len(newitem) > 0): 293 self.getNewItemHandler().addNewItem(newitem) 294 295 # Selection change 296 if 'selected' in variables: 297 ka = variables.get('selected') 298 299 # Multiselect mode 300 if self.isMultiSelect(): 301 302 # TODO Optimize by adding repaintNotNeeded when applicable 303 304 # Converts the key-array to id-set 305 s = list() 306 for i in range( len(ka) ): 307 idd = self.itemIdMapper.get(ka[i]) 308 if (not self.isNullSelectionAllowed() 309 and (idd is None 310 or idd == self.getNullSelectionItemId())): 311 # skip empty selection if nullselection 312 # is not allowed 313 self.requestRepaint() 314 elif (idd is not None) and self.containsId(idd): 315 s.append(idd) 316 317 if not self.isNullSelectionAllowed() and (len(s) < 1): 318 # empty selection not allowed, keep old value 319 self.requestRepaint() 320 return 321 322 # Limits the deselection to the set of visible items 323 # (non-visible items can not be deselected) 324 visible = self.getVisibleItemIds() 325 if visible is not None: 326 newsel = self.getValue() 327 if newsel is None: 328 newsel = set() 329 else: 330 newsel = set(newsel) 331 newsel = newsel.difference(visible) 332 newsel = newsel.union(s) 333 self.setValue(newsel, True) 334 else: 335 # Single select mode 336 if (not self.isNullSelectionAllowed() 337 and (len(ka) == 0 or ka[0] is None 338 or ka[0] == self.getNullSelectionItemId())): 339 self.requestRepaint() 340 return 341 342 if len(ka) == 0: 343 # Allows deselection only if the deselected item 344 # is visible 345 current = self.getValue() 346 visible = self.getVisibleItemIds() 347 if visible is not None and current in visible: 348 self.setValue(None, True) 349 else: 350 idd = self.itemIdMapper.get(ka[0]) 351 if not self.isNullSelectionAllowed() and (idd is None): 352 self.requestRepaint() 353 elif (idd is not None 354 and idd == self.getNullSelectionItemId()): 355 self.setValue(None, True) 356 else: 357 self.setValue(idd, True)
358 359
360 - def setNewItemHandler(self, newItemHandler):
361 """Setter for new item handler that is called when user adds 362 new item in newItemAllowed mode. 363 """ 364 self._newItemHandler = newItemHandler
365 366
367 - def getNewItemHandler(self):
368 """Getter for new item handler. 369 370 @return: newItemHandler 371 """ 372 if self._newItemHandler is None: 373 self._newItemHandler = DefaultNewItemHandler(self) 374 return self._newItemHandler
375 376
377 - def getVisibleItemIds(self):
378 """Gets the visible item ids. In Select, this returns list of all 379 item ids, but can be overriden in subclasses if they paint only 380 part of the items to the terminal or null if no items is visible. 381 """ 382 if self.isVisible(): 383 return self.getItemIds() 384 return None
385 386
387 - def getType(self, propertyId=None):
388 """Returns the type of the property. C{getValue} and 389 C{setValue} methods must be compatible with this type: 390 one can safely cast C{getValue} to given type and pass 391 any variable assignable to this type as a parameter to 392 C{setValue}. 393 394 @param propertyId: 395 the Id identifying the property. 396 @return: the Type of the property. 397 @see: L{IContainer.getType} 398 """ 399 if propertyId is None: 400 if self.isMultiSelect(): 401 return set 402 else: 403 return object 404 else: 405 return self.items.getType(propertyId)
406 407
408 - def getValue(self):
409 """Gets the selected item id or in multiselect mode a set of 410 selected ids. 411 412 @see: L{AbstractField.getValue} 413 """ 414 retValue = super(AbstractSelect, self).getValue() 415 416 if self.isMultiSelect(): 417 418 # If the return value is not a set 419 if retValue is None: 420 return set() 421 422 if isinstance(retValue, set): 423 return retValue 424 425 elif isinstance(retValue, list): 426 return set(retValue) 427 428 else: 429 s = set() 430 if self.items.containsId(retValue): 431 s.add(retValue) 432 return s 433 else: 434 return retValue
435 436
437 - def setValue(self, newValue, repaintIsNotNeeded=None):
438 """Sets the visible value of the property. 439 440 The value of the select is the selected item id. If the select 441 is in multiselect-mode, the value is a set of selected item keys. 442 In multiselect mode all collections of id:s can be assigned. 443 444 @param newValue: 445 the New selected item or collection of selected items. 446 @param repaintIsNotNeeded: 447 True if caller is sure that repaint is not needed. 448 @see: L{AbstractField.setValue} 449 """ 450 if repaintIsNotNeeded is None: 451 if newValue == self.getNullSelectionItemId(): 452 newValue = None 453 self.setValue(newValue, False) 454 else: 455 if self.isMultiSelect(): 456 457 if newValue is None: 458 super(AbstractSelect, self).setValue(set(), 459 repaintIsNotNeeded) 460 elif hasattr(newValue, '__iter__'): 461 super(AbstractSelect, self).setValue(set(newValue), 462 repaintIsNotNeeded) 463 464 elif (newValue is None) or (self.items.containsId(newValue)): 465 super(AbstractSelect, self).setValue(newValue, 466 repaintIsNotNeeded)
467 468 # IContainer methods 469
470 - def getItem(self, itemId):
471 """Gets the item from the container with given id. If the container 472 does not contain the requested item, null is returned. 473 474 @param itemId: 475 the item id. 476 @return: the item from the container. 477 """ 478 return self.items.getItem(itemId)
479 480
481 - def getItemIds(self):
482 """Gets the item Id collection from the container. 483 484 @return: the Collection of item ids. 485 """ 486 return self.items.getItemIds()
487 488
489 - def getContainerPropertyIds(self):
490 """Gets the property Id collection from the container. 491 492 @return: the Collection of property ids. 493 """ 494 return self.items.getContainerPropertyIds()
495 496 497 # Gets the number of items in the container. 498 # 499 # @return: the Number of items in the container. 500 # @see: L{IContainer.size}
501 - def size(self):
502 return len(self.items)
503 504
505 - def __len__(self):
506 return self.size()
507 508
509 - def containsId(self, itemId):
510 """Tests, if the collection contains an item with given id. 511 512 @param itemId: 513 the Id the of item to be tested. 514 """ 515 if itemId is not None: 516 return self.items.containsId(itemId) 517 else: 518 return False
519 520
521 - def getContainerProperty(self, itemId, propertyId):
522 """Gets the IProperty identified by the given itemId and propertyId 523 from the IContainer 524 525 @see: L{IContainer.getContainerProperty} 526 """ 527 return self.items.getContainerProperty(itemId, propertyId)
528 529
530 - def addContainerProperty(self, propertyId, typ, defaultValue):
531 """Adds the new property to all items. Adds a property with given 532 id, type and default value to all items in the container. 533 534 This functionality is optional. If the function is unsupported, 535 it always returns false. 536 537 @return: True if the operation succeeded. 538 @see: L{IContainer.addContainerProperty} 539 """ 540 retval = self.items.addContainerProperty(propertyId, typ, 541 defaultValue) 542 543 if (retval and (not isinstance(self.items, 544 container.IPropertySetChangeNotifier))): 545 self.firePropertySetChange() 546 547 return retval
548 549
550 - def removeAllItems(self):
551 """Removes all items from the container. 552 553 This functionality is optional. If the function is unsupported, 554 it always returns false. 555 556 @return: True if the operation succeeded. 557 @see: L{IContainer.removeAllItems} 558 """ 559 retval = self.items.removeAllItems() 560 self.itemIdMapper.removeAll() 561 562 if retval: 563 self.setValue(None) 564 if not isinstance(self.items, container.IItemSetChangeNotifier): 565 self.fireItemSetChange() 566 567 return retval
568 569
570 - def addItem(self, itemId=None):
571 """Create a new item into container. The created new item is returned 572 and ready for setting property values. if the creation fails, null 573 is returned. In case the container already contains the item, null 574 is returned. 575 576 This functionality is optional. If the function is unsupported, it 577 always returns null. 578 579 @param itemId: 580 the Identification of the item to be created. 581 @return: the Created item with the given id, or null in case of 582 failure. 583 @see: L{IContainer.addItem} 584 """ 585 if itemId is None: 586 retval = self.items.addItem() 587 if (retval is not None) and (not isinstance(self.items, 588 container.IItemSetChangeNotifier)): 589 self.fireItemSetChange() 590 return retval 591 else: 592 retval = self.items.addItem(itemId) 593 if (retval is not None) and (not isinstance(self.items, 594 container.IItemSetChangeNotifier)): 595 self.fireItemSetChange() 596 return retval
597 598
599 - def removeItem(self, itemId):
600 self.unselect(itemId) 601 retval = self.items.removeItem(itemId) 602 self.itemIdMapper.remove(itemId) 603 if retval and (not isinstance(self.items, 604 container.IItemSetChangeNotifier)): 605 self.fireItemSetChange() 606 return retval
607 608
609 - def removeContainerProperty(self, propertyId):
610 """Removes the property from all items. Removes a property with 611 given id from all the items in the container. 612 613 This functionality is optional. If the function is unsupported, 614 it always returns false. 615 616 @return: True if the operation succeeded. 617 @see: L{IContainer.removeContainerProperty} 618 """ 619 retval = self.items.removeContainerProperty(propertyId) 620 if retval and (not isinstance(self.items, 621 container.IPropertySetChangeNotifier)): 622 self.firePropertySetChange() 623 return retval
624 625
626 - def setContainerDataSource(self, newDataSource):
627 """Sets the IContainer that serves as the data source of the viewer. 628 629 As a side-effect the fields value (selection) is set to null due 630 old selection not necessary exists in new IContainer. 631 632 @see: L{muntjac.data.container.IViewer.setContainerDataSource} 633 634 @param newDataSource: 635 the new data source. 636 """ 637 if newDataSource is None: 638 newDataSource = IndexedContainer() 639 640 self.getCaptionChangeListener().clear() 641 642 if self.items != newDataSource: 643 644 # Removes listeners from the old datasource 645 if self.items is not None: 646 if isinstance(self.items, 647 container.IItemSetChangeNotifier): 648 self.items.removeListener(self, 649 container.IItemSetChangeListener) 650 651 if isinstance(self.items, 652 container.IPropertySetChangeNotifier): 653 self.items.removeListener(self, 654 container.IPropertySetChangeListener) 655 656 # Assigns new data source 657 self.items = newDataSource 658 659 # Clears itemIdMapper also 660 self.itemIdMapper.removeAll() 661 662 # Adds listeners 663 if self.items is not None: 664 if isinstance(self.items, 665 container.IItemSetChangeNotifier): 666 self.items.addListener(self, 667 container.IItemSetChangeListener) 668 if isinstance(self.items, 669 container.IPropertySetChangeNotifier): 670 self.items.addListener(self, 671 container.IPropertySetChangeListener) 672 673 # We expect changing the data source should also clean value. 674 # See #810, #4607, #5281 675 self.setValue(None) 676 677 self.requestRepaint()
678 679
680 - def getContainerDataSource(self):
681 """Gets the viewing data-source container. 682 683 @see: L{muntjac.data.container.IViewer.getContainerDataSource} 684 """ 685 return self.items
686 687 # Select attributes 688
689 - def isMultiSelect(self):
690 """Is the select in multiselect mode? In multiselect mode 691 692 @return: the Value of property multiSelect. 693 """ 694 return self._multiSelect
695 696
697 - def setMultiSelect(self, multiSelect):
698 """Sets the multiselect mode. Setting multiselect mode false may 699 loose selection information: if selected items set contains one 700 or more selected items, only one of the selected items is kept as 701 selected. 702 703 @param multiSelect: 704 the New value of property multiSelect. 705 """ 706 if multiSelect and (self.getNullSelectionItemId() is not None): 707 raise ValueError, ('Multiselect and NullSelectionItemId can ' 708 'not be set at the same time.') 709 710 if multiSelect != self._multiSelect: 711 712 # Selection before mode change 713 oldValue = self.getValue() 714 715 self._multiSelect = multiSelect 716 717 # Convert the value type 718 if multiSelect: 719 s = set() 720 if oldValue is not None: 721 s.add(oldValue) 722 723 self.setValue(s) 724 else: 725 s = oldValue 726 if (s is None) or (len(s) == 0): 727 self.setValue(None) 728 else: 729 # Set the single select to contain only the first 730 # selected value in the multiselect 731 self.setValue(s[0]) # FIXME: check iterator 732 733 self.requestRepaint()
734 735
736 - def isNewItemsAllowed(self):
737 """Does the select allow adding new options by the user. If true, 738 the new options can be added to the IContainer. The text entered by 739 the user is used as id. Note that data-source must allow adding new 740 items. 741 742 @return: True if additions are allowed. 743 """ 744 return self._allowNewOptions
745 746
747 - def setNewItemsAllowed(self, allowNewOptions):
748 """Enables or disables possibility to add new options by the user. 749 750 @param allowNewOptions: 751 the New value of property allowNewOptions. 752 """ 753 # Only handle change requests 754 if self._allowNewOptions != allowNewOptions: 755 756 self._allowNewOptions = allowNewOptions 757 758 self.requestRepaint()
759 760
761 - def setItemCaption(self, itemId, caption):
762 """Override the caption of an item. Setting caption explicitly 763 overrides id, item and index captions. 764 765 @param itemId: 766 the id of the item to be re-captioned. 767 @param caption: 768 the New caption. 769 """ 770 if itemId is not None: 771 self._itemCaptions[itemId] = caption 772 self.requestRepaint()
773 774
775 - def getItemCaption(self, itemId):
776 """Gets the caption of an item. The caption is generated as specified 777 by the item caption mode. See C{setItemCaptionMode()} for 778 more details. 779 780 @param itemId: 781 the id of the item to be queried. 782 @return: the caption for specified item. 783 """ 784 # Null items can not be found 785 if itemId is None: 786 return None 787 788 caption = None 789 790 test = self.getItemCaptionMode() 791 if test == self.ITEM_CAPTION_MODE_ID: 792 caption = str(itemId) 793 794 elif test == self.ITEM_CAPTION_MODE_INDEX: 795 if isinstance(self.items, container.IIndexed): 796 caption = str(self.items.indexOfId(itemId)) 797 else: 798 caption = 'ERROR: IContainer is not indexed' 799 800 elif test == self.ITEM_CAPTION_MODE_ITEM: 801 i = self.getItem(itemId) 802 if i is not None: 803 caption = str(i) 804 805 elif test == self.ITEM_CAPTION_MODE_EXPLICIT: 806 caption = self._itemCaptions.get(itemId) 807 808 elif test == self.ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID: 809 caption = self._itemCaptions.get(itemId) 810 if caption is None: 811 caption = str(itemId) 812 813 elif test == self.ITEM_CAPTION_MODE_PROPERTY: 814 p = self.getContainerProperty(itemId, 815 self.getItemCaptionPropertyId()) 816 if p is not None: 817 caption = str(p) 818 819 # All items must have some captions 820 return caption if caption is not None else ''
821 822
823 - def setItemIcon(self, itemId, icon):
824 """Sets the icon for an item. 825 826 @param itemId: 827 the id of the item to be assigned an icon. 828 @param icon: 829 the icon to use or null. 830 """ 831 if itemId is not None: 832 if icon is None: 833 if itemId in self._itemIcons: 834 del self._itemIcons[itemId] 835 else: 836 self._itemIcons[itemId] = icon 837 self.requestRepaint()
838 839
840 - def getItemIcon(self, itemId):
841 """Gets the item icon. 842 843 @param itemId: 844 the id of the item to be assigned an icon. 845 @return: the icon for the item or null, if not specified. 846 """ 847 explicit = self._itemIcons.get(itemId) 848 if explicit is not None: 849 return explicit 850 851 if self.getItemIconPropertyId() is None: 852 return None 853 854 ip = self.getContainerProperty(itemId, self.getItemIconPropertyId()) 855 if ip is None: 856 return None 857 858 icon = ip.getValue() 859 if isinstance(icon, IResource): 860 return icon 861 862 return None
863 864
865 - def setItemCaptionMode(self, mode):
866 """Sets the item caption mode. 867 868 The mode can be one of the following ones: 869 870 - C{ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID} : Items 871 Id-objects C{toString} is used as item caption. If caption 872 is explicitly specified, it overrides the id-caption. 873 - C{ITEM_CAPTION_MODE_ID} : Items Id-objects 874 C{toString} is used as item caption. 875 - C{ITEM_CAPTION_MODE_ITEM} : IItem-objects 876 C{toString} is used as item caption. 877 - C{ITEM_CAPTION_MODE_INDEX} : The index of the item is 878 used as item caption. The index mode can only be used with the 879 containers implementing C{IContainer.IIndexed} interface. 880 - C{ITEM_CAPTION_MODE_EXPLICIT} : The item captions must 881 be explicitly specified. 882 - C{ITEM_CAPTION_MODE_PROPERTY} : The item captions are 883 read from property, that must be specified with 884 C{setItemCaptionPropertyId}. 885 886 The C{ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID} is the default 887 mode. 888 889 @param mode: 890 the One of the modes listed above. 891 """ 892 if ((self.ITEM_CAPTION_MODE_ID <= mode) 893 and (mode <= self.ITEM_CAPTION_MODE_PROPERTY)): 894 self._itemCaptionMode = mode 895 self.requestRepaint()
896 897
898 - def getItemCaptionMode(self):
899 """Gets the item caption mode. 900 901 The mode can be one of the following ones: 902 903 - C{ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID} : Items 904 Id-objects C{toString} is used as item caption. If caption 905 is explicitly specified, it overrides the id-caption. 906 - C{ITEM_CAPTION_MODE_ID} : Items Id-objects 907 C{toString} is used as item caption. 908 - C{ITEM_CAPTION_MODE_ITEM} : IItem-objects 909 C{toString} is used as item caption. 910 - C{ITEM_CAPTION_MODE_INDEX} : The index of the item is 911 used as item caption. The index mode can only be used with the 912 containers implementing C{IContainer.IIndexed} interface. 913 - C{ITEM_CAPTION_MODE_EXPLICIT} : The item captions must 914 be explicitly specified. 915 - C{ITEM_CAPTION_MODE_PROPERTY} : The item captions are 916 read from property, that must be specified with 917 C{setItemCaptionPropertyId}. 918 919 The C{ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID} is the default 920 mode. 921 922 @return: the One of the modes listed above. 923 """ 924 return self._itemCaptionMode
925 926
927 - def setItemCaptionPropertyId(self, propertyId):
928 """Sets the item caption property. 929 930 Setting the id to a existing property implicitly sets the item caption 931 mode to C{ITEM_CAPTION_MODE_PROPERTY}. If the object is in 932 C{ITEM_CAPTION_MODE_PROPERTY} mode, setting caption property 933 id null resets the item caption mode to 934 C{ITEM_CAPTION_EXPLICIT_DEFAULTS_ID}. 935 936 Setting the property id to null disables this feature. The id is null 937 by default. 938 939 @param propertyId: 940 the id of the property. 941 """ 942 if propertyId is not None: 943 self._itemCaptionPropertyId = propertyId 944 self.setItemCaptionMode(self.ITEM_CAPTION_MODE_PROPERTY) 945 self.requestRepaint() 946 else: 947 self._itemCaptionPropertyId = None 948 if self.getItemCaptionMode() == self.ITEM_CAPTION_MODE_PROPERTY: 949 self.setItemCaptionMode( 950 self.ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID) 951 self.requestRepaint()
952 953
954 - def getItemCaptionPropertyId(self):
955 """Gets the item caption property. 956 957 @return: the Id of the property used as item caption source. 958 """ 959 return self._itemCaptionPropertyId
960 961
962 - def setItemIconPropertyId(self, propertyId):
963 """Sets the item icon property. 964 965 If the property id is set to a valid value, each item is given an 966 icon got from the given property of the items. The type of the 967 property must be assignable to IResource. 968 969 Note: The icons set with C{setItemIcon} function override 970 the icons from the property. 971 972 Setting the property id to null disables this feature. The id is 973 null by default. 974 975 @param propertyId: 976 the id of the property that specifies icons for items 977 or null 978 @raise ValueError: 979 If the propertyId is not in the container or is not of 980 a valid type 981 """ 982 if propertyId is None: 983 self._itemIconPropertyId = None 984 985 elif propertyId not in self.getContainerPropertyIds(): 986 raise ValueError, 'IProperty id not found in the container' 987 988 elif issubclass(self.getType(propertyId), IResource): 989 self._itemIconPropertyId = propertyId 990 else: 991 raise ValueError, 'IProperty type must be assignable to IResource' 992 993 self.requestRepaint()
994 995
996 - def getItemIconPropertyId(self):
997 """Gets the item icon property. 998 999 If the property id is set to a valid value, each item is given an 1000 icon got from the given property of the items. The type of the 1001 property must be assignable to Icon. 1002 1003 Note: The icons set with C{setItemIcon} function override 1004 the icons from the property. 1005 1006 Setting the property id to null disables this feature. The id is null 1007 by default. 1008 1009 @return: the Id of the property containing the item icons. 1010 """ 1011 return self._itemIconPropertyId
1012 1013
1014 - def isSelected(self, itemId):
1015 """Tests if an item is selected. 1016 1017 In single select mode testing selection status of the item identified 1018 by L{getNullSelectionItemId} returns true if the value of the 1019 property is null. 1020 1021 @param itemId: 1022 the Id the of the item to be tested. 1023 @see: L{getNullSelectionItemId} 1024 @see: L{setNullSelectionItemId} 1025 """ 1026 if itemId is None: 1027 return False 1028 1029 if self.isMultiSelect(): 1030 return itemId in self.getValue() 1031 else: 1032 value = self.getValue() 1033 if value is None: 1034 return itemId == self.getNullSelectionItemId() 1035 else: 1036 return itemId == value
1037 1038
1039 - def select(self, itemId):
1040 """Selects an item. 1041 1042 In single select mode selecting item identified by 1043 L{getNullSelectionItemId} sets the value of the property 1044 to null. 1045 1046 @param itemId: 1047 the identifier of IItem to be selected. 1048 @see: L{getNullSelectionItemId} 1049 @see: L{setNullSelectionItemId} 1050 """ 1051 if not self.isMultiSelect(): 1052 self.setValue(itemId) 1053 elif (not self.isSelected(itemId) and (itemId is not None) 1054 and self.items.containsId(itemId)): 1055 s = set(self.getValue()) 1056 s.add(itemId) 1057 self.setValue(s)
1058 1059
1060 - def unselect(self, itemId):
1061 """Unselects an item. 1062 1063 @param itemId: 1064 the identifier of the IItem to be unselected. 1065 @see: L{getNullSelectionItemId} 1066 @see: L{setNullSelectionItemId} 1067 """ 1068 if self.isSelected(itemId): 1069 if self.isMultiSelect(): 1070 s = set(self.getValue()) 1071 s.remove(itemId) 1072 self.setValue(s) 1073 else: 1074 self.setValue(None)
1075 1076
1077 - def containerPropertySetChange(self, event):
1078 """Notifies this listener that the Containers contents has changed. 1079 1080 @see: L{IPropertySetChangeListener.containerPropertySetChange} 1081 """ 1082 self.firePropertySetChange()
1083 1084
1085 - def addListener(self, listener, iface=None):
1086 """Adds a new IProperty or IItem set change listener for this 1087 IContainer. 1088 1089 @see: L{IPropertySetChangeNotifier.addListener} 1090 @see: L{IItemSetChangeNotifier.addListener} 1091 """ 1092 if (isinstance(listener, container.IItemSetChangeListener) and 1093 (iface is None or 1094 issubclass(iface, container.IItemSetChangeListener))): 1095 1096 self._itemSetEventListeners.add(listener) 1097 1098 if (isinstance(listener, container.IPropertySetChangeListener) and 1099 (iface is None or 1100 issubclass(iface, container.IPropertySetChangeListener))): 1101 1102 self._propertySetEventListeners.add(listener) 1103 1104 super(AbstractSelect, self).addListener(listener, iface)
1105 1106
1107 - def addCallback(self, callback, eventType=None, *args):
1108 if eventType is None: 1109 eventType = callback._eventType 1110 1111 if eventType == container.IItemSetChangeEvent: 1112 1113 self._itemSetEventCallbacks[callback] = args 1114 1115 elif eventType == container.IPropertySetChangeEvent: 1116 1117 self._propertySetEventCallbacks[callback] = args 1118 1119 else: 1120 super(AbstractSelect, self).addCallback(callback, eventType, *args)
1121 1122
1123 - def removeListener(self, listener, iface=None):
1124 """Removes a previously registered IProperty and IItemset change 1125 listener. 1126 1127 @see: L{IPropertySetChangeNotifier.removeListener} 1128 @see: L{IItemSetChangeNotifier.removeListener} 1129 """ 1130 if (isinstance(listener, container.IItemSetChangeListener) and 1131 (iface is None or iface == container.IItemSetChangeListener)): 1132 if listener in self._itemSetEventListeners: 1133 self._itemSetEventListeners.remove(listener) 1134 1135 if (isinstance(listener, container.IPropertySetChangeListener) and 1136 (iface is None or 1137 iface == container.IPropertySetChangeListener)): 1138 if listener in self._propertySetEventListeners: 1139 self._propertySetEventListeners.remove(listener) 1140 1141 super(AbstractSelect, self).removeListener(listener, iface)
1142 1143
1144 - def removeCallback(self, callback, eventType=None):
1145 if eventType is None: 1146 eventType = callback._eventType 1147 1148 if issubclass(eventType, container.IItemSetChangeEvent): 1149 if callback in self._itemSetEventCallbacks: 1150 del self._itemSetEventCallbacks[callback] 1151 1152 elif eventType == container.IPropertySetChangeEvent: 1153 if callback in self._propertySetEventCallbacks: 1154 del self._propertySetEventCallbacks[callback] 1155 1156 else: 1157 super(AbstractSelect, self).removeCallback(callback, eventType)
1158 1159
1160 - def getListeners(self, eventType):
1161 if issubclass(eventType, container.IItemSetChangeEvent): 1162 return OrderedSet(self._itemSetEventListeners) 1163 1164 elif issubclass(eventType, container.IPropertySetChangeEvent): 1165 return OrderedSet(self._propertySetEventListeners) 1166 1167 return super(AbstractSelect, self).getListeners(eventType)
1168 1169
1170 - def containerItemSetChange(self, event):
1171 """Lets the listener know a Containers IItem set has changed. 1172 1173 @see: L{IItemSetChangeListener.containerItemSetChange} 1174 """ 1175 # Clears the item id mapping table 1176 self.itemIdMapper.removeAll() 1177 1178 # Notify all listeners 1179 self.fireItemSetChange()
1180 1181
1182 - def firePropertySetChange(self):
1183 """Fires the property set change event.""" 1184 event = IPropertySetChangeEvent(self) 1185 1186 for l in self._propertySetEventListeners: 1187 l.containerPropertySetChange(event) 1188 1189 for callback, args in self._propertySetEventCallbacks.iteritems(): 1190 callback(event, *args) 1191 1192 self.requestRepaint()
1193 1194
1195 - def fireItemSetChange(self):
1196 """Fires the item set change event.""" 1197 event = IItemSetChangeEvent(self) 1198 1199 for l in self._itemSetEventListeners: 1200 l.containerItemSetChange(event) 1201 1202 for callback, args in self._itemSetEventCallbacks.iteritems(): 1203 callback(event, *args) 1204 1205 self.requestRepaint()
1206 1207
1208 - def isEmpty(self):
1209 """For multi-selectable fields, also an empty collection of values 1210 is considered to be an empty field. 1211 1212 @see: L{AbstractField.isEmpty}. 1213 """ 1214 if not self._multiSelect: 1215 return super(AbstractSelect, self).isEmpty() 1216 else: 1217 value = self.getValue() 1218 return (super(AbstractSelect, self).isEmpty() 1219 or (isinstance(value, list) and len(value) == 0))
1220 1221
1222 - def setNullSelectionAllowed(self, nullSelectionAllowed):
1223 """Allow or disallow empty selection by the user. If the select is 1224 in single-select mode, you can make an item represent the empty 1225 selection by calling C{setNullSelectionItemId()}. This 1226 way you can for instance set an icon and caption for the null 1227 selection item. 1228 1229 @param nullSelectionAllowed: 1230 whether or not to allow empty selection 1231 @see: L{setNullSelectionItemId} 1232 @see: L{isNullSelectionAllowed} 1233 """ 1234 if nullSelectionAllowed != self._nullSelectionAllowed: 1235 self._nullSelectionAllowed = nullSelectionAllowed 1236 self.requestRepaint()
1237 1238
1239 - def isNullSelectionAllowed(self):
1240 """Checks if null empty selection is allowed by the user. 1241 1242 @return: whether or not empty selection is allowed 1243 @see: L{setNullSelectionAllowed} 1244 """ 1245 return self._nullSelectionAllowed
1246 1247
1248 - def getNullSelectionItemId(self):
1249 """Returns the item id that represents null value of this select in 1250 single select mode. 1251 1252 Data interface does not support nulls as item ids. Selecting the item 1253 identified by this id is the same as selecting no items at all. This 1254 setting only affects the single select mode. 1255 1256 @return: the Object Null value item id. 1257 @see: L{setNullSelectionItemId} 1258 @see: L{isSelected} 1259 @see: L{select} 1260 """ 1261 return self._nullSelectionItemId
1262 1263
1264 - def setNullSelectionItemId(self, nullSelectionItemId):
1265 """Sets the item id that represents null value of this select. 1266 1267 Data interface does not support nulls as item ids. Selecting the 1268 item identified by this id is the same as selecting no items at all. 1269 This setting only affects the single select mode. 1270 1271 @param nullSelectionItemId: 1272 the nullSelectionItemId to set. 1273 @see: L{getNullSelectionItemId} 1274 @see: L{isSelected} 1275 @see: L{select} 1276 """ 1277 if (nullSelectionItemId is not None) and self.isMultiSelect(): 1278 raise ValueError, ('Multiselect and NullSelectionItemId can ' 1279 'not be set at the same time.') 1280 1281 self._nullSelectionItemId = nullSelectionItemId
1282 1283
1284 - def attach(self):
1285 """Notifies the component that it is connected to an application. 1286 1287 @see: L{AbstractField.attach} 1288 """ 1289 super(AbstractSelect, self).attach()
1290 1291
1292 - def detach(self):
1293 """Detaches the component from application. 1294 1295 @see: L{AbstractComponent.detach} 1296 """ 1297 self.getCaptionChangeListener().clear() 1298 super(AbstractSelect, self).detach()
1299 1300
1301 - def getCaptionChangeListener(self):
1302 if self._captionChangeListener is None: 1303 self._captionChangeListener = CaptionChangeListener(self) 1304 return self._captionChangeListener
1305
1306 1307 -class IFiltering(object):
1308 """Interface for option filtering, used to filter options based on 1309 user entered value. The value is matched to the item caption. 1310 C{FILTERINGMODE_OFF} (0) turns the filtering off. 1311 C{FILTERINGMODE_STARTSWITH} (1) matches from the start of 1312 the caption. C{FILTERINGMODE_CONTAINS} (1) matches anywhere 1313 in the caption. 1314 """ 1315 1316 FILTERINGMODE_OFF = 0 1317 1318 FILTERINGMODE_STARTSWITH = 1 1319 1320 FILTERINGMODE_CONTAINS = 2 1321
1322 - def setFilteringMode(self, filteringMode):
1323 """Sets the option filtering mode. 1324 1325 @param filteringMode: 1326 the filtering mode to use 1327 """ 1328 raise NotImplementedError
1329 1330
1331 - def getFilteringMode(self):
1332 """Gets the current filtering mode. 1333 1334 @return: the filtering mode in use 1335 """ 1336 raise NotImplementedError
1337
1338 1339 -class MultiSelectMode(object):
1340 """Multi select modes that controls how multi select behaves.""" 1341 1342 #: The default behavior of the multi select mode 1343 DEFAULT = 'DEFAULT' 1344 1345 #: The previous more simple behavior of the multselect 1346 SIMPLE = 'SIMPLE' 1347 1348 _values = [DEFAULT, SIMPLE] 1349 1350 @classmethod
1351 - def values(cls):
1352 return cls._values[:]
1353 1354 @classmethod
1355 - def ordinal(cls, val):
1356 return cls._values.index(val)
1357
1358 1359 -class INewItemHandler(object):
1360
1361 - def addNewItem(self, newItemCaption):
1362 raise NotImplementedError
1363
1364 1365 -class DefaultNewItemHandler(INewItemHandler):
1366 """This is a default class that handles adding new items that are typed 1367 by user to selects container. 1368 1369 By extending this class one may implement some logic on new item 1370 addition like database inserts. 1371 """ 1372
1373 - def __init__(self, select):
1374 self._select = select
1375
1376 - def addNewItem(self, newItemCaption):
1377 # Checks for readonly 1378 if self._select.isReadOnly(): 1379 raise prop.ReadOnlyException() 1380 1381 # Adds new option 1382 if self._select.addItem(newItemCaption) is not None: 1383 1384 # Sets the caption property, if used 1385 if self._select.getItemCaptionPropertyId() is not None: 1386 try: 1387 prop = self._select.getContainerProperty(newItemCaption, 1388 self._select.getItemCaptionPropertyId()) 1389 prop.setValue(newItemCaption) 1390 except prop.ConversionException: 1391 # The conversion exception is safely ignored, the 1392 # caption is just missing 1393 pass 1394 1395 if self._select.isMultiSelect(): 1396 values = set(self._select.getValue()) 1397 values.add(newItemCaption) 1398 self._select.setValue(values) 1399 else: 1400 self._select.setValue(newItemCaption)
1401
1402 1403 -class IItemSetChangeEvent(container.IItemSetChangeEvent):
1404 """Implementation of item set change event.""" 1405
1406 - def __init__(self, container):
1407 self._container = container
1408 1409
1410 - def getContainer(self):
1411 """Gets the IProperty where the event occurred. 1412 1413 @see: L{muntjac.data.container.IItemSetChangeEvent.getContainer} 1414 """ 1415 return self._container
1416
1417 1418 -class IPropertySetChangeEvent(container.IPropertySetChangeEvent):
1419 """Implementation of property set change event.""" 1420
1421 - def __init__(self, container):
1422 self._container = container
1423 1424
1425 - def getContainer(self):
1426 """Retrieves the IContainer whose contents have been modified. 1427 1428 @see: L{muntjac.data.container.IPropertySetChangeEvent.getContainer} 1429 """ 1430 return self._container
1431
1432 1433 -class AbstractSelectTargetDetails(TargetDetailsImpl):
1434 """TargetDetails implementation for subclasses of L{AbstractSelect} 1435 that implement L{DropTarget}. 1436 """ 1437
1438 - def __init__(self, rawVariables, select):
1439 """Constructor that automatically converts itemIdOver key to 1440 corresponding item Id 1441 """ 1442 super(AbstractSelectTargetDetails, self).__init__(rawVariables, select) 1443 1444 # The item id over which the drag event happened. 1445 self.idOver = None 1446 1447 # eagar fetch itemid, mapper may be emptied 1448 keyover = self.getData('itemIdOver') 1449 if keyover is not None: 1450 self.idOver = select.itemIdMapper.get(keyover)
1451 1452
1453 - def getItemIdOver(self):
1454 """If the drag operation is currently over an L{IItem}, this 1455 method returns the identifier of that L{IItem}. 1456 """ 1457 return self.idOver
1458 1459
1460 - def getDropLocation(self):
1461 """Returns a detailed vertical location where the drop happened on 1462 IItem. 1463 """ 1464 detail = self.getData('detail') 1465 if detail is None: 1466 return None 1467 return VerticalDropLocation.valueOf(detail)
1468
1469 1470 -class CaptionChangeListener(item.IPropertySetChangeListener, 1471 prop.IValueChangeListener):
1472 """This is a listener helper for IItem and IProperty changes that should 1473 cause a repaint. It should be attached to all items that are displayed, 1474 and the default implementation does this in paintContent(). Especially 1475 "lazyloading" components should take care to add and remove listeners as 1476 appropriate. Call addNotifierForItem() for each painted item (and 1477 remember to clear). 1478 1479 NOTE: singleton, use getCaptionChangeListener(). 1480 """ 1481
1482 - def __init__(self, select):
1483 self._select = select 1484 1485 # TODO clean this up - type is either item.IPropertySetChangeNotifier 1486 # or property.IValueChangeNotifier 1487 self._captionChangeNotifiers = set()
1488 1489
1490 - def addNotifierForItem(self, itemId):
1491 test = self._select.getItemCaptionMode() 1492 if test == self._select.ITEM_CAPTION_MODE_ITEM: 1493 i = self._select.getItem(itemId) 1494 if i is None: 1495 return 1496 1497 if isinstance(i, item.IPropertySetChangeNotifier): 1498 i.addListener(self._select.getCaptionChangeListener(), 1499 item.IPropertySetChangeListener) 1500 self._captionChangeNotifiers.add(i) 1501 1502 pids = i.getItemPropertyIds() 1503 if pids is not None: 1504 for pid in pids: 1505 p = i.getItemProperty(pid) 1506 if (p is not None 1507 and isinstance(p, prop.IValueChangeNotifier)): 1508 p.addListener(self._select.getCaptionChangeListener(), 1509 prop.IValueChangeListener) 1510 self._captionChangeNotifiers.add(p) 1511 1512 elif test == self._select.ITEM_CAPTION_MODE_PROPERTY: 1513 p = self._select.getContainerProperty(itemId, 1514 self._select.getItemCaptionPropertyId()) 1515 if p is not None and isinstance(p, prop.IValueChangeNotifier): 1516 p.addListener(self._select.getCaptionChangeListener(), 1517 prop.IValueChangeListener) 1518 self._captionChangeNotifiers.add(p)
1519 1520
1521 - def clear(self):
1522 for notifier in self._captionChangeNotifiers: 1523 if isinstance(notifier, item.IPropertySetChangeNotifier): 1524 notifier.removeListener(self._select.getCaptionChangeListener(), 1525 item.IPropertySetChangeListener) 1526 else: 1527 notifier.removeListener(self._select.getCaptionChangeListener(), 1528 prop.IValueChangeListener) 1529 self._captionChangeNotifiers.clear()
1530 1531
1532 - def valueChange(self, event):
1533 self._select.requestRepaint()
1534 1535
1536 - def itemPropertySetChange(self, event):
1537 self._select.requestRepaint()
1538
1539 1540 -class AbstractItemSetCriterion(ClientSideCriterion):
1541 """Abstract helper class to implement item id based criterion. 1542 1543 Note, inner class used not to open itemIdMapper for public access. 1544 """ 1545
1546 - def __init__(self, select, *itemId):
1547 if (self.itemIds is None) or (select is None): 1548 raise ValueError, 'Accepted item identifiers must be accepted.' 1549 self.itemIds = set(itemId) 1550 self.select = select
1551 1552
1553 - def paintContent(self, target):
1554 super(AbstractItemSetCriterion, self).paintContent(target) 1555 keys = [None] * len(self.itemIds) 1556 1557 for i, itemId in enumerate(self.itemIds): 1558 key = self.select.itemIdMapper.key(itemId) 1559 keys[i] = key 1560 1561 target.addAttribute('keys', keys) 1562 target.addAttribute('s', self.select)
1563
1564 1565 -class TargetItemIs(AbstractItemSetCriterion):
1566 """Criterion which accepts a drop only if the drop target is (one of) 1567 the given IItem identifier(s). Criterion can be used only on a drop 1568 targets that extends AbstractSelect like L{Table} and L{Tree}. 1569 The target and identifiers of valid Items are given in constructor. 1570 """ 1571
1572 - def __init__(self, select, *itemId):
1573 """@param select: 1574 the select implementation that is used as a drop target 1575 @param itemId: 1576 the identifier(s) that are valid drop locations 1577 """ 1578 super(TargetItemIs, self).__init__(select, itemId)
1579 1580
1581 - def accept(self, dragEvent):
1582 dropTargetData = dragEvent.getTargetDetails() 1583 if dropTargetData.getTarget() != self.select: 1584 return False 1585 return dropTargetData.getItemIdOver() in self.itemIds
1586
1587 1588 -class AcceptItem(AbstractItemSetCriterion):
1589 """This criterion accepts a only a L{Transferable} that contains 1590 given IItem (practically its identifier) from a specific AbstractSelect. 1591 """ 1592
1593 - def __init__(self, select, *itemId):
1594 """@param select: 1595 the select from which the item id's are checked 1596 @param itemId: 1597 the item identifier(s) of the select that are accepted 1598 """ 1599 super(AcceptItem, self).__init__(select, itemId)
1600 1601
1602 - def accept(self, dragEvent):
1603 transferable = dragEvent.getTransferable() 1604 1605 if transferable.getSourceComponent() != self.select: 1606 return False 1607 1608 return transferable.getItemId() in self.itemIds
1609 1610 # A simple accept criterion which ensures that L{Transferable} 1611 # contains an L{IItem} (or actually its identifier). In other words 1612 # the criterion check that drag is coming from a L{IContainer} like 1613 # L{Tree} or L{Table}. 1614 ALL = ContainsDataFlavor('itemId')
1615
1616 1617 -class VerticalLocationIs(TargetDetailIs):
1618 """An accept criterion to accept drops only on a specific vertical 1619 location of an item. 1620 1621 This accept criterion is currently usable in Tree and Table 1622 implementations. 1623 """ 1624 1625 TOP = None 1626 BOTTOM = None 1627 MIDDLE = None 1628
1629 - def __init__(self, l):
1630 super(VerticalLocationIs, self).__init__('detail', l)
1631 1632 VerticalLocationIs.TOP = VerticalLocationIs(VerticalDropLocation.TOP) 1633 VerticalLocationIs.BOTTOM = VerticalLocationIs(VerticalDropLocation.BOTTOM) 1634 VerticalLocationIs.MIDDLE = VerticalLocationIs(VerticalDropLocation.MIDDLE)
1635 1636 1637 -class ItemDescriptionGenerator(object):
1638 """Implement this interface and pass it to Tree.setItemDescriptionGenerator 1639 or Table.setItemDescriptionGenerator to generate mouse over descriptions 1640 ("tooltips") for the rows and cells in Table or for the items in Tree. 1641 """ 1642
1643 - def generateDescription(self, source, itemId, propertyId):
1644 """Called by Table when a cell (and row) is painted or a item is 1645 painted in Tree 1646 1647 @param source: 1648 The source of the generator, the Tree or Table the 1649 generator is attached to 1650 @param itemId: 1651 The itemId of the painted cell 1652 @param propertyId: 1653 The propertyId of the cell, null when getting row 1654 description 1655 @return: The description or "tooltip" of the item. 1656 """ 1657 raise NotImplementedError
1658