Source code: com/fm/gui/fmFrame.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.event.TreeSelectionListener;
14 import javax.swing.event.TreeSelectionEvent;
15 import javax.swing.event.HyperlinkListener;
16 import javax.swing.event.HyperlinkEvent;
17 import javax.swing.tree.*;
18 import javax.swing.*;
19 import java.util.*;
20 import java.util.regex.Pattern;
21 import java.awt.*;
22 import java.awt.event.*;
23 import java.io.*;
24
25 import com.fm.rss.*;
26 import com.fm.rss.rssParseException;
27 import com.fm.update.Updateable;
28 import com.fm.update.updateException;
29 import com.fm.update.updateFactory;
30 import com.fm.gui.action.*;
31
32
33 /**
34 * Manin frame in the JNR GUI version
35 */
36 public class fmFrame extends JFrame implements ActionListener
37 {
38 /** Content displaying panel */
39 private fmContentPanel contentPane;
40
41 /** Panel with search form */
42 private JPanel searchPanel;
43
44 /** Category tree (left side) */
45 private fmCategoryTree categoryTree;
46
47 /** Default channel storage */
48 private ChannelStorage storage;
49
50 private JPopupMenu popupMenu;
51 private JToolBar toolBar;
52
53 /** Runtime properties store in the fm.ini */
54 private Properties runtimeProperties;
55
56 /** Hyperlink router (used within content panel to handle URL events */
57 private fmHyperlinkRouter linkRouter;
58
59 //default channel storage
60
61
62 public fmFrame(ChannelStorage st, Properties props)
63 {
64 super();
65
66 runtimeProperties = props;
67 storage = st;
68
69 // Some prior UI initialization
70 try
71 {
72 setupLookAndFeelDefaults(runtimeProperties);
73 fmUITheme.initFeedManLookAndFeel();
74 }
75 catch (IOException e)
76 {
77 JOptionPane.showMessageDialog(null,"Program was unable to find or open default resource bundle,"+
78 "please close and try again or report error to aduka@users.sourceforge.net",
79 "Fatal error: ",
80 JOptionPane.ERROR_MESSAGE);
81 System.exit(0);
82 }
83
84
85 setTitle("feedMan");
86
87 this.initFmFrame();
88 }
89
90 public void initFmFrame()
91 {
92 this.getContentPane().removeAll();
93
94 // SETTING UP ICON
95 this.setIconImage((fmUITheme.FM_ICON).getImage());
96
97 ///////////////////////////////////////////////////////////////////////
98 // SEARCH PANEL
99 JPanel searchCatPanel = new JPanel(new BorderLayout());
100
101 searchPanel = new JPanel();
102 searchPanel.setBackground(fmUITheme.LIGHT_BACKGROUND_COLOR);
103 final JTextField patternInput = new JTextField(12);
104 patternInput.setBorder(fmUITheme.FM_BUTTON_BORDER);
105 searchPanel.add(patternInput);
106 patternInput.addKeyListener(new KeyAdapter() {
107 public void keyPressed(KeyEvent e) {
108 if (e.getKeyCode() == KeyEvent.VK_ENTER)
109 performSearchOperation(patternInput.getText());
110 }
111 });
112
113 ///////////////////////////////////////////////////////////////////////
114 // FIND button on search panel
115 JButton activateSearch = new JButton(fmUITheme.getString("button.find.label"));
116
117 activateSearch.addActionListener(new ActionListener() {
118 public void actionPerformed(ActionEvent e) {
119 performSearchOperation(patternInput.getText());
120 }
121
122 });
123
124 activateSearch.setToolTipText(fmUITheme.getString("button.find.tooltip"));
125 searchPanel.add(activateSearch);
126 searchPanel.setPreferredSize(new Dimension(230,35));
127 searchCatPanel.add(searchPanel, BorderLayout.SOUTH);
128
129 ///////////////////////////////////////////////////////////////////////
130 // CATEGORY TREE
131 categoryTree = fmCategoryTree.getInstance(storage.getCategories(), true);
132 categoryTree.setBackground(fmUITheme.TREE_BACKGROUND_COLOR);
133 JScrollPane treeView = new JScrollPane(categoryTree);
134 searchCatPanel.add(treeView, BorderLayout.CENTER);
135
136 //Listen for when the selection changes.
137 categoryTree.addTreeSelectionListener(new TreeSelectionListener() {
138 public void valueChanged(TreeSelectionEvent e) {
139 TreePath p = categoryTree.getSelectionPath();
140
141 if (p == null)
142 return;
143
144 fmTreeNode node = (fmTreeNode) p.getLastPathComponent();
145 if (node == null) {
146 return;
147 }
148 // we normally will never meet a string (root node)
149 if (node.isCategory() == false)
150 {
151 contentPane.setChannel((rssChannel)node.getUserObject());
152 }
153 }
154 });
155
156 categoryTree.addMouseListener(new treeMouseListener());
157
158
159 ///////////////////////////////////////////////////////
160 // HYPERLINK ROUTER
161 if (runtimeProperties.containsKey(fmGuiConstants.URL_HANDLING_CMD))
162 {
163 String cmd = runtimeProperties.getProperty(fmGuiConstants.URL_HANDLING_CMD);
164
165 if (cmd.equals(fmGuiConstants.INTERNAL_URL_HANDLER))
166 linkRouter = new fmHyperlinkRouter(null);
167 else {
168
169 if (cmd.equals(fmGuiConstants.WIN32_URL_HANDLER)) {
170 // CHECKING THAT WE HAVE WINDOWS HANDLE ONLY ON WINDOWS MACHINES
171 String os = System.getProperty("os.name");
172 if(os == null || ((os.startsWith("Windows") == false))) {
173 cmd = fmGuiConstants.INTERNAL_URL_HANDLER;
174 runtimeProperties.put(fmGuiConstants.URL_HANDLING_CMD,
175 fmGuiConstants.INTERNAL_URL_HANDLER);
176 }
177 }
178
179 linkRouter = new fmHyperlinkRouter(cmd);
180 }
181 }
182 else // NOT SET OR FIRST RUN
183 {
184 String os = System.getProperty("os.name");
185 if(os != null && os.startsWith("Windows")) {
186 // Setting default windows handler
187 runtimeProperties.put(fmGuiConstants.URL_HANDLING_CMD,
188 fmGuiConstants.WIN32_URL_HANDLER);
189 linkRouter = new fmHyperlinkRouter(fmGuiConstants.WIN32_URL_HANDLER);
190 }
191 else {
192 runtimeProperties.put(fmGuiConstants.URL_HANDLING_CMD,
193 fmGuiConstants.INTERNAL_URL_HANDLER);
194 linkRouter = new fmHyperlinkRouter(null);
195 }
196 }
197
198 // INTERNAL CHANNEL NAVIGATION HANDLER
199 // Enabling handler for internal navigation
200 linkRouter.setPrefixHandler("http://channel/", new HyperlinkListener() {
201 public void hyperlinkUpdate(HyperlinkEvent e) {
202 int channelId = Integer.parseInt(e.getURL().toString().replaceFirst("http://channel/",""));
203 fmTreeNode tNode = categoryTree.findChannelNode(categoryTree.getRootNode(),
204 channelId);
205 if (tNode != null) {
206 contentPane.setChannel((rssChannel) tNode.getUserObject());
207 }
208 }
209 });
210
211
212
213 ///////////////////////////////////////////////////////
214 // CONTENT VIEW PANEL
215 contentPane = new fmContentPanel(runtimeProperties, linkRouter, categoryTree);
216
217
218 //Add the scroll panes to a split pane.
219 JSplitPane splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
220 splitPane.setLeftComponent(searchCatPanel);
221 splitPane.setRightComponent(contentPane);
222
223 Dimension minimumSize = new Dimension(200, 300);
224 treeView.setMinimumSize(minimumSize);
225 splitPane.setDividerLocation(200);
226
227 splitPane.setPreferredSize(new Dimension(600, 400));
228 splitPane.setOneTouchExpandable(true);
229
230
231 //////////////////////////////////////////////////////////////////////
232 // REFRESH FACTORY
233 Vector refreshListeners = new Vector();
234 refreshListeners.addElement(this);
235 refreshListeners.addElement(categoryTree);
236 refreshListeners.addElement(contentPane);
237
238 updateFactory updater = new updateFactory(refreshListeners);
239 registerChannelsToUpdater(updater, categoryTree.getCategories());
240 updater.start();
241
242
243
244
245
246 //////////////////////////////////////////////////////////////////////
247 // TOOLBAR
248 //////////////////////////////////////////////////////////////////////
249 toolBar = new JToolBar("Standard FM toolbar");
250
251 // New channel button
252 JButton toolbarButton = new JButton(new createChannelAction(this,
253 fmUITheme.NEW_CHANNEL_ICON,
254 categoryTree.getModel()));
255 toolbarButton.setMargin(fmUITheme.TOOLBAR_BUTTON_MARGIN);
256 toolBar.add("Create feed", toolbarButton);
257
258 // Save button
259 toolbarButton = new JButton(fmUITheme.SAVE_STORAGE_ICON);
260 toolbarButton.setActionCommand("Save");
261 toolbarButton.addActionListener(this);
262 toolbarButton.setToolTipText(fmUITheme.getString("button.save.tooltip"));
263 toolbarButton.setMargin(fmUITheme.TOOLBAR_BUTTON_MARGIN);
264 toolBar.add("Save storage", toolbarButton);
265
266 // Settings button
267
268 toolbarButton = new JButton(new customizeSettingsAction(this,
269 fmUITheme.SETTINGS_ICON,
270 runtimeProperties));
271 //toolbarButton.setIcon(fmUITheme.SETTINGS_ICON);
272 toolbarButton.setMargin(fmUITheme.TOOLBAR_BUTTON_MARGIN);
273 toolBar.add(toolbarButton);
274
275 // Refresh button
276 toolbarButton = new JButton(new refreshAction(categoryTree,
277 fmUITheme.REFRESH_ICON,
278 updater));
279 toolbarButton.setMargin(fmUITheme.TOOLBAR_BUTTON_MARGIN);
280 toolBar.add("refresh", toolbarButton);
281
282 // Stopn refresh button
283 toolbarButton = new JButton(new stopRefreshAction(categoryTree,
284 fmUITheme.STOP_ICON,
285 updater));
286 toolbarButton.setMargin(fmUITheme.TOOLBAR_BUTTON_MARGIN);
287 toolBar.add("stop", toolbarButton);
288
289
290 //Add the split pane and toolbar to the content
291 this.getContentPane().add(splitPane, BorderLayout.CENTER);
292 this.getContentPane().add(toolBar, BorderLayout.NORTH);
293
294
295
296
297 // SETTING UP MENU BAR
298 JMenuBar menuBar = new JMenuBar();
299 popupMenu = new JPopupMenu("fM popup menu");
300 //this.getContentPane().add(popupMenu);
301
302 setJMenuBar(menuBar);
303
304 JMenu editMenu = new JMenu(fmUITheme.getString("menu.edit"));
305 editMenu.setMnemonic(KeyEvent.VK_E);
306
307 JMenu optionsMenu = new JMenu(fmUITheme.getString("menu.options"));
308 optionsMenu.setMnemonic(KeyEvent.VK_O);
309
310 JMenu navigationMenu = new JMenu(fmUITheme.getString("menu.navigation"));
311 navigationMenu.setMnemonic(KeyEvent.VK_N);
312
313 JMenu helpMenu = new JMenu(fmUITheme.getString("menu.help"));
314 helpMenu.setMnemonic(KeyEvent.VK_H);
315 helpMenu.setEnabled(false);
316
317 // FILE MENU
318 JMenu fileMenu = new JMenu(fmUITheme.getString("menu.file"));
319 fileMenu.setMnemonic(KeyEvent.VK_F);
320
321 // NEW objects
322 JMenu newMenu = new JMenu("New");
323 newMenu.setMnemonic(KeyEvent.VK_N);
324
325 JMenuItem menuItem;
326 // new feed
327 Action newChAction = new createChannelAction(this, categoryTree.getModel());
328 newMenu.add(new JMenuItem(newChAction));
329 popupMenu.add(new JMenuItem(newChAction));
330
331 // new category
332 Action newCatAction = new createCategoryAction(categoryTree);
333 newMenu.add(new JMenuItem(newCatAction));
334 popupMenu.add(new JMenuItem(newCatAction));
335 popupMenu.addSeparator();
336
337 fileMenu.add(newMenu);
338 fileMenu.addSeparator();
339
340 // LOAD ITEMS
341 menuItem = new JMenuItem(fmUITheme.getString("menu.load"));
342 menuItem.setActionCommand("Load");
343 menuItem.setAccelerator(KeyStroke.getKeyStroke(
344 KeyEvent.VK_O, ActionEvent.CTRL_MASK));
345 menuItem.addActionListener(this);
346 fileMenu.add(menuItem);
347
348
349 // SAVE ITEMS
350 menuItem = new JMenuItem(fmUITheme.getString("menu.save"));
351 menuItem.setActionCommand("Save");
352 menuItem.setAccelerator(KeyStroke.getKeyStroke(
353 KeyEvent.VK_S, ActionEvent.CTRL_MASK));
354 menuItem.addActionListener(this);
355 fileMenu.add(menuItem);
356
357
358 menuItem = new JMenuItem(fmUITheme.getString("menu.save_as"));
359 menuItem.setActionCommand("Save as");
360 menuItem.setAccelerator(KeyStroke.getKeyStroke(
361 KeyEvent.VK_S, ActionEvent.CTRL_MASK | ActionEvent.SHIFT_MASK));
362 menuItem.addActionListener(this);
363 fileMenu.add(menuItem);
364
365 fileMenu.addSeparator();
366
367 // Exit ITEM
368 JMenuItem exitItem = new JMenuItem(fmUITheme.getString("menu.exit"));
369 exitItem.setActionCommand("Exit");
370 exitItem.setAccelerator(KeyStroke.getKeyStroke(
371 KeyEvent.VK_X, ActionEvent.CTRL_MASK));
372 exitItem.addActionListener(this);
373 fileMenu.add(exitItem);
374
375
376 //////////////////////////////////////////////////////////////////////////
377 // EDIT MENU
378
379 //
380 Vector editListeners = new Vector();
381 editListeners.add(categoryTree);
382 editListeners.add(this);
383 editListeners.add(contentPane);
384 editListeners.add(updater);
385
386 // EDIT
387 menuItem = new JMenuItem(new editAction(categoryTree, editListeners));
388 editMenu.add(menuItem);
389 popupMenu.add(new editAction(categoryTree, editListeners));
390
391 // REMOVE
392 menuItem = new JMenuItem(new removeAction(categoryTree));
393 editMenu.add(menuItem);
394 editMenu.addSeparator();
395 popupMenu.add(new removeAction(categoryTree));
396 popupMenu.addSeparator();
397
398 // UPDATE
399 editMenu.add(new JMenuItem(new refreshAction(categoryTree, updater)));
400 popupMenu.add(new JMenuItem(new refreshAction(categoryTree, updater)));
401
402 // STOP UPDATE
403 editMenu.add(new JMenuItem(new stopRefreshAction(categoryTree, updater)));
404 popupMenu.add(new JMenuItem(new stopRefreshAction(categoryTree, updater)));
405
406 editMenu.addSeparator();
407 editMenu.add(new JMenuItem(new purgeAllAction(categoryTree)));
408
409
410
411 //NAVIGATION
412
413 // GO BACK
414 menuItem = new JMenuItem(fmUITheme.getString("menu.show_previous"));
415 menuItem.setAccelerator(KeyStroke.getKeyStroke(
416 KeyEvent.VK_LEFT, ActionEvent.ALT_MASK));
417 menuItem.addActionListener(new ActionListener() {
418 public void actionPerformed(ActionEvent e) {
419 contentPane.showPreviousChannel();
420 }
421 });
422 navigationMenu.add(menuItem);
423
424 // GO FORWARD
425 menuItem = new JMenuItem(fmUITheme.getString("menu.show_next"));
426 menuItem.setAccelerator(KeyStroke.getKeyStroke(
427 KeyEvent.VK_RIGHT, ActionEvent.ALT_MASK));
428 menuItem.addActionListener(new ActionListener() {
429 public void actionPerformed(ActionEvent e) {
430 contentPane.showNextChannel();
431 }
432 });
433 navigationMenu.add(menuItem);
434 navigationMenu.addSeparator();
435
436 // SHOW PREVIOUS ITEM
437 menuItem = new JMenuItem(fmUITheme.getString("menu.show_previous_item"));
438 menuItem.setAccelerator(KeyStroke.getKeyStroke(
439 KeyEvent.VK_UP, ActionEvent.ALT_MASK));
440 menuItem.addActionListener(new ActionListener() {
441 public void actionPerformed(ActionEvent e) {
442 contentPane.showPreviousItem();
443 }
444 });
445 navigationMenu.add(menuItem);
446
447 // SHOW NEXT ITEM
448 menuItem = new JMenuItem(fmUITheme.getString("menu.show_next_item"));
449 menuItem.setAccelerator(KeyStroke.getKeyStroke(
450 KeyEvent.VK_DOWN, ActionEvent.ALT_MASK));
451 menuItem.addActionListener(new ActionListener() {
452 public void actionPerformed(ActionEvent e) {
453 contentPane.showNextItem();
454 }
455 });
456 navigationMenu.add(menuItem);
457 navigationMenu.addSeparator();
458
459 // open LINK menu item
460 openLinkAction openLinkAct = new openLinkAction(contentPane);
461 navigationMenu.add(new JMenuItem(openLinkAct));
462
463
464 // OPTIONS MENU
465
466 // AUTO SAVE ON EXIT
467 boolean autosave;
468 if (runtimeProperties.containsKey(fmGuiConstants.AUTOSAVE_ON_EXIT))
469 {
470 autosave = (runtimeProperties.getProperty(fmGuiConstants.AUTOSAVE_ON_EXIT).equals("T")) ?
471 true : false;
472 }
473 else
474 {
475 runtimeProperties.put(fmGuiConstants.AUTOSAVE_ON_EXIT, "T");
476 autosave = true;
477 }
478
479 JCheckBoxMenuItem cbItem = new JCheckBoxMenuItem(fmUITheme.getString("menu.autosave"),
480 autosave);
481 cbItem.addItemListener(new ItemListener() {
482
483 public void itemStateChanged(ItemEvent e)
484 {
485 if (((JCheckBoxMenuItem)e.getSource()).isSelected() == false)
486 runtimeProperties.put(fmGuiConstants.AUTOSAVE_ON_EXIT, "F");
487 else
488 runtimeProperties.put(fmGuiConstants.AUTOSAVE_ON_EXIT, "T");
489 }
490 });
491 optionsMenu.add(cbItem);
492
493 // Show/hide toolbar
494 cbItem = new JCheckBoxMenuItem(fmUITheme.getString("menu.show_toolbar"), true);
495 cbItem.addItemListener(new ItemListener() {
496
497 public void itemStateChanged(ItemEvent e)
498 {
499 if (((JCheckBoxMenuItem)e.getSource()).isSelected() == false)
500 toolBar.setVisible(false);
501 else
502 toolBar.setVisible(true);
503 }
504
505 });
506 cbItem.setAccelerator(KeyStroke.getKeyStroke(
507 KeyEvent.VK_T, ActionEvent.CTRL_MASK | ActionEvent.ALT_MASK));
508 optionsMenu.add(cbItem);
509 optionsMenu.addSeparator();
510
511
512 JMenuItem customizeItem = new JMenuItem(new customizeSettingsAction(this, runtimeProperties));
513 optionsMenu.add(customizeItem);
514
515 //////////////////////////////////
516 // MENU FINISH
517 menuBar.add(fileMenu);
518 menuBar.add(editMenu);
519 menuBar.add(navigationMenu);
520 menuBar.add(optionsMenu);
521 menuBar.add(helpMenu);
522
523 /////////////////////////////////////////////////////////////////
524 // LOOKING FOR THE LAST OPENED CHANNEL AND DISPLAYING IT IF FOUND
525 if (runtimeProperties.containsKey(fmGuiConstants.LAST_OPEN_CHANNEL_ID))
526 {
527 int last_ch_id = Integer.parseInt(runtimeProperties.getProperty(fmGuiConstants.LAST_OPEN_CHANNEL_ID));
528
529 fmTreeNode tNode = categoryTree.findChannelNode(categoryTree.getRootNode(),last_ch_id);
530 if (tNode != null) {
531 ((DefaultTreeModel) categoryTree.getModel()).reload(tNode);
532 categoryTree.setSelectionPath(new TreePath(tNode.getPath()));
533 }
534 }
535
536 // DEFAULT WINDOW ADAPTER
537 addWindowListener(new WindowAdapter() {
538 public void windowClosing(WindowEvent e)
539 {
540 onClose();
541 }
542 });
543
544 }
545
546 /**
547 * Overrides <code>pack()</code>
548 */
549 public void pack()
550 {
551 //
552
553 // RESTORING WINDOW SETTINGS FROM RUNTIME PROPERTIES
554 if (runtimeProperties.containsKey("window.x") &&
555 runtimeProperties.containsKey("window.y") &&
556 runtimeProperties.containsKey("window.width") &&
557 runtimeProperties.containsKey("window.height"))
558 {
559 try
560 {
561 this.setSize(Integer.parseInt(runtimeProperties.getProperty("window.width")),
562 Integer.parseInt(runtimeProperties.getProperty("window.height")));
563 this.setLocation(Integer.parseInt(runtimeProperties.getProperty("window.x")),
564 Integer.parseInt(runtimeProperties.getProperty("window.y")));
565
566 }
567 catch (NumberFormatException ne)
568 {
569 super.pack();
570 }
571 }
572 else
573 {
574 super.pack();
575 }
576
577
578
579 }
580
581 /**
582 * Action event hanlder.
583 *
584 * Handled events: Load feeds,Save feeds, Save as feeds
585 *
586 * @param e
587 */
588 public void actionPerformed(ActionEvent e)
589 {
590 String actionCommand = e.getActionCommand();
591
592 if (actionCommand.equalsIgnoreCase("Load")) // LOAD FEEDS FROM FILE
593 {
594 JFileChooser fc;
595
596 // restoring previously opened directory
597 String prev_path = runtimeProperties.getProperty(fmGuiConstants.LAST_OPEN_DIRECTORY);
598 if (prev_path != null)
599 fc = new JFileChooser(prev_path);
600 else
601 fc = new JFileChooser();
602
603 int returnVal = fc.showOpenDialog(this);
604
605 if (returnVal == JFileChooser.APPROVE_OPTION)
606 {
607 File file = fc.getSelectedFile();
608 String srcPath = file.getPath();
609 storage.setParamValue(ChannelStorage.SOURCE,srcPath);
610
611 try
612 {
613 storage.load();
614 runtimeProperties.setProperty(fmGuiConstants.LAST_OPEN_DIRECTORY,
615 file.getPath());
616 }
617 catch (IOException ie)
618 {
619 ie.printStackTrace();
620 return;
621 }
622 catch (rssParseException pe)
623 {
624 pe.printStackTrace();
625 return;
626 }
627
628 this.categoryTree.regenerateTree(this.storage.getCategories());
629 }
630
631 }
632 else if (actionCommand.equalsIgnoreCase("Save")) // SAVE
633 {
634 saveStorage((String)storage.getParamValue(ChannelStorage.SOURCE)); // SAVE TO CURRENT FILE
635 }
636 else if (actionCommand.equalsIgnoreCase("Save as")) // SAVE FEEDS AS...
637 {
638 saveStorage(""); // will open choose file dialog
639 }
640 else if (actionCommand.equalsIgnoreCase("Refresh")) // REFRESH
641 {
642 fmTreeNode node = (fmTreeNode)categoryTree.getLastSelectedPathComponent();
643 if (node == null) return;
644
645 // we normally will never meet a string (root node)
646 try
647 {
648 ((Updateable)node.getUserObject()).update();
649 contentPane.setChannel((rssChannel)node.getUserObject());
650 }
651 catch (updateException ue)
652 {
653 ue.printStackTrace();
654 System.out.println("Update exception: " + e.toString());
655 }
656 }
657 else if (actionCommand.equalsIgnoreCase("Exit")) // Exit operation
658 {
659 onClose();
660 }
661
662 }
663
664
665
666 private class treeMouseListener extends MouseAdapter {
667
668 public void mousePressed(MouseEvent e) {
669 displayPopupMenu(e);
670 }
671
672 public void mouseReleased(MouseEvent e) {
673 displayPopupMenu(e);
674 }
675
676 private void displayPopupMenu(MouseEvent e) {
677
678 if (e.isPopupTrigger()) {
679 popupMenu.show(e.getComponent(),
680 e.getX(), e.getY());
681 }
682 }
683
684 }
685
686
687 /**
688 * Save storage to file specified by path.
689 *
690 * If string identifying path is zero length the 'Choose file' dialog
691 * will appear.
692 *
693 * @param path
694 */
695 private void saveStorage(String path)
696 {
697 if (path.length() == 0)
698 {
699 // restoring previously opened directory for save operation
700 JFileChooser fc;
701 String prev_path = runtimeProperties.getProperty(fmGuiConstants.LAST_SAVE_DIRECTORY);
702
703 if (prev_path != null)
704 fc = new JFileChooser(prev_path);
705 else
706 fc = new JFileChooser();
707
708
709 int returnVal = fc.showSaveDialog(this);
710
711 if (returnVal == JFileChooser.APPROVE_OPTION)
712 {
713 storage.setParamValue(ChannelStorage.DESTINATION,
714 fc.getSelectedFile().getPath());
715
716 runtimeProperties.setProperty(fmGuiConstants.LAST_SAVE_DIRECTORY,
717 fc.getSelectedFile().getPath());
718 }
719 else
720 return;
721 }
722 else
723 {
724 storage.setParamValue(ChannelStorage.DESTINATION,path);
725 }
726
727 try
728 {
729 storage.save();
730 }
731 catch (IOException ie)
732 {
733 ie.printStackTrace();
734 }
735
736 }
737
738 /**
739 * Perform post close operations
740 *
741 * This one will save our current state on local disk
742 *
743 */
744 private void onClose()
745 {
746 String file_name = runtimeProperties.getProperty("DEFAULT_INI_FILE");
747
748 FileOutputStream out;
749 try
750 {
751 out = new FileOutputStream(file_name);
752
753 // saving window position (possibly move names to GuiConstants)
754 runtimeProperties.put("window.x", (new Integer(this.getX())).toString());
755 runtimeProperties.put("window.y", (new Integer(this.getY())).toString());
756 runtimeProperties.put("window.width", (new Integer(this.getWidth())).toString());
757 runtimeProperties.put("window.height", (new Integer(this.getHeight())).toString());
758
759 //saving currently expanded tree nodes
760 runtimeProperties.store(out,"");
761 out.close();
762 }
763 catch (FileNotFoundException fnfe)
764 {
765 fnfe.printStackTrace();
766 }
767 catch (IOException ie)
768 {
769 ie.printStackTrace();
770 }
771
772 if (runtimeProperties.getProperty(fmGuiConstants.AUTOSAVE_ON_EXIT).equals("T"))
773 {
774 storage.setCategories(categoryTree.getCategories());
775 saveStorage((String)storage.getParamValue(ChannelStorage.SOURCE)); // SAVING TO CURRENT FILE
776 }
777
778 System.exit(0);
779 }
780
781 /**
782 * Find all matched items within all available channels
783 *
784 * <p>Results are set as new channel for content panel</p>
785 *
786 * @param pattern
787 */
788 public void performSearchOperation(String pattern)
789 {
790 if (pattern == null)
791 return;
792
793 String resultPattern = pattern;
794 //todo move to separate function
795 {
796 // Removing regular expression constructs fromsource string
797 String[] constructs = new String[] { "\\*",
798 "\\(","\\)",
799 "\\[","\\]",
800 "\\{","\\}",
801 "\\.","\\?","\\+"};
802 Pattern s;
803 for (int i = 0, n = constructs.length; i < n; i++) {
804 s = Pattern.compile(constructs[i]);
805 if (s.matcher(resultPattern).find())
806 resultPattern = resultPattern.replaceAll(constructs[i],"\\" + constructs[i]);
807 }
808
809 }
810
811 Pattern p = Pattern.compile(resultPattern, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.MULTILINE);
812 rssChannel searchResult = new rssChannel();
813 HashMap cats = new HashMap(categoryTree.getCategories());
814
815 if (resultPattern.length() > 0) {
816 synchronized (cats) {
817 for (Iterator i = cats.values().iterator(); i.hasNext();)
818 findItem((rssChannelCategory) i.next(), p, searchResult);
819 }
820 }
821
822 if (searchResult.getItemVector().size() > 0)
823 {
824 searchResult.setTitle(fmUITheme.getString("label.search_results") +
825 " " + pattern);
826 searchResult.setID(0);
827 contentPane.setChannel(searchResult);
828 }
829 else
830 JOptionPane.showMessageDialog(this,
831 fmUITheme.getString("msg.nothing_found"),
832 fmUITheme.getString("label.search_results"),
833 JOptionPane.INFORMATION_MESSAGE);
834
835 }
836
837 private void findItem(rssChannelCategory start_cat, Pattern pattern, rssChannel result)
838 {
839 Map subcats = start_cat.getSubCategories();
840
841 synchronized (subcats)
842 {
843 for (Iterator i = subcats.values().iterator(); i.hasNext();)
844 {
845 findItem((rssChannelCategory) i.next(), pattern, result);
846 }
847 }
848
849 Map channels = start_cat.getChannels();
850
851 synchronized (channels)
852 {
853 Vector items;
854 rssItem item;
855 rssChannel pCh;
856
857 for (Iterator i = channels.values().iterator(); i.hasNext();)
858 {
859 pCh = (rssChannel)i.next();
860 items = pCh.getItemVector();
861 for (int k=0, n = items.size(); k < n; k++)
862 {
863 item = (rssItem) items.get(k);
864 if (pattern.matcher(item.getDescription()).find() ||
865 pattern.matcher(item.getTitle()).find() ||
866 pattern.matcher(item.getAuthor()).find() ||
867 pattern.matcher(item.getDateCreated().toString()).find() ||
868 pattern.matcher(item.getLink()).find())
869 {
870 item.setParentInfo(pCh.getID(), pCh.getTitle());
871 result.addItem(item);
872 }
873 }
874 }
875 }
876 }
877
878 /**
879 * Perform L&F update using runtime and theme settings
880 */
881 public void performLookAndFeelUpdate() {
882
883 try {
884 fmUITheme.initFeedManLookAndFeel();
885 }
886 catch (IOException ie) {
887 ie.printStackTrace();
888 }
889
890 if (runtimeProperties.get(fmGuiConstants.URL_HANDLING_CMD).equals(fmGuiConstants.INTERNAL_URL_HANDLER))
891 linkRouter.setBrowserCommand(null);
892 else
893 linkRouter.setBrowserCommand((String) runtimeProperties.get(fmGuiConstants.URL_HANDLING_CMD));
894
895 categoryTree.repaint();
896 contentPane.performLookAndFeelUpdate();
897 repaint();
898 }
899
900
901 /**
902 * Does all L&F dirty checks
903 * @param props
904 */
905 public void setupLookAndFeelDefaults(Properties props) {
906
907 if (props.containsKey(fmGuiConstants.TREE_FONT))
908 fmUITheme.TREE_FONT = Font.decode((String) props.get(fmGuiConstants.TREE_FONT));
909 else
910 props.put(fmGuiConstants.TREE_FONT, fmUITheme.fontToString(fmUITheme.TREE_FONT,null));
911
912 if (props.containsKey(fmGuiConstants.CONTENT_FONT))
913 fmUITheme.CONTENT_FONT = Font.decode((String) props.get(fmGuiConstants.CONTENT_FONT));
914 else
915 props.put(fmGuiConstants.CONTENT_FONT, fmUITheme.fontToString(fmUITheme.CONTENT_FONT,null));
916
917 if (props.containsKey(fmGuiConstants.HEADERS_FONT))
918 fmUITheme.HEADERS_FONT = Font.decode((String) props.get(fmGuiConstants.HEADERS_FONT));
919 else
920 props.put(fmGuiConstants.HEADERS_FONT, fmUITheme.fontToString(fmUITheme.HEADERS_FONT,null));
921 }
922
923 /**
924 * Register specified channels in the update factory
925 * @param f
926 * @param cMap Map of categories of channels to register
927 */
928
929 public void registerChannelsToUpdater(updateFactory f, Map cMap) {
930
931 rssChannelCategory tcat;
932 Map tMap;
933
934 for (Iterator i = cMap.values().iterator(); i.hasNext();)
935 {
936 tcat = (rssChannelCategory) i.next();
937 registerChannelsToUpdater(f,tcat.getSubCategories());
938 tMap = tcat.getChannels();
939 for (Iterator chi = tMap.values().iterator(); chi.hasNext();)
940 f.registerForUpdate((Updateable) chi.next(), false); // LOOK AT SARTUP SETTINGS
941 }
942 }
943 }