/* * Copyright 2016 Nokia Solutions and Networks * Licensed under the Apache License, Version 2.0, * see license.txt file for details. */ package org.robotframework.red.nattable.edit; import static com.google.common.collect.Lists.transform; import java.util.List; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.DisposeEvent; import org.eclipse.swt.events.DisposeListener; import org.eclipse.swt.events.KeyAdapter; import org.eclipse.swt.events.KeyEvent; import org.eclipse.swt.events.MouseAdapter; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.PaintEvent; import org.eclipse.swt.events.PaintListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import org.robotframework.red.graphics.ColorsManager; import org.robotframework.red.nattable.edit.DetailCellEditorEntriesControlsSwitcher.Mode; import org.robotframework.red.nattable.edit.DetailCellEditorEntry.DetailEditorListener; import org.robotframework.red.nattable.edit.DetailEntriesCollection.DetailWithEntry; import com.google.common.base.Function; /** * @author Michal Anglart * */ public class DetailCellEditorEntriesComposite<D> extends Composite { private final DetailCellEditorEditingSupport<D> editSupport; private final AssistanceSupport assistSupport; private int column; private int row; private final MainControlChooser mainControlChooseCallback; private final Mode mode; private Composite entriesComposite; private final DetailEntriesCollection<D> entries = new DetailEntriesCollection<>(); private final EntriesChangeListener<D> entriesChangesListener; public DetailCellEditorEntriesComposite(final Composite parent, final DetailCellEditorEditingSupport<D> editSupport, final AssistanceSupport assistSupport, final Mode mode, final EntriesChangeListener<D> entriesChangesListener, final MainControlChooser mainControlChooseCallback) { super(parent, SWT.NONE); this.editSupport = editSupport; this.assistSupport = assistSupport; this.entriesChangesListener = entriesChangesListener; this.mainControlChooseCallback = mainControlChooseCallback; this.mode = mode; setBackground(getParent().getBackground()); if (mode == Mode.INLINED) { addPaintListener(new PaintListener() { @Override public void paintControl(final PaintEvent e) { e.gc.drawLine(0, 0, e.width, 0); e.gc.drawLine(e.width - 1, 0, e.width - 1, e.height - 1); e.gc.drawLine(e.width - 1, e.height - 1, 0, e.height - 1); e.gc.drawLine(0, e.height - 1, 0, 0); } }); GridLayoutFactory.fillDefaults().spacing(1, 1).extendedMargins(1, 1, 1, 1).applyTo(this); } else { GridLayoutFactory.fillDefaults().spacing(1, 1).applyTo(this); } createEntriesComposite(); createTooltipControls(); } DetailEntriesCollection<D> getEntries() { return entries; } private void createEntriesComposite() { final ScrolledComposite scrolledComposite = new ScrolledComposite(this, SWT.V_SCROLL); scrolledComposite.setBackground(getParent().getBackground()); scrolledComposite.setShowFocusedControl(true); scrolledComposite.setExpandHorizontal(true); final SelectionAdapter scrollingRefresher = new SelectionAdapter() { @Override public void widgetSelected(final SelectionEvent e) { entries.redrawEntries(); } }; scrolledComposite.getVerticalBar().addSelectionListener(scrollingRefresher); scrolledComposite.getVerticalBar().addDisposeListener(new DisposeListener() { @Override public void widgetDisposed(final DisposeEvent e) { scrolledComposite.getVerticalBar().removeSelectionListener(scrollingRefresher); } }); GridDataFactory.fillDefaults().grab(true, true).applyTo(scrolledComposite); entriesComposite = new Composite(scrolledComposite, SWT.NONE); scrolledComposite.setContent(entriesComposite); entriesComposite.setBackground(ColorsManager.getColor(230, 230, 230)); GridLayoutFactory.fillDefaults().spacing(1, 1).applyTo(entriesComposite); entriesComposite.setSize(entriesComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); } private void createDetailEntryControls(final List<D> details) { for (final D detail : details) { final DetailCellEditorEntry<D> entry = editSupport.createDetailEntry(entriesComposite, column, row, detail, assistSupport); GridDataFactory.fillDefaults().hint(SWT.DEFAULT, 25).grab(true, false).applyTo(entry); entry.setBackground(getParent().getBackground()); entry.addKeyListener(new EntryKeyPressListener(entry)); entry.addMouseListener(new MouseAdapter() { @Override public void mouseUp(final MouseEvent e) { if (entry.isSelected() && mode == Mode.WINDOWED) { entries.openEntryForEdit(entry); } else { entry.select(e.stateMask == 0 || e.stateMask != SWT.CTRL); } } }); entry.setEditorListener(new DetailEditorListener() { @Override public void editorApplied(final String value) { editSupport.setNewValue(detail, value); entry.update(detail); } }); entries.add(new DetailWithEntry<>(detail, entry)); } entriesComposite.setSize(entriesComposite.computeSize(SWT.DEFAULT, SWT.DEFAULT)); entriesChangesListener.entriesChanged(entries.getEntries()); } private void createTooltipControls() { final Label sep = new Label(this, SWT.SEPARATOR | SWT.HORIZONTAL); GridDataFactory.fillDefaults().align(SWT.FILL, SWT.BOTTOM).grab(true, false).applyTo(sep); final Label label = new Label(this, SWT.NONE); label.setBackground(getBackground()); label.setText("Edit details"); GridDataFactory.fillDefaults().align(SWT.RIGHT, SWT.BOTTOM).grab(true, false).applyTo(label); } void setInput(final int column, final int row) { setColumn(column); setRow(row); entries.disposeEntries(); createDetailEntryControls(editSupport.getInput(column, row)); } int getColumn() { return column; } void setColumn(final int column) { this.column = column; } int getRow() { return row; } void setRow(final int row) { this.row = row; } void refresh() { entries.disposeEntries(); createDetailEntryControls(editSupport.getDetailElements()); } void selectFirstEntry() { if (!entries.isEmpty()) { entries.selectOnlyEntry(0); } } static class EntriesChangeListener<D> { void entriesChanged(final List<DetailCellEditorEntry<D>> entries) { // override if needed } } static interface MainControlChooser { void focusMainControl(); } private class EntryKeyPressListener extends KeyAdapter { private final DetailCellEditorEntry<D> entry; private EntryKeyPressListener(final DetailCellEditorEntry<D> entry) { this.entry = entry; } @Override public void keyPressed(final KeyEvent e) { if (e.keyCode == SWT.HOME && e.stateMask == 0) { entries.selectOnlyFirstEntry(); } else if (e.keyCode == SWT.HOME && e.stateMask == SWT.SHIFT) { entries.selectTillFirstEntry(entry); } else if (e.keyCode == SWT.ARROW_UP && e.stateMask == 0) { if (!entries.isFirst(entry)) { entries.selectOnlyPreviousEntry(entry); } else { entry.deselect(); mainControlChooseCallback.focusMainControl(); } } else if (e.keyCode == SWT.ARROW_UP && e.stateMask == SWT.SHIFT) { entries.selectPreviousEntry(entry); } else if (e.keyCode == SWT.ARROW_UP && e.stateMask == SWT.CTRL) { if (!entries.isFirstSelected()) { final List<Integer> indexes = entries.getSelectedIndexes(); editSupport.moveLeft(entries.getSelectedDetails()); refresh(); entries.selectEntries(transform(indexes, precedessor())); } } else if (e.keyCode == SWT.END && e.stateMask == 0) { entries.selectOnlyLastEntry(); } else if (e.keyCode == SWT.END && e.stateMask == SWT.SHIFT) { entries.selectTillLastEntry(entry); } else if (e.keyCode == SWT.PAGE_UP && e.stateMask == 0) { entries.selectOnlyPreviousEntryJumping(entry); } else if (e.keyCode == SWT.PAGE_UP && e.stateMask == SWT.SHIFT) { entries.selectPreviousEntryJumping(entry); } else if (e.keyCode == SWT.ARROW_DOWN && e.stateMask == 0) { entries.selectOnlyNextEntry(entry); } else if (e.keyCode == SWT.ARROW_DOWN && e.stateMask == SWT.SHIFT) { entries.selectNextEntry(entry); } else if (e.keyCode == SWT.ARROW_DOWN && e.stateMask == SWT.CTRL) { if (!entries.isLastSelected()) { final List<Integer> indexes = entries.getSelectedIndexes(); editSupport.moveRight(entries.getSelectedDetails()); refresh(); entries.selectEntries(transform(indexes, successor())); } } else if (e.keyCode == SWT.PAGE_DOWN && e.stateMask == 0) { entries.selectOnlyNextEntryJumping(entry); } else if (e.keyCode == SWT.PAGE_DOWN && e.stateMask == SWT.SHIFT) { entries.selectNextEntriesJumping(entry); } else if (e.keyCode == SWT.ESC) { entries.deselectAll(); mainControlChooseCallback.focusMainControl(); } else if (e.keyCode == SWT.DEL) { final int index = entries.getEntryIndex(entry); editSupport.removeDetailElements(entries.getSelectedDetails()); refresh(); if (index < entries.size()) { entries.selectOnlyEntry(index); } else if (!entries.isEmpty()) { entries.selectOnlyLastEntry(); } } else if (e.keyCode == SWT.CR || e.keyCode == SWT.KEYPAD_CR) { entries.openEntryForEdit(entry); } else if (e.keyCode == 'a' && e.stateMask == SWT.CTRL) { entries.selectAll(); } } } private static Function<Integer, Integer> successor() { return new Function<Integer, Integer>() { @Override public Integer apply(final Integer number) { return number + 1; } }; } private static Function<Integer, Integer> precedessor() { return new Function<Integer, Integer>() { @Override public Integer apply(final Integer number) { return number - 1; } }; } }