Skip to content

Commit 240df37

Browse files
authored
Merge pull request #45 from lawson89/master
add find/replace based on RSTAUI components
2 parents d2e924b + 77f75f1 commit 240df37

3 files changed

Lines changed: 131 additions & 30 deletions

File tree

sierra-tools/previewer/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ dependencies {
2222
implementation "com.formdev:flatlaf-extras:${flatLafVersion}"
2323
implementation 'com.fifesoft:rsyntaxtextarea:3.6.0'
2424
implementation 'com.fifesoft:autocomplete:3.3.2'
25+
implementation 'com.fifesoft:rstaui:3.3.1'
2526

2627
testImplementation "org.junit.jupiter:junit-jupiter:${junitVersion}"
2728
testImplementation 'org.mockito:mockito-core:5.20.0'

sierra-tools/previewer/src/main/java/org/httprpc/sierra/tools/previewer/MainFrame.java

Lines changed: 124 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,16 @@
1313
*/
1414
package org.httprpc.sierra.tools.previewer;
1515

16-
import org.fife.ui.autocomplete.AutoCompletion;
17-
import org.fife.ui.autocomplete.CompletionProvider;
18-
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
19-
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
20-
import org.httprpc.sierra.Outlet;
21-
import org.httprpc.sierra.UILoader;
22-
import org.httprpc.sierra.tools.previewer.engine.RenderingEngine;
23-
import org.httprpc.sierra.tools.previewer.model.RenderError;
24-
import org.httprpc.sierra.tools.previewer.model.RenderResult;
25-
16+
import java.awt.BorderLayout;
17+
import java.awt.Toolkit;
18+
import java.awt.event.KeyEvent;
19+
import java.io.File;
20+
import java.io.IOException;
21+
import java.nio.file.Files;
22+
import java.nio.file.Path;
23+
import java.nio.file.StandardOpenOption;
24+
import java.util.concurrent.ExecutionException;
25+
import java.util.function.Consumer;
2626
import javax.swing.ImageIcon;
2727
import javax.swing.JFileChooser;
2828
import javax.swing.JFrame;
@@ -34,26 +34,36 @@
3434
import javax.swing.JPanel;
3535
import javax.swing.JScrollPane;
3636
import javax.swing.JSplitPane;
37+
import javax.swing.KeyStroke;
3738
import javax.swing.SwingWorker;
3839
import javax.swing.Timer;
40+
import javax.swing.UIManager;
3941
import javax.swing.event.DocumentEvent;
4042
import javax.swing.event.DocumentListener;
4143
import javax.swing.filechooser.FileNameExtensionFilter;
42-
import java.awt.BorderLayout;
43-
import java.io.File;
44-
import java.io.IOException;
45-
import java.nio.file.Files;
46-
import java.nio.file.Path;
47-
import java.nio.file.StandardOpenOption;
48-
import java.util.concurrent.ExecutionException;
49-
import java.util.function.Consumer;
44+
import org.fife.rsta.ui.search.FindDialog;
45+
import org.fife.rsta.ui.search.ReplaceDialog;
46+
import org.fife.rsta.ui.search.SearchEvent;
47+
import org.fife.rsta.ui.search.SearchListener;
48+
import org.fife.ui.autocomplete.AutoCompletion;
49+
import org.fife.ui.autocomplete.CompletionProvider;
50+
import org.fife.ui.rsyntaxtextarea.RSyntaxTextArea;
51+
import org.fife.ui.rsyntaxtextarea.SyntaxConstants;
52+
import org.fife.ui.rtextarea.SearchContext;
53+
import org.fife.ui.rtextarea.SearchEngine;
54+
import org.fife.ui.rtextarea.SearchResult;
55+
import org.httprpc.sierra.Outlet;
56+
import org.httprpc.sierra.UILoader;
57+
import org.httprpc.sierra.tools.previewer.engine.RenderingEngine;
58+
import org.httprpc.sierra.tools.previewer.model.RenderError;
59+
import org.httprpc.sierra.tools.previewer.model.RenderResult;
5060

5161
/**
5262
* The main application window for the Sierra UI Previewer. UI is defined in
5363
* MainFrame.xml and loaded by UILoader. This class contains the wiring and
5464
* business logic.
5565
*/
56-
public class MainFrame extends JFrame {
66+
public class MainFrame extends JFrame implements SearchListener {
5767
// --- Subsystems ---
5868
private final RenderingEngine renderingEngine;
5969
private final RecentFilesManager recentFilesManager; // NEW: Manager instance
@@ -69,6 +79,9 @@ public class MainFrame extends JFrame {
6979
private @Outlet JMenuBar menuBar = null;
7080
private @Outlet JMenuItem openItem = null;
7181
private @Outlet JMenuItem saveItem = null;
82+
private @Outlet JMenu searchMenu = null;
83+
private @Outlet JMenuItem findItem = null;
84+
private @Outlet JMenuItem replaceItem = null;
7285
private @Outlet JMenu recentMenu = null;
7386
private @Outlet JMenuItem exitItem = null;
7487
private @Outlet JMenuItem aboutItem = null;
@@ -79,6 +92,8 @@ public class MainFrame extends JFrame {
7992
private @Outlet JLabel filePathLabel = null; // The <label> for the file path
8093

8194
// --- Manually Created Components ---
95+
private FindDialog findDialog = null;
96+
private ReplaceDialog replaceDialog = null;
8297
private RSyntaxTextArea editorPane = null;
8398

8499
public MainFrame() {
@@ -97,12 +112,7 @@ public MainFrame() {
97112
fileChooser.setAcceptAllFileFilterUsed(false);
98113

99114
setupMenuBar();
100-
101115
setupCustomEditor();
102-
103-
// 5. Set layout for previewPanel
104-
// previewPanel.setLayout(new BorderLayout());
105-
106116
debounceTimer = setupDebounceTimer();
107117

108118
// Wire editor events
@@ -128,8 +138,7 @@ public void changedUpdate(DocumentEvent e) {
128138
var iconURL = getClass().getResource("/sierra.png");
129139
var icon = new ImageIcon(iconURL).getImage();
130140
setIconImage(icon);
131-
132-
// 8. Trigger an initial render
141+
133142
triggerRender();
134143
}
135144

@@ -206,10 +215,33 @@ private CompletionProvider createCompletionProvider() {
206215

207216
// --- Editor Setup ---
208217
/**
209-
* Creates the custom RSyntaxTextArea and adds it to the <scroll-pane>
210-
* placeholder that Sierra injected.
218+
* Creates the custom RSyntaxTextArea/associated functionality and adds it
219+
* to the placeholder that Sierra injected.
211220
*/
212221
private void setupCustomEditor() {
222+
findDialog = new FindDialog(this, this);
223+
replaceDialog = new ReplaceDialog(this, this);
224+
225+
// This ties the properties of the two dialogs together (match case,
226+
// regex, etc.).
227+
SearchContext context = findDialog.getSearchContext();
228+
replaceDialog.setSearchContext(context);
229+
230+
int acceleratorKey = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();
231+
findItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, acceleratorKey));
232+
findItem.addActionListener((e) -> {
233+
if (replaceDialog.isVisible()) {
234+
replaceDialog.setVisible(false);
235+
}
236+
findDialog.setVisible(true);
237+
});
238+
replaceItem.addActionListener((e) -> {
239+
if (findDialog.isVisible()) {
240+
findDialog.setVisible(false);
241+
}
242+
replaceDialog.setVisible(true);
243+
});
244+
replaceItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, acceleratorKey));
213245
editorPane = new RSyntaxTextArea(25, 80);
214246
editorPane.setSyntaxEditingStyle(SyntaxConstants.SYNTAX_STYLE_XML);
215247
editorPane.setCodeFoldingEnabled(true);
@@ -230,7 +262,71 @@ private void setupCustomEditor() {
230262

231263
editorScrollPane.setViewportView(editorPane);
232264
}
265+
266+
// -- Extra search/replace functionality
267+
268+
@Override
269+
public String getSelectedText() {
270+
return editorPane.getSelectedText();
271+
}
272+
273+
/**
274+
* Listens for events from our search dialogs and actually does the dirty
275+
* work.
276+
*/
277+
@Override
278+
public void searchEvent(SearchEvent e) {
279+
280+
SearchEvent.Type type = e.getType();
281+
SearchContext context = e.getSearchContext();
282+
SearchResult result = null;
283+
284+
switch (type) {
285+
case MARK_ALL:
286+
result = SearchEngine.markAll(editorPane, context);
287+
break;
288+
case FIND:
289+
result = SearchEngine.find(editorPane, context);
290+
if (!result.wasFound() || result.isWrapped()) {
291+
UIManager.getLookAndFeel().provideErrorFeedback(editorPane);
292+
}
293+
break;
294+
case REPLACE:
295+
result = SearchEngine.replace(editorPane, context);
296+
if (!result.wasFound() || result.isWrapped()) {
297+
UIManager.getLookAndFeel().provideErrorFeedback(editorPane);
298+
}
299+
break;
300+
case REPLACE_ALL:
301+
result = SearchEngine.replaceAll(editorPane, context);
302+
JOptionPane.showMessageDialog(null, result.getCount()
303+
+ " occurrences replaced.");
304+
break;
305+
default:
306+
statusBar.setText("Unknown search event");
307+
break;
308+
}
309+
310+
if(result == null){
311+
return;
312+
}
313+
314+
String text;
315+
if (result.wasFound()) {
316+
text = "Text found; occurrences marked: " + result.getMarkedCount();
317+
} else if (type == SearchEvent.Type.MARK_ALL) {
318+
if (result.getMarkedCount() > 0) {
319+
text = "Occurrences marked: " + result.getMarkedCount();
320+
} else {
321+
text = "";
322+
}
323+
} else {
324+
text = "Text not found";
325+
}
326+
statusBar.setText(text);
233327

328+
}
329+
234330
// --- Rendering/Control Logic ---
235331
/**
236332
* Implements the debounce mechanism.

sierra-tools/previewer/src/main/resources/org/httprpc/sierra/tools/previewer/MainFrame.xml

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
<popup-menu-separator/>
1313
<menu-item name="exitItem" text="Exit"/>
1414
</menu>
15+
<menu name="searchMenu" text="Search">
16+
<menu-item name="findItem" text="Find"/>
17+
<menu-item name="replaceItem" text="Replace"/>
18+
</menu>
1519
<menu text="About">
1620
<menu-item name="aboutItem" text="About Previewer"/>
1721
</menu>
@@ -22,6 +26,6 @@
2226
<column-panel name="previewPanel" padding="5, 5, 5, 5" weight="1"/>
2327
</split-pane>
2428

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

0 commit comments

Comments
 (0)