| 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 | } | 
|---|