001package jmri.jmrit.display.palette; 002 003import java.awt.Color; 004import java.awt.FlowLayout; 005import java.awt.event.WindowAdapter; 006import java.awt.event.WindowEvent; 007import java.util.HashMap; 008import java.util.Map.Entry; 009 010import javax.swing.BorderFactory; 011import javax.swing.Box; 012import javax.swing.BoxLayout; 013import javax.swing.JButton; 014import javax.swing.JLabel; 015import javax.swing.JPanel; 016import javax.swing.JScrollPane; 017 018import jmri.jmrit.catalog.CatalogPanel; 019import jmri.jmrit.catalog.NamedIcon; 020import jmri.util.swing.ImagePanel; 021import jmri.util.swing.JmriJOptionPane; 022 023/** 024 * This class is used when FamilyItemPanel classes add, modify or delete icon 025 * families. 026 * 027 * Note this class cannot be used with super classes of FamilyItemPanel 028 * (ItemPanel etc) since there are several casts to FamilyItemPanel. 029 * 030 * @author Pete Cressman Copyright (c) 2010, 2011, 2020 031 */ 032public class IconDialog extends ItemDialog { 033 034 protected String _family; 035 protected HashMap<String, NamedIcon> _iconMap; 036 protected ImagePanel _iconEditPanel; 037 protected CatalogPanel _catalog; 038 protected final JLabel _nameLabel; 039 040 /** 041 * Constructor for an existing family to change icons, add/delete icons, or to 042 * delete the family entirely. 043 * @param type itemType 044 * @param family icon family name 045 * @param parent the ItemPanel calling this class 046 */ 047 public IconDialog(String type, String family, FamilyItemPanel parent) { 048 super(type, Bundle.getMessage("ShowIconsTitle", family), parent); 049 _family = family; 050 JPanel panel = new JPanel(); 051 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); 052 panel.add(Box.createVerticalStrut(ItemPalette.STRUT_SIZE)); 053 054 JPanel p = new JPanel(); 055 _nameLabel = new JLabel(Bundle.getMessage("FamilyName", family)); 056 p.add(_nameLabel); 057 panel.add(p); 058 059 _iconEditPanel = new ImagePanel(); 060 _iconEditPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black, 1), 061 Bundle.getMessage("PreviewBorderTitle"))); 062 if (!_parent.isUpdate()) { 063 _iconEditPanel.setImage(_parent._frame.getPreviewBackground()); 064 } else { 065 _iconEditPanel.setImage(_parent._frame.getBackground(0)); //update always should be the panel background 066 } 067 panel.add(_iconEditPanel); // put icons above buttons 068 069 JPanel buttonPanel = new JPanel(); 070 buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.Y_AXIS)); 071 makeDoneButtonPanel(buttonPanel, "ButtonDone"); 072 panel.add(buttonPanel); 073 074 p = new JPanel(); 075 p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS)); 076 p.add(panel); 077 _catalog = makeCatalog(); 078 p.add(_catalog); 079 080 JScrollPane sp = new JScrollPane(p); 081 setContentPane(sp); 082 addWindowListener(new WindowAdapter() { 083 @Override 084 public void windowClosing(WindowEvent e) { 085 cancel(); 086 } 087 }); 088 } 089 090 protected void setMap(HashMap<String, NamedIcon> iconMap) { 091 if (iconMap != null) { 092 _iconMap = IconDialog.clone(iconMap); 093 } else { 094 _iconMap = _parent.makeNewIconMap(_type); 095 } 096 if (!(_type.equals("MultiSensor") || _type.equals("SignalHead"))) { 097 ItemPanel.checkIconMap(_type, _iconMap); 098 } 099 _parent.addIconsToPanel(_iconMap, _iconEditPanel, true); 100 setLocationRelativeTo(_parent); 101 setVisible(true); 102 pack(); 103 log.debug("setMap: initialization done."); 104 } 105 106 private CatalogPanel makeCatalog() { 107 CatalogPanel catalog = CatalogPanel.makeDefaultCatalog(false, false, true); 108 catalog.setToolTipText(Bundle.getMessage("ToolTipDragIcon")); 109 ImagePanel panel = catalog.getPreviewPanel(); 110 if (!_parent.isUpdate()) { 111 panel.setImage(_parent._frame.getPreviewBackground()); 112 } else { 113 panel.setImage(_parent._frame.getBackground(0)); //update always should be the panel background 114 } 115 return catalog; 116 } 117 118 // for _parent to update when background is changed 119 protected ImagePanel getIconEditPanel() { 120 return _iconEditPanel; 121 } 122 123 // for _parent to update when background is changed 124 protected ImagePanel getCatalogPreviewPanel() { 125 return _catalog.getPreviewPanel(); 126 } 127 128 /** 129 * Action for both create new family and change existing family. 130 * @return true if success 131 */ 132 protected boolean doDoneAction() { 133 if (log.isDebugEnabled()) { 134 log.debug("doDoneAction: {} for {} family= {}", (_parent._update?"Update":""), _type, _family); 135 } 136 if (_family == null || _family.isEmpty()) { 137 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("NoFamilyName"), 138 Bundle.getMessage("MessageTitle"), JmriJOptionPane.INFORMATION_MESSAGE); 139 return false; 140 } 141 HashMap<String, HashMap<String, NamedIcon>> families = ItemPalette.getFamilyMaps(_type); 142 String family; 143 HashMap<String, NamedIcon> catalogSet = families.get(_family); 144 boolean nameUsed; 145 if (catalogSet == null) { 146 family = _parent.findFamilyOfMap(null, _iconMap, families); 147 nameUsed = false; 148 } else { 149 family = _parent.findFamilyOfMap(_family, _iconMap, families); 150 nameUsed = true; // the map is found under another name than _family 151 } 152 if (family != null ) { // "new" map is stored 153 boolean sameMap = _parent.mapsAreEqual(_iconMap, families.get(family)); 154 if (!mapInCatalogOK(sameMap, nameUsed, _family, family)) { 155 return false; 156 } 157 } else { 158 boolean sameMap; 159 if (catalogSet == null) { 160 sameMap = false; 161 } else { 162 sameMap = _parent.mapsAreEqual(catalogSet, _iconMap); 163 } 164 if (!mapNotInCatalogOK(sameMap, nameUsed, _family)) { 165 return false; 166 } 167 } 168 _parent.dialogDoneAction(_family, _iconMap); 169 return true; 170 } 171 172 /** 173 * 174 * @param sameMap Map edited in dialog is the same as map found in catalog 175 * @param nameUsed Name as edited in dialog is the same as name found in catalog 176 * @param editFamily Map name as edited in this dialog 177 * @param catalogFamily Map name as found in the catalog 178 * @return false if not OK 179 */ 180 protected boolean mapInCatalogOK(boolean sameMap, boolean nameUsed, String editFamily, String catalogFamily) { 181 log.debug("doDoneAction: map of {} in storage with name= {}", editFamily, catalogFamily); 182 if (_parent._update) { 183 if (!catalogFamily.equals(editFamily)) { 184 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("CannotChangeName", editFamily, catalogFamily), 185 Bundle.getMessage("MessageTitle"), JmriJOptionPane.INFORMATION_MESSAGE); 186 return false; 187 } 188 } else { 189 log.debug("doDoneAction: name {} {} used and map {} same as {}", 190 editFamily, (nameUsed?"is":"NOT"), (sameMap?"":"NOT"), catalogFamily); 191 if (catalogFamily.equals(editFamily)) { 192 if (!sameMap) { 193 JmriJOptionPane.showMessageDialog(this, 194 Bundle.getMessage("DuplicateFamilyName", editFamily, _type, Bundle.getMessage("UseAnotherName")), 195 Bundle.getMessage("MessageTitle"), JmriJOptionPane.INFORMATION_MESSAGE); 196 return false; 197 } 198 } else { 199 if (sameMap) { 200 String oldFamily = _parent.getFamilyName(); // if oldFamily != null, this is an edit, not new set 201 if (oldFamily != null) { // editing an catalog set 202 if (nameUsed) { 203 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("SameNameSet", editFamily, catalogFamily), 204 Bundle.getMessage("MessageTitle"), JmriJOptionPane.INFORMATION_MESSAGE); 205 return false; 206 } 207 } else { 208 if (!nameUsed) { 209 JmriJOptionPane.showMessageDialog(this, 210 Bundle.getMessage("DuplicateFamilyName", editFamily, 211 _type, Bundle.getMessage("CannotUseName", catalogFamily)), 212 Bundle.getMessage("MessageTitle"), JmriJOptionPane.INFORMATION_MESSAGE); 213 return false; 214 } 215 } 216 } 217 } 218 } 219 return true; 220 } 221 222 /** 223 * Edited map is not in the catalog. 224 * 225 * @param sameMap Map edited in dialog is the same as map currently held in parent item panel 226 * @param nameUsed Name as edited in dialog is the same as a name found in catalog 227 * @param editFamily Map name as edited in this dialog 228 * @return false if not OK 229 */ 230 protected boolean mapNotInCatalogOK(boolean sameMap, boolean nameUsed, String editFamily) { 231 String oldFamily = _parent.getFamilyName(); 232 if (_parent._update) { 233 if (nameUsed) { // name is a key to stored map 234 log.debug("{} keys a stored map. name is used", editFamily); 235 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("NeedDifferentName", editFamily), 236 Bundle.getMessage("MessageTitle"), JmriJOptionPane.INFORMATION_MESSAGE); 237 return false; 238 } 239 } else { 240 if (oldFamily != null) { // editing an catalog set from parent 241 log.debug("Editing set {}. {} {} a stored map.", oldFamily, editFamily, (nameUsed?"is":"NOT")); 242 if (nameUsed) { // map in catalog under another name 243 if (!editFamily.equals(oldFamily)) { // named changed 244 if (!sameMap) { // also map changed 245 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("badReplaceIconSet", oldFamily, editFamily), 246 Bundle.getMessage("MessageTitle"), JmriJOptionPane.INFORMATION_MESSAGE); 247 return false; 248 } 249 } 250 } else { 251 int result = JmriJOptionPane.showOptionDialog(this, 252 Bundle.getMessage("ReplaceFamily", oldFamily, editFamily), 253 Bundle.getMessage("QuestionTitle"), JmriJOptionPane.DEFAULT_OPTION, 254 JmriJOptionPane.QUESTION_MESSAGE, null, 255 new Object[] {oldFamily, editFamily, Bundle.getMessage("ButtonCancel")}, 256 Bundle.getMessage("ButtonCancel")); 257 switch (result) { 258 case 0: // array position 0, oldFamily 259 _family = oldFamily; 260 break; 261 case 2: // array position 2 Cancel Button 262 case JmriJOptionPane.CLOSED_OPTION: // Dialog closed 263 return true; 264 case 1: // array position 1 editFamily 265 default: 266 break; 267 } 268 } 269 } else { 270 if (nameUsed) { // map in catalog under another name 271 JmriJOptionPane.showMessageDialog(this, 272 Bundle.getMessage("DuplicateFamilyName", editFamily, _type, Bundle.getMessage("UseAnotherName")), 273 Bundle.getMessage("MessageTitle"), JmriJOptionPane.INFORMATION_MESSAGE); 274 return false; 275 } 276 } 277 } 278 return true; 279 } 280 281 /** 282 * Action item to rename an icon family. 283 */ 284 protected void renameFamily() { 285 String family = _parent.getValidFamilyName(null, _iconMap); 286 if (family != null) { 287 _family = family; 288 _nameLabel.setText(Bundle.getMessage("FamilyName", _family)); 289 invalidate(); 290 } 291 } 292 293 protected void makeDoneButtonPanel(JPanel buttonPanel, String text) { 294 JPanel panel = new JPanel(); 295 panel.setLayout(new FlowLayout()); 296 JButton doneButton = new JButton(Bundle.getMessage(text)); 297 doneButton.addActionListener(a -> { 298 if (doDoneAction()) { 299 dispose(); 300 } 301 }); 302 panel.add(doneButton); 303 304 JButton renameButton = new JButton(Bundle.getMessage("renameFamily")); 305 renameButton.addActionListener(a -> renameFamily()); 306 panel.add(renameButton); 307 308 JButton cancelButton = new JButton(Bundle.getMessage("ButtonCancel")); 309 cancelButton.addActionListener(a -> cancel()); 310 panel.add(cancelButton); 311 buttonPanel.add(panel); 312 } 313 314 protected void cancel() { 315 _parent.setFamily(); 316 _parent._cntlDown = false; 317 super.dispose(); 318 } 319 static protected HashMap<String, NamedIcon> clone(HashMap<String, NamedIcon> map) { 320 HashMap<String, NamedIcon> clone = null; 321 if (map != null) { 322 clone = new HashMap<>(); 323 for (Entry<String, NamedIcon> entry : map.entrySet()) { 324 clone.put(entry.getKey(), new NamedIcon(entry.getValue())); 325 } 326 } 327 return clone; 328 } 329 330 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(IconDialog.class); 331 332}