/**
   * 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);
    }
  }
  public ExportDialogOld() {
    super(TabletopTool.getFrame(), "Export Screenshot", true);
    // The window uses about 1MB. Disposing frees this, but repeated uses
    // will cause more memory fragmentation.
    // MCL: I figure it's better to save the 1MB for low-mem systems,
    //      but it would be even better to HIDE it, and then dispose() it
    //      when the user clicks on the memory meter to free memory
    //		setDefaultCloseOperation(HIDE_ON_CLOSE);
    setDefaultCloseOperation(DISPOSE_ON_CLOSE);

    //
    // Initialize the panel and button actions
    //
    formPanel = new FormPanel("com/t3/client/ui/forms/exportDialog.xml");
    setLayout(new GridLayout());
    add(formPanel);
    getRootPane().setDefaultButton((JButton) formPanel.getButton("exportButton"));
    pack();

    ExportRadioButtons.setForm(formPanel);
    ExportLayers.setForm(formPanel);

    formPanel
        .getButton("exportButton")
        .addActionListener(
            new ActionListener() {
              @Override
              public void actionPerformed(ActionEvent evt) {
                exportButtonAction();
              }
            });

    formPanel
        .getButton("cancelButton")
        .addActionListener(
            new ActionListener() {
              @Override
              public void actionPerformed(ActionEvent evt) {
                dispose();
              }
            });

    formPanel
        .getButton("browseButton")
        .addActionListener(
            new ActionListener() {
              @Override
              public void actionPerformed(ActionEvent evt) {
                browseButtonAction();
              }
            });

    // Run this once to make sure the dialog is in a good starting state.
    ExportLayers.setDefaultChecked();
    enforceButtonRules();
  }
  @Override
  public void setVisible(boolean b) {
    // In case something changed while the dialog was closed...
    enforceButtonRules();

    if (b) {
      SwingUtil.centerOver(this, TabletopTool.getFrame());
    }
    super.setVisible(b);
  }
 /** Sets the layer-selection checkboxes to replicate the "current view". */
 public void setToDefault() {
   final Zone zone = TabletopTool.getFrame().getCurrentZoneRenderer().getZone();
   if (this == ExportLayers.LAYER_FOG) {
     ExportLayers.LAYER_FOG.setChecked(zone.hasFog());
   } else if (this == ExportLayers.LAYER_VISIBILITY) {
     ExportLayers.LAYER_VISIBILITY.setChecked(zone.getVisionType() != Zone.VisionType.OFF);
   } else {
     setChecked(true);
   }
 }
  /**
   * This is a wrapper that preserves the layer settings on the Zone object. It calls {@link
   * takeEntireMapScreenShot()} to to the real work.
   *
   * @return the image to be saved to a file
   */
  private static BufferedImage entireMapScreenShotWithLayers() throws Exception, OutOfMemoryError {
    final Zone zone = TabletopTool.getFrame().getCurrentZoneRenderer().getZone();

    //
    // Preserve settings
    //
    // psuedo-layers
    final Zone.VisionType savedVision = zone.getVisionType();
    final boolean savedFog = zone.hasFog();
    final boolean savedBoard = zone.drawBoard();
    // real layers
    final boolean savedToken = Zone.Layer.TOKEN.isEnabled();
    final boolean savedHidden = Zone.Layer.GM.isEnabled();
    final boolean savedObject = Zone.Layer.OBJECT.isEnabled();
    final boolean savedBackground = Zone.Layer.BACKGROUND.isEnabled();

    //
    // set according to dialog options
    //
    zone.setHasFog(ExportLayers.LAYER_FOG.isChecked());
    if (!ExportLayers.LAYER_VISIBILITY.isChecked()) zone.setVisionType(Zone.VisionType.OFF);
    zone.setDrawBoard(ExportLayers.LAYER_BOARD.isChecked());
    Zone.Layer.TOKEN.setEnabled(ExportLayers.LAYER_TOKEN.isChecked());
    Zone.Layer.GM.setEnabled(ExportLayers.LAYER_HIDDEN.isChecked());
    Zone.Layer.OBJECT.setEnabled(ExportLayers.LAYER_OBJECT.isChecked());
    Zone.Layer.BACKGROUND.setEnabled(ExportLayers.LAYER_BACKGROUND.isChecked());
    // This 'cache invalidation' is handled by setZone inside takeEntireMapScreenShot()
    // but it should be more robust.
    // TabletopTool.getFrame().getCurrentZoneRenderer().invalidateCurrentViewCache();

    //
    // screenshot!
    //   MCL: NOTE: while turning off Fog, there is a possibility the players
    //    may see the map flash for a second with fog turned off-- need to look into
    //    whether this is true.
    BufferedImage image = null;
    try {
      image = takeEntireMapScreenShot();
    } finally {
      //
      // Restore settings
      //
      zone.setHasFog(savedFog);
      zone.setVisionType(savedVision);
      zone.setDrawBoard(savedBoard);
      Zone.Layer.TOKEN.setEnabled(savedToken);
      Zone.Layer.GM.setEnabled(savedHidden);
      Zone.Layer.OBJECT.setEnabled(savedObject);
      Zone.Layer.BACKGROUND.setEnabled(savedBackground);
      //			TabletopTool.getFrame().getCurrentZoneRenderer().invalidateCurrentViewCache();
    }

    return image;
  }
 public static void setDefaultChecked() {
   // everything defaults to 'on' since the layers don't really have on/off capability
   // outside of this screenshot code
   for (ExportLayers layer : ExportLayers.values()) {
     layer.setChecked(true);
   }
   // however, some psuedo-layers do have a state, so set that appropriately
   final Zone zone = TabletopTool.getFrame().getCurrentZoneRenderer().getZone();
   ExportLayers.LAYER_VISIBILITY.setChecked(zone.getVisionType() != Zone.VisionType.OFF);
   ExportLayers.LAYER_FOG.setChecked(zone.hasFog());
 }
 public void setSizeCombo(Token token) {
   JComboBox size = getSizeCombo();
   Grid grid = TabletopTool.getFrame().getCurrentZoneRenderer().getZone().getGrid();
   DefaultComboBoxModel model = new DefaultComboBoxModel(grid.getFootprints().toArray());
   model.insertElementAt(token.getLayer() == Layer.TOKEN ? "Native Size" : "Free Size", 0);
   size.setModel(model);
   if (token.isSnapToScale()) {
     size.setSelectedItem(token.getFootprint(grid));
   } else {
     size.setSelectedIndex(0);
   }
 }
  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();
  }
  /**
   * Ensures that the user can only check/uncheck boxes as appropriate. For example, if "fog" is not
   * enabled on the map, it cannot be enabled for export.
   *
   * <p>This should get called during initialization and whenever the radio buttons change.
   *
   * <p>The GM and Players have different rules, to prevent players from gaining knowledge they
   * should not have using the screenshot (such as revealing things under other things by disabling
   * layers). Players can basically only turn off tokens, to get an 'empty' version of the map.
   */
  public static void enforceButtonRules() {
    if (!TabletopTool.getPlayer().isGM()) {
      ExportRadioButtons.VIEW_PLAYER.setChecked(true);
      ExportRadioButtons.VIEW_PLAYER.setEnabled(true);
      ExportRadioButtons.VIEW_GM.setEnabled(false);
    }
    if (ExportRadioButtons.LAYERS_CURRENT.isChecked()) {
      // By "current layers" we mean what you see in the editor, which is everything.
      // So disable mucking about with layers.
      formPanel.getLabel("LAYERS_LABEL").setEnabled(false);
      ExportLayers.setDefaultChecked();
      ExportLayers.setDisabled();
    } else /* if (ExportRadioButtons.LAYERS_AS_SELECTED.isChecked()) */ {
      formPanel.getLabel("LAYERS_LABEL").setEnabled(true);
      boolean isGM = ExportRadioButtons.VIEW_GM.isChecked();
      final Zone zone = TabletopTool.getFrame().getCurrentZoneRenderer().getZone();

      for (ExportLayers layer : ExportLayers.values()) {
        boolean enabled = isGM || layer.playerCanModify;
        // Regardless of whether it is a player or GM,
        // only enable fog and visibility check-boxes
        // when the map has those things turned on.
        switch (layer) {
          case LAYER_VISIBILITY:
            enabled &= (zone.getVisionType() != Zone.VisionType.OFF);
            break;
          case LAYER_FOG:
            enabled &= zone.hasFog();
            break;
        }
        layer.setEnabled(enabled);
        if (!enabled) {
          layer.setToDefault();
        }
      }
    }
  }
  @Override
  public boolean commit() {
    Token token = getModel();

    if (getNameField().getText().equals("")) {
      TabletopTool.showError("msg.error.emptyTokenName");
      return false;
    }
    if (getSpeechTable().isEditing()) {
      getSpeechTable().getCellEditor().stopCellEditing();
    }
    if (getPropertyTable().isEditing()) {
      getPropertyTable().getCellEditor().stopCellEditing();
    }
    // Commit the changes to the token properties
    if (!super.commit()) {
      return false;
    }
    // SIZE
    token.setSnapToScale(getSizeCombo().getSelectedIndex() != 0);
    if (getSizeCombo().getSelectedIndex() > 0) {
      Grid grid = TabletopTool.getFrame().getCurrentZoneRenderer().getZone().getGrid();
      token.setFootprint(grid, (TokenFootprint) getSizeCombo().getSelectedItem());
    }
    // Other
    token.setPropertyType((String) getPropertyTypeCombo().getSelectedItem());
    token.setSightType((String) getSightTypeCombo().getSelectedItem());

    // Get the states
    Component[] stateComponents = getStatesPanel().getComponents();
    Component barPanel = null;
    for (int j = 0; j < stateComponents.length; j++) {
      if ("bar".equals(stateComponents[j].getName())) {
        barPanel = stateComponents[j];
        continue;
      }
      Component[] components = ((Container) stateComponents[j]).getComponents();
      for (int i = 0; i < components.length; i++) {
        JCheckBox cb = (JCheckBox) components[i];
        String state = cb.getText();
        token.setState(state, cb.isSelected());
      }
    } // endfor

    // BARS
    if (barPanel != null) {
      Component[] bars = ((Container) barPanel).getComponents();
      for (int i = 0; i < bars.length; i += 2) {
        JCheckBox cb = (JCheckBox) ((Container) bars[i]).getComponent(1);
        JSlider bar = (JSlider) bars[i + 1];
        Float value = cb.isSelected() ? null : new Float(bar.getValue() / 100f);
        token.setBar(bar.getName(), value);
      }
    }
    // Ownership
    // If the token is owned by all and we are a player don't alter the ownership list.
    if (TabletopTool.getPlayer().isGM() || !token.isOwnedByAll()) {
      token.clearAllOwners();

      for (int i = 0; i < getOwnerList().getModel().getSize(); i++) {
        DefaultSelectable selectable =
            (DefaultSelectable) getOwnerList().getModel().getElementAt(i);
        if (selectable.isSelected()) {
          token.addOwner((String) selectable.getObject());
        }
      }
      // If we are not a GM and the only non GM owner make sure we can't
      // take our selves off of the owners list
      if (!TabletopTool.getPlayer().isGM()) {
        boolean hasPlayer = false;
        Set<String> owners = token.getOwners();
        if (owners != null) {
          Iterator<Player> playerIter = TabletopTool.getPlayerList().iterator();
          while (playerIter.hasNext()) {
            Player pl = playerIter.next();
            if (!pl.isGM() && owners.contains(pl.getName())) {
              hasPlayer = true;
            }
          }
        }
        if (!hasPlayer) {
          token.addOwner(TabletopTool.getPlayer().getName());
        }
      }
    }
    // SHAPE
    token.setShape((Token.TokenShape) getShapeCombo().getSelectedItem());

    // Macros
    token.setSpeechMap(((KeyValueTableModel) getSpeechTable().getModel()).getMap());

    // Properties
    ((TokenPropertyTableModel) getPropertyTable().getModel()).applyTo(token);

    // Charsheet
    if (getCharSheetPanel().getImageId() != null) {
      T3Util.uploadAsset(AssetManager.getAsset(getCharSheetPanel().getImageId()));
    }
    token.setCharsheetImage(getCharSheetPanel().getImageId());

    // IMAGE
    if (!token.getImageAssetId().equals(getTokenIconPanel().getImageId())) {
      BufferedImage image = ImageManager.getImageAndWait(getTokenIconPanel().getImageId());
      T3Util.uploadAsset(AssetManager.getAsset(getTokenIconPanel().getImageId()));
      token.setImageAsset(null, getTokenIconPanel().getImageId()); // Default image for now
      token.setWidth(image.getWidth(null));
      token.setHeight(image.getHeight(null));
    }
    // PORTRAIT
    if (getPortraitPanel().getImageId() != null) {
      // Make sure the server has the image
      if (!TabletopTool.getCampaign().containsAsset(getPortraitPanel().getImageId())) {
        TabletopTool.serverCommand()
            .putAsset(AssetManager.getAsset(getPortraitPanel().getImageId()));
      }
    }
    token.setPortraitImage(getPortraitPanel().getImageId());

    // LAYOUT
    token.setSizeScale(getTokenLayoutPanel().getSizeScale());
    token.setAnchor(getTokenLayoutPanel().getAnchorX(), getTokenLayoutPanel().getAnchorY());

    // OTHER
    tokenSaved = true;

    // Character Sheet
    //		Map<String, Object> properties = controller.getData();
    //		for (String prop : token.getPropertyNames())
    //			token.setProperty(prop, properties.get(prop));

    // Update UI
    TabletopTool.getFrame().updateTokenTree();
    TabletopTool.getFrame().resetTokenPanels();

    return true;
  }
  /**
   * 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;
  }