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