1 package org.simantics.logging.ui;
3 import java.util.HashMap;
6 import java.util.concurrent.atomic.AtomicReference;
7 import java.util.function.Consumer;
8 import java.util.function.Function;
9 import java.util.function.Predicate;
11 import org.eclipse.jface.dialogs.IDialogConstants;
12 import org.eclipse.jface.dialogs.IDialogSettings;
13 import org.eclipse.jface.dialogs.TrayDialog;
14 import org.eclipse.jface.layout.GridDataFactory;
15 import org.eclipse.jface.layout.GridLayoutFactory;
16 import org.eclipse.jface.layout.TableColumnLayout;
17 import org.eclipse.jface.resource.FontDescriptor;
18 import org.eclipse.jface.resource.JFaceResources;
19 import org.eclipse.jface.viewers.ArrayContentProvider;
20 import org.eclipse.jface.viewers.CellEditor;
21 import org.eclipse.jface.viewers.ColumnLabelProvider;
22 import org.eclipse.jface.viewers.ColumnWeightData;
23 import org.eclipse.jface.viewers.ComboBoxCellEditor;
24 import org.eclipse.jface.viewers.EditingSupport;
25 import org.eclipse.jface.viewers.TableViewer;
26 import org.eclipse.jface.viewers.TableViewerColumn;
27 import org.eclipse.jface.viewers.TextCellEditor;
28 import org.eclipse.jface.viewers.Viewer;
29 import org.eclipse.jface.viewers.ViewerCell;
30 import org.eclipse.jface.viewers.ViewerFilter;
31 import org.eclipse.swt.SWT;
32 import org.eclipse.swt.custom.CCombo;
33 import org.eclipse.swt.graphics.Font;
34 import org.eclipse.swt.widgets.Composite;
35 import org.eclipse.swt.widgets.Control;
36 import org.eclipse.swt.widgets.Display;
37 import org.eclipse.swt.widgets.Shell;
38 import org.eclipse.swt.widgets.Text;
39 import org.simantics.logging.LogConfigurator;
40 import org.simantics.logging.LoggerLevel;
42 public class LoggerManagementDialog extends TrayDialog {
44 private static final String DIALOG = LoggerManagementDialog.class.getSimpleName(); //$NON-NLS-1$
46 private static final String[] LEVELS = {
54 private static final Map<String, Integer> LEVEL_TO_INDEX = new HashMap<>();
57 for (int i = 0; i < LEVELS.length; i++) {
58 LEVEL_TO_INDEX.put(LEVELS[i], i);
63 * ID for Apply buttons.
65 private int APPLY_ID = IDialogConstants.CLIENT_ID + 1;
68 * The label for Apply buttons.
70 private String APPLY_LABEL = JFaceResources.getString("apply"); //$NON-NLS-1$
72 private TableViewer tableViewer;
73 private List<LoggerLevel> configuration;
74 private IDialogSettings dialogBoundsSettings;
76 private Consumer<List<LoggerLevel>> applyFunction;
78 public LoggerManagementDialog(Shell shell, Consumer<List<LoggerLevel>> applyFunction) {
80 reloadConfiguration();
81 this.applyFunction = applyFunction;
83 IDialogSettings settings = Activator.getDefault().getDialogSettings();
84 dialogBoundsSettings = settings.getSection(DIALOG);
85 if (dialogBoundsSettings == null)
86 dialogBoundsSettings = settings.addNewSection(DIALOG);
89 private void reloadConfiguration() {
90 this.configuration = LogConfigurator.listConfiguredLoggers();
94 private void addEmptyRow() {
95 this.configuration.add(new LoggerLevel("", LEVELS[2]));
99 protected IDialogSettings getDialogBoundsSettings() {
100 return dialogBoundsSettings;
104 protected void configureShell(Shell newShell) {
105 super.configureShell(newShell);
106 newShell.setText("Manage Loggers");
107 newShell.setMinimumSize(800, 600);
111 protected boolean isResizable() {
116 protected void createButtonsForButtonBar(Composite parent) {
117 createButton(parent, IDialogConstants.OK_ID, IDialogConstants.OK_LABEL, true);
118 createButton(parent, APPLY_ID, APPLY_LABEL, false);
119 createButton(parent, IDialogConstants.CANCEL_ID, IDialogConstants.CANCEL_LABEL, false);
123 protected void buttonPressed(int buttonId) {
124 if (buttonId == IDialogConstants.CLOSE_ID) {
125 super.buttonPressed(IDialogConstants.CANCEL_ID);
126 } else if (buttonId == APPLY_ID) {
127 if (applyFunction != null) {
128 applyFunction.accept(configuration);
129 reloadConfiguration();
133 super.buttonPressed(buttonId);
138 protected Control createDialogArea(Composite parent) {
139 final Composite composite = (Composite) super.createDialogArea(parent);
140 GridLayoutFactory.fillDefaults().margins(10, 10).numColumns(1).applyTo(composite);
141 GridDataFactory.fillDefaults().grab(true, true).applyTo(composite);
143 Composite tableComposite = new Composite(composite, SWT.NONE);
144 TableColumnLayout tcl = new TableColumnLayout();
145 tableComposite.setLayout(tcl);
146 GridDataFactory.fillDefaults().grab(true, true).applyTo(tableComposite);
148 tableViewer = new TableViewer(tableComposite, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI);
150 Display display = getShell().getDisplay();
153 final Font systemFont = display.getSystemFont();
154 final Font italic = FontDescriptor.createFrom(systemFont).setStyle(SWT.ITALIC).createFont(display);
155 final Font bold = FontDescriptor.createFrom(systemFont).setStyle(SWT.BOLD).createFont(display);
156 tableViewer.getTable().addDisposeListener(e -> {
161 Function<LoggerLevel, Font> fontFunction = l -> {
162 if (l.isLoggerDefined()) {
163 if (l.levelChanged()) {
171 // Column 1: logger name
172 TableViewerColumn column1 = new TableViewerColumn(tableViewer, SWT.NONE);
173 column1.getColumn().setWidth(300);
174 column1.getColumn().setText("Logger");
175 column1.getColumn().setToolTipText("Package Name of Logger");
176 column1.setLabelProvider(new ColumnLabelProvider() {
178 public void update(ViewerCell cell) {
179 LoggerLevel l = (LoggerLevel) cell.getElement();
180 cell.setText(l.getName());
181 cell.setFont(fontFunction.apply(l));
184 column1.setEditingSupport(new EditingSupport(tableViewer) {
185 CellEditor editor = new TextCellEditor(tableViewer.getTable());
187 protected CellEditor getCellEditor(Object element) {
191 protected boolean canEdit(Object element) {
192 LoggerLevel l = (LoggerLevel) element;
193 return !l.isLoggerDefined();
196 protected Object getValue(Object element) {
197 return ((LoggerLevel)element).getName();
200 protected void setValue(Object element, Object value) {
201 LoggerLevel l = (LoggerLevel) element;
202 String s = ((String) value).trim();
204 boolean sameNameExists = !s.isEmpty() && configuration.stream().anyMatch(ll -> ll != l && ll.getName().equals(s));
206 // Prevent providing the same package twice
210 String previousName = l.getName();
213 int index = configuration.indexOf(l);
214 if (index >= 0 && index < (configuration.size()-1)) {
215 configuration.remove(index);
217 } else if (previousName.isEmpty()) {
224 // Column 2: logging level of logger
225 TableViewerColumn column2 = new TableViewerColumn(tableViewer, SWT.NONE);
226 column2.getColumn().setWidth(100);
227 column2.getColumn().setText("Log Level");
228 column2.getColumn().setToolTipText("Logging Level for Package and Subpackages");
229 column2.setLabelProvider(new ColumnLabelProvider() {
231 public void update(ViewerCell cell) {
232 LoggerLevel l = (LoggerLevel) cell.getElement();
233 cell.setText(l.getLevel());
234 cell.setFont(fontFunction.apply(l));
237 column2.setEditingSupport(new EditingSupport(tableViewer) {
239 protected boolean canEdit(Object element) {
243 protected CellEditor getCellEditor(Object element) {
244 return new ComboBoxCellEditor(tableViewer.getTable(), LEVELS, SWT.READ_ONLY) {
246 protected Control createControl(Composite parent) {
247 CCombo combo = (CCombo) super.createControl(parent);
248 // The only way found to actually open the combo box list
249 // right away when starting to edit.
250 combo.getDisplay().asyncExec(() -> {
251 if (!combo.isDisposed())
252 combo.setListVisible(true);
259 protected Object getValue(Object element) {
260 LoggerLevel l = (LoggerLevel) element;
261 return LEVEL_TO_INDEX.get(l.getLevel());
264 protected void setValue(Object element, Object value) {
265 LoggerLevel l = (LoggerLevel)element;
266 l.setLevel(LEVELS[(Integer) value]);
267 getViewer().update(element, null);
271 tcl.setColumnData(column1.getColumn(), new ColumnWeightData(5, 300));
272 tcl.setColumnData(column2.getColumn(), new ColumnWeightData(1, 150));
275 tableViewer.getTable().setHeaderVisible(true);
276 tableViewer.getTable().setLinesVisible(true);
279 tableViewer.setContentProvider(ArrayContentProvider.getInstance());
281 tableViewer.getTable().addListener(SWT.KeyDown, e -> {
282 if (e.keyCode == SWT.DEL) {
283 @SuppressWarnings("unchecked")
284 List<Object> s = tableViewer.getStructuredSelection().toList();
285 Predicate<Object> removable = l -> {
286 LoggerLevel ll = (LoggerLevel) l;
287 return ll.isLoggerDefined() && !ll.getName().isEmpty();
289 if (s.stream().allMatch(removable)) {
290 s.forEach(configuration::remove);
296 Text filterText = new Text(composite, SWT.FLAT | SWT.BORDER);
297 GridDataFactory.fillDefaults().grab(true, false).applyTo(filterText);
298 filterText.setToolTipText("Package Name Filter");
299 filterText.moveAbove(tableComposite);
300 filterText.addModifyListener(e -> {
301 String filter = filterText.getText().trim().toLowerCase();
302 ViewerFilter[] filters = {};
303 if (!filter.isEmpty()) {
304 filters = new ViewerFilter[] {
307 public boolean select(Viewer viewer, Object parentElement, Object element) {
308 LoggerLevel l = (LoggerLevel) element;
309 return !l.isLoggerDefined() || l.levelChanged()
311 : l.getName().toLowerCase().contains(filter);
316 scheduleSetFilters(filters);
324 private void setInput() {
325 tableViewer.setInput(configuration.toArray(new LoggerLevel[configuration.size()]));
328 public List<LoggerLevel> getConfiguration() {
329 return configuration;
332 private final AtomicReference<ViewerFilter[]> filtersToSet = new AtomicReference<>();
333 private final Runnable setFilters = () -> {
334 if (!tableViewer.getTable().isDisposed()) {
335 ViewerFilter[] fs = filtersToSet.getAndSet(null);
337 tableViewer.getTable().setRedraw(false);
338 tableViewer.setFilters(fs);
339 tableViewer.getTable().setRedraw(true);
344 protected void scheduleSetFilters(ViewerFilter[] array) {
345 filtersToSet.set(array);
346 getShell().getDisplay().timerExec(250, setFilters);