public Tool createTool(Class<? extends Tool> toolClass) {
    Tool tool;
    try {
      Constructor<? extends Tool> constructor = toolClass.getDeclaredConstructor(new Class[] {});
      tool = constructor.newInstance(new Object[] {});
      //			tool = constructor.newInstance((Object) null);

      buttonGroup.add(tool);
      toolMap.put(toolClass, tool);
      tool.setToolbox(this);
      return tool;
    } catch (InstantiationException e) {
      TabletopTool.showError(
          I18N.getText("msg.error.toolCannotInstantiate", toolClass.getName()), e);
    } catch (IllegalAccessException e) {
      TabletopTool.showError(
          I18N.getText("msg.error.toolNeedPublicConstructor", toolClass.getName()), e);
    } catch (NoSuchMethodException nsme) {
      TabletopTool.showError(
          I18N.getText("msg.error.toolNeedValidConstructor", toolClass.getName()), nsme);
    } catch (InvocationTargetException ite) {
      TabletopTool.showError(
          I18N.getText("msg.error.toolConstructorFailed", toolClass.getName()), ite);
    }
    return null;
  }
  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();
    }
  }
  private void updateSightTypeCombo() {
    List<String> typeList = new ArrayList<String>(TabletopTool.getCampaign().getSightTypes());
    Collections.sort(typeList);

    DefaultComboBoxModel<String> model =
        new DefaultComboBoxModel<String>(typeList.toArray(new String[typeList.size()]));
    getSightTypeCombo().setModel(model);
  }
 private List<com.t3.model.campaign.TokenProperty> getPropertyList() {
   if (propertyList == null) {
     propertyList =
         TabletopTool.getCampaign()
             .getTokenPropertyList((String) getPropertyTypeCombo().getSelectedItem());
   }
   return propertyList;
 }
  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);
   }
 }
 /**
  * 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);
     }
   }
 }
  /**
   * 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();
        }
      }
    }
  }
 private void updatePropertyTypeCombo() {
   List<String> typeList = new ArrayList<String>(TabletopTool.getCampaign().getTokenTypes());
   Collections.sort(typeList);
   DefaultComboBoxModel<String> model =
       new DefaultComboBoxModel<String>(typeList.toArray(new String[typeList.size()]));
   getPropertyTypeCombo().setModel(model);
   getPropertyTypeCombo()
       .addItemListener(
           new ItemListener() {
             @Override
             public void itemStateChanged(ItemEvent e) {
               updatePropertiesTable((String) getPropertyTypeCombo().getSelectedItem());
             }
           });
 }
  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();
  }
  /**
   * 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 OwnerListModel() {
      List<String> list = new ArrayList<String>();
      Set<String> ownerSet = getModel().getOwners();
      list.addAll(ownerSet);

      ObservableList<Player> playerList = TabletopTool.getPlayerList();
      for (Object item : playerList) {
        Player player = (Player) item;
        String playerId = player.getName();
        if (!list.contains(playerId)) {
          list.add(playerId);
        }
      }
      Collections.sort(list);

      for (String id : list) {
        Selectable selectable = new DefaultSelectable(id);
        selectable.setSelected(ownerSet.contains(id));
        ownerList.add(selectable);
      }
    }
    //
    // 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);
        }
      }
    }
 public void initGMNotesTextArea() {
   getGMNotesTextArea().addMouseListener(new MouseHandler(getGMNotesTextArea()));
   getComponent("@GMNotes").setEnabled(TabletopTool.getPlayer().isGM());
 }
  private void updateStatesPanel() {
    // Group the states first into individual panels
    List<BooleanTokenOverlay> overlays =
        new ArrayList<BooleanTokenOverlay>(TabletopTool.getCampaign().getTokenStatesMap().values());
    Map<String, JPanel> groups = new TreeMap<String, JPanel>();
    groups.put("", new JPanel(new FormLayout("0px:grow 2px 0px:grow 2px 0px:grow 2px 0px:grow")));
    for (BooleanTokenOverlay overlay : overlays) {
      String group = overlay.getGroup();
      if (group != null && (group = group.trim()).length() != 0) {
        JPanel panel = groups.get(group);
        if (panel == null) {
          panel = new JPanel(new FormLayout("0px:grow 2px 0px:grow 2px 0px:grow 2px 0px:grow"));
          panel.setBorder(BorderFactory.createTitledBorder(group));
          groups.put(group, panel);
        }
      }
    }
    // Add the group panels and bar panel to the states panel
    JPanel panel = getStatesPanel();
    panel.removeAll();
    FormLayout layout = new FormLayout("0px:grow");
    panel.setLayout(layout);
    int row = 1;
    for (JPanel gPanel : groups.values()) {
      layout.appendRow(new RowSpec("pref"));
      layout.appendRow(new RowSpec("2px"));
      panel.add(gPanel, new CellConstraints(1, row));
      row += 2;
    }
    layout.appendRow(new RowSpec("pref"));
    layout.appendRow(new RowSpec("2px"));
    JPanel barPanel = new JPanel(new FormLayout("right:pref 2px pref 5px right:pref 2px pref"));
    panel.add(barPanel, new CellConstraints(1, row));

    // Add the individual check boxes.
    for (BooleanTokenOverlay state : overlays) {
      String group = state.getGroup();
      panel = groups.get("");
      if (group != null && (group = group.trim()).length() != 0) panel = groups.get(group);
      int x = panel.getComponentCount() % 4;
      int y = panel.getComponentCount() / 4;
      if (x == 0) {
        layout = (FormLayout) panel.getLayout();
        if (y != 0) layout.appendRow(new RowSpec("2px"));
        layout.appendRow(new RowSpec("pref"));
      }
      panel.add(new JCheckBox(state.getName()), new CellConstraints(x * 2 + 1, y * 2 + 1));
    }
    // Add sliders to the bar panel
    if (TabletopTool.getCampaign().getTokenBarsMap().size() > 0) {
      layout = (FormLayout) barPanel.getLayout();
      barPanel.setName("bar");
      barPanel.setBorder(BorderFactory.createTitledBorder("Bars"));
      int count = 0;
      row = 0;
      for (BarTokenOverlay bar : TabletopTool.getCampaign().getTokenBarsMap().values()) {
        int working = count % 2;
        if (working == 0) { // slider row
          layout.appendRow(new RowSpec("pref"));
          row += 1;
        }
        JPanel labelPanel = new JPanel(new FormLayout("pref", "pref 2px:grow pref"));
        barPanel.add(labelPanel, new CellConstraints(1 + working * 4, row));
        labelPanel.add(
            new JLabel(bar.getName() + ":"),
            new CellConstraints(1, 1, CellConstraints.RIGHT, CellConstraints.TOP));
        JSlider slider = new JSlider(0, 100);
        JCheckBox hide = new JCheckBox("Hide");
        hide.putClientProperty("JSlider", slider);
        hide.addChangeListener(
            new ChangeListener() {
              @Override
              public void stateChanged(ChangeEvent e) {
                JSlider js = (JSlider) ((JCheckBox) e.getSource()).getClientProperty("JSlider");
                js.setEnabled(!((JCheckBox) e.getSource()).isSelected());
              }
            });
        labelPanel.add(hide, new CellConstraints(1, 3, CellConstraints.RIGHT, CellConstraints.TOP));
        slider.setName(bar.getName());
        slider.setPaintLabels(true);
        slider.setPaintTicks(true);
        slider.setMajorTickSpacing(20);
        slider.createStandardLabels(20);
        slider.setMajorTickSpacing(10);
        barPanel.add(slider, new CellConstraints(3 + working * 4, row));
        if (working != 0) { // spacer row
          layout.appendRow(new RowSpec("2px"));
          row += 1;
        }
        count += 1;
      }
    }
  }
  @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;
  }
  @Override
  public void bind(final Token token) {
    // ICON
    getTokenIconPanel().setImageId(token.getImageAssetId());

    // PROPERTIES
    updatePropertyTypeCombo();
    updatePropertiesTable(token.getPropertyType());

    // SIGHT
    updateSightTypeCombo();

    // STATES
    Component barPanel = null;
    updateStatesPanel();
    Component[] statePanels = getStatesPanel().getComponents();
    for (int j = 0; j < statePanels.length; j++) {
      if ("bar".equals(statePanels[j].getName())) {
        barPanel = statePanels[j];
        continue;
      }
      Component[] states = ((Container) statePanels[j]).getComponents();
      for (int i = 0; i < states.length; i++) {
        JCheckBox state = (JCheckBox) states[i];
        state.setSelected(token.hasState(state.getText()));
      }
    }

    // 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];
        if (token.getBar(bar.getName()) == null) {
          cb.setSelected(true);
          bar.setEnabled(false);
          bar.setValue(100);
        } else {
          cb.setSelected(false);
          bar.setEnabled(true);
          bar.setValue((int) (token.getBar(bar.getName()) * 100));
        }
      }
    }

    // OWNER LIST
    EventQueue.invokeLater(
        new Runnable() {
          @Override
          public void run() {
            getOwnerList().setModel(new OwnerListModel());
          }
        });

    // SPEECH TABLE
    EventQueue.invokeLater(
        new Runnable() {
          @Override
          public void run() {
            getSpeechTable().setModel(new SpeechTableModel(token));
          }
        });

    //		Player player = TabletopTool.getPlayer();
    //		boolean editable = player.isGM() ||
    // !TabletopTool.getServerPolicy().useStrictTokenManagement() ||
    // token.isOwner(player.getName());
    //		getAllPlayersCheckBox().setSelected(token.isOwnedByAll());

    // OTHER
    getShapeCombo().setSelectedItem(token.getShape());
    setSizeCombo(token);

    getPropertyTypeCombo().setSelectedItem(token.getPropertyType());
    getSightTypeCombo()
        .setSelectedItem(
            token.getSightType() != null
                ? token.getSightType()
                : TabletopTool.getCampaign().getCampaignProperties().getDefaultSightType());
    getCharSheetPanel().setImageId(token.getCharsheetImage());
    getPortraitPanel().setImageId(token.getPortraitImage());
    getTokenLayoutPanel().setToken(token);

    // we will disable the Owner only visible check box if the token is not
    // visible to players to signify the relationship
    ActionListener tokenVisibleActionListener =
        new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent actionEvent) {
            AbstractButton abstractButton = (AbstractButton) actionEvent.getSource();
            boolean selected = abstractButton.getModel().isSelected();
            getVisibleOnlyToOwnerCheckBox().setEnabled(selected);
            getVisibleOnlyToOwnerLabel().setEnabled(selected);
          }
        };
    getVisibleCheckBox().addActionListener(tokenVisibleActionListener);

    // Character Sheets
    //		controller = null;
    //		String form = TabletopTool.getCampaign().getCharacterSheets().get(token.getPropertyType());
    //		if (form == null)
    //			return;
    //		URL formUrl = getClass().getClassLoader().getResource(form);
    //		if (formUrl == null)
    //			return;
    //		controller = new CharSheetController(formUrl, null);
    //		HashMap<String, Object> properties = new HashMap<String, Object>();
    //		for (String prop : token.getPropertyNames())
    //			properties.put(prop, token.getProperty(prop));
    //		controller.setData(properties);
    //		controller.getPanel().setName("characterSheet");
    //		replaceComponent("sheetPanel", "characterSheet", controller.getPanel());

    super.bind(token);
  }