InstructionView now guesses the width of columns better
[Mograsim.git] / plugins / net.mograsim.plugin.core / src / net / mograsim / plugin / tables / mi / InstructionView.java
1 package net.mograsim.plugin.tables.mi;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.util.Arrays;
6 import java.util.Optional;
7
8 import org.eclipse.jface.viewers.ColumnLabelProvider;
9 import org.eclipse.jface.viewers.EditingSupport;
10 import org.eclipse.jface.viewers.TableViewerColumn;
11 import org.eclipse.swt.SWT;
12 import org.eclipse.swt.layout.GridData;
13 import org.eclipse.swt.layout.GridLayout;
14 import org.eclipse.swt.widgets.Composite;
15 import org.eclipse.swt.widgets.FileDialog;
16 import org.eclipse.swt.widgets.Table;
17 import org.eclipse.swt.widgets.TableColumn;
18 import org.eclipse.ui.part.ViewPart;
19
20 import net.mograsim.machine.Machine;
21 import net.mograsim.machine.mi.MicroInstructionDefinition;
22 import net.mograsim.machine.mi.MicroInstructionMemory;
23 import net.mograsim.machine.mi.MicroInstructionMemoryParseException;
24 import net.mograsim.machine.mi.MicroInstructionMemoryParser;
25 import net.mograsim.machine.mi.parameters.MnemonicFamily;
26 import net.mograsim.machine.mi.parameters.ParameterClassification;
27 import net.mograsim.plugin.MachineContext;
28 import net.mograsim.plugin.MachineContext.ContextObserver;
29 import net.mograsim.plugin.tables.AddressLabelProvider;
30 import net.mograsim.plugin.tables.DisplaySettings;
31 import net.mograsim.plugin.tables.LazyTableViewer;
32 import net.mograsim.plugin.tables.RadixSelector;
33 import net.mograsim.plugin.util.DropDownMenu;
34 import net.mograsim.plugin.util.DropDownMenu.DropDownEntry;
35
36 public class InstructionView extends ViewPart implements ContextObserver
37 {
38         private String saveLoc = null;
39         private LazyTableViewer viewer;
40         private TableViewerColumn[] columns = new TableViewerColumn[0];
41         private MicroInstructionDefinition miDef;
42         private MicroInstructionMemory memory;
43         private DisplaySettings displaySettings;
44         private InstructionTableContentProvider provider;
45         private int highlighted = 0;
46
47         @SuppressWarnings("unused")
48         @Override
49         public void createPartControl(Composite parent)
50         {
51                 provider = new InstructionTableContentProvider();
52                 GridLayout layout = new GridLayout(3, false);
53                 setupMenuButtons(parent);
54
55                 displaySettings = new DisplaySettings();
56                 new RadixSelector(parent, displaySettings);
57
58                 parent.setLayout(layout);
59                 viewer = new LazyTableViewer(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER | SWT.VIRTUAL);
60
61                 Table table = viewer.getTable();
62                 table.setHeaderVisible(true);
63                 table.setLinesVisible(true);
64                 viewer.setUseHashlookup(true);
65                 viewer.setContentProvider(provider);
66                 getSite().setSelectionProvider(viewer);
67
68                 GridData viewerData = new GridData(GridData.GRAB_HORIZONTAL | GridData.GRAB_VERTICAL | GridData.FILL_BOTH);
69                 viewerData.horizontalSpan = 3;
70                 viewer.getTable().setLayoutData(viewerData);
71
72                 displaySettings.addObserver(() -> viewer.refresh());
73                 MachineContext.getInstance().registerObserver(this);
74                 setMachine(Optional.ofNullable(MachineContext.getInstance().getMachine()));
75         }
76
77         public void highlight(int index)
78         {
79                 viewer.highlightRow(highlighted, false);
80                 viewer.highlightRow(index, true);
81                 viewer.getTable().setTopIndex(index);
82         }
83
84         @SuppressWarnings("unused")
85         private void setupMenuButtons(Composite parent)
86         {
87                 DropDownEntry open = new DropDownEntry("Open", (e) ->
88                 {
89                         FileDialog d = new FileDialog(parent.getShell(), SWT.NONE);
90                         d.open();
91                         String filename = d.getFileName();
92                         if (!filename.equals(""))
93                                 open(d.getFilterPath() + File.separator + filename);
94                 });
95
96                 DropDownEntry save = new DropDownEntry("Save", (e) ->
97                 {
98                         if (saveLoc == null)
99                                 openSaveAsDialog(parent);
100                         save(saveLoc);
101                 });
102                 DropDownEntry saveAs = new DropDownEntry("SaveAs", (e) ->
103                 {
104                         openSaveAsDialog(parent);
105                         save(saveLoc);
106                 });
107                 new DropDownMenu(parent, "File", open, save, saveAs);
108         }
109
110         private void openSaveAsDialog(Composite parent)
111         {
112                 FileDialog d = new FileDialog(parent.getShell(), SWT.SAVE);
113                 d.open();
114                 String filename = d.getFileName();
115                 if (!filename.equals(""))
116                         saveLoc = d.getFilterPath() + File.separator + filename;
117         }
118
119         public void bindMicroInstructionMemory(MicroInstructionMemory memory)
120         {
121                 deleteColumns();
122                 this.memory = memory;
123                 viewer.setInput(memory);
124                 this.miDef = memory.getDefinition().getMicroInstructionDefinition();
125                 createColumns();
126         }
127
128         private void deleteColumns()
129         {
130                 for (TableViewerColumn col : columns)
131                         col.getColumn().dispose();
132         }
133
134         private void createColumns()
135         {
136                 int size = miDef.size();
137                 columns = new TableViewerColumn[size + 1];
138
139                 TableViewerColumn col = createTableViewerColumn("Address", generateLongestHexStrings(12));
140                 columns[0] = col;
141                 col.setLabelProvider(new AddressLabelProvider());
142
143                 int bit = miDef.sizeInBits();
144                 ParameterClassification[] classes = miDef.getParameterClassifications();
145
146                 for (int i = 0; i < size; i++)
147                 {
148                         int startBit = bit - 1;
149                         int endBit = bit = bit - classes[i].getExpectedBits();
150                         String name = startBit == endBit ? Integer.toString(startBit) : startBit + "..." + endBit;
151
152                         String[] longestPossibleContents;
153                         switch (classes[i].getExpectedType())
154                         {
155                         case INTEGER_IMMEDIATE:
156                                 longestPossibleContents = generateLongestHexStrings(classes[i].getExpectedBits());
157                                 break;
158                         case BOOLEAN_IMMEDIATE:
159                         case MNEMONIC:
160                                 longestPossibleContents = ((MnemonicFamily) classes[i]).getStringValues();
161                                 break;
162                         default:
163                                 longestPossibleContents = new String[0];
164                                 break;
165                         }
166
167                         col = createTableViewerColumn(name, longestPossibleContents);
168                         columns[i + 1] = col;
169                         createEditingAndLabel(col, miDef, i);
170                 }
171         }
172
173         private static final String[] HEX_DIGITS = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" };
174
175         private static String[] generateLongestHexStrings(int bitWidth)
176         {
177                 return Arrays.stream(HEX_DIGITS).map(s -> "0x" + s.repeat((bitWidth + 3) / 4)).toArray(String[]::new);
178         }
179
180         private void createEditingAndLabel(TableViewerColumn col, MicroInstructionDefinition miDef, int index)
181         {
182                 ParameterClassification parameterClassification = miDef.getParameterClassifications()[index];
183                 EditingSupport support;
184                 ColumnLabelProvider provider;
185                 switch (parameterClassification.getExpectedType())
186                 {
187                 case BOOLEAN_IMMEDIATE:
188                         support = new BooleanEditingSupport(viewer, miDef, index);
189                         provider = new ParameterLabelProvider(index);
190                         break;
191                 case INTEGER_IMMEDIATE:
192                         support = new IntegerEditingSupport(viewer, miDef, index, displaySettings, this.provider);
193                         provider = new IntegerColumnLabelProvider(displaySettings, index);
194                         break;
195                 case MNEMONIC:
196                         support = new MnemonicEditingSupport(viewer, miDef, index, this.provider);
197                         provider = new ParameterLabelProvider(index);
198                         break;
199                 default:
200                         throw new IllegalStateException(
201                                         "Unable to create EditingSupport for unknown ParameterType " + parameterClassification.getExpectedType());
202                 }
203                 col.setEditingSupport(support);
204                 col.setLabelProvider(provider);
205                 col.getColumn().setToolTipText(miDef.getParameterDescription(index).orElse(""));
206         }
207
208         private TableViewerColumn createTableViewerColumn(String title, String... longestPossibleContents)
209         {
210                 TableViewerColumn viewerColumn = new TableViewerColumn(viewer, SWT.NONE);
211                 TableColumn column = viewerColumn.getColumn();
212                 int maxWidth = 0;
213                 for (String s : longestPossibleContents)
214                 {
215                         column.setText(s);
216                         column.pack();
217                         if (column.getWidth() > maxWidth)
218                                 maxWidth = column.getWidth();
219                 }
220                 column.setText(title);
221                 column.pack();
222                 if (column.getWidth() < maxWidth)
223                         column.setWidth(maxWidth);
224                 column.setResizable(true);
225                 column.setMoveable(false);
226                 return viewerColumn;
227         }
228
229         private void open(String file)
230         {
231                 if (miDef == null)
232                 {
233                         System.err.println("Failed to parse MicroprogrammingMemory from File. No MicroInstructionDefinition assigned.");
234                         return;
235                 }
236                 try
237                 {
238                         MicroInstructionMemoryParser.parseMemory(memory, file);
239                         viewer.refresh();
240                         saveLoc = file;
241                 }
242                 catch (IOException | MicroInstructionMemoryParseException e)
243                 {
244                         e.printStackTrace();
245                 }
246         }
247
248         private void save(String file)
249         {
250                 if (memory == null)
251                 {
252                         System.err.println("Failed to write MicroprogrammingMemory to File. No MicroprogrammingMemory assigned.");
253                 }
254                 if (saveLoc != null)
255                 {
256                         try
257                         {
258                                 MicroInstructionMemoryParser.write(memory, file);
259                         }
260                         catch (IOException e)
261                         {
262                                 e.printStackTrace();
263                         }
264                 }
265         }
266
267         @Override
268         public void setFocus()
269         {
270                 viewer.getControl().setFocus();
271         }
272
273         @Override
274         public void setMachine(Optional<Machine> machine)
275         {
276                 if (machine.isPresent())
277                 {
278                         Machine actualMachine = machine.get();
279                         bindMicroInstructionMemory(actualMachine.getMicroInstructionMemory());
280                 }
281         }
282 }