1 package net.mograsim.plugin.editors;
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.math.BigInteger;
6 import java.util.Collection;
7 import java.util.HashSet;
9 import org.eclipse.core.resources.IFile;
10 import org.eclipse.core.runtime.CoreException;
11 import org.eclipse.core.runtime.IProgressMonitor;
12 import org.eclipse.jface.util.IPropertyChangeListener;
13 import org.eclipse.jface.util.SafeRunnable;
14 import org.eclipse.jface.viewers.TableViewerColumn;
15 import org.eclipse.swt.SWT;
16 import org.eclipse.swt.graphics.Font;
17 import org.eclipse.swt.layout.GridData;
18 import org.eclipse.swt.layout.GridLayout;
19 import org.eclipse.swt.widgets.Button;
20 import org.eclipse.swt.widgets.Composite;
21 import org.eclipse.swt.widgets.Control;
22 import org.eclipse.swt.widgets.Label;
23 import org.eclipse.swt.widgets.ScrollBar;
24 import org.eclipse.swt.widgets.Table;
25 import org.eclipse.swt.widgets.TableColumn;
26 import org.eclipse.swt.widgets.Text;
27 import org.eclipse.ui.IEditorInput;
28 import org.eclipse.ui.IEditorSite;
29 import org.eclipse.ui.IFileEditorInput;
30 import org.eclipse.ui.PartInitException;
31 import org.eclipse.ui.part.EditorPart;
32 import org.eclipse.ui.themes.ITheme;
33 import org.eclipse.ui.themes.IThemeManager;
35 import net.mograsim.machine.BitVectorMemory;
36 import net.mograsim.machine.MachineDefinition;
37 import net.mograsim.machine.Memory.MemoryCellModifiedListener;
38 import net.mograsim.machine.mi.MicroInstructionMemoryParseException;
39 import net.mograsim.machine.standard.memory.BitVectorBasedMemoryParser;
40 import net.mograsim.plugin.asm.AsmNumberUtil;
41 import net.mograsim.plugin.nature.MachineContext;
42 import net.mograsim.plugin.nature.ProjectMachineContext;
43 import net.mograsim.plugin.tables.AddressLabelProvider;
44 import net.mograsim.plugin.tables.DisplaySettings;
45 import net.mograsim.plugin.tables.LazyTableViewer;
46 import net.mograsim.plugin.tables.NumberColumnLabelProvider;
47 import net.mograsim.plugin.tables.RadixSelector;
48 import net.mograsim.plugin.tables.memory.MemoryCellEditingSupport;
49 import net.mograsim.plugin.tables.memory.MemoryTableContentProvider;
50 import net.mograsim.plugin.tables.memory.MemoryTableRow;
51 import net.mograsim.plugin.tables.memory.NumberVerifyListener;
53 public abstract class AbstractMemoryEditor extends EditorPart
55 private MachineContext context;
57 private BitVectorMemory memory;
59 private LazyTableViewer viewer;
60 private MemoryTableContentProvider provider;
61 private DisplaySettings displaySettings;
62 private final String addrColName;
63 private final String dataColName;
65 private Collection<Control> fontDependent = new HashSet<>();
67 private boolean dirty;
69 private final MemoryCellModifiedListener memListener;
71 private final static String font = "net.mograsim.plugin.memory.table_font";
72 private IPropertyChangeListener fontChangeListener;
74 public AbstractMemoryEditor(String addrColName, String dataColName)
76 this.addrColName = addrColName;
77 this.dataColName = dataColName;
79 memListener = this::cellModified;
83 public void createPartControl(Composite parent)
85 provider = new MemoryTableContentProvider();
86 displaySettings = new DisplaySettings();
88 parent.setLayout(new GridLayout(8, false));
92 displaySettings.addObserver(() -> viewer.refresh());
95 @SuppressWarnings("unused") // RadixSelector and exceptions
96 private void createHeader(Composite parent)
98 Text gotoText = new Text(parent, SWT.BORDER);
99 gotoText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
100 NumberVerifyListener vl = new NumberVerifyListener();
101 gotoText.addVerifyListener(vl);
102 gotoText.setText("0");
104 Runnable gotoAction = () ->
108 loadAround((int) (AsmNumberUtil.valueOf(gotoText.getText()).longValue() - provider.getLowerBound()));
109 viewer.getTable().deselectAll();
111 catch (NumberFormatException x)
113 // Nothing to do here
117 gotoText.addTraverseListener((e) ->
119 if (e.detail == SWT.TRAVERSE_RETURN)
123 Button gotoButton = new Button(parent, SWT.PUSH);
124 gotoButton.setText("Go to");
125 gotoButton.addListener(SWT.Selection, e ->
130 new Label(parent, SWT.NONE).setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
132 new RadixSelector(parent, displaySettings);
135 private void createViewer(Composite parent)
137 viewer = new LazyTableViewer(parent, SWT.FULL_SELECTION | SWT.BORDER | SWT.VIRTUAL);
138 fontDependent.add(viewer.getTable());
140 Table table = viewer.getTable();
141 table.setHeaderVisible(true);
142 table.setLinesVisible(true);
143 viewer.setUseHashlookup(true);
144 viewer.setContentProvider(provider);
145 getSite().setSelectionProvider(viewer);
146 viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 8, 1));
148 // TODO: Also support keyboard inputs for flexible scrolling
149 ScrollBar vBar = table.getVerticalBar();
150 vBar.addListener(SWT.Selection, (e) ->
153 if ((sel = vBar.getSelection()) < vBar.getMinimum() + vBar.getThumb() * 2 || sel > vBar.getMaximum() - vBar.getThumb() * 2)
155 loadAround(table.getTopIndex());
160 IThemeManager themeManager = getSite().getWorkbenchWindow().getWorkbench().getThemeManager();
161 themeManager.addPropertyChangeListener(fontChangeListener = (e) ->
163 if (IThemeManager.CHANGE_CURRENT_THEME.equals(e.getProperty()) || font.equals(e.getProperty()))
165 updateFont(themeManager.getCurrentTheme());
169 updateFont(themeManager.getCurrentTheme());
172 viewer.setInput(memory);
175 private void updateFont(ITheme theme)
177 Font newFont = theme.getFontRegistry().get(font);
178 // TODO: This is a quick fix! Still have to figure out why the CellEditors do not get the appropriate Font on their own
179 fontDependent.forEach(c -> c.setFont(newFont));
182 private void loadAround(int row)
184 long prevLb = provider.getLowerBound();
185 long address = prevLb + row;
186 long actualLb = Long.max(address - MemoryTableContentProvider.MAX_VISIBLE_ROWS / 2, memory.getDefinition().getMinimalAddress());
188 long prevHb = provider.getUpperBound();
189 // +- 1 row is not really important
190 long actualHb = Long.min(address + MemoryTableContentProvider.MAX_VISIBLE_ROWS / 2, memory.getDefinition().getMaximalAddress());
192 if (actualLb != prevLb || actualHb != prevHb)
194 Table table = viewer.getTable();
195 provider.setBounds(actualLb, actualHb);
196 int rowIndex = (int) (address - actualLb);
197 if (rowIndex >= 0 && rowIndex < table.getItemCount())
198 table.showItem(table.getItem(rowIndex));
203 private void createColumns()
205 TableViewerColumn addrCol = createTableViewerColumn(addrColName, 100);
206 addrCol.setLabelProvider(new AddressLabelProvider());
208 TableViewerColumn dataCol = createTableViewerColumn(dataColName, 100);
209 dataCol.setLabelProvider(new NumberColumnLabelProvider(displaySettings)
212 public BigInteger getAsBigInteger(Object element)
214 MemoryTableRow row = (MemoryTableRow) element;
215 return row.getMemory().getCellAsBigInteger(row.address);
219 public int getBitLength(Object element)
221 return ((MemoryTableRow) element).getMemory().getDefinition().getCellWidth();
224 MemoryCellEditingSupport eSup;
225 dataCol.setEditingSupport(eSup = new MemoryCellEditingSupport(viewer, displaySettings));
226 fontDependent.add(eSup.getCellEditorControl());
229 private TableViewerColumn createTableViewerColumn(String title, int width)
231 TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.NONE);
232 TableColumn column = viewerColumn.getColumn();
233 column.setText(title);
234 column.setWidth(width);
235 column.setResizable(true);
236 column.setMoveable(false);
241 public void init(IEditorSite site, IEditorInput input) throws PartInitException
243 if (input instanceof IFileEditorInput)
245 IFileEditorInput fileInput = (IFileEditorInput) input;
246 context = ProjectMachineContext.getMachineContextOf(fileInput.getFile().getProject());
248 setPartName(fileInput.getName());
251 open(fileInput.getFile());
255 throw new PartInitException("Failed to read input!", e);
258 throw new IllegalArgumentException("MemoryEditor can only be used with Files");
265 public void doSave(IProgressMonitor monitor)
267 IEditorInput input = getEditorInput();
268 if (input instanceof IFileEditorInput)
269 SafeRunnable.getRunner().run(() -> save(((IFileEditorInput) input).getFile(), monitor));
272 private void save(IFile file, IProgressMonitor monitor) throws CoreException, IOException
276 throw new MicroInstructionMemoryParseException("Failed to write the memory to File. No memory assigned.");
278 try (InputStream toWrite = BitVectorBasedMemoryParser.write(memory))
280 file.setContents(toWrite, 0, monitor);
285 private void open(IFile file) throws IOException, CoreException
287 MachineDefinition machDef = context.getMachineDefinition()
288 .orElseThrow(() -> new MicroInstructionMemoryParseException("No MachineDefinition assigned!"));
289 memory = createEmptyMemory(machDef);
290 BitVectorBasedMemoryParser.parseMemory(memory, file.getContents());
291 memory.registerCellModifiedListener(memListener);
293 viewer.setInput(memory);
296 protected abstract BitVectorMemory createEmptyMemory(MachineDefinition activeMachineDefinition);
298 private void cellModified(@SuppressWarnings("unused") long address)
303 private void setDirty(boolean newDirty)
306 firePropertyChange(PROP_DIRTY);
310 public void doSaveAs()
312 throw new UnsupportedOperationException();
316 public boolean isDirty()
322 public boolean isSaveAsAllowed()
328 public void setFocus()
330 viewer.getTable().setFocus();
334 public void dispose()
336 getSite().getWorkbenchWindow().getWorkbench().getThemeManager().removePropertyChangeListener(fontChangeListener);
338 memory.deregisterCellModifiedListener(memListener);