void onImport()
  {
    // The user don't select a sheet yet.
    if (!m_document) {
      Alert::show("Import Sprite Sheet<<Select a sprite first.||&Close");
      return;
    }

    // The list of frames imported from the sheet
    std::vector<Image*> animation;

    try {
      Sprite* sprite = m_document->getSprite();
      FrameNumber currentFrame = m_context->getActiveLocation().frame();

      // As first step, we cut each tile and add them into "animation" list.
      for (int y=m_rect.y; y<sprite->getHeight(); y += m_rect.h) {
        for (int x=m_rect.x; x<sprite->getWidth(); x += m_rect.w) {
          base::UniquePtr<Image> resultImage(Image::create(sprite->getPixelFormat(), m_rect.w, m_rect.h));

          // Clear the image with mask color.
          image_clear(resultImage, 0);

          // Render the portion of sheet.
          sprite->render(resultImage, -x, -y, currentFrame);
          animation.push_back(resultImage);
          resultImage.release();
        }
      }

      if (animation.size() == 0) {
        Alert::show("Import Sprite Sheet"
                    "<<The specified rectangle does not create any tile."
                    "<<Select a rectangle inside the sprite region."
                    "||&OK");
        return;
      }

      // The following steps modify the sprite, so we wrap all
      // operations in a undo-transaction.
      ContextWriter writer(m_context);
      UndoTransaction undoTransaction(writer.context(), "Import Sprite Sheet", undo::ModifyDocument);
      DocumentApi api = m_document->getApi();

      // Add the layer in the sprite.
      LayerImage* resultLayer = api.newLayer(sprite);

      // Add all frames+cels to the new layer
      for (size_t i=0; i<animation.size(); ++i) {
        int indexInStock;

        // Add the image into the sprite's stock
        indexInStock = api.addImageInStock(sprite, animation[i]);
        animation[i] = NULL;

        // Create the cel.
        base::UniquePtr<Cel> resultCel(new Cel(FrameNumber(i), indexInStock));

        // Add the cel in the layer.
        api.addCel(resultLayer, resultCel);
        resultCel.release();
      }

      // Copy the list of layers (because we will modify it in the iteration).
      LayerList layers = sprite->getFolder()->getLayersList();

      // Remove all other layers
      for (LayerIterator it=layers.begin(), end=layers.end(); it!=end; ++it) {
        if (*it != resultLayer)
          api.removeLayer(*it);
      }

      // Change the number of frames
      api.setTotalFrames(sprite, FrameNumber(animation.size()));

      // Set the size of the sprite to the tile size.
      api.setSpriteSize(sprite, m_rect.w, m_rect.h);

      undoTransaction.commit();
    }
    catch (...) {
      for (size_t i=0; i<animation.size(); ++i)
        delete animation[i];
      throw;
    }

    update_screen_for_document(m_document);
    closeWindow(NULL);
  }
void ImportSpriteSheetCommand::onExecute(Context* context)
{
  ImportSpriteSheetWindow window(context);

retry:;
  window.openWindowInForeground();
  if (!window.ok())
    return;

  Document* document = window.document();
  DocumentPreferences* docPref = window.docPref();
  gfx::Rect frameBounds = window.frameBounds();

  // The user don't select a sheet yet.
  if (!document) {
    Alert::show("Import Sprite Sheet<<Select a sprite first.||&Close");
    goto retry;
  }

  // The list of frames imported from the sheet
  std::vector<ImageRef> animation;

  try {
    Sprite* sprite = document->sprite();
    frame_t currentFrame = context->activeSite().frame();
    render::Render render;

    // As first step, we cut each tile and add them into "animation" list.
    for (int y=frameBounds.y; y<sprite->height(); y += frameBounds.h) {
      for (int x=frameBounds.x; x<sprite->width(); x += frameBounds.w) {
        ImageRef resultImage(
          Image::create(sprite->pixelFormat(), frameBounds.w, frameBounds.h));

        // Render the portion of sheet.
        render.renderSprite(resultImage.get(), sprite, currentFrame,
          gfx::Clip(0, 0, x, y, frameBounds.w, frameBounds.h));

        animation.push_back(resultImage);
      }
    }

    if (animation.size() == 0) {
      Alert::show("Import Sprite Sheet"
        "<<The specified rectangle does not create any tile."
        "<<Select a rectangle inside the sprite region."
        "||&OK");
      return;
    }

    // The following steps modify the sprite, so we wrap all
    // operations in a undo-transaction.
    ContextWriter writer(context);
    Transaction transaction(writer.context(), "Import Sprite Sheet", ModifyDocument);
    DocumentApi api = document->getApi(transaction);

    // Add the layer in the sprite.
    LayerImage* resultLayer = api.newLayer(sprite);

    // Add all frames+cels to the new layer
    for (size_t i=0; i<animation.size(); ++i) {
      // Create the cel.
      base::UniquePtr<Cel> resultCel(new Cel(frame_t(i), animation[i]));

      // Add the cel in the layer.
      api.addCel(resultLayer, resultCel);
      resultCel.release();
    }

    // Copy the list of layers (because we will modify it in the iteration).
    LayerList layers = sprite->folder()->getLayersList();

    // Remove all other layers
    for (LayerIterator it=layers.begin(), end=layers.end(); it!=end; ++it) {
      if (*it != resultLayer)
        api.removeLayer(*it);
    }

    // Change the number of frames
    api.setTotalFrames(sprite, frame_t(animation.size()));

    // Set the size of the sprite to the tile size.
    api.setSpriteSize(sprite, frameBounds.w, frameBounds.h);

    transaction.commit();

    ASSERT(docPref);
    if (docPref)
      docPref->importSpriteSheet.bounds(frameBounds);
  }
  catch (...) {
    throw;
  }

  update_screen_for_document(document);
}
void ImportSpriteSheetCommand::onExecute(Context* context)
{
  ImportSpriteSheetWindow window(context);

  window.openWindowInForeground();
  if (!window.ok())
    return;

  Document* document = window.document();
  DocumentPreferences* docPref = window.docPref();
  gfx::Rect frameBounds = window.frameBounds();
  bool partialTiles = window.partialTilesValue();
  auto sheetType = window.sheetTypeValue();

  ASSERT(document);
  if (!document)
    return;

  // The list of frames imported from the sheet
  std::vector<ImageRef> animation;

  try {
    Sprite* sprite = document->sprite();
    frame_t currentFrame = context->activeSite().frame();
    render::Render render;

    // Each sprite in the sheet
    std::vector<gfx::Rect> tileRects;
    int widthStop = sprite->width();
    int heightStop = sprite->height();
    if (partialTiles) {
      widthStop += frameBounds.w-1;
      heightStop += frameBounds.h-1;
    }

    switch (sheetType) {
      case app::SpriteSheetType::Horizontal:
        for (int x=frameBounds.x; x+frameBounds.w<=widthStop; x += frameBounds.w) {
          tileRects.push_back(gfx::Rect(x, frameBounds.y, frameBounds.w, frameBounds.h));
        }
        break;
      case app::SpriteSheetType::Vertical:
        for (int y=frameBounds.y; y+frameBounds.h<=heightStop; y += frameBounds.h) {
          tileRects.push_back(gfx::Rect(frameBounds.x, y, frameBounds.w, frameBounds.h));
        }
        break;
      case app::SpriteSheetType::Rows:
        for (int y=frameBounds.y; y+frameBounds.h<=heightStop; y += frameBounds.h) {
          for (int x=frameBounds.x; x+frameBounds.w<=widthStop; x += frameBounds.w) {
            tileRects.push_back(gfx::Rect(x, y, frameBounds.w, frameBounds.h));
          }
        }
        break;
      case app::SpriteSheetType::Columns:
        for (int x=frameBounds.x; x+frameBounds.w<=sprite->width(); x += frameBounds.w) {
          for (int y=frameBounds.y; y+frameBounds.h<=sprite->height(); y += frameBounds.h) {
            tileRects.push_back(gfx::Rect(x, y, frameBounds.w, frameBounds.h));
          }
        }
        break;
    }

    // As first step, we cut each tile and add them into "animation" list.
    for (const auto& tileRect : tileRects) {
      ImageRef resultImage(
        Image::create(
          sprite->pixelFormat(), tileRect.w, tileRect.h));

      // Render the portion of sheet.
      render.renderSprite(
        resultImage.get(), sprite, currentFrame,
        gfx::Clip(0, 0, tileRect));

      animation.push_back(resultImage);
    }

    if (animation.size() == 0) {
      Alert::show("Import Sprite Sheet"
        "<<The specified rectangle does not create any tile."
        "<<Select a rectangle inside the sprite region."
        "||&OK");
      return;
    }

    // The following steps modify the sprite, so we wrap all
    // operations in a undo-transaction.
    ContextWriter writer(context);
    Transaction transaction(writer.context(), "Import Sprite Sheet", ModifyDocument);
    DocumentApi api = document->getApi(transaction);

    // Add the layer in the sprite.
    LayerImage* resultLayer = api.newLayer(sprite, "Sprite Sheet");

    // Add all frames+cels to the new layer
    for (size_t i=0; i<animation.size(); ++i) {
      // Create the cel.
      base::UniquePtr<Cel> resultCel(new Cel(frame_t(i), animation[i]));

      // Add the cel in the layer.
      api.addCel(resultLayer, resultCel);
      resultCel.release();
    }

    // Copy the list of layers (because we will modify it in the iteration).
    LayerList layers = sprite->folder()->getLayersList();

    // Remove all other layers
    for (LayerIterator it=layers.begin(), end=layers.end(); it!=end; ++it) {
      if (*it != resultLayer)
        api.removeLayer(*it);
    }

    // Change the number of frames
    api.setTotalFrames(sprite, frame_t(animation.size()));

    // Set the size of the sprite to the tile size.
    api.setSpriteSize(sprite, frameBounds.w, frameBounds.h);

    transaction.commit();

    ASSERT(docPref);
    if (docPref) {
      docPref->importSpriteSheet.type(sheetType);
      docPref->importSpriteSheet.bounds(frameBounds);
      docPref->importSpriteSheet.partialTiles(partialTiles);
    }
  }
  catch (...) {
    throw;
  }

  update_screen_for_document(document);
}