|
9 | 9 | import java.awt.GridBagLayout; |
10 | 10 | import java.awt.GridLayout; |
11 | 11 | import java.awt.Insets; |
| 12 | +import java.awt.Point; |
| 13 | +import java.awt.event.ActionEvent; |
| 14 | +import java.awt.event.ActionListener; |
| 15 | +import java.awt.event.MouseAdapter; |
| 16 | +import java.awt.event.MouseEvent; |
| 17 | +import java.util.Objects; |
12 | 18 |
|
13 | 19 | import javax.swing.BorderFactory; |
14 | 20 | import javax.swing.DefaultListCellRenderer; |
15 | 21 | import javax.swing.JList; |
| 22 | +import javax.swing.JMenuItem; |
16 | 23 | import javax.swing.JPanel; |
| 24 | +import javax.swing.JPopupMenu; |
17 | 25 |
|
18 | 26 | import org.infinity.datatype.Flag; |
19 | 27 | import org.infinity.datatype.ResourceRef; |
| 28 | +import org.infinity.gui.ViewFrame; |
20 | 29 | import org.infinity.gui.ViewerUtil; |
21 | 30 | import org.infinity.gui.ViewerUtil.ListValueRenderer; |
| 31 | +import org.infinity.gui.ViewerUtil.StructListPanel; |
| 32 | +import org.infinity.icon.Icons; |
22 | 33 | import org.infinity.resource.AbstractStruct; |
23 | 34 | import org.infinity.resource.AbstractVariable; |
24 | 35 | import org.infinity.resource.StructEntry; |
@@ -95,10 +106,12 @@ private static JPanel makeMiscPanel(GamResource gam) { |
95 | 106 | } |
96 | 107 |
|
97 | 108 | Viewer(GamResource gam) { |
98 | | - final JPanel stats1Panel = ViewerUtil.makeListPanel("Non-player characters", gam, NonPartyNPC.class, NPC_ENTRY); |
99 | | - final JPanel stats2Panel = ViewerUtil.makeListPanel("Player characters", gam, PartyNPC.class, NPC_ENTRY); |
| 109 | + final StructListPanel stats1Panel = ViewerUtil.makeListPanel("Non-player characters", gam, NonPartyNPC.class, NPC_ENTRY); |
| 110 | + new NpcContextMenu(stats1Panel); |
| 111 | + final StructListPanel stats2Panel = ViewerUtil.makeListPanel("Player characters", gam, PartyNPC.class, NPC_ENTRY); |
| 112 | + new NpcContextMenu(stats2Panel); |
100 | 113 |
|
101 | | - JPanel var1Panel = ViewerUtil.makeListPanel("Variables", gam, Variable.class, AbstractVariable.VAR_NAME, |
| 114 | + StructListPanel var1Panel = ViewerUtil.makeListPanel("Variables", gam, Variable.class, AbstractVariable.VAR_NAME, |
102 | 115 | new VariableListRenderer()); |
103 | 116 |
|
104 | 117 | setLayout(new GridLayout(2, 3, 3, 3)); |
@@ -137,4 +150,119 @@ public String getListValue(Object value) { |
137 | 150 | return ""; |
138 | 151 | } |
139 | 152 | } |
| 153 | + |
| 154 | + /** |
| 155 | + * Handles a context menu associated with a {@code PartyNPC} list panel. |
| 156 | + */ |
| 157 | + private static class NpcContextMenu extends MouseAdapter implements ActionListener { |
| 158 | + private final JPopupMenu popup = new JPopupMenu(); |
| 159 | + private final JMenuItem miOpenChr = new JMenuItem("View/Edit CHR", Icons.ICON_ZOOM_16.getIcon()); |
| 160 | + private final JMenuItem miOpenCre = new JMenuItem("View/Edit CRE", Icons.ICON_ZOOM_16.getIcon()); |
| 161 | + |
| 162 | + private final StructListPanel panel; |
| 163 | + private final JList<StructEntry> npcList; |
| 164 | + |
| 165 | + public NpcContextMenu(StructListPanel npcPanel) { |
| 166 | + this.panel = Objects.requireNonNull(npcPanel); |
| 167 | + this.npcList = Objects.requireNonNull(this.panel.getList()); |
| 168 | + init(); |
| 169 | + } |
| 170 | + |
| 171 | +// /** Returns the associated {@code StructListPanel} instance. */ |
| 172 | +// public StructListPanel getPanel() { |
| 173 | +// return panel; |
| 174 | +// } |
| 175 | + |
| 176 | +// /** Returns the {@code JPopupMenu} associated with the list panel. */ |
| 177 | +// public JPopupMenu getPopupMenu() { |
| 178 | +// return popup; |
| 179 | +// } |
| 180 | + |
| 181 | + @Override |
| 182 | + public void mousePressed(MouseEvent e) { |
| 183 | + showPopup(e, true); |
| 184 | + } |
| 185 | + |
| 186 | + @Override |
| 187 | + public void mouseReleased(MouseEvent e) { |
| 188 | + showPopup(e, true); |
| 189 | + } |
| 190 | + |
| 191 | + @Override |
| 192 | + public void actionPerformed(ActionEvent e) { |
| 193 | + if (miOpenChr.equals(e.getSource())) { |
| 194 | + final PartyNPC npc = getSelectedNpc(null, false); |
| 195 | + if (npc != null) { |
| 196 | + new ViewFrame(panel.getTopLevelAncestor(), npc); |
| 197 | + } |
| 198 | + } else if (miOpenCre.equals(e.getSource())) { |
| 199 | + final PartyNPC npc = getSelectedNpc(null, false); |
| 200 | + if (npc != null) { |
| 201 | + final StructEntry se = npc.getAttribute(PartyNPC.GAM_NPC_CRE_RESOURCE); |
| 202 | + if (se instanceof CreResource) { |
| 203 | + new ViewFrame(panel.getTopLevelAncestor(), (CreResource) se); |
| 204 | + } |
| 205 | + } |
| 206 | + } |
| 207 | + } |
| 208 | + |
| 209 | + /** |
| 210 | + * Triggers the context menu addressed by the specified {@code MouseEvent}. |
| 211 | + * |
| 212 | + * @param e {@code MouseEvent} to handle. |
| 213 | + * @param autoSelect Indicates whether the list item at the current mouse coordinate should be selected. |
| 214 | + */ |
| 215 | + private void showPopup(MouseEvent e, boolean autoSelect) { |
| 216 | + if (e.isPopupTrigger()) { |
| 217 | + final PartyNPC npc = getSelectedNpc(new Point(e.getX(), e.getY()), autoSelect); |
| 218 | + if (npc != null) { |
| 219 | + miOpenCre.setEnabled(hasCreData(npc)); |
| 220 | + popup.show(e.getComponent(), e.getX(), e.getY()); |
| 221 | + } |
| 222 | + } |
| 223 | + } |
| 224 | + |
| 225 | + /** |
| 226 | + * Returns whether the specified {@code PartyNPC} structure contains a valid CRE resource substructure. |
| 227 | + */ |
| 228 | + private boolean hasCreData(PartyNPC npc) { |
| 229 | + return npc != null && npc.getAttribute(PartyNPC.GAM_NPC_CRE_RESOURCE) instanceof CreResource; |
| 230 | + } |
| 231 | + |
| 232 | + /** |
| 233 | + * Returns the NPC list element at the specified coordinate. |
| 234 | + * |
| 235 | + * @param p A location relative to the {@code npcList} component. Can be {@code null}. |
| 236 | + * @param autoSelect Indicates whether the list item at the specified location should be selected. |
| 237 | + * @return Returns the {@code PartyNPC} list item closest to the specified location. Returns the selected list item |
| 238 | + * if location is {@code null}. |
| 239 | + */ |
| 240 | + private PartyNPC getSelectedNpc(Point p, boolean autoSelect) { |
| 241 | + if (p != null && autoSelect) { |
| 242 | + int index = npcList.locationToIndex(p); |
| 243 | + if (index >= 0) { |
| 244 | + npcList.setSelectedIndex(index); |
| 245 | + } |
| 246 | + } |
| 247 | + |
| 248 | + int index = (p != null && !autoSelect) ? npcList.locationToIndex(p) : npcList.getSelectedIndex(); |
| 249 | + if (index >= 0) { |
| 250 | + final Object listItem = npcList.getModel().getElementAt(index); |
| 251 | + if (listItem instanceof PartyNPC) { |
| 252 | + return (PartyNPC) listItem; |
| 253 | + } |
| 254 | + } |
| 255 | + return null; |
| 256 | + } |
| 257 | + |
| 258 | + private void init() { |
| 259 | + miOpenChr.addActionListener(this); |
| 260 | + miOpenCre.addActionListener(this); |
| 261 | + |
| 262 | + popup.add(miOpenChr); |
| 263 | + popup.add(miOpenCre); |
| 264 | + |
| 265 | + npcList.addMouseListener(this); |
| 266 | + } |
| 267 | + } |
140 | 268 | } |
0 commit comments