Empty jtextfield/jpassword delete sound - java

Well, am working on a java project which requires input from the user. I realised that if the user press the backspace when there are no characters in the field, a window warning sound is heard. How can I stop this please. My system is windows 10 if at all the behavior may be different on different platforms. Thank you.

behavior may be different on different platforms.
Yes, the behaviour can be different because it is controlled by the LAF, so you should not really be changing it.
But to understand how Swing works you need to understand that Swing uses an Action provided by the DefaultEditorKit to provide the editing functions of text components.
Following is the code for the current "delete previous character" Action (taken from the DefaultEditKit):
/*
* Deletes the character of content that precedes the
* current caret position.
* #see DefaultEditorKit#deletePrevCharAction
* #see DefaultEditorKit#getActions
*/
static class DeletePrevCharAction extends TextAction {
/**
* Creates this object with the appropriate identifier.
*/
DeletePrevCharAction() {
super(DefaultEditorKit.deletePrevCharAction);
}
/**
* The operation to perform when this action is triggered.
*
* #param e the action event
*/
public void actionPerformed(ActionEvent e) {
JTextComponent target = getTextComponent(e);
boolean beep = true;
if ((target != null) && (target.isEditable())) {
try {
Document doc = target.getDocument();
Caret caret = target.getCaret();
int dot = caret.getDot();
int mark = caret.getMark();
if (dot != mark) {
doc.remove(Math.min(dot, mark), Math.abs(dot - mark));
beep = false;
} else if (dot > 0) {
int delChars = 1;
if (dot > 1) {
String dotChars = doc.getText(dot - 2, 2);
char c0 = dotChars.charAt(0);
char c1 = dotChars.charAt(1);
if (c0 >= '\uD800' && c0 <= '\uDBFF' &&
c1 >= '\uDC00' && c1 <= '\uDFFF') {
delChars = 2;
}
}
doc.remove(dot - delChars, delChars);
beep = false;
}
} catch (BadLocationException bl) {
}
}
if (beep) {
UIManager.getLookAndFeel().provideErrorFeedback(target);
}
}
}
If you don't like the beep then you would need to create your own custom Action to remove the beep sound. (ie. don't provide the error feedback). Once you customize the Action you can than change a single text field using:
textField.getActionMap().put(DefaultEditorKit.deletePrevCharAction, new MyDeletePrevCharAction());
Or you can change all text fields using:
ActionMap am = (ActionMap)UIManager.get("TextField.actionMap");
am.put(DefaultEditorKit.deletePrevCharAction, new MyDeletePrevCharAction());

Related

Replace text inside a PDF file using iText

Im using iText(5.5.13) library to read a .PDF and replace a pattern inside the file. The problem is that the pattern is not being found because somehow some weird characters appear when the library reads the pdf.
For example, in the sentence :
"This is a test in order to see if the"
becomes this one when I'm trying to read it:
[(This is a )9(te)-3(st)9( in o)-4(rd)15(er )-2(t)9(o)-5( s)8(ee)7( if t)-3(h)3(e )]
So if I tried to find and replace "test", no "test" word would be found in the pdf and it won't be replaced
here is the code i'm using:
public void processPDF(String src, String dest) {
try {
PdfReader reader = new PdfReader(src);
PdfArray refs = null;
PRIndirectReference reference = null;
int nPages = reader.getNumberOfPages();
for (int i = 1; i <= nPages; i++) {
PdfDictionary dict = reader.getPageN(i);
PdfObject object = dict.getDirectObject(PdfName.CONTENTS);
if (object.isArray()) {
refs = dict.getAsArray(PdfName.CONTENTS);
ArrayList<PdfObject> references = refs.getArrayList();
for (PdfObject r : references) {
reference = (PRIndirectReference) r;
PRStream stream = (PRStream) PdfReader.getPdfObject(reference);
byte[] data = PdfReader.getStreamBytes(stream);
String dd = new String(data, "UTF-8");
dd = dd.replaceAll("#pattern_1234", "trueValue");
dd = dd.replaceAll("test", "tested");
stream.setData(dd.getBytes());
}
}
if (object instanceof PRStream) {
PRStream stream = (PRStream) object;
byte[] data = PdfReader.getStreamBytes(stream);
String dd = new String(data, "UTF-8");
System.out.println("content---->" + dd);
dd = dd.replaceAll("#pattern_1234", "trueValue");
dd = dd.replaceAll("This", "FIRST");
stream.setData(dd.getBytes(StandardCharsets.UTF_8));
}
}
PdfStamper stamper = new PdfStamper(reader, new FileOutputStream(dest));
stamper.close();
reader.close();
}
catch (Exception e) {
}
}
As has already been mentioned in comments and answers, PDF is not a format meant for text editing. It is a final format, and information on the flow of text, its layout, and even its mapping to Unicode is optional.
Thus, even assuming the optional information on mapping glyphs to Unicode are present, the approach to this task with iText might look a bit unsatisfying: First one would determine the position of the text in question using a custom text extraction strategy, then continue by removing the current contents of everything at that position using the PdfCleanUpProcessor, and finally draw the replacement text into the gap.
In this answer I would present a helper class allowing to combine the first two steps, finding and removing the existing text, with the advantage that indeed only the text is removed, not also any background graphics etc. as in case of PdfCleanUpProcessor redaction. The helper furthermore returns the positions of the removed text allowing stamping of replacement thereon.
The helper class is based on the PdfContentStreamEditor presented in this earlier answer. Please use the version of this class on github, though, as the original class has been enhanced a bit since conception.
The SimpleTextRemover helper class illustrates what is necessary to properly remove text from a PDF. Actually it is limited in a few aspects:
It only replaces text in the actual page content streams.
To also replace text in embedded XObjects, one has to iterate through the XObject resources of the respective page in question recursively and also apply the editor to them.
It is "simple" in the same way the SimpleTextExtractionStrategy is: It assumes the text showing instructions to appear in the content in reading order.
To also work with content streams for which the order is different and the instructions must be sorted, and this implies that all incoming instructions and relevant render information must be cached until the end of page, not merely a few instruction at a time. Then the render information can be sorted, sections to remove can be identified in the sorted render information, the associated instructions can be manipulated, and the instructions can eventually be stored.
It does not try to identify gaps between glyphs that visually represent a white space while there actually is no glyph at all.
To identify gaps the code must be extended to check whether two consecutive glyphs exactly follow one another or whether there is a gap or a line jump.
When calculating the gap to leave where a glyph is removed, it does not yet take the character and word spacing into account.
To improve this, the glyph width calculation must be improved.
Considering your example excerpt from your content stream, though, you these restrictions probably won't hinder you.
public class SimpleTextRemover extends PdfContentStreamEditor {
public SimpleTextRemover() {
super (new SimpleTextRemoverListener());
((SimpleTextRemoverListener)getRenderListener()).simpleTextRemover = this;
}
/**
* <p>Removes the string to remove from the given page of the
* document in the PDF reader the given PDF stamper works on.</p>
* <p>The result is a list of glyph lists each of which represents
* a match can can be queried for position information.</p>
*/
public List<List<Glyph>> remove(PdfStamper pdfStamper, int pageNum, String toRemove) throws IOException {
if (toRemove.length() == 0)
return Collections.emptyList();
this.toRemove = toRemove;
cachedOperations.clear();
elementNumber = -1;
pendingMatch.clear();
matches.clear();
allMatches.clear();
editPage(pdfStamper, pageNum);
return allMatches;
}
/**
* Adds the given operation to the cached operations and checks
* whether some cached operations can meanwhile be processed and
* written to the result content stream.
*/
#Override
protected void write(PdfContentStreamProcessor processor, PdfLiteral operator, List<PdfObject> operands) throws IOException {
cachedOperations.add(new ArrayList<>(operands));
while (process(processor)) {
cachedOperations.remove(0);
}
}
/**
* Removes any started match and sends all remaining cached
* operations for processing.
*/
#Override
public void finalizeContent() {
pendingMatch.clear();
try {
while (!cachedOperations.isEmpty()) {
if (!process(this)) {
// TODO: Should not happen, so warn
System.err.printf("Failure flushing operation %s; dropping.\n", cachedOperations.get(0));
}
cachedOperations.remove(0);
}
} catch (IOException e) {
throw new ExceptionConverter(e);
}
}
/**
* Tries to process the first cached operation. Returns whether
* it could be processed.
*/
boolean process(PdfContentStreamProcessor processor) throws IOException {
if (cachedOperations.isEmpty())
return false;
List<PdfObject> operands = cachedOperations.get(0);
PdfLiteral operator = (PdfLiteral) operands.get(operands.size() - 1);
String operatorString = operator.toString();
if (TEXT_SHOWING_OPERATORS.contains(operatorString))
return processTextShowingOp(processor, operator, operands);
super.write(processor, operator, operands);
return true;
}
/**
* Tries to processes a text showing operation. Unless a match
* is pending and starts before the end of the argument of this
* instruction, it can be processed. If the instructions contains
* a part of a match, it is transformed to a TJ operation and
* the glyphs in question are replaced by text position adjustments.
* If the original operation had a side effect (jump to next line
* or spacing adjustment), this side effect is explicitly added.
*/
boolean processTextShowingOp(PdfContentStreamProcessor processor, PdfLiteral operator, List<PdfObject> operands) throws IOException {
PdfObject object = operands.get(operands.size() - 2);
boolean isArray = object instanceof PdfArray;
PdfArray array = isArray ? (PdfArray) object : new PdfArray(object);
int elementCount = countStrings(object);
// Currently pending glyph intersects parameter of this operation -> cannot yet process
if (!pendingMatch.isEmpty() && pendingMatch.get(0).elementNumber < processedElements + elementCount)
return false;
// The parameter of this operation is subject to a match -> copy as is
if (matches.size() == 0 || processedElements + elementCount <= matches.get(0).get(0).elementNumber || elementCount == 0) {
super.write(processor, operator, operands);
processedElements += elementCount;
return true;
}
// The parameter of this operation contains glyphs of a match -> manipulate
PdfArray newArray = new PdfArray();
for (int arrayIndex = 0; arrayIndex < array.size(); arrayIndex++) {
PdfObject entry = array.getPdfObject(arrayIndex);
if (!(entry instanceof PdfString)) {
newArray.add(entry);
} else {
PdfString entryString = (PdfString) entry;
byte[] entryBytes = entryString.getBytes();
for (int index = 0; index < entryBytes.length; ) {
List<Glyph> match = matches.size() == 0 ? null : matches.get(0);
Glyph glyph = match == null ? null : match.get(0);
if (glyph == null || processedElements < glyph.elementNumber) {
newArray.add(new PdfString(Arrays.copyOfRange(entryBytes, index, entryBytes.length)));
break;
}
if (index < glyph.index) {
newArray.add(new PdfString(Arrays.copyOfRange(entryBytes, index, glyph.index)));
index = glyph.index;
continue;
}
newArray.add(new PdfNumber(-glyph.width));
index++;
match.remove(0);
if (match.isEmpty())
matches.remove(0);
}
processedElements++;
}
}
writeSideEffect(processor, operator, operands);
writeTJ(processor, newArray);
return true;
}
/**
* Counts the strings in the given argument, itself a string or
* an array containing strings and non-strings.
*/
int countStrings(PdfObject textArgument) {
if (textArgument instanceof PdfArray) {
int result = 0;
for (PdfObject object : (PdfArray)textArgument) {
if (object instanceof PdfString)
result++;
}
return result;
} else
return textArgument instanceof PdfString ? 1 : 0;
}
/**
* Writes side effects of a text showing operation which is going to be
* replaced by a TJ operation. Side effects are line jumps and changes
* of character or word spacing.
*/
void writeSideEffect(PdfContentStreamProcessor processor, PdfLiteral operator, List<PdfObject> operands) throws IOException {
switch (operator.toString()) {
case "\"":
super.write(processor, OPERATOR_Tw, Arrays.asList(operands.get(0), OPERATOR_Tw));
super.write(processor, OPERATOR_Tc, Arrays.asList(operands.get(1), OPERATOR_Tc));
case "'":
super.write(processor, OPERATOR_Tasterisk, Collections.singletonList(OPERATOR_Tasterisk));
}
}
/**
* Writes a TJ operation with the given array unless array is empty.
*/
void writeTJ(PdfContentStreamProcessor processor, PdfArray array) throws IOException {
if (!array.isEmpty()) {
List<PdfObject> operands = Arrays.asList(array, OPERATOR_TJ);
super.write(processor, OPERATOR_TJ, operands);
}
}
/**
* Analyzes the given text render info whether it starts a new match or
* finishes / continues / breaks a pending match. This method is called
* by the {#link SimpleTextRemoverListener} registered as render listener
* of the underlying content stream processor.
*/
void renderText(TextRenderInfo renderInfo) {
elementNumber++;
int index = 0;
for (TextRenderInfo info : renderInfo.getCharacterRenderInfos()) {
int matchPosition = pendingMatch.size();
pendingMatch.add(new Glyph(info, elementNumber, index));
if (!toRemove.substring(matchPosition, matchPosition + info.getText().length()).equals(info.getText())) {
reduceToPartialMatch();
}
if (pendingMatch.size() == toRemove.length()) {
matches.add(new ArrayList<>(pendingMatch));
allMatches.add(new ArrayList<>(pendingMatch));
pendingMatch.clear();
}
index++;
}
}
/**
* Reduces the current pending match to an actual (partial) match
* after the addition of the next glyph has invalidated it as a
* whole match.
*/
void reduceToPartialMatch() {
outer:
while (!pendingMatch.isEmpty()) {
pendingMatch.remove(0);
int index = 0;
for (Glyph glyph : pendingMatch) {
if (!toRemove.substring(index, index + glyph.text.length()).equals(glyph.text)) {
continue outer;
}
index++;
}
break;
}
}
String toRemove = null;
final List<List<PdfObject>> cachedOperations = new LinkedList<>();
int elementNumber = -1;
int processedElements = 0;
final List<Glyph> pendingMatch = new ArrayList<>();
final List<List<Glyph>> matches = new ArrayList<>();
final List<List<Glyph>> allMatches = new ArrayList<>();
/**
* Render listener class used by {#link SimpleTextRemover} as listener
* of its content stream processor ancestor. Essentially it forwards
* {#link TextRenderInfo} events and ignores all else.
*/
static class SimpleTextRemoverListener implements RenderListener {
#Override
public void beginTextBlock() { }
#Override
public void renderText(TextRenderInfo renderInfo) {
simpleTextRemover.renderText(renderInfo);
}
#Override
public void endTextBlock() { }
#Override
public void renderImage(ImageRenderInfo renderInfo) { }
SimpleTextRemover simpleTextRemover = null;
}
/**
* Value class representing a glyph with information on
* the displayed text and its position, the overall number
* of the string argument of a text showing instruction
* it is in and the index at which it can be found therein,
* and the width to use as text position adjustment when
* replacing it. Beware, the width does not yet consider
* character and word spacing!
*/
public static class Glyph {
public Glyph(TextRenderInfo info, int elementNumber, int index) {
text = info.getText();
ascent = info.getAscentLine();
base = info.getBaseline();
descent = info.getDescentLine();
this.elementNumber = elementNumber;
this.index = index;
this.width = info.getFont().getWidth(text);
}
public final String text;
public final LineSegment ascent;
public final LineSegment base;
public final LineSegment descent;
final int elementNumber;
final int index;
final float width;
}
final PdfLiteral OPERATOR_Tasterisk = new PdfLiteral("T*");
final PdfLiteral OPERATOR_Tc = new PdfLiteral("Tc");
final PdfLiteral OPERATOR_Tw = new PdfLiteral("Tw");
final PdfLiteral OPERATOR_Tj = new PdfLiteral("Tj");
final PdfLiteral OPERATOR_TJ = new PdfLiteral("TJ");
final static List<String> TEXT_SHOWING_OPERATORS = Arrays.asList("Tj", "'", "\"", "TJ");
final static Glyph[] EMPTY_GLYPH_ARRAY = new Glyph[0];
}
(SimpleTextRemover helper class)
You can use it like this:
PdfReader pdfReader = new PdfReader(SOURCE);
PdfStamper pdfStamper = new PdfStamper(pdfReader, RESULT_STREAM);
SimpleTextRemover remover = new SimpleTextRemover();
System.out.printf("\ntest.pdf - Test\n");
for (int i = 1; i <= pdfReader.getNumberOfPages(); i++)
{
System.out.printf("Page %d:\n", i);
List<List<Glyph>> matches = remover.remove(pdfStamper, i, "Test");
for (List<Glyph> match : matches) {
Glyph first = match.get(0);
Vector baseStart = first.base.getStartPoint();
Glyph last = match.get(match.size()-1);
Vector baseEnd = last.base.getEndPoint();
System.out.printf(" Match from (%3.1f %3.1f) to (%3.1f %3.1f)\n", baseStart.get(I1), baseStart.get(I2), baseEnd.get(I1), baseEnd.get(I2));
}
}
pdfStamper.close();
(RemovePageTextContent test testRemoveTestFromTest)
with the following console output for my test file:
test.pdf - Test
Page 1:
Match from (134,8 666,9) to (177,8 666,9)
Match from (134,8 642,0) to (153,4 642,0)
Match from (172,8 642,0) to (191,4 642,0)
and the occurrences of "Test" missing at those positions in the output PDF.
Instead of outputting the match coordinates, you can use them to draw replacement text at the position in question.
A PDF file is not a Word Processing file. What you see are explicit placement of characters that are kerned together and/or many other things. your dream to "replace" text in such a way is not possible or better said, not likely if not impossible.
A PDF is a binary file with byte offsets. It have many parts. Like this is at this byte offset and read this, then go that that byte offset and read that.
You cannot just replace "foo" with "foobar" and think that it will work. It would disrupt all byte offsets and break the file completely.
Try it yourself before even asking.
In your example you have above, open the file in some editor and change the string in what you posted from this:
This is a
to this:
WOW Let me change this data around for the content "This is a"
Save that file and try an open it. Even that, which is a set string of content not crossing the boundaries you identified will not work. Because it is not a Word Processing file. It is not a text file. It is a binary file that you cannot manipulate as you think you can.

Optimising Code an Array of Strings for a History in Java

I am seeking guidance in the respect of optimising code. The code I have written is for a text-based game in which you type in commands into a command bar. One feature I wished to incorporate into my interface was the ability to scroll through a history of one's last 100 commands entered using the up and down arrow keys so that it would be more convenient for the user to play the game.
I have designed a class in which uses a String[] that will store each new entry in the second position (Array[1]) and move all entries back one position while the first position of the array (Array[0]) is just a blank, empty string. The code initialises the array to have 101 values to compensate for the first position being a blank line.
When a user inputs 0 - 100 in that order, it should then give me the reverse of the order (almost like a last in, first out kind of situation, but storing the last 100 values as opposed to removing them once they are accessed), and since 0 - 100 is 101 values, the last value will be overwritten.
Thus, scrolling up through the history, it would give me 100, 99, 98, ..., 2, 1. If I were to select 50 from the list, it would then be 50, 100, 99, ..., 3, 2. The code indeed does this.
The code is listed below:
public class CommandHistory {
private String[] history;
private final int firstIndex = 1;
private static int currentIndex = 0;
/**
* Default constructor, stores last 100 entries of commands plus the blank
* entry at the first index
*/
public CommandHistory() {
history = new String[101];
}
/**
* Constructor with a capacity, stores the last (capacity) entries of
* commands plus the blank entry at the first index
*
* #param capacity
* Capacity of the commands history list
*/
public CommandHistory(int capacity) {
history = new String[capacity + 1];
}
/**
* Returns the size (length) of the history list
*
* #return The size (length) of the history list
*/
private int size() {
return history.length;
}
/**
* Adds a command to the command history log
*
* #param command
* Command to be added to the history log
*/
public void add(String command) {
history[0] = "";
if (!command.equals("")) {
for (int i = firstIndex; i < size();) {
if (history[i] == null) {
history[i] = command;
break;
} else {
for (int j = size() - 1; j > firstIndex; j--) {
history[j] = history[j - 1];
}
history[firstIndex] = command;
break;
}
}
currentIndex = 0;
}
}
/**
* Gets the previous command in the history list
*
* #return The previous command from the history list
*/
public String previous() {
if (currentIndex > 0) {
currentIndex--;
}
return history[currentIndex];
}
/**
* Gets the next command in the history list
*
* #return The next command from the history list
*/
public String next() {
if (currentIndex >= 0 && (history[currentIndex + 1] != null)) {
currentIndex++;
}
return history[currentIndex];
}
/**
* Clears the command history list
*/
public void clear() {
for (int i = firstIndex; i < size(); i++) {
history[i] = null;
}
currentIndex = 0;
}
/**
* Returns the entire command history log
*/
public String toString() {
String history = "";
for (int i = 0; i < size(); i++) {
history += this.history[i];
}
return history;
}
}
In my interface class, once the user types something into the command bar and hits enter, it will get the text currently stored in the bar, uses the add method to add it to the history, parses the command via another class, and then sets the text in the bar to blank.
Pressing the up arrow calls the next method which scrolls up the list, and the down arrow calls the previous method which scrolls down the list.
It seems to work in every way I wish it to, but I was wondering if there was some way to optimise this code or perhaps even code it in a completely different way. I am making this game to keep myself practiced in Java and also to learn new and more advanced things, so I'd love to hear any suggestions on how to do so.
The comments to your question have already pointed out that you are somehow trying to reinvent the wheel by implementing functionality that the standard Java class library already provides to some extent (see LinkedList/Queue and Arraylist). But since you say you want to keep yourself practiced in Java I guess it is perfectly fine if you try to implement your own command history from scratch.
Here are some of my observations/suggestions:
1) It is not necessary and very counter-intuitive to declare a final first index of 1. It would be easy to start with a default index of 0 and add corresponding checks where necessary.
2) Forget about your private size() method - it is just returning the length of the internal array anyway (i.e. the initial capacity+1). Instead consider adding a public size() method that returns the actual number of added commands and internally update the actual size when adding new commands (see e.g. java.util.ArrayList for reference).
3) At the moment every call to add(String command) will set history[0] = "", which is not necessary. If you want the first index to be "", set it in the constructor. This is also a clear sign, that it would perhaps be better to start with an initial index of 0 instead of 1.
4) A minor issue: "if (!command.equals(""))" during your add method is perhaps OK for such a specialized class but it should definitely be commented in the documentation of the method. Personally I would always let the calling class decide if an empty "" command is considered valid or not. Also this method will throw an undocumented NullPointerException, when null is used as an argument. Consider changing this to "if (!"".equals(command))" or throw an IllegalArgumentException if null is added.
5) "if (history[i] == null)" during the add method is completely unnecessary, if you internally keep a pointer to the actual size of the commands - this is actually a special case that will only be true, when the very first command is added to the command history (i.e. when it's actual size == 0).
6) Having two nested for loops in your add method implementation is also unnecessary, if you keep a pointer to the actual size (see example below)
7) I would reconsider if it is necessary to keep a pointer to the current index in the command history. Personally I would avoid storing such a pointer and leave these details to the calling class - i.e. remove the previous and next methods and either provide a forward/backward Iterator and/or a random access to the index of the available commands. Interestingly, when this functionality is removed from your command history class, it actually comes down to either an implementation of a LinkedList or an ArrayList- whichever way you go. So in the end using one of the built in Java collections would actually be the way to go.
8) Last but nor least I would reconsider if it is useful to insert added commands at the beginning of the list - I believe it would be more natural to append them to the end as e.g. ArrayList does. Adding the commands to the end would make the swapping of all current commands during each call to add() unnecessary...
Here are some of the suggested changes to your class (not really tested...)
public class CommandHistory {
private String[] history;
private int size;
private static int currentIndex = 0;
/**
* Default constructor, stores last 100 entries of commands plus the blank
* entry at the first index
*/
public CommandHistory() {
this(100);
}
/**
* Constructor with a capacity, stores the last (capacity) entries of
* commands plus the blank entry at the first index
*
* #param capacity
* Capacity of the commands history list
*/
public CommandHistory(int capacity) {
history = new String[capacity];
}
/**
* Returns the size (length) of the history list
*
* #return The size (length) of the history list
*/
public int size() {
return size;
}
/**
* Adds a command to the command history log
*
* #param command
* Command to be added to the history log
*/
public void add(String command) {
if (!"".equals(command)) {
if (this.size < history.length) {
this.size++;
}
for (int i = size-1; i >0; i--) {
history[i] = history[i-1];
}
history[0] = command;
currentIndex = 0;
}
}
/**
* Gets the previous command in the history list
*
* #return The previous command from the history list
*/
public String previous() {
if (currentIndex >= 0 && currentIndex < size-1) {
currentIndex++;
}
return history[currentIndex];
}
/**
* Gets the next command in the history list
*
* #return The next command from the history list
*/
public String next() {
if (currentIndex > 0 && currentIndex < size) {
currentIndex--;
}
return history[currentIndex];
}
/**
* Clears the command history list
*/
public void clear() {
for (int i = 0; i < size; i++) {
history[i] = null;
}
currentIndex = 0;
}
/**
* Returns the entire command history log
*/
public String toString() {
String history = "";
for (int i = 0; i < size; i++) {
history += this.history[i] + ", ";
}
return history;
}
}
Well, I guess I have invested far too much time for this, but I learned quite a bit myself on the way - so thanks ;-)
Hope some of this is useful for you.

Java swt KeyListener "solution" barcode reading

I found some good Q/A here on my problem but couldn't find the right one.
I have a barcode reader that reads barcode and sends scanned code as keyboard input. It is alright I can catch input easily
browser.addKeyListener(new KeyAdapter() {
#Override public void keyPressed(KeyEvent e) {
if(e.keyCode >=48 && e.keyCode <=57) {
System.out.println("number caught");
}
}
});
But I will have more inputs in my application so I need to know if it is send by barcode reader or by keyboard.
I think it can be achieved by adding some timer in code that verifies how long is some "sequence" reading.
I just can not figure it out, (I mean logic behind it), I am missing piece of logic.
User is typing some info, (alpha numerical)
user desides to use barcode reader to read barcode
I tried timer e.g
if(System.currentTimeMillis() - lastPressProcessed ??? 500) { after keyListener is triggered but I think I am missing something.
sidenote:
USB barcode reads code fast so keystrokes are emulated really fast est whole barcode is written in about 1 second + carry /r/n (also enter is pressed).
sidenote2: barcodes are going to be different in length so I can not read just some length in short time and decide wether it is user input or barcode input (max numbers read 13 + enter).
sidenote3: I have no input field for barcode I am trying to achieve running it on "background".
I am seeking logic/pseudocode suggestions on topic.
related topics that are really close to mine are here, and here
Thank you.
edit
After deep tought I found out the solution I'll keep this Q here just for another users that might find this usable.
solution
--moved to answer + edited
This code coveres everything I wanted to achieve, it reads just numbers (actualy numbers that are under F keys, not numbers that are on numpad, I had problem with it because scanner is keyboard dependant so I made function signsToNumbers() that converts signs !##$%^&*() to numbers 1234567890. I may change this function because every key on keyboard has its own unique identifier + modifier, it seems that scanner sends also SHIFT modifier to the application but that is not as problem as it seems I'll just match e.keyCode.
The code below works as:
waits for number input otherwise does nothing
if 1st number is inserted it is looping in if condition until either 200ms is reached or '\r\n` is received
sends data to server via URL
code
#Override public void keyPressed(KeyEvent e) {
if (timer == true && System.currentTimeMillis() - lastTimer < 200) {
if(e.keyCode >=48 && e.keyCode <=57) { //number pressed
lastTimer = System.currentTimeMillis();
myString = myString + Character.toString(e.character);
}
if(e.keyCode == SWT.CR) {
myString = signsToNumbers(myString);
newUrl = browser.getUrl()+ "/newcode/" + myString;
browser.setUrl(newUrl);
text.setText(newUrl);
System.out.println(myString);
System.out.println("barcode read");
myString = "";
timer = false;
lastTimer = 0;
}
}else{
if(e.keyCode >=48 && e.keyCode <=57) {
lastTimer = System.currentTimeMillis();
timer = true;
myString = Character.toString(e.character);
}
myString = "";
lastTimer = 0;
}
}
});
Here you can download my solution:
http://jhead.hu/resource/java/general/BarcodeReader.java
The following code sample shows you, how to use it. When a new barcode is identified, an ActionEvent is generated and you can get the barcode via the getActionCommand() method. If the panel is not active you can send the characters further to the focus manager.
The only problem is that my barcode scanner sends the characters too fast so the character bits are sometimes mixed. I've got no better solution yet.
public class PanelWithBarcodeReading extends javax.swing.JPanel implements ActionListener {
private BarcodeReader barcodeReader = new BarcodeReader();
public PanelWithBarcodeReading() {
initComponents();
barcodeReader.addActionListener(this);
barcodeReader.setParent(this);
KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher(barcodeReader);
}
#Override
public void actionPerformed(ActionEvent e) {
if (SwingUtilities.getWindowAncestor(this).isActive()) {
System.out.println("BARCODE='" + e.getActionCommand() + "'");
} else {
barcodeReader.dispatchLastBarcodeAsKeyEvents();
}
}
...
}

JAVA - How to change states between single keyPressed of character 'F' / 'f'?

At the moment , when I hit F or f :
private static final char FILL_POLYGON_LOWERCASE = 'f';
private static final char FILL_POLYGON = 'F';
#Override
public void keyTyped(KeyEvent keyEvent)
{
PolygonFiller polyFiller = new PolygonFiller();
char key = keyEvent.getKeyChar();
switch(key)
{
/**
* Fill the polygons
*/
case FILL_POLYGON:
{
if (greenLightForFilling == true)
{
fillPolygon(polyFiller);
System.out.print("called");
}
break;
} // end FILL_POLYGON
case FILL_POLYGON_LOWERCASE:
{
if (greenLightForFilling == true)
{
fillPolygon(polyFiller);
}
break;
}
...
}
The program goes into fillPolygon(polyFiller); .
Meaning , when I hit for the first time f , I go into fillPolygon() .
How can I go into some other method , for example other() , when I hit f or F again ?
Thanks
So the thing is, if you click f/F you goto fill polygon, and pressing f/F again will call other().
This can be a classic case of Stateful Class.
Have an attribute in this at class level.
And on entering f/F check the value and increment it by one.
And on entering f/F again, check the value and increment it by one.
Before each increment you should check whether,
//Am assuming that there are more than two functions, else could use boolean
if (value == 1) {
fillpolygon();
}
else if (value == 2) {
other();
}
else if (value == 2) {
some_other();
}
Remember the entry point will be a single function, from there the flow is delegated based on checks similar to this.
Hope this helps.
Store the previously used button in a variable like 'currentCommand' and 'previousCommand'. Everytime you detect a new input you put current to previous and store the new one in the current member.
Or maybe if you want more than the last two key pressed use a stack.
How can I go into some other method, for example other(), when I hit f or F again ?
You need to introduce a boolean flag.
This is a very simple example of a state machine.

Ignoring focusLost(), SWT.Verify, or other SWT listeners in Java code

Outside of the actual SWT listener, is there any way to ignore a listener via code?
For example, I have a java program that implements SWT Text Widgets, and the widgets have:
SWT.Verify listeners to filter out unwanted text input.
ModifyListeners to wait for the correct number of valid input characters and automatically set focus (using setFocus())to the next valid field, skipping the other text widgets in the tab order.
focusLost(FocusEvent) FocusListeners that wait for the loss of focus from the text widget to perform additional input verification and execute an SQL query based on the user input.
The issue I run into is clearing the text widgets. One of the widgets has the format "####-##" (Four Numbers, a hyphen, then two numbers) and I have implemented this listener, which is a modified version of SWT Snippet Snippet179. The initial text for this text widget is " - " to provide visual feedback to the user as to the expected format. Only numbers are acceptable input, and the program automatically skips past the hyphen at the appropriate point.
/*
* This listener was adapted from the "verify input in a template (YYYY/MM/DD)" SWT Code
* Snippet (also known as Snippet179), from the Snippets page of the SWT Project.
* SWT Code Snippets can be found at:
* http://www.eclipse.org/swt/snippets/
*/
textBox.addListener(SWT.Verify, new Listener()
{
boolean ignore;
public void handleEvent(Event e)
{
if (ignore) return;
e.doit = false;
StringBuffer buffer = new StringBuffer(e.text);
char[] chars = new char[buffer.length()];
buffer.getChars(0, chars.length, chars, 0);
if (e.character == '\b')
{
for (int i = e.start; i < e.end; i++)
{
switch (i)
{
case 0: /* [x]xxx-xx */
case 1: /* x[x]xx-xx */
case 2: /* xx[x]x-xx */
case 3: /* xxx[x]-xx */
case 5: /* xxxx-[x]x */
case 6: /* xxxx-x[x] */
{
buffer.append(' ');
break;
}
case 4: /* xxxx[-]xx */
{
buffer.append('-');
break;
}
default:
return;
}
}
textBox.setSelection(e.start, e.start + buffer.length());
ignore = true;
textBox.insert(buffer.toString());
ignore = false;
textBox.setSelection(e.start, e.start);
return;
}
int start = e.start;
if (start > 6) return;
int index = 0;
for (int i = 0; i < chars.length; i++)
{
if (start + index == 4)
{
if (chars[i] == '-')
{
index++;
continue;
}
buffer.insert(index++, '-');
}
if (chars[i] < '0' || '9' < chars[i]) return;
index++;
}
String newText = buffer.toString();
int length = newText.length();
textBox.setSelection(e.start, e.start + length);
ignore = true;
textBox.insert(newText);
ignore = false;
/*
* After a valid key press, verifying if the input is completed
* and passing the cursor to the next text box.
*/
if (7 == textBox.getCaretPosition())
{
/*
* Attempting to change the text after receiving a known valid input that has no results (0000-00).
*/
if ("0000-00".equals(textBox.getText()))
{
// "0000-00" is the special "Erase Me" code for these text boxes.
ignore = true;
textBox.setText(" - ");
ignore = false;
}
// Changing focus to a different textBox by using "setFocus()" method.
differentTextBox.setFocus();
}
}
}
);
As you can see, the only method I've figured out to clear this text widget from a different point in the code is by assigning "0000-00"
textBox.setText("000000")
and checking for that input in the listener. When that input is received, the listener changes the text back to " - " (four spaces, a hyphen, then two spaces).
There is also a focusLost Listener that parses this text widget for spaces, then in order to avoid unnecessary SQL queries, it clears/resets all fields if the input is invalid (i.e contains spaces).
// Adding focus listener to textBox to wait for loss of focus to perform SQL statement.
textBox.addFocusListener(new FocusAdapter()
{
#Override
public void focusLost(FocusEvent evt)
{
// Get the contents of otherTextBox and textBox. (otherTextBox must be <= textBox)
String boxFour = otherTextBox.getText();
String boxFive = textBox.getText();
// If either text box has spaces in it, don't perform the search.
if (boxFour.contains(" ") || boxFive.contains(" "))
{
// Don't perform SQL statements. Debug statement.
System.out.println("Tray Position input contains spaces. Ignoring.");
//Make all previous results invisible, if any.
labels.setVisible(false);
differentTextBox.setText("");
labelResults.setVisible(false);
}
else
{
//... Perform SQL statement ...
}
}
}
);
OK. Often, I use SWT MessageBox widgets in this code to communicate to the user, or wish to change the text widgets back to an empty state after verifying the input. The problem is that messageboxes seem to create a focusLost event, and using the .setText(string) method is subject to SWT.Verify listeners that are present on the text widget.
Any suggestions as to selectively ignoring these listeners in code, but keeping them present for all other user input?
Thank you in advance for your assistance.
If you name the listener instead of using an anonymous one, you can add and remove it whenever you like.
Example:
// Adding focus listener to textBox to wait for loss of focus to perform SQL statement.
FocusAdapter focusTextBox = new FocusAdapter()
{
#Override
public void focusLost(FocusEvent evt)
{
// Get the contents of otherTextBox and textBox. (otherTextBox must be <= textBox)
String boxFour = otherTextBox.getText();
String boxFive = textBox.getText();
// If either text box has spaces in it, don't perform the search.
if (boxFour.contains(" ") || boxFive.contains(" "))
{
// Don't perform SQL statements. Debug statement.
System.out.println("Tray Position input contains spaces. Ignoring.");
//Make all previous results invisible, if any.
labels.setVisible(false);
differentTextBox.setText("");
labelResults.setVisible(false);
}
else
{
//... Perform SQL statement ...
}
}
};
You can then add the listener by doing this:
textBox.addFocusListener(focusTextBox);
Removal is as easy as this:
textBox.removeFocusListener(focusTextBox);
Just make sure to re-enable the listener after you've done what you've wanted to programmatically achieve, or your code will not act like you'd expect.
I didn't tested your code, but try method isFocusControl() in the listener to see if the user enters the text or you set the text with setText() when the focus is not in textBox.

Categories