Beta Público da nova IDE de Ruby on Rails da CodeGear

Escrito em 24 de Maio de 2007 em Rails por Leonel Togniolli

A CodeGear acaba de abrir as portas para o beta da sua nova IDE para Ruby on Rails que está sendo desenvolvida. É necessário uma conta na Developer Network da CodeGear, o que é rápido e grátis. O próximo passo é a assinatura (digital) de um Non-Disclosure Agreement que determina as restrições de divulgação das informaçõe, e lhe dará acesso ao Beta.

A nova IDE foi demonstrada na RailsConf2007 que ocorreu semana passada, e é baseada na infraestrutura do Eclipse, assim como o JBuilder mais recente.

Acompanhe o desenvolvimento da nova IDE, e não deixe de participar do beta!

Se você é novo por aqui, não deixe de assinar o feed RSS ou notificações por email. Não perca novos artigos!

Jash - JavaScript Shell - Um Prompt Interativo Dentro do Browser

Escrito em 20 de Maio de 2007 em Desenvolvimento Web, JavaScript por Leonel Togniolli

Seguindo no assunto de desenvolvimento de aplicações Web com JavaScript, vou apresentar uma ferramenta interessante para trabalhar com JavaScript: o Jash, ou JavaScript shell, um prompt interativo que permite você executar comandos e testar código JavaScript diretamente dentro do browser.

Ele funciona abrindo uma janela DHTML dentro de qualquer página que você estiver navegando, permitindo você interagir com o código JavaScript existente nessa página, com a própria página, ou digitar comandos independentes.

Você pode fazer o download diretamente do site oficial da ferramenta. Para usar, basta atualizar os caminhos dentro do arquivo .js para o local que ele foi salvo, adicionar uma referência na página que você está desenvolvendo, ou mais fácil ainda, criar um bookmarklet apontando para ele.

Vá em frente, clique no link acima e veja o Jash funcionando diretamente aqui. Ele completa nomes de objetos, propriedades e funções com a tecla Tab, além de ter vários recursos interessantes para ajudar a desenvolver sua aplicação web. Bem útil.

JavaScript - Nem Java, nem Script - Uma Interessante Linguagem Orientada a Objetos (sem classes)

Escrito em 19 de Maio de 2007 em JavaScript, Orientação a Objetos por Leonel Togniolli

Já escrevi sobre o Qooxdoo, uma biblioteca GUI para a web feita em JavaScript. Quem não quem conhece JavaScript, pode ficar impressionado com as capacidades desse framework orientado a objeto. E vai também, sem dúvida, ficar com as mãos amarradas no momento que tentar escrever alguma coisa com ele. Antes de nos aprofundarmos em Qooxdoo, temos que conhecer um pouco de JavaScript.

Este artigo é uma introdução de JavaScript, para você deixar de vê-la como uma forma de fazer pequenos scripts em browser e passar a reconhecer o potencial que ela tem, como uma linguagem orientada a objetos, com recursos únicos muito bons.

Primeiramente, é necessário se desligar do nome bastante enganador: JavaScript não tem herança alguma de Java - chamava LiveScript quando foi implementada pela primeira vez pela Netscape (e Mocha, antes disso). Ganhou o prefixo Java em uma jogada de marketing da Netscape aliada com a Sun, sem ter ganhado nada que a tornasse mais perto dessa outra linguagem da Sun, que estava começando a entrar na moda em 1995.

O sufixo Script do nome também dá uma impressão errada. Ela não tem mais de “script” do que outra linguagens interpretadas por aí. Apesar do host mais comum que estamos acostumados a ver JavaScript executando são os browsers, não existe nada na definição da linguagem que a limite nesse sentido.

Tendo isso esclarecido, podemos partir para JavaScript. É uma linguagem dinâmica, fracamente tipada, com orientação a objetos baseada em prototipos. O que isso quer dizer?

A primeira parte, linguagem dinâmica, é algo que está na moda ultimamente. Uma linguagem dinâmica é uma que é capaz de alterar sua estrutura durante a execução, algo que em linguagens tradicionais, acontece durante a compilação. Isso quer dizer que JavaScript pode criar novos tipos, adicionar métodos em objetos existentes.

Ser fracamente tipada é uma faca de dois gumes - variáveis não possuem tipos estritamente definidos, funções não obrigam tipos de parâmetros pré-determinados (nem a quantidade deles). Permite uma programação muito mais flexível do que seria possível em uma linguagem fortemente tipada, dando um poder muito grande para JavaScript, mas requer uma atenção maior do programador.

Finalmente, é orientada a objetos. Não tem, porém, classes ou tipos, como a orientação a objetos tradicional. Como pode ser orientada a objetos sem classes? JavaScript segue o modelo de orientação a objetos Prototipal. Nesse modelo, objetos são construídos, funções e campos são adicionados neles, e eles servem de protótipo, como uma forma, para outros objetos descendentes, sendo clonados e permitindo que funcionalidade seja adicionada na instância descendente. É uma modelo de difícil compreensão para quem está acostumado com a forma clássica, mas que, como os outros recursos que vimos até agora, dá para JavaScript um potencial enorme de solução de problemas de uma forma diferente da qual estamos acostumados.

Em JavaScript, tudo é um objeto. Inclusive funções são objetos, podendo ter membros, e métodos (outras funções). Outra característica interessante é que todos os objetos não são nada mais que Hashes - estruturas nome=valor contendo todos os campos e métodos da classe, que podem ser enumerados e alterados em qualquer momento que for necessário. 

Na próxima parte, vamos criar alguns objetos em JavaScript para aprender o básico da sintaxe, e descobrir o que é lambda e closures, recursos que tornam a implementações de funções um tanto mais fácil.

Lançamento de Busca Customizada Google para Delphi em Português

Escrito em 17 de Maio de 2007 em TechTips por Leonel Togniolli

O TechTips acaba de lançar um novo recurso: Busca Delphi Brasil. É uma engine de busca customizada do google que restringe a busca apenas em alguns sites brasileiros que possuem material de Delphi em português, selecionados manualmente e ajustados para retornar o melhor resultado para sua busca.

Não deixe de experimentar!

Se você tem ou conhece um site com material de Delphi em portugues que não está incluído nos resultados, não deixe de deixar um comentário aqui.

Tutorial: Instalando o SDK do Qooxdoo para criar aplicações AJAX

Escrito em 16 de Maio de 2007 em Ajax, Desenvolvimento Web, JavaScript por Leonel Togniolli

apresentei o Qooxdoo, uma biblioteca de interface visual escrita em JavaScript, com a finalidade de criar interfaces web ricas em aplicações AJAX. Se trabalhar com o Qooxdoo te interessou, aqui seguem os passos necessários para a instalação do SDK em plataforma windows, para poder desenvolver e depurar suas aplicações AJAX utilizando todos os recursos do Qooxdoo.

Pré-requisitos

O Qooxdoo utiliza uma espécie de compilação para gerar os arquivos de JavaScript para desenvolvimento e depuração ou para o deployment final (com otimizações extras). Isso é feito utilizando o make da GNU, disponível no windows através do cygwin. Baixe então o instalador web do cygwin. Rode a instalação e prossiga escolhendo um mirror qualquer, uma pasta de instalação qualquer, até chegar na seleção de pacotes. Não aceite os valores padrão - para desenvolver aplicações Qooxdoo, além dos pacotes padrão, você vai precisar selecionar os seguintes pacotes:

  • Archive
    • zip
    • unzip
  • Devel
    • make
    • gettext
    • gettext-devel
  • Interpreters
    • python
  • Net
    • rsync
  • Web
    • wget

 Para selecionar um pacote, clique sobre o texto “skip” ao lado dele para instalar a versão mais recente disponível.

Qooxdoo SDK

Após concluir a instalação do cygwin com sucesso, baixe o SDK do Qooxdoo. A versão mais recente, enquanto escrevo isto, é a 0.6.6. Basta descompactar o arquivo em alguma pasta.

Abrindo um prompt do cygwin (um shell bash, para quem está acostumado com ele no Linux), vá até a pasta que você descompactou o SDK. Geralmente, no cygwin, suas partições de dados do windows vão estar em /cydrive/unidade. No prompt do Cygwin, digite algo parecido com isso, portanto:

$ cd /cygdrive/c/projetos/qooxdoo-0.6.6-sdk/frontend/
$ make

 Se tudo correu bem com a instalação até aqui, ele deve fazer a geração dos arquivos JavaScript contendo o código fonte do Qooxdoo. Para testar, você já pode abrir o arquivo frontend/application/index.html em um browser e testar os exemplos que vem com ele.

A primeira aplicação

Se você gerou os arquivos com sucesso, o próximo passo é criar a sua primeira aplicação com o Qooxdoo (ou a segunda, se você acompanhou o primeiro artigo e já usou a versão quickstart).

A maneira mais fácil de iniciar uma nova aplicação vazia com o Qooxdoo é usando o esqueleto de uma aplicação vazia que vem com ele. Você vai encontrar esse esqueleto na pasta frontend/application da SDK, compactado em um arquivo .tar.gz. Descompacte esse arquivo para uma pasta qualquer (no meu caso, vou descompactar na mesma pasta onde descompactei a SDK, c:\projetos- você vai ter que arrumar os caminhos relativos nas configurações abaixo se colocar em uma pasta diferente). Renomeie a pasta para ter o nome do seu projeto.

O próximo passo é configurar o makefile, que vai estar dentro dessa pasta. Abra o makefile em um editor de texto e preencha os valores das variáveis QOOXDOO_PATH e QOOXDOO_URI. No meu caso, ficou assim:

QOOXDOO_PATH = ../qooxdoo-0.6.6-sdk
QOOXDOO_URI = ../../qooxdoo-0.6.6-sdk

 Neste ponto já podemos gerar nossa aplicação - basta navegar até essa pasta pelo prompt do cygwin e rodar o make.  Se tudo correu bem, você pode abrir o arquivo source/index.html no seu browser e ver (novamente) o hello world.

Agora basta editar o arquivo source/class/custom/Application.js colocando o código que você quiser. A partir desse ponto, não precisa mais rodar o make a cada alteração - basta salvar e recarregar a página no seu browser.

Espero que tenha conseguido instalar o Qooxdoo - nos próximos artigos, como depurar o JavaScript (seu e do Qooxdoo), e como gerar e distribuir sua aplicação AJAX.

Criando um Redimensionador de Imagens em PHP

Escrito em 15 de Maio de 2007 em Desenvolvimento Web, Orientação a Objetos, PHP por Cesar

Muitos sites hoje vistos pela web tem algum álbum de fotos ou algo parecido neles, então neste artigo veremos como fazer um redimensionador de imagens em PHP para que possa ser utilizado para padronizar o tamanho das imagens postadas no mesmo, inclusive podendo colocar uma “marca d’agua” nas imagens para os casos específicos e até rotacioná-las.

Vale lembrar que o PHP, ou melhor, a biblioteca de tratamento de imagens do PHP chamada de GD2, não é e não tem o objetivo de ser um “Photoshop”, mas supre as necessidades básicas para o tratamento de imagens para WEB. Devemos também saber que o resultado do uso desta biblioteca depende muito da imagem que será tratada, e que aumentar uma imagem através da biblioteca pode fazer com que perca significadamente sua qualidade.

Muito bem, para começarmos precisamos que a biblioteca GD2 do PHP esteja “descomentada” no arquivo PHP.INI no caso de usuários Windows, para usuários Linux será necessário recompilar o PHP com a biblioteca.

Com a GD2 habilitada, vamos a classe que será responsável pelo redimensionamento de imagens:

<?php
/**
 * Classe para redimensionamento de imagens com opções para rotacionar e adicionar marca d’agua.
 * 
 * @author Cesar Rodrigo Bagatoli -> e-mail: crbdigo@gmail.com
 * @version 1.0 em 15/05/2007.
 *
 */
class Resize {
    /**
     * Nome da imagem com seu caminho completo, dentro do servidor.
     *
     * @var string
     */
    protected $image;
    /**
     * Tipo da Imagem setado automaticamente (1 = GIF, 2 = JPG, 3 = PNG, 4 = SWF, 5 = PSD, 6 = BMP,
     * 7 = TIFF(intel byte order), 8 = TIFF(motorola byte order), 9 = JPC, 10 = JP2, 11 = JPX, 12 = JB2, 13 = SWC,
     * 14 = IFF, 15 = WBMP, 16 = XBM).
     * Somente serão aceitos os tipos 1, 2 e 3.
     *
     * @var int
     */
    protected $imageType;
    /**
     * Tamanho vertical da imagem original.
     *
     * @var int
     */
    protected $height;
    /**
     * Tamanho horizontal da imagem original.
     *
     * @var int
     */
    protected $width;

    /**
     * Nome da imagem com seu caminho completo no servidor que será feita uma cópia da original, para preservar a original.
     *
     * @var string
     */
    protected $newImage;
    
    /**
     * Novo tamanho vertical da imagem.
     *
     * @var int
     */
    protected $newHeight;

    /**
     * Novo tamanho horizontal da imagem.
     *
     * @var int
     */
    protected $newWidth;

    /**
     * Nome da imagem com seu caminho completo no servidor que será utilizada como marca d’água.
     *
     * @var string
     */
    protected $waterMarkImage;
    
    /**
     * Tipo da imagem da marca d’agua setado automaticamente (1 = GIF, 2 = JPG, 3 = PNG, 4 = SWF, 5 = PSD, 6 = BMP,
     * 7 = TIFF(intel byte order), 8 = TIFF(motorola byte order), 9 = JPC, 10 = JP2, 11 = JPX, 12 = JB2, 13 = SWC,
     * 14 = IFF, 15 = WBMP, 16 = XBM).
     * Somente serão aceitos os tipos 1, 2 e 3.
     *
     * @var int
     */
    protected $waterMarkImageType;
    
    /**
     * Tamanho vertical da marca d’agua.
     *
     * @var int
     */
    protected $waterMarkImageHeight;
    
    /**
     * Tamanho horizontal da marca d’agua.
     *
     * @var int
     */
    protected $waterMarkImageWidth;
    
    /**
     * Posição onde a marca d’agua será colocada.
     * Valores poderão ser ’topLeft’, ’topCenter’, ’topRight’, ’bottomLeft’, ’bottomCenter’, ’bottomRight’ ou ’center’.
     *
     * @var string
     */
    protected $waterMarkPosition = ’bottomRight’;
    
    /**
     * Opacidade da marca d’agua.
     * Valores de 0 a 100.
     *
     * @var integer
     */
    protected $waterMarkOpacity = 50;
    
    /**
     * 1 para redimensionar proporcionalmente a imagem, 0 para permitir distorção.
     *
     * @var int
     */
    protected $proportional = 1;

    /**
     * H para redimensinar proporcionalmente a imagem com base na Horizontal, e V para redimensionar
     * proporcionalmente a imagem com base na Vertical.
     *
     * @var string
     */
    protected $proportionalFlag = ’H';

    /**
     * Numero de graus em que a imagem deverá ser rotacionada.
     * Valor de -360 a 360.
     *
     * @var int
     */
    protected $degrees = 0;

    /**
     * Cor da zona descoberta da imagem após a rotação.
     *
     * @var int
     */
    protected $bgColor = 0;

    /**
     * Construtor
     *
     * @param string $image
     * @access public
     * @return boolean
     */
    public function Resize($image) {
        $this->setImage($image);

        return true;
    }

    /**
     * Seta uma nova imagem para ser tratada e pega as dimensões dela junto com seu tipo.
     *
     * @param string $image
     * @access public
     */
    public function setImage($image) {
        $this->image = $image;

        try {
            $tmp = getimagesize($this->image);
            $this->width  = $tmp[0];
            $this->height  = $tmp[1];
            $this->imageType = $tmp[2];
        }
        catch (Exception $e) {
            throw new Exception(”Não foi possível identificar o tamanho da imagem original.”);
        }
    }

    /**
     * Retorna o caminho e nome da imagem atual.
     *
     * @return string
     * @access public
     */
    public function getImage() {
        return $this->image;
    }

    /**
     * Seta uma imagem para ser criada como cópia da original.
     *
     * @param string $newImage
     * @access public
     */
    public function setNewImage($newImage) {
        $this->newImage = $newImage;
    }

    /**
     * Retorna o caminho e nome da imagem cópia.
     *
     * @return string
     * @access public
     */
    public function getNewImage() {
        return $this->newImage;
    }
    
    /**
     * Seta uma imagem para ser utilizada como marca d’água.
     *
     * @param string $waterMarkImage
     * @access public
     */
    public function setWaterMarkImage($waterMarkImage) {
        $this->waterMarkImage = $waterMarkImage;
    }
    
    /**
     * Retorna o caminho e nome da imagem para marca d’água.
     *
     * @return string
     * @access public
     */
    public function getWaterMarkImage() {
        return $this->waterMarkImage;
    }
    
    /**
     * Seta a posição em que a marca d’agua aparecerá na imagem.
     *
     * @param string $waterMarkPosition
     * @access public
     */
    public function setWaterMarkPosition($waterMarkPosition) {
        if($waterMarkPosition == ’topLeft’ || 
           $waterMarkPosition == ’topCenter’ || 
           $waterMarkPosition == ’topRight’ || 
           $waterMarkPosition == ’bottomLeft’ || 
           $waterMarkPosition == ’bottomCenter’ || 
           $waterMarkPosition == ’bottomRight’ || 
           $waterMarkPosition == ’center’) {
            $this->waterMarkPosition = $waterMarkPosition;
        }
        else {
            throw new Exception(”Posição da marca d’agua é inválida.”);
        }
    }
    
    /**
     * Retorna a posição em que a marca d’agua deve ser colocada.
     *
     * @return string
     * @access public
     */
    public function getWaterMarkPosition() {
        return $this->waterMarkPosition;
    }
    
    /**
     * Seta a opacidade da marca d’agua.
     * Valores de 0 a 100.
     *
     * @param int $waterMarkOpacity
     * @access public
     */
    public function setWaterMarkOpacity($waterMarkOpacity) {
        if($waterMarkOpacity >= 0 && $waterMarkOpacity <= 100) {
            $this->waterMarkOpacity = $waterMarkOpacity;
        }
        else {
            throw new Exception(”Opacidade informada é inválida. Use valores de 0 a 100.”);
        }
    }
    
    /**
     * Retorna a opacidade que a marca d’agua deverá ter.
     *
     * @return int
     * @access public
     */
    public function getWaterMarkOpacity() {
        return $this->waterMarkOpacity;
    }

    /**
     * Seta o tamanho para o qual a imagem (original ou cópia) será redimensionada.
     *
     * @param int $newHeight
     * @param int $newWidth
     * @access public
     */
    public function setNewSize($newHeight, $newWidth) {
        $this->newHeight = $newHeight;
        $this->newWidth = $newWidth;
    }

    /**
     * 1 para redimensionar proporcionalmente a imagem, 0 para permitir distorção.
     *
     * @param int $proportional
     * @access public
     */
    public function setProportional($proportional) {
        $this->proportional = $proportional;
    }

    /**
     * Retorna se é para ser proporcional ou não.
     *
     * @return integer
     * @access public
     */
    public function getProportional() {
        return $this->proportional;
    }

    /**
     * H para redimensinar proporcionalmente a imagem com base na Horizontal, e V para redimensionar
     * proporcionalmente a imagem com base na Vertical.
     *
     * @param string $proportionalFlag
     * @access public
     */
    public function setProportionalFlag($proportionalFlag) {
        $this->proportionalFlag = $proportionalFlag;
    }

    /**
     * Retorna a base pela qual será mantida a proporção de redimensionamento.
     *
     * @return string
     * @access public
     */
    public function getProportionalFlag() {
        return $this->proportionalFlag;
    }
    
    /**
     * Seta quantos graus em que a imagem deverá ser rotacionada.
     * Valor de -360 a 360.
     *
     * @param int $degrees
     * @access public
     */
    public function setDegrees($degrees) {
        if($degrees > 360 || $degrees < -360) {
            throw new Exception(”Ângulo informado incorreto. Valor aceito é de -360 a 360.”);
        }
        $this->degrees = $degrees;
    }
    
    /**
     * Retorna o numero de graus em que a imagem deverá ser rotacionada.
     *
     * @return int
     * @access public
     */
    public function getDegrees() {
        return $this->degrees;
    }
    
    /**
     * Seta a cor de fundo que a zona descoberta da imagem, depois de rotacionada, deverá ter.
     *
     * @param int $bgColor
     * @access public
     */
    public function setBgColor($bgColor) {
        $this->bgColor = $bgColor;
    }
    
    /**
     * Retorna a cor de fundo que a zona descoberta da imagem, depois de rotacionada, deverá ter.
     *
     * @return int
     * @access public
     */
    public function getBgColor() {
        return $this->bgColor;
    }

    /**
     * Faz uma cópia da imagem original e seta ela como $image para ser utilizada preservando a original.
     *
     * @return boolean
     * @access private
     * @uses setImage()
     * @uses getImage()
     * @uses setNewImage()
     * @uses getNewImage()
     */
    private function copyImage() {
        if(!empty($this->image) && !empty($this->newImage)) {
            if(!copy($this->getImage(), $this->getNewImage())) {
                throw new Exception(”Não foi possível copiar o arquivo.”);
            }
            else {
                $this->setImage($this->getNewImage());
                $this->setNewImage(”");
                return true;
            }
        }
        else {
            throw new Exception(”Erro. Verifique os atributos ’image’ e ’newImage’.”);
        }
    }
    
    /**
     * Verifica se todos os atributos necessários para o redimensionamento da imagem foram informados.
     * 
     * @access private
     */
    private function verifyAttributes() {
        if(empty($this->image)) {
            throw new Exception(”Atributo ’image’ não definido.”);
        }
        if(empty($this->imageType)) {
            throw new Exception(”Atributo ’imageType’ não definido.”);
        }
        if(empty($this->height)) {
            throw new Exception(”Atributo ’height’ não definido.”);
        }
        if(empty($this->width)) {
            throw new Exception(”Atributo ’width’ não definido.”);
        }
        if(empty($this->newHeight)) {
            throw new Exception(”Atributo ’newHeight’ não definido.”);
        }
        if(empty($this->newWidth)) {
            throw new Exception(”Atributo ’newWidth’ não definido.”);
        }
    }

    /**
     * Verifica se todos os atributos necessários para a imagem da marca d’agua foram informados.
     * 
     * @access private
     */
    private function verifyWaterMarkAttributes() {
        if(empty($this->waterMarkImage)) {
            throw new Exception(”Atributo ’waterMarkImage’ não definido.”);
        }
        if(empty($this->waterMarkImageType)) {
            throw new Exception(”Atributo ’waterMarkImageType’ não definido.”);
        }
        if(empty($this->waterMarkImageHeight)) {
            throw new Exception(”Atributo ’waterMarkHeight’ não definido.”);
        }
        if(empty($this->waterMarkImageWidth)) {
            throw new Exception(”Atributo ’waterMarkImageWidth’ não definido.”);
        }
    }

    /**
     * Redimensiona a imagem conforme configurado.
     *
     * @return boolean
     * @access public
     * @uses verifyAttributes()
     * @uses copyImage()
     * @uses getProportional()
     * @uses getProportionalFlag()
     * @uses getImage()
     * @uses getDegrees()
     * @uses getBgColor()
     * @uses getWaterMarkImage()
     * @uses getWaterMarkPosition()
     * @uses verifyWaterMarkAttributes()
     * @uses getWaterMarkOpacity();
     */
    public function make() {
        $this->verifyAttributes();
        
        /**
         * Faz a cópia da imagem caso tenha sido informado um novo nome de arquivo em ’newImage’.
         */
        if(!empty($this->newImage)) {
            $this->copyImage();
        }
        
        /**
         * Calcula a Proporção para o redimencionamento.
         */
        if($this->getProportional() == 1) {
            if($this->getProportionalFlag() == ’H') {
                /**
                 * Calcula o novo tamanho Vertical para ser proporcional.
                 */
                $this->newHeight = round(($this->newWidth * $this->height) / $this->width);
            }
            elseif($this->getProportionalFlag() == ’V') {
                /**
                 * Calcula o novo tamanho Horizontal para ser proporcional.
                 */
                $this->newWidth = round(($this->newHeight * $this->width) / $this->height);
            }
            else {
                throw new Exception(”Valor incorreto no atributo ’proportionalFlag’.”);
            }
        }
        elseif($this->getProportional() != 0) {
            throw new Exception(”Valor incorreto no atributo ’proportional’.”);
        }
        
        switch ($this->imageType) {
            case 1:
                $img    = imagecreatefromgif($this->getImage());
                $newImg = imagecreate($this->newWidth, $this->newHeight);
                break;
            case 2:
                $img    = imagecreatefromjpeg($this->getImage());
                $newImg = imagecreatetruecolor($this->newWidth, $this->newHeight);
                break;
            case 3:
                $img    = imagecreatefrompng($this->getImage());
                $newImg = imagecreatetruecolor($this->newWidth, $this->newHeight);
                break;
            default:
                throw new Exception(”Tipo de imagem informado não é compatível.”);
                break;
        }
        
        try {
            imagecopyresized($newImg, $img, 0, 0, 0, 0, $this->newWidth, $this->newHeight, $this->width, $this->height);
        }
        catch (Exception $e) {
            throw new Exception(”Não foi possível redimensionar a imagem.”);
        }
        
        /**
         * Gira a imagem.
         */
        if($this->getDegrees() != 0) {
            try {
                $newImg = imagerotate($newImg, $this->getDegrees(), $this->getBgColor());
                /**
                 * Pega os novos valores para ’newHeight’ e ’newWidth’.
                 */
                $this->newWidth = imagesx($newImg);
                $this->newHeight = imagesy($newImg);
            }
            catch (Exception $e) {
                throw new Exception(”Não foi possível rotacionar a imagem.”);
            }
        }
        
        /**
         * Adicionar uma marca d’agua na imagem pronta.
         */
        if(!empty($this->waterMarkImage)) {
            try {
                $tmp = getimagesize($this->getWaterMarkImage());
                $this->waterMarkImageWidth  = $tmp[0];
                $this->waterMarkImageHeight = $tmp[1];
                $this->waterMarkImageType   = $tmp[2];
                
                if($this->waterMarkImageHeight > $this->newHeight || 
                   $this->waterMarkImageWidth  > $this->newWidth) {
                    throw new Exception(”Marca d’agua é maior que imagem redimensionada.”);
                }
            }
            catch (Exception $e) {
                throw new Exception(”Não foi possível identificar o tamanho da imagem da marca d’agua.”);
            }
            
            $this->verifyWaterMarkAttributes();
            
            switch ($this->waterMarkImageType) {
                case 1:
                    $markImg = imagecreatefromgif($this->getWaterMarkImage());
                    break;
                case 2:
                    $markImg = imagecreatefromjpeg($this->getWaterMarkImage());
                    break;
                case 3:
                    $markImg = imagecreatefrompng($this->getWaterMarkImage());
                    break;
                default:
                    throw new Exception(”Tipo de imagem da marca d’agua informado não é compatível.”);
                    break;
            }
            
            /**
             * Calcula a área onde será colocada a marca d’agua.
             */
            switch($this->getWaterMarkPosition()) {
                case ’topLeft’:
                    $x = 10;
                    $y = 10;
                    break;
                case ’topCenter’:
                    $x = round(($this->newWidth / 2) - ($this->waterMarkImageWidth / 2));
                    $y = 10;
                    break;
                case ’topRight’:
                    $x = $this->newWidth - $this->waterMarkImageWidth - 10;
                    $y = 10;
                    break;
                case ’bottomLeft’:
                    $x = 10;
                    $y = $this->newHeight - $this->waterMarkImageHeight - 10;
                    break;
                case ’bottomCenter’:
                    $x = round(($this->newWidth / 2) - ($this->waterMarkImageWidth / 2));
                    $y = $this->newHeight - $this->waterMarkImageHeight - 10;
                    break;
                case ’bottomRight’:
                    $x = $this->newWidth - $this->waterMarkImageWidth - 10;
                    $y = $this->newHeight - $this->waterMarkImageHeight - 10;
                    break;
                case ’center’:
                    $x = round(($this->newWidth / 2) - ($this->waterMarkImageWidth / 2));
                    $y = round(($this->newHeight / 2) - ($this->waterMarkImageHeight / 2));
                    break;
                default:
                    $x = 10;
                    $y = 10;
            }
            
            /**
             * Adiciona a marca d’agua na imagem.
             */
            try {
                imagecopymerge($newImg, $markImg, $x, $y, 0, 0, $this->waterMarkImageWidth, $this->waterMarkImageHeight, $this->getWaterMarkOpacity());
            }
            catch (Exception $e) {
                throw new Exception(”Não foi possível adicionar marca d’agua.”);
            }
        }
        
        /**
         * Grava a imagem em arquivo.
         */
        try {
            switch ($this->imageType) {
             case 1:
             imagegif($newImg, $this->getImage());
             break;
             case 2:
             imagejpeg($newImg, $this->getImage(), 90);
             break;
             case 3:
             imagepng($newImg, $this->getImage());
             break;
             default:
                 throw new Exception(”Tipo de imagem informado não é compatível.”);
             break;
            }
        }
        catch (Exception $e) {
            throw new Exception($e);
        }
        
        return true;
    }
}
?>

Veja abaixo como é de facil utilização a classe que acabamos de criar:

try {
    $obj = new Resize(”imagem.jpg”);
    $obj->setNewImage(”novaImagem.jpg”);
    $obj->setWaterMarkImage(”marcaDagua.gif”);
    $obj->setWaterMarkOpacity(50);
    $obj->setWaterMarkPosition(’bottomCenter’);
    $obj->setProportionalFlag(’H');
    $obj->setProportional(1);
    $obj->setDegrees(90);
    $obj->setNewSize(500, 500);
    $obj->make();
}
catch (Exception $e) {
    die($e);
}

Pronto, acabamos tudo que é preciso para redimensionar as imagens em um site PHP.

Delphi Video Aula : Padronizando sistemas com Frames

Escrito em 11 de Maio de 2007 em Delphi, Orientação a Objetos, VCL/RTL, Video Aulas por leosimas

Olá pessoal!
Está é minha terceira video-aula sobre delphi para o TechTips.

Nesta video aula, vocês poderão ver o quanto é util criar frames padronizados para cadastro e consulta de dados de uma tabela.
Dessa forma, a produtividade aumenta imensamente e é uma opção aos famosos geradores de sistemas que existem por aí.

Para ver a video-aula, clique aqui.

Ps: Qualquer semelhança com nomes é pura coincidência.

Clientes Ricos Ajax na Web com Qooxdoo

Escrito em 11 de Maio de 2007 em Ajax, JavaScript, Qooxdoo por Leonel Togniolli

Qooxdoo é uma biblioteca de interface visual escrita em JavaScript que provê um grande conjunto de controles permitindo a criação de clientes ricos no browser, tendo compatibilidade com vários browsers em várias plataformas, geralmente em aplicações AJAX.

Ele tira proveito dos recursos de orientação a objeto de JavaScript (sabia que JavaScript é orientado a objeto?), tendo uma hierarquia poderosa de classes. O Delphi para PHP utiliza essa biblioteca para prover alguns recursos de forma bem automática, mas é possível utilizar o qooxdoo diretamente.

Para começar direto com a mão na massa, baixe a versão quickstart do qooxdoo, descompacte em uma pasta, e abra o index.html em um browser qualquer. Já é possível clicar no botão e ver a clássica mensagem “Hello World”. Deste ponto, você pode abrir o Application.js e fazer algumas alteracões para brincar um pouco com os recursos que ele tem. Uma sugestão:

qx.Proto.main = function(e)
{
        var docRoot=qx.ui.core.ClientDocument.getInstance();

        docRoot.setBackgroundColor("lightGray");

        var t1 = new qx.ui.form.TextField("Hello World");
        t1.setTop(25);
        t1.setLeft(10);

        var l1 = new qx.ui.basic.Label("Titulo");
        l1.setTop(10);
        l1.setLeft(10);

        var l2 = new qx.ui.basic.Label("<a href='http://www.techtips.com.br'>http://www.techtips.com.br</a>");
        l2.setTop(50);
        l2.setLeft(10);

        var btnAbreJanela = new qx.ui.form.Button("Abre Janela");
        btnAbreJanela.setTop(22);
        btnAbreJanela.setLeft(95);
        btnAbreJanela.addEventListener("execute", function() {
            var w1=new qx.ui.window.Window(t1.getValue());
            w1.setSpace(20,180,48,80);
            docRoot.add(w1);

            var btnFechaJanela = new qx.ui.form.Button("Fecha Janela");
            btnFechaJanela.setTop(13);
            btnFechaJanela.setLeft(50);
            btnFechaJanela.addEventListener("execute", function() {
                w1.close();
            });

            w1.add(btnFechaJanela);

            w1.open();
        });

        docRoot.add(l1);
        docRoot.add(l2);
        docRoot.add(t1);
        docRoot.add(btnAbreJanela);
};

Depois de copiar isso no seu arquivo .js, atualize o browser e crie várias janelas dentro do seu browser. Deixei disponível um demo online se você quiser ver o resultado diretamente. Ou ainda, veja o resultado rodando dentro do meu Firefox:

Para descobrir as classes que existem e suas propriedades, acompanhe a documentação do Qooxdoo pelo seu API Viewer (feito em Qooxdoo, aliás).

Depois disso, se você quiser utilizar o qooxdoo para um desenvolvimento maior, você deve instalar a versão completa, baixando o pacote do SDK. Vou escrever mais artigos detalhando a instalação dessa versão completa, a depuração de programas JavaScript dentro do browser, e algumas dicas da linguagem JavaScript (ela é bem mais poderosa do que muitos imaginam!). Fique ligado!

Conhecendo o Debugger do Delphi

Escrito em 10 de Maio de 2007 em Depuração por Leonel Togniolli

Se você usa o Delphi 7 ou versão anterior até tem uma desculpa para não conhecer todos os recursos do Debugger do Delphi - as janelas com as informações interessantes ficavam escondidas, por padrão, e era necessário ir atrás delas para exibí-las e utilizá-las. Nas versões mais recentes, até o novo Delphi 2007, ele vem com um “Debug Desktop” pré-definido com a maioria das janelas já configuradas como visíveis, e assim as informações ficaram bem mais acessíveis. Apesar disso, acredito que ainda existe quem as ignora e continua sem conhecer esses recursos interessantes.

Como pretendo escrever mais artigos sobre recursos de debugger, mostrando dicas e truques e citando as suas janelas, não posso deixar que você não conheça cada uma delas. Vamos dar uma passada rápida por elas.

  • Breakpoints - Ctrl-Alt-B

A janela de Breakpoints permite gerenciar todos os breakpoints de uma forma rápida. É possível adicionar novos breakpoints, incluindo breakpoints de dados, apagar/desabilitar todos ou seletivamente.

  • Call Stack - Ctrl-Alt-S

Call Stack mostra a pilha de chamadas, que é a sequência das rotinas que foram chamadas para que a execução chegasse naquele ponto que o programa se encontra. Bastante útil para saber o contexto que você está examinando, para saber porquê a execução chegou até ali. Particularmente útil quando a execução do programa passa por um ponto que não deveria e você não sabe bem o motivo.

  • Watches - Ctrl-Alt-W

Watches permitem monitorar o valor de variáveis e o resultado de expressões durante a depuração. Bom para acompanhar o conteúdo de alguma variável que não deveria estar sendo alterada. Não se esqueça do atalho para criar um novo watch - Ctrl-F5.

  • Local Variables - Ctrl-Alt-L

Uma espécie de watches automáticos, a janela de Local Variables mostra o conteúdo das variáveis locais à um método. Não deixe de usá-lo quando estiver passando por algum método que faz cálculos - é essencial ter todos os valores à vista.

  • Threads - Ctrl-Alt-T

Mostra todas as threads que pertencem ao seu processo, e seu status. Permite alterar a thread corrente para você depurá-la.

  • Event Log - Ctrl-Alt-V

O Event Log é um painel importante para vários truques de depuração. É nele que vão várias mensagens do debugger, como o inicio de threads, exceções, paradas em breakpoints.

  • Modules - Ctrl-Alt-M

A janela de Modules mostra todos os módulos carregados pelo seu executável, inclusive o caminho completo de cada um. Contém informação importante para aplicativos que são modularizados em DLLs e/ou BPLs, ajudando a descobrir qual arquivo está realmente sendo carregado, evitando o tempo perdido quando você compila um módulo mas o seu aplicativo carrega um cópia dele em uma pasta diferente.

Também diz quais dos módulos possuem símbolos para o debugger utilizar, e, para cada módulo, mostra uma lista de funções exportadas por ele, levando até a sua implementação.

  • CPU/FPU Window - Ctrl-Alt-C, Ctrl-Alt-F

As janelas de CPU e FPU mostram o conteúdo da memória, dos registradores do processador, e o disassembly do código que está sendo executado no momento. É relativamente assustadora para quem não conhece assembly, mas de qualquer forma, o acesso de baixo nível que é possível ter permite a investigação dos piores problemas, e alguns truques interessantes.

Todas as janelas acimas, além de disponíveis pelos atalhos que listei, também estão em View/Debug Windows.

Outras duas janelas importantes que não estão acessíveis por este menu são:

  • Evaluate/Modify - Ctrl-F7

Evaluate/Modify é um recurso que eu espero que você já tenha usado pelo menos alguma vez. Ele mostra o conteúdo de uma variável e permite a sua alteração. Também calcula o resultado de expressões e funções. Essencial para alguns truques que vou mostrar no futuro.

  • Debug Inspector

Uma janela que mostra o conteúdo de uma objeto ou outra estrutura em tempo de execução da mesma forma que o Object Inspector mostra as propriedades de componentes em tempo de projeto, é um recurso bastante útil e geralmente desconhecido e não utilizado.

Para ativá-lo existem algumas formas diferentes. Nas versões mais antigas, Ctrl-Clique em um identificador em tempo de projeto levava até a sua declaração, e durante a depuração abria o Debug Inspector. Nas versões mais recentes do Delphi o Ctrl-Clique foi incrementado para levar até a declaração durante a depuração também, então o atalho para o Debug Inspector mudou para Ctrl-Shift-Clique. Também é possível abrir o Debug Inspector dando um duplo clique em um objeto na janela de Local Variables. Tanto o Evaluate/Modify quanto o Debug Inspector também são acessíveis pelo menu de contexto do editor.

Isso foi um resumo rápido da interface do debugger do Delphi, que vai servir como base para futura dicas aqui.

Utilizando Assertions em Delphi

Escrito em 09 de Maio de 2007 em Linguagem Delphi por Leonel Togniolli

Assertions são validações sobre os fundamentos da lógica de um programa. A verificação de um assertion é a validação de que uma condição necessária para o algoritimo funcionar, algo que você supõe ser verdadeiro, é realmente válido durante a execução do programa.

Em Delphi, a sintaxe de um Assertion é a seguinte:

Assert(Condition: Boolean; [Message: string]);

Isso quer dizer que Assert é um procedimento que aceita uma condição, e opcionalmente uma mensagem de erro. É usado como uma prática defensiva de codificação para garantir o estado do seu programa, pegando falhas antes que aconteçam. Se a condição passada para o assert for avaliada como falso, será gerada uma exceção com a mensagem informada (ou ‘assertion failure’, se não for passada nenhuma), e o mais interessante, por uma mágica de compilador a mensagem também inclui o nome do arquivo e a linha onde a falha ocorreu.

Assertions não são feitas para validação de entrada de dados ou de resultados de funções, mas sim de pressupostos que deveriam sempre ser verdadeiros, e se não forem, indicam existe uma bug em alguma parte do seu programa. Por esse motivo, é prática comum desabilitar a validação de assertions no executável final produzido para distribuição, o que pode ser feito através de Project/Options, Compiler, Debug Information, Assertions.

Tendo em mente tudo isso, vamos a alguns exemplos:

procedure TMinhaClasse.AdicionaItem(Item: TMeuItem);
begin
  Assert(Assigned(Item), 'Foi passado um item nil!'); // ERRADO!
  FList.Add(Item);
end;

Um Assertion Failure não é uma validação apropriada para a validação de parâmetros de métodos públicos. Enquanto seria aceitável se esse método fosse chamado apenas internamente, se ele for disponibilizado para outros desenvolvedores ou para ser utilizado em outras classes, ele deve funcionar corretamente em todos os casos, com assertions habilitadas ou não. Desta forma, poderiamos escrever

procedure TMinhaClasse.AdicionaItem(Item: TMeuItem);
begin
  if not Assigned(Item) then
    raise Exception.Create('Foi passado um item nil!');
  Assert(FList.Add(Item) >= 0, 'Item não foi adicionado corretamente'); //ERRADO!
end;

Esta segunda tentativa de utilizar o assert também é falha, pois coloca código importante dentro do assert. Se essa classe for compilada com assertions desligados, esse código não vai ser executado e a lógica não vai mais funcionar.

procedure TMinhaClasse.AdicionaItem(Item: TMeuItem);
begin
  if not Assigned(Item) then
    raise Exception.Create('Foi passado um item nil!');
  Assert(Assigned(FLista), "FLista é nil');
  FList.Add(Item);
end;

Neste último exemplo estamos fazendo uma validação adequada para um assert - FLista deve estar sempre atribuída durante o tempo de vida da classe. Se não tiver, é um problema da lógica interna da classe (e não um problema externo como a validação do parâmetro). Essa validação apontaria o erro adequadamente durante a depuração, evitando causar um Access Violation mais dificil se se diagnosticar. E, caso seja removida para o executável de produção, não vai causar nenhum comportamento diferente no programa.

Próxima Página »