Dropdown List (PMIComboBox): Disable item from selection

image image

So I’ve worked it out with help from these two posts:
(1) swing - Java: Make one item of a jcombobox unselectable(like for a sub-caption) and edit font of that item - Stack Overflow
(2) Anyway to make drop down table bigger for touchscreen projects? - #5 by PGriffith

Nothing like hacking until you get it!
PS. I’ve kept the code in there to also be able to increase the height of the items by setting the ‘scale’ property. This scales the item height by the scale value (1 = no change).

Add this code into a shared library:
shared.components.ddm

with
'''
Description: 
This will modify a drop down list component and enable:
 - increasing the height of the drop down list items by a multiplication factor applied to the component height

 - allow disabling items by prefixing their item text with '!' E.g. '!Manual'
 - allow confirming selection of items by prefixing their item text with '*' e.g. '*Auto'
Usage:
Add to a dropdown component onPropertyChange event handler:
if event.propertyName in ('componentRunning', 'data'):
	target = event.source
	shared.components.ddm.setNewRenderer(target, 1.5)
Revision History
No.		Author			Date		Comment
1.0		Nick Minchin	??			Original
1.1		Nick Minchin	??			??
1.2		Nick Minchin	2019-06-19	Fixed for cases when no value is selected and combobox value is blank.
1.3		Nick Minchin	2019-10-25	Fixed bug in NewPMIComboBoxRenderer class that caused the current selection to be cleared when the script ran. Note: this would not do a tag write, but it would display <Select One> in the component.  
1.4     Nick Minchin    2020-03-02  Added Item selection with Confirmation using "*" prefix
1.4.1	Nick Minchin	2020-03-11	Added initialLoad variable to item confirmation case so that confirmation popups are not shown if confirmation items are selected when component first loads. 
1.4.2   Nick Minchin    2020-05-20  Moved confirmation code around to be more consistent with the enabled code. No changes made to functionality.
''' 

### added to library: shared.components.ddm
from com.inductiveautomation.factorypmi.application.components.PMIComboBox import DataSetListCellRenderer
from com.inductiveautomation.factorypmi.application.components.PMIComboBox import DataSetComboBoxModel
from java.awt import Dimension
from java.awt import Font

# this class edit stops the user from selecting the item
class NewPMIComboBoxModel(DataSetComboBoxModel):
	def __init__(self, target):
		DataSetComboBoxModel.__init__(self, target, target.data)
		self.target = target
		# ensure that the currently selected item remains selected (without this, the item would deslect for some reason)
		self.initialLoad = 1
		try:
			self.selectedItem = target.model.selectedItem
		except:
			pass
		self.initialLoad = 0
	
	# disable items that start with ! from being clicked
	def setSelectedItem(self, item):
		if str(item)[0] == "!":
			def make_setPopupVisible():
				self.target.setPopupVisible(1)
			# keep the popup open (or, reopen it rather)
			system.util.invokeLater(make_setPopupVisible)
		elif str(item)[0] == "*" and not self.initialLoad:
			if system.gui.confirm("Are you you want to change the value to '%s'" % str(item).replace("*",""), "Confirm Action"):
				DataSetComboBoxModel.setSelectedItem(self, item)
		else:
			DataSetComboBoxModel.setSelectedItem(self, item)

# this class edit changes the item's look and feel
class NewPMIComboBoxRenderer(DataSetListCellRenderer):
	# Note: type(target) = PMIComboBox
	def __init__(self, target, scale=1):
		DataSetListCellRenderer.__init__(self, target)
		self.target = target
		self.scale = scale
	
	def getListCellRendererComponent(self, list, value, index, isSelected, cellHasFocus):
		# check for a starting '!'. if exists, disable the item
		itemEnabled = 1
		itemConfirm = 0

		if len(str(value)) > 0:
			itemEnabled = str(value)[0] != "!"
			itemConfirm = str(value)[0] == "*"
				  
		
		# get the item component so we can manipulate it
		component = DataSetListCellRenderer.getListCellRendererComponent(self, list, value, index, isSelected and itemEnabled, cellHasFocus)
																													   
		
		# set the height of the item based on the scale value provided
		component.setPreferredSize(Dimension(self.target.width, int(self.target.height * self.scale)))
		
		component.setEnabled(itemEnabled)
		component.setFont(component.getFont() if itemEnabled else component.getFont().deriveFont(Font.ITALIC))
		if not itemEnabled:
			# remove the "!" from any disabled item names
			component.text = str(value)[1:] + ' (Disabled)'
		if itemConfirm:
			component.text = str(value)[1:] + ' (Confirm)'
		
		return component

def setNewRenderer(PMIComboBox, scale=1):
	'''
	Description:
	This class is used to:
		- modify the list item heights to the height of the component multiplied by a factor. This is useful to increase the size for clients running touchscreens or for mobile projects.
		- prefix characters:
            allow menu items to be disabled by prefixing the item with a '!'
        - allow menu items to require confirmation by prefixing with a '*'
	
	Revision:
	No.		Date		Author			Comment
	1.0		2018-03		Paul Griffith	Original from http://forum.inductiveautomation.com/t/anyway-to-make-drop-down-table-bigger-for-touchscreen-projects/15045/5?u=nminchin
	1.1		2018-03		Nick Minchin	Modified for library use (http://forum.inductiveautomation.com/t/anyway-to-make-drop-down-table-bigger-for-touchscreen-projects/15045/6?u=nminchin)
	1.2		2019-04		Nick Minchin	Modified and added ability to disable items from being selected (http://forum.inductiveautomation.com/t/dropdown-list-pmicombobox-component-disable-item-from-selection/23697)
	'''
	PMIComboBox.setRenderer(NewPMIComboBoxRenderer(PMIComboBox, scale))
	PMIComboBox.setModel(NewPMIComboBoxModel(PMIComboBox))

And this is set on my drop down component’s PropertyChange event handler script:

if event.propertyName == 'data':
	target = event.source
	shared.components.ddm.setNewRenderer(target, 1.0)

EDIT: Modified the onPropertyChange event script to call on change of ‘data’ property instead of ‘componentRunning’. Found when using ‘componentRunning’, SQL query-bound drop down components would fire the componentRunning event before the data was populated and the data never showed up in the list.

EDIT 2019-12-09: Fixed the NewPMIComboBoxModel, added the self.setSelectedItem call so that the currently selected item remains selected after replacing the Model.

EDIT 2020-05-20: Added confirmation ability. Prefix item with “*”

2 Likes