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