1 package com.hammurapi.jcapture;
2 import java.awt.BorderLayout;
3 import java.awt.Color;
4 import java.awt.Dimension;
5 import java.awt.Insets;
6 import java.awt.Point;
7 import java.awt.Rectangle;
8 import java.awt.Toolkit;
9 import java.awt.event.ActionEvent;
10 import java.awt.event.ComponentAdapter;
11 import java.awt.event.ComponentEvent;
12 import java.awt.event.ComponentListener;
13 import java.awt.image.BufferedImage;
14 import java.io.ByteArrayInputStream;
15 import java.io.ByteArrayOutputStream;
16 import java.io.IOException;
17 import java.text.SimpleDateFormat;
18 import java.util.Date;
19 import java.util.Properties;
20 import java.util.concurrent.atomic.AtomicBoolean;
21 
22 import javax.imageio.ImageIO;
23 import javax.swing.AbstractAction;
24 import javax.swing.JButton;
25 import javax.swing.JFrame;
26 import javax.swing.JLabel;
27 import javax.swing.JOptionPane;
28 import javax.swing.JPanel;
29 import javax.swing.SwingUtilities;
30 import javax.swing.SwingWorker;
31 import javax.swing.border.LineBorder;
32 
33 import netscape.javascript.JSObject;
34 
35 import org.apache.http.HttpResponse;
36 import org.apache.http.HttpStatus;
37 
38 /**
39  * License: LGPL.
40  * @author Pavel Vlasov.
41  *
42  */
43 public class CaptureFrame extends javax.swing.JFrame {
44 	private JPanel capturePanel;
45 	private SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss-SSS");
46 	private int counter;
47 	private CaptureConfig captureConfig;
48 	private AbstractCaptureApplet applet;
49 	private JButton recordButton;
50 
getCaptureConfig()51 	public CaptureConfig getCaptureConfig() {
52 		return captureConfig;
53 	}
54 
CaptureFrame(final AbstractCaptureApplet applet)55 	public CaptureFrame(final AbstractCaptureApplet applet) throws Exception {
56 		super("Screen capture");
57 		setIconImage(Toolkit.getDefaultToolkit().getImage(getClass().getResource("camera.png")));
58 
59     	setUndecorated(true);
60 
61     	Translucener.makeFrameTranslucent(this);
62 
63 		setAlwaysOnTop(true);
64 		this.applet = applet;
65 		captureConfig = new CaptureConfig();
66 		captureConfig.load(applet.loadConfig());
67 		captureConfig.setBackgroundProcessor(applet.getBackgroundProcessor());
68 
69 		//--- GUI construction ---
70 
71 		capturePanel = new JPanel();
72 
73 		final JLabel dimensionsLabel = new JLabel("");
74 		capturePanel.add(dimensionsLabel, BorderLayout.CENTER);
75 
76 		capturePanel.addComponentListener(new ComponentAdapter() {
77 
78 			@Override
79 			public void componentResized(ComponentEvent e) {
80 				super.componentResized(e);
81 				dimensionsLabel.setText(e.getComponent().getWidth()+" x "+e.getComponent().getHeight());
82 			}
83 		});
84 
85 		JButton captureButton = new JButton(new AbstractAction() {
86 
87 			@Override
88 			public void actionPerformed(ActionEvent e) {
89 				Rectangle bounds = capturePanel.getBounds();
90 				Point loc = bounds.getLocation();
91 				SwingUtilities.convertPointToScreen(loc, capturePanel);
92 				bounds.setLocation(loc);
93 				Properties props = captureConfig.setRecordingRectangle(bounds);
94 				if (props!=null) {
95 					getApplet().storeConfig(props);
96 				}
97 				capturing.set(true);
98 				setVisible(false);
99 			}
100 
101 		});
102 		captureButton.setText("Capture");
103 		captureButton.setToolTipText("Create a snapshot of the screen");
104 		capturePanel.add(captureButton, BorderLayout.CENTER);
105 
106 		recordButton = new JButton(new AbstractAction() {
107 
108 			@Override
109 			public void actionPerformed(ActionEvent e) {
110 				Rectangle bounds = capturePanel.getBounds();
111 				Point loc = bounds.getLocation();
112 				SwingUtilities.convertPointToScreen(loc, capturePanel);
113 				bounds.setLocation(loc);
114 				Properties props = captureConfig.setRecordingRectangle(bounds);
115 				if (props!=null) {
116 					getApplet().storeConfig(props);
117 				}
118 				recording.set(true);
119 				setVisible(false);
120 			}
121 
122 		});
123 		recordButton.setText("Record");
124 		setRecordButtonState();
125 		capturePanel.add(recordButton, BorderLayout.CENTER);
126 
127 		JButton optionsButton = new JButton(new AbstractAction() {
128 
129 			@Override
130 			public void actionPerformed(ActionEvent e) {
131 				new CaptureOptionsDialog(CaptureFrame.this).setVisible(true);
132 			}
133 
134 		});
135 		optionsButton.setText("Options");
136 		capturePanel.add(optionsButton, BorderLayout.CENTER);
137 
138 		JButton cancelButton = new JButton(new AbstractAction() {
139 
140 			@Override
141 			public void actionPerformed(ActionEvent e) {
142 				CaptureFrame.this.setVisible(false);
143 			}
144 
145 		});
146 		cancelButton.setText("Cancel");
147 		capturePanel.add(cancelButton, BorderLayout.CENTER);
148 
149 		getContentPane().add(capturePanel, BorderLayout.CENTER);
150 
151 		capturePanel.setBorder(new LineBorder(new java.awt.Color(0,0,0), 1, false));
152 
153 		if (captureConfig.getRecordingRectangle()==null) {
154 			setSize(400, 300);
155 			setLocationRelativeTo(null);
156 		} else {
157 			setBounds(captureConfig.getRecordingRectangle());
158 		}
159 
160 		Insets dragInsets = new Insets(5, 5, 5, 5);
161     	new ComponentResizer(dragInsets, this);
162 
163     	ComponentMover cm = new ComponentMover();
164     	cm.registerComponent(this);
165     	cm.setDragInsets(dragInsets);
166 
167     	addComponentListener(new ComponentListener() {
168 
169 			@Override
170 			public void componentShown(ComponentEvent e) {
171 				// TODO Auto-generated method stub
172 
173 			}
174 
175 			@Override
176 			public void componentResized(ComponentEvent e) {
177 				// TODO Auto-generated method stub
178 
179 			}
180 
181 			@Override
182 			public void componentMoved(ComponentEvent e) {
183 				// TODO Auto-generated method stub
184 
185 			}
186 
187 			@Override
188 			public void componentHidden(ComponentEvent e) {
189 				if (capturing.get()) {
190 					capturing.set(false);
191 					try {
192 						capture();
193 					} catch (Exception ex) {
194 						ex.printStackTrace();
195 					}
196 				} else if (recording.get()) {
197 					recording.set(false);
198 					record();
199 				}
200 			}
201 		});
202 
203 	}
204 
setRecordButtonState()205 	void setRecordButtonState() {
206 		if (captureConfig.getEncoder()==null) {
207 			recordButton.setEnabled(false);
208 			recordButton.setToolTipText("Video format not selected. Use Options dialog to select video format.");
209 		} else {
210 			recordButton.setEnabled(true);
211 			recordButton.setToolTipText("Record screen activity and audio");
212 		}
213 	}
214 
getApplet()215 	public AbstractCaptureApplet getApplet() {
216 		return applet;
217 	}
218 
capture()219 	protected void capture() throws Exception {
220 		try {
221 			Thread.sleep(200); // For Ubuntu.
222 		} catch (InterruptedException ie) {
223 			// Ignore
224 		}
225 
226 		BufferedImage screenShot = captureConfig.createScreenShot(null, null).call().getRegions().get(0).getImage().getImage();
227 
228 		String prefix = getDatePrefix();
229 
230 		String defaultImageFormat = applet.getParameter("imageFormat");
231 		if (defaultImageFormat==null || defaultImageFormat.trim().length()==0) {
232 			defaultImageFormat = "PNG";
233 		}
234 		final String defaultFileExtension=defaultImageFormat.toLowerCase();
235 
236 		final String fileName = JOptionPane.showInputDialog(CaptureFrame.this, "Upload as", applet.getParameter("pageName")+"-capture-"+prefix+"-" + nextCounter() +"."+defaultFileExtension);
237 		if (fileName!=null) {
238 			try {
239 				ByteArrayOutputStream baos = new ByteArrayOutputStream();
240 				int idx = fileName.lastIndexOf('.');
241 				String imageFormat = idx==-1 ? defaultImageFormat : fileName.substring(idx+1).toUpperCase();
242 				ImageIO.write(screenShot, imageFormat, baos);
243 				final byte[] imageBytes = baos.toByteArray();
244 				System.out.println("Image size: "+imageBytes.length);
245 				// Uploading
246 				SwingWorker<Boolean, Long> task = new SwingWorker<Boolean, Long>() {
247 
248 					@Override
249 					protected Boolean doInBackground() throws Exception {
250 
251 						System.out.println("Uploading in background");
252 						try {
253 							HttpResponse iResponse = applet.post(
254 									CaptureFrame.this,
255 									new ByteArrayInputStream(imageBytes),
256 									imageBytes.length,
257 									fileName,
258 									"application/octet-stream");
259 
260 							System.out.println("Response status line: "+iResponse.getStatusLine());
261 							if (iResponse.getStatusLine().getStatusCode()!=HttpStatus.SC_OK) {
262 						    	errorMessage = iResponse.getStatusLine();
263 						    	errorTitle = "Error saving image";
264 						    	return false;
265 							}
266 							return true;
267 						} catch (Error e) {
268 							errorMessage=e.toString();
269 							errorTitle = "Upload error";
270 							e.printStackTrace();
271 							return false;
272 						}
273 					}
274 
275 					private Object errorMessage;
276 					private String errorTitle;
277 
278 					protected void done() {
279 						try {
280 							if (get()) {
281 								JSObject window = JSObject.getWindow(applet);
282 								String toEval = "insertAtCarret('"+applet.getParameter("edid")+"','{{:"+fileName+"|}}')";
283 								System.out.println("Evaluating: "+toEval);
284 								window.eval(toEval);
285 								CaptureFrame.this.setVisible(false);
286 							} else {
287 						    	JOptionPane.showMessageDialog(
288 						    			CaptureFrame.this,
289 										errorMessage,
290 										errorTitle,
291 										JOptionPane.ERROR_MESSAGE);
292 							}
293 						} catch (Exception e) {
294 							e.printStackTrace();
295 					    	JOptionPane.showMessageDialog(
296 					    			CaptureFrame.this,
297 									e.toString(),
298 									"Exception",
299 									JOptionPane.ERROR_MESSAGE);
300 						}
301 					};
302 
303 				};
304 
305 				task.execute();
306 			} catch (IOException ex) {
307 		    	JOptionPane.showMessageDialog(
308 		    			applet,
309 						ex.toString(),
310 						"Error saving image",
311 						JOptionPane.ERROR_MESSAGE);
312 			}
313 		}
314 	}
315 
nextCounter()316 	public int nextCounter() {
317 		return counter++;
318 	}
319 
getDatePrefix()320 	public String getDatePrefix() {
321 		return dateFormat.format(new Date());
322 	}
323 
record()324 	protected void record() {
325 		try {
326 			Thread.sleep(200); // For Ubuntu.
327 		} catch (InterruptedException ie) {
328 			// Ignore
329 		}
330 
331 		int borderWidth = 1;
332 		JFrame[] borderFrames = new JFrame[4];
333 
334 		Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
335 
336 		Rectangle rr = captureConfig.getRecordingRectangle();
337 		Color borderColor = Color.RED;
338 		if (rr.x>=borderWidth) {
339 			// West border
340 			borderFrames[0] = new JFrame();
341 			borderFrames[0].setDefaultCloseOperation(DISPOSE_ON_CLOSE);
342 			borderFrames[0].setSize(borderWidth, rr.height+borderWidth*2);
343 			borderFrames[0].setLocation(rr.x-borderWidth, rr.y-borderWidth);
344 			borderFrames[0].setUndecorated(true);
345 			borderFrames[0].setAlwaysOnTop(true);
346 			borderFrames[0].setFocusableWindowState(false);
347 			borderFrames[0].getContentPane().setBackground(borderColor);
348 		}
349 		if (rr.x+rr.width<dim.width-borderWidth) {
350 			// East border
351 			borderFrames[1] = new JFrame();
352 			borderFrames[1].setDefaultCloseOperation(DISPOSE_ON_CLOSE);
353 			borderFrames[1].setSize(borderWidth, rr.height+borderWidth*2);
354 			borderFrames[1].setLocation(rr.x+rr.width, rr.y-borderWidth);
355 			borderFrames[1].setUndecorated(true);
356 			borderFrames[1].setAlwaysOnTop(true);
357 			borderFrames[1].setFocusableWindowState(false);
358 			borderFrames[1].getContentPane().setBackground(borderColor);
359 		}
360 		if (rr.y>=borderWidth) {
361 			// North border
362 			borderFrames[2] = new JFrame();
363 			borderFrames[2].setDefaultCloseOperation(DISPOSE_ON_CLOSE);
364 			borderFrames[2].setSize(rr.width, borderWidth);
365 			borderFrames[2].setLocation(rr.x, rr.y-borderWidth);
366 			borderFrames[2].setUndecorated(true);
367 			borderFrames[2].setAlwaysOnTop(true);
368 			borderFrames[2].setFocusableWindowState(false);
369 			borderFrames[2].getContentPane().setBackground(borderColor);
370 		}
371 		if (rr.y+rr.height<dim.height-borderWidth) {
372 			// South border
373 			borderFrames[3] = new JFrame();
374 			borderFrames[3].setDefaultCloseOperation(DISPOSE_ON_CLOSE);
375 			borderFrames[3].setSize(rr.width, borderWidth);
376 			borderFrames[3].setLocation(rr.x, rr.y+rr.height);
377 			borderFrames[3].setUndecorated(true);
378 			borderFrames[3].setAlwaysOnTop(true);
379 			borderFrames[3].setFocusableWindowState(false);
380 			borderFrames[3].getContentPane().setBackground(borderColor);
381 		}
382 
383 		RecordingControlsFrame inst = new RecordingControlsFrame(this, borderFrames);
384 		int x = getLocation().x + getWidth() - inst.getWidth();
385 		if (x+inst.getWidth()>dim.getWidth()) {
386 			x = dim.width-inst.getWidth();
387 		} else if (x<0) {
388 			x = 0;
389 		}
390 
391 		int y = rr.getLocation().y+getHeight()+1;
392 		if (y+inst.getHeight()>dim.height) {
393 			y = rr.getLocation().y-inst.getHeight();
394 			if (y<0) {
395 				y=dim.height-inst.getHeight();
396 			}
397 		}
398 		inst.setLocation(x, y);
399 		inst.setVisible(true);
400 	}
401 
402 	private AtomicBoolean capturing = new AtomicBoolean(false);
403 	private AtomicBoolean recording = new AtomicBoolean(false);
404 
405 }
406