/** * Updates the position and size of the edit panel relative to the given location. * * @param loc the location the edit panel should be relative to * @param absolute if {@code true} the loc is treated as absolute position on the process * renderer; if {@code false} it is treated as relative to the current process */ private void updateEditPanelPosition(final Rectangle2D loc, final boolean absolute) { int panelX = (int) loc.getCenterX() - EDIT_PANEL_WIDTH / 2; int panelY = (int) loc.getY() - EDIT_PANEL_HEIGHT - ProcessDrawer.PADDING; // if panel would be outside process renderer, fix it if (panelX < WorkflowAnnotation.MIN_X) { panelX = WorkflowAnnotation.MIN_X; } if (panelY < 0) { panelY = (int) loc.getMaxY() + ProcessDrawer.PADDING; } // last fallback is cramped to the bottom. If that does not fit either, don't care if (panelY + EDIT_PANEL_HEIGHT > view.getSize().getHeight() - ProcessDrawer.PADDING * 2) { panelY = (int) loc.getMaxY(); } int index = view.getModel().getProcessIndex(model.getSelected().getProcess()); if (absolute) { editPanel.setBounds(panelX, panelY, EDIT_PANEL_WIDTH, EDIT_PANEL_HEIGHT); } else { Point absoluteP = ProcessDrawUtils.convertToAbsoluteProcessPoint( new Point(panelX, panelY), index, rendererModel); editPanel.setBounds( (int) absoluteP.getX(), (int) absoluteP.getY(), EDIT_PANEL_WIDTH, EDIT_PANEL_HEIGHT); } }
/** * Creates and adds the JEditorPane for the currently selected annotation to the process renderer. */ private void createEditor() { final WorkflowAnnotation selected = model.getSelected(); Rectangle2D loc = selected.getLocation(); // JEditorPane to edit the comment string editPane = new JEditorPane("text/html", ""); editPane.setBorder(null); int paneX = (int) loc.getX(); int paneY = (int) loc.getY(); int index = view.getModel().getProcessIndex(selected.getProcess()); Point absolute = ProcessDrawUtils.convertToAbsoluteProcessPoint( new Point(paneX, paneY), index, rendererModel); editPane.setBounds( (int) absolute.getX(), (int) absolute.getY(), (int) loc.getWidth(), (int) loc.getHeight()); editPane.setText(AnnotationDrawUtils.createStyledCommentString(selected)); // use proxy for paste actions to trigger reload of editor after paste Action pasteFromClipboard = editPane.getActionMap().get(PASTE_FROM_CLIPBOARD_ACTION_NAME); Action paste = editPane.getActionMap().get(PASTE_ACTION_NAME); if (pasteFromClipboard != null) { editPane .getActionMap() .put( PASTE_FROM_CLIPBOARD_ACTION_NAME, new PasteAnnotationProxyAction(pasteFromClipboard, this)); } if (paste != null) { editPane.getActionMap().put(PASTE_ACTION_NAME, new PasteAnnotationProxyAction(paste, this)); } // use proxy for transfer actions to convert e.g. HTML paste to plaintext paste editPane.setTransferHandler(new TransferHandlerAnnotationPlaintext(editPane)); // IMPORTANT: Linebreaks do not work without the following! // this filter inserts a \r every time the user enters a newline // this signal is later used to convert newline to <br/> ((HTMLDocument) editPane.getDocument()) .setDocumentFilter( new DocumentFilter() { @Override public void insertString( DocumentFilter.FilterBypass fb, int offs, String str, AttributeSet a) throws BadLocationException { // this is never called.. super.insertString( fb, offs, str.replaceAll("\n", "\n" + AnnotationDrawUtils.ANNOTATION_HTML_NEWLINE_SIGNAL), a); } @Override public void replace(FilterBypass fb, int offs, int length, String str, AttributeSet a) throws BadLocationException { if (selected instanceof OperatorAnnotation) { // operator annotations have a character limit, enforce here try { int existingLength = AnnotationDrawUtils.getPlaintextFromEditor(editPane, false).length() - length; if (existingLength + str.length() > OperatorAnnotation.MAX_CHARACTERS) { // insert at beginning or end is fine, cut off excess characters if (existingLength <= 0 || offs >= existingLength) { int acceptableLength = OperatorAnnotation.MAX_CHARACTERS - existingLength; int newLength = Math.max(acceptableLength, 0); str = str.substring(0, newLength); } else { // inserting into middle, do NOT paste at all return; } } } catch (IOException e) { // should not happen, if it does this is our smallest problem -> ignore } } super.replace( fb, offs, length, str.replaceAll("\n", "\n" + AnnotationDrawUtils.ANNOTATION_HTML_NEWLINE_SIGNAL), a); } }); // set background color if (selected.getStyle().getAnnotationColor() == AnnotationColor.TRANSPARENT) { editPane.setBackground(Color.WHITE); } else { editPane.setBackground(selected.getStyle().getAnnotationColor().getColorHighlight()); } editPane.addFocusListener( new FocusAdapter() { @Override public void focusLost(final FocusEvent e) { // right-click menu if (e.isTemporary()) { return; } if (editPane != null && e.getOppositeComponent() != null) { // style edit menu, no real focus loss if (SwingUtilities.isDescendingFrom(e.getOppositeComponent(), editPanel)) { return; } if (SwingUtilities.isDescendingFrom(e.getOppositeComponent(), colorOverlay)) { return; } if (colorOverlay.getParent() == e.getOppositeComponent()) { return; } saveEdit(selected); removeEditor(); } } }); editPane.addKeyListener( new KeyAdapter() { /** keep track of control down so Ctrl+Enter works but Enter+Ctrl not */ private boolean controlDown; @Override public void keyPressed(final KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_CONTROL) { controlDown = true; } // consume so undo/redo etc are not passed to the process if (SwingTools.isControlOrMetaDown(e) && e.getKeyCode() == KeyEvent.VK_Z || e.getKeyCode() == KeyEvent.VK_Y) { e.consume(); } } @Override public void keyReleased(final KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_CONTROL: controlDown = false; break; case KeyEvent.VK_ENTER: if (!controlDown) { updateEditorHeight(selected); } else { // if control was down before Enter was pressed, save & exit saveEdit(selected); removeEditor(); model.setSelected(null); } break; case KeyEvent.VK_ESCAPE: // ignore changes on escape removeEditor(); model.setSelected(null); break; default: break; } } }); editPane .getDocument() .addDocumentListener( new DocumentListener() { @Override public void removeUpdate(DocumentEvent e) { updateEditorHeight(selected); } @Override public void insertUpdate(DocumentEvent e) { updateEditorHeight(selected); } @Override public void changedUpdate(DocumentEvent e) { updateEditorHeight(selected); } }); view.add(editPane); editPane.selectAll(); }