@Override
 public String getColumnName(int column) {
   switch (column) {
     case 0:
       return I18N.getString("EditTokenDialog.msg.speech.colID");
     case 1:
       return I18N.getString("EditTokenDialog.msg.speech.colSpeechText");
   }
   return "";
 }
 @Override
 public String getColumnName(int column) {
   switch (column) {
     case 0:
       return I18N.getString("EditTokenDialog.msg.generic.colKey");
     case 1:
       return I18N.getString("EditTokenDialog.msg.generic.colValue");
   }
   return "";
 }
  private void exportButtonAction() {
    // This block is to allow preservation of existing dialog behavior:
    // Neither button is set when the dialog first appears, so we have to
    // make sure the user picks one. Presumably this is to force the user
    // to pay attention to this choice and not just accept a default.
    if (!(ExportRadioButtons.VIEW_GM.isChecked() || ExportRadioButtons.VIEW_PLAYER.isChecked())) {
      TabletopTool.showError(I18N.getString("dialog.screenshot.error.mustSelectView"), null);
      return;
    }

    // LOCATION
    // TODO: Show a progress dialog
    // TODO: Make this less fragile
    switch (formPanel.getTabbedPane("tabs").getSelectedIndex()) {
      case 0:
        File file = new File(formPanel.getText("locationTextField"));

        // PNG only supported for now
        if (!file.getName().toLowerCase().endsWith(".png")) {
          file = new File(file.getAbsolutePath() + ".png");
        }

        exportLocation = new LocalLocation(file);
        break;
      case 1:
        String username = formPanel.getText("username");
        String password = formPanel.getText("password");
        String host = formPanel.getText("host");
        String path = formPanel.getText("path");

        // PNG only supported for now
        if (!path.toLowerCase().endsWith(".png")) {
          path += ".png";
        }

        exportLocation = new FTPLocation(username, password, host, path);
        break;
    }

    try {
      screenCapture();
    } catch (Exception ex) {
      TabletopTool.showError(I18N.getString("dialog.screenshot.error.failedExportingImage"), ex);
    } finally {
      dispose();
    }
  }
 /**
  * Stores the form this is attached to, so we don't have to store duplicate data locally (like
  * selected and enabled). Also perform some error checking, since we _are_ duplicating the
  * description of the form itself (like what buttons it has).
  *
  * @param form The FormPanel this dialog is part of.
  */
 public static void setForm(FormPanel form) {
   ExportLayers.form = form;
   for (ExportLayers button : ExportLayers.values()) {
     try {
       if (form.getButton(button.toString()) == null) {
         throw new Exception("Export Dialog has a mis-matched enum: " + button.toString());
       }
     } catch (Exception ex) {
       TabletopTool.showError(
           I18N.getString("dialog.screenshot.layer.button.uiImplementationError"), ex);
     }
   }
 }
  public void showDialog(Token token) {
    dialog =
        new GenericDialog(
            I18N.getString("EditTokenDialog.msg.title"), TabletopTool.getFrame(), this) {
          private static final long serialVersionUID = 5439449816096482201L;

          @Override
          public void closeDialog() {
            // TODO: I don't like this. There should really be a AbeilleDialog class that does this
            unbind();
            super.closeDialog();
          }
        };
    bind(token);
    getRootPane().setDefaultButton(getOKButton());
    dialog.showDialog();
  }
    //
    // SetForm stores the form this is attached to
    //
    public static void setForm(FormPanel form) {
      ExportRadioButtons.form = form;

      for (ExportRadioButtons button : ExportRadioButtons.values()) {
        try {
          if (form.getRadioButton(button.toString()) == null) {
            throw new Exception("Export Dialog has a mis-matched enum: " + button.toString());
          }
          button.addActionListener(
              new ActionListener() {
                @Override
                public void actionPerformed(ActionEvent evt) {
                  enforceButtonRules();
                }
              });
        } catch (Exception ex) {
          TabletopTool.showError(
              I18N.getString("dialog.screenshot.radio.button.uiImplementationError"), ex);
        }
      }
    }
  /**
   * This is the top-level screen-capture routine. It sends the resulting PNG image to the location
   * previously selected by the user. TODO: It currently calls {@link
   * TabletopTool.takeMapScreenShot()} for "normal" screenshots, but that's just until this code is
   * considered stable enough.
   *
   * @throws Exception
   */
  public void screenCapture() throws Exception {

    BufferedImage screenCap = null;

    TabletopTool.getFrame()
        .setStatusMessage(I18N.getString("dialog.screenshot.msg.GeneratingScreenshot"));
    ExportRadioButtons type = ExportRadioButtons.getType();
    try {
      switch (type) {
        case TYPE_CURRENT_VIEW:
          // This uses the original screenshot code: I didn't want to touch it, so I need
          // to pass it the same parameter it took before.
          Player.Role role =
              ExportRadioButtons.VIEW_GM.isChecked() ? Player.Role.GM : Player.Role.PLAYER;
          screenCap = TabletopTool.takeMapScreenShot(new PlayerView(role));
          // since old screenshot code doesn't throw exceptions, look for null
          if (screenCap == null) {
            throw new Exception(I18N.getString("dialog.screenshot.error.failedImageGeneration"));
          }
          break;
        case TYPE_ENTIRE_MAP:
          screenCap = entireMapScreenShotWithLayers();
          break;
        default:
          throw new Exception(I18N.getString("dialog.screenshot.error.invalidDialogSettings"));
      }
      TabletopTool.getFrame()
          .setStatusMessage(I18N.getString("dialog.screenshot.msg.screenshotStreaming"));
      try (ByteArrayOutputStream imageOut = new ByteArrayOutputStream()) {
        ImageIO.write(screenCap, "png", imageOut);
        screenCap = null; // Free up the memory as soon as possible

        TabletopTool.getFrame()
            .setStatusMessage(I18N.getString("dialog.screenshot.msg.screenshotSaving"));
        exportLocation.putContent(
            new BufferedInputStream(new ByteArrayInputStream(imageOut.toByteArray())));
      }

      TabletopTool.getFrame()
          .setStatusMessage(I18N.getString("dialog.screenshot.msg.screenshotSaved"));
    } catch (OutOfMemoryError e) {
      TabletopTool.showError("Out Of Memory", e);
    } catch (Exception ex) {
      TabletopTool.showError("screenCapture()", ex);
    }
  }
  /**
   * Finds the extents of the map, then takes a 'screenshot' of that area. If the user is the GM,
   * the extents include every object and everything that has any area, such as 'fog' and
   * 'visibility' objects.
   *
   * <p>If a background tiling texture is used, the image is aligned to it, so that it can be used
   * on re-import as a new base map image.
   *
   * <p>If the user is a player (or GM posing as a player), the extents only go as far as the
   * revealed fog-of-war.
   *
   * @return the image to be saved
   */
  private static BufferedImage takeEntireMapScreenShot() throws Exception, OutOfMemoryError {
    final ZoneRenderer renderer = TabletopTool.getFrame().getCurrentZoneRenderer();
    if (renderer == null) {
      throw (new Exception("renderer = NULL"));
    }

    boolean viewAsPlayer = ExportRadioButtons.VIEW_PLAYER.isChecked();

    //
    // First, figure out the 'extents' of the canvas
    //   This will be later modified by the fog (for players),
    //   and by the tiling texture (for re-importing)
    //
    final PlayerView view = new PlayerView(viewAsPlayer ? Player.Role.PLAYER : Player.Role.GM);
    Rectangle extents = renderer.zoneExtents(view);
    try {
      // Clip to what the players know about (if applicable).
      // This keeps the player from exporting the map to learn which
      // direction has more 'stuff' in it.

      if (viewAsPlayer) {
        Rectangle fogE = renderer.fogExtents();
        // TabletopTool.showError(fogE.x + " " + fogE.y + " " + fogE.width + " " + fogE.height);
        if ((fogE.width < 0) || (fogE.height < 0)) {
          TabletopTool.showError(
              I18N.getString(
                  "dialog.screenshot.error.negativeFogExtents")); // Image is not clipped to show
          // only fog-revealed areas!"));
        } else {
          extents = extents.intersection(fogE);
        }
      }
    } catch (Exception ex) {
      throw (new Exception(I18N.getString("dialog.screenshot.error.noArea"), ex));
    }
    if ((extents == null) || (extents.width == 0) || (extents.height == 0)) {
      throw (new Exception(I18N.getString("dialog.screenshot.error.noArea")));
    }

    // If output includes the tiling 'board' texture, move the upper-left corner
    // to an integer multiple of the background tile (so it matches up on import).
    // We don't need to move the lower-right corner because it doesn't matter for
    // aligning on importing.

    boolean drawBoard = ExportLayers.LAYER_BOARD.isChecked();
    if (drawBoard) {
      DrawablePaint paint = renderer.getZone().getBackgroundPaint();
      DrawableTexturePaint dummy = new DrawableTexturePaint();
      Integer tileX = 0, tileY = 0;

      if (paint.getClass() == dummy.getClass()) {
        Image bgTexture = ImageManager.getImage(((DrawableTexturePaint) paint).getAsset().getId());
        tileX = bgTexture.getWidth(null);
        tileY = bgTexture.getHeight(null);
        Integer x = ((int) Math.floor((float) extents.x / tileX)) * tileX;
        Integer y = ((int) Math.floor((float) extents.y / tileY)) * tileY;
        extents.width = extents.width + (extents.x - x);
        extents.height = extents.height + (extents.y - y);
        extents.x = x;
        extents.y = y;
      }
    }

    // Save the original state of the renderer to restore later.
    // Create a place to put the image, and
    // set up the renderer to encompass the whole extents of the map.

    Rectangle origBounds = renderer.getBounds();
    Scale origScale = renderer.getZoneScale();
    Dimension origSize = renderer.getSize();

    BufferedImage image = null;
    try {
      image = new BufferedImage(extents.width, extents.height, Transparency.OPAQUE);
    } catch (OutOfMemoryError me) {
      throw new OutOfMemoryError("image size = " + extents.width + " x " + extents.height);
    } catch (Exception e) {
      throw new Exception("image size = " + extents.width + " x " + extents.height, e);
    }

    final Graphics2D g = image.createGraphics();
    g.setClip(0, 0, extents.width, extents.height);
    Scale s = new Scale();
    s.setOffset(-extents.x, -extents.y);

    // Finally, draw the image.
    // Copied this thread concept from the original screenshot code in TabletopTool.
    //  Have to do this on the EDT so that there aren't any odd side effects
    //  of rendering using a renderer that's on screen.

    try {
      renderer.setZoneScale(s);
      renderer.setBounds(extents);
      renderer.setSize(extents.getSize());
      if (!EventQueue.isDispatchThread()) {
        EventQueue.invokeAndWait(
            new Runnable() {
              @Override
              public void run() {
                renderer.renderZone(g, view);
              }
            });
      } else {
        renderer.renderZone(g, view);
      }
      return image;
    } catch (OutOfMemoryError me) {
      throw new OutOfMemoryError("image size = " + extents.width + " x " + extents.height);
    } catch (InterruptedException ie) {
      TabletopTool.showError("While creating snapshot", ie);
    } catch (InvocationTargetException ite) {
      TabletopTool.showError("While creating snapshot", ite);
    } catch (Exception e) {
      throw new Exception("image size = " + extents.width + " x " + extents.height, e);
    } finally {
      g.dispose();
      // Restore original state
      renderer.setBounds(origBounds);
      renderer.setZoneScale(origScale);
      renderer.setSize(origSize);
    }
    // This is just to avoid the compiler error: it should be unreachable
    return null;
  }