Fixed a missing space in a Am2900Teaching's description
[Mograsim.git] / plugins / net.mograsim.plugin.core / src / net / mograsim / plugin / editors / MemoryEditor.java
index 2b02852..0186780 100644 (file)
@@ -1,19 +1,26 @@
 package net.mograsim.plugin.editors;
 
-import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
 import java.math.BigInteger;
+import java.util.Collection;
+import java.util.HashSet;
 
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jface.util.IPropertyChangeListener;
 import org.eclipse.jface.util.SafeRunnable;
 import org.eclipse.jface.viewers.TableViewerColumn;
 import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Font;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Button;
 import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.ScrollBar;
 import org.eclipse.swt.widgets.Table;
 import org.eclipse.swt.widgets.TableColumn;
 import org.eclipse.swt.widgets.Text;
@@ -22,10 +29,14 @@ import org.eclipse.ui.IEditorSite;
 import org.eclipse.ui.IFileEditorInput;
 import org.eclipse.ui.PartInitException;
 import org.eclipse.ui.part.EditorPart;
+import org.eclipse.ui.themes.ITheme;
+import org.eclipse.ui.themes.IThemeManager;
 
 import net.mograsim.machine.MainMemory;
+import net.mograsim.machine.MainMemoryDefinition;
+import net.mograsim.machine.Memory.MemoryCellModifiedListener;
 import net.mograsim.machine.mi.MicroInstructionMemoryParseException;
-import net.mograsim.machine.standard.memory.WordAddressableMemory;
+import net.mograsim.machine.standard.memory.MainMemoryParser;
 import net.mograsim.plugin.asm.AsmNumberUtil;
 import net.mograsim.plugin.nature.MachineContext;
 import net.mograsim.plugin.nature.ProjectMachineContext;
@@ -49,14 +60,27 @@ public class MemoryEditor extends EditorPart
        private MemoryTableContentProvider provider;
        private DisplaySettings displaySettings;
 
+       private Collection<Control> fontDependent = new HashSet<>();
+
+       private boolean dirty;
+
+       private final MemoryCellModifiedListener memListener;
+
+       private final static String font = "net.mograsim.plugin.memory.table_font";
+       private IPropertyChangeListener fontChangeListener;
+
+       public MemoryEditor()
+       {
+               memListener = this::cellModified;
+       }
+
        @Override
        public void createPartControl(Composite parent)
        {
                provider = new MemoryTableContentProvider();
                displaySettings = new DisplaySettings();
 
-               parent.setLayout(new GridLayout(7, false));
-
+               parent.setLayout(new GridLayout(8, false));
                createHeader(parent);
                createViewer(parent);
 
@@ -66,73 +90,111 @@ public class MemoryEditor extends EditorPart
        @SuppressWarnings("unused") // RadixSelector and exceptions
        private void createHeader(Composite parent)
        {
-               Label fromLabel = new Label(parent, SWT.NONE);
-               fromLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
-               fromLabel.setText("Address: ");
-
-               Text fromText = new Text(parent, SWT.BORDER);
-               fromText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
+               Text gotoText = new Text(parent, SWT.BORDER);
+               gotoText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
                NumberVerifyListener vl = new NumberVerifyListener();
-               fromText.addVerifyListener(vl);
-               fromText.setText("0");
-               fromText.addModifyListener(e ->
+               gotoText.addVerifyListener(vl);
+               gotoText.setText("0");
+
+               Runnable gotoAction = () ->
                {
                        try
                        {
-                               provider.setLowerBound(AsmNumberUtil.valueOf(fromText.getText()).longValue());
-                               viewer.refresh();
+                               loadAround((int) (AsmNumberUtil.valueOf(gotoText.getText()).longValue() - provider.getLowerBound()));
+                               viewer.getTable().deselectAll();
                        }
                        catch (NumberFormatException x)
                        {
                                // Nothing to do here
                        }
+               };
+
+               gotoText.addTraverseListener((e) ->
+               {
+                       if (e.detail == SWT.TRAVERSE_RETURN)
+                               gotoAction.run();
                });
-               Label amountLabel = new Label(parent, SWT.NONE);
-               amountLabel.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
-               amountLabel.setText("Number of cells: ");
-               Text amountText = new Text(parent, SWT.BORDER);
-               amountText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
-               amountText.addVerifyListener(vl);
-               amountText.setText("0");
-               amountText.addModifyListener(e ->
+
+               Button gotoButton = new Button(parent, SWT.PUSH);
+               gotoButton.setText("Go to");
+               gotoButton.addListener(SWT.Selection, e ->
                {
-                       try
-                       {
-                               provider.setAmount(AsmNumberUtil.valueOf(amountText.getText()).intValue());
-                               viewer.refresh();
-                       }
-                       catch (NumberFormatException x)
-                       {
-                               // Nothing to do here
-                       }
+                       gotoAction.run();
                });
-               new RadixSelector(parent, displaySettings);
 
-               addActivationButton(parent);
-       }
+               new Label(parent, SWT.NONE).setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
 
-       private void addActivationButton(Composite parent)
-       {
-               Button activationButton = new Button(parent, SWT.PUSH);
-               activationButton.setText("Set Active");
-               activationButton.addListener(SWT.Selection, e -> context.getActiveMachine().ifPresent(m -> m.getMainMemory().bind(memory)));
+               new RadixSelector(parent, displaySettings);
        }
 
        private void createViewer(Composite parent)
        {
                viewer = new LazyTableViewer(parent, SWT.FULL_SELECTION | SWT.BORDER | SWT.VIRTUAL);
+               fontDependent.add(viewer.getTable());
                createColumns();
                Table table = viewer.getTable();
                table.setHeaderVisible(true);
                table.setLinesVisible(true);
                viewer.setUseHashlookup(true);
                viewer.setContentProvider(provider);
-               getSite().setSelectionProvider(viewer);// TODO what does this?
-               viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 7, 1));
+               getSite().setSelectionProvider(viewer);
+               viewer.getControl().setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 8, 1));
+
+               // TODO: Also support keyboard inputs for flexible scrolling
+               ScrollBar vBar = table.getVerticalBar();
+               vBar.addListener(SWT.Selection, (e) ->
+               {
+                       int sel;
+                       if ((sel = vBar.getSelection()) < vBar.getMinimum() + vBar.getThumb() * 2 || sel > vBar.getMaximum() - vBar.getThumb() * 2)
+                       {
+                               loadAround(table.getTopIndex());
+                               table.deselectAll();
+                       }
+               });
+
+               IThemeManager themeManager = getSite().getWorkbenchWindow().getWorkbench().getThemeManager();
+               themeManager.addPropertyChangeListener(fontChangeListener = (e) ->
+               {
+                       if (IThemeManager.CHANGE_CURRENT_THEME.equals(e.getProperty()) || font.equals(e.getProperty()))
+                       {
+                               updateFont(themeManager.getCurrentTheme());
+                               viewer.refresh();
+                       }
+               });
+               updateFont(themeManager.getCurrentTheme());
+
                if (memory != null)
                        viewer.setInput(memory);
        }
 
+       private void updateFont(ITheme theme)
+       {
+               Font newFont = theme.getFontRegistry().get(font);
+               // TODO: This is a quick fix! Still have to figure out why the CellEditors do not get the appropriate Font on their own
+               fontDependent.forEach(c -> c.setFont(newFont));
+       }
+
+       private void loadAround(int row)
+       {
+               long prevLb = provider.getLowerBound();
+               long address = prevLb + row;
+               long actualLb = Long.max(address - MemoryTableContentProvider.MAX_VISIBLE_ROWS / 2, memory.getDefinition().getMinimalAddress());
+
+               long prevHb = provider.getUpperBound();
+               // +- 1 row is not really important
+               long actualHb = Long.min(address + MemoryTableContentProvider.MAX_VISIBLE_ROWS / 2, memory.getDefinition().getMaximalAddress());
+
+               if (actualLb != prevLb || actualHb != prevHb)
+               {
+                       Table table = viewer.getTable();
+                       provider.setBounds(actualLb, actualHb);
+                       int rowIndex = (int) (address - actualLb);
+                       if (rowIndex >= 0 && rowIndex < table.getItemCount())
+                               table.showItem(table.getItem(rowIndex));
+                       viewer.refresh();
+               }
+       }
+
        private void createColumns()
        {
                TableViewerColumn addrCol = createTableViewerColumn("Address", 100);
@@ -154,7 +216,9 @@ public class MemoryEditor extends EditorPart
                                return ((MemoryTableRow) element).getMemory().getDefinition().getCellWidth();
                        }
                });
-               dataCol.setEditingSupport(new MemoryCellEditingSupport(viewer, displaySettings));
+               MemoryCellEditingSupport eSup;
+               dataCol.setEditingSupport(eSup = new MemoryCellEditingSupport(viewer, displaySettings));
+               fontDependent.add(eSup.getCellEditorControl());
        }
 
        private TableViewerColumn createTableViewerColumn(String title, int width)
@@ -175,10 +239,16 @@ public class MemoryEditor extends EditorPart
                {
                        IFileEditorInput fileInput = (IFileEditorInput) input;
                        context = ProjectMachineContext.getMachineContextOf(fileInput.getFile().getProject());
-                       context.activateMachine();
 
                        setPartName(fileInput.getName());
-                       open(fileInput.getFile());
+                       try
+                       {
+                               open(fileInput.getFile());
+                       }
+                       catch (Exception e)
+                       {
+                               throw new PartInitException("Failed to read input!", e);
+                       }
                } else
                        throw new IllegalArgumentException("MemoryEditor can only be used with Files");
 
@@ -194,20 +264,40 @@ public class MemoryEditor extends EditorPart
                        SafeRunnable.getRunner().run(() -> save(((IFileEditorInput) input).getFile(), monitor));
        }
 
-       private void save(IFile file, IProgressMonitor monitor) throws CoreException
+       private void save(IFile file, IProgressMonitor monitor) throws CoreException, IOException
        {
-               file.setContents(new ByteArrayInputStream("actual contents will go here".getBytes()), 0, monitor);
+               if (memory == null)
+               {
+                       throw new MicroInstructionMemoryParseException("Failed to write MainMemory to File. No MainMemory assigned.");
+               }
+               try (InputStream toWrite = MainMemoryParser.write(memory))
+               {
+                       file.setContents(toWrite, 0, monitor);
+                       setDirty(false);
+               }
        }
 
-       private void open(IFile file)
+       private void open(IFile file) throws IOException, CoreException
        {
-               // TODO actually parse the file
-               memory = new WordAddressableMemory(context.getMachineDefinition()
-                               .orElseThrow(() -> new MicroInstructionMemoryParseException("No MachineDefinition assigned!")).getMainMemoryDefinition());
+               MainMemoryDefinition memDef = context.getMachineDefinition()
+                               .orElseThrow(() -> new MicroInstructionMemoryParseException("No MachineDefinition assigned!")).getMainMemoryDefinition();
+               memory = MainMemoryParser.parseMemory(memDef, file.getContents());
+               memory.registerCellModifiedListener(memListener);
                if (viewer != null)
                        viewer.setInput(memory);
        }
 
+       private void cellModified(@SuppressWarnings("unused") long address)
+       {
+               setDirty(true);
+       }
+
+       private void setDirty(boolean newDirty)
+       {
+               dirty = newDirty;
+               firePropertyChange(PROP_DIRTY);
+       }
+
        @Override
        public void doSaveAs()
        {
@@ -217,8 +307,7 @@ public class MemoryEditor extends EditorPart
        @Override
        public boolean isDirty()
        {
-               // TODO
-               return false;
+               return dirty;
        }
 
        @Override
@@ -236,7 +325,9 @@ public class MemoryEditor extends EditorPart
        @Override
        public void dispose()
        {
-               // TODO Auto-generated method stub
+               getSite().getWorkbenchWindow().getWorkbench().getThemeManager().removePropertyChangeListener(fontChangeListener);
+               if (memory != null)
+                       memory.deregisterCellModifiedListener(memListener);
                super.dispose();
        }
 }
\ No newline at end of file