public PledgeDisplayPanel() {
    try {
      logo = ImageIO.read(getClass().getResource("pp-logo-large.png"));
    } catch (IOException e) {
      System.err.println("Could not load background image");
      e.printStackTrace();
    }

    tickerLabel = new TickerLabel();
    tickerLabel.setVisible(true);
    slidePane = new SlidePane();
    slidePane.setVisible(false);
    pledgePane = new PledgePane();
    readProperties();

    idleTimer = new Timer(idleTimeout, new IdleTimeout());
    idleTimer.start();
    setForeground(Color.black);
    setLayout(null);
    renderHints =
        new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
    renderHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
    addComponentListener(this);
    add(tickerLabel);
    add(pledgePane);
    add(slidePane);
    addMouseListener(this);
  }
  @Override
  public void sendInitialStatus(PledgeServer.StatusSender s) {

    // Send pledge/slide mode:
    if (slideMode) s.send(new StatusCommand(StatusCommand.CMD_DISPLAY_MODE_SLIDE));
    else s.send(new StatusCommand(StatusCommand.CMD_DISPLAY_MODE_PLEDGE));

    // Send ticker speed
    s.send(
        new StatusCommand(
            StatusCommand.CMD_TICKER_SPEED, new Integer(tickerLabel.getTicksPerSecond())));

    // Send transition time
    s.send(
        new StatusCommand(
            StatusCommand.CMD_TRANSITION_TIME,
            new Integer(pledgePane.getNextPledgeTimeout() / 1000)));

    // Send accepted pledges
    Pledge[] list = pledgeList.toArray(new Pledge[pledgeList.size()]);

    // Do not send welcome pledge
    if (list.length > 0) {

      if (list[0].equals(welcomePledge)) list[0] = null;

      StatusCommand cmd = new StatusCommand(StatusCommand.CMD_PLEDGE_LIST, list);
      s.send(cmd);
    }
  }
  public void revokePledge(final Pledge p) {
    javax.swing.SwingUtilities.invokeLater(
        new Runnable() {
          @Override
          public void run() {
            pledgePane.removeText(p.getText());
          }
        });

    pledgeHash.remove(p);
    pledgeList.remove(p);
    tickerLabel.setText(createTickerString());
  }
  private void updateBackground() {
    int width = getWidth();
    int height = getHeight();
    int x = 0, y = 0;

    background = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    Graphics2D g2 = (Graphics2D) background.getGraphics();

    g2.setRenderingHints(renderHints);

    // System.out.printf("width1=%d height1=%d\n", width, height);

    double xOffset = (double) width / 12;
    double yOffset = (double) height / 12;

    // Fill with white background
    g2.setColor(Color.white);
    g2.fillRect(0, 0, width, height);

    int logoWidth = (int) (width / 4);
    Image scaledLogo = logo.getScaledInstance(logoWidth, -1, Image.SCALE_SMOOTH);
    y = (int) (height - scaledLogo.getHeight(this) - yOffset);
    cornerX = (int) xOffset;

    // Draw logo
    g2.drawImage(
        scaledLogo, cornerX, y, scaledLogo.getWidth(this), scaledLogo.getHeight(this), this);

    // Draw phone number
    g2.setFont(new Font(telephoneNrFontName, Font.BOLD, width / 30));
    int phoneNumberWidth = g2.getFontMetrics().stringWidth(phoneNumber);
    int phoneNumberHeight = g2.getFontMetrics().getHeight();
    g2.setColor(phonepledgeBlue2);

    // Move text down a bit
    y += 15;

    g2.drawString(
        phoneNumber, width - (int) xOffset - phoneNumberWidth, y + phoneNumberHeight - 10);

    // Draw phone number instruction
    g2.setFont(new Font(telephoneNrFontName, Font.BOLD, width / 70));
    int instructionWidth = g2.getFontMetrics().stringWidth(pledgeInstruction);
    // int instructionHeight = g2.getFontMetrics().getHeight();
    g2.setColor(Color.black);

    g2.drawString(
        pledgeInstruction,
        width - (int) xOffset - (phoneNumberWidth / 2) - (instructionWidth / 2),
        y - 10);

    double rectWidth = rectThickness;
    double rectHeight = height - (scaledLogo.getHeight(this) + yOffset * 2.5);

    // Draw blue L
    g2.setColor(phonepledgeBlue2);
    g2.fillRect(cornerX, (int) yOffset, (int) rectWidth, (int) rectHeight);

    cornerY = (int) yOffset + (int) rectHeight;
    blueLWidth = (int) (width - xOffset * 2);

    g2.fillRect((int) xOffset, cornerY - rectThickness, blueLWidth, rectThickness);

    // Calculate positions of our tickerLabel
    x = (int) xOffset;
    y = cornerY - rectThickness;

    tickerLabel.setLocation(x, y);
    tickerLabel.setSize(blueLWidth, rectThickness);

    // System.out.printf("Ticker x=%d y=%d width=%d height=%d\n",
    //	x, y, blueLWidth, rectThickness);

    // Calculate positions of other components
    int innerRectHeight = (int) (rectHeight - rectThickness);
    int innerRectWidth = (int) (blueLWidth - rectThickness);
    int innerRectStrutX = (innerRectWidth / 7);
    int innerRectStrutY = (innerRectHeight / 10);
    int dx = (int) (innerRectWidth - innerRectStrutX);
    int dy = (int) (innerRectHeight - innerRectStrutY);

    pledgePane.setSize(dx, dy);

    // Use aspect ration of, e.g., 800x600 for images
    dx = (int) (dy + dy / 3);

    slidePane.setSize(dx, dy);

    x = (int) (innerRectWidth / 2 - (pledgePane.getWidth() / 2) + xOffset + rectThickness);
    y = (int) (innerRectHeight / 2 - (pledgePane.getHeight() / 2) + yOffset);
    pledgePane.setLocation(x, y);

    x = (int) (innerRectWidth / 2 - (slidePane.getWidth() / 2) + xOffset + rectThickness);
    y = (int) (innerRectHeight / 2 - (slidePane.getHeight() / 2) + yOffset);
    slidePane.setLocation(x, y);
  }
  private void readProperties() {
    Properties props = new Properties();

    try {
      props.load(new FileInputStream("display.properties"));
    } catch (FileNotFoundException e) {
      return;
    } catch (IOException e) {
      e.printStackTrace();
      return;
    }

    String value;

    if ((value = (String) props.getProperty("NextSlideTimeout")) != null) {
      try {
        int val = Integer.parseInt(value);
        System.out.println("NextSlideTimeout=" + val);
        slidePane.setNextSlideTimeout(val * 1000);
      } catch (NumberFormatException e) {
        System.err.println("Bad NextSLideTimeout value " + value);
      }
    }

    if ((value = (String) props.getProperty("TelephoneNumberFont")) != null) {
      telephoneNrFontName = value;
    }

    if ((value = (String) props.getProperty("PledgeFont")) != null) {
      pledgePane.setFont(value);
    }

    if ((value = (String) props.getProperty("PledgeFontSize")) != null) {
      try {
        int val = Integer.parseInt(value);
        pledgePane.setFontSize(val);
      } catch (NumberFormatException e) {
        System.err.println("Bad PledgeFontSize value " + value);
      }
    }

    if ((value = (String) props.getProperty("TickerFont")) != null) {
      tickerLabel.setFont(value);
    }

    if ((value = (String) props.getProperty("TickerFontSize")) != null) {
      try {
        int val = Integer.parseInt(value);
        tickerLabel.setFontSize(val);
      } catch (NumberFormatException e) {
        System.err.println("Bad TickerFontSize value " + value);
      }
    }

    if ((value = (String) props.getProperty("NextPledgeTimeout")) != null) {
      try {
        int val = Integer.parseInt(value);
        System.out.println("NextPledgeTimeout=" + val);
        pledgePane.setNextPledgeTimeout(val * 1000);
      } catch (NumberFormatException e) {
        System.err.println("Bad NextPledgeTimeout value " + value);
      }
    }

    if ((value = (String) props.getProperty("TickerHz")) != null) {
      try {
        int val = Integer.parseInt(value);
        System.out.println("TickerHz=" + val);
        tickerLabel.setTickerHz(val);
      } catch (NumberFormatException e) {
        System.err.println("Bad TickerHz value " + value);
      }
    }
    if ((value = (String) props.getProperty("TicksPerSecond")) != null) {
      try {
        int val = Integer.parseInt(value);
        System.out.println("TicksPerSecond=" + val);
        tickerLabel.setTicksPerSecond(val);
      } catch (NumberFormatException e) {
        System.err.println("Bad TicksPerSecond value " + value);
      }
    }

    if ((value = (String) props.getProperty("PledgeInstruction")) != null) {
      System.out.println("PledgeInstruction=" + value);
      phoneNumber = value;
    }
  }