private JPanel buildMainPanel() {
		JPanel panel = new JPanel(new BorderLayout());
		createTableModel();
		TableChooserDialogPanel tablePanel = new TableChooserDialogPanel(model);
		tablePanel.getAccessibleContext().setAccessibleName("Table Dialog");

		table = tablePanel.getTable();
		table.setAccessibleNamePrefix("Chooser");
		table.setToolTipText("Table of choices for " + getTitle());

		GoToService goToService = tool.getService(GoToService.class);
		if (goToService != null) {
			navigatable = navigatable == null ? goToService.getDefaultNavigatable() : navigatable;
			navigatable.addNavigatableListener(this);
			table.installNavigation(tool, navigatable);
		}
		table.getSelectionModel()
				.addListSelectionListener(e -> setOkEnabled(table.getSelectedRowCount() > 0));

		GhidraTableFilterPanel<AddressableRowObject> filterPanel =
			new GhidraTableFilterPanel<>(table, model);
		filterPanel.getAccessibleContext().setAccessibleName("Dialog");
		panel.add(tablePanel, BorderLayout.CENTER);
		panel.add(filterPanel, BorderLayout.SOUTH);
		panel.getAccessibleContext().setAccessibleName("Table Chooser");
		return panel;
	}
Dialog to show a table of items.  If the dialog is constructed with a non-null
{@link TableChooserExecutor}, then a button will be placed in the dialog, allowing the user
to perform the action defined by the executor.

<p>Each button press will use the selected items as the items to be processed.  While the
items are scheduled to be processed, they will still be in the table, painted light gray.
Attempting to reschedule any of these pending items will have no effect.   Each time the
button is pressed, a new {@link SwingWorker} is created, which will put the processing into
a background thread.   Further, by using multiple workers, the work will be performed in
parallel.
Ghidra/Features/Base/src/main/java/ghidra/app/tablechooser/TableChooserDialog.java