Source code: com/fm/gui/fmCreateChannelDialog.java
1 /****************************************************************************
2 * Copyright (c) 2003 Andrew Duka | aduka@users.sourceforge.net
3 * All right reserved.
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 ****************************************************************************/
11 package com.fm.gui;
12
13 import javax.swing.*;
14 import javax.swing.event.TreeSelectionListener;
15 import javax.swing.event.TreeSelectionEvent;
16 import javax.swing.tree.TreeModel;
17 import javax.swing.tree.DefaultTreeModel;
18 import javax.swing.tree.TreePath;
19 import javax.swing.tree.DefaultMutableTreeNode;
20
21 import com.fm.rss.rssChannelCategory;
22 import com.fm.rss.rssChannel;
23 import com.fm.gui.action.createCategoryAction;
24 import com.fm.update.retrieveThread;
25 import com.fm.update.fmEvent;
26
27 import java.text.DateFormat;
28 import java.util.*;
29 import java.awt.*;
30 import java.awt.event.*;
31 import java.net.URL;
32 import java.net.MalformedURLException;
33
34 /**
35 * Add/Update category dialog.
36 *
37 * <p>This dialog supposed to handle add/update category operations.</p>
38 *
39 * <p>Default action listener simply disposes dialog then Save or Discard
40 * button is pressed</p>
41 *
42 */
43 public class fmCreateChannelDialog extends JDialog implements ActionListener {
44
45 private fmCategoryTree categoryTree;
46 private JTextField channelTitle;
47 private JTextField channelURL;
48
49 /** result channel */
50 private rssChannel resultChannel;
51
52 /** Parent frame */
53 private JFrame parentFrame;
54 /** Iterates operation step */
55 private JButton nextButton;
56 /** Dicreases operation step */
57 private JButton prevButton;
58 /** Panel with add steps controls */
59 private JPanel stepsPane;
60 /** Label for huge hints */
61 private JLabel titlePaneLabel;
62
63
64 /** Checked if user wish autoremove old news */
65 private JCheckBox useAutoremove;
66 /** Number of days we keep the news */
67 private JComboBox daysComboBox;
68 /** Checked if autoupdate will be used */
69 private JCheckBox useAutocheck;
70 /** Number of days we keep the news */
71 private JTextField autoUpdateMinutes;
72
73 private int createStep;
74 /** Channel retriever */
75 private retrieveThread retriever;
76
77
78 /**
79 * Default constructor
80 */
81 public fmCreateChannelDialog(JFrame parent, TreeModel m)
82 {
83 super(parent);
84 parentFrame = parent;
85
86 // DEFAULT WINDOW ADAPTER
87 addWindowListener(new WindowAdapter() {
88 public void windowClosing(WindowEvent e)
89 {
90 if (retriever != null)
91 {
92 stopRetrieveProcess();
93 }
94 dispose();
95 }
96 });
97
98 resultChannel = new rssChannel();
99 createStep = 0;
100 retriever = null;
101
102
103 setTitle(fmUITheme.getString("dialog.create_channel.step" +
104 createStep + ".title"));
105
106 JPanel titlePane = new JPanel(new FlowLayout(FlowLayout.LEFT));
107 titlePane.setBackground(fmUITheme.WHITE_COLOR);
108 titlePane.setPreferredSize(new Dimension(400,40));
109
110 titlePaneLabel = new JLabel(fmUITheme.getString("dialog.create_channel.step0" +
111 ".secondary_title"));
112 titlePaneLabel.setFont(new Font("Verdana",Font.BOLD,15));
113 titlePane.add(titlePaneLabel);
114
115 ///////////////////////////////////////////////////
116 // ESCAPE KEY LISTENER
117 KeyListener escapeListener = new KeyAdapter() {
118 public void keyPressed(KeyEvent e) {
119 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
120 dispose();
121 }
122 }
123 };
124
125 // Multi step panel
126 stepsPane = new JPanel(new CardLayout());
127 stepsPane.setBorder(fmUITheme.FM_BUTTON_BORDER);
128
129 //Common dimension for edit bocxes
130 Dimension editBoxDim = new Dimension(350,25);
131
132 // STEP 1 PANEL
133 JPanel step1Pane = new JPanel();
134 step1Pane.setLayout(new BoxLayout(step1Pane, BoxLayout.Y_AXIS));
135 JLabel label = new JLabel(fmUITheme.getString("label.feed_source"));
136 label.setAlignmentX(Component.LEFT_ALIGNMENT);
137
138 channelURL = new JTextField();
139 channelURL.setAlignmentX(LEFT_ALIGNMENT);
140 channelURL.setPreferredSize(editBoxDim);
141 channelURL.setMaximumSize(editBoxDim);
142 channelURL.setBorder(fmUITheme.TEXT_FIELD_BORDER);
143 channelURL.addKeyListener(escapeListener);
144
145
146
147 step1Pane.add(label);
148 step1Pane.add(Box.createRigidArea(new Dimension(0,3)));
149
150 step1Pane.add(channelURL);
151 step1Pane.add(Box.createRigidArea(new Dimension(0,10)));
152
153
154
155 label = new JLabel(fmUITheme.getString("dialog.create_channel.step0" +
156 ".help"));
157 label.setAlignmentX(LEFT_ALIGNMENT);
158
159 step1Pane.add(label);
160 // STEP 1 PANEL END
161
162
163 // STEP 2 PANEL
164 JPanel step2Pane = new JPanel();
165 step2Pane.setLayout(new BoxLayout(step2Pane, BoxLayout.Y_AXIS));
166
167 label = new JLabel(fmUITheme.getString("label.title"));
168 label.setAlignmentX(LEFT_ALIGNMENT);
169
170 channelTitle = new JTextField();
171 channelTitle.setAlignmentX(Component.LEFT_ALIGNMENT);
172 channelTitle.setPreferredSize(editBoxDim);
173 channelTitle.setMaximumSize(editBoxDim);
174 channelTitle.setBorder(fmUITheme.TEXT_FIELD_BORDER);
175 channelTitle.addKeyListener(escapeListener);
176
177 step2Pane.add(label);
178 step2Pane.add(Box.createRigidArea(new Dimension(0,3)));
179
180 step2Pane.add(channelTitle);
181 step2Pane.add(Box.createRigidArea(new Dimension(0,10)));
182
183 label = new JLabel(fmUITheme.getString("dialog.create_channel.step1" +
184 ".help"));
185 label.setAlignmentX(LEFT_ALIGNMENT);
186
187 step2Pane.add(label);
188 // STEP 2 PANEL END
189
190
191 // STEP3
192 JPanel step3Pane = new JPanel();
193 step3Pane.setLayout(new BoxLayout(step3Pane, BoxLayout.Y_AXIS));
194 /////////////////////////////////////////////
195 // AUTOCHECK
196 JPanel autoCheckPanel = new JPanel();
197 autoCheckPanel.setLayout(new BoxLayout(autoCheckPanel,BoxLayout.X_AXIS));
198 autoCheckPanel.setBorder(BorderFactory.createTitledBorder(fmUITheme.getString("label.autoupdate")));
199
200 autoUpdateMinutes = new JTextField(3);
201 autoUpdateMinutes.setText("60");
202 autoUpdateMinutes.setEditable(false);
203 autoUpdateMinutes.setMaximumSize(autoUpdateMinutes.getPreferredSize());
204 useAutocheck = new JCheckBox(fmUITheme.getString("box.auto_check_enable.label"),
205 false);
206 useAutocheck.setToolTipText(fmUITheme.getString("box.auto_check_enable.tooltip"));
207 useAutocheck.addItemListener(new ItemListener() {
208 public void itemStateChanged(ItemEvent e)
209 {
210 autoUpdateMinutes.setEditable(!autoUpdateMinutes.isEditable());
211 }
212 });
213
214
215 autoCheckPanel.add(useAutocheck);
216 autoCheckPanel.add(autoUpdateMinutes);
217 autoCheckPanel.add(new JLabel(" " + fmUITheme.getString("label.autoupdate.minutes")), BorderLayout.EAST);
218 autoCheckPanel.add(Box.createHorizontalGlue());
219 autoCheckPanel.add(Box.createVerticalGlue());
220
221
222
223
224 ////////////////////////////////////////////////////////
225 // AUTO REMOVE
226 JPanel storePolicyPanel = new JPanel();
227 storePolicyPanel.setLayout(new BoxLayout(storePolicyPanel,BoxLayout.X_AXIS));
228 storePolicyPanel.setBorder(BorderFactory.createTitledBorder(fmUITheme.getString("label.storepolicy")));
229
230 daysComboBox= new JComboBox(new Integer[]{new Integer(1),
231 new Integer(3),
232 new Integer(5),
233 new Integer(7),
234 new Integer(14),
235 new Integer(30),
236 new Integer(60),
237 new Integer(365)});
238 daysComboBox.setEnabled(false);
239 daysComboBox.setMaximumSize(daysComboBox.getPreferredSize());
240
241 useAutoremove = new JCheckBox(fmUITheme.getString("label.storepolicy.remove"),
242 false);
243 useAutoremove.setToolTipText(fmUITheme.getString("box.storepolicy.tooltip"));
244 useAutoremove.addItemListener(new ItemListener() {
245 public void itemStateChanged(ItemEvent e)
246 {
247 daysComboBox.setEnabled(!daysComboBox.isEnabled());
248 }
249 });
250 storePolicyPanel.add(useAutoremove);
251 storePolicyPanel.add(daysComboBox);
252 storePolicyPanel.add(new JLabel(fmUITheme.getString("label.storepolicy.days")));
253 storePolicyPanel.add(Box.createHorizontalGlue());
254 storePolicyPanel.add(Box.createVerticalGlue());
255
256
257 step3Pane.add(autoCheckPanel);//, BorderLayout.NORTH);
258 step3Pane.add(Box.createVerticalStrut(15));
259 step3Pane.add(storePolicyPanel);//, BorderLayout.CENTER);
260
261 ///////////////////////////////////////////////////////////////////////
262 // STEP 4 PANEL
263 JPanel step4Pane = new JPanel();
264 step4Pane.setLayout(new BoxLayout(step4Pane, BoxLayout.Y_AXIS));
265
266 label = new JLabel(fmUITheme.getString("label.folder"));
267 label.setAlignmentX(LEFT_ALIGNMENT);
268
269 categoryTree = new fmCategoryTree((DefaultTreeModel)m); // using common data
270 JScrollPane treeScroll = new JScrollPane(categoryTree);
271 categoryTree.addKeyListener(escapeListener);
272 //categoryTree.repaint();
273
274 treeScroll.setPreferredSize(new Dimension(200,150));
275 treeScroll.setAlignmentX(LEFT_ALIGNMENT);
276
277 // TREE LISTENER
278 categoryTree.addTreeSelectionListener(new TreeSelectionListener() {
279 public void valueChanged(TreeSelectionEvent e)
280 {
281 fmTreeNode node = (fmTreeNode)categoryTree.getLastSelectedPathComponent();
282 if (node != null)
283 nextButton.setEnabled(true);
284 else
285 nextButton.setEnabled(false);
286 }
287 });
288
289 step4Pane.add(label);
290 step4Pane.add(Box.createRigidArea(new Dimension(0,3)));
291
292 step4Pane.add(treeScroll);
293 step4Pane.add(Box.createRigidArea(new Dimension(0,8)));
294
295 label = new JLabel(fmUITheme.getString("dialog.create_channel.step3" +
296 ".help"));
297 step4Pane.add(label);
298
299 label.setAlignmentX(LEFT_ALIGNMENT);
300 JButton createFolderButton = new JButton(new createCategoryAction(categoryTree));
301 createFolderButton.setAlignmentX(LEFT_ALIGNMENT);
302 step4Pane.add(Box.createRigidArea(new Dimension(0,15)));
303 step4Pane.add(createFolderButton);
304 // STEP 4 PANEL END
305
306 stepsPane.add(step1Pane, "URL input pane");
307 stepsPane.add(step2Pane, "Channel title");
308 stepsPane.add(step3Pane, "Channel autoupdate");
309 stepsPane.add(step4Pane, "Channel folder");
310
311
312 // NAVIGATION PANE
313 GridBagLayout navGridBag = new GridBagLayout();
314 GridBagConstraints c = new GridBagConstraints();
315 JPanel navPane = new JPanel(navGridBag);
316
317 prevButton = fmUITheme.getButtonInstance("button.prev.label",
318 "button.prev.tooltip",
319 KeyEvent.VK_P);
320 prevButton.addKeyListener(escapeListener);
321
322 nextButton = fmUITheme.getButtonInstance("button.next.label",
323 "button.next.tooltip",
324 KeyEvent.VK_N);
325 nextButton.addKeyListener(escapeListener);
326
327 // PREV BUTTON LISTENER
328 prevButton.addActionListener(new ActionListener() {
329 public void actionPerformed(ActionEvent e)
330 {
331 previousStep();
332 }
333 });
334 // PREV BUTTON SETTINGS
335 c.anchor = GridBagConstraints.SOUTHEAST;
336 c.gridx = 0;
337 c.gridy = 1;
338 c.weightx = 0.15;
339 c.gridwidth = 2;
340 c.insets = new Insets(15,2,6,0);
341 c.ipadx = 15;
342 navGridBag.setConstraints(prevButton, c);
343 navPane.add(prevButton);
344
345 // NEXT BUTTON LISTENER
346 nextButton.addActionListener(new ActionListener() {
347 public void actionPerformed(ActionEvent e)
348 {
349 nextStep();
350 }
351 });
352 // NEXT BUTTON SETTINGS
353 c.gridx = GridBagConstraints.RELATIVE;
354 c.weightx = GridBagConstraints.RELATIVE;
355 navGridBag.setConstraints(nextButton, c);
356 navPane.add(nextButton);
357
358
359 JButton button = fmUITheme.getButtonInstance("button.cancel.label",
360 "button.cancel.label",
361 KeyEvent.VK_C);
362 c.insets = new Insets(15,8,6,0);
363 c.fill = GridBagConstraints.HORIZONTAL;
364 navGridBag.setConstraints(button, c);
365 button.addKeyListener(escapeListener);
366
367 // CANCEL ACTION LISTENER
368 button.addActionListener(new ActionListener() {
369 public void actionPerformed(ActionEvent e)
370 {
371 if (retriever != null)
372 stopRetrieveProcess();
373 else
374 dispose();
375 }
376 });
377 navPane.add(button);
378
379 // FINAL ADD
380 this.getContentPane().add(titlePane, BorderLayout.NORTH);
381 this.getContentPane().add(stepsPane, BorderLayout.CENTER);
382 this.getContentPane().add(navPane, BorderLayout.SOUTH);
383
384 // Resizeability
385 setResizable(false);
386
387
388 }
389
390 /**
391 * Default action handler.
392 *
393 * <p>Default action handler simply disposes dialog if one of
394 * the dialog buttons is pressed</p>
395 *
396 * @param e ActionEvent instance
397 */
398 public void actionPerformed(ActionEvent e)
399 {
400 if (e.getActionCommand().equals(fmEvent.RETRIEVE_FINISHED))
401 {
402 // restoring URL value
403 channelURL.setText(resultChannel.getSource());
404 // Enabling buttons
405 prevButton.setEnabled(true);
406 nextButton.setEnabled(true);
407
408 if (((fmEvent)e).getStatus() == fmEvent.SUCCESS_STATUS)
409 {
410 ++createStep;
411
412 setStepTitles(createStep);
413
414 channelTitle.setText(resultChannel.getTitle());
415
416 // Changing pane
417 ((CardLayout)stepsPane.getLayout()).next(stepsPane);
418
419 if (resultChannel.getTtl() != -1)
420 {
421 useAutocheck.setSelected(true);
422 autoUpdateMinutes.setText((new Integer(resultChannel.getTtl())).toString());
423 }
424 retriever = null; //reseting retriever
425 }
426 else
427 {
428 JOptionPane.showMessageDialog(this,
429 fmUITheme.getString("msg.channel_retrieve_failed"),
430 fmUITheme.getString("label.error"),
431 JOptionPane.ERROR_MESSAGE);
432 retriever = null; //reseting retriever
433 return;
434 }
435 }
436
437 }
438
439
440 /**
441 * Return current category tree view
442 */
443 public fmCategoryTree getCategoryTree()
444 {
445 return categoryTree;
446 }
447
448 /**
449 * Start thread, which will retrieve the channel specified by given URL.
450 * <p>Note: Blocks all controls except 'Cancel' button</p>
451 *
452 * @param sURL channel source URL
453 */
454 private void retrieveChannel(String sURL)
455 {
456 if (sURL.length() == 0)
457 return;
458
459 try { URL testURL = new URL(sURL); }
460 catch (MalformedURLException e)
461 {
462 JOptionPane.showMessageDialog(this,
463 fmUITheme.getString("msg.invalid_url"),
464 fmUITheme.getString("label.error"),
465 JOptionPane.ERROR_MESSAGE);
466 return;
467 }
468
469 // resultChannel object must be created prior, since
470 // create/edit operations both use this function
471 resultChannel.setSource(sURL);
472 retriever = new retrieveThread(this, resultChannel, retrieveThread.RETRIEVE_MODE);
473
474 nextButton.setEnabled(false);
475 prevButton.setEnabled(false);
476 channelURL.setText(fmUITheme.getString("dialog.open_url.retrieve_message"));
477 retriever.start();
478
479
480 }
481
482 /**
483 * Stop retrieve thread and enable all blocked buttons
484 * <p>Note: Unblocks all controls previously blocked by <see>retriveChannel()</see></p>
485 */
486 private void stopRetrieveProcess()
487 {
488
489
490 }
491
492 /**
493 * Overrides standard implementation
494 *
495 * @param isVisible boolean flag
496 */
497 public void setVisible(boolean isVisible)
498 {
499
500 if (isVisible)
501 {
502 // Location
503 Point location = new Point(parentFrame.getLocationOnScreen());
504 location.x += 30;
505 location.y += 30;
506 setLocation(location);
507
508 // Initializing source URL from the clipboard (if any URL there)
509 channelURL.paste();
510 String t_new = channelURL.getText();
511
512 if (t_new.length() > 1024) // TOO LONG STRING ?
513 channelURL.setText("");
514 else
515 {
516 try
517 {
518 URL checkURL = new URL(t_new); // Check for validness
519 }
520 catch (MalformedURLException e)
521 {
522 channelURL.setText("");
523 }
524 }
525 // END OF URL INIT
526 }
527
528 super.setVisible(isVisible);
529 }
530
531 /**
532 * React for Next button
533 */
534 private void nextStep()
535 {
536 if (createStep == 0) // PROCESS RETRIEVEMENT
537 {
538 retrieveChannel(channelURL.getText());
539 }
540 else if ((createStep > 0) && (createStep < 3))
541 {
542 ((CardLayout)stepsPane.getLayout()).next(stepsPane);
543 ++createStep;
544
545 setStepTitles(createStep);
546
547 // At the finish
548 if (createStep == 3)
549 {
550 nextButton.setText(fmUITheme.getString("button.finish.label"));
551 nextButton.setToolTipText(fmUITheme.getString("button.finish.tooltip"));
552
553 // disable finish if none folder has been selected
554 if (categoryTree.getSelectionPath() == null)
555 nextButton.setEnabled(false);
556 else
557 nextButton.setEnabled(true);
558 }
559 }
560 else // FINAL STEP - CREATION
561 {
562 performChannelCreate();
563 }
564 }
565
566
567
568 /**
569 * React for Back button
570 */
571 private void previousStep()
572 {
573 if (createStep == 3)
574 {
575 // RESTORING NEXT BUTTON
576 nextButton.setText(fmUITheme.getString("button.next.label"));
577 nextButton.setToolTipText(fmUITheme.getString("button.next.tooltip"));
578 nextButton.setEnabled(true);
579 }
580
581 if (createStep > 0)
582 {
583 ((CardLayout)stepsPane.getLayout()).previous(stepsPane);
584 --createStep;
585
586 setStepTitles(createStep);
587
588 // Nothing prior
589 if (createStep == 0)
590 prevButton.setEnabled(false);
591 }
592 }
593
594 /**
595 * Set titles accordingly to step number
596 *
597 * @param step
598 */
599 public void setStepTitles(int step)
600 {
601 setTitle(fmUITheme.getString("dialog.create_channel.step" +
602 step + ".title"));
603 titlePaneLabel.setText(fmUITheme.getString("dialog.create_channel.step" +
604 step + ".secondary_title"));
605 }
606
607 /**
608 * Create channel using current data
609 *
610 */
611 private void performChannelCreate()
612 {
613 String chTitleStr = channelTitle.getText();
614
615 if (chTitleStr.length() == 0)
616 {
617 JOptionPane.showMessageDialog(null,
618 fmUITheme.getString("msg.short_title_warning"),
619 fmUITheme.getString("label.error"),
620 JOptionPane.ERROR_MESSAGE);
621 return;
622 }
623 else
624 {
625 resultChannel.setTitle(chTitleStr);
626 }
627
628 if (resultChannel.getID() == 0)
629 resultChannel.setID(resultChannel.generateID());
630
631 ///////////////////////////////////////////////////////
632 // KeepPeriod & Autocheck
633 if (useAutocheck.isSelected())
634 {
635 resultChannel.setTtl(Integer.parseInt(autoUpdateMinutes.getText()));
636 }
637
638 if (useAutoremove.isSelected())
639 {
640 resultChannel.setKeepPeriod(((Integer) daysComboBox.getSelectedItem()).intValue());
641 resultChannel.filterItems();
642 }
643
644 // getting tree
645 fmTreeNode parentNode;
646 rssChannelCategory parentCat = null;
647 TreePath nodePath = categoryTree.getSelectionPath();
648
649 if (nodePath == null)
650 {
651 // none of categories is selected
652 // probably display message here
653 nextButton.setEnabled(false);
654 return;
655 }
656
657 else
658 {
659 parentNode = (fmTreeNode)nodePath.getLastPathComponent();
660 parentCat = (rssChannelCategory) parentNode.getUserObject();
661 }
662
663 int result = categoryTree.insertNode(parentCat, resultChannel, true);
664
665
666 if (result == fmCategoryTree.NODE_NOT_FOUND)
667 {
668 JOptionPane.showMessageDialog(null,
669 fmUITheme.getString("msg.cant_find_folder"),
670 fmUITheme.getString("label.error"),
671 JOptionPane.ERROR_MESSAGE);
672 }
673 else if (result == fmCategoryTree.NODE_EXISTS)
674 {
675 JOptionPane.showMessageDialog(null,
676 fmUITheme.getString("msg.channel_exists_warning"),
677 fmUITheme.getString("label.error"),
678 JOptionPane.ERROR_MESSAGE);
679 }
680 else
681 {
682 this.dispose();
683 }
684 }
685 }