⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 boardcanvas.java

📁 ErGo是一个很早的Java通用围棋服务器(IGS/NNGS)客户端程序。有全部源码和文档
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
   */
  private boolean regenerateRequired = false; 

  public void update (Graphics g) {
    if (getSize().width != width || getSize().height != height) {
      WoodGenerator.release(sideSize); // remove the generator's bitmap at the old size.
      width = getSize().width;
      height = getSize().height;
      sideSize = Math.min(width, height); // note that sideSize includes bevel.
      xoffset  = (width - sideSize) / 2 + getBevelWidth();
      yoffset  = (height - sideSize) / 2 + getBevelWidth();
      cellSize = (sideSize - 2 * getBevelWidth()) / gameSize;
      oodboard = WoodGenerator.request(sideSize, getBevelWidth(), this);
      if (offscreen != null) 
	offscreen.flush();
      offscreen = createImage(sideSize, sideSize);
      // in this case, and in this case only, must we re-draw ALL the 
      // elements of offscreen.....
      tempblit.reinit(cellSize, cellSize);
      // +++ Should tempblitIm be flush()ed here???
      tempblitIm = createImage(tempblit.mis());
      if (offg != null) 
	offg.dispose();
      offg = offscreen.getGraphics();
      if (staticg != null)
	staticg.dispose();
      staticg = getGraphics();
      regenerateRequired = true;
    }
    if (regenerateRequired) {
      // Note that it's important to set regenerateRequired to false immediately
      // here, to avoid a timing problem that will cause the wood board not to
      // be displayed sometimes.  It took a while to track down this bug... 
      regenerateRequired = false;
      stoneManufac();
      drawAll(offg);
      decorateStone(offg);
    }
    g.drawImage(offscreen, 0, 0, null);
  }
  

  // This is called directly by the AWT when the component is exposed (or resized?).
  //
  public void paint (Graphics g) {
    update(g);
  }


  private Position previousShadow = new Position(-1, -1);
  private static StoneMove tempMove;
  private static ILMV tempILMV = new ILMV();
  private static Position tempPosition = new Position(0, 0);

  // +++ In the case of server games this needs to display a temporary
  //     stone over the position the user clicks on, which will be replaced
  //     by a permanent stone once the move has been received from the
  //     server.
  // !!!!! ACK. Incredible dependence on gwin. Logic should be moved,
  // as per comment below.
  // One idea:
  // class GameStateInfo {
  //   Move currentMove();
  //   boolean canPlay();
  //   boolean ourTurn();
  //   boolean scoringMode();
  //   void placeMove(int row, int column); //?
  //   }
  // class GameResult {
  //   boolean isScoring();
  //   }
  private boolean mouseCommon (int x, int y, boolean exited, boolean moved,
			       boolean released) {
    // First we must determine among these situations:
    // 1. A local game - both players play, all the time
    // 2. A server game - 
    //   a) observing - play if browsing
    //   b) a match - play if browsing, or it is our move, or scoring
    //   c) a teaching game - play both all the time.
    // this logic will be moved to Game, GameWindow, or Move, someday..
    if (cellSize == 0) return true; // in some bizarre case, we receive a
                                    // mouse event BEFORE constructor finishes
    int thisColor = Move.nextColor(currentMove.color());
    boolean ourTurn = (gwin.game.scoringMode()
		       || gwin.ourColor() == thisColor
		       || gwin.isTeachingGame());
    int col = xtocol(x);
    int row = ytorow(y);
    boolean onBoard = true;
    if (col < 0	|| row < 0 || col >= gameSize || row >= gameSize
	|| exited)
      onBoard = false;
    boolean isEmpty = (onBoard && ss.readStone(row, col) == Move.EMPTY);
    boolean canPlay
      = onBoard && 
          (gwin.isLocalGame()
	   || gwin.isBrowsing()
	   || gwin.isParticipating()
	     && ourTurn
	     && onBoard
	     // Can't place a move on an empty position during actual
	     // server game scoring.  (i.e., not in browsing mode.)
	     && !(gwin.game.scoringMode() && !gwin.isBrowsing() && isEmpty)
	     && !(currentMove instanceof GameResultMove)
          );
    /*
    Debug.println("gwin.isParticipating() = " + gwin.isParticipating());
    Debug.println("ourTurn = " + ourTurn);
    Debug.println("gwin.game.scoringMode() = " + gwin.game.scoringMode());
    Debug.println("stone = " + (gwin.game.ss.readStone(row, col) != Move.EMPTY));
    Debug.println("canPlay = " + canPlay);
    */
    boolean shadowPossible = onBoard && canPlay;
    if (moved || exited) {
      // now think about shadow stones: 
      if (shadowPossible && previousShadow.row == row 
	  && previousShadow.column == col) 
        return true; 		// no action if shadow has not moved.
      if (previousShadow.row != -1) {
	// there was a previous shadow, and it's not here: 
        reflectis(previousShadow);    // remove it
      }
      boolean isLegal = false;
      if (shadowPossible) {
	// there is a current shadow, test heinously quickly for legality.
        tempPosition.setPosition(row, col);
        if (tempMove == null) 
          tempMove = new StoneMove(currentMove, 0, currentMove.moveNumber() + 1,
				   thisColor, false, tempPosition); 
        else
	  tempMove.reseatforShadow(tempPosition, thisColor, currentMove, 
				   currentMove.moveNumber() + 1);
        isLegal = tempMove.isLegal(gwin.game, true, tempILMV);
//		   && !(gwin.game.scoringMode() && !gwin.isBrowsing()
//			&& (ss.readStone(tempPosition.row,
//						    tempPosition.column)
//			    == Move.EMPTY));
      }
      if (isLegal) {		// there is a current legal shadow, draw it
        if (thisColor == Move.BLACK) 
          drawStone(offg, row, col, Move.BLACKSHADOW);
        else if (thisColor == Move.WHITE)
          drawStone(offg, row, col, Move.WHITESHADOW);
        invalidateInternal(row, col);
        updateInternal();
        previousShadow.row = row;
	previousShadow.column = col;
      }
      else previousShadow.row = -1; // if there is not, store the fact.
    }
    else if (released && canPlay) {
      // Commented out the following line until I get the sound done
      // right.  For now the user will just have to toggle bell on.
      //gwin.window.ergo.thwak();
      gwin.placeMove(row, col);
      ss.emit();
    }
    return true;
  }

  class mouseMotionListener extends MouseMotionAdapter {
    public void mouseMoved (MouseEvent e) {
      mouseCommon(e.getX(), e.getY(), false, true, false);
    }
  }

  class mouseListener extends MouseAdapter {

    // +++ Remove this when/if Java bug #4143473 is fixed.  The problem is that
    // MouseEvent.isPopupTrigger() returns different values during mousePressed()
    // and mouseReleased().  In addition, in Unix isPopupTrigger() returns true
    // in mousePressed() while in Windows+OS/2 it returns true in mouseReleased().
    // As far as I can tell this makes it impossible to write portable code that
    // tries to do one action when it's not the popup trigger and a different action
    // when it is, as I'm trying to do here.
    //
    // The bug is not fixed as of 1.1.7.  -cgay 99-01-16
    //
    public void mouseReleased (MouseEvent e) {
      if (Util.isUnix()) {
	if ((e.getModifiers() & InputEvent.BUTTON3_MASK) == 0)
	  mouseCommon(e.getX(), e.getY(), false, false, true);
      }
      else if (!e.isPopupTrigger())
	mouseCommon(e.getX(), e.getY(), false, false, true);
    }

    public void mouseExited (MouseEvent e) {
      mouseCommon(e.getX(), e.getY(), true, false, false);
    }
  }

  private void setWoodBoard (RawImage newboard) {
    synchronized(ss) {
      oodboard = newboard;
    }
  }

  private int currentBoardStyle () {
    synchronized(ss) {
      return ((oodboard == null && boardStyle == BOARD_WOOD)
	      ? BOARD_BASIC
	      : boardStyle);
    }
  }

  public void refreshLocal () {
    synchronized(ss) {
      // It may have arrived now.
      oodboard = WoodGenerator.request(sideSize, getBevelWidth(), this);
      // ---*** The following debug line causes a deadlock.  I should investigate.
      //Debug.tprintln("refreshLocal(): oodboard = " + oodboard);
      regenerateRequired = true;
    }
    repaint();
  }

}  // end class BoardCanvas



/**
 * This class manages off-thread generation and storage of wood bitmap,
 * which is currently pretty unoptimised. Even when it is optimised,
 * it'll never be an acceptable delay on slow and sub-Pentium systems,
 * so must be asynchronous.  AMB.
 */
class WoodGenerator extends Thread {

  /** Used to represent one request for a wood board.
   */
  private static class WorkEntry {
    int sideSize;
    int bevelWidth;
    // I think toRefresh should be a Vector of Refreshables, so if several boards
    // request the same size they will both be refreshed when it's done.  This
    // may be fairly common case since boards are created with the same size as
    // the most recently created board.  Need to check if this is an issue...  -sigue
    Refreshable toRefresh;
    WorkEntry (int sideSize, int bevelWidth, Refreshable toRefresh) {
      this.sideSize = sideSize;
      this.toRefresh = toRefresh;
      this.bevelWidth = bevelWidth;
    }
  }

  private static final Vector done = new Vector();
  private static final Vector todo = new Vector();
  private static final WoodGenerator woodWorker = new WoodGenerator();
  static {
    woodWorker.setName("Wood");  // For debugging, but harmless.
    woodWorker.start();
  }

  private static RawImage rawImageAt (int i) {
    return (RawImage)done.elementAt(i);
  }

  private static WorkEntry workAt (int i) {
    return (WorkEntry)todo.elementAt(i);
  }

// Public interface of class:

  // ---*** It seems to me the argument should be a Refreshable, and anything
  //        with that refreshable should be released, else one board might
  //        release an entry when another board still needs it.  -sigue
  public static void release (int sideSize) {
    for (int i = 0; i < done.size(); ++i) {
      RawImage look = rawImageAt(i);
      if (look.width == sideSize) {
	done.removeElementAt(i);
      }
    }
    synchronized (woodWorker) {
      for (int i = todo.size() - 1; i >= 0; --i) {
	WorkEntry work = workAt(i);
	if (work.sideSize == sideSize) {
	  todo.removeElementAt(i);
	}
      }
    }
  }

  public static RawImage request (int sideSize, int bevelWidth, Refreshable r) {
    return woodWorker.requesti(sideSize, bevelWidth, r);
  }

  // Must be instance method, or, peculiarly, cannot have monitor.
  //
  private RawImage requesti (int sideSize, int bevelWidth, Refreshable r) {
    if (sideSize < 1)
      return null; // Don't give me that trash, you grotty heap of parrot droppings!
		   // You wot?
		   // You 'eard me, you snivelling little git!
		   // ...
    for (int i = 0; i < done.size(); ++i) {
      RawImage look = rawImageAt(i);
      if (look.width == sideSize) {
	return look;
      }
    }
    for (int i = 0; i < todo.size(); ++i) {
      if (workAt(i).sideSize == sideSize) {
	return null;
      }
    }
    synchronized (woodWorker) {
      todo.addElement(new WorkEntry(sideSize, bevelWidth, r));
      notify();
    }
    return null;
  }

  public void run () {
    while (true) {
      if (todo.size() == 0) {
	synchronized (woodWorker) {
	  try {
	    wait();
	  } catch (InterruptedException ie) {}
	}			// end sync
      }				// end if nothing to do

      Debug.asser(todo.size() > 0, "Error: woken up thread, but nothing to do");

      WorkEntry active = workAt(0);
      RawImage wood = OodInnaBox.Ood(active.sideSize, active.bevelWidth);
      synchronized (woodWorker) {
	if (workAt(0).sideSize != active.sideSize) {
	  Debug.println("REQUEST CANCELLED!!");
	}
	else {
	  done.addElement(wood);
	  todo.removeElementAt(0);
	  active.toRefresh.refreshLocal();
	}
      }				// end sync block
    }				// end loop forever
  }
} // end class WoodGenerator

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -