quinta-feira, 11 de junho de 2015

Tagged under: , , , , , , , , ,

Trabalhando com Clipboard (área de transferência)


Ampliando o poder do CTRL+C / CTRL+V e tirando Printscreens da tela.


A maioria das vezes que utilizamos o CTRL+C, estamos com algum texto selecionado, mas
quando selecionamos um conjunto de arquivos, diretórios ou alguma imagem ou parte dela?

O Java nos fornece o objeto Clipboard, onde conseguimos acessar essa área de transferência.
Em conjunto com DataFlavor, conseguimos determinar quais tipos de dados estão na área.

Para obter uma instância do Clipboard, pedimos ao Toolkit:

Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();

Uma vez com o objeto clipboard em mãos, basta saber o que está na área de transferência:

try {
 if (clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor)) {
  clipboard.getData(DataFlavor.stringFlavor).toString();
  
 } else if (clipboard.isDataFlavorAvailable(DataFlavor.javaFileListFlavor)) {
  List<File> files = (List<File>) clipboard.getData(DataFlavor.javaFileListFlavor);
  
 } else if (clipboard.isDataFlavorAvailable(DataFlavor.imageFlavor)) {
  BufferedImage image = (BufferedImage) clipboard.getData(DataFlavor.imageFlavor);
 }
 
} catch (UnsupportedFlavorException | IOException ufe) {
 Logger.getLogger(getClass().getName()).log(null, null, ufe);
}

Neste exemplo estamos verificando apenas três tipos de dados, para ver outros tipos, consulte
a documentação. Na primeira verificação, temos um texto, nada que um editor de texto básico
não aceite. Já no segundo if, verificamos se o que foi copiado são pastas ou arquivos. Por fim, se temos uma imagem, gerada pela tecla Print Screen ou uma cópia do Paint, por exemplo.

Aplicação de exemplo

Nossa aplicação consiste em “colar” texto, arquivo e imagem, conforme código de exemplo anterior, ao clicar no botão Paste. Para texto, o comportamento é o padrão de qualquer editor. Já para os arquivos, ele apenas gera quatro listagens:
  •  Caminho completo dos arquivos, separados por “;”
  •  Caminho completo separados por linha
  •  Apenas o nome dos arquivos separados por “;”
  •  Nome dos arquivos separados por linha

private void pasteFiles(List<File> files) {
  if (files.size() == 1) {
   taResult.append(files.get(0).getAbsolutePath());
   taResult.append("\n");
   taResult.append(files.get(0).getName());
  } else {
   taResult.append("Caminho completo com ';':\n");
   for (File f : files) {
   taResult.append(f.getAbsolutePath());
   taResult.append(";");
   }
 
   taResult.append("\nNomes com ';':\n");
 
   for (File f : files) {
    taResult.append(f.getName());
    taResult.append(";");
   }
 
   taResult.append("\n");
   taResult.append("\nCaminho completo:\n");
 
   for (File f : files) {
    taResult.append(f.getAbsolutePath());
    taResult.append("\n");
   }
 
   taResult.append("\nNomes:\n");
 
   for (File f : files) {
    taResult.append(f.getName());
    taResult.append("\n");
   }
  }
}
Eu, por diversas vezes, preciso do nome de diversos arquivos em uma pasta para colocar na documentação, mas você poderia por exemplo, carregar o conteúdo do(s) arquivo(s), ou outras coisas (daquele tipo de coisa que se faz com arquivos). No caso da imagem, além de exibir uma prévia, a aplicação salva a imagem no formato JPG, na mesma pasta da aplicação.

As imagens são salvas em sequência, que é útil quando se está fazendo um tutorial.

private void pasteImage(BufferedImage image) {
 BufferedImage resizeImage = new BufferedImage(lblImage.getWidth(), lblImage.getHeight(), BufferedImage.TYPE_INT_RGB);
 Graphics g = resizeImage.createGraphics();

 g.drawImage(image, 0, 0, lblImage.getWidth(),lblImage.getHeight(), null);
 g.dispose();
 lblImage.setIcon(new ImageIcon(resizeImage));

 File fImg = new File("imagem_" + count + ".jpg");
 try {
 ImageIO.write(image, "jpg", fImg);
 count++;
 taResult.append("\nImagem salva em: " + fImg.getAbsolutePath());
 } catch (IOException ex) {
 Logger.getLogger(Window.class.getName()).log(Level.SEVERE, null, ex);
 }
}

Print Screen + Paste


Já sabemos como trabalhar com uma imagem no clipboard, mas dependemos de alguém ter clicado na tecla Print Screen antes, sendo que já vi teclados sem essa tecla. Mas o Java nos permite simular esse clique utilizando o método createScreenCapture da classe Robot, projetada inicialmente com o propósito de facilitar testes automáticos.

...
try {
 robo = new Robot();
} catch (AWTException ex) {
 Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, ex);
}
...

private void print() throws HeadlessException {
 if (robo != null) {
 Dimension screenSize = toolkit.getScreenSize();
 BufferedImage print = robo.createScreenCapture(new Rectangle(screenSize));
 pasteImage(print);
 }
}

Nó tiramos um print da tela inteira (toolkit.getScreenSize()) e depois salvamos a imagem utilizando o mesmo método pasteImage, visto anteriormente. Tudo isso ao clicar no botão Print and Save.
Para deixar as coisas mais interessantes, utilizamos a classe javax.swing.Timer para capturar a tela automaticamente a cada meio segundo, isso se o checkbox Auto Print estiver marcado.

timer = new Timer(500, new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {
 if (cbAutoPrint.isSelected()) {
 print();
 }
 }
});

Por fim

Com Java é fácil saber e obter o que o SO colocou na área de transferência e trabalhar com o que foi colocado lá, já saber o que fazer com isso, nem tanto. Em todo caso, qualquer editor de texto / imagem que se preze, precisa saber trabalhar com o clipboard. Vimos o objeto Robot superficialmente, mas com o que já vimos, criamos um programa para gravar a tela, facilitando a criação de tutoriais passo-a-passo (ou espionagem, vai saber...).

Você pode baixar a aplicação em https://sourceforge.net/projects/mvbostools.

PDF: https://drive.google.com/file/d/0B1rjzjT1KgoTanlfQ2dqRGNhNkk/view

Feito no NetBeans 8, o código fonte direto do SVN: https://sourceforge.net/p/mvbostools/code/HEAD/tree/Mlipboard

Java Doc:
http://docs.oracle.com/javase/7/docs/api/java/awt/Robot.html
https://docs.oracle.com/javase/8/docs/api/java/awt/datatransfer/DataFlavor.html

Marcus Becker
meumundojava@gmail.com

0 comentários: