Skip to content

Commit 2b9bcb5

Browse files
committed
GAM: Add context menu to NPC list panels for quick CRE structure access
1 parent 116a90d commit 2b9bcb5

File tree

1 file changed

+131
-3
lines changed

1 file changed

+131
-3
lines changed

src/org/infinity/resource/gam/Viewer.java

Lines changed: 131 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,27 @@
99
import java.awt.GridBagLayout;
1010
import java.awt.GridLayout;
1111
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;
1218

1319
import javax.swing.BorderFactory;
1420
import javax.swing.DefaultListCellRenderer;
1521
import javax.swing.JList;
22+
import javax.swing.JMenuItem;
1623
import javax.swing.JPanel;
24+
import javax.swing.JPopupMenu;
1725

1826
import org.infinity.datatype.Flag;
1927
import org.infinity.datatype.ResourceRef;
28+
import org.infinity.gui.ViewFrame;
2029
import org.infinity.gui.ViewerUtil;
2130
import org.infinity.gui.ViewerUtil.ListValueRenderer;
31+
import org.infinity.gui.ViewerUtil.StructListPanel;
32+
import org.infinity.icon.Icons;
2233
import org.infinity.resource.AbstractStruct;
2334
import org.infinity.resource.AbstractVariable;
2435
import org.infinity.resource.StructEntry;
@@ -95,10 +106,12 @@ private static JPanel makeMiscPanel(GamResource gam) {
95106
}
96107

97108
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);
100113

101-
JPanel var1Panel = ViewerUtil.makeListPanel("Variables", gam, Variable.class, AbstractVariable.VAR_NAME,
114+
StructListPanel var1Panel = ViewerUtil.makeListPanel("Variables", gam, Variable.class, AbstractVariable.VAR_NAME,
102115
new VariableListRenderer());
103116

104117
setLayout(new GridLayout(2, 3, 3, 3));
@@ -137,4 +150,119 @@ public String getListValue(Object value) {
137150
return "";
138151
}
139152
}
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+
}
140268
}

0 commit comments

Comments
 (0)