Skip to content

Commit 9b0f4e1

Browse files
author
Darren Spruce
committed
add extra paste as markdown textarea popup menu and shortcut key
1 parent 2231294 commit 9b0f4e1

File tree

1 file changed

+146
-23
lines changed

1 file changed

+146
-23
lines changed

app/logbook/olog/ui/src/main/java/org/phoebus/logbook/olog/ui/write/LogEntryEditorController.java

Lines changed: 146 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,21 @@
4747
import javafx.scene.control.TextArea;
4848
import javafx.scene.control.TextField;
4949
import javafx.scene.control.ToggleButton;
50+
import javafx.scene.control.SeparatorMenuItem;
5051
import javafx.scene.image.Image;
5152
import javafx.scene.image.ImageView;
53+
import javafx.scene.input.*;
5254
import javafx.scene.layout.HBox;
5355
import javafx.scene.layout.VBox;
5456
import javafx.scene.paint.Color;
57+
58+
59+
import java.net.MalformedURLException;
60+
import java.net.URL;
61+
import java.util.regex.Pattern;
62+
import java.util.regex.Matcher;
63+
import org.phoebus.logbook.olog.ui.LogbookUIPreferences;
64+
5565
import javafx.util.Callback;
5666
import javafx.util.StringConverter;
5767
import org.phoebus.framework.autocomplete.Proposal;
@@ -70,7 +80,6 @@
7080
import org.phoebus.logbook.LogbookPreferences;
7181
import org.phoebus.logbook.Tag;
7282
import org.phoebus.logbook.olog.ui.HelpViewer;
73-
import org.phoebus.logbook.olog.ui.LogbookUIPreferences;
7483
import org.phoebus.logbook.olog.ui.Messages;
7584
import org.phoebus.logbook.olog.ui.PreviewViewer;
7685
import org.phoebus.olog.es.api.model.OlogLog;
@@ -191,6 +200,8 @@ public class LogEntryEditorController {
191200
@SuppressWarnings("unused")
192201
private Node attachmentsPane;
193202

203+
204+
194205
private final ContextMenu logbookDropDown = new ContextMenu();
195206
private final ContextMenu tagDropDown = new ContextMenu();
196207

@@ -270,6 +281,7 @@ public LogEntryEditorController(LogEntry logEntry, LogEntry inReplyTo, EditMode
270281
@FXML
271282
public void initialize() {
272283

284+
273285
// Remote log service not reachable, so show error pane.
274286
if (!checkConnectivity()) {
275287
errorPane.visibleProperty().set(true);
@@ -392,16 +404,6 @@ public void initialize() {
392404
return text.substring(0, text.length() - 2);
393405
}, selectedLogbooks));
394406

395-
tagsSelection.textProperty().bind(Bindings.createStringBinding(() -> {
396-
if (selectedTags.isEmpty()) {
397-
return "";
398-
}
399-
StringBuilder stringBuilder = new StringBuilder();
400-
selectedTags.forEach(l -> stringBuilder.append(l).append(", "));
401-
String text = stringBuilder.toString();
402-
return text.substring(0, text.length() - 2);
403-
}, selectedTags));
404-
405407
logbooksDropdownButton.focusedProperty().addListener((changeListener, oldVal, newVal) ->
406408
{
407409
if (!newVal && !tagDropDown.isShowing() && !logbookDropDown.isShowing())
@@ -449,16 +451,6 @@ public void initialize() {
449451
newSelection.forEach(t -> updateDropDown(tagDropDown, t, true));
450452
});
451453

452-
selectedLogbooks.addListener((ListChangeListener<String>) change -> {
453-
if (change.getList() == null) {
454-
return;
455-
}
456-
List<String> newSelection = new ArrayList<>(change.getList());
457-
logbooksPopOver.setAvailable(availableLogbooksAsStringList, newSelection);
458-
logbooksPopOver.setSelected(newSelection);
459-
newSelection.forEach(l -> updateDropDown(logbookDropDown, l, true));
460-
});
461-
462454
AutocompleteMenu autocompleteMenu = new AutocompleteMenu(new ProposalService(new ProposalProvider() {
463455
@Override
464456
public String getName() {
@@ -547,8 +539,139 @@ public LogTemplate fromString(String name) {
547539

548540
// Note: logbooks and tags are retrieved asynchronously from service
549541
getServerSideStaticData();
542+
543+
setupTextAreaContextMenu();
544+
545+
}
546+
547+
private void setupTextAreaContextMenu() {
548+
// Create the context menu with default items
549+
ContextMenu contextMenu = new ContextMenu();
550+
551+
// Standard text editing items
552+
MenuItem undo = new MenuItem("Undo");
553+
undo.setOnAction(e -> textArea.undo());
554+
555+
MenuItem redo = new MenuItem("Redo");
556+
redo.setOnAction(e -> textArea.redo());
557+
558+
MenuItem cut = new MenuItem("Cut");
559+
cut.setOnAction(e -> textArea.cut());
560+
561+
MenuItem copy = new MenuItem("Copy");
562+
copy.setOnAction(e -> textArea.copy());
563+
564+
MenuItem paste = new MenuItem("Paste");
565+
paste.setOnAction(e -> textArea.paste());
566+
567+
MenuItem delete = new MenuItem("Delete");
568+
delete.setOnAction(e -> textArea.replaceSelection(""));
569+
570+
MenuItem selectAll = new MenuItem("Select All");
571+
selectAll.setOnAction(e -> textArea.selectAll());
572+
573+
// Our custom menu item
574+
MenuItem pasteUrlItem = new MenuItem("Paste URL as Markdown");
575+
pasteUrlItem.setOnAction(event -> handleSmartPaste());
576+
pasteUrlItem.setAccelerator(new KeyCodeCombination(KeyCode.V,
577+
KeyCombination.SHORTCUT_DOWN, KeyCombination.SHIFT_DOWN));
578+
579+
// Add all items to the menu
580+
contextMenu.getItems().addAll(
581+
undo,
582+
redo,
583+
new SeparatorMenuItem(),
584+
cut,
585+
copy,
586+
paste,
587+
delete,
588+
new SeparatorMenuItem(),
589+
selectAll,
590+
new SeparatorMenuItem(),
591+
pasteUrlItem
592+
);
593+
594+
// Bind the menu items to the text area's state
595+
undo.disableProperty().bind(textArea.undoableProperty().not());
596+
redo.disableProperty().bind(textArea.redoableProperty().not());
597+
cut.disableProperty().bind(textArea.selectedTextProperty().isEmpty());
598+
copy.disableProperty().bind(textArea.selectedTextProperty().isEmpty());
599+
delete.disableProperty().bind(textArea.selectedTextProperty().isEmpty());
600+
601+
// Set the context menu on the text area
602+
textArea.setContextMenu(contextMenu);
550603
}
551604

605+
private void handleSmartPaste() {
606+
final Clipboard clipboard = Clipboard.getSystemClipboard();
607+
if (!clipboard.hasString()) {
608+
return;
609+
}
610+
611+
String clipboardText = clipboard.getString();
612+
String ologUrl = extractOlogUrl(clipboardText);
613+
String selectedText = textArea.getSelectedText();
614+
615+
if (ologUrl != null) {
616+
// It's an Olog URL
617+
String logNumber = extractLogNumber(ologUrl);
618+
if (logNumber != null) {
619+
if (selectedText != null && !selectedText.isEmpty()) {
620+
// Use selected text as link text for the Olog reference
621+
String markdownLink = String.format("[%s](%s)", selectedText, ologUrl);
622+
textArea.replaceSelection(markdownLink);
623+
} else {
624+
// No selection - create a standard log entry reference
625+
String markdownLink = String.format("[%s](%s)", logNumber, ologUrl);
626+
textArea.replaceSelection(markdownLink);
627+
}
628+
}
629+
return;
630+
}
631+
632+
// Try to identify if clipboard content is a regular URL
633+
try {
634+
new URL(clipboardText);
635+
636+
if (selectedText != null && !selectedText.isEmpty()) {
637+
// Replace selection with markdown link using selected text
638+
String markdownLink = String.format("[%s](%s)", selectedText, clipboardText);
639+
textArea.replaceSelection(markdownLink);
640+
} else {
641+
// No selection - use URL as both link text and target
642+
String markdownLink = String.format("[%s](%s)", clipboardText, clipboardText);
643+
textArea.replaceSelection(markdownLink);
644+
}
645+
} catch (MalformedURLException e) {
646+
// Not a URL - do nothing
647+
}
648+
}
649+
private String extractOlogUrl(String text) {
650+
String rootUrl = LogbookUIPreferences.web_client_root_URL;
651+
if (rootUrl == null || rootUrl.isEmpty()) {
652+
return null;
653+
}
654+
655+
if (text.toLowerCase().contains("olog") &&
656+
text.matches(".*?/logs/\\d+/?$")) {
657+
return text;
658+
}
659+
return null;
660+
}
661+
662+
private String extractLogNumber(String url) {
663+
if (url == null) {
664+
return null;
665+
}
666+
Pattern pattern = Pattern.compile("/logs/(\\d+)/?$");
667+
Matcher matcher = pattern.matcher(url);
668+
if (matcher.find()) {
669+
return matcher.group(1);
670+
}
671+
return null;
672+
}
673+
674+
552675
/**
553676
* Handler for Cancel button. Note that any selections in the {@link SelectionService} are
554677
* cleared to prevent next launch of {@link org.phoebus.logbook.olog.ui.menu.SendToLogBookApp}
@@ -758,7 +881,7 @@ private void getServerSideStaticData() {
758881
List<String> preSelectedLogbooks =
759882
logEntry.getLogbooks().stream().map(Logbook::getName).toList();
760883
List<String> defaultLogbooks = Arrays.asList(LogbookUIPreferences.default_logbooks);
761-
availableLogbooksAsStringList.forEach(logbook -> {
884+
for (String logbook : availableLogbooksAsStringList) {
762885
CheckBox checkBox = new CheckBox(logbook);
763886
CustomMenuItem newLogbook = new CustomMenuItem(checkBox);
764887
newLogbook.setHideOnClick(false);
@@ -779,7 +902,7 @@ private void getServerSideStaticData() {
779902
selectedLogbooks.add(logbook);
780903
}
781904
logbookDropDown.getItems().add(newLogbook);
782-
});
905+
}
783906

784907
availableTags = logClient.listTags();
785908
availableTagsAsStringList =

0 commit comments

Comments
 (0)