better search box

This commit is contained in:
Zlatin Balevsky
2019-11-03 01:50:55 +00:00
parent 7ade4aa10d
commit e22d5fea11
8 changed files with 213 additions and 29 deletions

View File

@@ -78,10 +78,10 @@ class MuWireSettings {
totalUploadSlots = Integer.valueOf(props.getProperty("totalUploadSlots","-1"))
uploadSlotsPerUser = Integer.valueOf(props.getProperty("uploadSlotsPerUser","-1"))
watchedDirectories = readEncodedSet(props, "watchedDirectories")
watchedKeywords = readEncodedSet(props, "watchedKeywords")
watchedRegexes = readEncodedSet(props, "watchedRegexes")
negativeFileTree = readEncodedSet(props, "negativeFileTree")
watchedDirectories = DataUtil.readEncodedSet(props, "watchedDirectories")
watchedKeywords = DataUtil.readEncodedSet(props, "watchedKeywords")
watchedRegexes = DataUtil.readEncodedSet(props, "watchedRegexes")
negativeFileTree = DataUtil.readEncodedSet(props, "negativeFileTree")
trustSubscriptions = new HashSet<>()
if (props.containsKey("trustSubscriptions")) {
@@ -125,10 +125,10 @@ class MuWireSettings {
props.setProperty("totalUploadSlots", String.valueOf(totalUploadSlots))
props.setProperty("uploadSlotsPerUser", String.valueOf(uploadSlotsPerUser))
writeEncodedSet(watchedDirectories, "watchedDirectories", props)
writeEncodedSet(watchedKeywords, "watchedKeywords", props)
writeEncodedSet(watchedRegexes, "watchedRegexes", props)
writeEncodedSet(negativeFileTree, "negativeFileTree", props)
DataUtil.writeEncodedSet(watchedDirectories, "watchedDirectories", props)
DataUtil.writeEncodedSet(watchedKeywords, "watchedKeywords", props)
DataUtil.writeEncodedSet(watchedRegexes, "watchedRegexes", props)
DataUtil.writeEncodedSet(negativeFileTree, "negativeFileTree", props)
if (!trustSubscriptions.isEmpty()) {
String encoded = trustSubscriptions.stream().
@@ -139,24 +139,6 @@ class MuWireSettings {
props.store(out, "This file is UTF-8")
}
private static Set<String> readEncodedSet(Properties props, String property) {
Set<String> rv = new ConcurrentHashSet<>()
if (props.containsKey(property)) {
String[] encoded = props.getProperty(property).split(",")
encoded.each { rv << DataUtil.readi18nString(Base64.decode(it)) }
}
rv
}
private static void writeEncodedSet(Set<String> set, String property, Properties props) {
if (set.isEmpty())
return
String encoded = set.stream().
map({Base64.encode(DataUtil.encodei18nString(it))}).
collect(Collectors.joining(","))
props.setProperty(property, encoded)
}
boolean isLeaf() {
isLeaf

View File

@@ -11,10 +11,14 @@ import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import com.muwire.core.Constants;
import net.i2p.data.Base64;
import net.i2p.util.ConcurrentHashSet;
public class DataUtil {
@@ -165,4 +169,22 @@ public class DataUtil {
} catch(Exception ex) { }
cb = null;
}
public static Set<String> readEncodedSet(Properties props, String property) {
Set<String> rv = new ConcurrentHashSet<>();
if (props.containsKey(property)) {
String [] encoded = props.getProperty(property).split(",");
for(String s : encoded)
rv.add(readi18nString(Base64.decode(s)));
}
return rv;
}
public static void writeEncodedSet(Set<String> set, String property, Properties props) {
if (set.isEmpty())
return;
String encoded = set.stream().map(s -> Base64.encode(encodei18nString(s)))
.collect(Collectors.joining(","));
props.setProperty(property, encoded);
}
}

View File

@@ -11,6 +11,7 @@ import net.i2p.crypto.DSAEngine
import net.i2p.data.Base64
import net.i2p.data.Signature
import java.awt.event.ActionEvent
import java.nio.charset.StandardCharsets
import javax.annotation.Nonnull
@@ -51,11 +52,16 @@ class MainFrameController {
private volatile Core core
@ControllerAction
void search() {
void search(ActionEvent evt) {
if (evt.getActionCommand() == null)
return
def cardsPanel = builder.getVariable("cards-panel")
cardsPanel.getLayout().show(cardsPanel, "search window")
def search = builder.getVariable("search-field").text
def searchField = builder.getVariable("search-field")
def search = searchField.getSelectedItem()
searchField.model.addElement(search)
search = search.trim()
if (search.length() == 0)
return

View File

@@ -11,6 +11,7 @@ import net.i2p.data.DataHelper
import javax.swing.BorderFactory
import javax.swing.Box
import javax.swing.BoxLayout
import javax.swing.JComboBox
import javax.swing.JFileChooser
import javax.swing.JFrame
import javax.swing.JLabel
@@ -24,6 +25,8 @@ import javax.swing.SwingConstants
import javax.swing.SwingUtilities
import javax.swing.TransferHandler
import javax.swing.border.Border
import javax.swing.event.DocumentEvent
import javax.swing.event.DocumentListener
import javax.swing.event.TreeExpansionEvent
import javax.swing.event.TreeExpansionListener
import javax.swing.table.DefaultTableCellRenderer
@@ -135,7 +138,11 @@ class MainFrameView {
panel(constraints: BorderLayout.CENTER) {
borderLayout()
label(" Enter search here:", constraints: BorderLayout.WEST) // TODO: fix this
textField(id: "search-field", constraints: BorderLayout.CENTER, action : searchAction)
def searchFieldModel = new SearchFieldModel(settings, new File(application.context.get("muwire-home")))
JComboBox myComboBox = new SearchField(searchFieldModel)
myComboBox.setAction(searchAction)
widget(id: "search-field", constraints: BorderLayout.CENTER, myComboBox)
}
panel( constraints: BorderLayout.EAST) {
@@ -484,6 +491,10 @@ class MainFrameView {
}
}})
// search field
def searchField = builder.getVariable("search-field")
// downloads table
def downloadsTable = builder.getVariable("downloads-table")
def selectionModel = downloadsTable.getSelectionModel()
selectionModel.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)

View File

@@ -0,0 +1,14 @@
package com.muwire.gui
import java.awt.event.KeyEvent
import javax.swing.JComboBox
class SearchField extends JComboBox {
SearchField(SearchFieldModel model) {
super()
setEditable(true)
setModel(model)
setEditor(new SearchFieldEditor(model, this))
}
}

View File

@@ -0,0 +1,47 @@
package com.muwire.gui
import javax.swing.JTextField
import javax.swing.SwingUtilities
import javax.swing.event.DocumentEvent
import javax.swing.event.DocumentListener
import javax.swing.plaf.basic.BasicComboBoxEditor
class SearchFieldEditor extends BasicComboBoxEditor {
private final SearchFieldModel model
private final SearchField field
SearchFieldEditor(SearchFieldModel model, SearchField field) {
super()
this.model = model
this.field = field
def action = field.getAction()
field.setAction(null)
editor.setAction(action)
editor.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
SwingUtilities.invokeLater({
field.hidePopup()
if (model.onKeyStroke(editor.text))
field.showPopup()
})
}
@Override
public void removeUpdate(DocumentEvent e) {
SwingUtilities.invokeLater({
field.hidePopup()
if (model.onKeyStroke(editor.text))
field.showPopup()
})
}
@Override
public void changedUpdate(DocumentEvent e) {
}
})
}
}

View File

@@ -0,0 +1,96 @@
package com.muwire.gui
import javax.swing.AbstractListModel
import javax.swing.MutableComboBoxModel
class SearchFieldModel extends AbstractListModel implements MutableComboBoxModel {
private final UISettings uiSettings
private final File settingsFile
private final List<String> objects = new ArrayList<>()
private String selectedObject
SearchFieldModel(UISettings uiSettings, File home) {
super()
this.uiSettings = uiSettings
this.settingsFile = new File(home, "gui.properties")
uiSettings.searchHistory.each { objects.add(it) }
fireIntervalAdded(this, 0, objects.size() - 1)
}
public void addElement(Object string) {
if (!uiSettings.searchHistory.add(string))
return
settingsFile.withOutputStream { uiSettings.write(it) }
objects.add(string);
fireIntervalAdded(this,objects.size()-1, objects.size()-1);
if ( objects.size() == 1 && selectedObject == null && string != null ) {
setSelectedItem( string );
}
}
boolean onKeyStroke(String selected) {
selectedObject = selected
if (selected == null || selected.length() == 0) {
objects.clear()
uiSettings.searchHistory.each { objects.add(it) }
return true
}
objects.clear()
Set<String> matching = new HashSet<>(uiSettings.searchHistory)
matching.retainAll { it.contains(selected) }
matching.each {
objects.add(it)
}
Collections.sort(objects)
if (!objects.isEmpty()) {
fireIntervalAdded(this, 0, objects.size() - 1)
return true
}
false
}
@Override
public void setSelectedItem(Object anObject) {
if ((selectedObject != null && !selectedObject.equals( anObject )) ||
selectedObject == null && anObject != null) {
selectedObject = anObject;
fireContentsChanged(this, -1, -1);
}
}
@Override
public Object getSelectedItem() {
selectedObject
}
@Override
public int getSize() {
objects.size()
}
@Override
public Object getElementAt(int index) {
if ( index >= 0 && index < objects.size() )
return objects.get(index);
else
return null;
}
@Override
public void removeElement(Object obj) {
}
@Override
public void insertElementAt(Object item, int index) {
}
@Override
public void removeElementAt(int index) {
}
}

View File

@@ -1,5 +1,7 @@
package com.muwire.gui
import com.muwire.core.util.DataUtil
class UISettings {
String lnf
@@ -14,6 +16,7 @@ class UISettings {
boolean closeWarning
boolean exitOnClose
boolean clearUploads
Set<String> searchHistory
UISettings(Properties props) {
lnf = props.getProperty("lnf", "system")
@@ -28,6 +31,8 @@ class UISettings {
closeWarning = Boolean.parseBoolean(props.getProperty("closeWarning","true"))
exitOnClose = Boolean.parseBoolean(props.getProperty("exitOnClose","false"))
clearUploads = Boolean.parseBoolean(props.getProperty("clearUploads","false"))
searchHistory = DataUtil.readEncodedSet(props, "searchHistory")
}
void write(OutputStream out) throws IOException {
@@ -46,6 +51,7 @@ class UISettings {
if (font != null)
props.setProperty("font", font)
DataUtil.writeEncodedSet(searchHistory, "searchHistory", props)
props.store(out, "UI Properties")
}