| [66] | 1 | // | 
|---|
 | 2 | //  Copyright (C) 2002 Constantin Kaplinsky.  All Rights Reserved. | 
|---|
 | 3 | // | 
|---|
 | 4 | //  This is free software; you can redistribute it and/or modify | 
|---|
 | 5 | //  it under the terms of the GNU General Public License as published by | 
|---|
 | 6 | //  the Free Software Foundation; either version 2 of the License, or | 
|---|
 | 7 | //  (at your option) any later version. | 
|---|
 | 8 | // | 
|---|
 | 9 | //  This software is distributed in the hope that it will be useful, | 
|---|
 | 10 | //  but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
 | 11 | //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
 | 12 | //  GNU General Public License for more details. | 
|---|
 | 13 | // | 
|---|
 | 14 | //  You should have received a copy of the GNU General Public License | 
|---|
 | 15 | //  along with this software; if not, write to the Free Software | 
|---|
 | 16 | //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, | 
|---|
 | 17 | //  USA. | 
|---|
 | 18 | // | 
|---|
 | 19 |  | 
|---|
 | 20 | // | 
|---|
 | 21 | // Recording frame. It allows to control recording RFB sessions into | 
|---|
 | 22 | // FBS (FrameBuffer Stream) files. | 
|---|
 | 23 | // | 
|---|
 | 24 |  | 
|---|
 | 25 | import java.io.*; | 
|---|
 | 26 | import java.awt.*; | 
|---|
 | 27 | import java.awt.event.*; | 
|---|
 | 28 |  | 
|---|
 | 29 | class RecordingFrame extends Frame | 
|---|
 | 30 |   implements WindowListener, ActionListener { | 
|---|
 | 31 |  | 
|---|
 | 32 |   boolean recording; | 
|---|
 | 33 |  | 
|---|
 | 34 |   TextField fnameField; | 
|---|
 | 35 |   Button browseButton; | 
|---|
 | 36 |  | 
|---|
 | 37 |   Label statusLabel; | 
|---|
 | 38 |  | 
|---|
 | 39 |   Button recordButton, nextButton, closeButton; | 
|---|
 | 40 |   VncViewer viewer; | 
|---|
 | 41 |  | 
|---|
 | 42 |   // | 
|---|
 | 43 |   // Check if current security manager allows to create a | 
|---|
 | 44 |   // RecordingFrame object. | 
|---|
 | 45 |   // | 
|---|
 | 46 |  | 
|---|
 | 47 |   public static boolean checkSecurity() { | 
|---|
 | 48 |     SecurityManager security = System.getSecurityManager(); | 
|---|
 | 49 |     if (security != null) { | 
|---|
 | 50 |       try { | 
|---|
 | 51 |         security.checkPropertyAccess("user.dir"); | 
|---|
 | 52 |         security.checkPropertyAccess("file.separator"); | 
|---|
 | 53 |         // Work around (rare) checkPropertyAccess bug | 
|---|
 | 54 |         System.getProperty("user.dir"); | 
|---|
 | 55 |       } catch (SecurityException e) { | 
|---|
 | 56 |         System.out.println("SecurityManager restricts session recording."); | 
|---|
 | 57 |         return false; | 
|---|
 | 58 |       } | 
|---|
 | 59 |     } | 
|---|
 | 60 |     return true; | 
|---|
 | 61 |   } | 
|---|
 | 62 |  | 
|---|
 | 63 |   // | 
|---|
 | 64 |   // Constructor. | 
|---|
 | 65 |   // | 
|---|
 | 66 |  | 
|---|
 | 67 |   RecordingFrame(VncViewer v) { | 
|---|
 | 68 |     super("TightVNC Session Recording"); | 
|---|
 | 69 |  | 
|---|
 | 70 |     viewer = v; | 
|---|
 | 71 |  | 
|---|
 | 72 |     // Determine initial filename for next saved session. | 
|---|
 | 73 |     // FIXME: Check SecurityManager. | 
|---|
 | 74 |  | 
|---|
 | 75 |     String fname = nextNewFilename(System.getProperty("user.dir") + | 
|---|
 | 76 |                                    System.getProperty("file.separator") + | 
|---|
 | 77 |                                    "vncsession.fbs"); | 
|---|
 | 78 |  | 
|---|
 | 79 |     // Construct new panel with file name field and "Browse" button. | 
|---|
 | 80 |  | 
|---|
 | 81 |     Panel fnamePanel = new Panel(); | 
|---|
 | 82 |     GridBagLayout fnameGridbag = new GridBagLayout(); | 
|---|
 | 83 |     fnamePanel.setLayout(fnameGridbag); | 
|---|
 | 84 |  | 
|---|
 | 85 |     GridBagConstraints fnameConstraints = new GridBagConstraints(); | 
|---|
 | 86 |     fnameConstraints.gridwidth = GridBagConstraints.RELATIVE; | 
|---|
 | 87 |     fnameConstraints.fill = GridBagConstraints.BOTH; | 
|---|
 | 88 |     fnameConstraints.weightx = 4.0; | 
|---|
 | 89 |  | 
|---|
 | 90 |     fnameField = new TextField(fname, 64); | 
|---|
 | 91 |     fnameGridbag.setConstraints(fnameField, fnameConstraints); | 
|---|
 | 92 |     fnamePanel.add(fnameField); | 
|---|
 | 93 |     fnameField.addActionListener(this); | 
|---|
 | 94 |  | 
|---|
 | 95 |     fnameConstraints.gridwidth = GridBagConstraints.REMAINDER; | 
|---|
 | 96 |     fnameConstraints.weightx = 1.0; | 
|---|
 | 97 |  | 
|---|
 | 98 |     browseButton = new Button("Browse"); | 
|---|
 | 99 |     fnameGridbag.setConstraints(browseButton, fnameConstraints); | 
|---|
 | 100 |     fnamePanel.add(browseButton); | 
|---|
 | 101 |     browseButton.addActionListener(this); | 
|---|
 | 102 |  | 
|---|
 | 103 |     // Construct the frame. | 
|---|
 | 104 |  | 
|---|
 | 105 |     GridBagLayout gridbag = new GridBagLayout(); | 
|---|
 | 106 |     setLayout(gridbag); | 
|---|
 | 107 |  | 
|---|
 | 108 |     GridBagConstraints gbc = new GridBagConstraints(); | 
|---|
 | 109 |     gbc.gridwidth = GridBagConstraints.REMAINDER; | 
|---|
 | 110 |     gbc.fill = GridBagConstraints.BOTH; | 
|---|
 | 111 |     gbc.weighty = 1.0; | 
|---|
 | 112 |     gbc.insets = new Insets(10, 0, 0, 0); | 
|---|
 | 113 |  | 
|---|
 | 114 |     Label helpLabel = | 
|---|
 | 115 |       new Label("File name to save next recorded session in:", Label.CENTER); | 
|---|
 | 116 |     gridbag.setConstraints(helpLabel, gbc); | 
|---|
 | 117 |     add(helpLabel); | 
|---|
 | 118 |  | 
|---|
 | 119 |     gbc.fill = GridBagConstraints.HORIZONTAL; | 
|---|
 | 120 |     gbc.weighty = 0.0; | 
|---|
 | 121 |     gbc.insets = new Insets(0, 0, 0, 0); | 
|---|
 | 122 |  | 
|---|
 | 123 |     gridbag.setConstraints(fnamePanel, gbc); | 
|---|
 | 124 |     add(fnamePanel); | 
|---|
 | 125 |  | 
|---|
 | 126 |     gbc.fill = GridBagConstraints.BOTH; | 
|---|
 | 127 |     gbc.weighty = 1.0; | 
|---|
 | 128 |     gbc.insets = new Insets(10, 0, 10, 0); | 
|---|
 | 129 |  | 
|---|
 | 130 |     statusLabel = new Label("", Label.CENTER); | 
|---|
 | 131 |     gridbag.setConstraints(statusLabel, gbc); | 
|---|
 | 132 |     add(statusLabel); | 
|---|
 | 133 |  | 
|---|
 | 134 |     gbc.fill = GridBagConstraints.HORIZONTAL; | 
|---|
 | 135 |     gbc.weightx = 1.0; | 
|---|
 | 136 |     gbc.weighty = 0.0; | 
|---|
 | 137 |     gbc.gridwidth = 1; | 
|---|
 | 138 |     gbc.insets = new Insets(0, 0, 0, 0); | 
|---|
 | 139 |  | 
|---|
 | 140 |     recordButton = new Button("Record"); | 
|---|
 | 141 |     gridbag.setConstraints(recordButton, gbc); | 
|---|
 | 142 |     add(recordButton); | 
|---|
 | 143 |     recordButton.addActionListener(this); | 
|---|
 | 144 |  | 
|---|
 | 145 |     nextButton = new Button("Next file"); | 
|---|
 | 146 |     gridbag.setConstraints(nextButton, gbc); | 
|---|
 | 147 |     add(nextButton); | 
|---|
 | 148 |     nextButton.addActionListener(this); | 
|---|
 | 149 |  | 
|---|
 | 150 |     closeButton = new Button("Close"); | 
|---|
 | 151 |     gridbag.setConstraints(closeButton, gbc); | 
|---|
 | 152 |     add(closeButton); | 
|---|
 | 153 |     closeButton.addActionListener(this); | 
|---|
 | 154 |  | 
|---|
 | 155 |     // Set correct text, font and color for the statusLabel. | 
|---|
 | 156 |     stopRecording(); | 
|---|
 | 157 |  | 
|---|
 | 158 |     pack(); | 
|---|
 | 159 |  | 
|---|
 | 160 |     addWindowListener(this); | 
|---|
 | 161 |   } | 
|---|
 | 162 |  | 
|---|
 | 163 |   // | 
|---|
 | 164 |   // If the given string ends with ".NNN" where NNN is a decimal | 
|---|
 | 165 |   // number, increase this number by one. Otherwise, append ".001" | 
|---|
 | 166 |   // to the given string. | 
|---|
 | 167 |   // | 
|---|
 | 168 |  | 
|---|
 | 169 |   protected String nextFilename(String fname) { | 
|---|
 | 170 |     int len = fname.length(); | 
|---|
 | 171 |     int suffixPos = len; | 
|---|
 | 172 |     int suffixNum = 1; | 
|---|
 | 173 |  | 
|---|
 | 174 |     if (len > 4 && fname.charAt(len - 4) == '.') { | 
|---|
 | 175 |       try { | 
|---|
 | 176 |         suffixNum = Integer.parseInt(fname.substring(len - 3, len)) + 1; | 
|---|
 | 177 |         suffixPos = len - 4; | 
|---|
 | 178 |       } catch (NumberFormatException e) { } | 
|---|
 | 179 |     } | 
|---|
 | 180 |  | 
|---|
 | 181 |     char[] zeroes = {'0', '0', '0'}; | 
|---|
 | 182 |     String suffix = String.valueOf(suffixNum); | 
|---|
 | 183 |     if (suffix.length() < 3) { | 
|---|
 | 184 |       suffix = new String(zeroes, 0, 3 - suffix.length()) + suffix; | 
|---|
 | 185 |     } | 
|---|
 | 186 |  | 
|---|
 | 187 |     return fname.substring(0, suffixPos) + '.' + suffix; | 
|---|
 | 188 |   } | 
|---|
 | 189 |  | 
|---|
 | 190 |   // | 
|---|
 | 191 |   // Find next name of a file which does not exist yet. | 
|---|
 | 192 |   // | 
|---|
 | 193 |  | 
|---|
 | 194 |   protected String nextNewFilename(String fname) { | 
|---|
 | 195 |     String newName = fname; | 
|---|
 | 196 |     File f; | 
|---|
 | 197 |     try { | 
|---|
 | 198 |       do { | 
|---|
 | 199 |         newName = nextFilename(newName); | 
|---|
 | 200 |         f = new File(newName); | 
|---|
 | 201 |       } while (f.exists()); | 
|---|
 | 202 |     } catch (SecurityException e) { } | 
|---|
 | 203 |  | 
|---|
 | 204 |     return newName; | 
|---|
 | 205 |   } | 
|---|
 | 206 |  | 
|---|
 | 207 |   // | 
|---|
 | 208 |   // Let the user choose a file name showing a FileDialog. | 
|---|
 | 209 |   // | 
|---|
 | 210 |  | 
|---|
 | 211 |   protected boolean browseFile() { | 
|---|
 | 212 |     File currentFile = new File(fnameField.getText()); | 
|---|
 | 213 |  | 
|---|
 | 214 |     FileDialog fd = | 
|---|
 | 215 |       new FileDialog(this, "Save next session as...", FileDialog.SAVE); | 
|---|
 | 216 |     fd.setDirectory(currentFile.getParent()); | 
|---|
 | 217 |     fd.setVisible(true); | 
|---|
 | 218 |     if (fd.getFile() != null) { | 
|---|
 | 219 |       String newDir = fd.getDirectory(); | 
|---|
 | 220 |       String sep = System.getProperty("file.separator"); | 
|---|
 | 221 |       if (newDir.length() > 0) { | 
|---|
 | 222 |         if (!sep.equals(newDir.substring(newDir.length() - sep.length()))) | 
|---|
 | 223 |           newDir += sep; | 
|---|
 | 224 |       } | 
|---|
 | 225 |       String newFname = newDir + fd.getFile(); | 
|---|
 | 226 |       if (newFname.equals(fnameField.getText())) { | 
|---|
 | 227 |         fnameField.setText(newFname); | 
|---|
 | 228 |         return true; | 
|---|
 | 229 |       } | 
|---|
 | 230 |     } | 
|---|
 | 231 |     return false; | 
|---|
 | 232 |   } | 
|---|
 | 233 |  | 
|---|
 | 234 |   // | 
|---|
 | 235 |   // Start recording. | 
|---|
 | 236 |   // | 
|---|
 | 237 |  | 
|---|
 | 238 |   public void startRecording() { | 
|---|
 | 239 |     statusLabel.setText("Status: Recording..."); | 
|---|
 | 240 |     statusLabel.setFont(new Font("Helvetica", Font.BOLD, 12)); | 
|---|
 | 241 |     statusLabel.setForeground(Color.red); | 
|---|
 | 242 |     recordButton.setLabel("Stop recording"); | 
|---|
 | 243 |  | 
|---|
 | 244 |     recording = true; | 
|---|
 | 245 |  | 
|---|
 | 246 |     viewer.setRecordingStatus(fnameField.getText()); | 
|---|
 | 247 |   } | 
|---|
 | 248 |  | 
|---|
 | 249 |   // | 
|---|
 | 250 |   // Stop recording. | 
|---|
 | 251 |   // | 
|---|
 | 252 |  | 
|---|
 | 253 |   public void stopRecording() { | 
|---|
 | 254 |     statusLabel.setText("Status: Not recording."); | 
|---|
 | 255 |     statusLabel.setFont(new Font("Helvetica", Font.PLAIN, 12)); | 
|---|
 | 256 |     statusLabel.setForeground(Color.black); | 
|---|
 | 257 |     recordButton.setLabel("Record"); | 
|---|
 | 258 |  | 
|---|
 | 259 |     recording = false; | 
|---|
 | 260 |  | 
|---|
 | 261 |     viewer.setRecordingStatus(null); | 
|---|
 | 262 |   } | 
|---|
 | 263 |  | 
|---|
 | 264 |   // | 
|---|
 | 265 |   // Close our window properly. | 
|---|
 | 266 |   // | 
|---|
 | 267 |  | 
|---|
 | 268 |   public void windowClosing(WindowEvent evt) { | 
|---|
 | 269 |     setVisible(false); | 
|---|
 | 270 |   } | 
|---|
 | 271 |  | 
|---|
 | 272 |   // | 
|---|
 | 273 |   // Ignore window events we're not interested in. | 
|---|
 | 274 |   // | 
|---|
 | 275 |  | 
|---|
 | 276 |   public void windowActivated(WindowEvent evt) {} | 
|---|
 | 277 |   public void windowDeactivated (WindowEvent evt) {} | 
|---|
 | 278 |   public void windowOpened(WindowEvent evt) {} | 
|---|
 | 279 |   public void windowClosed(WindowEvent evt) {} | 
|---|
 | 280 |   public void windowIconified(WindowEvent evt) {} | 
|---|
 | 281 |   public void windowDeiconified(WindowEvent evt) {} | 
|---|
 | 282 |  | 
|---|
 | 283 |  | 
|---|
 | 284 |   // | 
|---|
 | 285 |   // Respond to button presses | 
|---|
 | 286 |   // | 
|---|
 | 287 |  | 
|---|
 | 288 |   public void actionPerformed(ActionEvent evt) { | 
|---|
 | 289 |     if (evt.getSource() == browseButton) { | 
|---|
 | 290 |       if (browseFile() && recording) | 
|---|
 | 291 |         startRecording(); | 
|---|
 | 292 |  | 
|---|
 | 293 |     } else if (evt.getSource() == recordButton) { | 
|---|
 | 294 |       if (!recording) { | 
|---|
 | 295 |         startRecording(); | 
|---|
 | 296 |       } else { | 
|---|
 | 297 |         stopRecording(); | 
|---|
 | 298 |         fnameField.setText(nextNewFilename(fnameField.getText())); | 
|---|
 | 299 |       } | 
|---|
 | 300 |  | 
|---|
 | 301 |     } else if (evt.getSource() == nextButton) { | 
|---|
 | 302 |       fnameField.setText(nextNewFilename(fnameField.getText())); | 
|---|
 | 303 |       if (recording) | 
|---|
 | 304 |         startRecording(); | 
|---|
 | 305 |  | 
|---|
 | 306 |     } else if (evt.getSource() == closeButton) { | 
|---|
 | 307 |       setVisible(false); | 
|---|
 | 308 |  | 
|---|
 | 309 |     } | 
|---|
 | 310 |   } | 
|---|
 | 311 | } | 
|---|