Moved TODOs from getting_started.md to the source code
[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 import java.util.Collection;
7 import java.util.HashSet;
8
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.Composite;
20 import org.eclipse.swt.widgets.Control;
21 import org.eclipse.swt.widgets.Label;
22 import org.eclipse.swt.widgets.Table;
23 import org.eclipse.swt.widgets.TableColumn;
24 import org.eclipse.swt.widgets.Text;
25 import org.eclipse.ui.IEditorInput;
26 import org.eclipse.ui.IEditorSite;
27 import org.eclipse.ui.IFileEditorInput;
28 import org.eclipse.ui.PartInitException;
29 import org.eclipse.ui.part.EditorPart;
30 import org.eclipse.ui.themes.ITheme;
31 import org.eclipse.ui.themes.IThemeManager;
32
33 import net.mograsim.machine.MainMemory;
34 import net.mograsim.machine.MainMemoryDefinition;
35 import net.mograsim.machine.Memory.MemoryCellModifiedListener;
36 import net.mograsim.machine.mi.MicroInstructionMemoryParseException;
37 import net.mograsim.machine.standard.memory.MainMemoryParser;
38 import net.mograsim.plugin.asm.AsmNumberUtil;
39 import net.mograsim.plugin.nature.MachineContext;
40 import net.mograsim.plugin.nature.ProjectMachineContext;
41 import net.mograsim.plugin.tables.AddressLabelProvider;
42 import net.mograsim.plugin.tables.DisplaySettings;
43 import net.mograsim.plugin.tables.LazyTableViewer;
44 import net.mograsim.plugin.tables.NumberColumnLabelProvider;
45 import net.mograsim.plugin.tables.RadixSelector;
46 import net.mograsim.plugin.tables.memory.MemoryCellEditingSupport;
47 import net.mograsim.plugin.tables.memory.MemoryTableContentProvider;
48 import net.mograsim.plugin.tables.memory.MemoryTableRow;
49 import net.mograsim.plugin.tables.memory.NumberVerifyListener;
50
51 public class MemoryEditor extends EditorPart
52 {
53         private MachineContext context;
54
55         private MainMemory memory;
56
57         private LazyTableViewer viewer;
58         private MemoryTableContentProvider provider;
59         private DisplaySettings displaySettings;
60
61         private Collection<Control> fontDependent = new HashSet<>();
62
63         private boolean dirty;
64
65         private final MemoryCellModifiedListener memListener;
66
67         private final static String font = "net.mograsim.plugin.memory.table_font";
68         private IPropertyChangeListener fontChangeListener;
69
70         public MemoryEditor()
71         {
72                 memListener = this::cellModified;
73         }
74
75         @Override
76         public void createPartControl(Composite parent)
77         {
78                 provider = new MemoryTableContentProvider();
79                 displaySettings = new DisplaySettings();
80
81                 parent.setLayout(new GridLayout(7, false));
82
83                 createHeader(parent);
84                 createViewer(parent);
85
86                 displaySettings.addObserver(() -> viewer.refresh());
87         }
88
89         @SuppressWarnings("unused") // RadixSelector and exceptions
90         private void createHeader(Composite parent)
91         {
92                 Label fromLabel = new Label(parent, SWT.NONE);
93                 fromLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
94                 fromLabel.setText("Address: ");
95
96                 Text fromText = new Text(parent, SWT.BORDER);
97                 fromText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
98                 NumberVerifyListener vl = new NumberVerifyListener();
99                 fromText.addVerifyListener(vl);
100                 fromText.setText("0");
101                 fromText.addModifyListener(e ->
102                 {
103                         try
104                         {
105                                 provider.setLowerBound(AsmNumberUtil.valueOf(fromText.getText()).longValue());
106                                 viewer.refresh();
107                         }
108                         catch (NumberFormatException x)
109                         {
110                                 // Nothing to do here
111                         }
112                 });
113                 Label amountLabel = new Label(parent, SWT.NONE);
114                 amountLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
115                 amountLabel.setText("Number of cells: ");
116                 Text amountText = new Text(parent, SWT.BORDER);
117                 amountText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
118                 amountText.addVerifyListener(vl);
119                 amountText.addModifyListener(e ->
120                 {
121                         try
122                         {
123                                 if (provider != null)
124                                         provider.setAmount(AsmNumberUtil.valueOf(amountText.getText()).intValue());
125                                 if (viewer != null)
126                                         viewer.refresh();
127                         }
128                         catch (NumberFormatException x)
129                         {
130                                 // Nothing to do here
131                         }
132                 });
133                 amountText.setText("100");// do this after registering the ModifyListener
134                 new RadixSelector(parent, displaySettings);
135         }
136
137         private void createViewer(Composite parent)
138         {
139                 viewer = new LazyTableViewer(parent, SWT.FULL_SELECTION | SWT.BORDER | SWT.VIRTUAL);
140                 fontDependent.add(viewer.getTable());
141                 createColumns();
142                 Table table = viewer.getTable();
143                 table.setHeaderVisible(true);
144                 table.setLinesVisible(true);
145                 viewer.setUseHashlookup(true);
146                 viewer.setContentProvider(provider);
147                 getSite().setSelectionProvider(viewer);// TODO what does this do?
148                 viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 7, 1));
149
150                 IThemeManager themeManager = getSite().getWorkbenchWindow().getWorkbench().getThemeManager();
151                 themeManager.addPropertyChangeListener(fontChangeListener = (e) ->
152                 {
153                         if (IThemeManager.CHANGE_CURRENT_THEME.equals(e.getProperty()) || font.equals(e.getProperty()))
154                         {
155                                 updateFont(themeManager.getCurrentTheme());
156                                 viewer.refresh();
157                         }
158                 });
159                 updateFont(themeManager.getCurrentTheme());
160
161                 if (memory != null)
162                         viewer.setInput(memory);
163         }
164
165         private void updateFont(ITheme theme)
166         {
167                 Font newFont = theme.getFontRegistry().get(font);
168                 // TODO: This is a quick fix! Still have to figure out why the CellEditors do not get the appropriate Font on their own
169                 fontDependent.forEach(c -> c.setFont(newFont));
170         }
171
172         private void createColumns()
173         {
174                 TableViewerColumn addrCol = createTableViewerColumn("Address", 100);
175                 addrCol.setLabelProvider(new AddressLabelProvider());
176
177                 TableViewerColumn dataCol = createTableViewerColumn("Data", 100);
178                 dataCol.setLabelProvider(new NumberColumnLabelProvider(displaySettings)
179                 {
180                         @Override
181                         public BigInteger getAsBigInteger(Object element)
182                         {
183                                 MemoryTableRow row = (MemoryTableRow) element;
184                                 return row.getMemory().getCellAsBigInteger(row.address);
185                         }
186
187                         @Override
188                         public int getBitLength(Object element)
189                         {
190                                 return ((MemoryTableRow) element).getMemory().getDefinition().getCellWidth();
191                         }
192                 });
193                 MemoryCellEditingSupport eSup;
194                 dataCol.setEditingSupport(eSup = new MemoryCellEditingSupport(viewer, displaySettings));
195                 fontDependent.add(eSup.getCellEditorControl());
196         }
197
198         private TableViewerColumn createTableViewerColumn(String title, int width)
199         {
200                 TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.NONE);
201                 TableColumn column = viewerColumn.getColumn();
202                 column.setText(title);
203                 column.setWidth(width);
204                 column.setResizable(true);
205                 column.setMoveable(false);
206                 return viewerColumn;
207         }
208
209         @Override
210         public void init(IEditorSite site, IEditorInput input) throws PartInitException
211         {
212                 if (input instanceof IFileEditorInput)
213                 {
214                         IFileEditorInput fileInput = (IFileEditorInput) input;
215                         context = ProjectMachineContext.getMachineContextOf(fileInput.getFile().getProject());
216
217                         setPartName(fileInput.getName());
218                         try
219                         {
220                                 open(fileInput.getFile());
221                         }
222                         catch (Exception e)
223                         {
224                                 throw new PartInitException("Failed to read input!", e);
225                         }
226                 } else
227                         throw new IllegalArgumentException("MemoryEditor can only be used with Files");
228
229                 setSite(site);
230                 setInput(input);
231         }
232
233         @Override
234         public void doSave(IProgressMonitor monitor)
235         {
236                 IEditorInput input = getEditorInput();
237                 if (input instanceof IFileEditorInput)
238                         SafeRunnable.getRunner().run(() -> save(((IFileEditorInput) input).getFile(), monitor));
239         }
240
241         private void save(IFile file, IProgressMonitor monitor) throws CoreException, IOException
242         {
243                 if (memory == null)
244                 {
245                         throw new MicroInstructionMemoryParseException("Failed to write MainMemory to File. No MainMemory assigned.");
246                 }
247                 try (InputStream toWrite = MainMemoryParser.write(memory))
248                 {
249                         file.setContents(toWrite, 0, monitor);
250                         setDirty(false);
251                 }
252         }
253
254         private void open(IFile file) throws IOException, CoreException
255         {
256                 MainMemoryDefinition memDef = context.getMachineDefinition()
257                                 .orElseThrow(() -> new MicroInstructionMemoryParseException("No MachineDefinition assigned!")).getMainMemoryDefinition();
258                 memory = MainMemoryParser.parseMemory(memDef, file.getContents());
259                 memory.registerCellModifiedListener(memListener);
260                 if (viewer != null)
261                         viewer.setInput(memory);
262         }
263
264         private void cellModified(@SuppressWarnings("unused") long address)
265         {
266                 setDirty(true);
267         }
268
269         private void setDirty(boolean newDirty)
270         {
271                 dirty = newDirty;
272                 firePropertyChange(PROP_DIRTY);
273         }
274
275         @Override
276         public void doSaveAs()
277         {
278                 throw new UnsupportedOperationException();
279         }
280
281         @Override
282         public boolean isDirty()
283         {
284                 return dirty;
285         }
286
287         @Override
288         public boolean isSaveAsAllowed()
289         {
290                 return false;
291         }
292
293         @Override
294         public void setFocus()
295         {
296                 viewer.getTable().setFocus();
297         }
298
299         @Override
300         public void dispose()
301         {
302                 getSite().getWorkbenchWindow().getWorkbench().getThemeManager().removePropertyChangeListener(fontChangeListener);
303                 if (memory != null)
304                         memory.deregisterCellModifiedListener(memListener);
305                 super.dispose();
306         }
307 }