package eu.irreality.age; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.Vector; import javax.swing.JTextField; import javax.swing.SwingUtilities; import eu.irreality.age.swing.FancyJTextField; class SwingEditBoxListener implements ActionListener , KeyListener { FancyJTextField elCampoJTexto; //JTextComponent elAreaJTexto; ColoredSwingClient cl; //Player esperando; Vector gameLog; boolean press_any_key = false; int ncommands=0; public void countCommand() { ncommands++; } public SwingEditBoxListener ( FancyJTextField nCampoJTexto , Vector gameLog , ColoredSwingClient cl ) { elCampoJTexto = nCampoJTexto; //elAreaJTexto = nAreaJTexto; this.gameLog = gameLog; this.cl = cl; } //public void setWaitingPlayer ( Player p ) //{ // esperando = p; //} //public void setWaitingClient ( Client cl ) public void actionPerformed( ActionEvent e ) { if ( inTransitoryState ) return; //we are changing between normal mode and "press any key" mode, discard all events until change is completed if ( !press_any_key ) { //System.err.println("[DN] editbox action performed, not in PAK state"); if ( cl.isMemoryEnabled() ) { cl.addToBackStack ( elCampoJTexto.getText().trim() ); //reintegrate forward stack into back stack cl.forwardStackIntoBackStack(); //if there was something in forward stack, add this //(this function doesn't add repeated) cl.addToBackStack ( elCampoJTexto.getText().trim() ); } cl.write("\n"); countCommand(); if ( cl.isEchoEnabled() ) cl.write( cl.getColorCode("input") + cl.getEchoText() + elCampoJTexto.getText().trim() + cl.getColorCode("reset") + "\n" ); cl.writeTitle ( ncommands + " comando" + ((ncommands==1)?"":"s") , 2 ); //elAreaJTexto.append("\n"); //elAreaJTexto.append("\n" + "[COMANDO] " + elCampoJTexto.getText()); //elAreaJTexto.getDocument().insertString("\n"); //elAreaJTexto.getDocument().insertString("\n" + "[COMANDO] " + elCampoJTexto.getText()); //esperando.setCommandString(elCampoJTexto.getText()); //notificar (setInputString ya notifica) cl.setInputString(elCampoJTexto.getText()); //cl.notify(); cl.setInactiveColor(); //check for threading errors TODO //System.err.println("[DN] Input string set to " + elCampoJTexto.getText()); //add new command to game log gameLog.addElement(elCampoJTexto.getText()); elCampoJTexto.setText(""); //esperando.resumeExecution(); } else { returnFromPressAnyKeyState(); } } /** * Returns from the "press any key" state, returning the edit box to its normal condition in which text can be typed, * and notifying the game engine thread which was caught in the client object so that world simulation continues. */ private void returnFromPressAnyKeyState ( ) { setPressAnyKeyState(false); //note that this method call includes a notify(), which means that the game engine thread will resume execution. cl.setInputString(null); } /** * Returns from the "press any key" state, if the edit box is on that state, returning the edit box to its normal condition in which text can be typed, * and notifying the game engine thread which was caught in the client object so that world simulation continues. * If the edit box is not on that state, does nothing. */ public void returnFromPressAnyKeyStateIfNeeded ( ) { if ( press_any_key ) returnFromPressAnyKeyState(); } private static boolean isPageUpDownEvent ( KeyEvent e ) { return ( e.getKeyCode() == KeyEvent.VK_PAGE_UP || e.getKeyCode() == KeyEvent.VK_PAGE_DOWN ); } private void redirectToTextArea ( KeyEvent e ) { cl.getTextArea().dispatchEvent(e); } private boolean consumeKeyEvents = false; public void keyTyped(KeyEvent e) { if ( isPageUpDownEvent(e) ) redirectToTextArea(e); if ( press_any_key || consumeKeyEvents ) //don't show the character that they have typed in this case: e.consume(); } public void keyReleased(KeyEvent e) { if ( isPageUpDownEvent(e) ) redirectToTextArea(e); if ( press_any_key || consumeKeyEvents ) //don't show the character that they have typed in this case: { e.consume(); consumeKeyEvents = false; //keyReleased is last event to be consumed for this key } } public boolean isModifierKey ( KeyEvent e ) { return ( e.getKeyCode() == KeyEvent.VK_ALT || e.getKeyCode() == KeyEvent.VK_CONTROL || e.getKeyCode() == KeyEvent.VK_SHIFT || e.getKeyCode() == KeyEvent.VK_META || e.getKeyCode() == KeyEvent.VK_ALT_GRAPH || e.getKeyCode() == KeyEvent.VK_PAUSE || e.getKeyCode() == KeyEvent.VK_PRINTSCREEN ); } public void keyPressed(KeyEvent e) { if ( isPageUpDownEvent(e) ) { redirectToTextArea(e); return; } if ( isModifierKey(e) ) return; if ( press_any_key ) { if ( e.getKeyCode() != KeyEvent.VK_ENTER ) { //ya pulsaron la tecla, contin�a la ejecuci�n normal. returnFromPressAnyKeyState(); e.consume(); consumeKeyEvents = true; //consume also the typed and released events //cl.notify(); } else { //ya pulsaron; pero es adem�s un action event. Ser� �ste el que se //encargue de cambiar el estado. } } else if ( cl.isMemoryEnabled() ) { //doskey (memoria) if ( e.getKeyCode() == KeyEvent.VK_UP ) { //System.out.println("GoBack"); cl.goBack(); } else if ( e.getKeyCode() == KeyEvent.VK_DOWN ) { cl.goForward(); } } } private boolean inTransitoryState = false; public void setPressAnyKeyState ( final boolean value ) { //System.out.println("In PAK, Thread is " + Thread.currentThread()); //System.out.println("And it's " + SwingUtilities.isEventDispatchThread()); if ( SwingUtilities.isEventDispatchThread() ) doSetPressAnyKeyState(value); else { try { /* * The inTransitoryState flag marks that a waitKeyPress() has been called but we have not yet established the * wait key press state in the edit box and edit box listener. * If this flag is not used, we are at risk of the following when holding the ENTER key for a while: * 1. waitKeyPress() is called, but * 2. a pending ActionEvent (ENTER keypress) is processed before the change of "press any key" state is realised * 3. this ActionEvent notifies the waiting waitKeyPress() method, so it returns and the game engine thread continues its run * 4. then, the change of "press any key" state happens, so we are at an inconsistent state: the edit box thinks we are * waiting for a key, but the game engine has moved on and issues a getInput() call * 5. the edit box's next ENTER key press notifies the getInput() call and it returns null */ inTransitoryState = true; SwingUtilities.invokeLater //to process all prev. events ( new Runnable() { public void run() { doSetPressAnyKeyState(value); } } ); } catch ( Exception e ) { e.printStackTrace(); } } } public void doSetPressAnyKeyState ( final boolean value ) { press_any_key = value; if ( value ) { //System.out.println("Setting PAK"); //esperamos por una tecla elCampoJTexto.setForeground(cl.getKeyRequestForeground()); //System.out.println("Setting PAK 1"); elCampoJTexto.setPromptsEnabled(false); elCampoJTexto.setText(cl.getKeyRequestText()); //System.out.println("Setting PAK 2"); elCampoJTexto.setEditable(false); //workaround for java bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6223733 : elCampoJTexto.getCaret().setVisible(false); //System.out.println("Setting PAK 3"); elCampoJTexto.grabFocus(); //System.out.println("Set PAK"); } else { //ejecuci�n normal //System.out.println("Setting UNPAK 1"); elCampoJTexto.setPromptsEnabled(true); elCampoJTexto.setText(""); //System.out.println("Setting UNPAK 2"); elCampoJTexto.setEditable(true); //workaround for java bug http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6223733 : elCampoJTexto.getCaret().setVisible(true); //System.out.println("Setting UNPAK 3"); elCampoJTexto.setForeground( cl.getInputFieldInactiveForeground() ); //until getInput() is called. } inTransitoryState = false; } }