]> Dogcows Code - chaz/vimcoder/blob - src/com/dogcows/VimCoder.java
do not screw up line endings when reading files
[chaz/vimcoder] / src / com / dogcows / VimCoder.java
1
2 package com.dogcows;
3
4 import java.awt.*;
5 import java.awt.event.*;
6 import java.io.*;
7 import java.text.SimpleDateFormat;
8 import java.util.*;
9 import javax.swing.*;
10 import javax.swing.border.*;
11
12 import com.topcoder.client.contestApplet.common.Common;
13 import com.topcoder.client.contestApplet.common.LocalPreferences;
14 import com.topcoder.client.contestant.ProblemComponentModel;
15 import com.topcoder.shared.language.Language;
16 import com.topcoder.shared.problem.Renderer;
17
18 /**
19 * @author Charles McGarvey
20 * The TopCoder Arena editor plug-in providing support for Vim.
21 *
22 * Distributable under the terms and conditions of the 2-clause BSD license;
23 * see the file COPYING for a complete text of the license.
24 */
25 public class VimCoder
26 {
27 /**
28 * The name and version of this plugin.
29 */
30 public final static String version = "VimCoder 0.3.6";
31
32
33 /**
34 * The first part of the command used to invoke the Vim server.
35 */
36 private static String vimCommand = "gvim";
37
38 /**
39 * The path to the main VimCoder directory.
40 */
41 private static File rootDir;
42 static
43 {
44 if (System.getProperty("os.name").toLowerCase().equals("win"))
45 {
46 vimCommand = "C:\\WINDOWS\\gvim.bat";
47 }
48 rootDir = new File(System.getProperty("user.home") + System.getProperty("file.separator") + ".vimcoder");
49 }
50
51 /**
52 * Whether or not to use the contest name and point value as problem
53 * directory names.
54 */
55 private static boolean contestDirNames = false;
56
57
58 /**
59 * The panel given to the Arena applet when it is requested.
60 */
61 private JPanel panel;
62
63 /**
64 * The text widget where log messages are appended.
65 */
66 private JTextArea logArea;
67
68 /**
69 * The current editor object (or null if there is none).
70 */
71 private Editor editor;
72
73 /**
74 * The configuration panel.
75 */
76 private JDialog configDialog;
77
78
79 /**
80 * The key for the vim command preference.
81 */
82 private final static String VIMCOMMAND = "com.dogcows.VimCoder.config.vimcommand";
83
84 /**
85 * The key for the root directory preference.
86 */
87 private final static String ROOTDIR = "com.dogcows.VimCoder.config.rootdir";
88
89 /**
90 * The key for the problem directory name preference.
91 */
92 private final static String CONTESTDIRNAMES = "com.dogcows.VimCoder.config.contestdirnames";
93
94 /**
95 * The preferences object for storing plugin settings.
96 */
97 private static LocalPreferences prefs = LocalPreferences.getInstance();
98
99
100 /**
101 * Get the command for invoking vim.
102 * @return The command.
103 */
104 public static String getVimCommand()
105 {
106 return vimCommand;
107 }
108
109 /**
110 * Get the storage directory.
111 * @return The directory.
112 */
113 public static File getStorageDirectory()
114 {
115 return rootDir;
116 }
117
118 /**
119 * Get whether or not to save problems in a human-readable directory
120 * structure.
121 * @return The directory name setting.
122 */
123 public static boolean isContestDirNames()
124 {
125 return contestDirNames;
126 }
127
128
129 /**
130 * Instantiate the entry point of the editor plugin.
131 * Sets up the log widget and panel.
132 */
133 public VimCoder()
134 {
135 logArea = new JTextArea();
136 logArea.setForeground(Color.GREEN);
137 logArea.setBackground(Color.BLACK);
138 logArea.setEditable(false);
139 Font font = new Font("Courier", Font.PLAIN, 12);
140 if (font != null) logArea.setFont(font);
141
142 panel = new JPanel(new BorderLayout());
143 panel.add(new JScrollPane(logArea), BorderLayout.CENTER);
144 }
145
146
147 /**
148 * Called by the Arena when the plugin is about to be used.
149 */
150 public void startUsing()
151 {
152 Runnable task = new Runnable()
153 {
154 public void run()
155 {
156 logArea.setText("");
157 }
158 };
159 if (SwingUtilities.isEventDispatchThread())
160 {
161 task.run();
162 }
163 else
164 {
165 SwingUtilities.invokeLater(task);
166 }
167 loadConfiguration();
168 }
169
170 /**
171 * Called by the Arena when the plugin is no longer needed.
172 */
173 public void stopUsing()
174 {
175 editor = null;
176 }
177
178 /**
179 * Called by the Arena to obtain the editor panel which we will use to
180 * show log messages.
181 * @return The editor panel.
182 */
183 public JPanel getEditorPanel()
184 {
185 return panel;
186 }
187
188 /**
189 * Called by the Arena to obtain the current source.
190 * This happens when the user is saving, compiling, and/or submitting.
191 * @return The current source code.
192 * @throws Exception If the source file edited by Vim couldn't be read.
193 */
194 public String getSource() throws Exception
195 {
196 try
197 {
198 String source = editor.getSource();
199 logInfo("Source code uploaded to server.");
200 return source;
201 }
202 catch (Exception exception)
203 {
204 logError("Failed to get source code: " + exception.getLocalizedMessage());
205 throw exception;
206 }
207 }
208
209 /**
210 * Called by the Arena to pass the source it has.
211 * @param source The source code.
212 */
213 public void setSource(String source)
214 {
215 try
216 {
217 editor.setSource(source);
218 logInfo("Source code downloaded from server.");
219 }
220 catch (Exception exception)
221 {
222 logError("Failed to save the source given by the server: " + exception.getLocalizedMessage());
223 return;
224 }
225 }
226
227 /**
228 * Called by the Arena to pass along information about the current
229 * problem.
230 * @param component A container for the particulars of the problem.
231 * @param language The currently selected language.
232 * @param renderer A helper object to help format the problem
233 * statement.
234 */
235 public void setProblemComponent(ProblemComponentModel component,
236 Language language, Renderer renderer)
237 {
238 try
239 {
240 editor = new Editor(component, language, renderer);
241 }
242 catch (Exception exception)
243 {
244 logError("An error occurred while loading the problem: " + exception.getLocalizedMessage());
245 }
246 }
247
248 /**
249 * Called by the Arena when it's time to show our configuration panel.
250 */
251 public void configure()
252 {
253 final int border = 10;
254 final int inset = 2;
255
256 loadConfiguration();
257
258 configDialog = new JDialog();
259 Container container = configDialog.getContentPane();
260 container.setForeground(Common.FG_COLOR);
261 container.setBackground(Common.WPB_COLOR);
262
263 JPanel pane = new JPanel();
264 container.add(pane);
265
266 BoxLayout boxLayout = new BoxLayout(pane, BoxLayout.Y_AXIS);
267 pane.setLayout(boxLayout);
268 pane.setBorder(BorderFactory.createEmptyBorder(border, border, border, border));
269
270 JPanel fieldPanel = new JPanel(new GridBagLayout());
271 pane.add(fieldPanel);
272 pane.add(Box.createRigidArea(new Dimension(0, border)));
273
274 GridBagConstraints c = new GridBagConstraints();
275 c.fill = GridBagConstraints.HORIZONTAL;
276 c.insets = new Insets(inset, inset, inset, inset);
277
278 JLabel rootDirLabel = new JLabel("Storage Directory:");
279 rootDirLabel.setForeground(Common.FG_COLOR);
280 c.gridx = 0;
281 c.gridy = 0;
282 c.gridwidth = 1;
283 fieldPanel.add(rootDirLabel, c);
284
285 final JTextField rootDirField = new JTextField(rootDir.getPath());
286 rootDirField.setPreferredSize(new Dimension(0, 24));
287 c.gridx = 1;
288 c.gridy = 0;
289 c.weightx = 1.0;
290 fieldPanel.add(rootDirField, c);
291
292 JButton browseButton = new JButton("Browse");
293 c.gridx = 2;
294 c.gridy = 0;
295 c.weightx = 0.0;
296 c.anchor = GridBagConstraints.BASELINE_LEADING;
297 fieldPanel.add(browseButton, c);
298
299 final JCheckBox contestDirNamesButton = new JCheckBox(
300 "Store problems according to contest name and point value.",
301 contestDirNames
302 );
303 contestDirNamesButton.setForeground(Common.FG_COLOR);
304 contestDirNamesButton.setBackground(Common.WPB_COLOR);
305 contestDirNamesButton.setFont(rootDirLabel.getFont());
306 c.gridx = 1;
307 c.gridy = 1;
308 c.gridwidth = 2;
309 fieldPanel.add(contestDirNamesButton, c);
310
311 JLabel vimCommandLabel = new JLabel("Vim Command:");
312 vimCommandLabel.setForeground(Common.FG_COLOR);
313 c.gridx = 0;
314 c.gridy = 2;
315 c.gridwidth = 1;
316 fieldPanel.add(vimCommandLabel, c);
317
318 final JTextField vimCommandField = new JTextField(vimCommand);
319 vimCommandField.setPreferredSize(new Dimension(0, 24));
320 c.gridx = 1;
321 c.gridy = 2;
322 c.weightx = 1.0;
323 c.gridwidth = 2;
324 fieldPanel.add(vimCommandField, c);
325
326 JPanel buttonPanel = new JPanel(new FlowLayout(FlowLayout.TRAILING, inset, inset));
327 buttonPanel.setPreferredSize(new Dimension(400, 24 + 2 * inset));
328 pane.add(buttonPanel);
329
330 JButton saveButton = new JButton("Save");
331 buttonPanel.add(saveButton);
332 buttonPanel.add(Box.createRigidArea(new Dimension(1, 0)));
333
334 JButton closeButton = new JButton("Close");
335 buttonPanel.add(closeButton);
336
337 browseButton.addActionListener(new ActionListener()
338 {
339 public void actionPerformed(ActionEvent actionEvent)
340 {
341 JFileChooser chooser = new JFileChooser();
342 chooser.setCurrentDirectory(new File("."));
343 chooser.setDialogTitle("Choose Storage Directory");
344 chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
345 chooser.setAcceptAllFileFilterUsed(false);
346
347 if (chooser.showOpenDialog(configDialog) == JFileChooser.APPROVE_OPTION)
348 {
349 rootDirField.setText(chooser.getSelectedFile().getPath());
350 }
351 }
352 });
353
354 closeButton.addActionListener(new ActionListener()
355 {
356 public void actionPerformed(ActionEvent actionEvent)
357 {
358 configDialog.dispose();
359 }
360 });
361
362 saveButton.addActionListener(new ActionListener()
363 {
364 public void actionPerformed(ActionEvent actionEvent)
365 {
366 prefs.setProperty(VIMCOMMAND, vimCommandField.getText());
367 prefs.setProperty(ROOTDIR, rootDirField.getText());
368 prefs.setProperty(CONTESTDIRNAMES, String.valueOf(contestDirNamesButton.isSelected()));
369 JOptionPane.showMessageDialog(null, "Preferences were saved successfully.");
370 }
371 });
372
373 configDialog.setTitle("VimCoder Preferences");
374 configDialog.pack();
375 configDialog.setLocationRelativeTo(null); // Center dialog in screen.
376 configDialog.setModalityType(Dialog.DEFAULT_MODALITY_TYPE);
377 configDialog.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
378 configDialog.setVisible(true);
379 }
380
381
382 /**
383 * Load the local preferences related to this plugin.
384 */
385 private void loadConfiguration()
386 {
387 String vc = prefs.getProperty(VIMCOMMAND);
388 if (vc != null) vimCommand = vc;
389
390 String dir = prefs.getProperty(ROOTDIR);
391 if (dir != null) rootDir = new File(dir);
392
393 String cn = prefs.getProperty(CONTESTDIRNAMES);
394 if (cn != null) contestDirNames = Boolean.parseBoolean(cn);
395 }
396
397
398 /**
399 * A generic logging function, appends text to the text area. A timestamp
400 * is also prepended to the next text.
401 * @param what The text to append.
402 */
403 private void log(final String what)
404 {
405 Runnable task = new Runnable()
406 {
407 public void run()
408 {
409 SimpleDateFormat format = new SimpleDateFormat("kk:mm:ss");
410 logArea.append(format.format(new Date()) + ", " + what);
411 }
412 };
413 if (SwingUtilities.isEventDispatchThread())
414 {
415 task.run();
416 }
417 else
418 {
419 SwingUtilities.invokeLater(task);
420 }
421 }
422
423 /**
424 * Output non-critical messages to the log.
425 * @param what The text of the message.
426 */
427 private void logInfo(String what)
428 {
429 log(" INFO: " + what + System.getProperty("line.separator"));
430 }
431
432 /**
433 * Output critical messages and errors to the log.
434 * @param what The text of the message.
435 */
436 private void logError(String what)
437 {
438 log("ERROR: " + what + System.getProperty("line.separator"));
439 }
440 }
441
442 // vim:et:ts=8:sts=4:sw=4
This page took 0.055978 seconds and 5 git commands to generate.