Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sierra-tools/previewer/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ dependencies {
implementation "com.formdev:flatlaf-extras:${flatLafVersion}"
implementation 'com.fifesoft:rsyntaxtextarea:3.6.0'
implementation 'com.fifesoft:autocomplete:3.3.2'
implementation 'com.fifesoft:rstaui:3.3.1'

testImplementation "org.junit.jupiter:junit-jupiter:${junitVersion}"
testImplementation 'org.mockito:mockito-core:5.20.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@
*/
package org.httprpc.sierra.tools.previewer;

import org.fife.ui.autocomplete.AutoCompletion;
import org.fife.ui.autocomplete.CompletionProvider;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.httprpc.sierra.Outlet;
import org.httprpc.sierra.UILoader;
import org.httprpc.sierra.tools.previewer.engine.RenderingEngine;
import org.httprpc.sierra.tools.previewer.model.RenderError;
import org.httprpc.sierra.tools.previewer.model.RenderResult;

import java.awt.BorderLayout;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import javax.swing.ImageIcon;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
Expand All @@ -34,26 +34,36 @@
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.KeyStroke;
import javax.swing.SwingWorker;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import java.awt.BorderLayout;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import org.fife.rsta.ui.search.FindDialog;
import org.fife.rsta.ui.search.ReplaceDialog;
import org.fife.rsta.ui.search.SearchEvent;
import org.fife.rsta.ui.search.SearchListener;
import org.fife.ui.autocomplete.AutoCompletion;
import org.fife.ui.autocomplete.CompletionProvider;
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
import org.fife.ui.rtextarea.SearchContext;
import org.fife.ui.rtextarea.SearchEngine;
import org.fife.ui.rtextarea.SearchResult;
import org.httprpc.sierra.Outlet;
import org.httprpc.sierra.UILoader;
import org.httprpc.sierra.tools.previewer.engine.RenderingEngine;
import org.httprpc.sierra.tools.previewer.model.RenderError;
import org.httprpc.sierra.tools.previewer.model.RenderResult;

/**
* The main application window for the Sierra UI Previewer. UI is defined in
* MainFrame.xml and loaded by UILoader. This class contains the wiring and
* business logic.
*/
public class MainFrame extends JFrame {
public class MainFrame extends JFrame implements SearchListener {
// --- Subsystems ---
private final RenderingEngine renderingEngine;
private final RecentFilesManager recentFilesManager; // NEW: Manager instance
Expand All @@ -69,6 +79,9 @@ public class MainFrame extends JFrame {
private @Outlet JMenuBar menuBar = null;
private @Outlet JMenuItem openItem = null;
private @Outlet JMenuItem saveItem = null;
private @Outlet JMenu searchMenu = null;
private @Outlet JMenuItem findItem = null;
private @Outlet JMenuItem replaceItem = null;
private @Outlet JMenu recentMenu = null;
private @Outlet JMenuItem exitItem = null;
private @Outlet JMenuItem aboutItem = null;
Expand All @@ -79,6 +92,8 @@ public class MainFrame extends JFrame {
private @Outlet JLabel filePathLabel = null; // The <label> for the file path

// --- Manually Created Components ---
private FindDialog findDialog = null;
private ReplaceDialog replaceDialog = null;
private RSyntaxTextArea editorPane = null;

public MainFrame() {
Expand All @@ -97,12 +112,7 @@ public MainFrame() {
fileChooser.setAcceptAllFileFilterUsed(false);

setupMenuBar();

setupCustomEditor();

// 5. Set layout for previewPanel
// previewPanel.setLayout(new BorderLayout());

debounceTimer = setupDebounceTimer();

// Wire editor events
Expand All @@ -128,8 +138,7 @@ public void changedUpdate(DocumentEvent e) {
var iconURL = getClass().getResource("/sierra.png");
var icon = new ImageIcon(iconURL).getImage();
setIconImage(icon);

// 8. Trigger an initial render

triggerRender();
}

Expand Down Expand Up @@ -206,10 +215,33 @@ private CompletionProvider createCompletionProvider() {

// --- Editor Setup ---
/**
* Creates the custom RSyntaxTextArea and adds it to the <scroll-pane>
* placeholder that Sierra injected.
* Creates the custom RSyntaxTextArea/associated functionality and adds it
* to the placeholder that Sierra injected.
*/
private void setupCustomEditor() {
findDialog = new FindDialog(this, this);
replaceDialog = new ReplaceDialog(this, this);

// This ties the properties of the two dialogs together (match case,
// regex, etc.).
SearchContext context = findDialog.getSearchContext();
replaceDialog.setSearchContext(context);

int acceleratorKey = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();
findItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, acceleratorKey));
findItem.addActionListener((e) -> {
if (replaceDialog.isVisible()) {
replaceDialog.setVisible(false);
}
findDialog.setVisible(true);
});
replaceItem.addActionListener((e) -> {
if (findDialog.isVisible()) {
findDialog.setVisible(false);
}
replaceDialog.setVisible(true);
});
replaceItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, acceleratorKey));
editorPane = new RSyntaxTextArea(25, 80);
editorPane.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML);
editorPane.setCodeFoldingEnabled(true);
Expand All @@ -230,7 +262,71 @@ private void setupCustomEditor() {

editorScrollPane.setViewportView(editorPane);
}

// -- Extra search/replace functionality

@Override
public String getSelectedText() {
return editorPane.getSelectedText();
}

/**
* Listens for events from our search dialogs and actually does the dirty
* work.
*/
@Override
public void searchEvent(SearchEvent e) {

SearchEvent.Type type = e.getType();
SearchContext context = e.getSearchContext();
SearchResult result = null;

switch (type) {
case MARK_ALL:
result = SearchEngine.markAll(editorPane, context);
break;
case FIND:
result = SearchEngine.find(editorPane, context);
if (!result.wasFound() || result.isWrapped()) {
UIManager.getLookAndFeel().provideErrorFeedback(editorPane);
}
break;
case REPLACE:
result = SearchEngine.replace(editorPane, context);
if (!result.wasFound() || result.isWrapped()) {
UIManager.getLookAndFeel().provideErrorFeedback(editorPane);
}
break;
case REPLACE_ALL:
result = SearchEngine.replaceAll(editorPane, context);
JOptionPane.showMessageDialog(null, result.getCount()
+ " occurrences replaced.");
break;
default:
statusBar.setText("Unknown search event");
break;
}

if(result == null){
return;
}

String text;
if (result.wasFound()) {
text = "Text found; occurrences marked: " + result.getMarkedCount();
} else if (type == SearchEvent.Type.MARK_ALL) {
if (result.getMarkedCount() > 0) {
text = "Occurrences marked: " + result.getMarkedCount();
} else {
text = "";
}
} else {
text = "Text not found";
}
statusBar.setText(text);

}

// --- Rendering/Control Logic ---
/**
* Implements the debounce mechanism.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
<popup-menu-separator/>
<menu-item name="exitItem" text="Exit"/>
</menu>
<menu name="searchMenu" text="Search">
<menu-item name="findItem" text="Find"/>
<menu-item name="replaceItem" text="Replace"/>
</menu>
<menu text="About">
<menu-item name="aboutItem" text="About Previewer"/>
</menu>
Expand All @@ -22,6 +26,6 @@
<column-panel name="previewPanel" padding="5, 5, 5, 5" weight="1"/>
</split-pane>

<label name="filePathLabel" text="" padding="4, 6, 4, 6"/>
<label name="statusBar" text="Ready." padding="2, 5, 2, 5"/>
<label name="filePathLabel" text="" padding="4, 6, 4, 6" />
<label name="statusBar" text="Ready." padding="2, 5, 2, 5" border="silver, 1"/>
</column-panel>