MemoryEditor now actually works
[Mograsim.git] / plugins / net.mograsim.plugin.core / src / net / mograsim / plugin / editors / MemoryEditor.java
1 package net.mograsim.plugin.editors;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.math.BigInteger;
6
7 import org.eclipse.core.resources.IFile;
8 import org.eclipse.core.runtime.CoreException;
9 import org.eclipse.core.runtime.IProgressMonitor;
10 import org.eclipse.jface.util.SafeRunnable;
11 import org.eclipse.jface.viewers.TableViewerColumn;
12 import org.eclipse.swt.SWT;
13 import org.eclipse.swt.layout.GridData;
14 import org.eclipse.swt.layout.GridLayout;
15 import org.eclipse.swt.widgets.Button;
16 import org.eclipse.swt.widgets.Composite;
17 import org.eclipse.swt.widgets.Label;
18 import org.eclipse.swt.widgets.Table;
19 import org.eclipse.swt.widgets.TableColumn;
20 import org.eclipse.swt.widgets.Text;
21 import org.eclipse.ui.IEditorInput;
22 import org.eclipse.ui.IEditorSite;
23 import org.eclipse.ui.IFileEditorInput;
24 import org.eclipse.ui.PartInitException;
25 import org.eclipse.ui.part.EditorPart;
26
27 import net.mograsim.machine.MainMemory;
28 import net.mograsim.machine.MainMemoryDefinition;
29 import net.mograsim.machine.Memory.MemoryCellModifiedListener;
30 import net.mograsim.machine.mi.MicroInstructionMemoryParseException;
31 import net.mograsim.machine.standard.memory.MainMemoryParser;
32 import net.mograsim.plugin.asm.AsmNumberUtil;
33 import net.mograsim.plugin.nature.MachineContext;
34 import net.mograsim.plugin.nature.ProjectMachineContext;
35 import net.mograsim.plugin.tables.AddressLabelProvider;
36 import net.mograsim.plugin.tables.DisplaySettings;
37 import net.mograsim.plugin.tables.LazyTableViewer;
38 import net.mograsim.plugin.tables.NumberColumnLabelProvider;
39 import net.mograsim.plugin.tables.RadixSelector;
40 import net.mograsim.plugin.tables.memory.MemoryCellEditingSupport;
41 import net.mograsim.plugin.tables.memory.MemoryTableContentProvider;
42 import net.mograsim.plugin.tables.memory.MemoryTableRow;
43 import net.mograsim.plugin.tables.memory.NumberVerifyListener;
44
45 public class MemoryEditor extends EditorPart
46 {
47         private MachineContext context;
48
49         private MainMemory memory;
50
51         private LazyTableViewer viewer;
52         private MemoryTableContentProvider provider;
53         private DisplaySettings displaySettings;
54
55         private boolean dirty;
56
57         private final MemoryCellModifiedListener memListener;
58
59         public MemoryEditor()
60         {
61                 memListener = this::cellModified;
62         }
63
64         @Override
65         public void createPartControl(Composite parent)
66         {
67                 provider = new MemoryTableContentProvider();
68                 displaySettings = new DisplaySettings();
69
70                 parent.setLayout(new GridLayout(7, false));
71
72                 createHeader(parent);
73                 createViewer(parent);
74
75                 displaySettings.addObserver(() -> viewer.refresh());
76         }
77
78         @SuppressWarnings("unused") // RadixSelector and exceptions
79         private void createHeader(Composite parent)
80         {
81                 Label fromLabel = new Label(parent, SWT.NONE);
82                 fromLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
83                 fromLabel.setText("Address: ");
84
85                 Text fromText = new Text(parent, SWT.BORDER);
86                 fromText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
87                 NumberVerifyListener vl = new NumberVerifyListener();
88                 fromText.addVerifyListener(vl);
89                 fromText.setText("0");
90                 fromText.addModifyListener(e ->
91                 {
92                         try
93                         {
94                                 provider.setLowerBound(AsmNumberUtil.valueOf(fromText.getText()).longValue());
95                                 viewer.refresh();
96                         }
97                         catch (NumberFormatException x)
98                         {
99                                 // Nothing to do here
100                         }
101                 });
102                 Label amountLabel = new Label(parent, SWT.NONE);
103                 amountLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
104                 amountLabel.setText("Number of cells: ");
105                 Text amountText = new Text(parent, SWT.BORDER);
106                 amountText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
107                 amountText.addVerifyListener(vl);
108                 amountText.setText("0");
109                 amountText.addModifyListener(e ->
110                 {
111                         try
112                         {
113                                 provider.setAmount(AsmNumberUtil.valueOf(amountText.getText()).intValue());
114                                 viewer.refresh();
115                         }
116                         catch (NumberFormatException x)
117                         {
118                                 // Nothing to do here
119                         }
120                 });
121                 new RadixSelector(parent, displaySettings);
122
123                 addActivationButton(parent);
124         }
125
126         private void addActivationButton(Composite parent)
127         {
128                 Button activationButton = new Button(parent, SWT.PUSH);
129                 activationButton.setText("Set Active");
130                 activationButton.addListener(SWT.Selection, e -> context.getActiveMachine().ifPresent(m -> m.getMainMemory().bind(memory)));
131         }
132
133         private void createViewer(Composite parent)
134         {
135                 viewer = new LazyTableViewer(parent, SWT.FULL_SELECTION | SWT.BORDER | SWT.VIRTUAL);
136                 createColumns();
137                 Table table = viewer.getTable();
138                 table.setHeaderVisible(true);
139                 table.setLinesVisible(true);
140                 viewer.setUseHashlookup(true);
141                 viewer.setContentProvider(provider);
142                 getSite().setSelectionProvider(viewer);// TODO what does this?
143                 viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 7, 1));
144                 if (memory != null)
145                         viewer.setInput(memory);
146         }
147
148         private void createColumns()
149         {
150                 TableViewerColumn addrCol = createTableViewerColumn("Address", 100);
151                 addrCol.setLabelProvider(new AddressLabelProvider());
152
153                 TableViewerColumn dataCol = createTableViewerColumn("Data", 100);
154                 dataCol.setLabelProvider(new NumberColumnLabelProvider(displaySettings)
155                 {
156                         @Override
157                         public BigInteger getAsBigInteger(Object element)
158                         {
159                                 MemoryTableRow row = (MemoryTableRow) element;
160                                 return row.getMemory().getCellAsBigInteger(row.address);
161                         }
162
163                         @Override
164                         public int getBitLength(Object element)
165                         {
166                                 return ((MemoryTableRow) element).getMemory().getDefinition().getCellWidth();
167                         }
168                 });
169                 dataCol.setEditingSupport(new MemoryCellEditingSupport(viewer, displaySettings));
170         }
171
172         private TableViewerColumn createTableViewerColumn(String title, int width)
173         {
174                 TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.NONE);
175                 TableColumn column = viewerColumn.getColumn();
176                 column.setText(title);
177                 column.setWidth(width);
178                 column.setResizable(true);
179                 column.setMoveable(false);
180                 return viewerColumn;
181         }
182
183         @Override
184         public void init(IEditorSite site, IEditorInput input) throws PartInitException
185         {
186                 if (input instanceof IFileEditorInput)
187                 {
188                         IFileEditorInput fileInput = (IFileEditorInput) input;
189                         context = ProjectMachineContext.getMachineContextOf(fileInput.getFile().getProject());
190                         context.activateMachine();
191
192                         setPartName(fileInput.getName());
193                         try
194                         {
195                                 open(fileInput.getFile());
196                         }
197                         catch (Exception e)
198                         {
199                                 throw new PartInitException("Failed to read input!", e);
200                         }
201                 } else
202                         throw new IllegalArgumentException("MemoryEditor can only be used with Files");
203
204                 setSite(site);
205                 setInput(input);
206         }
207
208         @Override
209         public void doSave(IProgressMonitor monitor)
210         {
211                 IEditorInput input = getEditorInput();
212                 if (input instanceof IFileEditorInput)
213                         SafeRunnable.getRunner().run(() -> save(((IFileEditorInput) input).getFile(), monitor));
214         }
215
216         private void save(IFile file, IProgressMonitor monitor) throws CoreException, IOException
217         {
218                 if (memory == null)
219                 {
220                         throw new MicroInstructionMemoryParseException("Failed to write MainMemory to File. No MainMemory assigned.");
221                 }
222                 try (InputStream toWrite = MainMemoryParser.write(memory))
223                 {
224                         file.setContents(toWrite, 0, monitor);
225                         setDirty(false);
226                 }
227         }
228
229         private void open(IFile file) throws IOException, CoreException
230         {
231                 MainMemoryDefinition memDef = context.getMachineDefinition()
232                                 .orElseThrow(() -> new MicroInstructionMemoryParseException("No MachineDefinition assigned!")).getMainMemoryDefinition();
233                 memory = MainMemoryParser.parseMemory(memDef, file.getContents());
234                 memory.registerCellModifiedListener(memListener);
235                 if (viewer != null)
236                         viewer.setInput(memory);
237         }
238
239         private void cellModified(@SuppressWarnings("unused") long address)
240         {
241                 setDirty(true);
242         }
243
244         private void setDirty(boolean newDirty)
245         {
246                 dirty = newDirty;
247                 firePropertyChange(PROP_DIRTY);
248         }
249
250         @Override
251         public void doSaveAs()
252         {
253                 throw new UnsupportedOperationException();
254         }
255
256         @Override
257         public boolean isDirty()
258         {
259                 return dirty;
260         }
261
262         @Override
263         public boolean isSaveAsAllowed()
264         {
265                 return false;
266         }
267
268         @Override
269         public void setFocus()
270         {
271                 viewer.getTable().setFocus();
272         }
273
274         @Override
275         public void dispose()
276         {
277                 if (memory != null)
278                         memory.deregisterCellModifiedListener(memListener);
279                 super.dispose();
280         }
281 }